Skip to content

Commit 4cfdeef

Browse files
de-vri-esThomasdezeeuw
authored andcommitted
Set close-on-exec and similar flag by default
Socket::new, Socket::pair and Socket::accept now set close-on-exec flag on Unix, on Apple platforms SOCK_NOSIGPIPE and on Windows the socket is made non-inheritable. New Socket::new_raw, Socket::pair_raw and Socket::accept_raw functions are added which do not set the above flags.
1 parent a12812b commit 4cfdeef

File tree

4 files changed

+210
-101
lines changed

4 files changed

+210
-101
lines changed

src/socket.rs

Lines changed: 119 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -47,41 +47,6 @@ use crate::{Domain, Protocol, SockAddr, Type};
4747
/// can lead to a data race when two threads are changing options in parallel.
4848
///
4949
/// # Examples
50-
///
51-
/// Creating a new socket setting all advisable flags.
52-
///
53-
#[cfg_attr(feature = "all", doc = "```")] // Protocol::cloexec requires the `all` feature.
54-
#[cfg_attr(not(feature = "all"), doc = "```ignore")]
55-
/// # fn main() -> std::io::Result<()> {
56-
/// use socket2::{Protocol, Domain, Type, Socket};
57-
///
58-
/// let domain = Domain::IPV4;
59-
/// let ty = Type::STREAM;
60-
/// let protocol = Protocol::TCP;
61-
///
62-
/// // On platforms that support it set `SOCK_CLOEXEC`.
63-
/// #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux", target_os = "netbsd", target_os = "openbsd"))]
64-
/// let ty = ty.cloexec();
65-
///
66-
/// // On windows set `WSA_FLAG_NO_HANDLE_INHERIT`.
67-
/// #[cfg(windows)]
68-
/// let ty = ty.no_inherit();
69-
///
70-
/// let socket = Socket::new(domain, ty, Some(protocol))?;
71-
///
72-
/// // On platforms that don't support `SOCK_CLOEXEC`, use `FD_CLOEXEC`.
73-
/// #[cfg(all(not(windows), not(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux", target_os = "netbsd", target_os = "openbsd"))))]
74-
/// socket.set_cloexec(true)?;
75-
///
76-
/// // On macOS and iOS set `NOSIGPIPE`.
77-
/// #[cfg(target_vendor = "apple")]
78-
/// socket.set_nosigpipe(true)?;
79-
///
80-
/// # drop(socket);
81-
/// # Ok(())
82-
/// # }
83-
/// ```
84-
///
8550
/// ```no_run
8651
/// # fn main() -> std::io::Result<()> {
8752
/// use std::net::{SocketAddr, TcpListener};
@@ -107,29 +72,26 @@ pub struct Socket {
10772
}
10873

