fix windows

This commit is contained in:
John Smith 2022-03-13 11:09:48 -04:00
parent c9bf4260c5
commit e729682804
8 changed files with 219 additions and 145 deletions

View File

@ -52,12 +52,12 @@ struct NetworkInner {
wss_port: u16,
interfaces: NetworkInterfaces,
// udp
bound_first_udp: BTreeMap<u16, (socket2::Socket, socket2::Socket)>,
bound_first_udp: BTreeMap<u16, Option<(socket2::Socket, socket2::Socket)>>,
inbound_udp_protocol_handlers: BTreeMap<SocketAddr, RawUdpProtocolHandler>,
outbound_udpv4_protocol_handler: Option<RawUdpProtocolHandler>,
outbound_udpv6_protocol_handler: Option<RawUdpProtocolHandler>,
//tcp
bound_first_tcp: BTreeMap<u16, (socket2::Socket, socket2::Socket)>,
bound_first_tcp: BTreeMap<u16, Option<(socket2::Socket, socket2::Socket)>>,
tls_acceptor: Option<TlsAcceptor>,
listener_states: BTreeMap<SocketAddr, Arc<RwLock<ListenerState>>>,
}

View File

@ -1,3 +1,4 @@
use super::sockets::*;
use super::*;
use crate::intf::*;
use crate::network_connection::*;

View File

@ -1,3 +1,4 @@
use super::sockets::*;
use super::*;
use futures_util::stream;

View File

