blob: 2e890b53e6bc48132bbcdab384069ca980d56924 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use fuchsia_sync::Mutex;
use std::collections::HashSet;
use {fidl_fuchsia_net as net, fidl_fuchsia_net_ext as net_ext};
/// Alias for a list of [`net::SocketAddress`].
///
/// The servers in the list are in priority order.
pub type ServerList = Vec<net::SocketAddress>;
/// Holds current [`ServerConfigSink`] state.
#[derive(Debug)]
struct ServerConfigInner {
servers: ServerList,
}
/// Provides shared access to a [`ServerList`].
#[derive(Debug)]
pub struct ServerConfigState(Mutex<ServerConfigInner>);
/// The result of updating a [`ServerConfigState`].
#[derive(Debug, PartialEq, Eq)]
pub enum UpdateServersResult {
/// Server list was updated to the provided value.
Updated(ServerList),
/// No change was applied to the server list.
NoChange,
/// Invalid servers provided.
InvalidsServers,
}
impl ServerConfigState {
/// Creates a new empty `ServerConfigState`.
pub fn new() -> Self {
Self(Mutex::new(ServerConfigInner { servers: Vec::new() }))
}
/// Returns the servers.
pub fn servers(&self) -> ServerList {
let Self(current) = self;
let inner = current.lock();
inner.servers.clone()
}
/// Updates the server list after deduplication.
pub fn update_servers(&self, mut servers: ServerList) -> UpdateServersResult {
let Self(current) = self;
if servers.iter().any(|s| {
// Addresses must not contain an unspecified or multicast address.
let net_ext::SocketAddress(sockaddr) = From::from(*s);
let ip = sockaddr.ip();
ip.is_multicast() || ip.is_unspecified()
}) {
return UpdateServersResult::InvalidsServers;
}
let mut set = HashSet::new();
let () = servers.retain(|s| set.insert(*s));
let mut inner = current.lock();
if inner.servers == servers {
return UpdateServersResult::NoChange;
}
inner.servers = servers.clone();
UpdateServersResult::Updated(servers)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_util::*;
use net_declare::fidl_socket_addr;
#[test]
fn test_config_state() {
let state = ServerConfigState::new();
assert_eq!(state.servers(), []);
// Ordering is respected.
let values = [DHCP_SERVER, NDP_SERVER];
assert_eq!(
state.update_servers(values.to_vec()),
UpdateServersResult::Updated(values.to_vec())
);
assert_eq!(state.servers(), values);
// Duplicates are removed.
assert_eq!(
state.update_servers(vec![DHCP_SERVER, DHCP_SERVER, NDP_SERVER]),
UpdateServersResult::NoChange
);
assert_eq!(state.servers(), values);
// Bad addresses are rejected.
for addr in [
fidl_socket_addr!("0.0.0.0:1111"),
fidl_socket_addr!("[::]:2222"),
fidl_socket_addr!("224.0.0.1:3333"),
fidl_socket_addr!("[ff02::1]:4444"),
] {
assert_eq!(state.update_servers(vec![addr]), UpdateServersResult::InvalidsServers);
}
assert_eq!(state.servers(), values);
// Empty inputs become empty output.
assert_eq!(state.update_servers(vec![]), UpdateServersResult::Updated(vec![]));
assert_eq!(state.servers(), []);
}
}