10974
impl Socket {
110-
/// Creates a new socket ready to be configured.
75+
/// Creates a new socket and sets common flags.
11176
///
11277
/// This function corresponds to `socket(2)` on Unix and `WSASocketW` on
113-
/// Windows and simply creates a new socket, no other configuration is done
114-
/// and further functions must be invoked to configure this socket.
115-
///
116-
/// # Notes
117-
///
118-
/// The standard library sets the `CLOEXEC` flag on Unix on sockets, this
119-
/// function does **not** do this, but its advisable. On supported platforms
120-
/// [`Type::cloexec`] can be used for this, or by using
121-
/// [`Socket::set_cloexec`].
78+
/// Windows.
12279
///
123-
/// Furthermore on macOS and iOS `NOSIGPIPE` is not set, this can be done
124-
/// using [`Socket::set_nosigpipe`].
80+
/// On Unix-like systems, the close-on-exec flag is set on the new socket.
81+
/// Additionally, on Apple platforms `SOCK_NOSIGPIPE` is set. On Windows,
82+
/// the socket is made non-inheritable.
12583
///
126-
/// Similarly on Windows the `HANDLE_FLAG_INHERIT` is **not** set to zero,
127-
/// but again in most cases its advisable to do so. This can be doing using
128-
/// [`Socket::set_no_inherit`].
129-
///
130-
/// See the `Socket` documentation for a full example of setting all the
131-
/// above mentioned flags.
84+
/// [`Socket::new_raw`] can be used if you don't want these flags to be set.
13285
pub fn new(domain: Domain, ty: Type, protocol: Option<Protocol>) -> io::Result<Socket> {
86+
let ty = set_common_type(ty);
87+
Socket::new_raw(domain, ty, protocol).and_then(set_common_flags)
88+
}
89+
90+
/// Creates a new socket ready to be configured.
91+
///
92+
/// This function corresponds to `socket(2)` on Unix and `WSASocketW` on
93+
/// Windows and simply creates a new socket, no other configuration is done.
94+
pub fn new_raw(domain: Domain, ty: Type, protocol: Option<Protocol>) -> io::Result<Socket> {
13395
let protocol = protocol.map(|p| p.0).unwrap_or(0);
13496
sys::socket(domain.0, ty.0, protocol).map(|inner| Socket { inner })
13597
}
@@ -138,17 +100,37 @@ impl Socket {
138100
///
139101
/// This function corresponds to `socketpair(2)`.
140102
///
141-
/// # Notes
103+
/// This function sets the same flags as in done for [`Socket::new`],
104+
/// [`Socket::pair_raw`] can be used if you don't want to set those flags.
142105
///
143-
/// Much like [`Socket::new`] this doesn't set any flags, which might be
144-
/// advisable.
106+
/// # Notes
145107
///
146108
/// This function is only available on Unix.
147109
#[cfg(all(feature = "all", unix))]
148110
pub fn pair(
149111
domain: Domain,
150112
ty: Type,
151113
protocol: Option<Protocol>,
114+
) -> io::Result<(Socket, Socket)> {
115+
let ty = set_common_type(ty);
116+
let (a, b) = Socket::pair_raw(domain, ty, protocol)?;
117+
let a = set_common_flags(a)?;
118+
let b = set_common_flags(b)?;
119+
Ok((a, b))
120+
}
121+
122+
/// Creates a pair of sockets which are connected to each other.
123+
///
124+
/// This function corresponds to `socketpair(2)`.
125+
///
126+
/// # Notes
127+
///
128+
/// This function is only available on Unix.
129+
#[cfg(all(feature = "all", unix))]
130+
pub fn pair_raw(
131+
domain: Domain,
132+
ty: Type,
133+
protocol: Option<Protocol>,
152134
) -> io::Result<(Socket, Socket)> {
153135
let protocol = protocol.map(|p| p.0).unwrap_or(0);
154136
sys::socketpair(domain.0, ty.0, protocol)
@@ -195,19 +177,43 @@ impl Socket {
195177

196178
/// Accept a new incoming connection from this listener.
197179
///
198-
/// This function directly corresponds to the `accept(2)` function on
199-
/// Windows and Unix.
200-
///
201-
/// This function will block the calling thread until a new connection is
202-
/// established. When established, the corresponding `Socket` and the
203-
/// remote peer's address will be returned.
204-
///
205-
/// # Notes
180+
/// This function uses `accept4(2)` on platforms that support it and
181+
/// `accept(2)` platforms that do not.
206182
///
207-
/// Like [`Socket::new`] this will not set any flags. If that is desirable,
208-
/// e.g. setting `CLOEXEC`, [`Socket::accept4`] can be used on supported
209-
/// OSes or [`Socket::set_cloexec`] can be called.
183+
/// This function sets the same flags as in done for [`Socket::new`],
184+
/// [`Socket::accept_raw`] can be used if you don't want to set those flags.
210185
pub fn accept(&self) -> io::Result<(Socket, SockAddr)> {
186+
// Use `accept4` on platforms that support it.
187+
#[cfg(any(
188+
target_os = "android",
189+
target_os = "dragonfly",
190+
target_os = "freebsd",
191+
target_os = "linux",
192+
target_os = "netbsd",
193+
target_os = "openbsd",
194+
))]
195+
return self._accept4(libc::SOCK_CLOEXEC);
196+
197+
// Fall back to `accept` on platforms that do not support `accept4`.
198+
#[cfg(not(any(
199+
target_os = "android",
200+
target_os = "dragonfly",
201+
target_os = "freebsd",
202+
target_os = "linux",
203+
target_os = "netbsd",
204+
target_os = "openbsd",
205+
)))]
206+
{
207+
let (socket, addr) = self.accept_raw()?;
208+
set_common_flags(socket).map(|socket| (socket, addr))
209+
}
210+
}
211+
212+
/// Accept a new incoming connection from this listener.
213+
///
214+
/// This function directly corresponds to the `accept(2)` function on
215+
/// Windows and Unix.
216+
pub fn accept_raw(&self) -> io::Result<(Socket, SockAddr)> {
211217
sys::accept(self.inner).map(|(inner, addr)| (Socket { inner }, addr))
212218
}
213219

