use std::{
mem,
net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
num::NonZero,
ptr,
};
use addr::{Error, Internet, Storage};
use dope_sys::io::socket::{
addr,
route::selector::{Frame, PrefixError, PrefixPlan},
};
use o3::buffer::bytes::scope;
#[test]
fn prefix_admission_retains_packet_borrows() {
let plan = PrefixPlan::fixed::<31, 0xc0, 0x14, 0x16>();
for first in [0x40, 0x80, 0xc0] {
let packet = [first];
scope::Owner::with(packet.as_slice(), |owner| {
assert!(matches!(plan.try_parse(owner.view()), Ok(Frame::Direct)));
});
for tag in plan.tags() {
let mut packet = [0xa5; 32];
packet[0] = tag;
packet[31] = first;
let pointer = packet.as_ptr();
let body = scope::Owner::with(packet.as_mut_slice(), |owner| {
let Frame::Prefixed { prefix, remaining } =
plan.try_parse(owner.view()).expect("valid prefix rejected")
else {
panic!("prefix classified as direct");
};
assert_eq!(prefix.as_ptr(), pointer);
assert_eq!(prefix.len(), 31);
owner.project(remaining)
});
assert_eq!(body.as_ptr(), pointer.wrapping_add(31));
assert_eq!(body, &[first]);
}
}
assert_eq!(
scope::Owner::with(&[][..], |owner| plan.try_parse(owner.view()).err()),
Some(PrefixError::Truncated {
actual: 0,
required: NonZero::<usize>::MIN,
})
);
for actual in [1, 30, 31] {
let packet = [0x14; 31];
assert_eq!(
scope::Owner::with(&packet[..actual], |owner| plan
.try_parse(owner.view())
.err()),
Some(PrefixError::Truncated {
actual,
required: NonZero::new(32).expect("nonzero extent"),
})
);
}
assert_eq!(
scope::Owner::with(&[0x15][..], |owner| plan.try_parse(owner.view()).err()),
Some(PrefixError::Unknown(0x15))
);
let mut packet = [0x14; 32];
for tag in plan.tags() {
packet[31] = tag;
assert_eq!(
scope::Owner::with(packet.as_slice(), |owner| plan
.try_parse(owner.view())
.err()),
Some(PrefixError::Nested(tag))
);
}
packet[31] = 0x15;
assert_eq!(
scope::Owner::with(packet.as_slice(), |owner| plan
.try_parse(owner.view())
.err()),
Some(PrefixError::Inner(0x15))
);
}
#[test]
fn inet_raw() {
for expected in [
SocketAddr::from(SocketAddrV4::new(Ipv4Addr::new(192, 0, 2, 7), 5353)),
SocketAddr::from(SocketAddrV6::new(
Ipv6Addr::new(0x2001, 0xdb8, 1, 2, 3, 4, 5, 6),
9443,
17,
4,
)),
] {
let (size, align, port, ip, family) = match expected {
SocketAddr::V4(_) => (
mem::size_of::<libc::sockaddr_in>(),
mem::align_of::<libc::sockaddr_in>(),
mem::offset_of!(libc::sockaddr_in, sin_port),
mem::offset_of!(libc::sockaddr_in, sin_addr),
libc::AF_INET,
),
SocketAddr::V6(_) => (
mem::size_of::<libc::sockaddr_in6>(),
mem::align_of::<libc::sockaddr_in6>(),
mem::offset_of!(libc::sockaddr_in6, sin6_port),
mem::offset_of!(libc::sockaddr_in6, sin6_addr),
libc::AF_INET6,
),
};
let mut buffer = [0xa5; addr::CAPACITY + 1];
let offset = usize::from((buffer.as_ptr() as usize).is_multiple_of(align));
let bytes = &mut buffer[offset..offset + addr::CAPACITY];
let start = mem::offset_of!(libc::sockaddr_storage, ss_family);
bytes[start..addr::FAMILY_BYTES]
.copy_from_slice(&(family as libc::sa_family_t).to_ne_bytes());
bytes[port..port + 2].copy_from_slice(&expected.port().to_be_bytes());
match expected {
SocketAddr::V4(value) => {
bytes[ip..ip + 4].copy_from_slice(&value.ip().octets());
}
SocketAddr::V6(value) => {
bytes[ip..ip + 16].copy_from_slice(&value.ip().octets());
let flow = mem::offset_of!(libc::sockaddr_in6, sin6_flowinfo);
bytes[flow..flow + 4].copy_from_slice(&value.flowinfo().to_ne_bytes());
let scope = mem::offset_of!(libc::sockaddr_in6, sin6_scope_id);
bytes[scope..scope + 4].copy_from_slice(&value.scope_id().to_ne_bytes());
}
}
assert_ne!(bytes.as_ptr() as usize % align, 0);
for len in [size, addr::CAPACITY] {
let bytes = &bytes[..len];
assert_eq!(Internet::try_parse(bytes), Ok(expected));
let mut storage = Storage::empty();
let (raw, len) = storage.output_mut();
unsafe {
ptr::copy_nonoverlapping(bytes.as_ptr(), ptr::from_mut(raw).cast(), bytes.len())
};
*len = bytes.len() as libc::socklen_t;
assert_eq!(storage.to_std(), Ok(expected));
}
}
}
#[test]
fn addr_err() {
let header = addr::FAMILY_BYTES;
let v4 = mem::size_of::<libc::sockaddr_in>();
let v6 = mem::size_of::<libc::sockaddr_in6>();
for (actual, family, error) in [
(
addr::CAPACITY + 1,
libc::AF_UNSPEC,
Error::Storage {
actual: addr::CAPACITY + 1,
capacity: addr::CAPACITY,
},
),
(
0,
libc::AF_INET,
Error::Header {
actual: 0,
required: header,
},
),
(
header - 1,
libc::AF_INET,
Error::Header {
actual: header - 1,
required: header,
},
),
(
header,
libc::AF_UNSPEC,
Error::Unsupported {
family: libc::AF_UNSPEC,
actual: header,
},
),
(
v4 - 1,
libc::AF_INET,
Error::Address {
family: libc::AF_INET,
actual: v4 - 1,
required: v4,
},
),
(
v6 - 1,
libc::AF_INET6,
Error::Address {
family: libc::AF_INET6,
actual: v6 - 1,
required: v6,
},
),
] {
let family = family as libc::sa_family_t;
let mut bytes = [0; addr::CAPACITY + 1];
let start = mem::offset_of!(libc::sockaddr_storage, ss_family);
bytes[start..header].copy_from_slice(&family.to_ne_bytes());
assert_eq!(Internet::try_parse(&bytes[..actual]), Err(error));
let mut storage = Storage::empty();
let (raw, len) = storage.output_mut();
raw.ss_family = family;
*len = actual as libc::socklen_t;
assert_eq!(storage.to_std(), Err(error));
}
}