use std::net::{
IpAddr,
Ipv4Addr,
Ipv6Addr,
SocketAddr,
};
use nom::{
IResult,
do_parse, alt, switch, terminated, map, cond, call, take,
number::complete::{be_u16, le_u8},
};
use cookie_factory::{do_gen, gen_slice, gen_cond, gen_call, gen_be_u8, gen_be_u16};
use tox_binary_io::*;
pub const SIZE_IPPORT: usize = 19;
pub const IPV4_PADDING_SIZE: usize = 12;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum IpPortPadding {
WithPadding,
NoPadding,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ProtocolType {
UDP,
TCP
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct IpPort {
pub protocol: ProtocolType,
pub ip_addr: IpAddr,
pub port: u16
}
impl IpPort {
fn ip_type(&self) -> u8 {
if self.ip_addr.is_ipv4() {
match self.protocol {
ProtocolType::UDP => 2,
ProtocolType::TCP => 130,
}
} else {
match self.protocol {
ProtocolType::UDP => 10,
ProtocolType::TCP => 138,
}
}
}
pub fn from_udp_bytes(input: &[u8], padding: IpPortPadding) -> IResult<&[u8], IpPort> {
do_parse!(input,
ip_addr: switch!(le_u8,
2 => terminated!(
map!(Ipv4Addr::from_bytes, IpAddr::V4),
cond!(padding == IpPortPadding::WithPadding, take!(IPV4_PADDING_SIZE))
) |
10 => map!(Ipv6Addr::from_bytes, IpAddr::V6)
) >>
port: be_u16 >>
(IpPort { protocol: ProtocolType::UDP, ip_addr, port })
)
}
pub fn from_tcp_bytes(input: &[u8], padding: IpPortPadding) -> IResult<&[u8], IpPort> {
do_parse!(input,
ip_addr: switch!(le_u8,
130 => terminated!(
map!(Ipv4Addr::from_bytes, IpAddr::V4),
cond!(padding == IpPortPadding::WithPadding, take!(IPV4_PADDING_SIZE))
) |
138 => map!(Ipv6Addr::from_bytes, IpAddr::V6)
) >>
port: be_u16 >>
(IpPort { protocol: ProtocolType::TCP, ip_addr, port })
)
}
pub fn from_bytes(input: &[u8], padding: IpPortPadding) -> IResult<&[u8], IpPort> {
alt!(input, call!(IpPort::from_udp_bytes, padding) | call!(IpPort::from_tcp_bytes, padding))
}
pub fn to_udp_bytes<'a>(&self, buf: (&'a mut [u8], usize), padding: IpPortPadding) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_cond!(self.protocol == ProtocolType::TCP, |buf| gen_error(buf, 0)) >>
gen_call!(|buf, ip_port| IpPort::to_bytes(ip_port, buf, padding), self)
)
}
pub fn to_tcp_bytes<'a>(&self, buf: (&'a mut [u8], usize), padding: IpPortPadding) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_cond!(self.protocol == ProtocolType::UDP, |buf| gen_error(buf, 0)) >>
gen_call!(|buf, ip_port| IpPort::to_bytes(ip_port, buf, padding), self)
)
}
pub fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize), padding: IpPortPadding) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_be_u8!(self.ip_type()) >>
gen_call!(|buf, ip_addr| IpAddr::to_bytes(ip_addr, buf), &self.ip_addr) >>
gen_cond!(padding == IpPortPadding::WithPadding && self.ip_addr.is_ipv4(), gen_slice!(&[0; IPV4_PADDING_SIZE])) >>
gen_be_u16!(self.port)
)
}
pub fn from_udp_saddr(saddr: SocketAddr) -> IpPort {
IpPort {
protocol: ProtocolType::UDP,
ip_addr: saddr.ip(),
port: saddr.port()
}
}
pub fn from_tcp_saddr(saddr: SocketAddr) -> IpPort {
IpPort {
protocol: ProtocolType::TCP,
ip_addr: saddr.ip(),
port: saddr.port()
}
}
pub fn to_saddr(&self) -> SocketAddr {
SocketAddr::new(self.ip_addr, self.port)
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! ip_port_with_padding_encode_decode_test (
($test:ident, $protocol:expr) => (
#[test]
fn $test() {
let value = IpPort {
protocol: $protocol,
ip_addr: "5.6.7.8".parse().unwrap(),
port: 12345
};
let mut buf = [0; SIZE_IPPORT];
let (_, size) = value.to_bytes((&mut buf, 0), IpPortPadding::WithPadding).unwrap();
assert_eq!(size, SIZE_IPPORT);
let (rest, decoded_value) = IpPort::from_bytes(&buf[..size], IpPortPadding::WithPadding).unwrap();
assert!(rest.is_empty());
assert_eq!(decoded_value, value);
}
)
);
ip_port_with_padding_encode_decode_test!(ip_port_udp_with_padding_encode_decode, ProtocolType::UDP);
ip_port_with_padding_encode_decode_test!(ip_port_tcp_with_padding_encode_decode, ProtocolType::TCP);
macro_rules! ip_port_without_padding_encode_decode_test (
($test:ident, $protocol:expr) => (
#[test]
fn $test() {
let value = IpPort {
protocol: $protocol,
ip_addr: "5.6.7.8".parse().unwrap(),
port: 12345
};
let mut buf = [0; SIZE_IPPORT - IPV4_PADDING_SIZE];
let (_, size) = value.to_bytes((&mut buf, 0), IpPortPadding::NoPadding).unwrap();
assert_eq!(size, SIZE_IPPORT - IPV4_PADDING_SIZE);
let (rest, decoded_value) = IpPort::from_bytes(&buf[..size], IpPortPadding::NoPadding).unwrap();
assert!(rest.is_empty());
assert_eq!(decoded_value, value);
}
)
);
ip_port_without_padding_encode_decode_test!(ip_port_udp_without_padding_encode_decode, ProtocolType::UDP);
ip_port_without_padding_encode_decode_test!(ip_port_tcp_without_padding_encode_decode, ProtocolType::TCP);
#[test]
fn ip_port_from_to_udp_saddr() {
let ip_port_1 = IpPort {
protocol: ProtocolType::UDP,
ip_addr: "5.6.7.8".parse().unwrap(),
port: 12345
};
let ip_port_2 = IpPort::from_udp_saddr(ip_port_1.to_saddr());
assert_eq!(ip_port_2, ip_port_1);
}
#[test]
fn ip_port_from_to_tcp_saddr() {
let ip_port_1 = IpPort {
protocol: ProtocolType::TCP,
ip_addr: "5.6.7.8".parse().unwrap(),
port: 12345
};
let ip_port_2 = IpPort::from_tcp_saddr(ip_port_1.to_saddr());
assert_eq!(ip_port_2, ip_port_1);
}
}