From 183bbe409ab69cbf9db41d0263b41ec86202d9a0 Mon Sep 17 00:00:00 2001 From: Zeki Sherif <9832640+zekisherif@users.noreply.github.com> Date: Sat, 24 Oct 2020 10:12:53 -0700 Subject: [PATCH] Add SO_REUSEPORT support for TCP socket (#1375) --- src/net/tcp/socket.rs | 19 ++++++++++++++++++ src/sys/shell/tcp.rs | 14 +++++++++++++ src/sys/unix/tcp.rs | 45 ++++++++++++++++++++++++++++++++++++++++++ src/sys/windows/tcp.rs | 18 ++++++++++++++++- tests/tcp_socket.rs | 16 +++++++++++++++ 5 files changed, 111 insertions(+), 1 deletion(-) diff --git a/src/net/tcp/socket.rs b/src/net/tcp/socket.rs index a91f665a7..efc23490a 100644 --- a/src/net/tcp/socket.rs +++ b/src/net/tcp/socket.rs @@ -82,6 +82,25 @@ impl TcpSocket { sys::tcp::set_reuseaddr(self.sys, reuseaddr) } + /// Get the value of `SO_REUSEADDR` set on this socket. + pub fn get_reuseaddr(&self) -> io::Result { + sys::tcp::get_reuseaddr(self.sys) + } + + /// Sets the value of `SO_REUSEPORT` on this socket. + /// Only supported available in unix + #[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))] + pub fn set_reuseport(&self, reuseport: bool) -> io::Result<()> { + sys::tcp::set_reuseport(self.sys, reuseport) + } + + /// Get the value of `SO_REUSEPORT` set on this socket. + /// Only supported available in unix + #[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))] + pub fn get_reuseport(&self) -> io::Result { + sys::tcp::get_reuseport(self.sys) + } + /// Sets the value of `SO_LINGER` on this socket. pub fn set_linger(&self, dur: Option) -> io::Result<()> { sys::tcp::set_linger(self.sys, dur) diff --git a/src/sys/shell/tcp.rs b/src/sys/shell/tcp.rs index de1520b8f..b3c4e2999 100644 --- a/src/sys/shell/tcp.rs +++ b/src/sys/shell/tcp.rs @@ -32,6 +32,20 @@ pub(crate) fn set_reuseaddr(_: TcpSocket, _: bool) -> io::Result<()> { os_required!(); } +pub(crate) fn get_reuseaddr(_: TcpSocket) -> io::Result { + os_required!(); +} + +#[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))] +pub(crate) fn set_reuseport(_: TcpSocket, _: bool) -> io::Result<()> { + os_required!(); +} + +#[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))] +pub(crate) fn get_reuseport(_: TcpSocket) -> io::Result { + os_required!(); +} + pub(crate) fn set_linger(_: TcpSocket, _: Option) -> io::Result<()> { os_required!(); } diff --git a/src/sys/unix/tcp.rs b/src/sys/unix/tcp.rs index 81b371bfc..a623cf391 100644 --- a/src/sys/unix/tcp.rs +++ b/src/sys/unix/tcp.rs @@ -1,4 +1,5 @@ use std::io; +use std::mem; use std::mem::{size_of, MaybeUninit}; use std::net::{self, SocketAddr}; use std::time::Duration; @@ -58,6 +59,50 @@ pub(crate) fn set_reuseaddr(socket: TcpSocket, reuseaddr: bool) -> io::Result<() )).map(|_| ()) } +pub(crate) fn get_reuseaddr(socket: TcpSocket) -> io::Result { + let mut optval: libc::c_int = 0; + let mut optlen = mem::size_of::() as libc::socklen_t; + + syscall!(getsockopt( + socket, + libc::SOL_SOCKET, + libc::SO_REUSEADDR, + &mut optval as *mut _ as *mut _, + &mut optlen, + ))?; + + Ok(optval != 0) +} + +#[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))] +pub(crate) fn set_reuseport(socket: TcpSocket, reuseport: bool) -> io::Result<()> { + let val: libc::c_int = if reuseport { 1 } else { 0 }; + + syscall!(setsockopt( + socket, + libc::SOL_SOCKET, + libc::SO_REUSEPORT, + &val as *const libc::c_int as *const libc::c_void, + size_of::() as libc::socklen_t, + )).map(|_| ()) +} + +#[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))] +pub(crate) fn get_reuseport(socket: TcpSocket) -> io::Result { + let mut optval: libc::c_int = 0; + let mut optlen = mem::size_of::() as libc::socklen_t; + + syscall!(getsockopt( + socket, + libc::SOL_SOCKET, + libc::SO_REUSEPORT, + &mut optval as *mut _ as *mut _, + &mut optlen, + ))?; + + Ok(optval != 0) +} + pub(crate) fn set_linger(socket: TcpSocket, dur: Option) -> io::Result<()> { let val: libc::linger = libc::linger { l_onoff: if dur.is_some() { 1 } else { 0 }, diff --git a/src/sys/windows/tcp.rs b/src/sys/windows/tcp.rs index 46ac1ac4f..42443f2af 100644 --- a/src/sys/windows/tcp.rs +++ b/src/sys/windows/tcp.rs @@ -8,7 +8,7 @@ use std::os::windows::raw::SOCKET as StdSocket; // winapi uses usize, stdlib use use winapi::ctypes::{c_char, c_int, c_ushort}; use winapi::shared::minwindef::{BOOL, TRUE, FALSE}; use winapi::um::winsock2::{ - self, closesocket, linger, setsockopt, PF_INET, PF_INET6, SOCKET, SOCKET_ERROR, + self, closesocket, linger, setsockopt, getsockopt, PF_INET, PF_INET6, SOCKET, SOCKET_ERROR, SOCK_STREAM, SOL_SOCKET, SO_LINGER, SO_REUSEADDR, }; @@ -87,6 +87,22 @@ pub(crate) fn set_reuseaddr(socket: TcpSocket, reuseaddr: bool) -> io::Result<() } } +pub(crate) fn get_reuseaddr(socket: TcpSocket) -> io::Result { + let mut optval: c_char = 0; + let mut optlen = size_of::() as c_int; + + match unsafe { getsockopt( + socket, + SOL_SOCKET, + SO_REUSEADDR, + &mut optval as *mut _ as *mut _, + &mut optlen, + ) } { + SOCKET_ERROR => Err(io::Error::last_os_error()), + _ => Ok(optval != 0), + } +} + pub(crate) fn set_linger(socket: TcpSocket, dur: Option) -> io::Result<()> { let val: linger = linger { l_onoff: if dur.is_some() { 1 } else { 0 }, diff --git a/tests/tcp_socket.rs b/tests/tcp_socket.rs index e741db92f..536e84b42 100644 --- a/tests/tcp_socket.rs +++ b/tests/tcp_socket.rs @@ -17,6 +17,22 @@ fn set_reuseaddr() { let socket = TcpSocket::new_v4().unwrap(); socket.set_reuseaddr(true).unwrap(); + assert!(socket.get_reuseaddr().unwrap()); + + socket.bind(addr).unwrap(); + + let _ = socket.listen(128).unwrap(); +} + +#[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))] +#[test] +fn set_reuseport() { + let addr = "127.0.0.1:0".parse().unwrap(); + + let socket = TcpSocket::new_v4().unwrap(); + socket.set_reuseport(true).unwrap(); + assert!(socket.get_reuseport().unwrap()); + socket.bind(addr).unwrap(); let _ = socket.listen(128).unwrap();