1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// 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(), []);
    }
}