mmids_core/net/
mod.rs

1//! Networking layer for Mmids applications
2
3use cidr_utils::cidr::{IpCidr, Ipv4Cidr};
4use std::fmt::Formatter;
5use std::net::Ipv4Addr;
6use thiserror::Error;
7
8pub mod tcp;
9
10/// A unique identifier for any given TCP connection, or unique UDP client.  If a TCP client
11/// disconnects and reconnects it will be seen with a brand new connection id
12#[derive(Clone, Debug, Eq, Hash)]
13pub struct ConnectionId(pub String);
14
15impl std::fmt::Display for ConnectionId {
16    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
17        write!(f, "{}", self.0)
18    }
19}
20
21impl PartialEq<Self> for ConnectionId {
22    fn eq(&self, other: &Self) -> bool {
23        self.0 == other.0
24    }
25}
26
27/// Enumeration to make handling ip addresses vs subnets easier
28#[derive(Debug, PartialEq)]
29pub enum IpAddress {
30    Exact(Ipv4Addr),
31    Cidr(Ipv4Cidr),
32}
33
34/// Error when a given ip address or subnet could not be parsed from a given input
35#[derive(Error, Debug)]
36pub enum IpAddressParseError {
37    #[error("The value '{0}' was not a valid ip address or cidr value")]
38    InvalidValue(String),
39}
40
41impl IpAddress {
42    /// Checks if the other exact ip address is a match for the current ip address specification.
43    /// An address is a match if the current ip address is an exact one and both are exactly equal,
44    /// or if the current ip address is a CIDR subnet mask and the other ip address is contained
45    /// within.
46    pub fn matches(&self, other_address: &Ipv4Addr) -> bool {
47        match self {
48            IpAddress::Exact(self_address) => self_address == other_address,
49            IpAddress::Cidr(cidr) => cidr.contains(other_address),
50        }
51    }
52
53    /// Attempts to parse a string supposedly containing a comma delimited list of ip addresses
54    /// and cidr values.  An empty string will return an empty collection of ips.
55    pub fn parse_comma_delimited_list(
56        input: Option<&String>,
57    ) -> Result<Vec<IpAddress>, IpAddressParseError> {
58        let mut ips = Vec::new();
59        match input {
60            None => (),
61            Some(input) => {
62                for input in input.split(",") {
63                    let ip = if let Ok(ip) = input.parse::<Ipv4Addr>() {
64                        Some(IpAddress::Exact(ip))
65                    } else if let Ok(cidr) = IpCidr::from_str(input) {
66                        match cidr {
67                            IpCidr::V4(cidr) => Some(IpAddress::Cidr(cidr)),
68                            _ => None,
69                        }
70                    } else {
71                        None
72                    };
73
74                    if let Some(ip) = ip {
75                        ips.push(ip);
76                    } else {
77                        return Err(IpAddressParseError::InvalidValue(input.to_string()));
78                    }
79                }
80            }
81        }
82
83        Ok(ips)
84    }
85}