Skip to content

Commit

Permalink
sys::socket listen's Backlog wrapper type addition.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
devnexen committed Dec 29, 2023
1 parent 4e2d917 commit 3e2735c
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 10 deletions.
108 changes: 106 additions & 2 deletions src/sys/socket/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2009,12 +2009,116 @@ pub fn socketpair<T: Into<Option<SockProtocol>>>(
unsafe { Ok((OwnedFd::from_raw_fd(fds[0]), OwnedFd::from_raw_fd(fds[1]))) }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Backlog(i32);

impl Backlog {
/// Sets the listen queue size to system `SOMAXCONN` value
#[cfg(target_os = "redox")]
pub const DEFAULT: Self = Self(128);
#[cfg(not(target_os = "redox"))]
pub const DEFAULT: Self = Self(libc::SOMAXCONN);
/// Sets the listen queue size to -1 for system supporting it
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
pub const MAXALLOWABLE: Self = Self(-1);
/// Reasonable upper limit, while in theory we might be able
/// to set it to a higher boundary, comes with a risk
/// of resource exhaustion in return
pub const MAXVALUE: Self = Self(511);
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BacklogTryFromError {
TooNegative,
TooPositive,
}

impl std::fmt::Display for BacklogTryFromError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::TooNegative => write!(f, "Passed a positive backlog less than -1"),
Self::TooPositive => write!(f, "Passed a positive backlog greater than `511`")
}
}
}

impl std::error::Error for BacklogTryFromError {}

impl<T: Into<Backlog>> From<Option<T>> for Backlog {
fn from(backlog: Option<T>) -> Self {
backlog.map_or(Self::DEFAULT, |b| b.into())
}
}

impl From<u16> for Backlog {
fn from(backlog: u16) -> Self {
Self(i32::from(backlog))
}
}

impl From<u8> for Backlog {
fn from(backlog: u8) -> Self {
Self(i32::from(backlog))
}
}

impl From<Backlog> for i32 {
fn from(backlog: Backlog) -> Self {
backlog.0
}
}

impl TryFrom<i64> for Backlog {
type Error = BacklogTryFromError;
fn try_from(backlog: i64) -> std::result::Result<Self, Self::Error> {
match backlog {
..=-2 => Err(BacklogTryFromError::TooNegative),
-1..=511 => Ok(Self(i32::try_from(backlog).map_err(|_| BacklogTryFromError::TooPositive)?)),
_ => Err(BacklogTryFromError::TooPositive),
}
}
}

impl TryFrom<i32> for Backlog {
type Error = BacklogTryFromError;
fn try_from(backlog: i32) -> std::result::Result<Self, Self::Error> {
match backlog {
..=-2 => Err(BacklogTryFromError::TooNegative),
-1..=511 => Ok(Self(backlog)),
_ => Err(BacklogTryFromError::TooPositive),
}
}
}

impl TryFrom<i16> for Backlog {
type Error = BacklogTryFromError;
fn try_from(backlog: i16) -> std::result::Result<Self, Self::Error> {
match backlog {
..=-2 => Err(BacklogTryFromError::TooNegative),
-1..=511 => Ok(Self(i32::try_from(backlog).map_err(|_| BacklogTryFromError::TooPositive)?)),
_ => Err(BacklogTryFromError::TooPositive),
}
}
}

impl TryFrom<i8> for Backlog {
type Error = BacklogTryFromError;
fn try_from(backlog: i8) -> std::result::Result<Self, Self::Error> {
match backlog {
..=-2 => Err(BacklogTryFromError::TooNegative),
_ => Err(BacklogTryFromError::TooPositive),
}
}
}

/// Listen for connections on a socket

