diff --git a/Cargo.lock b/Cargo.lock index 953f8bd1..067245dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,6 +95,8 @@ dependencies = [ "parking_lot", "rand_core 0.6.3", "ring", + "socket2", + "thiserror", "tracing", "tracing-subscriber", "untrusted 0.9.0", @@ -980,6 +982,16 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "spin" version = "0.5.2" diff --git a/boringtun-cli/Cargo.toml b/boringtun-cli/Cargo.toml index b0461af0..26450d6f 100644 --- a/boringtun-cli/Cargo.toml +++ b/boringtun-cli/Cargo.toml @@ -17,4 +17,5 @@ tracing-appender = "0.2.1" [dependencies.boringtun] version = "0.5.2" -path = "../boringtun" \ No newline at end of file +path = "../boringtun" +features = ["device"] \ No newline at end of file diff --git a/boringtun/Cargo.toml b/boringtun/Cargo.toml index 95ccf5d1..38c86106 100644 --- a/boringtun/Cargo.toml +++ b/boringtun/Cargo.toml @@ -9,6 +9,8 @@ documentation = "https://docs.rs/boringtun/0.5.2/boringtun/" edition = "2018" [features] +default = [] +device = ["socket2", "thiserror"] jni-bindings = ["ffi-bindings", "jni"] ffi-bindings = ["tracing-subscriber"] # mocks std::time::Instant with mock_instant @@ -33,9 +35,14 @@ blake2 = "0.10" hmac = "0.12" jni = { version = "0.19.0", optional = true } mock_instant = { version = "0.2", optional = true } +socket2 = { version = "0.4.7", features = ["all"], optional = true } +thiserror = { version = "1", optional = true } [target.'cfg(unix)'.dependencies] -nix = { version = "0.25", default-features = false, features = ["time", "user"] } +nix = { version = "0.25", default-features = false, features = [ + "time", + "user", +] } [dev-dependencies] etherparse = "0.12" diff --git a/boringtun/src/device/api.rs b/boringtun/src/device/api.rs index ed53aa5b..0486de68 100644 --- a/boringtun/src/device/api.rs +++ b/boringtun/src/device/api.rs @@ -236,6 +236,11 @@ fn api_set(reader: &mut BufReader<&UnixStream>, d: &mut LockReadGuard) - }, Err(_) => return EINVAL, }, + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux" + ))] "fwmark" => match val.parse::() { Ok(mark) => match device.set_fwmark(mark) { Ok(()) => {} diff --git a/boringtun/src/device/drop_privileges.rs b/boringtun/src/device/drop_privileges.rs index 6557319c..621c4d44 100644 --- a/boringtun/src/device/drop_privileges.rs +++ b/boringtun/src/device/drop_privileges.rs @@ -1,9 +1,9 @@ // Copyright (c) 2019 Cloudflare, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause -use crate::device::errno_str; use crate::device::Error; -use libc::*; +use libc::{gid_t, setgid, setuid, uid_t}; +use std::io; #[cfg(target_os = "macos")] use nix::unistd::User; @@ -27,6 +27,8 @@ pub fn get_saved_ids() -> Result<(uid_t, gid_t), Error> { } #[cfg(not(target_os = "macos"))] { + use libc::{getlogin, getpwnam}; + let uname = unsafe { getlogin() }; if uname.is_null() { return Err(Error::DropPrivileges("NULL from getlogin".to_owned())); @@ -50,12 +52,16 @@ pub fn drop_privileges() -> Result<(), Error> { if -1 == unsafe { setgid(saved_gid) } { // Set real and effective group ID - return Err(Error::DropPrivileges(errno_str())); + return Err(Error::DropPrivileges( + io::Error::last_os_error().to_string(), + )); } if -1 == unsafe { setuid(saved_uid) } { // Set real and effective user ID - return Err(Error::DropPrivileges(errno_str())); + return Err(Error::DropPrivileges( + io::Error::last_os_error().to_string(), + )); } // Validated we can't get sudo back again diff --git a/boringtun/src/device/epoll.rs b/boringtun/src/device/epoll.rs index e8219d5f..b6ecaf0b 100644 --- a/boringtun/src/device/epoll.rs +++ b/boringtun/src/device/epoll.rs @@ -1,9 +1,10 @@ // Copyright (c) 2019 Cloudflare, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause -use super::{errno_str, Error}; +use super::Error; use libc::*; use parking_lot::Mutex; +use std::io; use std::ops::Deref; use std::os::unix::io::RawFd; use std::ptr::null_mut; @@ -57,7 +58,7 @@ impl EventPoll { /// Create a new event registry pub fn new() -> Result, Error> { let epoll = match unsafe { epoll_create(1) } { - -1 => return Err(Error::EventQueue(errno_str())), + -1 => return Err(Error::EventQueue(io::Error::last_os_error())), epoll => epoll, }; @@ -125,7 +126,7 @@ impl EventPoll { let tfd = match unsafe { timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK) } { -1 => match unsafe { timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK) } { // A fallback for kernels < 3.15 - -1 => return Err(Error::Timer(errno_str())), + -1 => return Err(Error::Timer(io::Error::last_os_error())), efd => efd, }, efd => efd, @@ -143,7 +144,7 @@ impl EventPoll { if unsafe { timerfd_settime(tfd, 0, &spec, std::ptr::null_mut()) } == -1 { unsafe { close(tfd) }; - return Err(Error::Timer(errno_str())); + return Err(Error::Timer(io::Error::last_os_error())); } let ev = Event { @@ -171,7 +172,7 @@ impl EventPoll { // canceled. // When we want to stop the event, we read something once from the file descriptor. let efd = match unsafe { eventfd(0, EFD_NONBLOCK) } { - -1 => return Err(Error::EventQueue(errno_str())), + -1 => return Err(Error::EventQueue(io::Error::last_os_error())), efd => efd, }; @@ -198,7 +199,7 @@ impl EventPoll { sigprocmask(SIG_BLOCK, &sigset, null_mut()); signalfd(-1, &sigset, SFD_NONBLOCK) } { - -1 => return Err(Error::EventQueue(errno_str())), + -1 => return Err(Error::EventQueue(io::Error::last_os_error())), sfd => sfd, }; @@ -223,7 +224,7 @@ impl EventPoll { pub fn wait(&self) -> WaitResult<'_, H> { let mut event = epoll_event { events: 0, u64: 0 }; match unsafe { epoll_wait(self.epoll, &mut event, 1, -1) } { - -1 => return WaitResult::Error(errno_str()), + -1 => return WaitResult::Error(io::Error::last_os_error().to_string()), 1 => {} _ => return WaitResult::Error("unexpected number of events returned".to_string()), } @@ -260,7 +261,7 @@ impl EventPoll { self.insert_at(trigger as _, ev); // Add the event to epoll if unsafe { epoll_ctl(self.epoll, EPOLL_CTL_ADD, trigger, &mut event_desc) } == -1 { - return Err(Error::EventQueue(errno_str())); + return Err(Error::EventQueue(io::Error::last_os_error())); } Ok(EventRef { trigger }) @@ -405,10 +406,10 @@ pub fn block_signal(signal: c_int) -> Result { let mut sigset = std::mem::zeroed(); sigemptyset(&mut sigset); if sigaddset(&mut sigset, signal) == -1 { - return Err(errno_str()); + return Err(io::Error::last_os_error().to_string()); } if sigprocmask(SIG_BLOCK, &sigset, null_mut()) == -1 { - return Err(errno_str()); + return Err(io::Error::last_os_error().to_string()); } Ok(sigset) } diff --git a/boringtun/src/device/kqueue.rs b/boringtun/src/device/kqueue.rs index 932c9ee7..638665ef 100644 --- a/boringtun/src/device/kqueue.rs +++ b/boringtun/src/device/kqueue.rs @@ -1,9 +1,10 @@ // Copyright (c) 2019 Cloudflare, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause -use super::{errno_str, Error}; +use super::Error; use libc::*; use parking_lot::Mutex; +use std::io; use std::ops::Deref; use std::os::unix::io::RawFd; use std::ptr::{null, null_mut}; @@ -69,7 +70,7 @@ impl EventPoll { /// Create a new event registry pub fn new() -> Result, Error> { let kqueue = match unsafe { kqueue() } { - -1 => return Err(Error::EventQueue(errno_str())), + -1 => return Err(Error::EventQueue(io::Error::last_os_error())), kqueue => kqueue, }; @@ -187,7 +188,7 @@ impl EventPoll { }; if unsafe { kevent(self.kqueue, null(), 0, &mut event, 1, null()) } == -1 { - return WaitResult::Error(errno_str()); + return WaitResult::Error(io::Error::last_os_error().to_string()); } let event_data = unsafe { (event.udata as *mut Event).as_ref().unwrap() }; @@ -234,7 +235,7 @@ impl EventPoll { kev.flags |= EV_ADD; if unsafe { kevent(self.kqueue, &kev, 1, null_mut(), 0, null()) } == -1 { - return Err(Error::EventQueue(errno_str())); + return Err(Error::EventQueue(io::Error::last_os_error())); } if let Some(mut event) = events[index].take() { diff --git a/boringtun/src/device/mod.rs b/boringtun/src/device/mod.rs index b73fa8c5..bc14c896 100644 --- a/boringtun/src/device/mod.rs +++ b/boringtun/src/device/mod.rs @@ -25,13 +25,10 @@ pub mod tun; #[path = "tun_linux.rs"] pub mod tun; -#[cfg(unix)] -#[path = "udp_unix.rs"] -pub mod udp; - use std::collections::HashMap; -use std::io; -use std::net::{IpAddr, SocketAddr}; +use std::io::{self, Write as _}; +use std::mem::MaybeUninit; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; use std::os::unix::io::AsRawFd; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; @@ -48,8 +45,8 @@ use parking_lot::Mutex; use peer::{AllowedIP, Peer}; use poll::{EventPoll, EventRef, WaitResult}; use rand_core::{OsRng, RngCore}; -use tun::{errno, errno_str, TunSocket}; -use udp::UDPSocket; +use socket2::{Domain, Protocol, Type}; +use tun::TunSocket; use dev_lock::{Lock, LockReadGuard}; @@ -58,25 +55,40 @@ const HANDSHAKE_RATE_LIMIT: u64 = 100; // The number of handshakes per second we const MAX_UDP_SIZE: usize = (1 << 16) - 1; const MAX_ITR: usize = 100; // Number of packets to handle per handler call -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] pub enum Error { - Socket(String), + #[error("i/o error: {0}")] + IoError(#[from] io::Error), + #[error("{0}")] + Socket(io::Error), + #[error("{0}")] Bind(String), - FCntl(String), - EventQueue(String), - IOCtl(String), + #[error("{0}")] + FCntl(io::Error), + #[error("{0}")] + EventQueue(io::Error), + #[error("{0}")] + IOCtl(io::Error), + #[error("{0}")] Connect(String), + #[error("{0}")] SetSockOpt(String), + #[error("Invalid tunnel name")] InvalidTunnelName, #[cfg(any(target_os = "macos", target_os = "ios"))] - GetSockOpt(String), + #[error("{0}")] + GetSockOpt(io::Error), + #[error("{0}")] GetSockName(String), - UDPRead(i32), #[cfg(target_os = "linux")] - Timer(String), - IfaceRead(i32), + #[error("{0}")] + Timer(io::Error), + #[error("iface read: {0}")] + IfaceRead(io::Error), + #[error("{0}")] DropPrivileges(String), - ApiSocket(std::io::Error), + #[error("API socket error: {0}")] + ApiSocket(io::Error), } // What the event loop should do after a handler returns @@ -125,8 +137,8 @@ pub struct Device { fwmark: Option, iface: Arc, - udp4: Option>, - udp6: Option>, + udp4: Option, + udp6: Option, yield_notice: Option, exit_notice: Option, @@ -415,27 +427,23 @@ impl Device { } // Then open new sockets and bind to the port - let udp_sock4 = Arc::new( - UDPSocket::new()? - .set_non_blocking()? - .set_reuse()? - .bind(port)?, - ); + let udp_sock4 = socket2::Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP))?; + udp_sock4.set_reuse_address(true)?; + udp_sock4.bind(&SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, port).into())?; + udp_sock4.set_nonblocking(true)?; if port == 0 { // Random port was assigned - port = udp_sock4.port()?; + port = udp_sock4.local_addr()?.as_socket().unwrap().port(); } - let udp_sock6 = Arc::new( - UDPSocket::new6()? - .set_non_blocking()? - .set_reuse()? - .bind(port)?, - ); + let udp_sock6 = socket2::Socket::new(Domain::IPV6, Type::DGRAM, Some(Protocol::UDP))?; + udp_sock6.set_reuse_address(true)?; + udp_sock6.bind(&SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, port, 0, 0).into())?; + udp_sock6.set_nonblocking(true)?; - self.register_udp_handler(Arc::clone(&udp_sock4))?; - self.register_udp_handler(Arc::clone(&udp_sock6))?; + self.register_udp_handler(udp_sock4.try_clone().unwrap())?; + self.register_udp_handler(udp_sock6.try_clone().unwrap())?; self.udp4 = Some(udp_sock4); self.udp6 = Some(udp_sock6); @@ -485,22 +493,23 @@ impl Device { } } + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] fn set_fwmark(&mut self, mark: u32) -> Result<(), Error> { self.fwmark = Some(mark); // First set fwmark on listeners if let Some(ref sock) = self.udp4 { - sock.set_fwmark(mark)?; + sock.set_mark(mark)?; } if let Some(ref sock) = self.udp6 { - sock.set_fwmark(mark)?; + sock.set_mark(mark)?; } // Then on all currently connected sockets for peer in self.peers.values() { if let Some(ref sock) = peer.lock().endpoint().conn { - sock.set_fwmark(mark)? + sock.set_mark(mark)? } } @@ -566,8 +575,12 @@ impl Device { TunnResult::Err(e) => tracing::error!(message = "Timer error", error = ?e), TunnResult::WriteToNetwork(packet) => { match endpoint_addr { - SocketAddr::V4(_) => udp4.sendto(packet, endpoint_addr), - SocketAddr::V6(_) => udp6.sendto(packet, endpoint_addr), + SocketAddr::V4(_) => { + udp4.send_to(packet, &endpoint_addr.into()).ok() + } + SocketAddr::V6(_) => { + udp6.send_to(packet, &endpoint_addr.into()).ok() + } }; } _ => panic!("Unexpected result from update_timers"), @@ -595,7 +608,7 @@ impl Device { .stop_notification(self.yield_notice.as_ref().unwrap()) } - fn register_udp_handler(&self, udp: Arc) -> Result<(), Error> { + fn register_udp_handler(&self, udp: socket2::Socket) -> Result<(), Error> { self.queue.new_event( udp.as_raw_fd(), Box::new(move |d, t| { @@ -606,17 +619,26 @@ impl Device { let rate_limiter = d.rate_limiter.as_ref().unwrap(); // Loop while we have packets on the anonymous connection - while let Ok((addr, packet)) = udp.recvfrom(&mut t.src_buf[..]) { + + // Safety: the `recv_from` implementation promises not to write uninitialised + // bytes to the buffer, so this casting is safe. + let src_buf = + unsafe { &mut *(&mut t.src_buf[..] as *mut [u8] as *mut [MaybeUninit]) }; + while let Ok((packet_len, addr)) = udp.recv_from(src_buf) { + let packet = &t.src_buf[..packet_len]; // The rate limiter initially checks mac1 and mac2, and optionally asks to send a cookie - let parsed_packet = - match rate_limiter.verify_packet(Some(addr.ip()), packet, &mut t.dst_buf) { - Ok(packet) => packet, - Err(TunnResult::WriteToNetwork(cookie)) => { - udp.sendto(cookie, addr); - continue; - } - Err(_) => continue, - }; + let parsed_packet = match rate_limiter.verify_packet( + Some(addr.as_socket().unwrap().ip()), + packet, + &mut t.dst_buf, + ) { + Ok(packet) => packet, + Err(TunnResult::WriteToNetwork(cookie)) => { + let _: Result<_, _> = udp.send_to(cookie, &addr); + continue; + } + Err(_) => continue, + }; let peer = match &parsed_packet { Packet::HandshakeInit(p) => { @@ -648,7 +670,7 @@ impl Device { TunnResult::Err(_) => continue, TunnResult::WriteToNetwork(packet) => { flush = true; - udp.sendto(packet, addr); + let _: Result<_, _> = udp.send_to(packet, &addr); } TunnResult::WriteToTunnelV4(packet, addr) => { if p.is_allowed_ip(addr) { @@ -667,11 +689,12 @@ impl Device { while let TunnResult::WriteToNetwork(packet) = p.tunnel.decapsulate(None, &[], &mut t.dst_buf[..]) { - udp.sendto(packet, addr); + let _: Result<_, _> = udp.send_to(packet, &addr); } } // This packet was OK, that means we want to create a connected socket for this peer + let addr = addr.as_socket().unwrap(); let ip_addr = addr.ip(); p.set_endpoint(addr); if d.config.use_connected_socket { @@ -695,7 +718,7 @@ impl Device { fn register_conn_handler( &self, peer: Arc>, - udp: Arc, + udp: socket2::Socket, peer_addr: IpAddr, ) -> Result<(), Error> { self.queue.new_event( @@ -707,18 +730,24 @@ impl Device { let iface = &t.iface; let mut iter = MAX_ITR; - while let Ok(src) = udp.read(&mut t.src_buf[..]) { + // Safety: the `recv_from` implementation promises not to write uninitialised + // bytes to the buffer, so this casting is safe. + let src_buf = + unsafe { &mut *(&mut t.src_buf[..] as *mut [u8] as *mut [MaybeUninit]) }; + + while let Ok(read_bytes) = udp.recv(src_buf) { let mut flush = false; let mut p = peer.lock(); - match p - .tunnel - .decapsulate(Some(peer_addr), src, &mut t.dst_buf[..]) - { + match p.tunnel.decapsulate( + Some(peer_addr), + &t.src_buf[..read_bytes], + &mut t.dst_buf[..], + ) { TunnResult::Done => {} TunnResult::Err(e) => eprintln!("Decapsulate error {:?}", e), TunnResult::WriteToNetwork(packet) => { flush = true; - udp.write(packet); + let _: Result<_, _> = udp.send(packet); } TunnResult::WriteToTunnelV4(packet, addr) => { if p.is_allowed_ip(addr) { @@ -737,7 +766,7 @@ impl Device { while let TunnResult::WriteToNetwork(packet) = p.tunnel.decapsulate(None, &[], &mut t.dst_buf[..]) { - udp.write(packet); + let _: Result<_, _> = udp.send(packet); } } @@ -771,12 +800,12 @@ impl Device { for _ in 0..MAX_ITR { let src = match iface.read(&mut t.src_buf[..mtu]) { Ok(src) => src, - Err(Error::IfaceRead(errno)) => { - let ek = io::Error::from_raw_os_error(errno).kind(); + Err(Error::IfaceRead(e)) => { + let ek = e.kind(); if ek == io::ErrorKind::Interrupted || ek == io::ErrorKind::WouldBlock { break; } - eprintln!("Fatal read error on tun interface: errno {:?}", errno); + eprintln!("Fatal read error on tun interface: {:?}", e); return Action::Exit; } Err(e) => { @@ -801,14 +830,14 @@ impl Device { tracing::error!(message = "Encapsulate error", error = ?e) } TunnResult::WriteToNetwork(packet) => { - let endpoint = peer.endpoint(); - if let Some(ref conn) = endpoint.conn { + let mut endpoint = peer.endpoint_mut(); + if let Some(conn) = endpoint.conn.as_mut() { // Prefer to send using the connected socket - conn.write(packet); + let _: Result<_, _> = conn.write(packet); } else if let Some(addr @ SocketAddr::V4(_)) = endpoint.addr { - udp4.sendto(packet, addr); + let _: Result<_, _> = udp4.send_to(packet, &addr.into()); } else if let Some(addr @ SocketAddr::V6(_)) = endpoint.addr { - udp6.sendto(packet, addr); + let _: Result<_, _> = udp6.send_to(packet, &addr.into()); } else { tracing::error!("No endpoint"); } diff --git a/boringtun/src/device/peer.rs b/boringtun/src/device/peer.rs index 10771c25..d7f2c22e 100644 --- a/boringtun/src/device/peer.rs +++ b/boringtun/src/device/peer.rs @@ -2,21 +2,18 @@ // SPDX-License-Identifier: BSD-3-Clause use parking_lot::RwLock; +use socket2::{Domain, Protocol, Type}; -use std::net::IpAddr; -use std::net::SocketAddr; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6}; use std::str::FromStr; -use std::sync::Arc; use crate::device::{AllowedIps, Error}; use crate::noise::{Tunn, TunnResult}; -use crate::device::udp::UDPSocket; - #[derive(Default, Debug)] pub struct Endpoint { pub addr: Option, - pub conn: Option>, + pub conn: Option, } pub struct Peer { @@ -81,10 +78,14 @@ impl Peer { self.endpoint.read() } + pub(crate) fn endpoint_mut(&self) -> parking_lot::RwLockWriteGuard<'_, Endpoint> { + self.endpoint.write() + } + pub fn shutdown_endpoint(&self) { if let Some(conn) = self.endpoint.write().conn.take() { tracing::info!("Disconnecting from endpoint"); - conn.shutdown(); + conn.shutdown(Shutdown::Both).unwrap(); } } @@ -93,43 +94,43 @@ impl Peer { if endpoint.addr != Some(addr) { // We only need to update the endpoint if it differs from the current one if let Some(conn) = endpoint.conn.take() { - conn.shutdown(); + conn.shutdown(Shutdown::Both).unwrap(); } - *endpoint = Endpoint { - addr: Some(addr), - conn: None, - } - }; + endpoint.addr = Some(addr); + } } pub fn connect_endpoint( &self, port: u16, fwmark: Option, - ) -> Result, Error> { + ) -> Result { let mut endpoint = self.endpoint.write(); if endpoint.conn.is_some() { return Err(Error::Connect("Connected".to_owned())); } - let udp_conn = Arc::new(match endpoint.addr { - Some(addr @ SocketAddr::V4(_)) => UDPSocket::new()? - .set_non_blocking()? - .set_reuse()? - .bind(port)? - .connect(&addr)?, - Some(addr @ SocketAddr::V6(_)) => UDPSocket::new6()? - .set_non_blocking()? - .set_reuse()? - .bind(port)? - .connect(&addr)?, - None => panic!("Attempt to connect to undefined endpoint"), - }); + let addr = endpoint + .addr + .expect("Attempt to connect to undefined endpoint"); + + let udp_conn = + socket2::Socket::new(Domain::for_address(addr), Type::STREAM, Some(Protocol::UDP))?; + udp_conn.set_reuse_address(true)?; + let bind_addr = if addr.is_ipv4() { + SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, port).into() + } else { + SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, port, 0, 0).into() + }; + udp_conn.bind(&bind_addr)?; + udp_conn.connect(&addr.into())?; + udp_conn.set_nonblocking(true)?; + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] if let Some(fwmark) = fwmark { - udp_conn.set_fwmark(fwmark)?; + udp_conn.set_mark(fwmark)?; } tracing::info!( @@ -138,7 +139,7 @@ impl Peer { endpoint=?endpoint.addr.unwrap() ); - endpoint.conn = Some(Arc::clone(&udp_conn)); + endpoint.conn = Some(udp_conn.try_clone().unwrap()); Ok(udp_conn) } diff --git a/boringtun/src/device/tun_darwin.rs b/boringtun/src/device/tun_darwin.rs index 5f86cfe1..0732e952 100644 --- a/boringtun/src/device/tun_darwin.rs +++ b/boringtun/src/device/tun_darwin.rs @@ -3,21 +3,12 @@ use super::Error; use libc::*; +use std::io; use std::mem::size_of; use std::mem::size_of_val; use std::os::unix::io::{AsRawFd, RawFd}; use std::ptr::null_mut; -pub fn errno() -> i32 { - unsafe { *__error() } -} - -pub fn errno_str() -> String { - let strerr = unsafe { strerror(*__error()) }; - let c_str = unsafe { std::ffi::CStr::from_ptr(strerr) }; - c_str.to_string_lossy().into_owned() -} - const CTRL_NAME: &[u8] = b"com.apple.net.utun_control"; #[repr(C)] @@ -128,7 +119,7 @@ impl TunSocket { let idx = parse_utun_name(name)?; let fd = match unsafe { socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL) } { - -1 => return Err(Error::Socket(errno_str())), + -1 => return Err(Error::Socket(io::Error::last_os_error())), fd => fd, }; @@ -140,7 +131,7 @@ impl TunSocket { if unsafe { ioctl(fd, CTLIOCGINFO, &mut info as *mut ctl_info) } < 0 { unsafe { close(fd) }; - return Err(Error::IOCtl(errno_str())); + return Err(Error::IOCtl(io::Error::last_os_error())); } let addr = sockaddr_ctl { @@ -161,7 +152,7 @@ impl TunSocket { } < 0 { unsafe { close(fd) }; - let mut err_string = errno_str(); + let mut err_string = io::Error::last_os_error().to_string(); err_string.push_str("(did you run with sudo?)"); return Err(Error::Connect(err_string)); } @@ -171,9 +162,9 @@ impl TunSocket { pub fn set_non_blocking(self) -> Result { match unsafe { fcntl(self.fd, F_GETFL) } { - -1 => Err(Error::FCntl(errno_str())), + -1 => Err(Error::FCntl(io::Error::last_os_error())), flags => match unsafe { fcntl(self.fd, F_SETFL, flags | O_NONBLOCK) } { - -1 => Err(Error::FCntl(errno_str())), + -1 => Err(Error::FCntl(io::Error::last_os_error())), _ => Ok(self), }, } @@ -193,7 +184,7 @@ impl TunSocket { } < 0 || tunnel_name_len == 0 { - return Err(Error::GetSockOpt(errno_str())); + return Err(Error::GetSockOpt(io::Error::last_os_error())); } Ok(String::from_utf8_lossy(&tunnel_name[..(tunnel_name_len - 1) as usize]).to_string()) @@ -202,7 +193,7 @@ impl TunSocket { /// Get the current MTU value pub fn mtu(&self) -> Result { let fd = match unsafe { socket(AF_INET, SOCK_STREAM, IPPROTO_IP) } { - -1 => return Err(Error::Socket(errno_str())), + -1 => return Err(Error::Socket(io::Error::last_os_error())), fd => fd, }; @@ -216,7 +207,7 @@ impl TunSocket { ifr.ifr_name[..iface_name.len()].copy_from_slice(iface_name); if unsafe { ioctl(fd, SIOCGIFMTU, &ifr) } < 0 { - return Err(Error::IOCtl(errno_str())); + return Err(Error::IOCtl(io::Error::last_os_error())); } unsafe { close(fd) }; @@ -257,7 +248,7 @@ impl TunSocket { }; match unsafe { recvmsg(self.fd, &mut msg_hdr, 0) } { - -1 => Err(Error::IfaceRead(errno())), + -1 => Err(Error::IfaceRead(io::Error::last_os_error())), 0..=4 => Ok(&mut dst[..0]), n => Ok(&mut dst[..(n - 4) as usize]), } diff --git a/boringtun/src/device/tun_linux.rs b/boringtun/src/device/tun_linux.rs index 7c6cc146..dee2999e 100644 --- a/boringtun/src/device/tun_linux.rs +++ b/boringtun/src/device/tun_linux.rs @@ -3,18 +3,9 @@ use super::Error; use libc::*; +use std::io; use std::os::unix::io::{AsRawFd, RawFd}; -pub fn errno() -> i32 { - unsafe { *__errno_location() } -} - -pub fn errno_str() -> String { - let strerr = unsafe { strerror(*__errno_location()) }; - let c_str = unsafe { std::ffi::CStr::from_ptr(strerr) }; - c_str.to_string_lossy().into_owned() -} - const TUNSETIFF: u64 = 0x4004_54ca; #[repr(C)] @@ -82,7 +73,7 @@ impl TunSocket { } let fd = match unsafe { open(b"/dev/net/tun\0".as_ptr() as _, O_RDWR) } { - -1 => return Err(Error::Socket(errno_str())), + -1 => return Err(Error::Socket(io::Error::last_os_error())), fd => fd, }; let iface_name = name.as_bytes(); @@ -100,7 +91,7 @@ impl TunSocket { ifr.ifr_name[..iface_name.len()].copy_from_slice(iface_name); if unsafe { ioctl(fd, TUNSETIFF as _, &ifr) } < 0 { - return Err(Error::IOCtl(errno_str())); + return Err(Error::IOCtl(io::Error::last_os_error())); } let name = name.to_string(); @@ -109,9 +100,9 @@ impl TunSocket { pub fn set_non_blocking(self) -> Result { match unsafe { fcntl(self.fd, F_GETFL) } { - -1 => Err(Error::FCntl(errno_str())), + -1 => Err(Error::FCntl(io::Error::last_os_error())), flags => match unsafe { fcntl(self.fd, F_SETFL, flags | O_NONBLOCK) } { - -1 => Err(Error::FCntl(errno_str())), + -1 => Err(Error::FCntl(io::Error::last_os_error())), _ => Ok(self), }, } @@ -129,7 +120,7 @@ impl TunSocket { } let fd = match unsafe { socket(AF_INET, SOCK_STREAM, IPPROTO_IP) } { - -1 => return Err(Error::Socket(errno_str())), + -1 => return Err(Error::Socket(io::Error::last_os_error())), fd => fd, }; @@ -143,7 +134,7 @@ impl TunSocket { ifr.ifr_name[..iface_name.len()].copy_from_slice(iface_name); if unsafe { ioctl(fd, SIOCGIFMTU as _, &ifr) } < 0 { - return Err(Error::IOCtl(errno_str())); + return Err(Error::IOCtl(io::Error::last_os_error())); } unsafe { close(fd) }; @@ -161,7 +152,7 @@ impl TunSocket { pub fn read<'a>(&self, dst: &'a mut [u8]) -> Result<&'a mut [u8], Error> { match unsafe { read(self.fd, dst.as_mut_ptr() as _, dst.len()) } { - -1 => Err(Error::IfaceRead(errno())), + -1 => Err(Error::IfaceRead(io::Error::last_os_error())), n => Ok(&mut dst[..n as usize]), } } diff --git a/boringtun/src/device/udp_unix.rs b/boringtun/src/device/udp_unix.rs deleted file mode 100644 index fe56679b..00000000 --- a/boringtun/src/device/udp_unix.rs +++ /dev/null @@ -1,375 +0,0 @@ -// Copyright (c) 2019 Cloudflare, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause - -use super::{errno, errno_str, Error}; -use libc::*; -use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; -use std::os::unix::io::{AsRawFd, RawFd}; - -/// Receives and sends UDP packets over the network -#[derive(Debug)] -pub struct UDPSocket { - fd: RawFd, - version: u8, -} - -impl UDPSocket { - fn bind4(self, port: u16) -> Result { - let addr = sockaddr_in { - #[cfg(any(target_os = "macos", target_os = "ios"))] - sin_len: std::mem::size_of::() as u8, - sin_family: AF_INET as _, - sin_port: port.to_be(), - sin_addr: in_addr { s_addr: INADDR_ANY }, - sin_zero: [0; 8], - }; - - match unsafe { - bind( - self.fd, - &addr as *const sockaddr_in as *const sockaddr, - std::mem::size_of::() as u32, - ) - } { - -1 => Err(Error::Bind(errno_str())), - _ => Ok(self), - } - } - - fn bind6(self, port: u16) -> Result { - let mut addr: libc::sockaddr_in6 = unsafe { std::mem::zeroed() }; - addr.sin6_family = AF_INET6 as _; - addr.sin6_port = port.to_be(); - - match unsafe { - bind( - self.fd, - &addr as *const sockaddr_in6 as *const sockaddr, - std::mem::size_of::() as u32, - ) - } { - -1 => Err(Error::Bind(errno_str())), - _ => Ok(self), - } - } - - fn connect4(self, dst: &SocketAddrV4) -> Result { - assert_eq!(self.version, 4); - let addr = sockaddr_in { - #[cfg(any(target_os = "macos", target_os = "ios"))] - sin_len: std::mem::size_of::() as u8, - sin_family: AF_INET as _, - sin_port: dst.port().to_be(), - sin_addr: in_addr { - s_addr: u32::from(*dst.ip()).to_be(), - }, - sin_zero: [0; 8], - }; - - match unsafe { - connect( - self.fd, - &addr as *const sockaddr_in as *const sockaddr, - std::mem::size_of::() as u32, - ) - } { - -1 => Err(Error::Connect(errno_str())), - _ => Ok(self), - } - } - - fn connect6(self, dst: &SocketAddrV6) -> Result { - assert_eq!(self.version, 6); - let mut addr: libc::sockaddr_in6 = unsafe { std::mem::zeroed() }; - addr.sin6_family = AF_INET6 as _; - addr.sin6_port = dst.port().to_be(); - addr.sin6_addr.s6_addr = dst.ip().octets(); - - match unsafe { - connect( - self.fd, - &addr as *const sockaddr_in6 as *const sockaddr, - std::mem::size_of::() as u32, - ) - } { - -1 => Err(Error::Connect(errno_str())), - _ => Ok(self), - } - } - - fn sendto4(&self, buf: &[u8], dst: SocketAddrV4) -> usize { - assert_eq!(self.version, 4); - let addr = sockaddr_in { - #[cfg(any(target_os = "macos", target_os = "ios"))] - sin_len: std::mem::size_of::() as _, - sin_family: AF_INET as _, - sin_port: dst.port().to_be(), - sin_addr: in_addr { - s_addr: u32::from(*dst.ip()).to_be(), - }, - sin_zero: [0; 8], - }; - - match unsafe { - sendto( - self.fd, - &buf[0] as *const u8 as _, - buf.len() as _, - 0, - &addr as *const sockaddr_in as _, - std::mem::size_of::() as _, - ) - } { - -1 => 0, - n => n as usize, - } - } - - fn sendto6(&self, buf: &[u8], dst: SocketAddrV6) -> usize { - assert_eq!(self.version, 6); - let mut addr: libc::sockaddr_in6 = unsafe { std::mem::zeroed() }; - addr.sin6_family = AF_INET6 as _; - addr.sin6_port = dst.port().to_be(); - addr.sin6_addr.s6_addr = dst.ip().octets(); - - match unsafe { - sendto( - self.fd, - &buf[0] as *const u8 as _, - buf.len() as _, - 0, - &addr as *const sockaddr_in6 as _, - std::mem::size_of::() as _, - ) - } { - -1 => 0, - n => n as usize, - } - } - - fn recvfrom6<'a>(&self, buf: &'a mut [u8]) -> Result<(SocketAddr, &'a mut [u8]), Error> { - let mut addr: libc::sockaddr_in6 = unsafe { std::mem::zeroed() }; - let mut addr_len: socklen_t = std::mem::size_of::() as socklen_t; - - let n = unsafe { - recvfrom( - self.fd, - buf.as_mut_ptr() as *mut c_void, - buf.len(), - 0, - &mut addr as *mut sockaddr_in6 as *mut sockaddr, - &mut addr_len, - ) - }; - - if n == -1 { - return Err(Error::UDPRead(errno())); - } - - // This is the endpoint - let origin = SocketAddrV6::new( - std::net::Ipv6Addr::from(addr.sin6_addr.s6_addr), - u16::from_be(addr.sin6_port), - 0, - 0, - ); - - Ok((SocketAddr::V6(origin), &mut buf[..n as usize])) - } - - fn recvfrom4<'a>(&self, buf: &'a mut [u8]) -> Result<(SocketAddr, &'a mut [u8]), Error> { - let mut addr = sockaddr_in { - #[cfg(any(target_os = "macos", target_os = "ios"))] - sin_len: 0, - sin_family: 0, - sin_port: 0, - sin_addr: in_addr { s_addr: 0 }, - sin_zero: [0; 8], - }; - let mut addr_len: socklen_t = std::mem::size_of::() as socklen_t; - - let n = unsafe { - recvfrom( - self.fd, - buf.as_mut_ptr() as *mut c_void, - buf.len(), - 0, - &mut addr as *mut sockaddr_in as *mut sockaddr, - &mut addr_len, - ) - }; - - if n == -1 { - return Err(Error::UDPRead(errno())); - } - - // This is the endpoint - let origin = SocketAddrV4::new( - std::net::Ipv4Addr::from(u32::from_be(addr.sin_addr.s_addr)), - u16::from_be(addr.sin_port), - ); - - Ok((SocketAddr::V4(origin), &mut buf[..n as usize])) - } - - fn write_fd(fd: RawFd, src: &[u8]) -> usize { - match unsafe { send(fd, &src[0] as *const u8 as _, src.len(), 0) } { - -1 => 0, - n => n as usize, - } - } -} - -/// Socket is closed when it goes out of scope -impl Drop for UDPSocket { - fn drop(&mut self) { - unsafe { close(self.fd) }; - } -} - -impl AsRawFd for UDPSocket { - fn as_raw_fd(&self) -> RawFd { - self.fd - } -} - -impl UDPSocket { - /// Create a new IPv4 UDP socket - pub fn new() -> Result { - match unsafe { socket(AF_INET, SOCK_DGRAM, 0) } { - -1 => Err(Error::Socket(errno_str())), - fd => Ok(UDPSocket { fd, version: 4 }), - } - } - - /// Create a new IPv6 UDP socket - pub fn new6() -> Result { - match unsafe { socket(AF_INET6, SOCK_DGRAM, 0) } { - -1 => Err(Error::Socket(errno_str())), - fd => Ok(UDPSocket { fd, version: 6 }), - } - } - - /// Bind the socket to a local port - pub fn bind(self, port: u16) -> Result { - if self.version == 6 { - return self.bind6(port); - } - - self.bind4(port) - } - - /// Connect a socket to a remote address, must call bind prior to connect - /// # Panics - /// When connecting an IPv4 socket to an IPv6 address and vice versa - pub fn connect(self, dst: &SocketAddr) -> Result { - match dst { - SocketAddr::V4(dst) => self.connect4(dst), - SocketAddr::V6(dst) => self.connect6(dst), - } - } - - /// Set socket mode to non blocking - pub fn set_non_blocking(self) -> Result { - match unsafe { fcntl(self.fd, F_GETFL) } { - -1 => Err(Error::FCntl(errno_str())), - flags => match unsafe { fcntl(self.fd, F_SETFL, flags | O_NONBLOCK) } { - -1 => Err(Error::FCntl(errno_str())), - _ => Ok(self), - }, - } - } - - /// Set the SO_REUSEPORT/SO_REUSEADDR option, so multiple sockets can bind on the same port - pub fn set_reuse(self) -> Result { - match unsafe { - setsockopt( - self.fd, - SOL_SOCKET, - #[cfg(target_os = "linux")] - SO_REUSEADDR, // On Linux SO_REUSEPORT won't prefer a connected IPv6 socket - #[cfg(not(target_os = "linux"))] - SO_REUSEPORT, - &1u32 as *const u32 as *const c_void, - std::mem::size_of::() as u32, - ) - } { - -1 => Err(Error::SetSockOpt(errno_str())), - _ => Ok(self), - } - } - - #[cfg(target_os = "linux")] - /// Set the mark on all packets sent by this socket using SO_MARK - /// Only available on Linux - pub fn set_fwmark(&self, mark: u32) -> Result<(), Error> { - match unsafe { - setsockopt( - self.fd, - SOL_SOCKET, - SO_MARK, - &mark as *const u32 as *const c_void, - std::mem::size_of_val(&mark) as _, - ) - } { - -1 => Err(Error::SetSockOpt(errno_str())), - _ => Ok(()), - } - } - - #[cfg(any(target_os = "macos", target_os = "ios"))] - pub fn set_fwmark(&self, _: u32) -> Result<(), Error> { - Ok(()) - } - - /// Query the local port the socket is bound to - /// # Panics - /// If socket is IPv6 - pub fn port(&self) -> Result { - if self.version != 4 { - panic!("Can only query ports of IPv4 sockets"); - } - let mut addr: sockaddr_in = unsafe { std::mem::zeroed() }; - let mut addr_len = std::mem::size_of_val(&addr) as _; - match unsafe { getsockname(self.fd, &mut addr as *mut sockaddr_in as _, &mut addr_len) } { - -1 => Err(Error::GetSockName(errno_str())), - _ => Ok(u16::from_be(addr.sin_port)), - } - } - - /// Send buf to a remote address, returns 0 on error, or amount of data send on success - /// # Panics - /// When sending from an IPv4 socket to an IPv6 address and vice versa - pub fn sendto(&self, buf: &[u8], dst: SocketAddr) -> usize { - match dst { - SocketAddr::V4(addr) => self.sendto4(buf, addr), - SocketAddr::V6(addr) => self.sendto6(buf, addr), - } - } - - /// Receives a message on a non-connected UDP socket and returns its contents and origin address - pub fn recvfrom<'a>(&self, buf: &'a mut [u8]) -> Result<(SocketAddr, &'a mut [u8]), Error> { - match self.version { - 4 => self.recvfrom4(buf), - _ => self.recvfrom6(buf), - } - } - - /// Receives a message on a connected UDP socket and returns its contents - pub fn read<'a>(&self, dst: &'a mut [u8]) -> Result<&'a mut [u8], Error> { - match unsafe { recv(self.fd, &mut dst[0] as *mut u8 as _, dst.len(), 0) } { - -1 => Err(Error::UDPRead(errno())), - n => Ok(&mut dst[..n as usize]), - } - } - - /// Sends a message on a connected UDP socket. Returns number of bytes successfully sent. - pub fn write(&self, src: &[u8]) -> usize { - UDPSocket::write_fd(self.fd, src) - } - - /// Calls shutdown on a connected socket. This will trigger an EOF in the event queue. - pub fn shutdown(&self) { - unsafe { shutdown(self.fd, SHUT_RDWR) }; - } -} diff --git a/boringtun/src/lib.rs b/boringtun/src/lib.rs index b657b73c..6ab410dc 100644 --- a/boringtun/src/lib.rs +++ b/boringtun/src/lib.rs @@ -5,7 +5,7 @@ //! //! git clone https://github.com/cloudflare/boringtun.git -#[cfg(not(any(target_os = "windows", target_os = "android", target_os = "ios")))] +#[cfg(feature = "device")] pub mod device; #[cfg(feature = "ffi-bindings")]