From 28da05437de8f15df379041a653ee72cf136204b Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Fri, 7 May 2021 03:05:09 +0300 Subject: Fix default binding behaviour On some platforms, binding to to both "::" and "0.0.0.0" at the same time is not allowed because "::" may already accepts ipv4 connections. For other platforms, binding to both is necessary to support ipv4 and ipv6. This platform-specific behaviour is due to the variation in the default value for the socket option "IPV6_ONLY". Fix this by always setting the "IPv6_ONLY" sockopt to true! --- Cargo.toml | 1 + src/main.rs | 27 ++++++++++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4efc015..f72597a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,7 @@ http = "0.2" bytes = "1" atty = "0.2" rustls = { version = "0.19", optional = true } +socket2 = "0.4" [features] default = ["tls"] diff --git a/src/main.rs b/src/main.rs index 19bd16b..d34f0aa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ use std::io; use std::io::Write; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener}; use std::thread; use std::time::Duration; @@ -200,7 +200,7 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { .interfaces .iter() .map(|&interface| SocketAddr::new(interface, miniserve_config.port)) - .collect::>(); + .collect::>(); let srv = actix_web::HttpServer::new(move || { App::new() @@ -222,14 +222,18 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { }); let srv = socket_addresses.iter().try_fold(srv, |srv, addr| { + let listener = create_tcp_listener(*addr).map_err(|e| { + ContextualError::IoError(format!("Failed to bind server to {}", addr), e) + })?; + #[cfg(feature = "tls")] let srv = match &miniserve_config.tls_rustls_config { - Some(tls_config) => srv.bind_rustls(addr, tls_config.clone()), - None => srv.bind(addr), + Some(tls_config) => srv.listen_rustls(listener, tls_config.clone()), + None => srv.listen(listener), }; #[cfg(not(feature = "tls"))] - let srv = srv.bind(addr); + let srv = srv.listen(listener); srv.map_err(|e| ContextualError::IoError(format!("Failed to bind server to {}", addr), e)) })?; @@ -250,6 +254,19 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { .map_err(|e| ContextualError::IoError("".to_owned(), e)) } +/// Allows us to set low-level socket options +fn create_tcp_listener(addr: SocketAddr) -> io::Result { + use socket2::{Domain, Protocol, Socket, Type}; + let socket = Socket::new(Domain::for_address(addr), Type::STREAM, Some(Protocol::TCP))?; + if addr.is_ipv6() { + socket.set_only_v6(true)?; + } + socket.set_reuse_address(true)?; + socket.bind(&addr.into())?; + socket.listen(1024 /* Default backlog */)?; + Ok(TcpListener::from(socket)) +} + fn configure_header(conf: &MiniserveConfig) -> middleware::DefaultHeaders { let headers = conf.clone().header; -- cgit v1.2.3