diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index af66bc4715..5676e0fb52 100644 --- a/src/sys/socket/mod.rs +++ b/src/sys/socket/mod.rs @@ -86,32 +86,49 @@ pub enum SockProtocol { KextControl = libc::SYSPROTO_CONTROL, } -libc_bitflags!{ - /// Additional socket options - pub struct SockFlag: c_int { - /// Set non-blocking mode on the new socket - #[cfg(any(target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd"))] - SOCK_NONBLOCK; - /// Set close-on-exec on the new descriptor - #[cfg(any(target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd"))] - SOCK_CLOEXEC; - /// Return `EPIPE` instead of raising `SIGPIPE` - #[cfg(target_os = "netbsd")] - SOCK_NOSIGPIPE; - /// For domains `AF_INET(6)`, only allow `connect(2)`, `sendto(2)`, or `sendmsg(2)` - /// to the DNS port (typically 53) - #[cfg(target_os = "openbsd")] - SOCK_DNS; +cfg_if! { + /* + Because `nix` provides a new API that does not conform to + POSIX or BSD (or directly Linux), add virtual definitions + for macOS and iOS that are backed by their equivalent `fcntl` flags. + */ + if #[cfg(any(target_os = "macos", + target_os = "ios"))] { + bitflags! { + pub struct SockFlag: c_int { + const SOCK_NONBLOCK = libc::O_NONBLOCK; + const SOCK_CLOEXEC = libc::O_CLOEXEC; + } + } + } else { + libc_bitflags!{ + /// Additional socket options + pub struct SockFlag: c_int { + /// Set non-blocking mode on the new socket + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd"))] + SOCK_NONBLOCK; + /// Set close-on-exec on the new descriptor + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd"))] + SOCK_CLOEXEC; + /// Return `EPIPE` instead of raising `SIGPIPE` + #[cfg(target_os = "netbsd")] + SOCK_NOSIGPIPE; + /// For domains `AF_INET(6)`, only allow `connect(2)`, `sendto(2)`, or `sendmsg(2)` + /// to the DNS port (typically 53) + #[cfg(target_os = "openbsd")] + SOCK_DNS; + } + } } } @@ -709,7 +726,9 @@ pub fn socket>>(domain: AddressFamily, ty: SockType #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", + target_os = "ios", target_os = "linux", + target_os = "macos", target_os = "netbsd", target_os = "openbsd"))] { @@ -754,7 +773,9 @@ pub fn socketpair>>(domain: AddressFamily, ty: Sock #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", + target_os = "ios", target_os = "linux", + target_os = "macos", target_os = "netbsd", target_os = "openbsd"))] { @@ -836,7 +857,9 @@ fn accept4_polyfill(sockfd: RawFd, flags: SockFlag) -> Result { #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", + target_os = "ios", target_os = "linux", + target_os = "macos", target_os = "netbsd", target_os = "openbsd"))] { diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs index a997fbca9e..567ae030b9 100644 --- a/test/sys/test_socket.rs +++ b/test/sys/test_socket.rs @@ -255,3 +255,69 @@ pub fn test_syscontrol() { // requires root privileges // connect(fd, &sockaddr).expect("connect failed"); } + +/// Test non-blocking mode on new sockets via SockFlag::O_NONBLOCK +#[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +#[test] +pub fn test_sockflag_nonblock() { + use libc; + use nix::fcntl::{fcntl}; + use nix::fcntl::FcntlArg::{F_GETFL}; + use nix::sys::socket::{socket, AddressFamily, SockType, SockFlag}; + + /* first, try without SockFlag::SOCK_NONBLOCK */ + let sock = socket(AddressFamily::Unix, SockType::Stream, SockFlag::empty(), None) + .expect("socket failed"); + + let fcntl_res = fcntl(sock, F_GETFL).expect("fcntl failed"); + + assert!(fcntl_res & libc::O_NONBLOCK == 0); + + /* next, try with SockFlag::SOCK_NONBLOCK */ + let sock = socket(AddressFamily::Unix, SockType::Stream, SockFlag::SOCK_NONBLOCK, None) + .expect("socket failed"); + + let fcntl_res = fcntl(sock, F_GETFL).expect("fcntl failed"); + + assert!(fcntl_res & libc::O_NONBLOCK == libc::O_NONBLOCK); +} + +/// Test close-on-exec on new sockets via SockFlag::SOCK_CLOEXEC +#[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +#[test] +pub fn test_sockflag_cloexec() { + use libc; + use nix::fcntl::{fcntl}; + use nix::fcntl::FcntlArg::{F_GETFD}; + use nix::sys::socket::{socket, AddressFamily, SockType, SockFlag}; + + /* first, test without SockFlag::SOCK_CLOEXEC */ + let sock = socket(AddressFamily::Unix, SockType::Stream, SockFlag::empty(), None) + .expect("socket failed"); + + let fcntl_res = fcntl(sock, F_GETFD).expect("fcntl failed"); + + assert!(fcntl_res & libc::FD_CLOEXEC == 0); + + /* next, test without SockFlag::SOCK_CLOEXEC */ + let sock = socket(AddressFamily::Unix, SockType::Stream, SockFlag::SOCK_CLOEXEC, None) + .expect("socket failed"); + + let fcntl_res = fcntl(sock, F_GETFD).expect("fcntl failed"); + + assert!(fcntl_res & libc::FD_CLOEXEC == libc::FD_CLOEXEC); +}