@@ -918,6 +924,52 @@ impl Socket {
918924
}
919925
}
920926

927+
/// Set `SOCK_CLOEXEC` and `NO_HANDLE_INHERIT` on the `ty`pe on platforms that
928+
/// support it.
929+
#[inline(always)]
930+
fn set_common_type(ty: Type) -> Type {
931+
// On platforms that support it set `SOCK_CLOEXEC`.
932+
#[cfg(any(
933+
target_os = "android",
934+
target_os = "dragonfly",
935+
target_os = "freebsd",
936+
target_os = "linux",
937+
target_os = "netbsd",
938+
target_os = "openbsd",
939+
))]
940+
let ty = ty._cloexec();
941+
942+
// On windows set `NO_HANDLE_INHERIT`.
943+
#[cfg(windows)]
944+
let ty = ty._no_inherit();
945+
946+
ty
947+
}
948+
949+
/// Set `FD_CLOEXEC` and `NOSIGPIPE` on the `socket` for platforms that need it.
950+
#[inline(always)]
951+
fn set_common_flags(socket: Socket) -> io::Result<Socket> {
952+
// On platforms that don't have `SOCK_CLOEXEC` use `FD_CLOEXEC`.
953+
#[cfg(all(
954+
unix,
955+
not(any(
956+
target_os = "android",
957+
target_os = "dragonfly",
958+
target_os = "freebsd",
959+
target_os = "linux",
960+
target_os = "netbsd",
961+
target_os = "openbsd",
962+
))
963+
))]
964+
socket._set_cloexec(true)?;
965+
966+
// On Apple platforms set `NOSIGPIPE`.
967+
#[cfg(target_vendor = "apple")]
968+
socket._set_nosigpipe(true)?;
969+
970+
Ok(socket)
971+
}
972+
921973
impl Read for Socket {
922974
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
923975
self.recv(buf)

src/sys/unix.rs

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,18 @@ impl Type {
186186
)
187187
))]
188188
pub const fn cloexec(self) -> Type {
189+
self._cloexec()
190+
}
191+
192+
#[cfg(any(
193+
target_os = "android",
194+
target_os = "dragonfly",
195+
target_os = "freebsd",
196+
target_os = "linux",
197+
target_os = "netbsd",
198+
target_os = "openbsd"
199+
))]
200+
pub(crate) const fn _cloexec(self) -> Type {
189201
Type(self.0 | libc::SOCK_CLOEXEC)
190202
}
191203
}
@@ -569,32 +581,45 @@ impl crate::Socket {
569581
)
570582
))]
571583
pub fn accept4(&self, flags: c_int) -> io::Result<(crate::Socket, SockAddr)> {
584+
self._accept4(flags)
585+
}
586+
587+
#[cfg(any(
588+
target_os = "android",
589+
target_os = "dragonfly",
590+
target_os = "freebsd",
591+
target_os = "illumos",
592+
target_os = "linux",
593+
target_os = "netbsd",
594+
target_os = "openbsd"
595+
))]
596+
pub(crate) fn _accept4(&self, flags: c_int) -> io::Result<(crate::Socket, SockAddr)> {
597+
// Safety: zeroed `sockaddr_storage` is valid.
572598
let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() };
573599
let mut len = mem::size_of_val(&storage) as socklen_t;
574-
575-
let res = syscall!(accept4(
600+
syscall!(accept4(
576601
self.inner,
577602
&mut storage as *mut _ as *mut _,
578603
&mut len,
579-
flags,
580-
));
581-
match res {
582-
Ok(inner) => {
583-
let socket = crate::Socket { inner };
584-
let addr =
585-
unsafe { SockAddr::from_raw_parts(&storage as *const _ as *const _, len) };
586-
Ok((socket, addr))
587-
}
588-
Err(e) => Err(e),
589-
}
604+
flags
605+
))
606+
.map(|inner| {
607+
let addr = unsafe { SockAddr::from_raw_parts(&storage as *const _ as *const _, len) };
608+
(crate::Socket { inner }, addr)
609+
})
590610
}
591611

