aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlice Ryhl <aliceryhl@google.com>2023-11-02 12:11:04 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-11-02 12:11:04 +0000
commitb0ebb402b36b3ea726ba57fd106a2ce980740370 (patch)
tree67fc88de9a7a10daefb5a4747bd02aee125f6d27
parent6749d34ca21ea4c723a0ac0bebc813b62b8623cf (diff)
parent46a17ad87fbe989d11fd61eba20dbc789dfd0697 (diff)
downloadsocket2-b0ebb402b36b3ea726ba57fd106a2ce980740370.tar.gz
Upgrade socket2 to 0.5.5 am: 46a17ad87f
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/socket2/+/2815233 Change-Id: I33f49049c612f8a25c3e2ae399e5170ee2a811a4 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--Android.bp4
-rw-r--r--Cargo.toml35
-rw-r--r--Cargo.toml.orig19
-rw-r--r--METADATA12
-rw-r--r--README.md11
-rw-r--r--TEST_MAPPING6
-rw-r--r--src/lib.rs333
-rw-r--r--src/sockaddr.rs435
-rw-r--r--src/socket.rs282
-rw-r--r--src/sockref.rs52
-rw-r--r--src/sys/unix.rs1539
-rw-r--r--src/sys/windows.rs468
13 files changed, 2549 insertions, 649 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 85f94b1..75be603 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
{
"git": {
- "sha1": "ff79dee5174b8beb32b54e35636fc45358e2d705"
+ "sha1": "328dac6604b5eb4bb358915e51a3b33a8e433e39"
},
"path_in_vcs": ""
} \ No newline at end of file
diff --git a/Android.bp b/Android.bp
index 36fa48e..19df21a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -42,9 +42,9 @@ rust_library {
host_supported: true,
crate_name: "socket2",
cargo_env_compat: true,
- cargo_pkg_version: "0.4.7",
+ cargo_pkg_version: "0.5.5",
srcs: ["src/lib.rs"],
- edition: "2018",
+ edition: "2021",
features: ["all"],
rustlibs: [
"liblibc",
diff --git a/Cargo.toml b/Cargo.toml
index 70a2506..9250ab4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,9 +10,10 @@
# See Cargo.toml.orig for the original contents.
[package]
-edition = "2018"
+edition = "2021"
+rust-version = "1.63"
name = "socket2"
-version = "0.4.7"
+version = "0.5.5"
authors = [
"Alex Crichton <alex@alexcrichton.com>",
"Thomas de Zeeuw <thomasdezeeuw@gmail.com>",
@@ -49,6 +50,22 @@ rustdoc-args = [
"--cfg",
"docsrs",
]
+targets = [
+ "aarch64-apple-ios",
+ "aarch64-linux-android",
+ "x86_64-apple-darwin",
+ "x86_64-unknown-fuchsia",
+ "x86_64-pc-windows-msvc",
+ "x86_64-pc-solaris",
+ "x86_64-unknown-freebsd",
+ "x86_64-unknown-illumos",
+ "x86_64-unknown-linux-gnu",
+ "x86_64-unknown-linux-musl",
+ "x86_64-unknown-netbsd",
+ "x86_64-unknown-redox",
+ "armv7-linux-androideabi",
+ "i686-linux-android",
+]
[package.metadata.playground]
features = ["all"]
@@ -57,12 +74,14 @@ features = ["all"]
all = []
[target."cfg(unix)".dependencies.libc]
-version = "0.2.124"
+version = "0.2.149"
-[target."cfg(windows)".dependencies.winapi]
-version = "0.3.9"
+[target."cfg(windows)".dependencies.windows-sys]
+version = "0.48"
features = [
- "handleapi",
- "ws2ipdef",
- "ws2tcpip",
+ "Win32_Foundation",
+ "Win32_Networking_WinSock",
+ "Win32_System_IO",
+ "Win32_System_Threading",
+ "Win32_System_WindowsProgramming",
]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 6784df9..d62d5ad 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
[package]
name = "socket2"
-version = "0.4.7"
+version = "0.5.5"
authors = [
"Alex Crichton <alex@alexcrichton.com>",
"Thomas de Zeeuw <thomasdezeeuw@gmail.com>"
@@ -16,7 +16,8 @@ possible intended.
"""
keywords = ["io", "socket", "network"]
categories = ["api-bindings", "network-programming"]
-edition = "2018"
+edition = "2021"
+rust-version = "1.63"
include = [
"Cargo.toml",
"LICENSE-APACHE",
@@ -28,15 +29,23 @@ include = [
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
+targets = ["aarch64-apple-ios", "aarch64-linux-android", "x86_64-apple-darwin", "x86_64-unknown-fuchsia", "x86_64-pc-windows-msvc", "x86_64-pc-solaris", "x86_64-unknown-freebsd", "x86_64-unknown-illumos", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "x86_64-unknown-netbsd", "x86_64-unknown-redox", "armv7-linux-androideabi", "i686-linux-android"]
[package.metadata.playground]
features = ["all"]
[target."cfg(unix)".dependencies]
-libc = "0.2.124"
+libc = "0.2.149"
-[target."cfg(windows)".dependencies]
-winapi = { version = "0.3.9", features = ["handleapi", "ws2ipdef", "ws2tcpip"] }
+[target.'cfg(windows)'.dependencies.windows-sys]
+version = "0.48"
+features = [
+ "Win32_Foundation",
+ "Win32_Networking_WinSock",
+ "Win32_System_IO",
+ "Win32_System_Threading",
+ "Win32_System_WindowsProgramming",
+]
[features]
# Enable all API, even ones not available on all OSs.
diff --git a/METADATA b/METADATA
index 4be1590..73ea070 100644
--- a/METADATA
+++ b/METADATA
@@ -1,6 +1,4 @@
-# This project was upgraded with external_updater.
-# Usage: tools/external_updater/updater.sh update rust/crates/socket2
-# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+# This project was upgraded manually.
name: "socket2"
description: "Utilities for handling networking sockets with a maximal amount of configuration possible intended."
@@ -11,13 +9,13 @@ third_party {
}
url {
type: ARCHIVE
- value: "https://static.crates.io/crates/socket2/socket2-0.4.7.crate"
+ value: "https://static.crates.io/crates/socket2/socket2-0.5.5.crate"
}
- version: "0.4.7"
+ version: "0.5.5"
license_type: NOTICE
last_upgrade_date {
year: 2023
- month: 2
- day: 6
+ month: 10
+ day: 25
}
}
diff --git a/README.md b/README.md
index 77963b7..8bb0949 100644
--- a/README.md
+++ b/README.md
@@ -19,12 +19,13 @@ See the [API documentation] for more.
[API documentation]: https://docs.rs/socket2
-# Two branches
+# Branches
-Currently Socket2 supports two versions: v0.4 and v0.3. Version 0.4 is developed
-in the master branch, version 0.3 in the [v0.3.x branch].
+Currently Socket2 supports two versions: v0.5 and v0.4. Version 0.5 is being
+developed in the master branch. Version 0.4 is developed in the [v0.4.x branch]
+branch.
-[v0.3.x branch]: https://github.com/rust-lang/socket2/tree/v0.3.x
+[v0.4.x branch]: https://github.com/rust-lang/socket2/tree/v0.4.x
# OS support
@@ -64,7 +65,7 @@ feature flag.
# Minimum Supported Rust Version (MSRV)
-Socket2 uses 1.46.0 as MSRV.
+Socket2 uses 1.63.0 as MSRV.
# License
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 170da8f..a32d61c 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -18,6 +18,12 @@
},
{
"path": "packages/modules/DnsResolver"
+ },
+ {
+ "path": "system/security/keystore2"
+ },
+ {
+ "path": "system/security/keystore2/legacykeystore"
}
]
}
diff --git a/src/lib.rs b/src/lib.rs
index da9e864..127e070 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -50,7 +50,6 @@
//! This crate has a single feature `all`, which enables all functions even ones
//! that are not available on all OSs.
-#![doc(html_root_url = "https://docs.rs/socket2/0.4")]
#![deny(missing_docs, missing_debug_implementations, rust_2018_idioms)]
// Show required OS/features on docs.rs.
#![cfg_attr(docsrs, feature(doc_cfg))]
@@ -60,6 +59,12 @@
#![doc(test(attr(deny(warnings))))]
use std::fmt;
+#[cfg(not(target_os = "redox"))]
+use std::io::IoSlice;
+#[cfg(not(target_os = "redox"))]
+use std::marker::PhantomData;
+#[cfg(not(target_os = "redox"))]
+use std::mem;
use std::mem::MaybeUninit;
use std::net::SocketAddr;
use std::ops::{Deref, DerefMut};
@@ -78,7 +83,7 @@ macro_rules! impl_debug {
$(#[$target: meta])*
// The flag(s) to check.
// Need to specific the libc crate because Windows doesn't use
- // `libc` but `winapi`.
+ // `libc` but `windows_sys`.
$libc: ident :: $flag: ident
),+ $(,)*
) => {
@@ -89,7 +94,7 @@ macro_rules! impl_debug {
$(#[$target])*
$libc :: $flag => stringify!($flag),
)+
- n => return write!(f, "{}", n),
+ n => return write!(f, "{n}"),
};
f.write_str(string)
}
@@ -115,6 +120,56 @@ macro_rules! from {
};
}
+/// Link to online documentation for (almost) all supported OSs.
+#[rustfmt::skip]
+macro_rules! man_links {
+ // Links to all OSs.
+ ($syscall: tt ( $section: tt ) ) => {
+ concat!(
+ man_links!(__ intro),
+ man_links!(__ unix $syscall($section)),
+ man_links!(__ windows $syscall($section)),
+ )
+ };
+ // Links to Unix-like OSs.
+ (unix: $syscall: tt ( $section: tt ) ) => {
+ concat!(
+ man_links!(__ intro),
+ man_links!(__ unix $syscall($section)),
+ )
+ };
+ // Links to Windows only.
+ (windows: $syscall: tt ( $section: tt ) ) => {
+ concat!(
+ man_links!(__ intro),
+ man_links!(__ windows $syscall($section)),
+ )
+ };
+ // Internals.
+ (__ intro) => {
+ "\n\nAdditional documentation can be found in manual of the OS:\n\n"
+ };
+ // List for Unix-like OSs.
+ (__ unix $syscall: tt ( $section: tt ) ) => {
+ concat!(
+ " * DragonFly BSD: <https://man.dragonflybsd.org/?command=", stringify!($syscall), "&section=", stringify!($section), ">\n",
+ " * FreeBSD: <https://www.freebsd.org/cgi/man.cgi?query=", stringify!($syscall), "&sektion=", stringify!($section), ">\n",
+ " * Linux: <https://man7.org/linux/man-pages/man", stringify!($section), "/", stringify!($syscall), ".", stringify!($section), ".html>\n",
+ " * macOS: <https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/", stringify!($syscall), ".", stringify!($section), ".html> (archived, actually for iOS)\n",
+ " * NetBSD: <https://man.netbsd.org/", stringify!($syscall), ".", stringify!($section), ">\n",
+ " * OpenBSD: <https://man.openbsd.org/", stringify!($syscall), ".", stringify!($section), ">\n",
+ " * iOS: <https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/", stringify!($syscall), ".", stringify!($section), ".html> (archived)\n",
+ " * illumos: <https://illumos.org/man/3SOCKET/", stringify!($syscall), ">\n",
+ )
+ };
+ // List for Window (so just Windows).
+ (__ windows $syscall: tt ( $section: tt ) ) => {
+ concat!(
+ " * Windows: <https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-", stringify!($syscall), ">\n",
+ )
+ };
+}
+
mod sockaddr;
mod socket;
mod sockref;
@@ -160,6 +215,9 @@ impl Domain {
/// Domain for IPv6 communication, corresponding to `AF_INET6`.
pub const IPV6: Domain = Domain(sys::AF_INET6);
+ /// Domain for Unix socket communication, corresponding to `AF_UNIX`.
+ pub const UNIX: Domain = Domain(sys::AF_UNIX);
+
/// Returns the correct domain for `address`.
pub const fn for_address(address: SocketAddr) -> Domain {
match address {
@@ -204,14 +262,24 @@ impl Type {
/// Used for protocols such as UDP.
pub const DGRAM: Type = Type(sys::SOCK_DGRAM);
+ /// Type corresponding to `SOCK_DCCP`.
+ ///
+ /// Used for the DCCP protocol.
+ #[cfg(all(feature = "all", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
+ pub const DCCP: Type = Type(sys::SOCK_DCCP);
+
/// Type corresponding to `SOCK_SEQPACKET`.
- #[cfg(feature = "all")]
- #[cfg_attr(docsrs, doc(cfg(feature = "all")))]
+ #[cfg(all(feature = "all", not(target_os = "espidf")))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", not(target_os = "espidf")))))]
pub const SEQPACKET: Type = Type(sys::SOCK_SEQPACKET);
/// Type corresponding to `SOCK_RAW`.
- #[cfg(all(feature = "all", not(target_os = "redox")))]
- #[cfg_attr(docsrs, doc(cfg(all(feature = "all", not(target_os = "redox")))))]
+ #[cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))]
+ #[cfg_attr(
+ docsrs,
+ doc(cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf")))))
+ )]
pub const RAW: Type = Type(sys::SOCK_RAW);
}
@@ -249,6 +317,35 @@ impl Protocol {
/// Protocol corresponding to `UDP`.
pub const UDP: Protocol = Protocol(sys::IPPROTO_UDP);
+
+ #[cfg(target_os = "linux")]
+ /// Protocol corresponding to `MPTCP`.
+ pub const MPTCP: Protocol = Protocol(sys::IPPROTO_MPTCP);
+
+ /// Protocol corresponding to `DCCP`.
+ #[cfg(all(feature = "all", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
+ pub const DCCP: Protocol = Protocol(sys::IPPROTO_DCCP);
+
+ /// Protocol corresponding to `SCTP`.
+ #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
+ pub const SCTP: Protocol = Protocol(sys::IPPROTO_SCTP);
+
+ /// Protocol corresponding to `UDPLITE`.
+ #[cfg(all(
+ feature = "all",
+ any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "linux",
+ )
+ ))]
+ pub const UDPLITE: Protocol = Protocol(sys::IPPROTO_UDPLITE);
+
+ /// Protocol corresponding to `DIVERT`.
+ #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "openbsd")))]
+ pub const DIVERT: Protocol = Protocol(sys::IPPROTO_DIVERT);
}
impl From<c_int> for Protocol {
@@ -280,6 +377,7 @@ impl RecvFlags {
///
/// On Unix this corresponds to the `MSG_TRUNC` flag.
/// On Windows this corresponds to the `WSAEMSGSIZE` error code.
+ #[cfg(not(target_os = "espidf"))]
pub const fn is_truncated(self) -> bool {
self.0 & sys::MSG_TRUNC != 0
}
@@ -327,15 +425,25 @@ impl<'a> DerefMut for MaybeUninitSlice<'a> {
/// See [`Socket::set_tcp_keepalive`].
#[derive(Debug, Clone)]
pub struct TcpKeepalive {
- #[cfg_attr(target_os = "openbsd", allow(dead_code))]
+ #[cfg_attr(any(target_os = "openbsd", target_os = "vita"), allow(dead_code))]
time: Option<Duration>,
- #[cfg(not(any(target_os = "openbsd", target_os = "redox", target_os = "solaris")))]
+ #[cfg(not(any(
+ target_os = "openbsd",
+ target_os = "redox",
+ target_os = "solaris",
+ target_os = "nto",
+ target_os = "espidf",
+ target_os = "vita",
+ )))]
interval: Option<Duration>,
#[cfg(not(any(
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
- target_os = "windows"
+ target_os = "windows",
+ target_os = "nto",
+ target_os = "espidf",
+ target_os = "vita",
)))]
retries: Option<u32>,
}
@@ -345,13 +453,23 @@ impl TcpKeepalive {
pub const fn new() -> TcpKeepalive {
TcpKeepalive {
time: None,
- #[cfg(not(any(target_os = "openbsd", target_os = "redox", target_os = "solaris")))]
+ #[cfg(not(any(
+ target_os = "openbsd",
+ target_os = "redox",
+ target_os = "solaris",
+ target_os = "nto",
+ target_os = "espidf",
+ target_os = "vita",
+ )))]
interval: None,
#[cfg(not(any(
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
- target_os = "windows"
+ target_os = "windows",
+ target_os = "nto",
+ target_os = "espidf",
+ target_os = "vita",
)))]
retries: None,
}
@@ -382,35 +500,35 @@ impl TcpKeepalive {
///
/// Some platforms specify this value in seconds, so sub-second
/// specifications may be omitted.
- #[cfg(all(
- feature = "all",
- any(
+ #[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "tvos",
+ target_os = "watchos",
+ target_os = "windows",
+ ))]
+ #[cfg_attr(
+ docsrs,
+ doc(cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
+ target_os = "ios",
target_os = "linux",
+ target_os = "macos",
target_os = "netbsd",
- target_vendor = "apple",
- windows,
- )
- ))]
- #[cfg_attr(
- docsrs,
- doc(cfg(all(
- feature = "all",
- any(
- target_os = "android",
- target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "fuchsia",
- target_os = "illumos",
- target_os = "linux",
- target_os = "netbsd",
- target_vendor = "apple",
- windows,
- )
+ target_os = "tvos",
+ target_os = "watchos",
+ target_os = "windows",
)))
)]
pub const fn with_interval(self, interval: Duration) -> Self {
@@ -427,15 +545,17 @@ impl TcpKeepalive {
#[cfg(all(
feature = "all",
any(
- doc,
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
+ target_os = "ios",
target_os = "linux",
+ target_os = "macos",
target_os = "netbsd",
- target_vendor = "apple",
+ target_os = "tvos",
+ target_os = "watchos",
)
))]
#[cfg_attr(
@@ -448,9 +568,12 @@ impl TcpKeepalive {
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
+ target_os = "ios",
target_os = "linux",
+ target_os = "macos",
target_os = "netbsd",
- target_vendor = "apple",
+ target_os = "tvos",
+ target_os = "watchos",
)
)))
)]
@@ -461,3 +584,139 @@ impl TcpKeepalive {
}
}
}
+
+/// Configuration of a `sendmsg(2)` system call.
+///
+/// This wraps `msghdr` on Unix and `WSAMSG` on Windows. Also see [`MsgHdrMut`]
+/// for the variant used by `recvmsg(2)`.
+#[cfg(not(target_os = "redox"))]
+pub struct MsgHdr<'addr, 'bufs, 'control> {
+ inner: sys::msghdr,
+ #[allow(clippy::type_complexity)]
+ _lifetimes: PhantomData<(&'addr SockAddr, &'bufs IoSlice<'bufs>, &'control [u8])>,
+}
+
+#[cfg(not(target_os = "redox"))]
+impl<'addr, 'bufs, 'control> MsgHdr<'addr, 'bufs, 'control> {
+ /// Create a new `MsgHdr` with all empty/zero fields.
+ #[allow(clippy::new_without_default)]
+ pub fn new() -> MsgHdr<'addr, 'bufs, 'control> {
+ // SAFETY: all zero is valid for `msghdr` and `WSAMSG`.
+ MsgHdr {
+ inner: unsafe { mem::zeroed() },
+ _lifetimes: PhantomData,
+ }
+ }
+
+ /// Set the address (name) of the message.
+ ///
+ /// Corresponds to setting `msg_name` and `msg_namelen` on Unix and `name`
+ /// and `namelen` on Windows.
+ pub fn with_addr(mut self, addr: &'addr SockAddr) -> Self {
+ sys::set_msghdr_name(&mut self.inner, addr);
+ self
+ }
+
+ /// Set the buffer(s) of the message.
+ ///
+ /// Corresponds to setting `msg_iov` and `msg_iovlen` on Unix and `lpBuffers`
+ /// and `dwBufferCount` on Windows.
+ pub fn with_buffers(mut self, bufs: &'bufs [IoSlice<'_>]) -> Self {
+ let ptr = bufs.as_ptr() as *mut _;
+ sys::set_msghdr_iov(&mut self.inner, ptr, bufs.len());
+ self
+ }
+
+ /// Set the control buffer of the message.
+ ///
+ /// Corresponds to setting `msg_control` and `msg_controllen` on Unix and
+ /// `Control` on Windows.
+ pub fn with_control(mut self, buf: &'control [u8]) -> Self {
+ let ptr = buf.as_ptr() as *mut _;
+ sys::set_msghdr_control(&mut self.inner, ptr, buf.len());
+ self
+ }
+
+ /// Set the flags of the message.
+ ///
+ /// Corresponds to setting `msg_flags` on Unix and `dwFlags` on Windows.
+ pub fn with_flags(mut self, flags: sys::c_int) -> Self {
+ sys::set_msghdr_flags(&mut self.inner, flags);
+ self
+ }
+}
+
+#[cfg(not(target_os = "redox"))]
+impl<'name, 'bufs, 'control> fmt::Debug for MsgHdr<'name, 'bufs, 'control> {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ "MsgHdr".fmt(fmt)
+ }
+}
+
+/// Configuration of a `recvmsg(2)` system call.
+///
+/// This wraps `msghdr` on Unix and `WSAMSG` on Windows. Also see [`MsgHdr`] for
+/// the variant used by `sendmsg(2)`.
+#[cfg(not(target_os = "redox"))]
+pub struct MsgHdrMut<'addr, 'bufs, 'control> {
+ inner: sys::msghdr,
+ #[allow(clippy::type_complexity)]
+ _lifetimes: PhantomData<(
+ &'addr mut SockAddr,
+ &'bufs mut MaybeUninitSlice<'bufs>,
+ &'control mut [u8],
+ )>,
+}
+
+#[cfg(not(target_os = "redox"))]
+impl<'addr, 'bufs, 'control> MsgHdrMut<'addr, 'bufs, 'control> {
+ /// Create a new `MsgHdrMut` with all empty/zero fields.
+ #[allow(clippy::new_without_default)]
+ pub fn new() -> MsgHdrMut<'addr, 'bufs, 'control> {
+ // SAFETY: all zero is valid for `msghdr` and `WSAMSG`.
+ MsgHdrMut {
+ inner: unsafe { mem::zeroed() },
+ _lifetimes: PhantomData,
+ }
+ }
+
+ /// Set the mutable address (name) of the message.
+ ///
+ /// Corresponds to setting `msg_name` and `msg_namelen` on Unix and `name`
+ /// and `namelen` on Windows.
+ #[allow(clippy::needless_pass_by_ref_mut)]
+ pub fn with_addr(mut self, addr: &'addr mut SockAddr) -> Self {
+ sys::set_msghdr_name(&mut self.inner, addr);
+ self
+ }
+
+ /// Set the mutable buffer(s) of the message.
+ ///
+ /// Corresponds to setting `msg_iov` and `msg_iovlen` on Unix and `lpBuffers`
+ /// and `dwBufferCount` on Windows.
+ pub fn with_buffers(mut self, bufs: &'bufs mut [MaybeUninitSlice<'_>]) -> Self {
+ sys::set_msghdr_iov(&mut self.inner, bufs.as_mut_ptr().cast(), bufs.len());
+ self
+ }
+
+ /// Set the mutable control buffer of the message.
+ ///
+ /// Corresponds to setting `msg_control` and `msg_controllen` on Unix and
+ /// `Control` on Windows.
+ pub fn with_control(mut self, buf: &'control mut [MaybeUninit<u8>]) -> Self {
+ sys::set_msghdr_control(&mut self.inner, buf.as_mut_ptr().cast(), buf.len());
+ self
+ }
+
+ /// Returns the flags of the message.
+ pub fn flags(&self) -> RecvFlags {
+ sys::msghdr_flags(&self.inner)
+ }
+}
+
+#[cfg(not(target_os = "redox"))]
+impl<'name, 'bufs, 'control> fmt::Debug for MsgHdrMut<'name, 'bufs, 'control> {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ "MsgHdrMut".fmt(fmt)
+ }
+}
diff --git a/src/sockaddr.rs b/src/sockaddr.rs
index 16a4f0e..6df22fd 100644
--- a/src/sockaddr.rs
+++ b/src/sockaddr.rs
@@ -1,13 +1,17 @@
+use std::hash::Hash;
use std::mem::{self, size_of, MaybeUninit};
use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
-use std::{fmt, io};
+use std::path::Path;
+use std::{fmt, io, ptr};
+
+#[cfg(windows)]
+use windows_sys::Win32::Networking::WinSock::SOCKADDR_IN6_0;
use crate::sys::{
- sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, sockaddr_storage, socklen_t, AF_INET,
- AF_INET6,
+ c_int, sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, sockaddr_storage, socklen_t, AF_INET,
+ AF_INET6, AF_UNIX,
};
-#[cfg(windows)]
-use winapi::shared::ws2ipdef::SOCKADDR_IN6_LH_u;
+use crate::Domain;
/// The address of a socket.
///
@@ -100,7 +104,7 @@ impl SockAddr {
///
/// // Initialise a `SocketAddr` byte calling `getsockname(2)`.
/// let (_, address) = unsafe {
- /// SockAddr::init(|addr_storage, len| {
+ /// SockAddr::try_init(|addr_storage, len| {
/// // The `getsockname(2)` system call will intiliase `storage` for
/// // us, setting `len` to the correct length.
/// if libc::getsockname(socket.as_raw_fd(), addr_storage.cast(), len) == -1 {
@@ -115,7 +119,7 @@ impl SockAddr {
/// # Ok(())
/// # }
/// ```
- pub unsafe fn init<F, T>(init: F) -> io::Result<(T, SockAddr)>
+ pub unsafe fn try_init<F, T>(init: F) -> io::Result<(T, SockAddr)>
where
F: FnOnce(*mut sockaddr_storage, *mut socklen_t) -> io::Result<T>,
{
@@ -139,11 +143,36 @@ impl SockAddr {
})
}
+ /// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path.
+ ///
+ /// Returns an error if the path is longer than `SUN_LEN`.
+ pub fn unix<P>(path: P) -> io::Result<SockAddr>
+ where
+ P: AsRef<Path>,
+ {
+ crate::sys::unix_sockaddr(path.as_ref())
+ }
+
+ /// Set the length of the address.
+ ///
+ /// # Safety
+ ///
+ /// Caller must ensure that the address up to `length` bytes are properly
+ /// initialised.
+ pub unsafe fn set_length(&mut self, length: socklen_t) {
+ self.len = length;
+ }
+
/// Returns this address's family.
pub const fn family(&self) -> sa_family_t {
self.storage.ss_family
}
+ /// Returns this address's `Domain`.
+ pub const fn domain(&self) -> Domain {
+ Domain(self.storage.ss_family as c_int)
+ }
+
/// Returns the size of this address in bytes.
pub const fn len(&self) -> socklen_t {
self.len
@@ -151,29 +180,45 @@ impl SockAddr {
/// Returns a raw pointer to the address.
pub const fn as_ptr(&self) -> *const sockaddr {
- &self.storage as *const _ as *const _
+ ptr::addr_of!(self.storage).cast()
+ }
+
+ /// Retuns the address as the storage.
+ pub const fn as_storage(self) -> sockaddr_storage {
+ self.storage
+ }
+
+ /// Returns true if this address is in the `AF_INET` (IPv4) family, false otherwise.
+ pub const fn is_ipv4(&self) -> bool {
+ self.storage.ss_family == AF_INET as sa_family_t
}
- /// Returns a raw pointer to the address storage.
- #[cfg(all(unix, not(target_os = "redox")))]
- pub(crate) const fn as_storage_ptr(&self) -> *const sockaddr_storage {
- &self.storage
+ /// Returns true if this address is in the `AF_INET6` (IPv6) family, false
+ /// otherwise.
+ pub const fn is_ipv6(&self) -> bool {
+ self.storage.ss_family == AF_INET6 as sa_family_t
+ }
+
+ /// Returns true if this address is of a unix socket (for local interprocess communication),
+ /// i.e. it is from the `AF_UNIX` family, false otherwise.
+ pub fn is_unix(&self) -> bool {
+ self.storage.ss_family == AF_UNIX as sa_family_t
}
/// Returns this address as a `SocketAddr` if it is in the `AF_INET` (IPv4)
/// or `AF_INET6` (IPv6) family, otherwise returns `None`.
pub fn as_socket(&self) -> Option<SocketAddr> {
if self.storage.ss_family == AF_INET as sa_family_t {
- // Safety: if the ss_family field is AF_INET then storage must be a sockaddr_in.
- let addr = unsafe { &*(&self.storage as *const _ as *const sockaddr_in) };
-
+ // SAFETY: if the `ss_family` field is `AF_INET` then storage must
+ // be a `sockaddr_in`.
+ let addr = unsafe { &*(ptr::addr_of!(self.storage).cast::<sockaddr_in>()) };
let ip = crate::sys::from_in_addr(addr.sin_addr);
let port = u16::from_be(addr.sin_port);
Some(SocketAddr::V4(SocketAddrV4::new(ip, port)))
} else if self.storage.ss_family == AF_INET6 as sa_family_t {
- // Safety: if the ss_family field is AF_INET6 then storage must be a sockaddr_in6.
- let addr = unsafe { &*(&self.storage as *const _ as *const sockaddr_in6) };
-
+ // SAFETY: if the `ss_family` field is `AF_INET6` then storage must
+ // be a `sockaddr_in6`.
+ let addr = unsafe { &*(ptr::addr_of!(self.storage).cast::<sockaddr_in6>()) };
let ip = crate::sys::from_in6_addr(addr.sin6_addr);
let port = u16::from_be(addr.sin6_port);
Some(SocketAddr::V6(SocketAddrV6::new(
@@ -184,7 +229,7 @@ impl SockAddr {
addr.sin6_scope_id,
#[cfg(windows)]
unsafe {
- *addr.u.sin6_scope_id()
+ addr.Anonymous.sin6_scope_id
},
)))
} else {
@@ -209,6 +254,14 @@ impl SockAddr {
_ => None,
}
}
+
+ /// Returns the initialised storage bytes.
+ fn as_bytes(&self) -> &[u8] {
+ // SAFETY: `self.storage` is a C struct which can always be treated a
+ // slice of bytes. Futhermore we ensure we don't read any unitialised
+ // bytes by using `self.len`.
+ unsafe { std::slice::from_raw_parts(self.as_ptr().cast(), self.len as usize) }
+ }
}
impl From<SocketAddr> for SockAddr {
@@ -222,70 +275,77 @@ impl From<SocketAddr> for SockAddr {
impl From<SocketAddrV4> for SockAddr {
fn from(addr: SocketAddrV4) -> SockAddr {
- let sockaddr_in = sockaddr_in {
- sin_family: AF_INET as sa_family_t,
- sin_port: addr.port().to_be(),
- sin_addr: crate::sys::to_in_addr(addr.ip()),
- sin_zero: Default::default(),
- #[cfg(any(
- target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "haiku",
- target_os = "ios",
- target_os = "macos",
- target_os = "netbsd",
- target_os = "openbsd"
- ))]
- sin_len: 0,
+ // SAFETY: a `sockaddr_storage` of all zeros is valid.
+ let mut storage = unsafe { mem::zeroed::<sockaddr_storage>() };
+ let len = {
+ let storage = unsafe { &mut *ptr::addr_of_mut!(storage).cast::<sockaddr_in>() };
+ storage.sin_family = AF_INET as sa_family_t;
+ storage.sin_port = addr.port().to_be();
+ storage.sin_addr = crate::sys::to_in_addr(addr.ip());
+ storage.sin_zero = Default::default();
+ mem::size_of::<sockaddr_in>() as socklen_t
};
- let mut storage = MaybeUninit::<sockaddr_storage>::zeroed();
- // Safety: A `sockaddr_in` is memory compatible with a `sockaddr_storage`
- unsafe { (storage.as_mut_ptr() as *mut sockaddr_in).write(sockaddr_in) };
- SockAddr {
- storage: unsafe { storage.assume_init() },
- len: mem::size_of::<sockaddr_in>() as socklen_t,
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "hermit",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "nto",
+ target_os = "openbsd",
+ target_os = "tvos",
+ target_os = "vxworks",
+ target_os = "watchos",
+ ))]
+ {
+ storage.ss_len = len as u8;
}
+ SockAddr { storage, len }
}
}
impl From<SocketAddrV6> for SockAddr {
fn from(addr: SocketAddrV6) -> SockAddr {
- #[cfg(windows)]
- let u = unsafe {
- let mut u = mem::zeroed::<SOCKADDR_IN6_LH_u>();
- *u.sin6_scope_id_mut() = addr.scope_id();
- u
- };
-
- let sockaddr_in6 = sockaddr_in6 {
- sin6_family: AF_INET6 as sa_family_t,
- sin6_port: addr.port().to_be(),
- sin6_addr: crate::sys::to_in6_addr(addr.ip()),
- sin6_flowinfo: addr.flowinfo(),
+ // SAFETY: a `sockaddr_storage` of all zeros is valid.
+ let mut storage = unsafe { mem::zeroed::<sockaddr_storage>() };
+ let len = {
+ let storage = unsafe { &mut *ptr::addr_of_mut!(storage).cast::<sockaddr_in6>() };
+ storage.sin6_family = AF_INET6 as sa_family_t;
+ storage.sin6_port = addr.port().to_be();
+ storage.sin6_addr = crate::sys::to_in6_addr(addr.ip());
+ storage.sin6_flowinfo = addr.flowinfo();
#[cfg(unix)]
- sin6_scope_id: addr.scope_id(),
+ {
+ storage.sin6_scope_id = addr.scope_id();
+ }
#[cfg(windows)]
- u,
- #[cfg(any(
- target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "haiku",
- target_os = "ios",
- target_os = "macos",
- target_os = "netbsd",
- target_os = "openbsd"
- ))]
- sin6_len: 0,
- #[cfg(any(target_os = "solaris", target_os = "illumos"))]
- __sin6_src_id: 0,
+ {
+ storage.Anonymous = SOCKADDR_IN6_0 {
+ sin6_scope_id: addr.scope_id(),
+ };
+ }
+ mem::size_of::<sockaddr_in6>() as socklen_t
};
- let mut storage = MaybeUninit::<sockaddr_storage>::zeroed();
- // Safety: A `sockaddr_in6` is memory compatible with a `sockaddr_storage`
- unsafe { (storage.as_mut_ptr() as *mut sockaddr_in6).write(sockaddr_in6) };
- SockAddr {
- storage: unsafe { storage.assume_init() },
- len: mem::size_of::<sockaddr_in6>() as socklen_t,
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "hermit",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "nto",
+ target_os = "openbsd",
+ target_os = "tvos",
+ target_os = "vxworks",
+ target_os = "watchos",
+ ))]
+ {
+ storage.ss_len = len as u8;
}
+ SockAddr { storage, len }
}
}
@@ -300,8 +360,11 @@ impl fmt::Debug for SockAddr {
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
+ target_os = "nto",
target_os = "openbsd",
+ target_os = "tvos",
target_os = "vxworks",
+ target_os = "watchos",
))]
f.field("ss_len", &self.storage.ss_len);
f.field("ss_family", &self.storage.ss_family)
@@ -310,40 +373,200 @@ impl fmt::Debug for SockAddr {
}
}
-#[test]
-fn ipv4() {
- use std::net::Ipv4Addr;
- let std = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876);
- let addr = SockAddr::from(std);
- assert_eq!(addr.family(), AF_INET as sa_family_t);
- assert_eq!(addr.len(), size_of::<sockaddr_in>() as socklen_t);
- assert_eq!(addr.as_socket(), Some(SocketAddr::V4(std)));
- assert_eq!(addr.as_socket_ipv4(), Some(std));
- assert!(addr.as_socket_ipv6().is_none());
-
- let addr = SockAddr::from(SocketAddr::from(std));
- assert_eq!(addr.family(), AF_INET as sa_family_t);
- assert_eq!(addr.len(), size_of::<sockaddr_in>() as socklen_t);
- assert_eq!(addr.as_socket(), Some(SocketAddr::V4(std)));
- assert_eq!(addr.as_socket_ipv4(), Some(std));
- assert!(addr.as_socket_ipv6().is_none());
+impl PartialEq for SockAddr {
+ fn eq(&self, other: &Self) -> bool {
+ self.as_bytes() == other.as_bytes()
+ }
}
-#[test]
-fn ipv6() {
- use std::net::Ipv6Addr;
- let std = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12);
- let addr = SockAddr::from(std);
- assert_eq!(addr.family(), AF_INET6 as sa_family_t);
- assert_eq!(addr.len(), size_of::<sockaddr_in6>() as socklen_t);
- assert_eq!(addr.as_socket(), Some(SocketAddr::V6(std)));
- assert!(addr.as_socket_ipv4().is_none());
- assert_eq!(addr.as_socket_ipv6(), Some(std));
-
- let addr = SockAddr::from(SocketAddr::from(std));
- assert_eq!(addr.family(), AF_INET6 as sa_family_t);
- assert_eq!(addr.len(), size_of::<sockaddr_in6>() as socklen_t);
- assert_eq!(addr.as_socket(), Some(SocketAddr::V6(std)));
- assert!(addr.as_socket_ipv4().is_none());
- assert_eq!(addr.as_socket_ipv6(), Some(std));
+impl Eq for SockAddr {}
+
+impl Hash for SockAddr {
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ self.as_bytes().hash(state);
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn ipv4() {
+ use std::net::Ipv4Addr;
+ let std = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876);
+ let addr = SockAddr::from(std);
+ assert!(addr.is_ipv4());
+ assert!(!addr.is_ipv6());
+ assert!(!addr.is_unix());
+ assert_eq!(addr.family(), AF_INET as sa_family_t);
+ assert_eq!(addr.domain(), Domain::IPV4);
+ assert_eq!(addr.len(), size_of::<sockaddr_in>() as socklen_t);
+ assert_eq!(addr.as_socket(), Some(SocketAddr::V4(std)));
+ assert_eq!(addr.as_socket_ipv4(), Some(std));
+ assert!(addr.as_socket_ipv6().is_none());
+
+ let addr = SockAddr::from(SocketAddr::from(std));
+ assert_eq!(addr.family(), AF_INET as sa_family_t);
+ assert_eq!(addr.len(), size_of::<sockaddr_in>() as socklen_t);
+ assert_eq!(addr.as_socket(), Some(SocketAddr::V4(std)));
+ assert_eq!(addr.as_socket_ipv4(), Some(std));
+ assert!(addr.as_socket_ipv6().is_none());
+ #[cfg(unix)]
+ {
+ assert!(addr.as_pathname().is_none());
+ assert!(addr.as_abstract_namespace().is_none());
+ }
+ }
+
+ #[test]
+ fn ipv6() {
+ use std::net::Ipv6Addr;
+ let std = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12);
+ let addr = SockAddr::from(std);
+ assert!(addr.is_ipv6());
+ assert!(!addr.is_ipv4());
+ assert!(!addr.is_unix());
+ assert_eq!(addr.family(), AF_INET6 as sa_family_t);
+ assert_eq!(addr.domain(), Domain::IPV6);
+ assert_eq!(addr.len(), size_of::<sockaddr_in6>() as socklen_t);
+ assert_eq!(addr.as_socket(), Some(SocketAddr::V6(std)));
+ assert!(addr.as_socket_ipv4().is_none());
+ assert_eq!(addr.as_socket_ipv6(), Some(std));
+
+ let addr = SockAddr::from(SocketAddr::from(std));
+ assert_eq!(addr.family(), AF_INET6 as sa_family_t);
+ assert_eq!(addr.len(), size_of::<sockaddr_in6>() as socklen_t);
+ assert_eq!(addr.as_socket(), Some(SocketAddr::V6(std)));
+ assert!(addr.as_socket_ipv4().is_none());
+ assert_eq!(addr.as_socket_ipv6(), Some(std));
+ #[cfg(unix)]
+ {
+ assert!(addr.as_pathname().is_none());
+ assert!(addr.as_abstract_namespace().is_none());
+ }
+ }
+
+ #[test]
+ fn ipv4_eq() {
+ use std::net::Ipv4Addr;
+
+ let std1 = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876);
+ let std2 = SocketAddrV4::new(Ipv4Addr::new(5, 6, 7, 8), 8765);
+
+ test_eq(
+ SockAddr::from(std1),
+ SockAddr::from(std1),
+ SockAddr::from(std2),
+ );
+ }
+
+ #[test]
+ fn ipv4_hash() {
+ use std::net::Ipv4Addr;
+
+ let std1 = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876);
+ let std2 = SocketAddrV4::new(Ipv4Addr::new(5, 6, 7, 8), 8765);
+
+ test_hash(
+ SockAddr::from(std1),
+ SockAddr::from(std1),
+ SockAddr::from(std2),
+ );
+ }
+
+ #[test]
+ fn ipv6_eq() {
+ use std::net::Ipv6Addr;
+
+ let std1 = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12);
+ let std2 = SocketAddrV6::new(Ipv6Addr::new(3, 4, 5, 6, 7, 8, 9, 0), 7654, 13, 14);
+
+ test_eq(
+ SockAddr::from(std1),
+ SockAddr::from(std1),
+ SockAddr::from(std2),
+ );
+ }
+
+ #[test]
+ fn ipv6_hash() {
+ use std::net::Ipv6Addr;
+
+ let std1 = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12);
+ let std2 = SocketAddrV6::new(Ipv6Addr::new(3, 4, 5, 6, 7, 8, 9, 0), 7654, 13, 14);
+
+ test_hash(
+ SockAddr::from(std1),
+ SockAddr::from(std1),
+ SockAddr::from(std2),
+ );
+ }
+
+ #[test]
+ fn ipv4_ipv6_eq() {
+ use std::net::Ipv4Addr;
+ use std::net::Ipv6Addr;
+
+ let std1 = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876);
+ let std2 = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12);
+
+ test_eq(
+ SockAddr::from(std1),
+ SockAddr::from(std1),
+ SockAddr::from(std2),
+ );
+
+ test_eq(
+ SockAddr::from(std2),
+ SockAddr::from(std2),
+ SockAddr::from(std1),
+ );
+ }
+
+ #[test]
+ fn ipv4_ipv6_hash() {
+ use std::net::Ipv4Addr;
+ use std::net::Ipv6Addr;
+
+ let std1 = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876);
+ let std2 = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12);
+
+ test_hash(
+ SockAddr::from(std1),
+ SockAddr::from(std1),
+ SockAddr::from(std2),
+ );
+
+ test_hash(
+ SockAddr::from(std2),
+ SockAddr::from(std2),
+ SockAddr::from(std1),
+ );
+ }
+
+ #[allow(clippy::eq_op)] // allow a0 == a0 check
+ fn test_eq(a0: SockAddr, a1: SockAddr, b: SockAddr) {
+ assert!(a0 == a0);
+ assert!(a0 == a1);
+ assert!(a1 == a0);
+ assert!(a0 != b);
+ assert!(b != a0);
+ }
+
+ fn test_hash(a0: SockAddr, a1: SockAddr, b: SockAddr) {
+ assert!(calculate_hash(&a0) == calculate_hash(&a0));
+ assert!(calculate_hash(&a0) == calculate_hash(&a1));
+ // technically unequal values can have the same hash, in this case x != z and both have different hashes
+ assert!(calculate_hash(&a0) != calculate_hash(&b));
+ }
+
+ fn calculate_hash(x: &SockAddr) -> u64 {
+ use std::collections::hash_map::DefaultHasher;
+ use std::hash::Hasher;
+
+ let mut hasher = DefaultHasher::new();
+ x.hash(&mut hasher);
+ hasher.finish()
+ }
}
diff --git a/src/socket.rs b/src/socket.rs
index 87c143a..efe2b0a 100644
--- a/src/socket.rs
+++ b/src/socket.rs
@@ -11,7 +11,9 @@ use std::io::{self, Read, Write};
#[cfg(not(target_os = "redox"))]
use std::io::{IoSlice, IoSliceMut};
use std::mem::MaybeUninit;
-use std::net::{self, Ipv4Addr, Ipv6Addr, Shutdown};
+#[cfg(not(target_os = "nto"))]
+use std::net::Ipv6Addr;
+use std::net::{self, Ipv4Addr, Shutdown};
#[cfg(unix)]
use std::os::unix::io::{FromRawFd, IntoRawFd};
#[cfg(windows)]
@@ -19,9 +21,11 @@ use std::os::windows::io::{FromRawSocket, IntoRawSocket};
use std::time::Duration;
use crate::sys::{self, c_int, getsockopt, setsockopt, Bool};
+#[cfg(all(unix, not(target_os = "redox")))]
+use crate::MsgHdrMut;
use crate::{Domain, Protocol, SockAddr, TcpKeepalive, Type};
#[cfg(not(target_os = "redox"))]
-use crate::{MaybeUninitSlice, RecvFlags};
+use crate::{MaybeUninitSlice, MsgHdr, RecvFlags};
/// Owned wrapper around a system socket.
///
@@ -44,8 +48,8 @@ use crate::{MaybeUninitSlice, RecvFlags};
/// # Notes
///
/// Some methods that set options on `Socket` require two system calls to set
-/// there options without overwriting previously set options. We do this by
-/// first getting the current settings, applying the desired changes and than
+/// their options without overwriting previously set options. We do this by
+/// first getting the current settings, applying the desired changes, and then
/// updating the settings. This means that the operation is **not** atomic. This
/// can lead to a data race when two threads are changing options in parallel.
///
@@ -55,13 +59,12 @@ use crate::{MaybeUninitSlice, RecvFlags};
/// use std::net::{SocketAddr, TcpListener};
/// use socket2::{Socket, Domain, Type};
///
-/// // create a TCP listener bound to two addresses
-/// let socket = Socket::new(Domain::IPV4, Type::STREAM, None)?;
+/// // create a TCP listener
+/// let socket = Socket::new(Domain::IPV6, Type::STREAM, None)?;
///
/// let address: SocketAddr = "[::1]:12345".parse().unwrap();
/// let address = address.into();
/// socket.bind(&address)?;
-/// socket.bind(&address)?;
/// socket.listen(128)?;
///
/// let listener: TcpListener = socket.into();
@@ -124,6 +127,7 @@ impl Socket {
/// the socket is made non-inheritable.
///
/// [`Socket::new_raw`] can be used if you don't want these flags to be set.
+ #[doc = man_links!(socket(2))]
pub fn new(domain: Domain, ty: Type, protocol: Option<Protocol>) -> io::Result<Socket> {
let ty = set_common_type(ty);
Socket::new_raw(domain, ty, protocol).and_then(set_common_flags)
@@ -134,7 +138,7 @@ impl Socket {
/// This function corresponds to `socket(2)` on Unix and `WSASocketW` on
/// Windows and simply creates a new socket, no other configuration is done.
pub fn new_raw(domain: Domain, ty: Type, protocol: Option<Protocol>) -> io::Result<Socket> {
- let protocol = protocol.map(|p| p.0).unwrap_or(0);
+ let protocol = protocol.map_or(0, |p| p.0);
sys::socket(domain.0, ty.0, protocol).map(Socket::from_raw)
}
@@ -144,7 +148,8 @@ impl Socket {
///
/// This function sets the same flags as in done for [`Socket::new`],
/// [`Socket::pair_raw`] can be used if you don't want to set those flags.
- #[cfg(any(doc, all(feature = "all", unix)))]
+ #[doc = man_links!(unix: socketpair(2))]
+ #[cfg(all(feature = "all", unix))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "all", unix))))]
pub fn pair(
domain: Domain,
@@ -161,14 +166,14 @@ impl Socket {
/// Creates a pair of sockets which are connected to each other.
///
/// This function corresponds to `socketpair(2)`.
- #[cfg(any(doc, all(feature = "all", unix)))]
+ #[cfg(all(feature = "all", unix))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "all", unix))))]
pub fn pair_raw(
domain: Domain,
ty: Type,
protocol: Option<Protocol>,
) -> io::Result<(Socket, Socket)> {
- let protocol = protocol.map(|p| p.0).unwrap_or(0);
+ let protocol = protocol.map_or(0, |p| p.0);
sys::socketpair(domain.0, ty.0, protocol)
.map(|[a, b]| (Socket::from_raw(a), Socket::from_raw(b)))
}
@@ -177,6 +182,7 @@ impl Socket {
///
/// This function directly corresponds to the `bind(2)` function on Windows
/// and Unix.
+ #[doc = man_links!(bind(2))]
pub fn bind(&self, address: &SockAddr) -> io::Result<()> {
sys::bind(self.as_raw(), address)
}
@@ -188,6 +194,7 @@ impl Socket {
///
/// An error will be returned if `listen` or `connect` has already been
/// called on this builder.
+ #[doc = man_links!(connect(2))]
///
/// # Notes
///
@@ -242,6 +249,7 @@ impl Socket {
///
/// An error will be returned if `listen` or `connect` has already been
/// called on this builder.
+ #[doc = man_links!(listen(2))]
pub fn listen(&self, backlog: c_int) -> io::Result<()> {
sys::listen(self.as_raw(), backlog)
}
@@ -253,6 +261,7 @@ impl Socket {
///
/// This function sets the same flags as in done for [`Socket::new`],
/// [`Socket::accept_raw`] can be used if you don't want to set those flags.
+ #[doc = man_links!(accept(2))]
pub fn accept(&self) -> io::Result<(Socket, SockAddr)> {
// Use `accept4` on platforms that support it.
#[cfg(any(
@@ -299,6 +308,10 @@ impl Socket {
/// Returns the socket address of the local half of this socket.
///
+ /// This function directly corresponds to the `getsockname(2)` function on
+ /// Windows and Unix.
+ #[doc = man_links!(getsockname(2))]
+ ///
/// # Notes
///
/// Depending on the OS this may return an error if the socket is not
@@ -311,6 +324,10 @@ impl Socket {
/// Returns the socket address of the remote peer of this socket.
///
+ /// This function directly corresponds to the `getpeername(2)` function on
+ /// Windows and Unix.
+ #[doc = man_links!(getpeername(2))]
+ ///
/// # Notes
///
/// This returns an error if the socket is not [`connect`ed].
@@ -343,7 +360,21 @@ impl Socket {
sys::try_clone(self.as_raw()).map(Socket::from_raw)
}
- /// Moves this TCP stream into or out of nonblocking mode.
+ /// Returns true if this socket is set to nonblocking mode, false otherwise.
+ ///
+ /// # Notes
+ ///
+ /// On Unix this corresponds to calling `fcntl` returning the value of
+ /// `O_NONBLOCK`.
+ ///
+ /// On Windows it is not possible retrieve the nonblocking mode status.
+ #[cfg(all(feature = "all", unix))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", unix))))]
+ pub fn nonblocking(&self) -> io::Result<bool> {
+ sys::nonblocking(self.as_raw())
+ }
+
+ /// Moves this socket into or out of nonblocking mode.
///
/// # Notes
///
@@ -359,6 +390,7 @@ impl Socket {
///
/// This function will cause all pending and future I/O on the specified
/// portions to return immediately with an appropriate value.
+ #[doc = man_links!(shutdown(2))]
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
sys::shutdown(self.as_raw(), how)
}
@@ -368,6 +400,7 @@ impl Socket {
///
/// The [`connect`] method will connect this socket to a remote address.
/// This method might fail if the socket is not connected.
+ #[doc = man_links!(recv(2))]
///
/// [`connect`]: Socket::connect
///
@@ -394,6 +427,7 @@ impl Socket {
///
/// [`recv`]: Socket::recv
/// [`out_of_band_inline`]: Socket::out_of_band_inline
+ #[cfg_attr(target_os = "redox", allow(rustdoc::broken_intra_doc_links))]
pub fn recv_out_of_band(&self, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
self.recv_with_flags(buf, sys::MSG_OOB)
}
@@ -419,6 +453,7 @@ impl Socket {
/// In addition to the number of bytes read, this function returns the flags
/// for the received message. See [`RecvFlags`] for more information about
/// the returned flags.
+ #[doc = man_links!(recvmsg(2))]
///
/// [`recv`]: Socket::recv
/// [`connect`]: Socket::connect
@@ -484,6 +519,7 @@ impl Socket {
/// Receives data from the socket. On success, returns the number of bytes
/// read and the address from whence the data came.
+ #[doc = man_links!(recvfrom(2))]
///
/// # Safety
///
@@ -510,6 +546,7 @@ impl Socket {
/// Receives data from the socket. Returns the amount of bytes read, the
/// [`RecvFlags`] and the remote address from the data is coming. Unlike
/// [`recv_from`] this allows passing multiple buffers.
+ #[doc = man_links!(recvmsg(2))]
///
/// [`recv_from`]: Socket::recv_from
///
@@ -562,17 +599,56 @@ impl Socket {
/// `peek_from` makes the same safety guarantees regarding the `buf`fer as
/// [`recv`].
///
+ /// # Note: Datagram Sockets
+ /// For datagram sockets, the behavior of this method when `buf` is smaller than
+ /// the datagram at the head of the receive queue differs between Windows and
+ /// Unix-like platforms (Linux, macOS, BSDs, etc: colloquially termed "*nix").
+ ///
+ /// On *nix platforms, the datagram is truncated to the length of `buf`.
+ ///
+ /// On Windows, an error corresponding to `WSAEMSGSIZE` will be returned.
+ ///
+ /// For consistency between platforms, be sure to provide a sufficiently large buffer to avoid
+ /// truncation; the exact size required depends on the underlying protocol.
+ ///
+ /// If you just want to know the sender of the data, try [`peek_sender`].
+ ///
/// [`recv`]: Socket::recv
+ /// [`peek_sender`]: Socket::peek_sender
pub fn peek_from(&self, buf: &mut [MaybeUninit<u8>]) -> io::Result<(usize, SockAddr)> {
self.recv_from_with_flags(buf, sys::MSG_PEEK)
}
+ /// Retrieve the sender for the data at the head of the receive queue.
+ ///
+ /// This is equivalent to calling [`peek_from`] with a zero-sized buffer,
+ /// but suppresses the `WSAEMSGSIZE` error on Windows.
+ ///
+ /// [`peek_from`]: Socket::peek_from
+ pub fn peek_sender(&self) -> io::Result<SockAddr> {
+ sys::peek_sender(self.as_raw())
+ }
+
+ /// Receive a message from a socket using a message structure.
+ ///
+ /// This is not supported on Windows as calling `WSARecvMsg` (the `recvmsg`
+ /// equivalent) is not straight forward on Windows. See
+ /// <https://github.com/microsoft/Windows-classic-samples/blob/7cbd99ac1d2b4a0beffbaba29ea63d024ceff700/Samples/Win7Samples/netds/winsock/recvmsg/rmmc.cpp>
+ /// for an example (in C++).
+ #[doc = man_links!(recvmsg(2))]
+ #[cfg(all(unix, not(target_os = "redox")))]
+ #[cfg_attr(docsrs, doc(cfg(all(unix, not(target_os = "redox")))))]
+ pub fn recvmsg(&self, msg: &mut MsgHdrMut<'_, '_, '_>, flags: sys::c_int) -> io::Result<usize> {
+ sys::recvmsg(self.as_raw(), msg, flags)
+ }
+
/// Sends data on the socket to a connected peer.
///
/// This is typically used on TCP sockets or datagram sockets which have
/// been connected.
///
/// On success returns the number of bytes that were sent.
+ #[doc = man_links!(send(2))]
pub fn send(&self, buf: &[u8]) -> io::Result<usize> {
self.send_with_flags(buf, 0)
}
@@ -580,7 +656,7 @@ impl Socket {
/// Identical to [`send`] but allows for specification of arbitrary flags to the underlying
/// `send` call.
///
- /// [`send`]: #method.send
+ /// [`send`]: Socket::send
pub fn send_with_flags(&self, buf: &[u8], flags: c_int) -> io::Result<usize> {
sys::send(self.as_raw(), buf, flags)
}
@@ -594,6 +670,7 @@ impl Socket {
/// Identical to [`send_vectored`] but allows for specification of arbitrary
/// flags to the underlying `sendmsg`/`WSASend` call.
+ #[doc = man_links!(sendmsg(2))]
///
/// [`send_vectored`]: Socket::send_vectored
#[cfg(not(target_os = "redox"))]
@@ -611,8 +688,9 @@ impl Socket {
///
/// For more information, see [`send`], [`out_of_band_inline`].
///
- /// [`send`]: #method.send
- /// [`out_of_band_inline`]: #method.out_of_band_inline
+ /// [`send`]: Socket::send
+ /// [`out_of_band_inline`]: Socket::out_of_band_inline
+ #[cfg_attr(target_os = "redox", allow(rustdoc::broken_intra_doc_links))]
pub fn send_out_of_band(&self, buf: &[u8]) -> io::Result<usize> {
self.send_with_flags(buf, sys::MSG_OOB)
}
@@ -621,6 +699,7 @@ impl Socket {
/// number of bytes written.
///
/// This is typically used on UDP or datagram-oriented sockets.
+ #[doc = man_links!(sendto(2))]
pub fn send_to(&self, buf: &[u8], addr: &SockAddr) -> io::Result<usize> {
self.send_to_with_flags(buf, addr, 0)
}
@@ -640,6 +719,7 @@ impl Socket {
/// Send data to a peer listening on `addr`. Returns the amount of bytes
/// written.
+ #[doc = man_links!(sendmsg(2))]
#[cfg(not(target_os = "redox"))]
#[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))]
pub fn send_to_vectored(&self, bufs: &[IoSlice<'_>], addr: &SockAddr) -> io::Result<usize> {
@@ -660,12 +740,20 @@ impl Socket {
) -> io::Result<usize> {
sys::send_to_vectored(self.as_raw(), bufs, addr, flags)
}
+
+ /// Send a message on a socket using a message structure.
+ #[doc = man_links!(sendmsg(2))]
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))]
+ pub fn sendmsg(&self, msg: &MsgHdr<'_, '_, '_>, flags: sys::c_int) -> io::Result<usize> {
+ sys::sendmsg(self.as_raw(), msg, flags)
+ }
}
/// Set `SOCK_CLOEXEC` and `NO_HANDLE_INHERIT` on the `ty`pe on platforms that
/// support it.
#[inline(always)]
-fn set_common_type(ty: Type) -> Type {
+const fn set_common_type(ty: Type) -> Type {
// On platforms that support it set `SOCK_CLOEXEC`.
#[cfg(any(
target_os = "android",
@@ -702,12 +790,19 @@ fn set_common_flags(socket: Socket) -> io::Result<Socket> {
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd",
+ target_os = "espidf",
+ target_os = "vita",
))
))]
socket._set_cloexec(true)?;
// On Apple platforms set `NOSIGPIPE`.
- #[cfg(target_vendor = "apple")]
+ #[cfg(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
+ ))]
socket._set_nosigpipe(true)?;
Ok(socket)
@@ -983,7 +1078,7 @@ impl Socket {
}
}
-fn from_linger(linger: sys::linger) -> Option<Duration> {
+const fn from_linger(linger: sys::linger) -> Option<Duration> {
if linger.l_onoff == 0 {
None
} else {
@@ -991,7 +1086,7 @@ fn from_linger(linger: sys::linger) -> Option<Duration> {
}
}
-fn into_linger(duration: Option<Duration>) -> sys::linger {
+const fn into_linger(duration: Option<Duration>) -> sys::linger {
match duration {
Some(duration) => sys::linger {
l_onoff: 1,
@@ -1015,8 +1110,11 @@ impl Socket {
/// For more information about this option, see [`set_header_included`].
///
/// [`set_header_included`]: Socket::set_header_included
- #[cfg(all(feature = "all", not(target_os = "redox")))]
- #[cfg_attr(docsrs, doc(all(feature = "all", not(target_os = "redox"))))]
+ #[cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))]
+ #[cfg_attr(
+ docsrs,
+ doc(cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf")))))
+ )]
pub fn header_included(&self) -> io::Result<bool> {
unsafe {
getsockopt::<c_int>(self.as_raw(), sys::IPPROTO_IP, sys::IP_HDRINCL)
@@ -1035,8 +1133,15 @@ impl Socket {
/// [raw(7)]: https://man7.org/linux/man-pages/man7/raw.7.html
/// [`IP_TTL`]: Socket::set_ttl
/// [`IP_TOS`]: Socket::set_tos
- #[cfg(all(feature = "all", not(target_os = "redox")))]
- #[cfg_attr(docsrs, doc(all(feature = "all", not(target_os = "redox"))))]
+ #[cfg_attr(
+ any(target_os = "fuchsia", target_os = "illumos", target_os = "solaris"),
+ allow(rustdoc::broken_intra_doc_links)
+ )]
+ #[cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))]
+ #[cfg_attr(
+ docsrs,
+ doc(cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf")))))
+ )]
pub fn set_header_included(&self, included: bool) -> io::Result<()> {
unsafe {
setsockopt(
@@ -1053,7 +1158,7 @@ impl Socket {
/// For more information about this option, see [`set_ip_transparent`].
///
/// [`set_ip_transparent`]: Socket::set_ip_transparent
- #[cfg(any(doc, all(feature = "all", target_os = "linux")))]
+ #[cfg(all(feature = "all", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
pub fn ip_transparent(&self) -> io::Result<bool> {
unsafe {
@@ -1077,7 +1182,7 @@ impl Socket {
///
/// TProxy redirection with the iptables TPROXY target also
/// requires that this option be set on the redirected socket.
- #[cfg(any(doc, all(feature = "all", target_os = "linux")))]
+ #[cfg(all(feature = "all", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
pub fn set_ip_transparent(&self, transparent: bool) -> io::Result<()> {
unsafe {
@@ -1132,12 +1237,16 @@ impl Socket {
/// the local interface with which the system should join the multicast
/// group. See [`InterfaceIndexOrAddress`].
#[cfg(not(any(
+ target_os = "aix",
target_os = "haiku",
target_os = "illumos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
+ target_os = "nto",
+ target_os = "espidf",
+ target_os = "vita",
)))]
pub fn join_multicast_v4_n(
&self,
@@ -1161,12 +1270,16 @@ impl Socket {
///
/// [`join_multicast_v4_n`]: Socket::join_multicast_v4_n
#[cfg(not(any(
+ target_os = "aix",
target_os = "haiku",
target_os = "illumos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
+ target_os = "nto",
+ target_os = "espidf",
+ target_os = "vita",
)))]
pub fn leave_multicast_v4_n(
&self,
@@ -1198,6 +1311,9 @@ impl Socket {
target_os = "openbsd",
target_os = "redox",
target_os = "fuchsia",
+ target_os = "nto",
+ target_os = "espidf",
+ target_os = "vita",
)))]
pub fn join_ssm_v4(
&self,
@@ -1232,6 +1348,9 @@ impl Socket {
target_os = "openbsd",
target_os = "redox",
target_os = "fuchsia",
+ target_os = "nto",
+ target_os = "espidf",
+ target_os = "vita",
)))]
pub fn leave_ssm_v4(
&self,
@@ -1395,10 +1514,11 @@ impl Socket {
/// Set the value of the `IP_RECVTOS` option for this socket.
///
- /// If enabled, the IP_TOS ancillary message is passed with
+ /// If enabled, the `IP_TOS` ancillary message is passed with
/// incoming packets. It contains a byte which specifies the
/// Type of Service/Precedence field of the packet header.
#[cfg(not(any(
+ target_os = "aix",
target_os = "dragonfly",
target_os = "fuchsia",
target_os = "illumos",
@@ -1406,11 +1526,12 @@ impl Socket {
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
- target_os = "windows",
+ target_os = "haiku",
+ target_os = "nto",
+ target_os = "espidf",
+ target_os = "vita",
)))]
pub fn set_recv_tos(&self, recv_tos: bool) -> io::Result<()> {
- let recv_tos = if recv_tos { 1 } else { 0 };
-
unsafe {
setsockopt(
self.as_raw(),
@@ -1427,6 +1548,7 @@ impl Socket {
///
/// [`set_recv_tos`]: Socket::set_recv_tos
#[cfg(not(any(
+ target_os = "aix",
target_os = "dragonfly",
target_os = "fuchsia",
target_os = "illumos",
@@ -1434,7 +1556,10 @@ impl Socket {
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
- target_os = "windows",
+ target_os = "haiku",
+ target_os = "nto",
+ target_os = "espidf",
+ target_os = "vita",
)))]
pub fn recv_tos(&self) -> io::Result<bool> {
unsafe {
@@ -1457,6 +1582,7 @@ impl Socket {
/// This function specifies a new multicast group for this socket to join.
/// The address must be a valid multicast address, and `interface` is the
/// index of the interface to join/leave (or 0 to indicate any interface).
+ #[cfg(not(target_os = "nto"))]
pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> {
let mreq = sys::Ipv6Mreq {
ipv6mr_multiaddr: sys::to_in6_addr(multiaddr),
@@ -1480,6 +1606,7 @@ impl Socket {
/// For more information about this option, see [`join_multicast_v6`].
///
/// [`join_multicast_v6`]: Socket::join_multicast_v6
+ #[cfg(not(target_os = "nto"))]
pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> {
let mreq = sys::Ipv6Mreq {
ipv6mr_multiaddr: sys::to_in6_addr(multiaddr),
@@ -1633,6 +1760,58 @@ impl Socket {
)
}
}
+
+ /// Get the value of the `IPV6_RECVTCLASS` option for this socket.
+ ///
+ /// For more information about this option, see [`set_recv_tclass_v6`].
+ ///
+ /// [`set_recv_tclass_v6`]: Socket::set_recv_tclass_v6
+ #[cfg(not(any(
+ target_os = "dragonfly",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "redox",
+ target_os = "solaris",
+ target_os = "haiku",
+ target_os = "espidf",
+ target_os = "vita",
+ )))]
+ pub fn recv_tclass_v6(&self) -> io::Result<bool> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), sys::IPPROTO_IPV6, sys::IPV6_RECVTCLASS)
+ .map(|recv_tclass| recv_tclass > 0)
+ }
+ }
+
+ /// Set the value of the `IPV6_RECVTCLASS` option for this socket.
+ ///
+ /// If enabled, the `IPV6_TCLASS` ancillary message is passed with incoming
+ /// packets. It contains a byte which specifies the traffic class field of
+ /// the packet header.
+ #[cfg(not(any(
+ target_os = "dragonfly",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "redox",
+ target_os = "solaris",
+ target_os = "haiku",
+ target_os = "espidf",
+ target_os = "vita",
+ )))]
+ pub fn set_recv_tclass_v6(&self, recv_tclass: bool) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ sys::IPPROTO_IPV6,
+ sys::IPV6_RECVTCLASS,
+ recv_tclass as c_int,
+ )
+ }
+ }
}
/// Socket options for TCP sockets, get/set using `IPPROTO_TCP`.
@@ -1645,18 +1824,25 @@ impl Socket {
///
/// This returns the value of `TCP_KEEPALIVE` on macOS and iOS and `TCP_KEEPIDLE` on all other
/// supported Unix operating systems.
- #[cfg(any(
- doc,
- all(
- feature = "all",
- not(any(windows, target_os = "haiku", target_os = "openbsd"))
- )
+ #[cfg(all(
+ feature = "all",
+ not(any(
+ windows,
+ target_os = "haiku",
+ target_os = "openbsd",
+ target_os = "vita"
+ ))
))]
#[cfg_attr(
docsrs,
doc(cfg(all(
feature = "all",
- not(any(windows, target_os = "haiku", target_os = "openbsd"))
+ not(any(
+ windows,
+ target_os = "haiku",
+ target_os = "openbsd",
+ target_os = "vita"
+ ))
)))
)]
pub fn keepalive_time(&self) -> io::Result<Duration> {
@@ -1671,15 +1857,17 @@ impl Socket {
#[cfg(all(
feature = "all",
any(
- doc,
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
+ target_os = "ios",
target_os = "linux",
+ target_os = "macos",
target_os = "netbsd",
- target_vendor = "apple",
+ target_os = "tvos",
+ target_os = "watchos",
)
))]
#[cfg_attr(
@@ -1692,9 +1880,12 @@ impl Socket {
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
+ target_os = "ios",
target_os = "linux",
+ target_os = "macos",
target_os = "netbsd",
- target_vendor = "apple",
+ target_os = "tvos",
+ target_os = "watchos",
)
)))
)]
@@ -1713,15 +1904,17 @@ impl Socket {
#[cfg(all(
feature = "all",
any(
- doc,
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
+ target_os = "ios",
target_os = "linux",
+ target_os = "macos",
target_os = "netbsd",
- target_vendor = "apple",
+ target_os = "tvos",
+ target_os = "watchos",
)
))]
#[cfg_attr(
@@ -1734,9 +1927,12 @@ impl Socket {
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
+ target_os = "ios",
target_os = "linux",
+ target_os = "macos",
target_os = "netbsd",
- target_vendor = "apple",
+ target_os = "tvos",
+ target_os = "watchos",
)
)))
)]
diff --git a/src/sockref.rs b/src/sockref.rs
index 257323b..d23b7c0 100644
--- a/src/sockref.rs
+++ b/src/sockref.rs
@@ -3,9 +3,9 @@ use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::ops::Deref;
#[cfg(unix)]
-use std::os::unix::io::{AsRawFd, FromRawFd};
+use std::os::unix::io::{AsFd, AsRawFd, FromRawFd};
#[cfg(windows)]
-use std::os::windows::io::{AsRawSocket, FromRawSocket};
+use std::os::windows::io::{AsRawSocket, AsSocket, FromRawSocket};
use crate::Socket;
@@ -15,14 +15,13 @@ use crate::Socket;
/// This allows for example a [`TcpStream`], found in the standard library, to
/// be configured using all the additional methods found in the [`Socket`] API.
///
-/// `SockRef` can be created from any socket type that implements [`AsRawFd`]
-/// (Unix) or [`AsRawSocket`] (Windows) using the [`From`] implementation, but
-/// the caller must ensure the file descriptor/socket is a valid.
+/// `SockRef` can be created from any socket type that implements [`AsFd`]
+/// (Unix) or [`AsSocket`] (Windows) using the [`From`] implementation.
///
/// [`TcpStream`]: std::net::TcpStream
// Don't use intra-doc links because they won't build on every platform.
-/// [`AsRawFd`]: https://doc.rust-lang.org/stable/std/os/unix/io/trait.AsRawFd.html
-/// [`AsRawSocket`]: https://doc.rust-lang.org/stable/std/os/windows/io/trait.AsRawSocket.html
+/// [`AsFd`]: https://doc.rust-lang.org/stable/std/os/unix/io/trait.AsFd.html
+/// [`AsSocket`]: https://doc.rust-lang.org/stable/std/os/windows/io/trait.AsSocket.html
///
/// # Examples
///
@@ -59,29 +58,6 @@ use crate::Socket;
/// # Ok(())
/// # }
/// ```
-///
-/// Below is an example of **incorrect usage** of `SockRef::from`, which is
-/// currently possible (but not intended and will be fixed in future versions).
-///
-/// ```compile_fail
-/// use socket2::SockRef;
-///
-/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
-/// /// THIS USAGE IS NOT VALID!
-/// let socket_ref = SockRef::from(&123);
-/// // The above line is overseen possibility when using `SockRef::from`, it
-/// // uses the `RawFd` (on Unix), which is a type alias for `c_int`/`i32`,
-/// // which implements `AsRawFd`. However it may be clear that this usage is
-/// // invalid as it doesn't guarantee that `123` is a valid file descriptor.
-///
-/// // Using `Socket::set_nodelay` now will call it on a file descriptor we
-/// // don't own! We don't even not if the file descriptor is valid or a socket.
-/// socket_ref.set_nodelay(true)?;
-/// drop(socket_ref);
-/// # Ok(())
-/// # }
-/// # DO_NOT_COMPILE
-/// ```
pub struct SockRef<'s> {
/// Because this is a reference we don't own the `Socket`, however `Socket`
/// closes itself when dropped, so we use `ManuallyDrop` to prevent it from
@@ -100,16 +76,16 @@ impl<'s> Deref for SockRef<'s> {
}
}
-/// On Windows, a corresponding `From<&impl AsRawSocket>` implementation exists.
+/// On Windows, a corresponding `From<&impl AsSocket>` implementation exists.
#[cfg(unix)]
#[cfg_attr(docsrs, doc(cfg(unix)))]
impl<'s, S> From<&'s S> for SockRef<'s>
where
- S: AsRawFd,
+ S: AsFd,
{
/// The caller must ensure `S` is actually a socket.
fn from(socket: &'s S) -> Self {
- let fd = socket.as_raw_fd();
+ let fd = socket.as_fd().as_raw_fd();
assert!(fd >= 0);
SockRef {
socket: ManuallyDrop::new(unsafe { Socket::from_raw_fd(fd) }),
@@ -118,17 +94,17 @@ where
}
}
-/// On Unix, a corresponding `From<&impl AsRawFd>` implementation exists.
+/// On Unix, a corresponding `From<&impl AsFd>` implementation exists.
#[cfg(windows)]
#[cfg_attr(docsrs, doc(cfg(windows)))]
impl<'s, S> From<&'s S> for SockRef<'s>
where
- S: AsRawSocket,
+ S: AsSocket,
{
- /// See the `From<&impl AsRawFd>` implementation.
+ /// See the `From<&impl AsFd>` implementation.
fn from(socket: &'s S) -> Self {
- let socket = socket.as_raw_socket();
- assert!(socket != winapi::um::winsock2::INVALID_SOCKET as _);
+ let socket = socket.as_socket().as_raw_socket();
+ assert!(socket != windows_sys::Win32::Networking::WinSock::INVALID_SOCKET as _);
SockRef {
socket: ManuallyDrop::new(unsafe { Socket::from_raw_socket(socket) }),
_lifetime: PhantomData,
diff --git a/src/sys/unix.rs b/src/sys/unix.rs
index 8400d14..c562600 100644
--- a/src/sys/unix.rs
+++ b/src/sys/unix.rs
@@ -7,77 +7,134 @@
// except according to those terms.
use std::cmp::min;
+use std::ffi::OsStr;
#[cfg(not(target_os = "redox"))]
use std::io::IoSlice;
use std::marker::PhantomData;
use std::mem::{self, size_of, MaybeUninit};
use std::net::Shutdown;
use std::net::{Ipv4Addr, Ipv6Addr};
-#[cfg(all(feature = "all", target_vendor = "apple"))]
+#[cfg(all(
+ feature = "all",
+ any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
+ )
+))]
use std::num::NonZeroU32;
#[cfg(all(
feature = "all",
any(
+ target_os = "aix",
target_os = "android",
target_os = "freebsd",
+ target_os = "ios",
target_os = "linux",
- target_vendor = "apple",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
)
))]
use std::num::NonZeroUsize;
-#[cfg(feature = "all")]
use std::os::unix::ffi::OsStrExt;
#[cfg(all(
feature = "all",
any(
+ target_os = "aix",
target_os = "android",
target_os = "freebsd",
+ target_os = "ios",
target_os = "linux",
- target_vendor = "apple",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
)
))]
use std::os::unix::io::RawFd;
-use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
+use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd};
#[cfg(feature = "all")]
use std::os::unix::net::{UnixDatagram, UnixListener, UnixStream};
-#[cfg(feature = "all")]
use std::path::Path;
-#[cfg(not(all(target_os = "redox", not(feature = "all"))))]
use std::ptr;
use std::time::{Duration, Instant};
use std::{io, slice};
-#[cfg(not(target_vendor = "apple"))]
+#[cfg(not(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
+)))]
use libc::ssize_t;
-use libc::{c_void, in6_addr, in_addr};
+use libc::{in6_addr, in_addr};
-#[cfg(not(target_os = "redox"))]
-use crate::RecvFlags;
use crate::{Domain, Protocol, SockAddr, TcpKeepalive, Type};
+#[cfg(not(target_os = "redox"))]
+use crate::{MsgHdr, MsgHdrMut, RecvFlags};
pub(crate) use libc::c_int;
// Used in `Domain`.
-pub(crate) use libc::{AF_INET, AF_INET6};
+pub(crate) use libc::{AF_INET, AF_INET6, AF_UNIX};
// Used in `Type`.
-#[cfg(all(feature = "all", not(target_os = "redox")))]
+#[cfg(all(feature = "all", target_os = "linux"))]
+pub(crate) use libc::SOCK_DCCP;
+#[cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))]
pub(crate) use libc::SOCK_RAW;
-#[cfg(feature = "all")]
+#[cfg(all(feature = "all", not(target_os = "espidf")))]
pub(crate) use libc::SOCK_SEQPACKET;
pub(crate) use libc::{SOCK_DGRAM, SOCK_STREAM};
// Used in `Protocol`.
+#[cfg(all(feature = "all", target_os = "linux"))]
+pub(crate) use libc::IPPROTO_DCCP;
+#[cfg(target_os = "linux")]
+pub(crate) use libc::IPPROTO_MPTCP;
+#[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
+pub(crate) use libc::IPPROTO_SCTP;
+#[cfg(all(
+ feature = "all",
+ any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "linux",
+ )
+))]
+pub(crate) use libc::IPPROTO_UDPLITE;
pub(crate) use libc::{IPPROTO_ICMP, IPPROTO_ICMPV6, IPPROTO_TCP, IPPROTO_UDP};
// Used in `SockAddr`.
+#[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "openbsd")))]
+pub(crate) use libc::IPPROTO_DIVERT;
pub(crate) use libc::{
sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, sockaddr_storage, socklen_t,
};
// Used in `RecvFlags`.
+#[cfg(not(any(target_os = "redox", target_os = "espidf")))]
+pub(crate) use libc::MSG_TRUNC;
#[cfg(not(target_os = "redox"))]
-pub(crate) use libc::{MSG_TRUNC, SO_OOBINLINE};
+pub(crate) use libc::SO_OOBINLINE;
// Used in `Socket`.
-#[cfg(all(feature = "all", not(target_os = "redox")))]
+#[cfg(not(target_os = "nto"))]
+pub(crate) use libc::ipv6_mreq as Ipv6Mreq;
+#[cfg(not(any(
+ target_os = "dragonfly",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "redox",
+ target_os = "solaris",
+ target_os = "haiku",
+ target_os = "espidf",
+ target_os = "vita",
+)))]
+pub(crate) use libc::IPV6_RECVTCLASS;
+#[cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))]
pub(crate) use libc::IP_HDRINCL;
#[cfg(not(any(
+ target_os = "aix",
target_os = "dragonfly",
target_os = "fuchsia",
target_os = "illumos",
@@ -85,6 +142,10 @@ pub(crate) use libc::IP_HDRINCL;
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
+ target_os = "haiku",
+ target_os = "nto",
+ target_os = "espidf",
+ target_os = "vita",
)))]
pub(crate) use libc::IP_RECVTOS;
#[cfg(not(any(
@@ -94,16 +155,26 @@ pub(crate) use libc::IP_RECVTOS;
target_os = "illumos",
)))]
pub(crate) use libc::IP_TOS;
-#[cfg(not(target_vendor = "apple"))]
+#[cfg(not(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
+)))]
pub(crate) use libc::SO_LINGER;
-#[cfg(target_vendor = "apple")]
+#[cfg(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
+))]
pub(crate) use libc::SO_LINGER_SEC as SO_LINGER;
pub(crate) use libc::{
- ip_mreq as IpMreq, ipv6_mreq as Ipv6Mreq, linger, IPPROTO_IP, IPPROTO_IPV6,
- IPV6_MULTICAST_HOPS, IPV6_MULTICAST_IF, IPV6_MULTICAST_LOOP, IPV6_UNICAST_HOPS, IPV6_V6ONLY,
- IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_MULTICAST_IF, IP_MULTICAST_LOOP, IP_MULTICAST_TTL,
- IP_TTL, MSG_OOB, MSG_PEEK, SOL_SOCKET, SO_BROADCAST, SO_ERROR, SO_KEEPALIVE, SO_RCVBUF,
- SO_RCVTIMEO, SO_REUSEADDR, SO_SNDBUF, SO_SNDTIMEO, SO_TYPE, TCP_NODELAY,
+ ip_mreq as IpMreq, linger, IPPROTO_IP, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, IPV6_MULTICAST_IF,
+ IPV6_MULTICAST_LOOP, IPV6_UNICAST_HOPS, IPV6_V6ONLY, IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP,
+ IP_MULTICAST_IF, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TTL, MSG_OOB, MSG_PEEK, SOL_SOCKET,
+ SO_BROADCAST, SO_ERROR, SO_KEEPALIVE, SO_RCVBUF, SO_RCVTIMEO, SO_REUSEADDR, SO_SNDBUF,
+ SO_SNDTIMEO, SO_TYPE, TCP_NODELAY,
};
#[cfg(not(any(
target_os = "dragonfly",
@@ -112,6 +183,9 @@ pub(crate) use libc::{
target_os = "openbsd",
target_os = "redox",
target_os = "fuchsia",
+ target_os = "nto",
+ target_os = "espidf",
+ target_os = "vita",
)))]
pub(crate) use libc::{
ip_mreq_source as IpMreqSource, IP_ADD_SOURCE_MEMBERSHIP, IP_DROP_SOURCE_MEMBERSHIP,
@@ -121,10 +195,14 @@ pub(crate) use libc::{
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
target_os = "netbsd",
+ target_os = "nto",
target_os = "openbsd",
target_os = "solaris",
- target_vendor = "apple"
+ target_os = "tvos",
+ target_os = "watchos",
)))]
pub(crate) use libc::{IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP};
#[cfg(any(
@@ -132,10 +210,13 @@ pub(crate) use libc::{IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP};
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
- target_vendor = "apple",
+ target_os = "tvos",
+ target_os = "watchos",
))]
pub(crate) use libc::{
IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP, IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP,
@@ -148,9 +229,12 @@ pub(crate) use libc::{
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
+ target_os = "ios",
target_os = "linux",
+ target_os = "macos",
target_os = "netbsd",
- target_vendor = "apple",
+ target_os = "tvos",
+ target_os = "watchos",
)
))]
pub(crate) use libc::{TCP_KEEPCNT, TCP_KEEPINTVL};
@@ -158,9 +242,24 @@ pub(crate) use libc::{TCP_KEEPCNT, TCP_KEEPINTVL};
// See this type in the Windows file.
pub(crate) type Bool = c_int;
-#[cfg(target_vendor = "apple")]
+#[cfg(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "nto",
+ target_os = "tvos",
+ target_os = "watchos",
+))]
use libc::TCP_KEEPALIVE as KEEPALIVE_TIME;
-#[cfg(not(any(target_vendor = "apple", target_os = "haiku", target_os = "openbsd")))]
+#[cfg(not(any(
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "nto",
+ target_os = "openbsd",
+ target_os = "tvos",
+ target_os = "watchos",
+ target_os = "vita",
+)))]
use libc::TCP_KEEPIDLE as KEEPALIVE_TIME;
/// Helper macro to execute a system call that returns an `io::Result`.
@@ -177,8 +276,13 @@ macro_rules! syscall {
}
/// Maximum size of a buffer passed to system call like `recv` and `send`.
-#[cfg(not(target_vendor = "apple"))]
-const MAX_BUF_LEN: usize = <ssize_t>::max_value() as usize;
+#[cfg(not(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
+)))]
+const MAX_BUF_LEN: usize = ssize_t::MAX as usize;
// The maximum read limit on most posix-like systems is `SSIZE_MAX`, with the
// man page quoting that if the count of bytes to read is greater than
@@ -188,8 +292,17 @@ const MAX_BUF_LEN: usize = <ssize_t>::max_value() as usize;
// intentionally showing odd behavior by rejecting any read with a size larger
// than or equal to INT_MAX. To handle both of these the read size is capped on
// both platforms.
-#[cfg(target_vendor = "apple")]
-const MAX_BUF_LEN: usize = <c_int>::max_value() as usize - 1;
+#[cfg(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
+))]
+const MAX_BUF_LEN: usize = c_int::MAX as usize - 1;
+
+// TCP_CA_NAME_MAX isn't defined in user space include files(not in libc)
+#[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
+const TCP_CA_NAME_MAX: usize = 16;
#[cfg(any(
all(
@@ -211,24 +324,27 @@ type IovLen = usize;
all(target_env = "uclibc", target_pointer_width = "32")
)
),
+ target_os = "aix",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "haiku",
target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
target_os = "netbsd",
+ target_os = "nto",
target_os = "openbsd",
target_os = "solaris",
- target_vendor = "apple",
+ target_os = "tvos",
+ target_os = "watchos",
+ target_os = "espidf",
+ target_os = "vita",
))]
type IovLen = c_int;
/// Unix only API.
impl Domain {
- /// Domain for Unix socket communication, corresponding to `AF_UNIX`.
- #[cfg_attr(docsrs, doc(cfg(unix)))]
- pub const UNIX: Domain = Domain(libc::AF_UNIX);
-
/// Domain for low-level packet interface, corresponding to `AF_PACKET`.
#[cfg(all(
feature = "all",
@@ -316,7 +432,9 @@ impl Type {
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
- target_os = "openbsd"
+ target_os = "openbsd",
+ target_os = "redox",
+ target_os = "solaris",
)
))]
#[cfg_attr(
@@ -331,7 +449,9 @@ impl Type {
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
- target_os = "openbsd"
+ target_os = "openbsd",
+ target_os = "redox",
+ target_os = "solaris",
)
)))
)]
@@ -347,7 +467,9 @@ impl Type {
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
- target_os = "openbsd"
+ target_os = "openbsd",
+ target_os = "redox",
+ target_os = "solaris",
))]
pub(crate) const fn _cloexec(self) -> Type {
Type(self.0 | libc::SOCK_CLOEXEC)
@@ -358,10 +480,13 @@ impl_debug!(
Type,
libc::SOCK_STREAM,
libc::SOCK_DGRAM,
- #[cfg(not(target_os = "redox"))]
+ #[cfg(all(feature = "all", target_os = "linux"))]
+ libc::SOCK_DCCP,
+ #[cfg(not(any(target_os = "redox", target_os = "espidf")))]
libc::SOCK_RAW,
- #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "espidf")))]
libc::SOCK_RDM,
+ #[cfg(not(target_os = "espidf"))]
libc::SOCK_SEQPACKET,
/* TODO: add these optional bit OR-ed flags:
#[cfg(any(
@@ -393,6 +518,24 @@ impl_debug!(
libc::IPPROTO_ICMPV6,
libc::IPPROTO_TCP,
libc::IPPROTO_UDP,
+ #[cfg(target_os = "linux")]
+ libc::IPPROTO_MPTCP,
+ #[cfg(all(feature = "all", target_os = "linux"))]
+ libc::IPPROTO_DCCP,
+ #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
+ libc::IPPROTO_SCTP,
+ #[cfg(all(
+ feature = "all",
+ any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "linux",
+ )
+ ))]
+ libc::IPPROTO_UDPLITE,
+ #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "openbsd")))]
+ libc::IPPROTO_DIVERT,
);
/// Unix-only API.
@@ -400,11 +543,14 @@ impl_debug!(
impl RecvFlags {
/// Check if the message terminates a record.
///
- /// Not all socket types support the notion of records.
- /// For socket types that do support it (such as [`SEQPACKET`][Type::SEQPACKET]),
- /// a record is terminated by sending a message with the end-of-record flag set.
+ /// Not all socket types support the notion of records. For socket types
+ /// that do support it (such as [`SEQPACKET`]), a record is terminated by
+ /// sending a message with the end-of-record flag set.
+ ///
+ /// On Unix this corresponds to the `MSG_EOR` flag.
///
- /// On Unix this corresponds to the MSG_EOR flag.
+ /// [`SEQPACKET`]: Type::SEQPACKET
+ #[cfg(not(target_os = "espidf"))]
pub const fn is_end_of_record(self) -> bool {
self.0 & libc::MSG_EOR != 0
}
@@ -414,7 +560,7 @@ impl RecvFlags {
/// This is useful for protocols where you receive out-of-band data
/// mixed in with the normal data stream.
///
- /// On Unix this corresponds to the MSG_OOB flag.
+ /// On Unix this corresponds to the `MSG_OOB` flag.
pub const fn is_out_of_band(self) -> bool {
self.0 & libc::MSG_OOB != 0
}
@@ -423,11 +569,13 @@ impl RecvFlags {
#[cfg(not(target_os = "redox"))]
impl std::fmt::Debug for RecvFlags {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.debug_struct("RecvFlags")
- .field("is_end_of_record", &self.is_end_of_record())
- .field("is_out_of_band", &self.is_out_of_band())
- .field("is_truncated", &self.is_truncated())
- .finish()
+ let mut s = f.debug_struct("RecvFlags");
+ #[cfg(not(target_os = "espidf"))]
+ s.field("is_end_of_record", &self.is_end_of_record());
+ s.field("is_out_of_band", &self.is_out_of_band());
+ #[cfg(not(target_os = "espidf"))]
+ s.field("is_truncated", &self.is_truncated());
+ s.finish()
}
}
@@ -461,72 +609,92 @@ impl<'a> MaybeUninitSlice<'a> {
}
}
-/// Unix only API.
-impl SockAddr {
- /// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path.
- ///
- /// # Failure
- ///
- /// Returns an error if the path is longer than `SUN_LEN`.
- #[cfg(feature = "all")]
- #[cfg_attr(docsrs, doc(cfg(all(unix, feature = "all"))))]
- #[allow(unused_unsafe)] // TODO: replace with `unsafe_op_in_unsafe_fn` once stable.
- pub fn unix<P>(path: P) -> io::Result<SockAddr>
- where
- P: AsRef<Path>,
- {
- unsafe {
- SockAddr::init(|storage, len| {
- // Safety: `SockAddr::init` zeros the address, which is a valid
- // representation.
- let storage: &mut libc::sockaddr_un = unsafe { &mut *storage.cast() };
- let len: &mut socklen_t = unsafe { &mut *len };
-
- let bytes = path.as_ref().as_os_str().as_bytes();
- let too_long = match bytes.first() {
- None => false,
- // linux abstract namespaces aren't null-terminated
- Some(&0) => bytes.len() > storage.sun_path.len(),
- Some(_) => bytes.len() >= storage.sun_path.len(),
- };
- if too_long {
- return Err(io::Error::new(
- io::ErrorKind::InvalidInput,
- "path must be shorter than SUN_LEN",
- ));
- }
+/// Returns the offset of the `sun_path` member of the passed unix socket address.
+pub(crate) fn offset_of_path(storage: &libc::sockaddr_un) -> usize {
+ let base = storage as *const _ as usize;
+ let path = ptr::addr_of!(storage.sun_path) as usize;
+ path - base
+}
- storage.sun_family = libc::AF_UNIX as sa_family_t;
- // Safety: `bytes` and `addr.sun_path` are not overlapping and
- // both point to valid memory.
- // `SockAddr::init` zeroes the memory, so the path is already
- // null terminated.
- unsafe {
- ptr::copy_nonoverlapping(
- bytes.as_ptr(),
- storage.sun_path.as_mut_ptr() as *mut u8,
- bytes.len(),
- )
- };
-
- let base = storage as *const _ as usize;
- let path = &storage.sun_path as *const _ as usize;
- let sun_path_offset = path - base;
- let length = sun_path_offset
- + bytes.len()
- + match bytes.first() {
- Some(&0) | None => 0,
- Some(_) => 1,
- };
- *len = length as socklen_t;
-
- Ok(())
- })
+#[allow(unsafe_op_in_unsafe_fn)]
+pub(crate) fn unix_sockaddr(path: &Path) -> io::Result<SockAddr> {
+ // SAFETY: a `sockaddr_storage` of all zeros is valid.
+ let mut storage = unsafe { mem::zeroed::<sockaddr_storage>() };
+ let len = {
+ let storage = unsafe { &mut *ptr::addr_of_mut!(storage).cast::<libc::sockaddr_un>() };
+
+ let bytes = path.as_os_str().as_bytes();
+ let too_long = match bytes.first() {
+ None => false,
+ // linux abstract namespaces aren't null-terminated
+ Some(&0) => bytes.len() > storage.sun_path.len(),
+ Some(_) => bytes.len() >= storage.sun_path.len(),
+ };
+ if too_long {
+ return Err(io::Error::new(
+ io::ErrorKind::InvalidInput,
+ "path must be shorter than SUN_LEN",
+ ));
}
- .map(|(_, addr)| addr)
- }
+
+ storage.sun_family = libc::AF_UNIX as sa_family_t;
+ // SAFETY: `bytes` and `addr.sun_path` are not overlapping and
+ // both point to valid memory.
+ // `storage` was initialized to zero above, so the path is
+ // already NULL terminated.
+ unsafe {
+ ptr::copy_nonoverlapping(
+ bytes.as_ptr(),
+ storage.sun_path.as_mut_ptr().cast(),
+ bytes.len(),
+ );
+ }
+
+ let sun_path_offset = offset_of_path(storage);
+ sun_path_offset
+ + bytes.len()
+ + match bytes.first() {
+ Some(&0) | None => 0,
+ Some(_) => 1,
+ }
+ };
+ Ok(unsafe { SockAddr::new(storage, len as socklen_t) })
+}
+
+// Used in `MsgHdr`.
+#[cfg(not(target_os = "redox"))]
+pub(crate) use libc::msghdr;
+
+#[cfg(not(target_os = "redox"))]
+pub(crate) fn set_msghdr_name(msg: &mut msghdr, name: &SockAddr) {
+ msg.msg_name = name.as_ptr() as *mut _;
+ msg.msg_namelen = name.len();
}
+#[cfg(not(target_os = "redox"))]
+#[allow(clippy::unnecessary_cast)] // IovLen type can be `usize`.
+pub(crate) fn set_msghdr_iov(msg: &mut msghdr, ptr: *mut libc::iovec, len: usize) {
+ msg.msg_iov = ptr;
+ msg.msg_iovlen = min(len, IovLen::MAX as usize) as IovLen;
+}
+
+#[cfg(not(target_os = "redox"))]
+pub(crate) fn set_msghdr_control(msg: &mut msghdr, ptr: *mut libc::c_void, len: usize) {
+ msg.msg_control = ptr;
+ msg.msg_controllen = len as _;
+}
+
+#[cfg(not(target_os = "redox"))]
+pub(crate) fn set_msghdr_flags(msg: &mut msghdr, flags: libc::c_int) {
+ msg.msg_flags = flags;
+}
+
+#[cfg(not(target_os = "redox"))]
+pub(crate) fn msghdr_flags(msg: &msghdr) -> RecvFlags {
+ RecvFlags(msg.msg_flags)
+}
+
+/// Unix only API.
impl SockAddr {
/// Constructs a `SockAddr` with the family `AF_VSOCK` and the provided CID/port.
///
@@ -534,30 +702,23 @@ impl SockAddr {
///
/// This function can never fail. In a future version of this library it will be made
/// infallible.
- #[allow(unused_unsafe)] // TODO: replace with `unsafe_op_in_unsafe_fn` once stable.
+ #[allow(unsafe_op_in_unsafe_fn)]
#[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
#[cfg_attr(
docsrs,
doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux"))))
)]
- pub fn vsock(cid: u32, port: u32) -> io::Result<SockAddr> {
- unsafe {
- SockAddr::init(|storage, len| {
- // Safety: `SockAddr::init` zeros the address, which is a valid
- // representation.
- let storage: &mut libc::sockaddr_vm = unsafe { &mut *storage.cast() };
- let len: &mut socklen_t = unsafe { &mut *len };
-
- storage.svm_family = libc::AF_VSOCK as sa_family_t;
- storage.svm_cid = cid;
- storage.svm_port = port;
-
- *len = mem::size_of::<libc::sockaddr_vm>() as socklen_t;
-
- Ok(())
- })
+ pub fn vsock(cid: u32, port: u32) -> SockAddr {
+ // SAFETY: a `sockaddr_storage` of all zeros is valid.
+ let mut storage = unsafe { mem::zeroed::<sockaddr_storage>() };
+ {
+ let storage: &mut libc::sockaddr_vm =
+ unsafe { &mut *((&mut storage as *mut sockaddr_storage).cast()) };
+ storage.svm_family = libc::AF_VSOCK as sa_family_t;
+ storage.svm_cid = cid;
+ storage.svm_port = port;
}
- .map(|(_, addr)| addr)
+ unsafe { SockAddr::new(storage, mem::size_of::<libc::sockaddr_vm>() as socklen_t) }
}
/// Returns this address VSOCK CID/port if it is in the `AF_VSOCK` family,
@@ -567,7 +728,7 @@ impl SockAddr {
docsrs,
doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux"))))
)]
- pub fn vsock_address(&self) -> Option<(u32, u32)> {
+ pub fn as_vsock_address(&self) -> Option<(u32, u32)> {
if self.family() == libc::AF_VSOCK as sa_family_t {
// Safety: if the ss_family field is AF_VSOCK then storage must be a sockaddr_vm.
let addr = unsafe { &*(self.as_ptr() as *const libc::sockaddr_vm) };
@@ -576,6 +737,100 @@ impl SockAddr {
None
}
}
+
+ /// Returns true if this address is an unnamed address from the `AF_UNIX` family (for local
+ /// interprocess communication), false otherwise.
+ pub fn is_unnamed(&self) -> bool {
+ self.as_sockaddr_un()
+ .map(|storage| {
+ self.len() == offset_of_path(storage) as _
+ // On some non-linux platforms a zeroed path is returned for unnamed.
+ // Abstract addresses only exist on Linux.
+ // NOTE: although Fuchsia does define `AF_UNIX` it's not actually implemented.
+ // See https://github.com/rust-lang/socket2/pull/403#discussion_r1123557978
+ || (cfg!(not(any(target_os = "linux", target_os = "android")))
+ && storage.sun_path[0] == 0)
+ })
+ .unwrap_or_default()
+ }
+
+ /// Returns the underlying `sockaddr_un` object if this addres is from the `AF_UNIX` family,
+ /// otherwise returns `None`.
+ pub(crate) fn as_sockaddr_un(&self) -> Option<&libc::sockaddr_un> {
+ self.is_unix().then(|| {
+ // SAFETY: if unix socket, i.e. the `ss_family` field is `AF_UNIX` then storage must be
+ // a `sockaddr_un`.
+ unsafe { &*self.as_ptr().cast::<libc::sockaddr_un>() }
+ })
+ }
+
+ /// Get the length of the path bytes of the address, not including the terminating or initial
+ /// (for abstract names) null byte.
+ ///
+ /// Should not be called on unnamed addresses.
+ fn path_len(&self, storage: &libc::sockaddr_un) -> usize {
+ debug_assert!(!self.is_unnamed());
+ self.len() as usize - offset_of_path(storage) - 1
+ }
+
+ /// Get a u8 slice for the bytes of the pathname or abstract name.
+ ///
+ /// Should not be called on unnamed addresses.
+ fn path_bytes(&self, storage: &libc::sockaddr_un, abstract_name: bool) -> &[u8] {
+ debug_assert!(!self.is_unnamed());
+ // SAFETY: the pointed objects of type `i8` have the same memory layout as `u8`. The path is
+ // the last field in the storage and so its length is equal to
+ // TOTAL_LENGTH - OFFSET_OF_PATH -1
+ // Where the 1 is either a terminating null if we have a pathname address, or the initial
+ // null byte, if it's an abstract name address. In the latter case, the path bytes start
+ // after the initial null byte, hence the `offset`.
+ // There is no safe way to convert a `&[i8]` to `&[u8]`
+ unsafe {
+ slice::from_raw_parts(
+ (storage.sun_path.as_ptr() as *const u8).offset(abstract_name as isize),
+ self.path_len(storage),
+ )
+ }
+ }
+
+ /// Returns this address as Unix `SocketAddr` if it is an `AF_UNIX` pathname
+ /// address, otherwise returns `None`.
+ pub fn as_unix(&self) -> Option<std::os::unix::net::SocketAddr> {
+ let path = self.as_pathname()?;
+ // SAFETY: we can represent this as a valid pathname, then so can the
+ // standard library.
+ Some(std::os::unix::net::SocketAddr::from_pathname(path).unwrap())
+ }
+
+ /// Returns this address as a `Path` reference if it is an `AF_UNIX`
+ /// pathname address, otherwise returns `None`.
+ pub fn as_pathname(&self) -> Option<&Path> {
+ self.as_sockaddr_un().and_then(|storage| {
+ (self.len() > offset_of_path(storage) as _ && storage.sun_path[0] != 0).then(|| {
+ let path_slice = self.path_bytes(storage, false);
+ Path::new::<OsStr>(OsStrExt::from_bytes(path_slice))
+ })
+ })
+ }
+
+ /// Returns this address as a slice of bytes representing an abstract address if it is an
+ /// `AF_UNIX` abstract address, otherwise returns `None`.
+ ///
+ /// Abstract addresses are a Linux extension, so this method returns `None` on all non-Linux
+ /// platforms.
+ pub fn as_abstract_namespace(&self) -> Option<&[u8]> {
+ // NOTE: although Fuchsia does define `AF_UNIX` it's not actually implemented.
+ // See https://github.com/rust-lang/socket2/pull/403#discussion_r1123557978
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ {
+ self.as_sockaddr_un().and_then(|storage| {
+ (self.len() > offset_of_path(storage) as _ && storage.sun_path[0] == 0)
+ .then(|| self.path_bytes(storage, true))
+ })
+ }
+ #[cfg(not(any(target_os = "linux", target_os = "android")))]
+ None
+ }
}
pub(crate) type Socket = c_int;
@@ -596,7 +851,8 @@ pub(crate) fn socket(family: c_int, ty: c_int, protocol: c_int) -> io::Result<So
syscall!(socket(family, ty, protocol))
}
-#[cfg(feature = "all")]
+#[cfg(all(feature = "all", unix))]
+#[cfg_attr(docsrs, doc(cfg(all(feature = "all", unix))))]
pub(crate) fn socketpair(family: c_int, ty: c_int, protocol: c_int) -> io::Result<[Socket; 2]> {
let mut fds = [0, 0];
syscall!(socketpair(family, ty, protocol, fds.as_mut_ptr())).map(|_| fds)
@@ -626,7 +882,7 @@ pub(crate) fn poll_connect(socket: &crate::Socket, timeout: Duration) -> io::Res
}
let timeout = (timeout - elapsed).as_millis();
- let timeout = clamp(timeout, 1, c_int::max_value() as u128) as c_int;
+ let timeout = timeout.clamp(1, c_int::MAX as u128) as c_int;
match syscall!(poll(&mut pollfd, 1, timeout)) {
Ok(0) => return Err(io::ErrorKind::TimedOut.into()),
@@ -634,14 +890,13 @@ pub(crate) fn poll_connect(socket: &crate::Socket, timeout: Duration) -> io::Res
// Error or hang up indicates an error (or failure to connect).
if (pollfd.revents & libc::POLLHUP) != 0 || (pollfd.revents & libc::POLLERR) != 0 {
match socket.take_error() {
- Ok(Some(err)) => return Err(err),
+ Ok(Some(err)) | Err(err) => return Err(err),
Ok(None) => {
return Err(io::Error::new(
io::ErrorKind::Other,
"no error set after POLLHUP",
))
}
- Err(err) => return Err(err),
}
}
return Ok(());
@@ -653,38 +908,24 @@ pub(crate) fn poll_connect(socket: &crate::Socket, timeout: Duration) -> io::Res
}
}
-// TODO: use clamp from std lib, stable since 1.50.
-fn clamp<T>(value: T, min: T, max: T) -> T
-where
- T: Ord,
-{
- if value <= min {
- min
- } else if value >= max {
- max
- } else {
- value
- }
-}
-
pub(crate) fn listen(fd: Socket, backlog: c_int) -> io::Result<()> {
syscall!(listen(fd, backlog)).map(|_| ())
}
pub(crate) fn accept(fd: Socket) -> io::Result<(Socket, SockAddr)> {
// Safety: `accept` initialises the `SockAddr` for us.
- unsafe { SockAddr::init(|storage, len| syscall!(accept(fd, storage.cast(), len))) }
+ unsafe { SockAddr::try_init(|storage, len| syscall!(accept(fd, storage.cast(), len))) }
}
pub(crate) fn getsockname(fd: Socket) -> io::Result<SockAddr> {
// Safety: `accept` initialises the `SockAddr` for us.
- unsafe { SockAddr::init(|storage, len| syscall!(getsockname(fd, storage.cast(), len))) }
+ unsafe { SockAddr::try_init(|storage, len| syscall!(getsockname(fd, storage.cast(), len))) }
.map(|(_, addr)| addr)
}
pub(crate) fn getpeername(fd: Socket) -> io::Result<SockAddr> {
// Safety: `accept` initialises the `SockAddr` for us.
- unsafe { SockAddr::init(|storage, len| syscall!(getpeername(fd, storage.cast(), len))) }
+ unsafe { SockAddr::try_init(|storage, len| syscall!(getpeername(fd, storage.cast(), len))) }
.map(|(_, addr)| addr)
}
@@ -692,6 +933,20 @@ pub(crate) fn try_clone(fd: Socket) -> io::Result<Socket> {
syscall!(fcntl(fd, libc::F_DUPFD_CLOEXEC, 0))
}
+#[cfg(all(feature = "all", unix, not(target_os = "vita")))]
+pub(crate) fn nonblocking(fd: Socket) -> io::Result<bool> {
+ let file_status_flags = fcntl_get(fd, libc::F_GETFL)?;
+ Ok((file_status_flags & libc::O_NONBLOCK) != 0)
+}
+
+#[cfg(all(feature = "all", target_os = "vita"))]
+pub(crate) fn nonblocking(fd: Socket) -> io::Result<bool> {
+ unsafe {
+ getsockopt::<Bool>(fd, libc::SOL_SOCKET, libc::SO_NONBLOCK).map(|non_block| non_block != 0)
+ }
+}
+
+#[cfg(not(target_os = "vita"))]
pub(crate) fn set_nonblocking(fd: Socket, nonblocking: bool) -> io::Result<()> {
if nonblocking {
fcntl_add(fd, libc::F_GETFL, libc::F_SETFL, libc::O_NONBLOCK)
@@ -700,6 +955,18 @@ pub(crate) fn set_nonblocking(fd: Socket, nonblocking: bool) -> io::Result<()> {
}
}
+#[cfg(target_os = "vita")]
+pub(crate) fn set_nonblocking(fd: Socket, nonblocking: bool) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ fd,
+ libc::SOL_SOCKET,
+ libc::SO_NONBLOCK,
+ nonblocking as libc::c_int,
+ )
+ }
+}
+
pub(crate) fn shutdown(fd: Socket, how: Shutdown) -> io::Result<()> {
let how = match how {
Shutdown::Write => libc::SHUT_WR,
@@ -726,7 +993,7 @@ pub(crate) fn recv_from(
) -> io::Result<(usize, SockAddr)> {
// Safety: `recvfrom` initialises the `SockAddr` for us.
unsafe {
- SockAddr::init(|addr, addrlen| {
+ SockAddr::try_init(|addr, addrlen| {
syscall!(recvfrom(
fd,
buf.as_mut_ptr().cast(),
@@ -740,13 +1007,24 @@ pub(crate) fn recv_from(
}
}
+pub(crate) fn peek_sender(fd: Socket) -> io::Result<SockAddr> {
+ // Unix-like platforms simply truncate the returned data, so this implementation is trivial.
+ // However, for Windows this requires suppressing the `WSAEMSGSIZE` error,
+ // so that requires a different approach.
+ // NOTE: macOS does not populate `sockaddr` if you pass a zero-sized buffer.
+ let (_, sender) = recv_from(fd, &mut [MaybeUninit::uninit(); 8], MSG_PEEK)?;
+ Ok(sender)
+}
+
#[cfg(not(target_os = "redox"))]
pub(crate) fn recv_vectored(
fd: Socket,
bufs: &mut [crate::MaybeUninitSlice<'_>],
flags: c_int,
) -> io::Result<(usize, RecvFlags)> {
- recvmsg(fd, ptr::null_mut(), bufs, flags).map(|(n, _, recv_flags)| (n, recv_flags))
+ let mut msg = MsgHdrMut::new().with_buffers(bufs);
+ let n = recvmsg(fd, &mut msg, flags)?;
+ Ok((n, msg.flags()))
}
#[cfg(not(target_os = "redox"))]
@@ -755,41 +1033,29 @@ pub(crate) fn recv_from_vectored(
bufs: &mut [crate::MaybeUninitSlice<'_>],
flags: c_int,
) -> io::Result<(usize, RecvFlags, SockAddr)> {
- // Safety: `recvmsg` initialises the address storage and we set the length
+ let mut msg = MsgHdrMut::new().with_buffers(bufs);
+ // SAFETY: `recvmsg` initialises the address storage and we set the length
// manually.
- unsafe {
- SockAddr::init(|storage, len| {
- recvmsg(fd, storage, bufs, flags).map(|(n, addrlen, recv_flags)| {
- // Set the correct address length.
- *len = addrlen;
- (n, recv_flags)
- })
- })
- }
- .map(|((n, recv_flags), addr)| (n, recv_flags, addr))
+ let (n, addr) = unsafe {
+ SockAddr::try_init(|storage, len| {
+ msg.inner.msg_name = storage.cast();
+ msg.inner.msg_namelen = *len;
+ let n = recvmsg(fd, &mut msg, flags)?;
+ // Set the correct address length.
+ *len = msg.inner.msg_namelen;
+ Ok(n)
+ })?
+ };
+ Ok((n, msg.flags(), addr))
}
-/// Returns the (bytes received, sending address len, `RecvFlags`).
#[cfg(not(target_os = "redox"))]
-fn recvmsg(
+pub(crate) fn recvmsg(
fd: Socket,
- msg_name: *mut sockaddr_storage,
- bufs: &mut [crate::MaybeUninitSlice<'_>],
+ msg: &mut MsgHdrMut<'_, '_, '_>,
flags: c_int,
-) -> io::Result<(usize, libc::socklen_t, RecvFlags)> {
- let msg_namelen = if msg_name.is_null() {
- 0
- } else {
- size_of::<sockaddr_storage>() as libc::socklen_t
- };
- // libc::msghdr contains unexported padding fields on Fuchsia.
- let mut msg: libc::msghdr = unsafe { mem::zeroed() };
- msg.msg_name = msg_name.cast();
- msg.msg_namelen = msg_namelen;
- msg.msg_iov = bufs.as_mut_ptr().cast();
- msg.msg_iovlen = min(bufs.len(), IovLen::MAX as usize) as IovLen;
- syscall!(recvmsg(fd, &mut msg, flags))
- .map(|n| (n as usize, msg.msg_namelen, RecvFlags(msg.msg_flags)))
+) -> io::Result<usize> {
+ syscall!(recvmsg(fd, &mut msg.inner, flags)).map(|n| n as usize)
}
pub(crate) fn send(fd: Socket, buf: &[u8], flags: c_int) -> io::Result<usize> {
@@ -804,7 +1070,8 @@ pub(crate) fn send(fd: Socket, buf: &[u8], flags: c_int) -> io::Result<usize> {
#[cfg(not(target_os = "redox"))]
pub(crate) fn send_vectored(fd: Socket, bufs: &[IoSlice<'_>], flags: c_int) -> io::Result<usize> {
- sendmsg(fd, ptr::null(), 0, bufs, flags)
+ let msg = MsgHdr::new().with_buffers(bufs);
+ sendmsg(fd, &msg, flags)
}
pub(crate) fn send_to(fd: Socket, buf: &[u8], addr: &SockAddr, flags: c_int) -> io::Result<usize> {
@@ -826,29 +1093,13 @@ pub(crate) fn send_to_vectored(
addr: &SockAddr,
flags: c_int,
) -> io::Result<usize> {
- sendmsg(fd, addr.as_storage_ptr(), addr.len(), bufs, flags)
+ let msg = MsgHdr::new().with_addr(addr).with_buffers(bufs);
+ sendmsg(fd, &msg, flags)
}
-/// Returns the (bytes received, sending address len, `RecvFlags`).
#[cfg(not(target_os = "redox"))]
-fn sendmsg(
- fd: Socket,
- msg_name: *const sockaddr_storage,
- msg_namelen: socklen_t,
- bufs: &[IoSlice<'_>],
- flags: c_int,
-) -> io::Result<usize> {
- // libc::msghdr contains unexported padding fields on Fuchsia.
- let mut msg: libc::msghdr = unsafe { mem::zeroed() };
- // Safety: we're creating a `*mut` pointer from a reference, which is UB
- // once actually used. However the OS should not write to it in the
- // `sendmsg` system call.
- msg.msg_name = (msg_name as *mut sockaddr_storage).cast();
- msg.msg_namelen = msg_namelen;
- // Safety: Same as above about `*const` -> `*mut`.
- msg.msg_iov = bufs.as_ptr() as *mut _;
- msg.msg_iovlen = min(bufs.len(), IovLen::MAX as usize) as IovLen;
- syscall!(sendmsg(fd, &msg, flags)).map(|n| n as usize)
+pub(crate) fn sendmsg(fd: Socket, msg: &MsgHdr<'_, '_, '_>, flags: c_int) -> io::Result<usize> {
+ syscall!(sendmsg(fd, &msg.inner, flags)).map(|n| n as usize)
}
/// Wrapper around `getsockopt` to deal with platform specific timeouts.
@@ -856,7 +1107,7 @@ pub(crate) fn timeout_opt(fd: Socket, opt: c_int, val: c_int) -> io::Result<Opti
unsafe { getsockopt(fd, opt, val).map(from_timeval) }
}
-fn from_timeval(duration: libc::timeval) -> Option<Duration> {
+const fn from_timeval(duration: libc::timeval) -> Option<Duration> {
if duration.tv_sec == 0 && duration.tv_usec == 0 {
None
} else {
@@ -882,7 +1133,7 @@ fn into_timeval(duration: Option<Duration>) -> libc::timeval {
// https://github.com/rust-lang/libc/issues/1848
#[cfg_attr(target_env = "musl", allow(deprecated))]
Some(duration) => libc::timeval {
- tv_sec: min(duration.as_secs(), libc::time_t::max_value() as u64) as libc::time_t,
+ tv_sec: min(duration.as_secs(), libc::time_t::MAX as u64) as libc::time_t,
tv_usec: duration.subsec_micros() as libc::suseconds_t,
},
None => libc::timeval {
@@ -892,8 +1143,17 @@ fn into_timeval(duration: Option<Duration>) -> libc::timeval {
}
}
-#[cfg(feature = "all")]
-#[cfg(not(any(target_os = "haiku", target_os = "openbsd")))]
+#[cfg(all(
+ feature = "all",
+ not(any(target_os = "haiku", target_os = "openbsd", target_os = "vita"))
+))]
+#[cfg_attr(
+ docsrs,
+ doc(cfg(all(
+ feature = "all",
+ not(any(target_os = "haiku", target_os = "openbsd", target_os = "vita"))
+ )))
+)]
pub(crate) fn keepalive_time(fd: Socket) -> io::Result<Duration> {
unsafe {
getsockopt::<c_int>(fd, IPPROTO_TCP, KEEPALIVE_TIME)
@@ -903,21 +1163,30 @@ pub(crate) fn keepalive_time(fd: Socket) -> io::Result<Duration> {
#[allow(unused_variables)]
pub(crate) fn set_tcp_keepalive(fd: Socket, keepalive: &TcpKeepalive) -> io::Result<()> {
- #[cfg(not(any(target_os = "haiku", target_os = "openbsd")))]
+ #[cfg(not(any(
+ target_os = "haiku",
+ target_os = "openbsd",
+ target_os = "nto",
+ target_os = "vita"
+ )))]
if let Some(time) = keepalive.time {
let secs = into_secs(time);
unsafe { setsockopt(fd, libc::IPPROTO_TCP, KEEPALIVE_TIME, secs)? }
}
#[cfg(any(
+ target_os = "aix",
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
+ target_os = "ios",
target_os = "linux",
+ target_os = "macos",
target_os = "netbsd",
- target_vendor = "apple",
+ target_os = "tvos",
+ target_os = "watchos",
))]
{
if let Some(interval) = keepalive.interval {
@@ -930,17 +1199,35 @@ pub(crate) fn set_tcp_keepalive(fd: Socket, keepalive: &TcpKeepalive) -> io::Res
}
}
+ #[cfg(target_os = "nto")]
+ if let Some(time) = keepalive.time {
+ let secs = into_timeval(Some(time));
+ unsafe { setsockopt(fd, libc::IPPROTO_TCP, KEEPALIVE_TIME, secs)? }
+ }
+
Ok(())
}
-#[cfg(not(any(target_os = "haiku", target_os = "openbsd")))]
+#[cfg(not(any(
+ target_os = "haiku",
+ target_os = "openbsd",
+ target_os = "nto",
+ target_os = "vita"
+)))]
fn into_secs(duration: Duration) -> c_int {
- min(duration.as_secs(), c_int::max_value() as u64) as c_int
+ min(duration.as_secs(), c_int::MAX as u64) as c_int
+}
+
+/// Get the flags using `cmd`.
+#[cfg(not(target_os = "vita"))]
+fn fcntl_get(fd: Socket, cmd: c_int) -> io::Result<c_int> {
+ syscall!(fcntl(fd, cmd))
}
/// Add `flag` to the current set flags of `F_GETFD`.
+#[cfg(not(target_os = "vita"))]
fn fcntl_add(fd: Socket, get_cmd: c_int, set_cmd: c_int, flag: c_int) -> io::Result<()> {
- let previous = syscall!(fcntl(fd, get_cmd))?;
+ let previous = fcntl_get(fd, get_cmd)?;
let new = previous | flag;
if new != previous {
syscall!(fcntl(fd, set_cmd, new)).map(|_| ())
@@ -951,8 +1238,9 @@ fn fcntl_add(fd: Socket, get_cmd: c_int, set_cmd: c_int, flag: c_int) -> io::Res
}
/// Remove `flag` to the current set flags of `F_GETFD`.
+#[cfg(not(target_os = "vita"))]
fn fcntl_remove(fd: Socket, get_cmd: c_int, set_cmd: c_int, flag: c_int) -> io::Result<()> {
- let previous = syscall!(fcntl(fd, get_cmd))?;
+ let previous = fcntl_get(fd, get_cmd)?;
let new = previous & !flag;
if new != previous {
syscall!(fcntl(fd, set_cmd, new)).map(|_| ())
@@ -987,7 +1275,7 @@ pub(crate) unsafe fn setsockopt<T>(
val: c_int,
payload: T,
) -> io::Result<()> {
- let payload = &payload as *const T as *const c_void;
+ let payload = ptr::addr_of!(payload).cast();
syscall!(setsockopt(
fd,
opt,
@@ -998,7 +1286,7 @@ pub(crate) unsafe fn setsockopt<T>(
.map(|_| ())
}
-pub(crate) fn to_in_addr(addr: &Ipv4Addr) -> in_addr {
+pub(crate) const fn to_in_addr(addr: &Ipv4Addr) -> in_addr {
// `s_addr` is stored as BE on all machines, and the array is in BE order.
// So the native endian conversion method is used so that it's never
// swapped.
@@ -1011,7 +1299,7 @@ pub(crate) fn from_in_addr(in_addr: in_addr) -> Ipv4Addr {
Ipv4Addr::from(in_addr.s_addr.to_ne_bytes())
}
-pub(crate) fn to_in6_addr(addr: &Ipv6Addr) -> in6_addr {
+pub(crate) const fn to_in6_addr(addr: &Ipv6Addr) -> in6_addr {
in6_addr {
s6_addr: addr.octets(),
}
@@ -1022,14 +1310,18 @@ pub(crate) fn from_in6_addr(addr: in6_addr) -> Ipv6Addr {
}
#[cfg(not(any(
+ target_os = "aix",
target_os = "haiku",
target_os = "illumos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
+ target_os = "nto",
+ target_os = "espidf",
+ target_os = "vita",
)))]
-pub(crate) fn to_mreqn(
+pub(crate) const fn to_mreqn(
multiaddr: &Ipv4Addr,
interface: &crate::socket::InterfaceIndexOrAddress,
) -> libc::ip_mreqn {
@@ -1056,6 +1348,7 @@ impl crate::Socket {
/// This function will block the calling thread until a new connection is
/// established. When established, the corresponding `Socket` and the remote
/// peer's address will be returned.
+ #[doc = man_links!(unix: accept4(2))]
#[cfg(all(
feature = "all",
any(
@@ -1066,7 +1359,7 @@ impl crate::Socket {
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
- target_os = "openbsd"
+ target_os = "openbsd",
)
))]
#[cfg_attr(
@@ -1081,7 +1374,7 @@ impl crate::Socket {
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
- target_os = "openbsd"
+ target_os = "openbsd",
)
)))
)]
@@ -1097,12 +1390,12 @@ impl crate::Socket {
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
- target_os = "openbsd"
+ target_os = "openbsd",
))]
pub(crate) fn _accept4(&self, flags: c_int) -> io::Result<(crate::Socket, SockAddr)> {
// Safety: `accept4` initialises the `SockAddr` for us.
unsafe {
- SockAddr::init(|storage, len| {
+ SockAddr::try_init(|storage, len| {
syscall!(accept4(self.as_raw(), storage.cast(), len, flags))
.map(crate::Socket::from_raw)
})
@@ -1114,12 +1407,22 @@ impl crate::Socket {
/// # Notes
///
/// On supported platforms you can use [`Type::cloexec`].
- #[cfg(feature = "all")]
+ #[cfg_attr(
+ any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos"
+ ),
+ allow(rustdoc::broken_intra_doc_links)
+ )]
+ #[cfg(all(feature = "all", not(target_os = "vita")))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "all", unix))))]
pub fn set_cloexec(&self, close_on_exec: bool) -> io::Result<()> {
self._set_cloexec(close_on_exec)
}
+ #[cfg(not(target_os = "vita"))]
pub(crate) fn _set_cloexec(&self, close_on_exec: bool) -> io::Result<()> {
if close_on_exec {
fcntl_add(
@@ -1139,13 +1442,37 @@ impl crate::Socket {
}
/// Sets `SO_NOSIGPIPE` on the socket.
- #[cfg(all(feature = "all", any(doc, target_vendor = "apple")))]
- #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_vendor = "apple"))))]
+ #[cfg(all(
+ feature = "all",
+ any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
+ )
+ ))]
+ #[cfg_attr(
+ docsrs,
+ doc(cfg(all(
+ feature = "all",
+ any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
+ )
+ )))
+ )]
pub fn set_nosigpipe(&self, nosigpipe: bool) -> io::Result<()> {
self._set_nosigpipe(nosigpipe)
}
- #[cfg(target_vendor = "apple")]
+ #[cfg(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
+ ))]
pub(crate) fn _set_nosigpipe(&self, nosigpipe: bool) -> io::Result<()> {
unsafe {
setsockopt(
@@ -1193,6 +1520,7 @@ impl crate::Socket {
#[cfg(all(
feature = "all",
any(
+ target_os = "aix",
target_os = "android",
target_os = "freebsd",
target_os = "fuchsia",
@@ -1204,6 +1532,7 @@ impl crate::Socket {
doc(cfg(all(
feature = "all",
any(
+ target_os = "aix",
target_os = "android",
target_os = "freebsd",
target_os = "fuchsia",
@@ -1334,7 +1663,7 @@ impl crate::Socket {
///
/// For more information about this option, see [`set_cork`].
///
- /// [`set_cork`]: Socket::set_cork
+ /// [`set_cork`]: crate::Socket::set_cork
#[cfg(all(
feature = "all",
any(target_os = "android", target_os = "fuchsia", target_os = "linux")
@@ -1385,7 +1714,7 @@ impl crate::Socket {
///
/// For more information about this option, see [`set_quickack`].
///
- /// [`set_quickack`]: Socket::set_quickack
+ /// [`set_quickack`]: crate::Socket::set_quickack
#[cfg(all(
feature = "all",
any(target_os = "android", target_os = "fuchsia", target_os = "linux")
@@ -1436,7 +1765,7 @@ impl crate::Socket {
///
/// For more information about this option, see [`set_thin_linear_timeouts`].
///
- /// [`set_thin_linear_timeouts`]: Socket::set_thin_linear_timeouts
+ /// [`set_thin_linear_timeouts`]: crate::Socket::set_thin_linear_timeouts
#[cfg(all(
feature = "all",
any(target_os = "android", target_os = "fuchsia", target_os = "linux")
@@ -1558,8 +1887,8 @@ impl crate::Socket {
/// Sets the value for the `SO_SETFIB` option on this socket.
///
/// Bind socket to the specified forwarding table (VRF) on a FreeBSD.
- #[cfg(all(feature = "all", any(target_os = "freebsd")))]
- #[cfg_attr(docsrs, doc(cfg(all(feature = "all", any(target_os = "freebsd")))))]
+ #[cfg(all(feature = "all", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "freebsd"))))]
pub fn set_fib(&self, fib: u32) -> io::Result<()> {
syscall!(setsockopt(
self.as_raw(),
@@ -1571,6 +1900,33 @@ impl crate::Socket {
.map(|_| ())
}
+ /// This method is deprecated, use [`crate::Socket::bind_device_by_index_v4`].
+ #[cfg(all(
+ feature = "all",
+ any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
+ )
+ ))]
+ #[cfg_attr(
+ docsrs,
+ doc(cfg(all(
+ feature = "all",
+ any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
+ )
+ )))
+ )]
+ #[deprecated = "Use `Socket::bind_device_by_index_v4` instead"]
+ pub fn bind_device_by_index(&self, interface: Option<NonZeroU32>) -> io::Result<()> {
+ self.bind_device_by_index_v4(interface)
+ }
+
/// Sets the value for `IP_BOUND_IF` option on this socket.
///
/// If a socket is bound to an interface, only packets received from that
@@ -1579,28 +1935,162 @@ impl crate::Socket {
/// If `interface` is `None`, the binding is removed. If the `interface`
/// index is not valid, an error is returned.
///
- /// One can use `libc::if_nametoindex` to convert an interface alias to an
+ /// One can use [`libc::if_nametoindex`] to convert an interface alias to an
/// index.
- #[cfg(all(feature = "all", target_vendor = "apple"))]
- #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_vendor = "apple"))))]
- pub fn bind_device_by_index(&self, interface: Option<NonZeroU32>) -> io::Result<()> {
- let index = interface.map(NonZeroU32::get).unwrap_or(0);
+ #[cfg(all(
+ feature = "all",
+ any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
+ )
+ ))]
+ #[cfg_attr(
+ docsrs,
+ doc(cfg(all(
+ feature = "all",
+ any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
+ )
+ )))
+ )]
+ pub fn bind_device_by_index_v4(&self, interface: Option<NonZeroU32>) -> io::Result<()> {
+ let index = interface.map_or(0, NonZeroU32::get);
unsafe { setsockopt(self.as_raw(), IPPROTO_IP, libc::IP_BOUND_IF, index) }
}
+ /// Sets the value for `IPV6_BOUND_IF` option on this socket.
+ ///
+ /// If a socket is bound to an interface, only packets received from that
+ /// particular interface are processed by the socket.
+ ///
+ /// If `interface` is `None`, the binding is removed. If the `interface`
+ /// index is not valid, an error is returned.
+ ///
+ /// One can use [`libc::if_nametoindex`] to convert an interface alias to an
+ /// index.
+ #[cfg(all(
+ feature = "all",
+ any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
+ )
+ ))]
+ #[cfg_attr(
+ docsrs,
+ doc(cfg(all(
+ feature = "all",
+ any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
+ )
+ )))
+ )]
+ pub fn bind_device_by_index_v6(&self, interface: Option<NonZeroU32>) -> io::Result<()> {
+ let index = interface.map_or(0, NonZeroU32::get);
+ unsafe { setsockopt(self.as_raw(), IPPROTO_IPV6, libc::IPV6_BOUND_IF, index) }
+ }
+
/// Gets the value for `IP_BOUND_IF` option on this socket, i.e. the index
/// for the interface to which the socket is bound.
///
/// Returns `None` if the socket is not bound to any interface, otherwise
/// returns an interface index.
- #[cfg(all(feature = "all", target_vendor = "apple"))]
- #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_vendor = "apple"))))]
- pub fn device_index(&self) -> io::Result<Option<NonZeroU32>> {
+ #[cfg(all(
+ feature = "all",
+ any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
+ )
+ ))]
+ #[cfg_attr(
+ docsrs,
+ doc(cfg(all(
+ feature = "all",
+ any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
+ )
+ )))
+ )]
+ pub fn device_index_v4(&self) -> io::Result<Option<NonZeroU32>> {
let index =
unsafe { getsockopt::<libc::c_uint>(self.as_raw(), IPPROTO_IP, libc::IP_BOUND_IF)? };
Ok(NonZeroU32::new(index))
}
+ /// This method is deprecated, use [`crate::Socket::device_index_v4`].
+ #[cfg(all(
+ feature = "all",
+ any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
+ )
+ ))]
+ #[cfg_attr(
+ docsrs,
+ doc(cfg(all(
+ feature = "all",
+ any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
+ )
+ )))
+ )]
+ #[deprecated = "Use `Socket::device_index_v4` instead"]
+ pub fn device_index(&self) -> io::Result<Option<NonZeroU32>> {
+ self.device_index_v4()
+ }
+
+ /// Gets the value for `IPV6_BOUND_IF` option on this socket, i.e. the index
+ /// for the interface to which the socket is bound.
+ ///
+ /// Returns `None` if the socket is not bound to any interface, otherwise
+ /// returns an interface index.
+ #[cfg(all(
+ feature = "all",
+ any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
+ )
+ ))]
+ #[cfg_attr(
+ docsrs,
+ doc(cfg(all(
+ feature = "all",
+ any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
+ )
+ )))
+ )]
+ pub fn device_index_v6(&self) -> io::Result<Option<NonZeroU32>> {
+ let index = unsafe {
+ getsockopt::<libc::c_uint>(self.as_raw(), IPPROTO_IPV6, libc::IPV6_BOUND_IF)?
+ };
+ Ok(NonZeroU32::new(index))
+ }
+
/// Get the value of the `SO_INCOMING_CPU` option on this socket.
///
/// For more information about this option, see [`set_cpu_affinity`].
@@ -1683,6 +2173,35 @@ impl crate::Socket {
}
}
+ /// Get the value of the `SO_REUSEPORT_LB` option on this socket.
+ ///
+ /// For more information about this option, see [`set_reuse_port_lb`].
+ ///
+ /// [`set_reuse_port_lb`]: crate::Socket::set_reuse_port_lb
+ #[cfg(all(feature = "all", target_os = "freebsd"))]
+ pub fn reuse_port_lb(&self) -> io::Result<bool> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_REUSEPORT_LB)
+ .map(|reuse| reuse != 0)
+ }
+ }
+
+ /// Set value for the `SO_REUSEPORT_LB` option on this socket.
+ ///
+ /// This allows multiple programs or threads to bind to the same port and
+ /// incoming connections will be load balanced using a hash function.
+ #[cfg(all(feature = "all", target_os = "freebsd"))]
+ pub fn set_reuse_port_lb(&self, reuse: bool) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ libc::SOL_SOCKET,
+ libc::SO_REUSEPORT_LB,
+ reuse as c_int,
+ )
+ }
+ }
+
/// Get the value of the `IP_FREEBIND` option on this socket.
///
/// For more information about this option, see [`set_freebind`].
@@ -1800,6 +2319,62 @@ impl crate::Socket {
}
}
+ /// Get the value for the `SO_ORIGINAL_DST` option on this socket.
+ ///
+ /// This value contains the original destination IPv4 address of the connection
+ /// redirected using `iptables` `REDIRECT` or `TPROXY`.
+ #[cfg(all(
+ feature = "all",
+ any(target_os = "android", target_os = "fuchsia", target_os = "linux")
+ ))]
+ #[cfg_attr(
+ docsrs,
+ doc(cfg(all(
+ feature = "all",
+ any(target_os = "android", target_os = "fuchsia", target_os = "linux")
+ )))
+ )]
+ pub fn original_dst(&self) -> io::Result<SockAddr> {
+ // Safety: `getsockopt` initialises the `SockAddr` for us.
+ unsafe {
+ SockAddr::try_init(|storage, len| {
+ syscall!(getsockopt(
+ self.as_raw(),
+ libc::SOL_IP,
+ libc::SO_ORIGINAL_DST,
+ storage.cast(),
+ len
+ ))
+ })
+ }
+ .map(|(_, addr)| addr)
+ }
+
+ /// Get the value for the `IP6T_SO_ORIGINAL_DST` option on this socket.
+ ///
+ /// This value contains the original destination IPv6 address of the connection
+ /// redirected using `ip6tables` `REDIRECT` or `TPROXY`.
+ #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
+ #[cfg_attr(
+ docsrs,
+ doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux"))))
+ )]
+ pub fn original_dst_ipv6(&self) -> io::Result<SockAddr> {
+ // Safety: `getsockopt` initialises the `SockAddr` for us.
+ unsafe {
+ SockAddr::try_init(|storage, len| {
+ syscall!(getsockopt(
+ self.as_raw(),
+ libc::SOL_IPV6,
+ libc::IP6T_SO_ORIGINAL_DST,
+ storage.cast(),
+ len
+ ))
+ })
+ }
+ .map(|(_, addr)| addr)
+ }
+
/// Copies data between a `file` and this socket using the `sendfile(2)`
/// system call. Because this copying is done within the kernel,
/// `sendfile()` is more efficient than the combination of `read(2)` and
@@ -1809,6 +2384,7 @@ impl crate::Socket {
/// Different OSs support different kinds of `file`s, see the OS
/// documentation for what kind of files are supported. Generally *regular*
/// files are supported by all OSs.
+ #[doc = man_links!(unix: sendfile(2))]
///
/// The `offset` is the absolute offset into the `file` to use as starting
/// point.
@@ -1821,10 +2397,14 @@ impl crate::Socket {
#[cfg(all(
feature = "all",
any(
+ target_os = "aix",
target_os = "android",
target_os = "freebsd",
+ target_os = "ios",
target_os = "linux",
- target_vendor = "apple",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
)
))]
#[cfg_attr(
@@ -1832,10 +2412,14 @@ impl crate::Socket {
doc(cfg(all(
feature = "all",
any(
+ target_os = "aix",
target_os = "android",
target_os = "freebsd",
+ target_os = "ios",
target_os = "linux",
- target_vendor = "apple",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
)
)))
)]
@@ -1851,7 +2435,15 @@ impl crate::Socket {
self._sendfile(file.as_raw_fd(), offset as _, length)
}
- #[cfg(all(feature = "all", target_vendor = "apple"))]
+ #[cfg(all(
+ feature = "all",
+ any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "tvos",
+ target_os = "watchos",
+ )
+ ))]
fn _sendfile(
&self,
file: RawFd,
@@ -1917,6 +2509,37 @@ impl crate::Socket {
.map(|_| sbytes as usize)
}
+ #[cfg(all(feature = "all", target_os = "aix"))]
+ fn _sendfile(
+ &self,
+ file: RawFd,
+ offset: libc::off_t,
+ length: Option<NonZeroUsize>,
+ ) -> io::Result<usize> {
+ let nbytes = match length {
+ Some(n) => n.get() as i64,
+ None => -1,
+ };
+ let mut params = libc::sf_parms {
+ header_data: ptr::null_mut(),
+ header_length: 0,
+ file_descriptor: file,
+ file_size: 0,
+ file_offset: offset as u64,
+ file_bytes: nbytes,
+ trailer_data: ptr::null_mut(),
+ trailer_length: 0,
+ bytes_sent: 0,
+ };
+ // AIX doesn't support SF_REUSE, socket will be closed after successful transmission.
+ syscall!(send_file(
+ &mut self.as_raw() as *mut _,
+ &mut params as *mut _,
+ libc::SF_CLOSE as libc::c_uint,
+ ))
+ .map(|_| params.bytes_sent as usize)
+ }
+
/// Set the value of the `TCP_USER_TIMEOUT` option on this socket.
///
/// If set, this specifies the maximum amount of time that transmitted data may remain
@@ -1939,9 +2562,9 @@ impl crate::Socket {
)))
)]
pub fn set_tcp_user_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
- let timeout = timeout
- .map(|to| min(to.as_millis(), libc::c_uint::MAX as u128) as libc::c_uint)
- .unwrap_or(0);
+ let timeout = timeout.map_or(0, |to| {
+ min(to.as_millis(), libc::c_uint::MAX as u128) as libc::c_uint
+ });
unsafe {
setsockopt(
self.as_raw(),
@@ -1956,7 +2579,7 @@ impl crate::Socket {
///
/// For more information about this option, see [`set_tcp_user_timeout`].
///
- /// [`set_tcp_user_timeout`]: Socket::set_tcp_user_timeout
+ /// [`set_tcp_user_timeout`]: crate::Socket::set_tcp_user_timeout
#[cfg(all(
feature = "all",
any(target_os = "android", target_os = "fuchsia", target_os = "linux")
@@ -2007,10 +2630,414 @@ impl crate::Socket {
/// Detach Berkeley Packet Filter(BPF) from this socket.
///
/// For more information about this option, see [`attach_filter`]
+ ///
+ /// [`attach_filter`]: crate::Socket::attach_filter
#[cfg(all(feature = "all", any(target_os = "linux", target_os = "android")))]
pub fn detach_filter(&self) -> io::Result<()> {
unsafe { setsockopt(self.as_raw(), libc::SOL_SOCKET, libc::SO_DETACH_FILTER, 0) }
}
+
+ /// Gets the value for the `SO_COOKIE` option on this socket.
+ ///
+ /// The socket cookie is a unique, kernel-managed identifier tied to each socket.
+ /// Therefore, there is no corresponding `set` helper.
+ ///
+ /// For more information about this option, see [Linux patch](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=5daab9db7b65df87da26fd8cfa695fb9546a1ddb)
+ #[cfg(all(feature = "all", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
+ pub fn cookie(&self) -> io::Result<u64> {
+ unsafe { getsockopt::<libc::c_ulonglong>(self.as_raw(), libc::SOL_SOCKET, libc::SO_COOKIE) }
+ }
+
+ /// Get the value of the `IPV6_TCLASS` option for this socket.
+ ///
+ /// For more information about this option, see [`set_tclass_v6`].
+ ///
+ /// [`set_tclass_v6`]: crate::Socket::set_tclass_v6
+ #[cfg(all(
+ feature = "all",
+ any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ )
+ ))]
+ #[cfg_attr(
+ docsrs,
+ doc(cfg(all(
+ feature = "all",
+ any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ )
+ )))
+ )]
+ pub fn tclass_v6(&self) -> io::Result<u32> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), IPPROTO_IPV6, libc::IPV6_TCLASS)
+ .map(|tclass| tclass as u32)
+ }
+ }
+
+ /// Set the value of the `IPV6_TCLASS` option for this socket.
+ ///
+ /// Specifies the traffic class field that is used in every packets
+ /// sent from this socket.
+ #[cfg(all(
+ feature = "all",
+ any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ )
+ ))]
+ #[cfg_attr(
+ docsrs,
+ doc(cfg(all(
+ feature = "all",
+ any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ )
+ )))
+ )]
+ pub fn set_tclass_v6(&self, tclass: u32) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ IPPROTO_IPV6,
+ libc::IPV6_TCLASS,
+ tclass as c_int,
+ )
+ }
+ }
+
+ /// Get the value of the `TCP_CONGESTION` option for this socket.
+ ///
+ /// For more information about this option, see [`set_tcp_congestion`].
+ ///
+ /// [`set_tcp_congestion`]: crate::Socket::set_tcp_congestion
+ #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
+ #[cfg_attr(
+ docsrs,
+ doc(cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux"))))
+ )]
+ pub fn tcp_congestion(&self) -> io::Result<Vec<u8>> {
+ let mut payload: [u8; TCP_CA_NAME_MAX] = [0; TCP_CA_NAME_MAX];
+ let mut len = payload.len() as libc::socklen_t;
+ syscall!(getsockopt(
+ self.as_raw(),
+ IPPROTO_TCP,
+ libc::TCP_CONGESTION,
+ payload.as_mut_ptr().cast(),
+ &mut len,
+ ))
+ .map(|_| payload[..len as usize].to_vec())
+ }
+
+ /// Set the value of the `TCP_CONGESTION` option for this socket.
+ ///
+ /// Specifies the TCP congestion control algorithm to use for this socket.
+ ///
+ /// The value must be a valid TCP congestion control algorithm name of the
+ /// platform. For example, Linux may supports "reno", "cubic".
+ #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
+ #[cfg_attr(
+ docsrs,
+ doc(cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux"))))
+ )]
+ pub fn set_tcp_congestion(&self, tcp_ca_name: &[u8]) -> io::Result<()> {
+ syscall!(setsockopt(
+ self.as_raw(),
+ IPPROTO_TCP,
+ libc::TCP_CONGESTION,
+ tcp_ca_name.as_ptr() as *const _,
+ tcp_ca_name.len() as libc::socklen_t,
+ ))
+ .map(|_| ())
+ }
+
+ /// Set value for the `DCCP_SOCKOPT_SERVICE` option on this socket.
+ ///
+ /// Sets the DCCP service. The specification mandates use of service codes.
+ /// If this socket option is not set, the socket will fall back to 0 (which
+ /// means that no meaningful service code is present). On active sockets
+ /// this is set before [`connect`]. On passive sockets up to 32 service
+ /// codes can be set before calling [`bind`]
+ ///
+ /// [`connect`]: crate::Socket::connect
+ /// [`bind`]: crate::Socket::bind
+ #[cfg(all(feature = "all", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
+ pub fn set_dccp_service(&self, code: u32) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ libc::SOL_DCCP,
+ libc::DCCP_SOCKOPT_SERVICE,
+ code,
+ )
+ }
+ }
+
+ /// Get the value of the `DCCP_SOCKOPT_SERVICE` option on this socket.
+ ///
+ /// For more information about this option see [`set_dccp_service`]
+ ///
+ /// [`set_dccp_service`]: crate::Socket::set_dccp_service
+ #[cfg(all(feature = "all", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
+ pub fn dccp_service(&self) -> io::Result<u32> {
+ unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_SERVICE) }
+ }
+
+ /// Set value for the `DCCP_SOCKOPT_CCID` option on this socket.
+ ///
+ /// This option sets both the TX and RX CCIDs at the same time.
+ #[cfg(all(feature = "all", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
+ pub fn set_dccp_ccid(&self, ccid: u8) -> io::Result<()> {
+ unsafe { setsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_CCID, ccid) }
+ }
+
+ /// Get the value of the `DCCP_SOCKOPT_TX_CCID` option on this socket.
+ ///
+ /// For more information about this option see [`set_dccp_ccid`].
+ ///
+ /// [`set_dccp_ccid`]: crate::Socket::set_dccp_ccid
+ #[cfg(all(feature = "all", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
+ pub fn dccp_tx_ccid(&self) -> io::Result<u32> {
+ unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_TX_CCID) }
+ }
+
+ /// Get the value of the `DCCP_SOCKOPT_RX_CCID` option on this socket.
+ ///
+ /// For more information about this option see [`set_dccp_ccid`].
+ ///
+ /// [`set_dccp_ccid`]: crate::Socket::set_dccp_ccid
+ #[cfg(all(feature = "all", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
+ pub fn dccp_xx_ccid(&self) -> io::Result<u32> {
+ unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_RX_CCID) }
+ }
+
+ /// Set value for the `DCCP_SOCKOPT_SERVER_TIMEWAIT` option on this socket.
+ ///
+ /// Enables a listening socket to hold timewait state when closing the
+ /// connection. This option must be set after `accept` returns.
+ #[cfg(all(feature = "all", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
+ pub fn set_dccp_server_timewait(&self, hold_timewait: bool) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ libc::SOL_DCCP,
+ libc::DCCP_SOCKOPT_SERVER_TIMEWAIT,
+ hold_timewait as c_int,
+ )
+ }
+ }
+
+ /// Get the value of the `DCCP_SOCKOPT_SERVER_TIMEWAIT` option on this socket.
+ ///
+ /// For more information see [`set_dccp_server_timewait`]
+ ///
+ /// [`set_dccp_server_timewait`]: crate::Socket::set_dccp_server_timewait
+ #[cfg(all(feature = "all", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
+ pub fn dccp_server_timewait(&self) -> io::Result<bool> {
+ unsafe {
+ getsockopt(
+ self.as_raw(),
+ libc::SOL_DCCP,
+ libc::DCCP_SOCKOPT_SERVER_TIMEWAIT,
+ )
+ }
+ }
+
+ /// Set value for the `DCCP_SOCKOPT_SEND_CSCOV` option on this socket.
+ ///
+ /// Both this option and `DCCP_SOCKOPT_RECV_CSCOV` are used for setting the
+ /// partial checksum coverage. The default is that checksums always cover
+ /// the entire packet and that only fully covered application data is
+ /// accepted by the receiver. Hence, when using this feature on the sender,
+ /// it must be enabled at the receiver too, with suitable choice of CsCov.
+ #[cfg(all(feature = "all", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
+ pub fn set_dccp_send_cscov(&self, level: u32) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ libc::SOL_DCCP,
+ libc::DCCP_SOCKOPT_SEND_CSCOV,
+ level,
+ )
+ }
+ }
+
+ /// Get the value of the `DCCP_SOCKOPT_SEND_CSCOV` option on this socket.
+ ///
+ /// For more information on this option see [`set_dccp_send_cscov`].
+ ///
+ /// [`set_dccp_send_cscov`]: crate::Socket::set_dccp_send_cscov
+ #[cfg(all(feature = "all", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
+ pub fn dccp_send_cscov(&self) -> io::Result<u32> {
+ unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_SEND_CSCOV) }
+ }
+
+ /// Set the value of the `DCCP_SOCKOPT_RECV_CSCOV` option on this socket.
+ ///
+ /// This option is only useful when combined with [`set_dccp_send_cscov`].
+ ///
+ /// [`set_dccp_send_cscov`]: crate::Socket::set_dccp_send_cscov
+ #[cfg(all(feature = "all", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
+ pub fn set_dccp_recv_cscov(&self, level: u32) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ libc::SOL_DCCP,
+ libc::DCCP_SOCKOPT_RECV_CSCOV,
+ level,
+ )
+ }
+ }
+
+ /// Get the value of the `DCCP_SOCKOPT_RECV_CSCOV` option on this socket.
+ ///
+ /// For more information on this option see [`set_dccp_recv_cscov`].
+ ///
+ /// [`set_dccp_recv_cscov`]: crate::Socket::set_dccp_recv_cscov
+ #[cfg(all(feature = "all", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
+ pub fn dccp_recv_cscov(&self) -> io::Result<u32> {
+ unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_RECV_CSCOV) }
+ }
+
+ /// Set value for the `DCCP_SOCKOPT_QPOLICY_TXQLEN` option on this socket.
+ ///
+ /// This option sets the maximum length of the output queue. A zero value is
+ /// interpreted as unbounded queue length.
+ #[cfg(all(feature = "all", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
+ pub fn set_dccp_qpolicy_txqlen(&self, length: u32) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ libc::SOL_DCCP,
+ libc::DCCP_SOCKOPT_QPOLICY_TXQLEN,
+ length,
+ )
+ }
+ }
+
+ /// Get the value of the `DCCP_SOCKOPT_QPOLICY_TXQLEN` on this socket.
+ ///
+ /// For more information on this option see [`set_dccp_qpolicy_txqlen`].
+ ///
+ /// [`set_dccp_qpolicy_txqlen`]: crate::Socket::set_dccp_qpolicy_txqlen
+ #[cfg(all(feature = "all", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
+ pub fn dccp_qpolicy_txqlen(&self) -> io::Result<u32> {
+ unsafe {
+ getsockopt(
+ self.as_raw(),
+ libc::SOL_DCCP,
+ libc::DCCP_SOCKOPT_QPOLICY_TXQLEN,
+ )
+ }
+ }
+
+ /// Get the value of the `DCCP_SOCKOPT_AVAILABLE_CCIDS` option on this socket.
+ ///
+ /// Returns the list of CCIDs supported by the endpoint.
+ ///
+ /// The parameter `N` is used to get the maximum number of supported
+ /// endpoints. The [documentation] recommends a minimum of four at the time
+ /// of writing.
+ ///
+ /// [documentation]: https://www.kernel.org/doc/html/latest/networking/dccp.html
+ #[cfg(all(feature = "all", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
+ pub fn dccp_available_ccids<const N: usize>(&self) -> io::Result<CcidEndpoints<N>> {
+ let mut endpoints = [0; N];
+ let mut length = endpoints.len() as libc::socklen_t;
+ syscall!(getsockopt(
+ self.as_raw(),
+ libc::SOL_DCCP,
+ libc::DCCP_SOCKOPT_AVAILABLE_CCIDS,
+ endpoints.as_mut_ptr().cast(),
+ &mut length,
+ ))?;
+ Ok(CcidEndpoints { endpoints, length })
+ }
+
+ /// Get the value of the `DCCP_SOCKOPT_GET_CUR_MPS` option on this socket.
+ ///
+ /// This option retrieves the current maximum packet size (application
+ /// payload size) in bytes.
+ #[cfg(all(feature = "all", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
+ pub fn dccp_cur_mps(&self) -> io::Result<u32> {
+ unsafe {
+ getsockopt(
+ self.as_raw(),
+ libc::SOL_DCCP,
+ libc::DCCP_SOCKOPT_GET_CUR_MPS,
+ )
+ }
+ }
+}
+
+/// See [`Socket::dccp_available_ccids`].
+#[cfg(all(feature = "all", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
+#[derive(Debug)]
+pub struct CcidEndpoints<const N: usize> {
+ endpoints: [u8; N],
+ length: u32,
+}
+
+#[cfg(all(feature = "all", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
+impl<const N: usize> std::ops::Deref for CcidEndpoints<N> {
+ type Target = [u8];
+
+ fn deref(&self) -> &[u8] {
+ &self.endpoints[0..self.length as usize]
+ }
+}
+
+#[cfg_attr(docsrs, doc(cfg(unix)))]
+impl AsFd for crate::Socket {
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ // SAFETY: lifetime is bound by self.
+ unsafe { BorrowedFd::borrow_raw(self.as_raw()) }
+ }
}
#[cfg_attr(docsrs, doc(cfg(unix)))]
@@ -2021,6 +3048,14 @@ impl AsRawFd for crate::Socket {
}
#[cfg_attr(docsrs, doc(cfg(unix)))]
+impl From<crate::Socket> for OwnedFd {
+ fn from(sock: crate::Socket) -> OwnedFd {
+ // SAFETY: sock.into_raw() always returns a valid fd.
+ unsafe { OwnedFd::from_raw_fd(sock.into_raw()) }
+ }
+}
+
+#[cfg_attr(docsrs, doc(cfg(unix)))]
impl IntoRawFd for crate::Socket {
fn into_raw_fd(self) -> c_int {
self.into_raw()
@@ -2028,6 +3063,14 @@ impl IntoRawFd for crate::Socket {
}
#[cfg_attr(docsrs, doc(cfg(unix)))]
+impl From<OwnedFd> for crate::Socket {
+ fn from(fd: OwnedFd) -> crate::Socket {
+ // SAFETY: `OwnedFd` ensures the fd is valid.
+ unsafe { crate::Socket::from_raw_fd(fd.into_raw_fd()) }
+ }
+}
+
+#[cfg_attr(docsrs, doc(cfg(unix)))]
impl FromRawFd for crate::Socket {
unsafe fn from_raw_fd(fd: c_int) -> crate::Socket {
crate::Socket::from_raw(fd)
diff --git a/src/sys/windows.rs b/src/sys/windows.rs
index c37ca85..4c5d987 100644
--- a/src/sys/windows.rs
+++ b/src/sys/windows.rs
@@ -11,31 +11,29 @@ use std::io::{self, IoSlice};
use std::marker::PhantomData;
use std::mem::{self, size_of, MaybeUninit};
use std::net::{self, Ipv4Addr, Ipv6Addr, Shutdown};
-use std::os::windows::prelude::*;
+use std::os::windows::io::{
+ AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket,
+};
+use std::path::Path;
use std::sync::Once;
use std::time::{Duration, Instant};
-use std::{ptr, slice};
-
-use winapi::ctypes::c_long;
-use winapi::shared::in6addr::*;
-use winapi::shared::inaddr::*;
-use winapi::shared::minwindef::DWORD;
-use winapi::shared::minwindef::ULONG;
-use winapi::shared::mstcpip::{tcp_keepalive, SIO_KEEPALIVE_VALS};
-use winapi::shared::ntdef::HANDLE;
-use winapi::shared::ws2def;
-use winapi::shared::ws2def::WSABUF;
-use winapi::um::handleapi::SetHandleInformation;
-use winapi::um::processthreadsapi::GetCurrentProcessId;
-use winapi::um::winbase::{self, INFINITE};
-use winapi::um::winsock2::{
- self as sock, u_long, POLLERR, POLLHUP, POLLRDNORM, POLLWRNORM, SD_BOTH, SD_RECEIVE, SD_SEND,
- WSAPOLLFD,
+use std::{process, ptr, slice};
+
+use windows_sys::Win32::Foundation::{SetHandleInformation, HANDLE, HANDLE_FLAG_INHERIT};
+#[cfg(feature = "all")]
+use windows_sys::Win32::Networking::WinSock::SO_PROTOCOL_INFOW;
+use windows_sys::Win32::Networking::WinSock::{
+ self, tcp_keepalive, FIONBIO, IN6_ADDR, IN6_ADDR_0, INVALID_SOCKET, IN_ADDR, IN_ADDR_0,
+ POLLERR, POLLHUP, POLLRDNORM, POLLWRNORM, SD_BOTH, SD_RECEIVE, SD_SEND, SIO_KEEPALIVE_VALS,
+ SOCKET_ERROR, WSABUF, WSAEMSGSIZE, WSAESHUTDOWN, WSAPOLLFD, WSAPROTOCOL_INFOW,
+ WSA_FLAG_NO_HANDLE_INHERIT, WSA_FLAG_OVERLAPPED,
};
+use windows_sys::Win32::System::Threading::INFINITE;
-use crate::{RecvFlags, SockAddr, TcpKeepalive, Type};
+use crate::{MsgHdr, RecvFlags, SockAddr, TcpKeepalive, Type};
-pub(crate) use winapi::ctypes::c_int;
+#[allow(non_camel_case_types)]
+pub(crate) type c_int = std::os::raw::c_int;
/// Fake MSG_TRUNC flag for the [`RecvFlags`] struct.
///
@@ -44,39 +42,45 @@ pub(crate) use winapi::ctypes::c_int;
pub(crate) const MSG_TRUNC: c_int = 0x01;
// Used in `Domain`.
-pub(crate) use winapi::shared::ws2def::{AF_INET, AF_INET6};
+pub(crate) const AF_INET: c_int = windows_sys::Win32::Networking::WinSock::AF_INET as c_int;
+pub(crate) const AF_INET6: c_int = windows_sys::Win32::Networking::WinSock::AF_INET6 as c_int;
+pub(crate) const AF_UNIX: c_int = windows_sys::Win32::Networking::WinSock::AF_UNIX as c_int;
+pub(crate) const AF_UNSPEC: c_int = windows_sys::Win32::Networking::WinSock::AF_UNSPEC as c_int;
// Used in `Type`.
-pub(crate) use winapi::shared::ws2def::{SOCK_DGRAM, SOCK_STREAM};
-#[cfg(feature = "all")]
-pub(crate) use winapi::shared::ws2def::{SOCK_RAW, SOCK_SEQPACKET};
+pub(crate) const SOCK_STREAM: c_int = windows_sys::Win32::Networking::WinSock::SOCK_STREAM as c_int;
+pub(crate) const SOCK_DGRAM: c_int = windows_sys::Win32::Networking::WinSock::SOCK_DGRAM as c_int;
+pub(crate) const SOCK_RAW: c_int = windows_sys::Win32::Networking::WinSock::SOCK_RAW as c_int;
+const SOCK_RDM: c_int = windows_sys::Win32::Networking::WinSock::SOCK_RDM as c_int;
+pub(crate) const SOCK_SEQPACKET: c_int =
+ windows_sys::Win32::Networking::WinSock::SOCK_SEQPACKET as c_int;
// Used in `Protocol`.
-pub(crate) const IPPROTO_ICMP: c_int = winapi::shared::ws2def::IPPROTO_ICMP as c_int;
-pub(crate) const IPPROTO_ICMPV6: c_int = winapi::shared::ws2def::IPPROTO_ICMPV6 as c_int;
-pub(crate) const IPPROTO_TCP: c_int = winapi::shared::ws2def::IPPROTO_TCP as c_int;
-pub(crate) const IPPROTO_UDP: c_int = winapi::shared::ws2def::IPPROTO_UDP as c_int;
+pub(crate) use windows_sys::Win32::Networking::WinSock::{
+ IPPROTO_ICMP, IPPROTO_ICMPV6, IPPROTO_TCP, IPPROTO_UDP,
+};
// Used in `SockAddr`.
-pub(crate) use winapi::shared::ws2def::{
- ADDRESS_FAMILY as sa_family_t, SOCKADDR as sockaddr, SOCKADDR_IN as sockaddr_in,
+pub(crate) use windows_sys::Win32::Networking::WinSock::{
+ SOCKADDR as sockaddr, SOCKADDR_IN as sockaddr_in, SOCKADDR_IN6 as sockaddr_in6,
SOCKADDR_STORAGE as sockaddr_storage,
};
-pub(crate) use winapi::shared::ws2ipdef::SOCKADDR_IN6_LH as sockaddr_in6;
-pub(crate) use winapi::um::ws2tcpip::socklen_t;
+#[allow(non_camel_case_types)]
+pub(crate) type sa_family_t = windows_sys::Win32::Networking::WinSock::ADDRESS_FAMILY;
+#[allow(non_camel_case_types)]
+pub(crate) type socklen_t = windows_sys::Win32::Networking::WinSock::socklen_t;
// Used in `Socket`.
-pub(crate) use winapi::shared::ws2def::{
- IPPROTO_IP, SOL_SOCKET, SO_BROADCAST, SO_ERROR, SO_KEEPALIVE, SO_LINGER, SO_OOBINLINE,
- SO_RCVBUF, SO_RCVTIMEO, SO_REUSEADDR, SO_SNDBUF, SO_SNDTIMEO, SO_TYPE, TCP_NODELAY,
-};
#[cfg(feature = "all")]
-pub(crate) use winapi::shared::ws2ipdef::IP_HDRINCL;
-pub(crate) use winapi::shared::ws2ipdef::{
- IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_MREQ as Ipv6Mreq, IPV6_MULTICAST_HOPS,
- IPV6_MULTICAST_IF, IPV6_MULTICAST_LOOP, IPV6_UNICAST_HOPS, IPV6_V6ONLY, IP_ADD_MEMBERSHIP,
- IP_ADD_SOURCE_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_DROP_SOURCE_MEMBERSHIP, IP_MREQ as IpMreq,
- IP_MREQ_SOURCE as IpMreqSource, IP_MULTICAST_IF, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TOS,
- IP_TTL,
+pub(crate) use windows_sys::Win32::Networking::WinSock::IP_HDRINCL;
+pub(crate) use windows_sys::Win32::Networking::WinSock::{
+ IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_MREQ as Ipv6Mreq,
+ IPV6_MULTICAST_HOPS, IPV6_MULTICAST_IF, IPV6_MULTICAST_LOOP, IPV6_RECVTCLASS,
+ IPV6_UNICAST_HOPS, IPV6_V6ONLY, IP_ADD_MEMBERSHIP, IP_ADD_SOURCE_MEMBERSHIP,
+ IP_DROP_MEMBERSHIP, IP_DROP_SOURCE_MEMBERSHIP, IP_MREQ as IpMreq,
+ IP_MREQ_SOURCE as IpMreqSource, IP_MULTICAST_IF, IP_MULTICAST_LOOP, IP_MULTICAST_TTL,
+ IP_RECVTOS, IP_TOS, IP_TTL, LINGER as linger, MSG_OOB, MSG_PEEK, SO_BROADCAST, SO_ERROR,
+ SO_KEEPALIVE, SO_LINGER, SO_OOBINLINE, SO_RCVBUF, SO_RCVTIMEO, SO_REUSEADDR, SO_SNDBUF,
+ SO_SNDTIMEO, SO_TYPE, TCP_NODELAY,
};
-pub(crate) use winapi::um::winsock2::{linger, MSG_OOB, MSG_PEEK};
-pub(crate) const IPPROTO_IPV6: c_int = winapi::shared::ws2def::IPPROTO_IPV6 as c_int;
+pub(crate) const IPPROTO_IP: c_int = windows_sys::Win32::Networking::WinSock::IPPROTO_IP as c_int;
+pub(crate) const SOL_SOCKET: c_int = windows_sys::Win32::Networking::WinSock::SOL_SOCKET as c_int;
/// Type used in set/getsockopt to retrieve the `TCP_NODELAY` option.
///
@@ -85,16 +89,16 @@ pub(crate) const IPPROTO_IPV6: c_int = winapi::shared::ws2def::IPPROTO_IPV6 as c
/// `BOOL` (alias for `c_int`, 4 bytes), however in practice this turns out to
/// be false (or misleading) as a `BOOLEAN` (`c_uchar`, 1 byte) is returned by
/// `getsockopt`.
-pub(crate) type Bool = winapi::shared::ntdef::BOOLEAN;
+pub(crate) type Bool = windows_sys::Win32::Foundation::BOOLEAN;
/// Maximum size of a buffer passed to system call like `recv` and `send`.
-const MAX_BUF_LEN: usize = <c_int>::max_value() as usize;
+const MAX_BUF_LEN: usize = c_int::MAX as usize;
/// Helper macro to execute a system call that returns an `io::Result`.
macro_rules! syscall {
($fn: ident ( $($arg: expr),* $(,)* ), $err_test: path, $err_value: expr) => {{
#[allow(unused_unsafe)]
- let res = unsafe { sock::$fn($($arg, )*) };
+ let res = unsafe { windows_sys::Win32::Networking::WinSock::$fn($($arg, )*) };
if $err_test(&res, &$err_value) {
Err(io::Error::last_os_error())
} else {
@@ -105,10 +109,10 @@ macro_rules! syscall {
impl_debug!(
crate::Domain,
- ws2def::AF_INET,
- ws2def::AF_INET6,
- ws2def::AF_UNIX,
- ws2def::AF_UNSPEC, // = 0.
+ self::AF_INET,
+ self::AF_INET6,
+ self::AF_UNIX,
+ self::AF_UNSPEC,
);
/// Windows only API.
@@ -131,19 +135,19 @@ impl Type {
impl_debug!(
crate::Type,
- ws2def::SOCK_STREAM,
- ws2def::SOCK_DGRAM,
- ws2def::SOCK_RAW,
- ws2def::SOCK_RDM,
- ws2def::SOCK_SEQPACKET,
+ self::SOCK_STREAM,
+ self::SOCK_DGRAM,
+ self::SOCK_RAW,
+ self::SOCK_RDM,
+ self::SOCK_SEQPACKET,
);
impl_debug!(
crate::Protocol,
- self::IPPROTO_ICMP,
- self::IPPROTO_ICMPV6,
- self::IPPROTO_TCP,
- self::IPPROTO_UDP,
+ WinSock::IPPROTO_ICMP,
+ WinSock::IPPROTO_ICMPV6,
+ WinSock::IPPROTO_TCP,
+ WinSock::IPPROTO_UDP,
);
impl std::fmt::Debug for RecvFlags {
@@ -166,10 +170,10 @@ unsafe impl<'a> Sync for MaybeUninitSlice<'a> {}
impl<'a> MaybeUninitSlice<'a> {
pub fn new(buf: &'a mut [MaybeUninit<u8>]) -> MaybeUninitSlice<'a> {
- assert!(buf.len() <= ULONG::MAX as usize);
+ assert!(buf.len() <= u32::MAX as usize);
MaybeUninitSlice {
vec: WSABUF {
- len: buf.len() as ULONG,
+ len: buf.len() as u32,
buf: buf.as_mut_ptr().cast(),
},
_lifetime: PhantomData,
@@ -185,6 +189,32 @@ impl<'a> MaybeUninitSlice<'a> {
}
}
+// Used in `MsgHdr`.
+pub(crate) use windows_sys::Win32::Networking::WinSock::WSAMSG as msghdr;
+
+pub(crate) fn set_msghdr_name(msg: &mut msghdr, name: &SockAddr) {
+ msg.name = name.as_ptr() as *mut _;
+ msg.namelen = name.len();
+}
+
+pub(crate) fn set_msghdr_iov(msg: &mut msghdr, ptr: *mut WSABUF, len: usize) {
+ msg.lpBuffers = ptr;
+ msg.dwBufferCount = min(len, u32::MAX as usize) as u32;
+}
+
+pub(crate) fn set_msghdr_control(msg: &mut msghdr, ptr: *mut u8, len: usize) {
+ msg.Control.buf = ptr;
+ msg.Control.len = len as u32;
+}
+
+pub(crate) fn set_msghdr_flags(msg: &mut msghdr, flags: c_int) {
+ msg.dwFlags = flags as u32;
+}
+
+pub(crate) fn msghdr_flags(msg: &msghdr) -> RecvFlags {
+ RecvFlags(msg.dwFlags as c_int)
+}
+
fn init() {
static INIT: Once = Once::new();
@@ -196,7 +226,7 @@ fn init() {
});
}
-pub(crate) type Socket = sock::SOCKET;
+pub(crate) type Socket = windows_sys::Win32::Networking::WinSock::SOCKET;
pub(crate) unsafe fn socket_from_raw(socket: Socket) -> crate::socket::Inner {
crate::socket::Inner::from_raw_socket(socket as RawSocket)
@@ -216,7 +246,7 @@ pub(crate) fn socket(family: c_int, mut ty: c_int, protocol: c_int) -> io::Resul
// Check if we set our custom flag.
let flags = if ty & Type::NO_INHERIT != 0 {
ty = ty & !Type::NO_INHERIT;
- sock::WSA_FLAG_NO_HANDLE_INHERIT
+ WSA_FLAG_NO_HANDLE_INHERIT
} else {
0
};
@@ -228,10 +258,10 @@ pub(crate) fn socket(family: c_int, mut ty: c_int, protocol: c_int) -> io::Resul
protocol,
ptr::null_mut(),
0,
- sock::WSA_FLAG_OVERLAPPED | flags,
+ WSA_FLAG_OVERLAPPED | flags,
),
PartialEq::eq,
- sock::INVALID_SOCKET
+ INVALID_SOCKET
)
}
@@ -248,7 +278,7 @@ pub(crate) fn poll_connect(socket: &crate::Socket, timeout: Duration) -> io::Res
let mut fd_array = WSAPOLLFD {
fd: socket.as_raw(),
- events: POLLRDNORM | POLLWRNORM,
+ events: (POLLRDNORM | POLLWRNORM) as i16,
revents: 0,
};
@@ -259,17 +289,19 @@ pub(crate) fn poll_connect(socket: &crate::Socket, timeout: Duration) -> io::Res
}
let timeout = (timeout - elapsed).as_millis();
- let timeout = clamp(timeout, 1, c_int::max_value() as u128) as c_int;
+ let timeout = clamp(timeout, 1, c_int::MAX as u128) as c_int;
match syscall!(
WSAPoll(&mut fd_array, 1, timeout),
PartialEq::eq,
- sock::SOCKET_ERROR
+ SOCKET_ERROR
) {
Ok(0) => return Err(io::ErrorKind::TimedOut.into()),
Ok(_) => {
// Error or hang up indicates an error (or failure to connect).
- if (fd_array.revents & POLLERR) != 0 || (fd_array.revents & POLLHUP) != 0 {
+ if (fd_array.revents & POLLERR as i16) != 0
+ || (fd_array.revents & POLLHUP as i16) != 0
+ {
match socket.take_error() {
Ok(Some(err)) => return Err(err),
Ok(None) => {
@@ -311,11 +343,11 @@ pub(crate) fn listen(socket: Socket, backlog: c_int) -> io::Result<()> {
pub(crate) fn accept(socket: Socket) -> io::Result<(Socket, SockAddr)> {
// Safety: `accept` initialises the `SockAddr` for us.
unsafe {
- SockAddr::init(|storage, len| {
+ SockAddr::try_init(|storage, len| {
syscall!(
accept(socket, storage.cast(), len),
PartialEq::eq,
- sock::INVALID_SOCKET
+ INVALID_SOCKET
)
})
}
@@ -324,11 +356,11 @@ pub(crate) fn accept(socket: Socket) -> io::Result<(Socket, SockAddr)> {
pub(crate) fn getsockname(socket: Socket) -> io::Result<SockAddr> {
// Safety: `getsockname` initialises the `SockAddr` for us.
unsafe {
- SockAddr::init(|storage, len| {
+ SockAddr::try_init(|storage, len| {
syscall!(
getsockname(socket, storage.cast(), len),
PartialEq::eq,
- sock::SOCKET_ERROR
+ SOCKET_ERROR
)
})
}
@@ -338,11 +370,11 @@ pub(crate) fn getsockname(socket: Socket) -> io::Result<SockAddr> {
pub(crate) fn getpeername(socket: Socket) -> io::Result<SockAddr> {
// Safety: `getpeername` initialises the `SockAddr` for us.
unsafe {
- SockAddr::init(|storage, len| {
+ SockAddr::try_init(|storage, len| {
syscall!(
getpeername(socket, storage.cast(), len),
PartialEq::eq,
- sock::SOCKET_ERROR
+ SOCKET_ERROR
)
})
}
@@ -350,11 +382,12 @@ pub(crate) fn getpeername(socket: Socket) -> io::Result<SockAddr> {
}
pub(crate) fn try_clone(socket: Socket) -> io::Result<Socket> {
- let mut info: MaybeUninit<sock::WSAPROTOCOL_INFOW> = MaybeUninit::uninit();
+ let mut info: MaybeUninit<WSAPROTOCOL_INFOW> = MaybeUninit::uninit();
syscall!(
- WSADuplicateSocketW(socket, GetCurrentProcessId(), info.as_mut_ptr()),
+ // NOTE: `process.id` is the same as `GetCurrentProcessId`.
+ WSADuplicateSocketW(socket, process::id(), info.as_mut_ptr()),
PartialEq::eq,
- sock::SOCKET_ERROR
+ SOCKET_ERROR
)?;
// Safety: `WSADuplicateSocketW` intialised `info` for us.
let mut info = unsafe { info.assume_init() };
@@ -366,16 +399,16 @@ pub(crate) fn try_clone(socket: Socket) -> io::Result<Socket> {
info.iProtocol,
&mut info,
0,
- sock::WSA_FLAG_OVERLAPPED | sock::WSA_FLAG_NO_HANDLE_INHERIT,
+ WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT,
),
PartialEq::eq,
- sock::INVALID_SOCKET
+ INVALID_SOCKET
)
}
pub(crate) fn set_nonblocking(socket: Socket, nonblocking: bool) -> io::Result<()> {
- let mut nonblocking = nonblocking as u_long;
- ioctlsocket(socket, sock::FIONBIO, &mut nonblocking)
+ let mut nonblocking = if nonblocking { 1 } else { 0 };
+ ioctlsocket(socket, FIONBIO, &mut nonblocking)
}
pub(crate) fn shutdown(socket: Socket, how: Shutdown) -> io::Result<()> {
@@ -383,8 +416,8 @@ pub(crate) fn shutdown(socket: Socket, how: Shutdown) -> io::Result<()> {
Shutdown::Write => SD_SEND,
Shutdown::Read => SD_RECEIVE,
Shutdown::Both => SD_BOTH,
- };
- syscall!(shutdown(socket, how), PartialEq::eq, sock::SOCKET_ERROR).map(|_| ())
+ } as i32;
+ syscall!(shutdown(socket, how), PartialEq::eq, SOCKET_ERROR).map(|_| ())
}
pub(crate) fn recv(socket: Socket, buf: &mut [MaybeUninit<u8>], flags: c_int) -> io::Result<usize> {
@@ -396,11 +429,11 @@ pub(crate) fn recv(socket: Socket, buf: &mut [MaybeUninit<u8>], flags: c_int) ->
flags,
),
PartialEq::eq,
- sock::SOCKET_ERROR
+ SOCKET_ERROR
);
match res {
Ok(n) => Ok(n as usize),
- Err(ref err) if err.raw_os_error() == Some(sock::WSAESHUTDOWN as i32) => Ok(0),
+ Err(ref err) if err.raw_os_error() == Some(WSAESHUTDOWN as i32) => Ok(0),
Err(err) => Err(err),
}
}
@@ -411,26 +444,24 @@ pub(crate) fn recv_vectored(
flags: c_int,
) -> io::Result<(usize, RecvFlags)> {
let mut nread = 0;
- let mut flags = flags as DWORD;
+ let mut flags = flags as u32;
let res = syscall!(
WSARecv(
socket,
bufs.as_mut_ptr().cast(),
- min(bufs.len(), DWORD::max_value() as usize) as DWORD,
+ min(bufs.len(), u32::MAX as usize) as u32,
&mut nread,
&mut flags,
ptr::null_mut(),
None,
),
PartialEq::eq,
- sock::SOCKET_ERROR
+ SOCKET_ERROR
);
match res {
Ok(_) => Ok((nread as usize, RecvFlags(0))),
- Err(ref err) if err.raw_os_error() == Some(sock::WSAESHUTDOWN as i32) => {
- Ok((0, RecvFlags(0)))
- }
- Err(ref err) if err.raw_os_error() == Some(sock::WSAEMSGSIZE as i32) => {
+ Err(ref err) if err.raw_os_error() == Some(WSAESHUTDOWN as i32) => Ok((0, RecvFlags(0))),
+ Err(ref err) if err.raw_os_error() == Some(WSAEMSGSIZE as i32) => {
Ok((nread as usize, RecvFlags(MSG_TRUNC)))
}
Err(err) => Err(err),
@@ -444,7 +475,7 @@ pub(crate) fn recv_from(
) -> io::Result<(usize, SockAddr)> {
// Safety: `recvfrom` initialises the `SockAddr` for us.
unsafe {
- SockAddr::init(|storage, addrlen| {
+ SockAddr::try_init(|storage, addrlen| {
let res = syscall!(
recvfrom(
socket,
@@ -455,17 +486,49 @@ pub(crate) fn recv_from(
addrlen,
),
PartialEq::eq,
- sock::SOCKET_ERROR
+ SOCKET_ERROR
);
match res {
Ok(n) => Ok(n as usize),
- Err(ref err) if err.raw_os_error() == Some(sock::WSAESHUTDOWN as i32) => Ok(0),
+ Err(ref err) if err.raw_os_error() == Some(WSAESHUTDOWN as i32) => Ok(0),
Err(err) => Err(err),
}
})
}
}
+pub(crate) fn peek_sender(socket: Socket) -> io::Result<SockAddr> {
+ // Safety: `recvfrom` initialises the `SockAddr` for us.
+ let ((), sender) = unsafe {
+ SockAddr::try_init(|storage, addrlen| {
+ let res = syscall!(
+ recvfrom(
+ socket,
+ // Windows *appears* not to care if you pass a null pointer.
+ ptr::null_mut(),
+ 0,
+ MSG_PEEK,
+ storage.cast(),
+ addrlen,
+ ),
+ PartialEq::eq,
+ SOCKET_ERROR
+ );
+ match res {
+ Ok(_n) => Ok(()),
+ Err(e) => match e.raw_os_error() {
+ Some(code) if code == (WSAESHUTDOWN as i32) || code == (WSAEMSGSIZE as i32) => {
+ Ok(())
+ }
+ _ => Err(e),
+ },
+ }
+ })
+ }?;
+
+ Ok(sender)
+}
+
pub(crate) fn recv_from_vectored(
socket: Socket,
bufs: &mut [crate::MaybeUninitSlice<'_>],
@@ -473,14 +536,14 @@ pub(crate) fn recv_from_vectored(
) -> io::Result<(usize, RecvFlags, SockAddr)> {
// Safety: `recvfrom` initialises the `SockAddr` for us.
unsafe {
- SockAddr::init(|storage, addrlen| {
+ SockAddr::try_init(|storage, addrlen| {
let mut nread = 0;
- let mut flags = flags as DWORD;
+ let mut flags = flags as u32;
let res = syscall!(
WSARecvFrom(
socket,
bufs.as_mut_ptr().cast(),
- min(bufs.len(), DWORD::max_value() as usize) as DWORD,
+ min(bufs.len(), u32::MAX as usize) as u32,
&mut nread,
&mut flags,
storage.cast(),
@@ -489,14 +552,14 @@ pub(crate) fn recv_from_vectored(
None,
),
PartialEq::eq,
- sock::SOCKET_ERROR
+ SOCKET_ERROR
);
match res {
Ok(_) => Ok((nread as usize, RecvFlags(0))),
- Err(ref err) if err.raw_os_error() == Some(sock::WSAESHUTDOWN as i32) => {
+ Err(ref err) if err.raw_os_error() == Some(WSAESHUTDOWN as i32) => {
Ok((nread as usize, RecvFlags(0)))
}
- Err(ref err) if err.raw_os_error() == Some(sock::WSAEMSGSIZE as i32) => {
+ Err(ref err) if err.raw_os_error() == Some(WSAEMSGSIZE as i32) => {
Ok((nread as usize, RecvFlags(MSG_TRUNC)))
}
Err(err) => Err(err),
@@ -515,7 +578,7 @@ pub(crate) fn send(socket: Socket, buf: &[u8], flags: c_int) -> io::Result<usize
flags,
),
PartialEq::eq,
- sock::SOCKET_ERROR
+ SOCKET_ERROR
)
.map(|n| n as usize)
}
@@ -543,14 +606,14 @@ pub(crate) fn send_vectored(
//
// [1] https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasend
bufs.as_ptr() as *mut _,
- min(bufs.len(), DWORD::max_value() as usize) as DWORD,
+ min(bufs.len(), u32::MAX as usize) as u32,
&mut nsent,
- flags as DWORD,
+ flags as u32,
std::ptr::null_mut(),
None,
),
PartialEq::eq,
- sock::SOCKET_ERROR
+ SOCKET_ERROR
)
.map(|_| nsent as usize)
}
@@ -571,7 +634,7 @@ pub(crate) fn send_to(
addr.len(),
),
PartialEq::eq,
- sock::SOCKET_ERROR
+ SOCKET_ERROR
)
.map(|n| n as usize)
}
@@ -588,26 +651,43 @@ pub(crate) fn send_to_vectored(
socket,
// FIXME: Same problem as in `send_vectored`.
bufs.as_ptr() as *mut _,
- bufs.len().min(DWORD::MAX as usize) as DWORD,
+ bufs.len().min(u32::MAX as usize) as u32,
&mut nsent,
- flags as DWORD,
+ flags as u32,
addr.as_ptr(),
addr.len(),
ptr::null_mut(),
None,
),
PartialEq::eq,
- sock::SOCKET_ERROR
+ SOCKET_ERROR
+ )
+ .map(|_| nsent as usize)
+}
+
+pub(crate) fn sendmsg(socket: Socket, msg: &MsgHdr<'_, '_, '_>, flags: c_int) -> io::Result<usize> {
+ let mut nsent = 0;
+ syscall!(
+ WSASendMsg(
+ socket,
+ &msg.inner,
+ flags as u32,
+ &mut nsent,
+ ptr::null_mut(),
+ None,
+ ),
+ PartialEq::eq,
+ SOCKET_ERROR
)
.map(|_| nsent as usize)
}
/// Wrapper around `getsockopt` to deal with platform specific timeouts.
-pub(crate) fn timeout_opt(fd: Socket, lvl: c_int, name: c_int) -> io::Result<Option<Duration>> {
+pub(crate) fn timeout_opt(fd: Socket, lvl: c_int, name: i32) -> io::Result<Option<Duration>> {
unsafe { getsockopt(fd, lvl, name).map(from_ms) }
}
-fn from_ms(duration: DWORD) -> Option<Duration> {
+fn from_ms(duration: u32) -> Option<Duration> {
if duration == 0 {
None
} else {
@@ -619,16 +699,16 @@ fn from_ms(duration: DWORD) -> Option<Duration> {
/// Wrapper around `setsockopt` to deal with platform specific timeouts.
pub(crate) fn set_timeout_opt(
- fd: Socket,
+ socket: Socket,
level: c_int,
- optname: c_int,
+ optname: i32,
duration: Option<Duration>,
) -> io::Result<()> {
let duration = into_ms(duration);
- unsafe { setsockopt(fd, level, optname, duration) }
+ unsafe { setsockopt(socket, level, optname, duration) }
}
-fn into_ms(duration: Option<Duration>) -> DWORD {
+fn into_ms(duration: Option<Duration>) -> u32 {
// Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the
// timeouts in windows APIs are typically u32 milliseconds. To translate, we
// have two pieces to take care of:
@@ -636,9 +716,9 @@ fn into_ms(duration: Option<Duration>) -> DWORD {
// * Nanosecond precision is rounded up
// * Greater than u32::MAX milliseconds (50 days) is rounded up to
// INFINITE (never time out).
- duration
- .map(|duration| min(duration.as_millis(), INFINITE as u128) as DWORD)
- .unwrap_or(0)
+ duration.map_or(0, |duration| {
+ min(duration.as_millis(), INFINITE as u128) as u32
+ })
}
pub(crate) fn set_tcp_keepalive(socket: Socket, keepalive: &TcpKeepalive) -> io::Result<()> {
@@ -661,25 +741,26 @@ pub(crate) fn set_tcp_keepalive(socket: Socket, keepalive: &TcpKeepalive) -> io:
None,
),
PartialEq::eq,
- sock::SOCKET_ERROR
+ SOCKET_ERROR
)
.map(|_| ())
}
/// Caller must ensure `T` is the correct type for `level` and `optname`.
-pub(crate) unsafe fn getsockopt<T>(socket: Socket, level: c_int, optname: c_int) -> io::Result<T> {
+// NOTE: `optname` is actually `i32`, but all constants are `u32`.
+pub(crate) unsafe fn getsockopt<T>(socket: Socket, level: c_int, optname: i32) -> io::Result<T> {
let mut optval: MaybeUninit<T> = MaybeUninit::uninit();
let mut optlen = mem::size_of::<T>() as c_int;
syscall!(
getsockopt(
socket,
- level,
+ level as i32,
optname,
optval.as_mut_ptr().cast(),
&mut optlen,
),
PartialEq::eq,
- sock::SOCKET_ERROR
+ SOCKET_ERROR
)
.map(|_| {
debug_assert_eq!(optlen as usize, mem::size_of::<T>());
@@ -689,57 +770,61 @@ pub(crate) unsafe fn getsockopt<T>(socket: Socket, level: c_int, optname: c_int)
}
/// Caller must ensure `T` is the correct type for `level` and `optname`.
+// NOTE: `optname` is actually `i32`, but all constants are `u32`.
pub(crate) unsafe fn setsockopt<T>(
socket: Socket,
level: c_int,
- optname: c_int,
+ optname: i32,
optval: T,
) -> io::Result<()> {
syscall!(
setsockopt(
socket,
- level,
+ level as i32,
optname,
(&optval as *const T).cast(),
mem::size_of::<T>() as c_int,
),
PartialEq::eq,
- sock::SOCKET_ERROR
+ SOCKET_ERROR
)
.map(|_| ())
}
-fn ioctlsocket(socket: Socket, cmd: c_long, payload: &mut u_long) -> io::Result<()> {
+fn ioctlsocket(socket: Socket, cmd: i32, payload: &mut u32) -> io::Result<()> {
syscall!(
ioctlsocket(socket, cmd, payload),
PartialEq::eq,
- sock::SOCKET_ERROR
+ SOCKET_ERROR
)
.map(|_| ())
}
pub(crate) fn to_in_addr(addr: &Ipv4Addr) -> IN_ADDR {
- let mut s_un: in_addr_S_un = unsafe { mem::zeroed() };
- // `S_un` is stored as BE on all machines, and the array is in BE order. So
- // the native endian conversion method is used so that it's never swapped.
- unsafe { *(s_un.S_addr_mut()) = u32::from_ne_bytes(addr.octets()) };
- IN_ADDR { S_un: s_un }
+ IN_ADDR {
+ S_un: IN_ADDR_0 {
+ // `S_un` is stored as BE on all machines, and the array is in BE
+ // order. So the native endian conversion method is used so that
+ // it's never swapped.
+ S_addr: u32::from_ne_bytes(addr.octets()),
+ },
+ }
}
pub(crate) fn from_in_addr(in_addr: IN_ADDR) -> Ipv4Addr {
- Ipv4Addr::from(unsafe { *in_addr.S_un.S_addr() }.to_ne_bytes())
+ Ipv4Addr::from(unsafe { in_addr.S_un.S_addr }.to_ne_bytes())
}
-pub(crate) fn to_in6_addr(addr: &Ipv6Addr) -> in6_addr {
- let mut ret_addr: in6_addr_u = unsafe { mem::zeroed() };
- unsafe { *(ret_addr.Byte_mut()) = addr.octets() };
- let mut ret: in6_addr = unsafe { mem::zeroed() };
- ret.u = ret_addr;
- ret
+pub(crate) fn to_in6_addr(addr: &Ipv6Addr) -> IN6_ADDR {
+ IN6_ADDR {
+ u: IN6_ADDR_0 {
+ Byte: addr.octets(),
+ },
+ }
}
-pub(crate) fn from_in6_addr(addr: in6_addr) -> Ipv6Addr {
- Ipv6Addr::from(*unsafe { addr.u.Byte() })
+pub(crate) fn from_in6_addr(addr: IN6_ADDR) -> Ipv6Addr {
+ Ipv6Addr::from(unsafe { addr.u.Byte })
}
pub(crate) fn to_mreqn(
@@ -768,6 +853,49 @@ pub(crate) fn to_mreqn(
}
}
+#[allow(unsafe_op_in_unsafe_fn)]
+pub(crate) fn unix_sockaddr(path: &Path) -> io::Result<SockAddr> {
+ // SAFETY: a `sockaddr_storage` of all zeros is valid.
+ let mut storage = unsafe { mem::zeroed::<sockaddr_storage>() };
+ let len = {
+ let storage: &mut windows_sys::Win32::Networking::WinSock::SOCKADDR_UN =
+ unsafe { &mut *(&mut storage as *mut sockaddr_storage).cast() };
+
+ // Windows expects a UTF-8 path here even though Windows paths are
+ // usually UCS-2 encoded. If Rust exposed OsStr's Wtf8 encoded
+ // buffer, this could be used directly, relying on Windows to
+ // validate the path, but Rust hides this implementation detail.
+ //
+ // See <https://github.com/rust-lang/rust/pull/95290>.
+ let bytes = path
+ .to_str()
+ .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "path must be valid UTF-8"))?
+ .as_bytes();
+
+ // Windows appears to allow non-null-terminated paths, but this is
+ // not documented, so do not rely on it yet.
+ //
+ // See <https://github.com/rust-lang/socket2/issues/331>.
+ if bytes.len() >= storage.sun_path.len() {
+ return Err(io::Error::new(
+ io::ErrorKind::InvalidInput,
+ "path must be shorter than SUN_LEN",
+ ));
+ }
+
+ storage.sun_family = crate::sys::AF_UNIX as sa_family_t;
+ // `storage` was initialized to zero above, so the path is
+ // already null terminated.
+ storage.sun_path[..bytes.len()].copy_from_slice(bytes);
+
+ let base = storage as *const _ as usize;
+ let path = &storage.sun_path as *const _ as usize;
+ let sun_path_offset = path - base;
+ sun_path_offset + bytes.len() + 1
+ };
+ Ok(unsafe { SockAddr::new(storage, len as socklen_t) })
+}
+
/// Windows only API.
impl crate::Socket {
/// Sets `HANDLE_FLAG_INHERIT` using `SetHandleInformation`.
@@ -779,11 +907,11 @@ impl crate::Socket {
pub(crate) fn _set_no_inherit(&self, no_inherit: bool) -> io::Result<()> {
// NOTE: can't use `syscall!` because it expects the function in the
- // `sock::` path.
+ // `windows_sys::Win32::Networking::WinSock::` path.
let res = unsafe {
SetHandleInformation(
self.as_raw() as HANDLE,
- winbase::HANDLE_FLAG_INHERIT,
+ HANDLE_FLAG_INHERIT,
!no_inherit as _,
)
};
@@ -794,20 +922,62 @@ impl crate::Socket {
Ok(())
}
}
+
+ /// Returns the [`Protocol`] of this socket by checking the `SO_PROTOCOL_INFOW`
+ /// option on this socket.
+ ///
+ /// [`Protocol`]: crate::Protocol
+ #[cfg(feature = "all")]
+ pub fn protocol(&self) -> io::Result<Option<crate::Protocol>> {
+ let info = unsafe {
+ getsockopt::<WSAPROTOCOL_INFOW>(self.as_raw(), SOL_SOCKET, SO_PROTOCOL_INFOW)?
+ };
+ match info.iProtocol {
+ 0 => Ok(None),
+ p => Ok(Some(crate::Protocol::from(p))),
+ }
+ }
}
+#[cfg_attr(docsrs, doc(cfg(windows)))]
+impl AsSocket for crate::Socket {
+ fn as_socket(&self) -> BorrowedSocket<'_> {
+ // SAFETY: lifetime is bound by self.
+ unsafe { BorrowedSocket::borrow_raw(self.as_raw() as RawSocket) }
+ }
+}
+
+#[cfg_attr(docsrs, doc(cfg(windows)))]
impl AsRawSocket for crate::Socket {
fn as_raw_socket(&self) -> RawSocket {
self.as_raw() as RawSocket
}
}
+#[cfg_attr(docsrs, doc(cfg(windows)))]
+impl From<crate::Socket> for OwnedSocket {
+ fn from(sock: crate::Socket) -> OwnedSocket {
+ // SAFETY: sock.into_raw() always returns a valid fd.
+ unsafe { OwnedSocket::from_raw_socket(sock.into_raw() as RawSocket) }
+ }
+}
+
+#[cfg_attr(docsrs, doc(cfg(windows)))]
impl IntoRawSocket for crate::Socket {
fn into_raw_socket(self) -> RawSocket {
self.into_raw() as RawSocket
}
}
+#[cfg_attr(docsrs, doc(cfg(windows)))]
+impl From<OwnedSocket> for crate::Socket {
+ fn from(fd: OwnedSocket) -> crate::Socket {
+ // SAFETY: `OwnedFd` ensures the fd is valid.
+ unsafe { crate::Socket::from_raw_socket(fd.into_raw_socket()) }
+ }
+}
+
+#[cfg_attr(docsrs, doc(cfg(windows)))]
impl FromRawSocket for crate::Socket {
unsafe fn from_raw_socket(socket: RawSocket) -> crate::Socket {
crate::Socket::from_raw(socket as Socket)
@@ -818,13 +988,13 @@ impl FromRawSocket for crate::Socket {
fn in_addr_convertion() {
let ip = Ipv4Addr::new(127, 0, 0, 1);
let raw = to_in_addr(&ip);
- assert_eq!(unsafe { *raw.S_un.S_addr() }, 127 << 0 | 1 << 24);
+ assert_eq!(unsafe { raw.S_un.S_addr }, 127 << 0 | 1 << 24);
assert_eq!(from_in_addr(raw), ip);
let ip = Ipv4Addr::new(127, 34, 4, 12);
let raw = to_in_addr(&ip);
assert_eq!(
- unsafe { *raw.S_un.S_addr() },
+ unsafe { raw.S_un.S_addr },
127 << 0 | 34 << 8 | 4 << 16 | 12 << 24
);
assert_eq!(from_in_addr(raw), ip);
@@ -844,6 +1014,6 @@ fn in6_addr_convertion() {
6u16.to_be(),
7u16.to_be(),
];
- assert_eq!(unsafe { *raw.u.Word() }, want);
+ assert_eq!(unsafe { raw.u.Word }, want);
assert_eq!(from_in6_addr(raw), ip);
}