@ -1,3 +1,4 @@
pub mod sockets;
pub mod tcp;
pub mod udp;
pub mod wrtc;
@ -6,7 +7,6 @@ pub mod ws;
use crate::network_connection::*;
use crate::xx::*;
use crate::*;
use socket2::{Domain, Protocol, Socket, Type};
#[derive(Debug)]
pub enum ProtocolNetworkConnection {
@ -85,141 +85,3 @@ impl ProtocolNetworkConnection {
}
}
}
pub fn new_unbound_shared_udp_socket(domain: Domain) -> Result<socket2::Socket, String> {
let socket = Socket::new(domain, Type::DGRAM, Some(Protocol::UDP))
.map_err(|e| format!("Couldn't create UDP socket: {}", e))?;
if domain == Domain::IPV6 {
socket
.set_only_v6(true)
.map_err(|e| format!("Couldn't set IPV6_V6ONLY: {}", e))?;
}
socket
.set_reuse_address(true)
.map_err(|e| format!("Couldn't set reuse address: {}", e))?;
cfg_if! {
if #[cfg(unix)] {
socket.set_reuse_port(true).map_err(|e| format!("Couldn't set reuse port: {}", e))?;
}
}
Ok(socket)
}
pub fn new_bound_shared_udp_socket(local_address: SocketAddr) -> Result<socket2::Socket, String> {
let domain = Domain::for_address(local_address);
let socket = new_unbound_shared_udp_socket(domain)?;
let socket2_addr = socket2::SockAddr::from(local_address);
socket
.bind(&socket2_addr)
.map_err(|e| format!("failed to bind UDP socket: {}", e))?;
log_net!("created shared udp socket on {:?}", &local_address);
Ok(socket)
}
pub fn new_bound_first_udp_socket(local_address: SocketAddr) -> Result<socket2::Socket, String> {
let domain = Domain::for_address(local_address);
let socket = Socket::new(domain, Type::DGRAM, Some(Protocol::UDP))
.map_err(|e| format!("Couldn't create UDP socket: {}", e))?;
if domain == Domain::IPV6 {
socket
.set_only_v6(true)
.map_err(|e| format!("Couldn't set IPV6_V6ONLY: {}", e))?;
}
// Bind the socket -first- before turning on 'reuse address' this way it will
// fail if the port is already taken
let socket2_addr = socket2::SockAddr::from(local_address);
socket
.bind(&socket2_addr)
.map_err(|e| format!("failed to bind UDP socket: {}", e))?;
// Set 'reuse address' so future binds to this port will succeed
socket
.set_reuse_address(true)
.map_err(|e| format!("Couldn't set reuse address: {}", e))?;
cfg_if! {
if #[cfg(unix)] {
socket.set_reuse_port(true).map_err(|e| format!("Couldn't set reuse port: {}", e))?;
}
}
log_net!("created shared udp socket on {:?}", &local_address);
Ok(socket)
}
pub fn new_unbound_shared_tcp_socket(domain: Domain) -> Result<socket2::Socket, String> {
let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))
.map_err(map_to_string)
.map_err(logthru_net!("failed to create TCP socket"))?;
if let Err(e) = socket.set_linger(None) {
log_net!(error "Couldn't set TCP linger: {}", e);
}
if let Err(e) = socket.set_nodelay(true) {
log_net!(error "Couldn't set TCP nodelay: {}", e);
}
if domain == Domain::IPV6 {
socket
.set_only_v6(true)
.map_err(|e| format!("Couldn't set IPV6_V6ONLY: {}", e))?;
}
socket
.set_reuse_address(true)
.map_err(|e| format!("Couldn't set reuse address: {}", e))?;
cfg_if! {
if #[cfg(unix)] {
socket.set_reuse_port(true).map_err(|e| format!("Couldn't set reuse port: {}", e))?;
}
}
Ok(socket)
}
pub fn new_bound_shared_tcp_socket(local_address: SocketAddr) -> Result<socket2::Socket, String> {
let domain = Domain::for_address(local_address);
let socket = new_unbound_shared_tcp_socket(domain)?;
let socket2_addr = socket2::SockAddr::from(local_address);
socket
.bind(&socket2_addr)
.map_err(|e| format!("failed to bind TCP socket: {}", e))?;
Ok(socket)
}
pub fn new_bound_first_tcp_socket(local_address: SocketAddr) -> Result<socket2::Socket, String> {
let domain = Domain::for_address(local_address);
let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))
.map_err(map_to_string)
.map_err(logthru_net!("failed to create TCP socket"))?;
if let Err(e) = socket.set_linger(None) {
log_net!(error "Couldn't set TCP linger: {}", e);
}
if let Err(e) = socket.set_nodelay(true) {
log_net!(error "Couldn't set TCP nodelay: {}", e);
}
if domain == Domain::IPV6 {
socket
.set_only_v6(true)
.map_err(|e| format!("Couldn't set IPV6_V6ONLY: {}", e))?;
}
// Bind the socket -first- before turning on 'reuse address' this way it will
// fail if the port is already taken
let socket2_addr = socket2::SockAddr::from(local_address);
socket
.bind(&socket2_addr)
.map_err(|e| format!("failed to bind TCP socket: {}", e))?;
// Set 'reuse address' so future binds to this port will succeed
socket
.set_reuse_address(true)
.map_err(|e| format!("Couldn't set reuse address: {}", e))?;
cfg_if! {
if #[cfg(unix)] {
socket.set_reuse_port(true).map_err(|e| format!("Couldn't set reuse port: {}", e))?;
}
}
Ok(socket)
}

View File