592612
/// Sets `CLOEXEC` on the socket.
593613
///
594614
/// # Notes
595615
///
596616
/// On supported platforms you can use [`Protocol::cloexec`].
617+
#[cfg(feature = "all")]
597618
pub fn set_cloexec(&self, close_on_exec: bool) -> io::Result<()> {
619+
self._set_cloexec(close_on_exec)
620+
}
621+
622+
pub(crate) fn _set_cloexec(&self, close_on_exec: bool) -> io::Result<()> {
598623
if close_on_exec {
599624
fcntl_add(self.inner, libc::F_GETFD, libc::F_SETFD, libc::FD_CLOEXEC)
600625
} else {
@@ -609,12 +634,17 @@ impl crate::Socket {
609634
/// Only supported on Apple platforms (`target_vendor = "apple"`).
610635
#[cfg(all(feature = "all", target_vendor = "apple"))]
611636
pub fn set_nosigpipe(&self, nosigpipe: bool) -> io::Result<()> {
637+
self._set_nosigpipe(nosigpipe)
638+
}
639+
640+
#[cfg(target_vendor = "apple")]
641+
pub(crate) fn _set_nosigpipe(&self, nosigpipe: bool) -> io::Result<()> {
612642
unsafe {
613-
setsockopt::<c_int>(
643+
setsockopt(
614644
self.inner,
615645
libc::SOL_SOCKET,
616646
libc::SO_NOSIGPIPE,
617-
nosigpipe as _,
647+
nosigpipe as c_int,
618648
)
619649
}
620650
}
@@ -663,7 +693,7 @@ unsafe fn getsockopt<T>(fd: SysSocket, opt: c_int, val: c_int) -> io::Result<T>
663693
}
664694

665695
/// Caller must ensure `T` is the correct type for `opt` and `val`.
666-
#[cfg(all(feature = "all", target_vendor = "apple"))]
696+
#[cfg(target_vendor = "apple")]
667697
unsafe fn setsockopt<T>(fd: SysSocket, opt: c_int, val: c_int, payload: T) -> io::Result<()> {
668698
let payload = &payload as *const T as *const c_void;
669699
syscall!(setsockopt(

src/sys/windows.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ impl Type {
9696
/// Set `WSA_FLAG_NO_HANDLE_INHERIT` on the socket.
9797
#[cfg(feature = "all")]
9898
pub const fn no_inherit(self) -> Type {
99+
self._no_inherit()
100+
}
101+
102+
pub(crate) const fn _no_inherit(self) -> Type {
99103
Type(self.0 | Type::NO_INHERIT)
100104
}
101105
}
@@ -528,7 +532,12 @@ fn ioctlsocket(socket: SysSocket, cmd: c_long, payload: &mut u_long) -> io::Resu
528532
/// Windows only API.
529533
impl crate::Socket {
530534
/// Sets `HANDLE_FLAG_INHERIT` to zero using `SetHandleInformation`.
535+
#[cfg(feature = "all")]
531536
pub fn set_no_inherit(&self, no_inherit: bool) -> io::Result<()> {
537+
self._set_no_inherit(no_inherit)
538+
}
539+
540+
pub(crate) fn _set_no_inherit(&self, no_inherit: bool) -> io::Result<()> {
532541
// NOTE: can't use `syscall!` because it expects the function in the
533542
// `sock::` path.
534543
let res = unsafe {
@@ -884,10 +893,6 @@ impl Socket {
884893
Err(last_error())
885894
}
886895
}
887-
888-
pub fn inner(self) -> SysSocket {
889-
self.socket
890-
}
891896
}
892897

893898
impl fmt::Debug for Socket {
@@ -943,7 +948,7 @@ impl IntoRawSocket for crate::Socket {
943948
impl FromRawSocket for crate::Socket {
944949
unsafe fn from_raw_socket(socket: RawSocket) -> crate::Socket {
945950
crate::Socket {
946-
inner: Socket::from_raw_socket(socket).inner(),
951+
inner: socket as SysSocket,
947952
}
948953
}
949954
}

0 commit comments

Comments
 (0)