From a761af62475b0edd38d6e82a732f9f4b4bd31f22 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Thu, 22 Feb 2018 21:29:06 +0900 Subject: [PATCH 1/2] nix::sys::socket: fix `SockFlag::SOCK_NONBLOCK` and `SockFlag::SOCK_CLOEXEC` on macos and ios; ref #244 #861 --- src/sys/socket/mod.rs | 66 +++++++++++++++++++++++++---------------- test/sys/test_socket.rs | 58 ++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 26 deletions(-) diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index af66bc4715..c11d18af22 100644 --- a/src/sys/socket/mod.rs +++ b/src/sys/socket/mod.rs @@ -86,32 +86,44 @@ 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! { + 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 +721,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"))] { diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs index a997fbca9e..99099e7a54 100644 --- a/test/sys/test_socket.rs +++ b/test/sys/test_socket.rs @@ -255,3 +255,61 @@ 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 +#[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); +} From dca2b2eff74d6930cfd0135d9491bb5c43915e33 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 23 Feb 2018 18:23:03 +0900 Subject: [PATCH 2/2] nix::sys::socket: cover all cases of `struct SockFlag` and put a cfg gate on `test_sockflag_cloexec`; ref #244 #861 --- src/sys/socket/mod.rs | 9 +++++++++ test/sys/test_socket.rs | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index c11d18af22..5676e0fb52 100644 --- a/src/sys/socket/mod.rs +++ b/src/sys/socket/mod.rs @@ -87,6 +87,11 @@ pub enum SockProtocol { } 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! { @@ -768,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"))] { @@ -850,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 99099e7a54..567ae030b9 100644 --- a/test/sys/test_socket.rs +++ b/test/sys/test_socket.rs @@ -290,6 +290,14 @@ pub fn test_sockflag_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;