Skip to main content

tox_packet/
ip_port.rs

1//! `IpAddr` with a port number.
2
3use std::net::{
4    IpAddr,
5    Ipv4Addr,
6    Ipv6Addr,
7    SocketAddr,
8};
9
10use nom::{
11    IResult,
12    do_parse, alt, switch, terminated, map, cond, call, take,
13    number::complete::{be_u16, le_u8},
14};
15use cookie_factory::{do_gen, gen_slice, gen_cond, gen_call, gen_be_u8, gen_be_u16};
16
17use tox_binary_io::*;
18
19/// Size of serialized `IpPort` struct.
20pub const SIZE_IPPORT: usize = 19;
21
22/// IPv4 can be padded with 12 bytes of zeros so that both IPv4 and IPv6 have
23/// the same stored size.
24pub const IPV4_PADDING_SIZE: usize = 12;
25
26/// Defines whether 12 bytes padding should be inserted after IPv4 address to
27/// align it with IPv6 address.
28#[derive(Copy, Clone, Debug, Eq, PartialEq)]
29pub enum IpPortPadding {
30    /// Padding should be inserted.
31    WithPadding,
32    /// Padding should not be inserted.
33    NoPadding,
34}
35
36/** Transport protocol type: `UDP` or `TCP`.
37
38The binary representation of `ProtocolType` is a single bit: 0 for `UDP`, 1 for
39`TCP`. If encoded as standalone value, the bit is stored in the least
40significant bit of a byte.
41
42*/
43#[derive(Copy, Clone, Debug, Eq, PartialEq)]
44pub enum ProtocolType {
45    /// `UDP` type if the least significant bit is 0.
46    UDP,
47    /// `TCP` type if the least significant bit is 1.
48    TCP
49}
50
51/** `IpAddr` with a port number. IPv4 can be padded with 12 bytes of zeros
52so that both IPv4 and IPv6 have the same stored size.
53
54Serialized form:
55
56Length      | Content
57----------- | ------
58`1`         | IpType
59`4` or `16` | IPv4 or IPv6 address
60`0` or `12` | Padding for IPv4 (if needed)
61`2`         | Port
62
63*/
64#[derive(Clone, Debug, Eq, PartialEq)]
65pub struct IpPort {
66    /// Type of protocol
67    pub protocol: ProtocolType,
68    /// IP address
69    pub ip_addr: IpAddr,
70    /// Port number
71    pub port: u16
72}
73
74impl IpPort {
75    /** Get IP Type byte.
76
77    * 1st bit - protocol
78    * 4th bit - address family
79
80    Value | Type
81    ----- | ----
82    `2`   | UDP IPv4
83    `10`  | UDP IPv6
84    `130` | TCP IPv4
85    `138` | TCP IPv6
86
87    */
88    fn ip_type(&self) -> u8 {
89        if self.ip_addr.is_ipv4() {
90            match self.protocol {
91                ProtocolType::UDP => 2,
92                ProtocolType::TCP => 130,
93            }
94        } else {
95            match self.protocol {
96                ProtocolType::UDP => 10,
97                ProtocolType::TCP => 138,
98            }
99        }
100    }
101
102    /// Parse `IpPort` with UDP protocol type with optional padding.
103    pub fn from_udp_bytes(input: &[u8], padding: IpPortPadding) -> IResult<&[u8], IpPort> {
104        do_parse!(input,
105            ip_addr: switch!(le_u8,
106                2 => terminated!(
107                    map!(Ipv4Addr::from_bytes, IpAddr::V4),
108                    cond!(padding == IpPortPadding::WithPadding, take!(IPV4_PADDING_SIZE))
109                ) |
110                10 => map!(Ipv6Addr::from_bytes, IpAddr::V6)
111            ) >>
112            port: be_u16 >>
113            (IpPort { protocol: ProtocolType::UDP, ip_addr, port })
114        )
115    }
116
117    /// Parse `IpPort` with TCP protocol type with optional padding.
118    pub fn from_tcp_bytes(input: &[u8], padding: IpPortPadding) -> IResult<&[u8], IpPort> {
119        do_parse!(input,
120            ip_addr: switch!(le_u8,
121                130 => terminated!(
122                    map!(Ipv4Addr::from_bytes, IpAddr::V4),
123                    cond!(padding == IpPortPadding::WithPadding, take!(IPV4_PADDING_SIZE))
124                ) |
125                138 => map!(Ipv6Addr::from_bytes, IpAddr::V6)
126            ) >>
127            port: be_u16 >>
128            (IpPort { protocol: ProtocolType::TCP, ip_addr, port })
129        )
130    }
131
132    /// Parse `IpPort` with optional padding.
133    pub fn from_bytes(input: &[u8], padding: IpPortPadding) -> IResult<&[u8], IpPort> {
134        alt!(input, call!(IpPort::from_udp_bytes, padding) | call!(IpPort::from_tcp_bytes, padding))
135    }
136
137    /// Write `IpPort` with UDP protocol type with optional padding.
138    pub fn to_udp_bytes<'a>(&self, buf: (&'a mut [u8], usize), padding: IpPortPadding) -> Result<(&'a mut [u8], usize), GenError> {
139        do_gen!(buf,
140            gen_cond!(self.protocol == ProtocolType::TCP, |buf| gen_error(buf, 0)) >>
141            gen_call!(|buf, ip_port| IpPort::to_bytes(ip_port, buf, padding), self)
142        )
143    }
144
145    /// Write `IpPort` with TCP protocol type with optional padding.
146    pub fn to_tcp_bytes<'a>(&self, buf: (&'a mut [u8], usize), padding: IpPortPadding) -> Result<(&'a mut [u8], usize), GenError> {
147        do_gen!(buf,
148            gen_cond!(self.protocol == ProtocolType::UDP, |buf| gen_error(buf, 0)) >>
149            gen_call!(|buf, ip_port| IpPort::to_bytes(ip_port, buf, padding), self)
150        )
151    }
152
153    /// Write `IpPort` with optional padding.
154    pub fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize), padding: IpPortPadding) -> Result<(&'a mut [u8], usize), GenError> {
155        do_gen!(buf,
156            gen_be_u8!(self.ip_type()) >>
157            gen_call!(|buf, ip_addr| IpAddr::to_bytes(ip_addr, buf), &self.ip_addr) >>
158            gen_cond!(padding == IpPortPadding::WithPadding && self.ip_addr.is_ipv4(), gen_slice!(&[0; IPV4_PADDING_SIZE])) >>
159            gen_be_u16!(self.port)
160        )
161    }
162
163    /// Create new `IpPort` from `SocketAddr` with UDP type.
164    pub fn from_udp_saddr(saddr: SocketAddr) -> IpPort {
165        IpPort {
166            protocol: ProtocolType::UDP,
167            ip_addr: saddr.ip(),
168            port: saddr.port()
169        }
170    }
171
172    /// Create new `IpPort` from `SocketAddr` with TCP type.
173    pub fn from_tcp_saddr(saddr: SocketAddr) -> IpPort {
174        IpPort {
175            protocol: ProtocolType::TCP,
176            ip_addr: saddr.ip(),
177            port: saddr.port()
178        }
179    }
180
181    /// Convert `IpPort` to `SocketAddr`.
182    pub fn to_saddr(&self) -> SocketAddr {
183        SocketAddr::new(self.ip_addr, self.port)
184    }
185}
186
187#[cfg(test)]
188mod tests {
189    use super::*;
190
191    macro_rules! ip_port_with_padding_encode_decode_test (
192        ($test:ident, $protocol:expr) => (
193            #[test]
194            fn $test() {
195                let value = IpPort {
196                    protocol: $protocol,
197                    ip_addr: "5.6.7.8".parse().unwrap(),
198                    port: 12345
199                };
200                let mut buf = [0; SIZE_IPPORT];
201                let (_, size) = value.to_bytes((&mut buf, 0), IpPortPadding::WithPadding).unwrap();
202                assert_eq!(size, SIZE_IPPORT);
203                let (rest, decoded_value) = IpPort::from_bytes(&buf[..size], IpPortPadding::WithPadding).unwrap();
204                assert!(rest.is_empty());
205                assert_eq!(decoded_value, value);
206            }
207        )
208    );
209
210    ip_port_with_padding_encode_decode_test!(ip_port_udp_with_padding_encode_decode, ProtocolType::UDP);
211    ip_port_with_padding_encode_decode_test!(ip_port_tcp_with_padding_encode_decode, ProtocolType::TCP);
212
213    macro_rules! ip_port_without_padding_encode_decode_test (
214        ($test:ident, $protocol:expr) => (
215            #[test]
216            fn $test() {
217                let value = IpPort {
218                    protocol: $protocol,
219                    ip_addr: "5.6.7.8".parse().unwrap(),
220                    port: 12345
221                };
222                let mut buf = [0; SIZE_IPPORT - IPV4_PADDING_SIZE];
223                let (_, size) = value.to_bytes((&mut buf, 0), IpPortPadding::NoPadding).unwrap();
224                assert_eq!(size, SIZE_IPPORT - IPV4_PADDING_SIZE);
225                let (rest, decoded_value) = IpPort::from_bytes(&buf[..size], IpPortPadding::NoPadding).unwrap();
226                assert!(rest.is_empty());
227                assert_eq!(decoded_value, value);
228            }
229        )
230    );
231
232    ip_port_without_padding_encode_decode_test!(ip_port_udp_without_padding_encode_decode, ProtocolType::UDP);
233    ip_port_without_padding_encode_decode_test!(ip_port_tcp_without_padding_encode_decode, ProtocolType::TCP);
234
235    #[test]
236    fn ip_port_from_to_udp_saddr() {
237        let ip_port_1 = IpPort {
238            protocol: ProtocolType::UDP,
239            ip_addr: "5.6.7.8".parse().unwrap(),
240            port: 12345
241        };
242        let ip_port_2 = IpPort::from_udp_saddr(ip_port_1.to_saddr());
243        assert_eq!(ip_port_2, ip_port_1);
244    }
245
246    #[test]
247    fn ip_port_from_to_tcp_saddr() {
248        let ip_port_1 = IpPort {
249            protocol: ProtocolType::TCP,
250            ip_addr: "5.6.7.8".parse().unwrap(),
251            port: 12345
252        };
253        let ip_port_2 = IpPort::from_tcp_saddr(ip_port_1.to_saddr());
254        assert_eq!(ip_port_2, ip_port_1);
255    }
256}