Build fixes, InetAddress in Rust implementing union of C sockaddrs.

This commit is contained in:
Adam Ierymenko 2021-07-23 00:20:58 -04:00
parent a88d89a854
commit aa4336db98
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
8 changed files with 484 additions and 5 deletions

View file

@ -422,6 +422,8 @@ dependencies = [
"base64", "base64",
"ed25519-dalek", "ed25519-dalek",
"gcrypt", "gcrypt",
"libc",
"rand_core", "rand_core",
"winapi",
"x25519-dalek", "x25519-dalek",
] ]

View file

@ -21,3 +21,9 @@ x25519-dalek = "^1"
ed25519-dalek = "^1" ed25519-dalek = "^1"
gcrypt = "^0" gcrypt = "^0"
base64 = "^0" base64 = "^0"
[target."cfg(not(windows))".dependencies]
libc = "^0"
[target."cfg(windows)".dependencies]
winapi = { version = "0.3.9", features = ["handleapi", "ws2ipdef", "ws2tcpip"] }

View file

@ -2,7 +2,6 @@ use std::convert::TryInto;
use std::io::Write; use std::io::Write;
use ed25519_dalek::Digest; use ed25519_dalek::Digest;
use std::error::Error;
pub const C25519_PUBLIC_KEY_SIZE: usize = 32; pub const C25519_PUBLIC_KEY_SIZE: usize = 32;
pub const C25519_SECRET_KEY_SIZE: usize = 32; pub const C25519_SECRET_KEY_SIZE: usize = 32;

View file