/// Listen for connections on a socket
///
/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html)
pub fn listen<F: AsFd>(sock: &F, backlog: usize) -> Result<()> {
pub fn listen<F: AsFd, B: Into<Backlog>>(sock: &F, backlog: B) -> Result<()> {
let fd = sock.as_fd().as_raw_fd();
let res = unsafe { libc::listen(fd, backlog as c_int) };
let res = unsafe { libc::listen(fd, i32::from(backlog.into())) };

Errno::result(res).map(drop)
}
Expand Down
16 changes: 14 additions & 2 deletions test/sys/test_socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1611,7 +1611,9 @@ fn test_impl_scm_credentials_and_rights(mut space: Vec<u8>) {
// Test creating and using named unix domain sockets
#[test]
pub fn test_named_unixdomain() {
use nix::sys::socket::{accept, bind, connect, listen, socket, UnixAddr};
use nix::sys::socket::{
accept, bind, connect, listen, socket, Backlog, UnixAddr,
};
use nix::sys::socket::{SockFlag, SockType};
use nix::unistd::{read, write};
use std::thread;
Expand All @@ -1627,7 +1629,7 @@ pub fn test_named_unixdomain() {
.expect("socket failed");
let sockaddr = UnixAddr::new(&sockname).unwrap();
bind(s1.as_raw_fd(), &sockaddr).expect("bind failed");
listen(&s1, 10).expect("listen failed");
listen(&s1, Backlog::from(10u16)).expect("listen failed");

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

#[test]
pub fn test_listen_wrongbacklog() {
use nix::sys::socket::Backlog;

assert!(Backlog::try_from(512i16).is_err());
assert!(Backlog::try_from(512i64).is_err());
assert!(Backlog::try_from(-2i16).is_err());
assert!(Backlog::try_from(-2i8).is_err());
}

// Test using unnamed unix domain addresses
#[cfg(linux_android)]
#[test]
Expand Down
15 changes: 9 additions & 6 deletions test/sys/test_sockopt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ fn test_so_buf() {
#[cfg(target_os = "freebsd")]
#[test]
fn test_so_listen_q_limit() {
use nix::sys::socket::{bind, listen, SockaddrIn};
use nix::sys::socket::{bind, listen, Backlog, SockaddrIn};
use std::net::SocketAddrV4;
use std::str::FromStr;

Expand All @@ -123,14 +123,16 @@ fn test_so_listen_q_limit() {
bind(rsock.as_raw_fd(), &sock_addr).unwrap();
let pre_limit = getsockopt(&rsock, sockopt::ListenQLimit).unwrap();
assert_eq!(pre_limit, 0);
listen(&rsock, 42).unwrap();
listen(&rsock, Backlog::from(42u16)).unwrap();
let post_limit = getsockopt(&rsock, sockopt::ListenQLimit).unwrap();
assert_eq!(post_limit, 42);
}

#[test]
fn test_so_tcp_maxseg() {
use nix::sys::socket::{accept, bind, connect, listen, SockaddrIn};
use nix::sys::socket::{
accept, bind, connect, listen, Backlog, SockaddrIn,
};
use nix::unistd::write;
use std::net::SocketAddrV4;
use std::str::FromStr;
Expand All @@ -146,7 +148,7 @@ fn test_so_tcp_maxseg() {
)
.unwrap();
bind(rsock.as_raw_fd(), &sock_addr).unwrap();
listen(&rsock, 10).unwrap();
listen(&rsock, Backlog::from(10u16)).unwrap();
let initial = getsockopt(&rsock, sockopt::TcpMaxSeg).unwrap();
// Initial MSS is expected to be 536 (https://tools.ietf.org/html/rfc879#section-1) but some
// platforms keep it even lower. This might fail if you've tuned your initial MSS to be larger
Expand Down Expand Up @@ -716,7 +718,8 @@ fn is_socket_type_dgram() {
#[test]
fn can_get_listen_on_tcp_socket() {
use nix::sys::socket::{
getsockopt, listen, socket, sockopt, AddressFamily, SockFlag, SockType,
getsockopt, listen, socket, sockopt, AddressFamily, Backlog, SockFlag,
SockType,
};

let s = socket(
Expand All @@ -728,7 +731,7 @@ fn can_get_listen_on_tcp_socket() {
.unwrap();
let s_listening = getsockopt(&s, sockopt::AcceptConn).unwrap();
assert!(!s_listening);
listen(&s, 10).unwrap();
listen(&s, Backlog::from(10u16)).unwrap();
let s_listening2 = getsockopt(&s, sockopt::AcceptConn).unwrap();
assert!(s_listening2);
}

0 comments on commit 3e2735c

Please sign in to comment.