@ -0,0 +1,181 @@
use crate::xx::*;
use crate::*;
use socket2::{Domain, Protocol, Socket, Type};
cfg_if! {
if #[cfg(windows)] {
use winapi::shared::ws2def::{ SOL_SOCKET, SO_EXCLUSIVEADDRUSE};
use winapi::um::winsock2::{SOCKET_ERROR, setsockopt};
use winapi::ctypes::c_int;
use std::os::windows::io::AsRawSocket;
fn set_exclusiveaddruse(socket: &Socket) -> Result<(), String> {
unsafe {
let optval:c_int = 1;
if setsockopt(socket.as_raw_socket().try_into().unwrap(), SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (&optval as *const c_int).cast(),
std::mem::size_of::<c_int>() as c_int) == SOCKET_ERROR {
return Err("Unable to SO_EXCLUSIVEADDRUSE".to_owned());
}
Ok(())
}
}
}
}
pub fn new_unbound_shared_udp_socket(domain: Domain) -> Result<Socket, String> {
let socket = Socket::new(domain, Type::DGRAM, Some(Protocol::UDP))
.map_err(|e| format!("Couldn't create UDP socket: {}", e))?;
if domain == Domain::IPV6 {
socket
.set_only_v6(true)
.map_err(|e| format!("Couldn't set IPV6_V6ONLY: {}", e))?;
}
socket
.set_reuse_address(true)
.map_err(|e| format!("Couldn't set reuse address: {}", e))?;
cfg_if! {
if #[cfg(unix)] {
socket.set_reuse_port(true).map_err(|e| format!("Couldn't set reuse port: {}", e))?;
}
}
Ok(socket)
}
pub fn new_bound_shared_udp_socket(local_address: SocketAddr) -> Result<Socket, String> {
let domain = Domain::for_address(local_address);
let socket = new_unbound_shared_udp_socket(domain)?;
let socket2_addr = socket2::SockAddr::from(local_address);
socket.bind(&socket2_addr).map_err(|e| {
format!(
"failed to bind UDP socket to '{}' in domain '{:?}': {} ",
local_address, domain, e
)
})?;
log_net!("created shared udp socket on {:?}", &local_address);
Ok(socket)
}
pub fn new_bound_first_udp_socket(local_address: SocketAddr) -> Result<Socket, String> {
let domain = Domain::for_address(local_address);
let socket = Socket::new(domain, Type::DGRAM, Some(Protocol::UDP))
.map_err(|e| format!("Couldn't create UDP socket: {}", e))?;
if domain == Domain::IPV6 {
socket
.set_only_v6(true)
.map_err(|e| format!("Couldn't set IPV6_V6ONLY: {}", e))?;
}
// Bind the socket -first- before turning on 'reuse address' this way it will
// fail if the port is already taken
let socket2_addr = socket2::SockAddr::from(local_address);
// On windows, do SO_EXCLUSIVEADDRUSE before the bind to ensure the port is fully available
cfg_if! {
if #[cfg(windows)] {
set_exclusiveaddruse(&socket)?;
}
}
socket
.bind(&socket2_addr)
.map_err(|e| format!("failed to bind UDP socket: {}", e))?;
// Set 'reuse address' so future binds to this port will succeed
// This does not work on Windows, where reuse options can not be set after the bind
cfg_if! {
if #[cfg(unix)] {
socket
.set_reuse_address(true)
.map_err(|e| format!("Couldn't set reuse address: {}", e))?;
socket.set_reuse_port(true).map_err(|e| format!("Couldn't set reuse port: {}", e))?;
}
}
log_net!("created shared udp socket on {:?}", &local_address);
Ok(socket)
}
pub fn new_unbound_shared_tcp_socket(domain: Domain) -> Result<Socket, String> {
let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))
.map_err(map_to_string)
.map_err(logthru_net!("failed to create TCP socket"))?;
if let Err(e) = socket.set_linger(None) {
log_net!(error "Couldn't set TCP linger: {}", e);
}
if let Err(e) = socket.set_nodelay(true) {
log_net!(error "Couldn't set TCP nodelay: {}", e);
}
if domain == Domain::IPV6 {
socket
.set_only_v6(true)
.map_err(|e| format!("Couldn't set IPV6_V6ONLY: {}", e))?;
}
socket
.set_reuse_address(true)
.map_err(|e| format!("Couldn't set reuse address: {}", e))?;
cfg_if! {
if #[cfg(unix)] {
socket.set_reuse_port(true).map_err(|e| format!("Couldn't set reuse port: {}", e))?;
}
}
Ok(socket)
}
pub fn new_bound_shared_tcp_socket(local_address: SocketAddr) -> Result<Socket, String> {
let domain = Domain::for_address(local_address);
let socket = new_unbound_shared_tcp_socket(domain)?;
let socket2_addr = socket2::SockAddr::from(local_address);
socket
.bind(&socket2_addr)
.map_err(|e| format!("failed to bind TCP socket: {}", e))?;
Ok(socket)
}
pub fn new_bound_first_tcp_socket(local_address: SocketAddr) -> Result<Socket, String> {
let domain = Domain::for_address(local_address);
let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))
.map_err(map_to_string)
.map_err(logthru_net!("failed to create TCP socket"))?;
if let Err(e) = socket.set_linger(None) {
log_net!(error "Couldn't set TCP linger: {}", e);
}
if let Err(e) = socket.set_nodelay(true) {
log_net!(error "Couldn't set TCP nodelay: {}", e);
}
if domain == Domain::IPV6 {
socket
.set_only_v6(true)
.map_err(|e| format!("Couldn't set IPV6_V6ONLY: {}", e))?;
}
// On windows, do SO_EXCLUSIVEADDRUSE before the bind to ensure the port is fully available
cfg_if! {
if #[cfg(windows)] {
set_exclusiveaddruse(&socket)?;
}
}
// Bind the socket -first- before turning on 'reuse address' this way it will
// fail if the port is already taken
let socket2_addr = socket2::SockAddr::from(local_address);
socket
.bind(&socket2_addr)
.map_err(|e| format!("failed to bind TCP socket: {}", e))?;
// Set 'reuse address' so future binds to this port will succeed
// This does not work on Windows, where reuse options can not be set after the bind
cfg_if! {
if #[cfg(unix)] {
socket
.set_reuse_address(true)
.map_err(|e| format!("Couldn't set reuse address: {}", e))?;
socket.set_reuse_port(true).map_err(|e| format!("Couldn't set reuse port: {}", e))?;
}
}
Ok(socket)
}