@ -1,5 +1,14 @@
pub mod hex; pub mod hex;
pub(crate) unsafe fn equal_bytes(a: *const u8, b: *const u8, l: usize) -> bool {
for i in 0..l {
if *a.offset(i as isize) != *b.offset(i as isize) {
return false;
}
}
true
}
#[inline(always)] #[inline(always)]
pub(crate) fn integer_store_be_u16(i: u16, d: &mut [u8]) { pub(crate) fn integer_store_be_u16(i: u16, d: &mut [u8]) {
d[0] = (i >> 8) as u8; d[0] = (i >> 8) as u8;

View file

@ -0,0 +1,458 @@
use std::mem::{zeroed, size_of};
use std::ptr::{write_bytes, copy_nonoverlapping, null};
use std::str::FromStr;
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use std::net::{IpAddr, Ipv6Addr};
#[cfg(windows)]
use winapi::um::winsock2 as winsock2;
use crate::error::InvalidFormatError;
use crate::util::equal_bytes;
#[allow(non_camel_case_types)]
#[cfg(not(windows))]
type sockaddr = libc::sockaddr;
#[allow(non_camel_case_types)]
#[cfg(not(windows))]
type sockaddr_in = libc::sockaddr_in;
#[allow(non_camel_case_types)]
#[cfg(not(windows))]
type sockaddr_in6 = libc::sockaddr_in6;
#[allow(non_camel_case_types)]
#[cfg(not(windows))]
type in6_addr = libc::in6_addr;
#[cfg(not(windows))]
pub const AF_INET: u8 = libc::AF_INET as u8;
#[cfg(not(windows))]
pub const AF_INET6: u8 = libc::AF_INET6 as u8;
const NO_IP: [u8; 0] = [];
#[repr(u8)]
pub enum IpScope {
None = 0,
Multicast = 1,
Loopback = 2,
PseudoPrivate = 3,
Global = 4,
LinkLocal = 5,
Shared = 6,
Private = 7
}
#[repr(C)]
pub union InetAddress {
sa: sockaddr,
sin: sockaddr_in,
sin6: sockaddr_in6
}
impl Default for InetAddress {
#[inline(always)]
fn default() -> InetAddress {
unsafe { zeroed() }
}
}
impl InetAddress {
/// Get a new zero/nil InetAddress.
#[inline(always)]
pub fn new() -> InetAddress {
unsafe { zeroed() }
}
/// Zero the contents of this InetAddress.
#[inline(always)]
pub fn zero(&mut self) {
unsafe { write_bytes((self as *mut InetAddress).cast::<u8>(), 0, size_of::<Self>()) };
}
/// Returns true if this InetAddress is the nil value (zero).
#[inline(always)]
pub fn is_nil(&self) -> bool {
unsafe { self.sa.sa_family == 0 }
}
/// Check if this is an IPv4 address.
#[inline(always)]
pub fn is_ipv4(&self) -> bool {
unsafe { self.sa.sa_family as u8 == AF_INET }
}
/// Check if this is an IPv6 address.
#[inline(always)]
pub fn is_ipv6(&self) -> bool {
unsafe { self.sa.sa_family as u8 == AF_INET6 }
}
/// Get a pointer to the C "sockaddr" structure and the size of the returned structure in bytes.
/// This is useful for interacting with C-level socket APIs. If this is a nil InetAddress this
/// returns a null pointer and 0 for the size.
#[inline(always)]
pub fn c_sockaddr(&self) -> (*const (), usize) {
unsafe {
match self.sa.sa_family as u8 {
AF_INET => ((&self.sin as *const sockaddr_in).cast(), size_of::<sockaddr_in>()),
AF_INET6 => ((&self.sin6 as *const sockaddr_in6).cast(), size_of::<sockaddr_in6>()),
_ => (null(), 0)
}
}
}
/// Set the IP and port of this InetAddress.
/// Whether this is IPv4 or IPv6 is inferred from the size of ip[], which must be
/// either 4 or 16 bytes. The family (AF_INET or AF_INET6) is returned, or zero on
/// success.
pub fn set(&mut self, ip: &[u8], port: u16) -> u8 {
self.zero();
let port = port.to_be();
unsafe {
if ip.len() == 4 {
self.sin.sin_family = AF_INET.into();
self.sin.sin_port = port.into();
copy_nonoverlapping(ip.as_ptr(), (&mut self.sin.sin_addr.s_addr as *mut u32).cast::<u8>(), 4);
AF_INET
} else if ip.len() == 16 {
self.sin6.sin6_family = AF_INET6.into();
self.sin6.sin6_port = port.into();
copy_nonoverlapping(ip.as_ptr(), (&mut self.sin6.sin6_addr as *mut in6_addr).cast::<u8>(), 16);
AF_INET6
} else {
0
}
}
}
/// Get raw IP bytes, with length dependent on address family.
#[inline(always)]
pub fn ip(&self) -> &[u8] {
unsafe {
match self.sa.sa_family as u8 {
AF_INET => &*(&self.sin.sin_addr.s_addr as *const u32).cast::<[u8; 4]>(),
AF_INET6 => &*(&(self.sin6.sin6_addr) as *const in6_addr).cast::<[u8; 16]>(),
_ => &NO_IP
}
}
}
/// Get the IP port for this InetAddress.
#[inline(always)]
pub fn port(&self) -> u16 {
unsafe {
match self.sa.sa_family as u8 {
AF_INET => u16::from_be(self.sin.sin_port as u16),
AF_INET6 => u16::from_be(self.sin6.sin6_port as u16),
_ => 0
}
}
}
/// Set the IP port.
#[inline(always)]
pub fn set_port(&mut self, port: u16) {
let port = port.to_be();
unsafe {
match self.sa.sa_family as u8 {
AF_INET => self.sin.sin_port = port,
AF_INET6 => self.sin6.sin6_port = port,
_ => {}
}
}
}
/// Get this IP address's scope as per RFC documents and what is advertised via BGP.
pub fn scope(&self) -> IpScope {
unsafe {
match self.sa.sa_family as u8 {
AF_INET => {
let ip = self.sin.sin_addr.s_addr as u32;
let class_a = (ip >> 24) as u8;
match class_a {
0x00 | 0xff => IpScope::None, // 0.0.0.0/8 and 255.0.0.0/8 are not usable
0x0a => IpScope::Private, // 10.0.0.0/8
0x7f => IpScope::Loopback, // 127.0.0.0/8
0x64 => {
if (ip & 0xffc00000) == 0x64400000 { // 100.64.0.0/10
IpScope::Private
} else {
IpScope::Global
}
},
0xa9 => {
if (ip & 0xffff0000) == 0xa9fe0000 { // 169.254.0.0/16
IpScope::LinkLocal
} else {
IpScope::Global
}
},
0xac => {
if (ip & 0xfff00000) == 0xac100000 { // 172.16.0.0/12
IpScope::Private
} else {
IpScope::Global
}
},
0xc0 => {
if (ip & 0xffff0000) == 0xc0a80000 || (ip & 0xffffff00) == 0xc0000200 { // 192.168.0.0/16 and 192.0.2.0/24
IpScope::Private
} else {
IpScope::Global
}
},
0xc6 => {
if (ip & 0xfffe0000) == 0xc6120000 || (ip & 0xffffff00) == 0xc6336400 { // 198.18.0.0/15 and 198.51.100.0/24
IpScope::Private
} else {
IpScope::Global
}
},
0xcb => {
if (ip & 0xffffff00) == 0xcb007100 { // 203.0.113.0/24
IpScope::Private
} else {
IpScope::Global
}
},
_ => {
if [
0x06_u8, // 6.0.0.0/8 (US Army)
0x15_u8, // 21.0.0.0/8 (US DDN-RVN)
0x16_u8, // 22.0.0.0/8 (US DISA)
0x19_u8, // 25.0.0.0/8 (UK Ministry of Defense)
0x1a_u8, // 26.0.0.0/8 (US DISA)
0x1c_u8, // 28.0.0.0/8 (US DSI-North)
0x1d_u8, // 29.0.0.0/8 (US DISA)
0x1e_u8, // 30.0.0.0/8 (US DISA)
0x33_u8, // 51.0.0.0/8 (UK Department of Social Security)
0x37_u8, // 55.0.0.0/8 (US DoD)
0x38_u8, // 56.0.0.0/8 (US Postal Service)
].contains(&class_a) {
IpScope::PseudoPrivate
} else {
match ip >> 28 {
0xe => IpScope::Multicast, // 224.0.0.0/4
0xf => IpScope::Private, // 240.0.0.0/4 ("reserved," usually unusable)
_ => IpScope::Global
}
}
}
}
},
AF_INET6 => {
let ip = &*(&(self.sin6.sin6_addr) as *const in6_addr).cast::<[u8; 16]>();
if (ip[0] & 0xf0) == 0xf0 {
if ip[0] == 0xff {
return IpScope::Multicast; // ff00::/8
}
if ip[0] == 0xfe && (ip[1] & 0xc0) == 0x80 {
let mut k: usize = 2;
while ip[k] == 0 && k < 15 {
k += 1;
}
if k == 15 && ip[15] == 0x01 {
return IpScope::Loopback; // fe80::1/128
} else {
return IpScope::LinkLocal; // fe80::/10
}
}
if (ip[0] & 0xfe) == 0xfc {
return IpScope::Private; // fc00::/7
}
}
let mut k: usize = 0;
while ip[k] == 0 && k < 15 {
k += 1;
}
if k == 15 {
if ip[15] == 0x01 {
return IpScope::Loopback; // ::1/128
} else if ip[15] == 0x00 {
return IpScope::None; // ::/128
}
}
IpScope::Global
},
_ => IpScope::None
}
}
}
/// Get only the IP portion of this address as a string.
pub fn to_ip_string(&self) -> String {
unsafe {
match self.sa.sa_family as u8 {
AF_INET => {
let ip = self.sin.sin_addr.s_addr as u32;
format!("{}.{}.{}.{}", (ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff)
},
AF_INET6 => {
Ipv6Addr::from(*(&(self.sin6.sin6_addr) as *const in6_addr).cast::<[u8; 16]>()).to_string()
},
_ => {
String::from("(null)")
}
}
}
}
}
impl ToString for InetAddress {
fn to_string(&self) -> String {
let mut s = self.to_ip_string();
unsafe {
match self.sa.sa_family as u8 {
AF_INET => {
s.push('/');
s.push_str(u16::from_be(self.sin.sin_port as u16).to_string().as_str())
},
AF_INET6 => {
s.push('/');
s.push_str(u16::from_be(self.sin6.sin6_port as u16).to_string().as_str())
},
_ => {}
}
}
s
}
}
impl FromStr for InetAddress {
type Err = InvalidFormatError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut addr = InetAddress::new();
let (ip_str, port) = s.find('/').map_or_else(|| {
(s, 0)
}, |pos| {
let ss = s.split_at(pos);
(ss.0, u16::from_str_radix(ss.1, 10).unwrap_or(0).to_be())
});
IpAddr::from_str(ip_str).map_or_else(|_| Err(InvalidFormatError), |ip| {
unsafe {
match ip {
IpAddr::V4(v4) => {
addr.sin.sin_family = AF_INET.into();
addr.sin.sin_port = port.into();
copy_nonoverlapping(v4.octets().as_ptr(), (&mut (addr.sin.sin_addr.s_addr) as *mut u32).cast(), 4);
},
IpAddr::V6(v6) => {
addr.sin6.sin6_family = AF_INET6.into();
addr.sin6.sin6_port = port.into();
copy_nonoverlapping(v6.octets().as_ptr(), (&mut (addr.sin6.sin6_addr) as *mut in6_addr).cast(), 16);
}
}
}
Ok(addr)
})
}
}
impl PartialEq for InetAddress {
fn eq(&self, other: &Self) -> bool {
unsafe {
if self.sa.sa_family == other.sa.sa_family {
match self.sa.sa_family as u8 {
AF_INET => { self.sin.sin_port == other.sin.sin_port && self.sin.sin_addr.s_addr == other.sin.sin_addr.s_addr },
AF_INET6 => {
if self.sin6.sin6_port == other.sin6.sin6_port {
equal_bytes((&(self.sin6.sin6_addr) as *const in6_addr).cast(), (&(other.sin6.sin6_addr) as *const in6_addr).cast(), 16)
} else {
false
}
},
_ => true
}
} else {
false
}
}
}
}
impl Eq for InetAddress {}
impl PartialOrd for InetAddress {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for InetAddress {
fn cmp(&self, other: &Self) -> Ordering {
unsafe {
if self.sa.sa_family == other.sa.sa_family {
match self.sa.sa_family as u8 {
AF_INET => {
if self.sin.sin_port == other.sin.sin_port {
u32::from_be(self.sin.sin_addr.s_addr as u32).cmp(&u32::from_be(other.sin.sin_addr.s_addr as u32))
} else {
u16::from_be(self.sin.sin_port as u16).cmp(&u16::from_be(other.sin.sin_port as u16))
}
},
AF_INET6 => {
if self.sin6.sin6_port == other.sin6.sin6_port {
let a = &*(&(self.sin6.sin6_addr) as *const in6_addr).cast::<[u8; 16]>();
let b = &*(&(other.sin6.sin6_addr) as *const in6_addr).cast::<[u8; 16]>();
a.cmp(b)
} else {
u16::from_be(self.sin6.sin6_port as u16).cmp(&u16::from_be(other.sin6.sin6_port as u16))
}
},
_ => {
Ordering::Equal
}
}
} else if self.sa.sa_family < other.sa.sa_family {
Ordering::Less
} else {
Ordering::Greater
}
}
}
}
impl Hash for InetAddress {
fn hash<H: Hasher>(&self, state: &mut H) {
unsafe {
state.write_u8(self.sa.sa_family as u8);
match self.sa.sa_family as u8 {
AF_INET => {
state.write_u16(self.sin.sin_port as u16);
state.write_u32(self.sin.sin_addr.s_addr as u32);
},
AF_INET6 => {
state.write_u16(self.sin6.sin6_port as u16);
state.write(&*(&(self.sin6.sin6_addr) as *const in6_addr).cast::<[u8; 16]>());
},
_ => {}
}
}
}
}
#[cfg(test)]
mod tests {
use crate::vl1::inetaddress::InetAddress;
#[test]
fn struct_layout() {
let mut tmp = InetAddress::new();
unsafe {
tmp.sa.sa_family = 0xf7;
if tmp.sin.sin_family != 0xf7 {
panic!("sin_family misaligned in union");
}
if tmp.sin6.sin6_family != 0xf7 {
panic!("sin6_family misaligned in union");
}
}
}
}

View file

@ -3,6 +3,7 @@ pub mod packet;
pub mod buffer; pub mod buffer;
pub mod node; pub mod node;
pub mod identity; pub mod identity;
pub mod inetaddress;
mod address; mod address;
pub use address::Address; pub use address::Address;

View file

@ -82,7 +82,7 @@ impl FragmentHeader {
} }
} }
type Fragment = Buffer<FragmentHeader, { crate::vl1::protocol::PACKET_SIZE_MAX }>; pub type Fragment = Buffer<FragmentHeader, { crate::vl1::protocol::PACKET_SIZE_MAX }>;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -27,10 +27,14 @@ use crate::osdep as osdep;
* for each thread using options like SO_REUSEPORT and concurrent packet listening. * for each thread using options like SO_REUSEPORT and concurrent packet listening.
*/ */
#[cfg(windows)] use winapi::um::winsock2 as winsock2; #[cfg(windows)]
use winapi::um::winsock2 as winsock2;
#[cfg(windows)] pub(crate) type FastUDPRawOsSocket = winsock2::SOCKET; #[cfg(windows)]
#[cfg(unix)] pub(crate) type FastUDPRawOsSocket = c_int; pub(crate) type FastUDPRawOsSocket = winsock2::SOCKET;
#[cfg(unix)]
pub(crate) type FastUDPRawOsSocket = c_int;
#[cfg(unix)] #[cfg(unix)]
fn bind_udp_socket(_device_name: &str, address: &InetAddress) -> Result<FastUDPRawOsSocket, &'static str> { fn bind_udp_socket(_device_name: &str, address: &InetAddress) -> Result<FastUDPRawOsSocket, &'static str> {