#![forbid(unsafe_code)]
use std::{
fmt::{LowerHex, UpperHex},
net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6},
};
use ipnet::{IpNet, Ipv4Net};
use nix::{errno::Errno, sys::socket::SockaddrStorage};
const LOOPBACK_MAPPED: Ipv6Addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 1);
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(u8)]
pub(crate) enum SocketCall {
Socket = 0x1,
Bind = 0x2,
Connect = 0x3,
Accept = 0x5,
GetSockName = 0x6,
GetPeerName = 0x7,
SocketPair = 0x8,
Send = 0x9,
Recv = 0xa,
SendTo = 0xb,
RecvFrom = 0xc,
GetSockOpt = 0xf,
SendMsg = 0x10,
RecvMsg = 0x11,
Accept4 = 0x12,
RecvMmsg = 0x13,
SendMmsg = 0x14,
RecvMmsg64 = 0xFF, }
impl SocketCall {
pub(crate) const fn name(self) -> &'static str {
match self {
Self::Socket => "socket",
Self::Bind => "bind",
Self::Connect => "connect",
Self::Accept => "accept",
Self::GetSockName => "getsockname",
Self::GetPeerName => "getpeername",
Self::SocketPair => "socketpair",
Self::Send => "send",
Self::Recv => "recv",
Self::SendTo => "sendto",
Self::RecvFrom => "recvfrom",
Self::GetSockOpt => "getsockopt",
Self::SendMsg => "sendmsg",
Self::RecvMsg => "recvmsg",
Self::Accept4 => "accept4",
Self::RecvMmsg => "recvmmsg",
Self::RecvMmsg64 => "recvmmsg_time64",
Self::SendMmsg => "sendmmsg",
}
}
}
impl From<SocketCall> for Errno {
fn from(call: SocketCall) -> Self {
match call {
SocketCall::Bind => Self::EADDRNOTAVAIL,
SocketCall::Connect => Self::ECONNREFUSED,
SocketCall::SendTo | SocketCall::SendMsg | SocketCall::SendMmsg => Self::ENOTCONN,
SocketCall::Accept | SocketCall::Accept4 => Self::ECONNABORTED,
SocketCall::SocketPair => Self::EOPNOTSUPP,
_ => Self::EACCES,
}
}
}
impl TryFrom<u8> for SocketCall {
type Error = Errno;
fn try_from(v: u8) -> Result<Self, Errno> {
match v {
0x1 => Ok(Self::Socket),
0x2 => Ok(Self::Bind),
0x3 => Ok(Self::Connect),
0x5 => Ok(Self::Accept),
0x6 => Ok(Self::GetSockName),
0x7 => Ok(Self::GetPeerName),
0x8 => Ok(Self::SocketPair),
0x9 => Ok(Self::Send),
0xa => Ok(Self::Recv),
0xb => Ok(Self::SendTo),
0xc => Ok(Self::RecvFrom),
0xf => Ok(Self::GetSockOpt),
0x10 => Ok(Self::SendMsg),
0x11 => Ok(Self::RecvMsg),
0x12 => Ok(Self::Accept4),
0x13 => Ok(Self::RecvMmsg),
0x14 => Ok(Self::SendMmsg),
0xFF => Ok(Self::RecvMmsg64),
_ => Err(Errno::EINVAL),
}
}
}
impl LowerHex for SocketCall {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
LowerHex::fmt(&(*self as u8), f)
}
}
impl UpperHex for SocketCall {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
UpperHex::fmt(&(*self as u8), f)
}
}
pub(crate) const fn is_outbound_call(subcall: SocketCall) -> bool {
matches!(
subcall,
SocketCall::Connect
| SocketCall::Send
| SocketCall::SendTo
| SocketCall::SendMsg
| SocketCall::SendMmsg
)
}
pub const fn loopback6(addr: Ipv6Addr) -> Ipv6Addr {
if addr.to_ipv4_mapped().is_some() {
LOOPBACK_MAPPED
} else {
Ipv6Addr::LOCALHOST
}
}
pub fn normalize_ipnet(net: IpNet) -> IpNet {
if let IpNet::V6(v6) = net {
if let Some(v4) = v6.addr().to_ipv4_mapped() {
let prefix = v6.prefix_len().saturating_sub(96);
return IpNet::from(Ipv4Net::new_assert(v4, prefix));
}
}
net
}
pub fn is_anyaddr(addr: &SockaddrStorage) -> bool {
addr.as_sockaddr_in()
.map(|sin| sin.ip() == Ipv4Addr::UNSPECIFIED)
.unwrap_or(false)
}
pub fn is_any6addr(addr: &SockaddrStorage) -> bool {
addr.as_sockaddr_in6()
.map(|sin6| sin6.ip().to_canonical().is_unspecified())
.unwrap_or(false)
}
pub fn has_privileged_port_v4(addr: &SockaddrStorage) -> bool {
addr.as_sockaddr_in()
.map(|sin| {
let port = sin.port();
port != 0 && port < 1024
})
.unwrap_or(false)
}
pub fn has_privileged_port_v6(addr: &SockaddrStorage) -> bool {
addr.as_sockaddr_in6()
.map(|sin6| {
let port = sin6.port();
port != 0 && port < 1024
})
.unwrap_or(false)
}
pub(crate) fn make_loaddr(
call: SocketCall,
addr: &mut SockaddrStorage,
force: bool,
) -> Result<(), Errno> {
let is_any = is_anyaddr(addr);
if is_any && is_outbound_call(call) {
return Err(Errno::ENETUNREACH);
}
if !force && !is_any {
return Ok(());
}
if let Some(sin) = addr.as_sockaddr_in() {
let port = sin.port();
*addr = SockaddrStorage::from(SocketAddrV4::new(Ipv4Addr::LOCALHOST, port));
}
Ok(())
}
pub(crate) fn make_lo6addr(
call: SocketCall,
addr: &mut SockaddrStorage,
force: bool,
) -> Result<(), Errno> {
let is_any = is_any6addr(addr);
if is_any && is_outbound_call(call) {
return Err(Errno::ENETUNREACH);
}
if !force && !is_any {
return Ok(());
}
if let Some(sin6) = addr.as_sockaddr_in6() {
let port = sin6.port();
let flowinfo = sin6.flowinfo();
let scope_id = sin6.scope_id();
let loopback = loopback6(sin6.ip());
*addr = SockaddrStorage::from(SocketAddrV6::new(loopback, port, flowinfo, scope_id));
}
Ok(())
}
pub(crate) fn clear_scope6(addr: &mut SockaddrStorage) -> Option<(u32, Ipv6Addr, u16)> {
let sin6 = addr.as_sockaddr_in6()?;
let scope_id = sin6.scope_id();
if scope_id == 0 {
return None;
}
let port = sin6.port();
let flowinfo = sin6.flowinfo();
let ip = sin6.ip();
*addr = SockaddrStorage::from(SocketAddrV6::new(ip, port, flowinfo, 0));
Some((scope_id, ip, port))
}
#[cfg(test)]
mod tests {
use super::*;
fn v4(ip: Ipv4Addr, port: u16) -> SockaddrStorage {
SockaddrStorage::from(SocketAddrV4::new(ip, port))
}
fn v6(ip: Ipv6Addr, port: u16) -> SockaddrStorage {
SockaddrStorage::from(SocketAddrV6::new(ip, port, 0, 0))
}
#[test]
fn test_loopback6_1() {
assert_eq!(loopback6(Ipv6Addr::UNSPECIFIED), Ipv6Addr::LOCALHOST);
}
#[test]
fn test_loopback6_2() {
let addr = "2001:db8::1".parse::<Ipv6Addr>().unwrap();
assert_eq!(loopback6(addr), Ipv6Addr::LOCALHOST);
}
#[test]
fn test_loopback6_3() {
assert_eq!(loopback6(Ipv6Addr::LOCALHOST), Ipv6Addr::LOCALHOST);
}
#[test]
fn test_loopback6_4() {
let mapped = "::ffff:0.0.0.0".parse::<Ipv6Addr>().unwrap();
assert_eq!(loopback6(mapped), LOOPBACK_MAPPED);
}
#[test]
fn test_loopback6_5() {
let mapped = "::ffff:10.0.0.1".parse::<Ipv6Addr>().unwrap();
assert_eq!(loopback6(mapped), LOOPBACK_MAPPED);
}
#[test]
fn test_loopback6_6() {
assert_eq!(loopback6(LOOPBACK_MAPPED), LOOPBACK_MAPPED);
}
#[test]
fn test_is_anyaddr_1() {
assert!(is_anyaddr(&v4(Ipv4Addr::UNSPECIFIED, 0)));
assert!(is_anyaddr(&v4(Ipv4Addr::UNSPECIFIED, 8080)));
}
#[test]
fn test_is_anyaddr_2() {
assert!(!is_anyaddr(&v4(Ipv4Addr::LOCALHOST, 0)));
}
#[test]
fn test_is_anyaddr_3() {
assert!(!is_anyaddr(&v4(Ipv4Addr::new(192, 168, 1, 1), 443)));
}
#[test]
fn test_is_anyaddr_4() {
assert!(!is_anyaddr(&v6(Ipv6Addr::UNSPECIFIED, 0)));
}
#[test]
fn test_is_any6addr_1() {
assert!(is_any6addr(&v6(Ipv6Addr::UNSPECIFIED, 0)));
assert!(is_any6addr(&v6(Ipv6Addr::UNSPECIFIED, 8080)));
}
#[test]
fn test_is_any6addr_2() {
let mapped = "::ffff:0.0.0.0".parse::<Ipv6Addr>().unwrap();
assert!(is_any6addr(&v6(mapped, 0)));
assert!(is_any6addr(&v6(mapped, 4244)));
}
#[test]
fn test_is_any6addr_3() {
let mapped_lo = "::ffff:127.0.0.1".parse::<Ipv6Addr>().unwrap();
assert!(!is_any6addr(&v6(mapped_lo, 0)));
}
#[test]
fn test_is_any6addr_4() {
assert!(!is_any6addr(&v6(Ipv6Addr::LOCALHOST, 0)));
}
#[test]
fn test_is_any6addr_5() {
let addr = "2001:db8::1".parse::<Ipv6Addr>().unwrap();
assert!(!is_any6addr(&v6(addr, 443)));
}
#[test]
fn test_is_any6addr_6() {
assert!(!is_any6addr(&v4(Ipv4Addr::UNSPECIFIED, 0)));
}
#[test]
fn test_privileged_port_v4_1() {
assert!(has_privileged_port_v4(&v4(Ipv4Addr::LOCALHOST, 1)));
assert!(has_privileged_port_v4(&v4(Ipv4Addr::LOCALHOST, 80)));
assert!(has_privileged_port_v4(&v4(Ipv4Addr::LOCALHOST, 443)));
assert!(has_privileged_port_v4(&v4(Ipv4Addr::LOCALHOST, 1023)));
}
#[test]
fn test_privileged_port_v4_2() {
assert!(!has_privileged_port_v4(&v4(Ipv4Addr::LOCALHOST, 0)));
assert!(!has_privileged_port_v4(&v4(Ipv4Addr::LOCALHOST, 1024)));
assert!(!has_privileged_port_v4(&v4(Ipv4Addr::LOCALHOST, 8080)));
assert!(!has_privileged_port_v4(&v4(Ipv4Addr::LOCALHOST, 65535)));
}
#[test]
fn test_privileged_port_v4_3() {
assert!(!has_privileged_port_v4(&v6(Ipv6Addr::LOCALHOST, 80)));
}
#[test]
fn test_privileged_port_v6_1() {
assert!(has_privileged_port_v6(&v6(Ipv6Addr::LOCALHOST, 1)));
assert!(has_privileged_port_v6(&v6(Ipv6Addr::LOCALHOST, 80)));
assert!(has_privileged_port_v6(&v6(Ipv6Addr::LOCALHOST, 1023)));
}
#[test]
fn test_privileged_port_v6_2() {
assert!(!has_privileged_port_v6(&v6(Ipv6Addr::LOCALHOST, 0)));
assert!(!has_privileged_port_v6(&v6(Ipv6Addr::LOCALHOST, 1024)));
assert!(!has_privileged_port_v6(&v6(Ipv6Addr::LOCALHOST, 65535)));
}
#[test]
fn test_privileged_port_v6_3() {
assert!(!has_privileged_port_v6(&v4(Ipv4Addr::LOCALHOST, 80)));
}
#[test]
fn test_make_loaddr_1() {
let mut addr = v4(Ipv4Addr::UNSPECIFIED, 8080);
assert!(make_loaddr(SocketCall::Bind, &mut addr, false).is_ok());
assert_eq!(addr.as_sockaddr_in().unwrap().ip(), Ipv4Addr::LOCALHOST);
}
#[test]
fn test_make_loaddr_2() {
let mut addr = v4(Ipv4Addr::UNSPECIFIED, 8080);
assert_eq!(
make_loaddr(SocketCall::Connect, &mut addr, false),
Err(Errno::ENETUNREACH)
);
}
#[test]
fn test_make_loaddr_3() {
let mut addr = v4(Ipv4Addr::new(10, 0, 0, 1), 8080);
assert!(make_loaddr(SocketCall::Connect, &mut addr, false).is_ok());
assert_eq!(
addr.as_sockaddr_in().unwrap().ip(),
Ipv4Addr::new(10, 0, 0, 1)
);
}
#[test]
fn test_make_loaddr_4() {
let mut addr = v4(Ipv4Addr::new(10, 0, 0, 1), 8080);
assert!(make_loaddr(SocketCall::Connect, &mut addr, true).is_ok());
assert_eq!(addr.as_sockaddr_in().unwrap().ip(), Ipv4Addr::LOCALHOST);
}
#[test]
fn test_make_lo6addr_1() {
let mut addr = v6(Ipv6Addr::UNSPECIFIED, 8080);
assert!(make_lo6addr(SocketCall::Bind, &mut addr, false).is_ok());
assert_eq!(addr.as_sockaddr_in6().unwrap().ip(), Ipv6Addr::LOCALHOST);
}
#[test]
fn test_make_lo6addr_2() {
let mut addr = v6(Ipv6Addr::UNSPECIFIED, 8080);
assert_eq!(
make_lo6addr(SocketCall::Connect, &mut addr, false),
Err(Errno::ENETUNREACH)
);
}
#[test]
fn test_make_lo6addr_3() {
let mapped = "::ffff:0.0.0.0".parse::<Ipv6Addr>().unwrap();
let mut addr = v6(mapped, 8080);
assert_eq!(
make_lo6addr(SocketCall::Connect, &mut addr, false),
Err(Errno::ENETUNREACH)
);
}
#[test]
fn test_make_lo6addr_4() {
let mapped = "::ffff:0.0.0.0".parse::<Ipv6Addr>().unwrap();
let mut addr = v6(mapped, 8080);
assert!(make_lo6addr(SocketCall::Bind, &mut addr, false).is_ok());
assert_eq!(addr.as_sockaddr_in6().unwrap().ip(), LOOPBACK_MAPPED);
}
#[test]
fn test_make_lo6addr_5() {
let mapped = "::ffff:0.0.0.0".parse::<Ipv6Addr>().unwrap();
let mut addr = v6(mapped, 8080);
assert_eq!(
make_lo6addr(SocketCall::SendTo, &mut addr, false),
Err(Errno::ENETUNREACH)
);
}
#[test]
fn test_make_lo6addr_6() {
let mapped = "::ffff:0.0.0.0".parse::<Ipv6Addr>().unwrap();
let mut addr = v6(mapped, 8080);
assert_eq!(
make_lo6addr(SocketCall::SendMsg, &mut addr, false),
Err(Errno::ENETUNREACH)
);
}
#[test]
fn test_make_lo6addr_7() {
let mapped_lo = "::ffff:127.0.0.1".parse::<Ipv6Addr>().unwrap();
let mut addr = v6(mapped_lo, 8080);
assert!(make_lo6addr(SocketCall::Connect, &mut addr, false).is_ok());
assert_eq!(addr.as_sockaddr_in6().unwrap().ip(), mapped_lo);
}
#[test]
fn test_make_lo6addr_8() {
let mut addr = v6(Ipv6Addr::LOCALHOST, 8080);
assert!(make_lo6addr(SocketCall::Connect, &mut addr, false).is_ok());
assert_eq!(addr.as_sockaddr_in6().unwrap().ip(), Ipv6Addr::LOCALHOST);
}
#[test]
fn test_make_lo6addr_9() {
let normal = "2001:db8::1".parse::<Ipv6Addr>().unwrap();
let mut addr = v6(normal, 8080);
assert!(make_lo6addr(SocketCall::Connect, &mut addr, true).is_ok());
assert_eq!(addr.as_sockaddr_in6().unwrap().ip(), Ipv6Addr::LOCALHOST);
}
#[test]
fn test_make_lo6addr_10() {
let mapped_normal = "::ffff:10.0.0.1".parse::<Ipv6Addr>().unwrap();
let mut addr = v6(mapped_normal, 8080);
assert!(make_lo6addr(SocketCall::Connect, &mut addr, true).is_ok());
assert_eq!(addr.as_sockaddr_in6().unwrap().ip(), LOOPBACK_MAPPED);
}
#[test]
fn test_make_lo6addr_11() {
let mut addr = v6(Ipv6Addr::UNSPECIFIED, 443);
assert!(make_lo6addr(SocketCall::Bind, &mut addr, false).is_ok());
assert_eq!(addr.as_sockaddr_in6().unwrap().ip(), Ipv6Addr::LOCALHOST);
}
#[test]
fn test_normalize_ipnet_1() {
let net: IpNet = "127.0.0.1/32".parse().unwrap();
assert!(matches!(normalize_ipnet(net), IpNet::V4(_)));
}
#[test]
fn test_normalize_ipnet_2() {
let net: IpNet = "::1/128".parse().unwrap();
assert!(matches!(normalize_ipnet(net), IpNet::V6(_)));
}
#[test]
fn test_normalize_ipnet_3() {
let net: IpNet = "::ffff:127.0.0.1/128".parse().unwrap();
let norm = normalize_ipnet(net);
assert!(matches!(norm, IpNet::V4(_)), "not normalized: {norm}");
assert_eq!(norm.to_string(), "127.0.0.1/32");
}
#[test]
fn test_normalize_ipnet_4() {
let net: IpNet = "::ffff:0.0.0.0/128".parse().unwrap();
let norm = normalize_ipnet(net);
assert!(matches!(norm, IpNet::V4(_)), "not normalized: {norm}");
assert_eq!(norm.to_string(), "0.0.0.0/32");
}
#[test]
fn test_normalize_ipnet_5() {
let net: IpNet = "::ffff:10.0.0.0/104".parse().unwrap();
let norm = normalize_ipnet(net);
assert!(matches!(norm, IpNet::V4(_)), "not normalized: {norm}");
assert_eq!(norm.to_string(), "10.0.0.0/8");
}
#[test]
fn test_normalize_ipnet_6() {
let net: IpNet = "::ffff:0.0.0.0/96".parse().unwrap();
let norm = normalize_ipnet(net);
assert!(matches!(norm, IpNet::V4(_)), "not normalized: {norm}");
assert_eq!(norm.to_string(), "0.0.0.0/0");
}
#[test]
fn test_normalize_ipnet_7() {
let net: IpNet = "2001:db8::/32".parse().unwrap();
assert!(matches!(normalize_ipnet(net), IpNet::V6(_)));
}
#[test]
fn test_clear_scope6_1() {
let mut addr = SockaddrStorage::from(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 4242));
let prev = addr;
assert_eq!(clear_scope6(&mut addr), None);
assert_eq!(addr, prev);
}
#[test]
fn test_clear_scope6_2() {
let mut addr = SockaddrStorage::from(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 4242, 0, 0));
let prev = addr;
assert_eq!(clear_scope6(&mut addr), None);
assert_eq!(addr, prev);
}
#[test]
fn test_clear_scope6_3() {
let lla = "fe80::1".parse::<Ipv6Addr>().unwrap();
let mut addr = SockaddrStorage::from(SocketAddrV6::new(lla, 4242, 0, 1));
assert_eq!(clear_scope6(&mut addr), Some((1, lla, 4242)));
assert_eq!(addr.as_sockaddr_in6().unwrap().scope_id(), 0);
}
#[test]
fn test_clear_scope6_4() {
let lla = "fe80::1".parse::<Ipv6Addr>().unwrap();
let mut addr = SockaddrStorage::from(SocketAddrV6::new(lla, 4242, 0, u32::MAX));
assert_eq!(clear_scope6(&mut addr), Some((u32::MAX, lla, 4242)));
assert_eq!(addr.as_sockaddr_in6().unwrap().scope_id(), 0);
}
#[test]
fn test_clear_scope6_5() {
let lla = "fe80::beef".parse::<Ipv6Addr>().unwrap();
let mut addr = SockaddrStorage::from(SocketAddrV6::new(lla, 4242, 0xCAFE, 7));
assert_eq!(clear_scope6(&mut addr), Some((7, lla, 4242)));
let sin6 = addr.as_sockaddr_in6().unwrap();
assert_eq!(sin6.ip(), lla);
assert_eq!(sin6.port(), 4242);
assert_eq!(sin6.flowinfo(), 0xCAFE);
assert_eq!(sin6.scope_id(), 0);
}
#[test]
fn test_clear_scope6_6() {
let mcast = "ff02::1".parse::<Ipv6Addr>().unwrap();
let mut addr = SockaddrStorage::from(SocketAddrV6::new(mcast, 4242, 0, 2));
assert_eq!(clear_scope6(&mut addr), Some((2, mcast, 4242)));
assert_eq!(addr.as_sockaddr_in6().unwrap().scope_id(), 0);
}
#[test]
fn test_clear_scope6_7() {
let mut addr = SockaddrStorage::from(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 4242, 0, 3));
assert_eq!(
clear_scope6(&mut addr),
Some((3, Ipv6Addr::LOCALHOST, 4242))
);
assert_eq!(addr.as_sockaddr_in6().unwrap().scope_id(), 0);
}
#[test]
fn test_clear_scope6_8() {
let mut addr = SockaddrStorage::from(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0));
let prev = addr;
assert_eq!(clear_scope6(&mut addr), None);
assert_eq!(addr, prev);
}
#[test]
fn test_clear_scope6_9() {
let mut addr = SockaddrStorage::from(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 5));
assert_eq!(clear_scope6(&mut addr), Some((5, Ipv6Addr::UNSPECIFIED, 0)));
assert_eq!(addr.as_sockaddr_in6().unwrap().scope_id(), 0);
}
}