Skip to content

Commit

Permalink
Add SO_REUSEPORT support for TCP socket (#1375)
Browse files Browse the repository at this point in the history
  • Loading branch information
zekisherif authored Oct 24, 2020
1 parent 2b7c096 commit 183bbe4
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 1 deletion.
19 changes: 19 additions & 0 deletions src/net/tcp/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool> {
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<bool> {
sys::tcp::get_reuseport(self.sys)
}

/// Sets the value of `SO_LINGER` on this socket.
pub fn set_linger(&self, dur: Option<Duration>) -> io::Result<()> {
sys::tcp::set_linger(self.sys, dur)
Expand Down
14 changes: 14 additions & 0 deletions src/sys/shell/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ pub(crate) fn set_reuseaddr(_: TcpSocket, _: bool) -> io::Result<()> {
os_required!();
}

pub(crate) fn get_reuseaddr(_: TcpSocket) -> io::Result<bool> {
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<bool> {
os_required!();
}

pub(crate) fn set_linger(_: TcpSocket, _: Option<Duration>) -> io::Result<()> {
os_required!();
}
Expand Down
45 changes: 45 additions & 0 deletions src/sys/unix/tcp.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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<bool> {
let mut optval: libc::c_int = 0;
let mut optlen = mem::size_of::<libc::c_int>() 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::<libc::c_int>() 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<bool> {
let mut optval: libc::c_int = 0;
let mut optlen = mem::size_of::<libc::c_int>() 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<Duration>) -> io::Result<()> {
let val: libc::linger = libc::linger {
l_onoff: if dur.is_some() { 1 } else { 0 },
Expand Down
18 changes: 17 additions & 1 deletion src/sys/windows/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down Expand Up @@ -87,6 +87,22 @@ pub(crate) fn set_reuseaddr(socket: TcpSocket, reuseaddr: bool) -> io::Result<()
}
}

pub(crate) fn get_reuseaddr(socket: TcpSocket) -> io::Result<bool> {
let mut optval: c_char = 0;
let mut optlen = size_of::<BOOL>() 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<Duration>) -> io::Result<()> {
let val: linger = linger {
l_onoff: if dur.is_some() { 1 } else { 0 },
Expand Down
16 changes: 16 additions & 0 deletions tests/tcp_socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down

0 comments on commit 183bbe4

Please sign in to comment.