Skip to content

Commit b8630c3

Browse files
committed
sys::socket listen's Backlog wrapper type addition.
changing the sys::socket::listen backlog type from `usize` to a `i32` wrapper, offering known sane values, from -1, SOMAXCONN to 511. close gh-2264
1 parent 4e2d917 commit b8630c3

File tree

5 files changed

+145
-11
lines changed

5 files changed

+145
-11
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ targets = [
2828
]
2929

3030
[dependencies]
31-
libc = { version = "0.2.151", features = ["extra_traits"] }
31+
libc = { git = "https://github.com/rust-lang/libc", rev = "6a203e955b60cca48562f020f0e4e003079f3199", features = ["extra_traits"] }
3232
bitflags = "2.3.1"
3333
cfg-if = "1.0"
3434
pin-utils = { version = "0.1.0", optional = true }

changelog/2276.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added the `Backlog` wrapper type for the `listen` call.

src/sys/socket/mod.rs

Lines changed: 120 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2009,12 +2009,130 @@ pub fn socketpair<T: Into<Option<SockProtocol>>>(
20092009
unsafe { Ok((OwnedFd::from_raw_fd(fds[0]), OwnedFd::from_raw_fd(fds[1]))) }
20102010
}
20112011

2012+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2013+
pub struct Backlog(i32);
2014+
2015+
impl Backlog {
2016+
/// Sets the listen queue size to system `SOMAXCONN` value
2017+
pub const MAXCONN: Self = Self(libc::SOMAXCONN);
2018+
/// Sets the listen queue size to -1 for system supporting it
2019+
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
2020+
pub const MAXALLOWABLE: Self = Self(-1);
2021+
}
2022+
2023+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2024+
pub enum BacklogTryFromError {
2025+
TooNegative,
2026+
TooPositive,
2027+
}
2028+
2029+
impl std::fmt::Display for BacklogTryFromError {
2030+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2031+
match self {
2032+
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
2033+
Self::TooNegative => write!(f, "Passed a positive backlog less than -1"),
2034+
#[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
2035+
Self::TooNegative => write!(f, "Passed a positive backlog less than 0"),
2036+
Self::TooPositive => write!(f, "Passed a positive backlog greater than `{:?}`", Backlog::MAXCONN)
2037+
}
2038+
}
2039+
}
2040+
2041+
impl std::error::Error for BacklogTryFromError {}
2042+
2043+
impl<T: Into<Backlog>> From<Option<T>> for Backlog {
2044+
fn from(backlog: Option<T>) -> Self {
2045+
backlog.map_or(Self::MAXCONN, |b| b.into())
2046+
}
2047+
}
2048+
2049+
impl From<u16> for Backlog {
2050+
fn from(backlog: u16) -> Self {
2051+
Self(i32::from(backlog))
2052+
}
2053+
}
2054+
2055+
impl From<u8> for Backlog {
2056+
fn from(backlog: u8) -> Self {
2057+
Self(i32::from(backlog))
2058+
}
2059+
}
2060+
2061+
impl From<Backlog> for i32 {
2062+
fn from(backlog: Backlog) -> Self {
2063+
backlog.0
2064+
}
2065+
}
2066+
2067+
impl TryFrom<i64> for Backlog {
2068+
type Error = BacklogTryFromError;
2069+
fn try_from(backlog: i64) -> std::result::Result<Self, Self::Error> {
2070+
match backlog {
2071+
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
2072+
..=-2 => Err(BacklogTryFromError::TooNegative),
2073+
#[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
2074+
..=-1 => Err(BacklogTryFromError::TooNegative),
2075+
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
2076+
val if (-1..=i64::from(Backlog::MAXCONN.0)).contains(&val) => Ok(Self(i32::try_from(backlog).map_err(|_| BacklogTryFromError::TooPositive)?)),
2077+
#[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
2078+
val if (0..=i64::from(Backlog::MAXCONN.0)).contains(&val) => Ok(Self(i32::try_from(backlog).map_err(|_| BacklogTryFromError::TooPositive)?)),
2079+
_ => Err(BacklogTryFromError::TooPositive),
2080+
}
2081+
}
2082+
}
2083+
2084+
impl TryFrom<i32> for Backlog {
2085+
type Error = BacklogTryFromError;
2086+
fn try_from(backlog: i32) -> std::result::Result<Self, Self::Error> {
2087+
match backlog {
2088+
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
2089+
..=-2 => Err(BacklogTryFromError::TooNegative),
2090+
#[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
2091+
..=-1 => Err(BacklogTryFromError::TooNegative),
2092+
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
2093+
val if (-1..=Backlog::MAXCONN.0).contains(&val) => Ok(Self(backlog)),
2094+
#[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
2095+
val if (0..=Backlog::MAXCONN.0).contains(&val) => Ok(Self(backlog)),
2096+
_ => Err(BacklogTryFromError::TooPositive),
2097+
}
2098+
}
2099+
}
2100+
2101+
impl TryFrom<i16> for Backlog {
2102+
type Error = BacklogTryFromError;
2103+
fn try_from(backlog: i16) -> std::result::Result<Self, Self::Error> {
2104+
match backlog {
2105+
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
2106+
..=-2 => Err(BacklogTryFromError::TooNegative),
2107+
#[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
2108+
..=-1 => Err(BacklogTryFromError::TooNegative),
2109+
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
2110+
val if (-1..=i16::try_from(Backlog::MAXCONN.0).unwrap()).contains(&val) => Ok(Self(i32::from(backlog))),
2111+
#[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
2112+
val if (0..=i16::try_from(Backlog::MAXCONN.0).unwrap()).contains(&val) => Ok(Self(i32::from(backlog))),
2113+
_ => Err(BacklogTryFromError::TooPositive),
2114+
}
2115+
}
2116+
}
2117+
2118+
impl TryFrom<i8> for Backlog {
2119+
type Error = BacklogTryFromError;
2120+
fn try_from(backlog: i8) -> std::result::Result<Self, Self::Error> {
2121+
match backlog {
2122+
..=-2 => Err(BacklogTryFromError::TooNegative),
2123+
_ => Err(BacklogTryFromError::TooPositive),
2124+
}
2125+
}
2126+
}
2127+
2128+
/// Listen for connections on a socket
2129+
20122130
/// Listen for connections on a socket
20132131
///
20142132
/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html)
2015-
pub fn listen<F: AsFd>(sock: &F, backlog: usize) -> Result<()> {
2133+
pub fn listen<F: AsFd, B: Into<Backlog>>(sock: &F, backlog: B) -> Result<()> {
20162134
let fd = sock.as_fd().as_raw_fd();
2017-
let res = unsafe { libc::listen(fd, backlog as c_int) };
2135+
let res = unsafe { libc::listen(fd, i32::from(backlog.into())) };
20182136

20192137
Errno::result(res).map(drop)
20202138
}

test/sys/test_socket.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1611,7 +1611,9 @@ fn test_impl_scm_credentials_and_rights(mut space: Vec<u8>) {
16111611
// Test creating and using named unix domain sockets
16121612
#[test]
16131613
pub fn test_named_unixdomain() {
1614-
use nix::sys::socket::{accept, bind, connect, listen, socket, UnixAddr};
1614+
use nix::sys::socket::{
1615+
accept, bind, connect, listen, socket, Backlog, UnixAddr,
1616+
};
16151617
use nix::sys::socket::{SockFlag, SockType};
16161618
use nix::unistd::{read, write};
16171619
use std::thread;
@@ -1627,7 +1629,7 @@ pub fn test_named_unixdomain() {
16271629
.expect("socket failed");
16281630
let sockaddr = UnixAddr::new(&sockname).unwrap();
16291631
bind(s1.as_raw_fd(), &sockaddr).expect("bind failed");
1630-
listen(&s1, 10).expect("listen failed");
1632+
listen(&s1, Backlog::from(10u16)).expect("listen failed");
16311633

16321634
let thr = thread::spawn(move || {
16331635
let s2 = socket(
@@ -1650,6 +1652,16 @@ pub fn test_named_unixdomain() {
16501652
assert_eq!(&buf[..], b"hello");
16511653
}
16521654

1655+
#[test]
1656+
pub fn test_listen_wrongbacklog() {
1657+
use nix::sys::socket::Backlog;
1658+
1659+
assert!(Backlog::try_from(5012i16).is_err());
1660+
assert!(Backlog::try_from(65535i64).is_err());
1661+
assert!(Backlog::try_from(-2i16).is_err());
1662+
assert!(Backlog::try_from(-2i8).is_err());
1663+
}
1664+
16531665
// Test using unnamed unix domain addresses
16541666
#[cfg(linux_android)]
16551667
#[test]

test/sys/test_sockopt.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ fn test_so_buf() {
106106
#[cfg(target_os = "freebsd")]
107107
#[test]
108108
fn test_so_listen_q_limit() {
109-
use nix::sys::socket::{bind, listen, SockaddrIn};
109+
use nix::sys::socket::{bind, listen, Backlog, SockaddrIn};
110110
use std::net::SocketAddrV4;
111111
use std::str::FromStr;
112112

@@ -123,14 +123,16 @@ fn test_so_listen_q_limit() {
123123
bind(rsock.as_raw_fd(), &sock_addr).unwrap();
124124
let pre_limit = getsockopt(&rsock, sockopt::ListenQLimit).unwrap();
125125
assert_eq!(pre_limit, 0);
126-
listen(&rsock, 42).unwrap();
126+
listen(&rsock, Backlog::from(42u16)).unwrap();
127127
let post_limit = getsockopt(&rsock, sockopt::ListenQLimit).unwrap();
128128
assert_eq!(post_limit, 42);
129129
}
130130

131131
#[test]
132132
fn test_so_tcp_maxseg() {
133-
use nix::sys::socket::{accept, bind, connect, listen, SockaddrIn};
133+
use nix::sys::socket::{
134+
accept, bind, connect, listen, Backlog, SockaddrIn,
135+
};
134136
use nix::unistd::write;
135137
use std::net::SocketAddrV4;
136138
use std::str::FromStr;
@@ -146,7 +148,7 @@ fn test_so_tcp_maxseg() {
146148
)
147149
.unwrap();
148150
bind(rsock.as_raw_fd(), &sock_addr).unwrap();
149-
listen(&rsock, 10).unwrap();
151+
listen(&rsock, Backlog::from(10u16)).unwrap();
150152
let initial = getsockopt(&rsock, sockopt::TcpMaxSeg).unwrap();
151153
// Initial MSS is expected to be 536 (https://tools.ietf.org/html/rfc879#section-1) but some
152154
// platforms keep it even lower. This might fail if you've tuned your initial MSS to be larger
@@ -716,7 +718,8 @@ fn is_socket_type_dgram() {
716718
#[test]
717719
fn can_get_listen_on_tcp_socket() {
718720
use nix::sys::socket::{
719-
getsockopt, listen, socket, sockopt, AddressFamily, SockFlag, SockType,
721+
getsockopt, listen, socket, sockopt, AddressFamily, Backlog, SockFlag,
722+
SockType,
720723
};
721724

722725
let s = socket(
@@ -728,7 +731,7 @@ fn can_get_listen_on_tcp_socket() {
728731
.unwrap();
729732
let s_listening = getsockopt(&s, sockopt::AcceptConn).unwrap();
730733
assert!(!s_listening);
731-
listen(&s, 10).unwrap();
734+
listen(&s, Backlog::from(10u16)).unwrap();
732735
let s_listening2 = getsockopt(&s, sockopt::AcceptConn).unwrap();
733736
assert!(s_listening2);
734737
}

0 commit comments

Comments
 (0)