#![forbid(unsafe_code)]
use std::{
fmt::{self, LowerHex, UpperHex},
net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6},
os::fd::{AsFd, BorrowedFd},
str::FromStr,
};
use bitflags::bitflags;
use ipnet::{IpNet, Ipv4Net};
use libc::c_int;
use libseccomp::ScmpArch;
use nix::{errno::Errno, sys::socket::SockaddrStorage};
use serde::{Serialize, Serializer};
use crate::{
compat::{
AddressFamily, SockType, BTPROTO_AVDTP, BTPROTO_BNEP, BTPROTO_CMTP, BTPROTO_HCI,
BTPROTO_HIDP, BTPROTO_ISO, BTPROTO_L2CAP, BTPROTO_RFCOMM, BTPROTO_SCO, CAN_BCM, CAN_ISOTP,
CAN_J1939, CAN_MCNET, CAN_RAW, CAN_TP16, CAN_TP20, NFC_SOCKPROTO_LLCP, NFC_SOCKPROTO_RAW,
PF_KEY_V2, PN_PROTO_PHONET, PN_PROTO_PIPE, PN_PROTO_TRANSPORT, PX_PROTO_OE, PX_PROTO_OL2TP,
PX_PROTO_PPTP, SMCPROTO_SMC, SMCPROTO_SMC6,
},
config::MAX_RW_COUNT,
confine::SydSys,
cookie::{
safe_getsockdomain, safe_getsockproto, safe_getsocktcpulp, safe_getsocktlsrx,
safe_getsocktype,
},
fd::SafeOwnedFd,
proc::info::PROC_INFO,
};
const LOOPBACK_MAPPED: Ipv6Addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 1);
bitflags! {
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub(crate) struct NetlinkFamily: u32 {
const NETLINK_ROUTE = 1 << 0; const NETLINK_USERSOCK = 1 << 2; const NETLINK_FIREWALL = 1 << 3; const NETLINK_SOCK_DIAG = 1 << 4; const NETLINK_NFLOG = 1 << 5; const NETLINK_XFRM = 1 << 6; const NETLINK_SELINUX = 1 << 7; const NETLINK_ISCSI = 1 << 8; const NETLINK_AUDIT = 1 << 9; const NETLINK_FIB_LOOKUP = 1 << 10;
const NETLINK_CONNECTOR = 1 << 11;
const NETLINK_NETFILTER = 1 << 12; const NETLINK_IP6_FW = 1 << 13;
const NETLINK_DNRTMSG = 1 << 14; const NETLINK_KOBJECT_UEVENT = 1 << 15; const NETLINK_GENERIC = 1 << 16;
const NETLINK_SCSITRANSPORT = 1 << 18; const NETLINK_ECRYPTFS = 1 << 19;
const NETLINK_RDMA = 1 << 20;
const NETLINK_CRYPTO = 1 << 21; const NETLINK_SMC = 1 << 22;
const NONROOT_RECV =
Self::NETLINK_ROUTE.bits() |
Self::NETLINK_SOCK_DIAG.bits() |
Self::NETLINK_SELINUX.bits() |
Self::NETLINK_AUDIT.bits() |
Self::NETLINK_CONNECTOR.bits() |
Self::NETLINK_KOBJECT_UEVENT.bits() |
Self::NETLINK_GENERIC.bits() |
Self::NETLINK_RDMA.bits();
}
}
impl NetlinkFamily {
pub(crate) const fn from_proto(proto: c_int) -> Option<Self> {
if proto < 0 || proto >= 32 {
return None;
}
Self::from_bits(1u32 << proto)
}
}
impl FromStr for NetlinkFamily {
type Err = Errno;
fn from_str(value: &str) -> Result<Self, Self::Err> {
let mut families = NetlinkFamily::empty();
for family in value.split(',') {
families |= match family.to_ascii_lowercase().as_str() {
"all" => Self::all(),
"route" => Self::NETLINK_ROUTE,
"usersock" => Self::NETLINK_USERSOCK,
"firewall" => Self::NETLINK_FIREWALL,
"inet_diag" | "sock_diag" => Self::NETLINK_SOCK_DIAG,
"nflog" => Self::NETLINK_NFLOG,
"xfrm" => Self::NETLINK_XFRM,
"selinux" => Self::NETLINK_SELINUX,
"iscsi" => Self::NETLINK_ISCSI,
"audit" => Self::NETLINK_AUDIT,
"fib_lookup" => Self::NETLINK_FIB_LOOKUP,
"connector" => Self::NETLINK_CONNECTOR,
"netfilter" => Self::NETLINK_NETFILTER,
"ip6_fw" => Self::NETLINK_IP6_FW,
"dnrtmsg" => Self::NETLINK_DNRTMSG,
"kobject_uevent" => Self::NETLINK_KOBJECT_UEVENT,
"generic" => Self::NETLINK_GENERIC,
"scsitransport" => Self::NETLINK_SCSITRANSPORT,
"ecryptfs" => Self::NETLINK_ECRYPTFS,
"rdma" => Self::NETLINK_RDMA,
"crypto" => Self::NETLINK_CRYPTO,
"smc" => Self::NETLINK_SMC,
_ => return Err(Errno::EINVAL),
};
}
if !families.is_empty() {
Ok(families)
} else {
Err(Errno::ENOENT)
}
}
}
impl NetlinkFamily {
pub(crate) fn max() -> c_int {
libc::NETLINK_CRYPTO + 1 }
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum IpProto {
Tcp,
Mptcp,
Udp,
UdpLite,
Sctp,
Dccp,
Icmp4,
Icmp6,
}
impl IpProto {
pub const fn name(self) -> &'static str {
match self {
Self::Tcp => "tcp",
Self::Mptcp => "mptcp",
Self::Udp => "udp",
Self::UdpLite => "udplite",
Self::Sctp => "sctp",
Self::Dccp => "dccp",
Self::Icmp4 => "icmp4",
Self::Icmp6 => "icmp6",
}
}
pub const fn from_raw(proto: c_int) -> Option<Self> {
match proto {
libc::IPPROTO_TCP => Some(Self::Tcp),
libc::IPPROTO_MPTCP => Some(Self::Mptcp),
libc::IPPROTO_UDP => Some(Self::Udp),
libc::IPPROTO_UDPLITE => Some(Self::UdpLite),
libc::IPPROTO_SCTP => Some(Self::Sctp),
libc::IPPROTO_DCCP => Some(Self::Dccp),
libc::IPPROTO_ICMP => Some(Self::Icmp4),
libc::IPPROTO_ICMPV6 => Some(Self::Icmp6),
_ => None,
}
}
pub const fn has_port(self) -> bool {
!matches!(self, Self::Icmp4 | Self::Icmp6)
}
}
impl fmt::Display for IpProto {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
impl Serialize for IpProto {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.name())
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum BtProto {
L2cap,
Hci,
Sco,
Rfcomm,
Bnep,
Cmtp,
Hidp,
Avdtp,
Iso,
}
impl BtProto {
pub(crate) const fn from_raw(proto: c_int) -> Option<Self> {
match proto {
BTPROTO_L2CAP => Some(Self::L2cap),
BTPROTO_HCI => Some(Self::Hci),
BTPROTO_SCO => Some(Self::Sco),
BTPROTO_RFCOMM => Some(Self::Rfcomm),
BTPROTO_BNEP => Some(Self::Bnep),
BTPROTO_CMTP => Some(Self::Cmtp),
BTPROTO_HIDP => Some(Self::Hidp),
BTPROTO_AVDTP => Some(Self::Avdtp),
BTPROTO_ISO => Some(Self::Iso),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum CanProto {
Raw,
Bcm,
Tp16,
Tp20,
McNet,
IsoTp,
J1939,
}
impl CanProto {
pub(crate) const fn from_raw(proto: c_int) -> Option<Self> {
match proto {
CAN_RAW => Some(Self::Raw),
CAN_BCM => Some(Self::Bcm),
CAN_TP16 => Some(Self::Tp16),
CAN_TP20 => Some(Self::Tp20),
CAN_MCNET => Some(Self::McNet),
CAN_ISOTP => Some(Self::IsoTp),
CAN_J1939 => Some(Self::J1939),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum NfcProto {
Raw,
Llcp,
}
impl NfcProto {
pub(crate) const fn from_raw(proto: c_int) -> Option<Self> {
match proto {
NFC_SOCKPROTO_RAW => Some(Self::Raw),
NFC_SOCKPROTO_LLCP => Some(Self::Llcp),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum PppoxProto {
Oe,
Ol2tp,
Pptp,
}
impl PppoxProto {
pub(crate) const fn from_raw(proto: c_int) -> Option<Self> {
match proto {
PX_PROTO_OE => Some(Self::Oe),
PX_PROTO_OL2TP => Some(Self::Ol2tp),
PX_PROTO_PPTP => Some(Self::Pptp),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum PhonetProto {
Transport,
Phonet,
Pipe,
}
impl PhonetProto {
pub(crate) const fn from_raw(proto: c_int) -> Option<Self> {
match proto {
PN_PROTO_TRANSPORT => Some(Self::Transport),
PN_PROTO_PHONET => Some(Self::Phonet),
PN_PROTO_PIPE => Some(Self::Pipe),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum SmcProto {
Smc,
Smc6,
}
impl SmcProto {
pub(crate) const fn from_raw(proto: c_int) -> Option<Self> {
match proto {
SMCPROTO_SMC => Some(Self::Smc),
SMCPROTO_SMC6 => Some(Self::Smc6),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum SockProto {
Ip(IpProto),
Netlink(NetlinkFamily),
Bluetooth(BtProto),
Can(CanProto),
Nfc(NfcProto),
Pppox(PppoxProto),
Phonet(PhonetProto),
Smc(SmcProto),
Key,
Other(AddressFamily, c_int),
}
impl SockProto {
pub(crate) const fn from_raw(dom: AddressFamily, proto: c_int) -> Self {
match dom {
AddressFamily::Inet | AddressFamily::Inet6 => match IpProto::from_raw(proto) {
Some(proto) => Self::Ip(proto),
None => Self::Other(dom, proto),
},
AddressFamily::Netlink => match NetlinkFamily::from_proto(proto) {
Some(family) => Self::Netlink(family),
None => Self::Other(dom, proto),
},
AddressFamily::Bluetooth => match BtProto::from_raw(proto) {
Some(proto) => Self::Bluetooth(proto),
None => Self::Other(dom, proto),
},
AddressFamily::Can => match CanProto::from_raw(proto) {
Some(proto) => Self::Can(proto),
None => Self::Other(dom, proto),
},
AddressFamily::Nfc => match NfcProto::from_raw(proto) {
Some(proto) => Self::Nfc(proto),
None => Self::Other(dom, proto),
},
AddressFamily::Pppox => match PppoxProto::from_raw(proto) {
Some(proto) => Self::Pppox(proto),
None => Self::Other(dom, proto),
},
AddressFamily::Phonet => match PhonetProto::from_raw(proto) {
Some(proto) => Self::Phonet(proto),
None => Self::Other(dom, proto),
},
AddressFamily::Smc => match SmcProto::from_raw(proto) {
Some(proto) => Self::Smc(proto),
None => Self::Other(dom, proto),
},
AddressFamily::Key if proto == PF_KEY_V2 => Self::Key,
_ => Self::Other(dom, proto),
}
}
pub(crate) const fn ip(self) -> Option<IpProto> {
match self {
Self::Ip(proto) => Some(proto),
_ => None,
}
}
pub(crate) const fn nonroot_recv(self) -> bool {
match self {
Self::Netlink(family) => family.intersects(NetlinkFamily::NONROOT_RECV),
_ => false,
}
}
}
pub(crate) const SOCKETCALL_NARGS: [u8; 21] = [
0, 3, 3, 3, 2, 3, 3, 3, 4, 4, 4, 6, 6, 2, 5, 5, 3, 3, 4, 5, 4,
];
#[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,
SetSockOpt = 0xe,
GetSockOpt = 0xf,
SendMsg = 0x10,
RecvMsg = 0x11,
Accept4 = 0x12,
RecvMmsg = 0x13,
SendMmsg = 0x14,
RecvMmsg64 = 0xFF, }
impl SocketCall {
pub(crate) fn name(self) -> &'static str {
SydSys::from(self).name()
}
}
impl From<SocketCall> for SydSys {
fn from(call: SocketCall) -> Self {
match call {
SocketCall::Socket => SydSys::SysSocket,
SocketCall::Bind => SydSys::SysBind,
SocketCall::Connect => SydSys::SysConnect,
SocketCall::Accept => SydSys::SysAccept,
SocketCall::GetSockName => SydSys::SysGetsockname,
SocketCall::GetPeerName => SydSys::SysGetpeername,
SocketCall::SocketPair => SydSys::SysSocketpair,
SocketCall::Send => SydSys::SysSend,
SocketCall::Recv => SydSys::SysRecv,
SocketCall::SendTo => SydSys::SysSendto,
SocketCall::RecvFrom => SydSys::SysRecvfrom,
SocketCall::SetSockOpt => SydSys::SysSetsockopt,
SocketCall::GetSockOpt => SydSys::SysGetsockopt,
SocketCall::SendMsg => SydSys::SysSendmsg,
SocketCall::RecvMsg => SydSys::SysRecvmsg,
SocketCall::Accept4 => SydSys::SysAccept4,
SocketCall::RecvMmsg => SydSys::SysRecvmmsg,
SocketCall::RecvMmsg64 => SydSys::SysRecvmmsgTime64,
SocketCall::SendMmsg => SydSys::SysSendmmsg,
}
}
}
impl fmt::Display for SocketCall {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
impl Serialize for SocketCall {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.name())
}
}
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),
0xe => Ok(Self::SetSockOpt),
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) -> Result<IpNet, ipnet::PrefixLenError> {
if let IpNet::V6(v6) = net {
if let Some(v4) = v6.addr().to_ipv4_mapped() {
let prefix = v6
.prefix_len()
.checked_sub(96)
.ok_or(ipnet::PrefixLenError)?;
return Ok(IpNet::from(Ipv4Net::new_assert(v4, prefix)));
}
}
Ok(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 < PROC_INFO.ip_unprivileged_port_start
})
.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 < PROC_INFO.ip_unprivileged_port_start
})
.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))
}
pub(crate) fn sock2direct(name: &str, arch: ScmpArch) -> Option<i32> {
Some(match arch {
ScmpArch::M68k => match name {
"socket" => 356,
"socketpair" => 357,
"bind" => 358,
"accept4" => 361, "connect" => 359,
"getpeername" => 365,
"getsockname" => 364,
"getsockopt" => 362,
"setsockopt" => 363,
"recvfrom" => 368,
"recvmsg" => 369,
"recvmmsg" => 371,
"recvmmsg_time64" => 417,
"sendto" => 366,
"sendmsg" => 367,
"sendmmsg" => 372,
_ => return None,
},
ScmpArch::Mips | ScmpArch::Mipsel => match name {
"socket" => 183,
"socketpair" => 184,
"bind" => 169,
"accept" => 168,
"accept4" => 334,
"connect" => 170,
"getpeername" => 171,
"getsockname" => 172,
"getsockopt" => 173,
"setsockopt" => 181,
"recvfrom" => 176,
"recvmsg" => 177,
"recvmmsg" => 335,
"recvmmsg_time64" => 417,
"sendto" => 180,
"sendmsg" => 179,
"sendmmsg" => 343,
_ => return None,
},
ScmpArch::Ppc | ScmpArch::Ppc64 | ScmpArch::Ppc64Le => match name {
"socket" => 326,
"socketpair" => 333,
"bind" => 327,
"accept" => 330,
"accept4" => 344,
"connect" => 328,
"getpeername" => 332,
"getsockname" => 331,
"getsockopt" => 340,
"setsockopt" => 339,
"recvfrom" => 337,
"recvmsg" => 342,
"recvmmsg" => 343,
"recvmmsg_time64" => 417,
"sendto" => 335,
"sendmsg" => 341,
"sendmmsg" => 349,
_ => return None,
},
ScmpArch::S390X | ScmpArch::S390 => match name {
"socket" => 359,
"socketpair" => 360,
"bind" => 361,
"accept4" => 364, "connect" => 362,
"getpeername" => 368,
"getsockname" => 367,
"getsockopt" => 365,
"setsockopt" => 366,
"recvfrom" => 371,
"recvmsg" => 372,
"recvmmsg" => 357,
"sendto" => 369,
"sendmsg" => 370,
"sendmmsg" => 358,
_ => return None,
},
ScmpArch::X86 => match name {
"socket" => 359,
"socketpair" => 360,
"bind" => 361,
"accept4" => 364, "connect" => 362,
"getpeername" => 368,
"getsockname" => 367,
"getsockopt" => 365,
"setsockopt" => 366,
"recvfrom" => 371,
"recvmsg" => 372,
"recvmmsg" => 337,
"recvmmsg_time64" => 417,
"sendto" => 369,
"sendmsg" => 370,
"sendmmsg" => 345,
_ => return None,
},
_ => return None,
})
}
pub(crate) struct SockInfo {
fd: SafeOwnedFd,
dom: Option<AddressFamily>,
proto: Option<SockProto>,
stype: Option<SockType>,
trunc_discard: Option<bool>,
}
impl SockInfo {
pub(crate) fn new(fd: SafeOwnedFd) -> Self {
Self {
fd,
dom: None,
proto: None,
stype: None,
trunc_discard: None,
}
}
pub(crate) fn into_fd(self) -> SafeOwnedFd {
self.fd
}
pub(crate) fn fd(&self) -> &SafeOwnedFd {
&self.fd
}
pub(crate) fn get_dom(&mut self) -> Result<AddressFamily, Errno> {
match self.dom {
Some(dom) => Ok(dom),
None => {
let dom = safe_getsockdomain(&self.fd)?;
self.dom = Some(dom);
Ok(dom)
}
}
}
pub(crate) fn get_ipproto(&mut self) -> Result<Option<IpProto>, Errno> {
Ok(self.get_sockproto()?.ip())
}
pub(crate) fn get_sockproto(&mut self) -> Result<SockProto, Errno> {
match self.proto {
Some(proto) => Ok(proto),
None => {
let dom = self.get_dom()?;
let proto = safe_getsockproto(&self.fd, dom)?;
self.proto = Some(proto);
Ok(proto)
}
}
}
pub(crate) fn get_stype(&mut self) -> Result<SockType, Errno> {
match self.stype {
Some(stype) => Ok(stype),
None => {
let stype = safe_getsocktype(&self.fd)?;
self.stype = Some(stype);
Ok(stype)
}
}
}
pub(crate) fn get_trunc_discard(&mut self) -> Result<bool, Errno> {
match self.trunc_discard {
Some(discard) => Ok(discard),
None => {
let discard = match self.get_ipproto()? {
Some(IpProto::Mptcp) => true,
Some(IpProto::Tcp) => match safe_getsocktcpulp(&self.fd)? {
None => true,
Some(name) if name.starts_with(b"tls\0") => !safe_getsocktlsrx(&self.fd)?,
Some(_) => false,
},
_ => false,
};
self.trunc_discard = Some(discard);
Ok(discard)
}
}
}
pub(crate) fn get_send_errno(&mut self) -> Result<Errno, Errno> {
if self.get_dom()? != AddressFamily::Inet6 {
return Ok(Errno::EAFNOSUPPORT);
}
Ok(match (self.get_stype()?, self.get_ipproto()?) {
(SockType::Raw, _) | (SockType::Datagram, Some(IpProto::Icmp6)) => Errno::EAFNOSUPPORT,
_ => Errno::EINVAL,
})
}
pub(crate) fn get_send_sigpipe(&mut self) -> Result<bool, Errno> {
Ok(self.get_dom()? != AddressFamily::Unix || self.get_stype()? == SockType::Stream)
}
pub(crate) fn get_stream_send(&mut self) -> Result<bool, Errno> {
if self.get_stype()? != SockType::Stream {
return Ok(false);
}
Ok(match self.get_dom()? {
AddressFamily::Unix => true,
AddressFamily::Inet | AddressFamily::Inet6 => {
matches!(self.get_ipproto()?, Some(IpProto::Tcp | IpProto::Mptcp))
}
_ => false,
})
}
pub(crate) fn check_send_len(&mut self, len: usize) -> Result<(), Errno> {
if len.min(*MAX_RW_COUNT) <= PROC_INFO.sndbuf_max {
return Ok(());
}
if self.get_dom()? == AddressFamily::Alg || self.get_stream_send()? {
return Ok(());
}
Err(Errno::EMSGSIZE)
}
}
impl AsFd for SockInfo {
fn as_fd(&self) -> BorrowedFd<'_> {
self.fd.as_fd()
}
}
#[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_ip_proto_1() {
assert_eq!(IpProto::from_raw(libc::IPPROTO_TCP), Some(IpProto::Tcp));
}
#[test]
fn test_ip_proto_2() {
assert_eq!(IpProto::from_raw(libc::IPPROTO_UDP), Some(IpProto::Udp));
}
#[test]
fn test_ip_proto_3() {
assert_eq!(IpProto::from_raw(0), None);
assert_eq!(IpProto::from_raw(libc::IPPROTO_RAW), None);
assert_eq!(IpProto::from_raw(-1), None);
}
#[test]
fn test_ip_proto_4() {
assert_eq!(IpProto::Tcp.name(), "tcp");
assert_eq!(IpProto::Mptcp.name(), "mptcp");
assert_eq!(IpProto::Udp.name(), "udp");
assert_eq!(IpProto::UdpLite.name(), "udplite");
assert_eq!(IpProto::Sctp.name(), "sctp");
assert_eq!(IpProto::Dccp.name(), "dccp");
assert_eq!(IpProto::Icmp4.name(), "icmp4");
assert_eq!(IpProto::Icmp6.name(), "icmp6");
}
#[test]
fn test_ip_proto_5() {
assert_eq!(IpProto::from_raw(libc::IPPROTO_MPTCP), Some(IpProto::Mptcp));
assert_eq!(
IpProto::from_raw(libc::IPPROTO_UDPLITE),
Some(IpProto::UdpLite)
);
assert_eq!(IpProto::from_raw(libc::IPPROTO_SCTP), Some(IpProto::Sctp));
assert_eq!(IpProto::from_raw(libc::IPPROTO_DCCP), Some(IpProto::Dccp));
assert_eq!(IpProto::from_raw(libc::IPPROTO_ICMP), Some(IpProto::Icmp4));
assert_eq!(
IpProto::from_raw(libc::IPPROTO_ICMPV6),
Some(IpProto::Icmp6)
);
}
#[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() {
let start = PROC_INFO.ip_unprivileged_port_start;
if start > 1 {
assert!(has_privileged_port_v4(&v4(Ipv4Addr::LOCALHOST, 1)));
assert!(has_privileged_port_v4(&v4(Ipv4Addr::LOCALHOST, start - 1)));
}
}
#[test]
fn test_privileged_port_v4_2() {
let start = PROC_INFO.ip_unprivileged_port_start;
assert!(!has_privileged_port_v4(&v4(Ipv4Addr::LOCALHOST, 0)));
assert!(!has_privileged_port_v4(&v4(Ipv4Addr::LOCALHOST, start)));
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() {
let start = PROC_INFO.ip_unprivileged_port_start;
if start > 1 {
assert!(has_privileged_port_v6(&v6(Ipv6Addr::LOCALHOST, 1)));
assert!(has_privileged_port_v6(&v6(Ipv6Addr::LOCALHOST, start - 1)));
}
}
#[test]
fn test_privileged_port_v6_2() {
let start = PROC_INFO.ip_unprivileged_port_start;
assert!(!has_privileged_port_v6(&v6(Ipv6Addr::LOCALHOST, 0)));
assert!(!has_privileged_port_v6(&v6(Ipv6Addr::LOCALHOST, start)));
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).unwrap(), IpNet::V4(_)));
}
#[test]
fn test_normalize_ipnet_2() {
let net: IpNet = "::1/128".parse().unwrap();
assert!(matches!(normalize_ipnet(net).unwrap(), IpNet::V6(_)));
}
#[test]
fn test_normalize_ipnet_3() {
let net: IpNet = "::ffff:127.0.0.1/128".parse().unwrap();
let norm = normalize_ipnet(net).unwrap();
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).unwrap();
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).unwrap();
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).unwrap();
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).unwrap(), IpNet::V6(_)));
}
#[test]
fn test_normalize_ipnet_8() {
let net: IpNet = "::ffff:10.0.0.0/64".parse().unwrap();
assert!(normalize_ipnet(net).is_err());
}
#[test]
fn test_normalize_ipnet_9() {
let net: IpNet = "::ffff:0.0.0.0/95".parse().unwrap();
assert!(normalize_ipnet(net).is_err());
}
#[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);
}
}