use std::io::{Read, Write};
use std::net::{SocketAddr, IpAddr};
use handy_async::sync_io::{ReadExt, WriteExt};
use trackable::error::ErrorKindExt;
use {Result, ErrorKind};
use constants;
#[derive(Debug, Default, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct U12(u16);
impl U12 {
pub fn from_u8(value: u8) -> Self {
U12(value as u16)
}
pub fn from_u16(value: u16) -> Option<Self> {
if value < 0x1000 {
Some(U12(value))
} else {
None
}
}
pub fn as_u16(&self) -> u16 {
self.0
}
}
pub type TransactionId = [u8; 12];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SocketAddrValue(SocketAddr);
impl SocketAddrValue {
pub fn new(addr: SocketAddr) -> Self {
SocketAddrValue(addr)
}
pub fn address(&self) -> SocketAddr {
self.0
}
pub fn xor(&self, transaction_id: &TransactionId) -> Self {
let addr = self.0;
let xor_port = addr.port() ^ (constants::MAGIC_COOKIE >> 16) as u16;
let xor_addr = match addr.ip() {
IpAddr::V4(ip) => {
let mut octets = ip.octets();
for i in 0..octets.len() {
octets[i] ^= (constants::MAGIC_COOKIE >> (24 - i * 8)) as u8;
}
let xor_ip = From::from(octets);
SocketAddr::new(IpAddr::V4(xor_ip), xor_port)
}
IpAddr::V6(ip) => {
let mut octets = ip.octets();
for i in 0..4 {
octets[i] ^= (constants::MAGIC_COOKIE >> (24 - i * 8)) as u8;
}
for i in 4..16 {
octets[i] ^= transaction_id[i - 4];
}
let xor_ip = From::from(octets);
SocketAddr::new(IpAddr::V6(xor_ip), xor_port)
}
};
Self::new(xor_addr)
}
pub fn read_from<R: Read>(reader: &mut R) -> Result<Self> {
let _ = track_try!(reader.read_u8());
let family = track_try!(reader.read_u8());
let port = track_try!(reader.read_u16be());
let ip = match family {
1 => {
let ip = track_try!(reader.read_u32be());
IpAddr::V4(From::from(ip))
}
2 => {
let mut octets = [0; 16];
track_try!(reader.read_exact(&mut octets[..]));
IpAddr::V6(From::from(octets))
}
_ => {
let message = format!("Unsupported address family: {}", family);
return Err(ErrorKind::Unsupported.cause(message));
}
};
Ok(Self::new(SocketAddr::new(ip, port)))
}
pub fn write_to<W: Write>(&self, writer: &mut W) -> Result<()> {
let addr = self.0;
track_try!(writer.write_u8(0));
match addr.ip() {
IpAddr::V4(ip) => {
track_try!(writer.write_u8(1));
track_try!(writer.write_u16be(addr.port()));
track_try!(writer.write_all(&ip.octets()));
}
IpAddr::V6(ip) => {
track_try!(writer.write_u8(2));
track_try!(writer.write_u16be(addr.port()));
track_try!(writer.write_all(&ip.octets()));
}
}
Ok(())
}
}
pub trait TryAsRef<T> {
fn try_as_ref(&self) -> Option<&T>;
}