View File

@ -1,3 +1,4 @@
use super::sockets::*;
use super::*;
use crate::intf::*;
use crate::network_manager::MAX_MESSAGE_SIZE;
@ -139,7 +140,9 @@ impl RawTcpProtocolHandler {
// Make a shared socket
let socket = match local_address {
Some(a) => new_bound_shared_tcp_socket(a)?,
None => new_unbound_shared_tcp_socket(Domain::for_address(remote_socket_addr))?,
None => {
new_unbound_shared_tcp_socket(socket2::Domain::for_address(remote_socket_addr))?
}
};
// Connect to the remote address

View File

@ -1,3 +1,4 @@
use super::sockets::*;
use super::*;
use crate::intf::*;
use crate::network_manager::MAX_MESSAGE_SIZE;
@ -207,7 +208,9 @@ impl WebsocketProtocolHandler {
// Make a shared socket
let socket = match local_address {
Some(a) => new_bound_shared_tcp_socket(a)?,
None => new_unbound_shared_tcp_socket(Domain::for_address(remote_socket_addr))?,
None => {
new_unbound_shared_tcp_socket(socket2::Domain::for_address(remote_socket_addr))?
}
};
// Connect to the remote address

View File

@ -1,3 +1,4 @@
use super::sockets::*;
use super::*;
impl Network {
@ -26,7 +27,18 @@ impl Network {
}
}
if let (Some(bfs4), Some(bfs6)) = (bound_first_socket_v4, bound_first_socket_v6) {
inner.bound_first_udp.insert(udp_port, (bfs4, bfs6));
cfg_if! {
if #[cfg(windows)] {
// On windows, drop the socket. This is a race condition, but there's
// no way around it. This isn't for security anyway, it's to prevent multiple copies of the
// app from binding on the same port.
drop(bfs4);
drop(bfs6);
inner.bound_first_udp.insert(udp_port, None);
} else {
inner.bound_first_udp.insert(udp_port, Some(bfs4, bfs6));
}
}
true
} else {
false
@ -53,7 +65,18 @@ impl Network {
}
}
if let (Some(bfs4), Some(bfs6)) = (bound_first_socket_v4, bound_first_socket_v6) {
inner.bound_first_tcp.insert(tcp_port, (bfs4, bfs6));
cfg_if! {
if #[cfg(windows)] {
// On windows, drop the socket. This is a race condition, but there's
// no way around it. This isn't for security anyway, it's to prevent multiple copies of the
// app from binding on the same port.
drop(bfs4);
drop(bfs6);
inner.bound_first_tcp.insert(tcp_port, None);
} else {
inner.bound_first_tcp.insert(tcp_port, Some(bfs4, bfs6));
}
}
true
} else {
false