aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Wailes <chriswailes@google.com>2023-01-18 16:50:43 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-01-18 16:50:43 +0000
commitab84aca097b86fbf52e7a60fe764e7aefa740d27 (patch)
tree6e48d468b8dd2789fa8555152570d6e1274c8ec9
parent0ac472e6c673320591e2845cda81405f3703583f (diff)
parent6e3f4b48ba69adc85bc246c6b495dbc62f17b123 (diff)
downloadsocket2-ab84aca097b86fbf52e7a60fe764e7aefa740d27.tar.gz
Import socket2 0.4.4 am: beba3e6188 am: 9fb7ef254d am: 6e3f4b48ba
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/socket2/+/2377156 Change-Id: Iea52674971c7a5769a5e4428320a70b55f03946d Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--.cargo_vcs_info.json6
-rw-r--r--Android.bp27
-rw-r--r--Cargo.toml39
-rw-r--r--Cargo.toml.orig43
l---------LICENSE1
-rw-r--r--LICENSE-APACHE201
-rw-r--r--LICENSE-MIT25
-rw-r--r--METADATA20
-rw-r--r--MODULE_LICENSE_APACHE20
-rw-r--r--OWNERS1
-rw-r--r--README.md84
-rw-r--r--cargo.out20
-rw-r--r--cargo2android.json13
-rw-r--r--src/lib.rs442
-rw-r--r--src/sockaddr.rs348
-rw-r--r--src/socket.rs1781
-rw-r--r--src/sockref.rs147
-rw-r--r--src/sys/unix.rs2053
-rw-r--r--src/sys/windows.rs848
19 files changed, 6099 insertions, 0 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..b0a8014
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+ "git": {
+ "sha1": "ac391b27fe7ab6740a61b8d52c2920ee383d5817"
+ },
+ "path_in_vcs": ""
+} \ No newline at end of file
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..a39ddf5
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,27 @@
+// This file is generated by cargo2android.py --config cargo2android.json.
+// Do not modify this file as changes will be overridden on upgrade.
+
+
+
+rust_library {
+ name: "libsocket2",
+ // has rustc warnings
+ host_supported: true,
+ crate_name: "socket2",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.4.4",
+ srcs: ["src/lib.rs"],
+ edition: "2018",
+ features: ["all"],
+ rustlibs: [
+ "liblibc",
+ ],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.btservices",
+ "com.android.resolv",
+ "com.android.uwb",
+ ],
+ vendor_available: true,
+ min_sdk_version: "29",
+}
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..8fd3884
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,39 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+name = "socket2"
+version = "0.4.4"
+authors = ["Alex Crichton <alex@alexcrichton.com>", "Thomas de Zeeuw <thomasdezeeuw@gmail.com>"]
+include = ["Cargo.toml", "LICENSE-APACHE", "LICENSE-MIT", "README.md", "src/**/*.rs"]
+description = "Utilities for handling networking sockets with a maximal amount of configuration\npossible intended.\n"
+homepage = "https://github.com/rust-lang/socket2"
+documentation = "https://docs.rs/socket2"
+readme = "README.md"
+keywords = ["io", "socket", "network"]
+categories = ["api-bindings", "network-programming"]
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/rust-lang/socket2"
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
+
+[package.metadata.playground]
+features = ["all"]
+
+[features]
+all = []
+[target."cfg(unix)".dependencies.libc]
+version = "0.2.114"
+[target."cfg(windows)".dependencies.winapi]
+version = "0.3.9"
+features = ["handleapi", "ws2ipdef", "ws2tcpip"]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..6ca3422
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,43 @@
+[package]
+name = "socket2"
+version = "0.4.4"
+authors = [
+ "Alex Crichton <alex@alexcrichton.com>",
+ "Thomas de Zeeuw <thomasdezeeuw@gmail.com>"
+]
+license = "MIT OR Apache-2.0"
+readme = "README.md"
+repository = "https://github.com/rust-lang/socket2"
+homepage = "https://github.com/rust-lang/socket2"
+documentation = "https://docs.rs/socket2"
+description = """
+Utilities for handling networking sockets with a maximal amount of configuration
+possible intended.
+"""
+keywords = ["io", "socket", "network"]
+categories = ["api-bindings", "network-programming"]
+edition = "2018"
+include = [
+ "Cargo.toml",
+ "LICENSE-APACHE",
+ "LICENSE-MIT",
+ "README.md",
+ "src/**/*.rs",
+]
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
+
+[package.metadata.playground]
+features = ["all"]
+
+[target."cfg(unix)".dependencies]
+libc = "0.2.114"
+
+[target."cfg(windows)".dependencies]
+winapi = { version = "0.3.9", features = ["handleapi", "ws2ipdef", "ws2tcpip"] }
+
+[features]
+# Enable all API, even ones not available on all OSs.
+all = []
diff --git a/LICENSE b/LICENSE
new file mode 120000
index 0000000..6b579aa
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1 @@
+LICENSE-APACHE \ No newline at end of file
diff --git a/LICENSE-APACHE b/LICENSE-APACHE
new file mode 100644
index 0000000..16fe87b
--- /dev/null
+++ b/LICENSE-APACHE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/LICENSE-MIT b/LICENSE-MIT
new file mode 100644
index 0000000..39e0ed6
--- /dev/null
+++ b/LICENSE-MIT
@@ -0,0 +1,25 @@
+Copyright (c) 2014 Alex Crichton
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..7c214be
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,20 @@
+name: "socket2"
+description: "Utilities for handling networking sockets with a maximal amount of configuration possible intended."
+third_party {
+ url {
+ type: HOMEPAGE
+ value: "https://crates.io/crates/socket2"
+ }
+ url {
+ type: ARCHIVE
+ value: "https://static.crates.io/crates/socket2/socket2-0.4.4.crate"
+ }
+ version: "0.4.4"
+ # Dual-licensed, using the least restrictive per go/thirdpartylicenses#same.
+ license_type: NOTICE
+ last_upgrade_date {
+ year: 2022
+ month: 12
+ day: 12
+ }
+}
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..45dc4dd
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1 @@
+include platform/prebuilts/rust:master:/OWNERS
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..45f4cf8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,84 @@
+# Socket2
+
+Socket2 is a crate that provides utilities for creating and using sockets.
+
+The goal of this crate is to create and use a socket using advanced
+configuration options (those that are not available in the types in the standard
+library) without using any unsafe code.
+
+This crate provides as direct as possible access to the system's functionality
+for sockets, this means little effort to provide cross-platform utilities. It is
+up to the user to know how to use sockets when using this crate. *If you don't
+know how to create a socket using libc/system calls then this crate is not for
+you*. Most, if not all, functions directly relate to the equivalent system call
+with no error handling applied, so no handling errors such as `EINTR`. As a
+result using this crate can be a little wordy, but it should give you maximal
+flexibility over configuration of sockets.
+
+See the [API documentation] for more.
+
+[API documentation]: https://docs.rs/socket2
+
+# Two 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].
+
+[v0.3.x branch]: https://github.com/rust-lang/socket2/tree/v0.3.x
+
+# OS support
+
+Socket2 attempts to support the same OS/architectures as Rust does, see
+https://doc.rust-lang.org/nightly/rustc/platform-support.html. However this is
+not always possible, below is current list of support OSs.
+
+*If your favorite OS is not on the list consider contributing it! See [issue
+#78].*
+
+[issue #78]: https://github.com/rust-lang/socket2/issues/78
+
+### Tier 1
+
+These OSs are tested with each commit in the CI and must always pass the tests.
+All functions/types/etc., excluding ones behind the `all` feature, must work on
+these OSs.
+
+* Linux
+* macOS
+* Windows
+
+### Tier 2
+
+These OSs are currently build in the CI, but not tested. Not all
+functions/types/etc. may work on these OSs, even ones **not** behind the `all`
+feature flag.
+
+* Android
+* FreeBSD
+* Fuchsia
+* iOS
+* illumos
+* NetBSD
+* Redox
+* Solaris
+
+# Minimum Supported Rust Version (MSRV)
+
+Socket2 uses 1.46.0 as MSRV.
+
+# License
+
+This project is licensed under either of
+
+ * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
+ http://www.apache.org/licenses/LICENSE-2.0)
+ * MIT license ([LICENSE-MIT](LICENSE-MIT) or
+ http://opensource.org/licenses/MIT)
+
+at your option.
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in this project by you, as defined in the Apache-2.0 license,
+shall be dual licensed as above, without any additional terms or conditions.
diff --git a/cargo.out b/cargo.out
new file mode 100644
index 0000000..cec2d0b
--- /dev/null
+++ b/cargo.out
@@ -0,0 +1,20 @@
+### Running: /usr/local/google/home/chriswailes/projects/android/aosp-plus-rust/development/scripts/../../prebuilts/rust/linux-x86/1.64.0/bin/cargo -v clean --target-dir target.tmp >> ./cargo.out 2>&1
+ Removing /usr/local/google/home/chriswailes/projects/android/aosp-plus-rust/external/rust/crates/socket2/target.tmp
+### Running: /usr/local/google/home/chriswailes/projects/android/aosp-plus-rust/development/scripts/../../prebuilts/rust/linux-x86/1.64.0/bin/cargo -v build --target x86_64-unknown-linux-gnu --no-default-features --features all --target-dir target.tmp >> ./cargo.out 2>&1
+ Updating crates.io index
+ Compiling libc v0.2.138
+ Running `rustc --crate-name build_script_build /usr/local/google/home/chriswailes/.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.138/build.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --crate-type bin --emit=dep-info,link -C embed-bitcode=no -C debuginfo=2 --cfg 'feature="default"' --cfg 'feature="std"' -C metadata=a91c13d3321fe99a -C extra-filename=-a91c13d3321fe99a --out-dir /usr/local/google/home/chriswailes/projects/android/aosp-plus-rust/external/rust/crates/socket2/target.tmp/debug/build/libc-a91c13d3321fe99a -L dependency=/usr/local/google/home/chriswailes/projects/android/aosp-plus-rust/external/rust/crates/socket2/target.tmp/debug/deps --cap-lints allow`
+ Running `/usr/local/google/home/chriswailes/projects/android/aosp-plus-rust/external/rust/crates/socket2/target.tmp/debug/build/libc-a91c13d3321fe99a/build-script-build`
+ Running `rustc --crate-name libc /usr/local/google/home/chriswailes/.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.138/src/lib.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --crate-type lib --emit=dep-info,metadata,link -C embed-bitcode=no -C debuginfo=2 --cfg 'feature="default"' --cfg 'feature="std"' -C metadata=c27f6107a08ccd2c -C extra-filename=-c27f6107a08ccd2c --out-dir /usr/local/google/home/chriswailes/projects/android/aosp-plus-rust/external/rust/crates/socket2/target.tmp/x86_64-unknown-linux-gnu/debug/deps --target x86_64-unknown-linux-gnu -L dependency=/usr/local/google/home/chriswailes/projects/android/aosp-plus-rust/external/rust/crates/socket2/target.tmp/x86_64-unknown-linux-gnu/debug/deps -L dependency=/usr/local/google/home/chriswailes/projects/android/aosp-plus-rust/external/rust/crates/socket2/target.tmp/debug/deps --cap-lints allow --cfg freebsd11 --cfg libc_priv_mod_use --cfg libc_union --cfg libc_const_size_of --cfg libc_align --cfg libc_int128 --cfg libc_core_cvoid --cfg libc_packedN --cfg libc_cfg_target_vendor --cfg libc_non_exhaustive --cfg libc_ptr_addr_of --cfg libc_underscore_const_names --cfg libc_const_extern_fn`
+ Compiling socket2 v0.4.4 (/usr/local/google/home/chriswailes/projects/android/aosp-plus-rust/external/rust/crates/socket2)
+ Running `rustc --crate-name socket2 --edition=2018 src/lib.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --crate-type lib --emit=dep-info,metadata,link -C embed-bitcode=no -C debuginfo=2 --cfg 'feature="all"' -C metadata=96fa3d53ddb1d7a8 -C extra-filename=-96fa3d53ddb1d7a8 --out-dir /usr/local/google/home/chriswailes/projects/android/aosp-plus-rust/external/rust/crates/socket2/target.tmp/x86_64-unknown-linux-gnu/debug/deps --target x86_64-unknown-linux-gnu -C incremental=/usr/local/google/home/chriswailes/projects/android/aosp-plus-rust/external/rust/crates/socket2/target.tmp/x86_64-unknown-linux-gnu/debug/incremental -L dependency=/usr/local/google/home/chriswailes/projects/android/aosp-plus-rust/external/rust/crates/socket2/target.tmp/x86_64-unknown-linux-gnu/debug/deps -L dependency=/usr/local/google/home/chriswailes/projects/android/aosp-plus-rust/external/rust/crates/socket2/target.tmp/debug/deps --extern libc=/usr/local/google/home/chriswailes/projects/android/aosp-plus-rust/external/rust/crates/socket2/target.tmp/x86_64-unknown-linux-gnu/debug/deps/liblibc-c27f6107a08ccd2c.rmeta`
+warning: unnecessary `unsafe` block
+ --> src/sys/unix.rs:1486:9
+ |
+1486 | unsafe {
+ | ^^^^^^ unnecessary `unsafe` block
+ |
+ = note: `#[warn(unused_unsafe)]` on by default
+
+warning: `socket2` (lib) generated 1 warning
+ Finished dev [unoptimized + debuginfo] target(s) in 2.18s
diff --git a/cargo2android.json b/cargo2android.json
new file mode 100644
index 0000000..5488125
--- /dev/null
+++ b/cargo2android.json
@@ -0,0 +1,13 @@
+{
+ "apex-available": [
+ "//apex_available:platform",
+ "com.android.btservices",
+ "com.android.resolv",
+ "com.android.uwb"
+ ],
+ "device": true,
+ "features": "all",
+ "min-sdk-version": "29",
+ "vendor-available": true,
+ "run": true
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..d01b652
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,442 @@
+// Copyright 2015 The Rust Project Developers.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Utilities for creating and using sockets.
+//!
+//! The goal of this crate is to create and use a socket using advanced
+//! configuration options (those that are not available in the types in the
+//! standard library) without using any unsafe code.
+//!
+//! This crate provides as direct as possible access to the system's
+//! functionality for sockets, this means little effort to provide
+//! cross-platform utilities. It is up to the user to know how to use sockets
+//! when using this crate. *If you don't know how to create a socket using
+//! libc/system calls then this crate is not for you*. Most, if not all,
+//! functions directly relate to the equivalent system call with no error
+//! handling applied, so no handling errors such as [`EINTR`]. As a result using
+//! this crate can be a little wordy, but it should give you maximal flexibility
+//! over configuration of sockets.
+//!
+//! [`EINTR`]: std::io::ErrorKind::Interrupted
+//!
+//! # Examples
+//!
+//! ```no_run
+//! # fn main() -> std::io::Result<()> {
+//! use std::net::{SocketAddr, TcpListener};
+//! use socket2::{Socket, Domain, Type};
+//!
+//! // Create a TCP listener bound to two addresses.
+//! let socket = Socket::new(Domain::IPV6, Type::STREAM, None)?;
+//!
+//! socket.set_only_v6(false)?;
+//! let address: SocketAddr = "[::1]:12345".parse().unwrap();
+//! socket.bind(&address.into())?;
+//! socket.listen(128)?;
+//!
+//! let listener: TcpListener = socket.into();
+//! // ...
+//! # drop(listener);
+//! # Ok(()) }
+//! ```
+//!
+//! ## Features
+//!
+//! 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.3")]
+#![deny(missing_docs, missing_debug_implementations, rust_2018_idioms)]
+// Show required OS/features on docs.rs.
+#![cfg_attr(docsrs, feature(doc_cfg))]
+// Disallow warnings when running tests.
+#![cfg_attr(test, deny(warnings))]
+// Disallow warnings in examples.
+#![doc(test(attr(deny(warnings))))]
+
+use std::fmt;
+use std::mem::MaybeUninit;
+use std::net::SocketAddr;
+use std::ops::{Deref, DerefMut};
+use std::time::Duration;
+
+/// Macro to implement `fmt::Debug` for a type, printing the constant names
+/// rather than a number.
+///
+/// Note this is used in the `sys` module and thus must be defined before
+/// defining the modules.
+macro_rules! impl_debug {
+ (
+ // Type name for which to implement `fmt::Debug`.
+ $type: path,
+ $(
+ $(#[$target: meta])*
+ // The flag(s) to check.
+ // Need to specific the libc crate because Windows doesn't use
+ // `libc` but `winapi`.
+ $libc: ident :: $flag: ident
+ ),+ $(,)*
+ ) => {
+ impl std::fmt::Debug for $type {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let string = match self.0 {
+ $(
+ $(#[$target])*
+ $libc :: $flag => stringify!($flag),
+ )+
+ n => return write!(f, "{}", n),
+ };
+ f.write_str(string)
+ }
+ }
+ };
+}
+
+/// Macro to convert from one network type to another.
+macro_rules! from {
+ ($from: ty, $for: ty) => {
+ impl From<$from> for $for {
+ fn from(socket: $from) -> $for {
+ #[cfg(unix)]
+ unsafe {
+ <$for>::from_raw_fd(socket.into_raw_fd())
+ }
+ #[cfg(windows)]
+ unsafe {
+ <$for>::from_raw_socket(socket.into_raw_socket())
+ }
+ }
+ }
+ };
+}
+
+mod sockaddr;
+mod socket;
+mod sockref;
+
+#[cfg_attr(unix, path = "sys/unix.rs")]
+#[cfg_attr(windows, path = "sys/windows.rs")]
+mod sys;
+
+#[cfg(not(any(windows, unix)))]
+compile_error!("Socket2 doesn't support the compile target");
+
+use sys::c_int;
+
+pub use sockaddr::SockAddr;
+pub use socket::Socket;
+pub use sockref::SockRef;
+
+#[cfg(not(any(
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "redox",
+ target_os = "solaris",
+)))]
+pub use socket::InterfaceIndexOrAddress;
+
+/// Specification of the communication domain for a socket.
+///
+/// This is a newtype wrapper around an integer which provides a nicer API in
+/// addition to an injection point for documentation. Convenience constants such
+/// as [`Domain::IPV4`], [`Domain::IPV6`], etc, are provided to avoid reaching
+/// into libc for various constants.
+///
+/// This type is freely interconvertible with C's `int` type, however, if a raw
+/// value needs to be provided.
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub struct Domain(c_int);
+
+impl Domain {
+ /// Domain for IPv4 communication, corresponding to `AF_INET`.
+ pub const IPV4: Domain = Domain(sys::AF_INET);
+
+ /// Domain for IPv6 communication, corresponding to `AF_INET6`.
+ pub const IPV6: Domain = Domain(sys::AF_INET6);
+
+ /// Returns the correct domain for `address`.
+ pub const fn for_address(address: SocketAddr) -> Domain {
+ match address {
+ SocketAddr::V4(_) => Domain::IPV4,
+ SocketAddr::V6(_) => Domain::IPV6,
+ }
+ }
+}
+
+impl From<c_int> for Domain {
+ fn from(d: c_int) -> Domain {
+ Domain(d)
+ }
+}
+
+impl From<Domain> for c_int {
+ fn from(d: Domain) -> c_int {
+ d.0
+ }
+}
+
+/// Specification of communication semantics on a socket.
+///
+/// This is a newtype wrapper around an integer which provides a nicer API in
+/// addition to an injection point for documentation. Convenience constants such
+/// as [`Type::STREAM`], [`Type::DGRAM`], etc, are provided to avoid reaching
+/// into libc for various constants.
+///
+/// This type is freely interconvertible with C's `int` type, however, if a raw
+/// value needs to be provided.
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub struct Type(c_int);
+
+impl Type {
+ /// Type corresponding to `SOCK_STREAM`.
+ ///
+ /// Used for protocols such as TCP.
+ pub const STREAM: Type = Type(sys::SOCK_STREAM);
+
+ /// Type corresponding to `SOCK_DGRAM`.
+ ///
+ /// Used for protocols such as UDP.
+ pub const DGRAM: Type = Type(sys::SOCK_DGRAM);
+
+ /// Type corresponding to `SOCK_SEQPACKET`.
+ #[cfg(feature = "all")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "all")))]
+ 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")))))]
+ pub const RAW: Type = Type(sys::SOCK_RAW);
+}
+
+impl From<c_int> for Type {
+ fn from(t: c_int) -> Type {
+ Type(t)
+ }
+}
+
+impl From<Type> for c_int {
+ fn from(t: Type) -> c_int {
+ t.0
+ }
+}
+
+/// Protocol specification used for creating sockets via `Socket::new`.
+///
+/// This is a newtype wrapper around an integer which provides a nicer API in
+/// addition to an injection point for documentation.
+///
+/// This type is freely interconvertible with C's `int` type, however, if a raw
+/// value needs to be provided.
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub struct Protocol(c_int);
+
+impl Protocol {
+ /// Protocol corresponding to `ICMPv4`.
+ pub const ICMPV4: Protocol = Protocol(sys::IPPROTO_ICMP);
+
+ /// Protocol corresponding to `ICMPv6`.
+ pub const ICMPV6: Protocol = Protocol(sys::IPPROTO_ICMPV6);
+
+ /// Protocol corresponding to `TCP`.
+ pub const TCP: Protocol = Protocol(sys::IPPROTO_TCP);
+
+ /// Protocol corresponding to `UDP`.
+ pub const UDP: Protocol = Protocol(sys::IPPROTO_UDP);
+}
+
+impl From<c_int> for Protocol {
+ fn from(p: c_int) -> Protocol {
+ Protocol(p)
+ }
+}
+
+impl From<Protocol> for c_int {
+ fn from(p: Protocol) -> c_int {
+ p.0
+ }
+}
+
+/// Flags for incoming messages.
+///
+/// Flags provide additional information about incoming messages.
+#[cfg(not(target_os = "redox"))]
+#[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))]
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub struct RecvFlags(c_int);
+
+#[cfg(not(target_os = "redox"))]
+impl RecvFlags {
+ /// Check if the message contains a truncated datagram.
+ ///
+ /// This flag is only used for datagram-based sockets,
+ /// not for stream sockets.
+ ///
+ /// On Unix this corresponds to the `MSG_TRUNC` flag.
+ /// On Windows this corresponds to the `WSAEMSGSIZE` error code.
+ pub const fn is_truncated(self) -> bool {
+ self.0 & sys::MSG_TRUNC != 0
+ }
+}
+
+/// A version of [`IoSliceMut`] that allows the buffer to be uninitialised.
+///
+/// [`IoSliceMut`]: std::io::IoSliceMut
+#[repr(transparent)]
+pub struct MaybeUninitSlice<'a>(sys::MaybeUninitSlice<'a>);
+
+impl<'a> fmt::Debug for MaybeUninitSlice<'a> {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Debug::fmt(self.0.as_slice(), fmt)
+ }
+}
+
+impl<'a> MaybeUninitSlice<'a> {
+ /// Creates a new `MaybeUninitSlice` wrapping a byte slice.
+ ///
+ /// # Panics
+ ///
+ /// Panics on Windows if the slice is larger than 4GB.
+ pub fn new(buf: &'a mut [MaybeUninit<u8>]) -> MaybeUninitSlice<'a> {
+ MaybeUninitSlice(sys::MaybeUninitSlice::new(buf))
+ }
+}
+
+impl<'a> Deref for MaybeUninitSlice<'a> {
+ type Target = [MaybeUninit<u8>];
+
+ fn deref(&self) -> &[MaybeUninit<u8>] {
+ self.0.as_slice()
+ }
+}
+
+impl<'a> DerefMut for MaybeUninitSlice<'a> {
+ fn deref_mut(&mut self) -> &mut [MaybeUninit<u8>] {
+ self.0.as_mut_slice()
+ }
+}
+
+/// Configures a socket's TCP keepalive parameters.
+///
+/// See [`Socket::set_tcp_keepalive`].
+#[derive(Debug, Clone)]
+pub struct TcpKeepalive {
+ time: Option<Duration>,
+ #[cfg(not(any(target_os = "redox", target_os = "solaris")))]
+ interval: Option<Duration>,
+ #[cfg(not(any(target_os = "redox", target_os = "solaris", target_os = "windows")))]
+ retries: Option<u32>,
+}
+
+impl TcpKeepalive {
+ /// Returns a new, empty set of TCP keepalive parameters.
+ pub const fn new() -> TcpKeepalive {
+ TcpKeepalive {
+ time: None,
+ #[cfg(not(any(target_os = "redox", target_os = "solaris")))]
+ interval: None,
+ #[cfg(not(any(target_os = "redox", target_os = "solaris", target_os = "windows")))]
+ retries: None,
+ }
+ }
+
+ /// Set the amount of time after which TCP keepalive probes will be sent on
+ /// idle connections.
+ ///
+ /// This will set `TCP_KEEPALIVE` on macOS and iOS, and
+ /// `TCP_KEEPIDLE` on all other Unix operating systems, except
+ /// OpenBSD and Haiku which don't support any way to set this
+ /// option. On Windows, this sets the value of the `tcp_keepalive`
+ /// struct's `keepalivetime` field.
+ ///
+ /// Some platforms specify this value in seconds, so sub-second
+ /// specifications may be omitted.
+ pub const fn with_time(self, time: Duration) -> Self {
+ Self {
+ time: Some(time),
+ ..self
+ }
+ }
+
+ /// Set the value of the `TCP_KEEPINTVL` option. On Windows, this sets the
+ /// value of the `tcp_keepalive` struct's `keepaliveinterval` field.
+ ///
+ /// Sets the time interval between TCP keepalive probes.
+ ///
+ /// Some platforms specify this value in seconds, so sub-second
+ /// specifications may be omitted.
+ #[cfg(all(
+ feature = "all",
+ any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_vendor = "apple",
+ windows,
+ )
+ ))]
+ #[cfg_attr(
+ docsrs,
+ doc(cfg(all(
+ feature = "all",
+ any(
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_vendor = "apple",
+ windows,
+ )
+ )))
+ )]
+ pub const fn with_interval(self, interval: Duration) -> Self {
+ Self {
+ interval: Some(interval),
+ ..self
+ }
+ }
+
+ /// Set the value of the `TCP_KEEPCNT` option.
+ ///
+ /// Set the maximum number of TCP keepalive probes that will be sent before
+ /// dropping a connection, if TCP keepalive is enabled on this socket.
+ #[cfg(all(
+ feature = "all",
+ any(
+ doc,
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_vendor = "apple",
+ )
+ ))]
+ #[cfg_attr(
+ docsrs,
+ doc(cfg(all(
+ feature = "all",
+ any(
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_vendor = "apple",
+ )
+ )))
+ )]
+ pub const fn with_retries(self, retries: u32) -> Self {
+ Self {
+ retries: Some(retries),
+ ..self
+ }
+ }
+}
diff --git a/src/sockaddr.rs b/src/sockaddr.rs
new file mode 100644
index 0000000..55951b6
--- /dev/null
+++ b/src/sockaddr.rs
@@ -0,0 +1,348 @@
+use std::mem::{self, size_of, MaybeUninit};
+use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
+use std::{fmt, io};
+
+use crate::sys::{
+ sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, sockaddr_storage, socklen_t, AF_INET,
+ AF_INET6,
+};
+#[cfg(windows)]
+use winapi::shared::ws2ipdef::SOCKADDR_IN6_LH_u;
+
+/// The address of a socket.
+///
+/// `SockAddr`s may be constructed directly to and from the standard library
+/// [`SocketAddr`], [`SocketAddrV4`], and [`SocketAddrV6`] types.
+pub struct SockAddr {
+ storage: sockaddr_storage,
+ len: socklen_t,
+}
+
+#[allow(clippy::len_without_is_empty)]
+impl SockAddr {
+ /// Create a `SockAddr` from the underlying storage and its length.
+ ///
+ /// # Safety
+ ///
+ /// Caller must ensure that the address family and length match the type of
+ /// storage address. For example if `storage.ss_family` is set to `AF_INET`
+ /// the `storage` must be initialised as `sockaddr_in`, setting the content
+ /// and length appropriately.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # fn main() -> std::io::Result<()> {
+ /// # #[cfg(unix)] {
+ /// use std::io;
+ /// use std::mem;
+ /// use std::os::unix::io::AsRawFd;
+ ///
+ /// use socket2::{SockAddr, Socket, Domain, Type};
+ ///
+ /// let socket = Socket::new(Domain::IPV4, Type::STREAM, None)?;
+ ///
+ /// // Initialise a `SocketAddr` byte calling `getsockname(2)`.
+ /// let mut addr_storage: libc::sockaddr_storage = unsafe { mem::zeroed() };
+ /// let mut len = mem::size_of_val(&addr_storage) as libc::socklen_t;
+ ///
+ /// // The `getsockname(2)` system call will intiliase `storage` for
+ /// // us, setting `len` to the correct length.
+ /// let res = unsafe {
+ /// libc::getsockname(
+ /// socket.as_raw_fd(),
+ /// (&mut addr_storage as *mut libc::sockaddr_storage).cast(),
+ /// &mut len,
+ /// )
+ /// };
+ /// if res == -1 {
+ /// return Err(io::Error::last_os_error());
+ /// }
+ ///
+ /// let address = unsafe { SockAddr::new(addr_storage, len) };
+ /// # drop(address);
+ /// # }
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub const unsafe fn new(storage: sockaddr_storage, len: socklen_t) -> SockAddr {
+ SockAddr { storage, len }
+ }
+
+ /// Initialise a `SockAddr` by calling the function `init`.
+ ///
+ /// The type of the address storage and length passed to the function `init`
+ /// is OS/architecture specific.
+ ///
+ /// The address is zeroed before `init` is called and is thus valid to
+ /// dereference and read from. The length initialised to the maximum length
+ /// of the storage.
+ ///
+ /// # Safety
+ ///
+ /// Caller must ensure that the address family and length match the type of
+ /// storage address. For example if `storage.ss_family` is set to `AF_INET`
+ /// the `storage` must be initialised as `sockaddr_in`, setting the content
+ /// and length appropriately.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # fn main() -> std::io::Result<()> {
+ /// # #[cfg(unix)] {
+ /// use std::io;
+ /// use std::os::unix::io::AsRawFd;
+ ///
+ /// use socket2::{SockAddr, Socket, Domain, Type};
+ ///
+ /// let socket = Socket::new(Domain::IPV4, Type::STREAM, None)?;
+ ///
+ /// // Initialise a `SocketAddr` byte calling `getsockname(2)`.
+ /// let (_, address) = unsafe {
+ /// SockAddr::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 {
+ /// Err(io::Error::last_os_error())
+ /// } else {
+ /// Ok(())
+ /// }
+ /// })
+ /// }?;
+ /// # drop(address);
+ /// # }
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub unsafe fn init<F, T>(init: F) -> io::Result<(T, SockAddr)>
+ where
+ F: FnOnce(*mut sockaddr_storage, *mut socklen_t) -> io::Result<T>,
+ {
+ const STORAGE_SIZE: socklen_t = size_of::<sockaddr_storage>() as socklen_t;
+ // NOTE: `SockAddr::unix` depends on the storage being zeroed before
+ // calling `init`.
+ // NOTE: calling `recvfrom` with an empty buffer also depends on the
+ // storage being zeroed before calling `init` as the OS might not
+ // initialise it.
+ let mut storage = MaybeUninit::<sockaddr_storage>::zeroed();
+ let mut len = STORAGE_SIZE;
+ init(storage.as_mut_ptr(), &mut len).map(|res| {
+ debug_assert!(len <= STORAGE_SIZE, "overflown address storage");
+ let addr = SockAddr {
+ // Safety: zeroed-out `sockaddr_storage` is valid, caller must
+ // ensure at least `len` bytes are valid.
+ storage: storage.assume_init(),
+ len,
+ };
+ (res, addr)
+ })
+ }
+
+ /// Returns this address's family.
+ pub const fn family(&self) -> sa_family_t {
+ self.storage.ss_family
+ }
+
+ /// Returns the size of this address in bytes.
+ pub const fn len(&self) -> socklen_t {
+ self.len
+ }
+
+ /// Returns a raw pointer to the address.
+ pub const fn as_ptr(&self) -> *const sockaddr {
+ &self.storage as *const _ as *const _
+ }
+
+ /// 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 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) };
+
+ 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) };
+
+ let ip = crate::sys::from_in6_addr(addr.sin6_addr);
+ let port = u16::from_be(addr.sin6_port);
+ Some(SocketAddr::V6(SocketAddrV6::new(
+ ip,
+ port,
+ addr.sin6_flowinfo,
+ #[cfg(unix)]
+ addr.sin6_scope_id,
+ #[cfg(windows)]
+ unsafe {
+ *addr.u.sin6_scope_id()
+ },
+ )))
+ } else {
+ None
+ }
+ }
+
+ /// Returns this address as a [`SocketAddrV4`] if it is in the `AF_INET`
+ /// family.
+ pub fn as_socket_ipv4(&self) -> Option<SocketAddrV4> {
+ match self.as_socket() {
+ Some(SocketAddr::V4(addr)) => Some(addr),
+ _ => None,
+ }
+ }
+
+ /// Returns this address as a [`SocketAddrV6`] if it is in the `AF_INET6`
+ /// family.
+ pub fn as_socket_ipv6(&self) -> Option<SocketAddrV6> {
+ match self.as_socket() {
+ Some(SocketAddr::V6(addr)) => Some(addr),
+ _ => None,
+ }
+ }
+}
+
+impl From<SocketAddr> for SockAddr {
+ fn from(addr: SocketAddr) -> SockAddr {
+ match addr {
+ SocketAddr::V4(addr) => addr.into(),
+ SocketAddr::V6(addr) => addr.into(),
+ }
+ }
+}
+
+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,
+ };
+ 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,
+ }
+ }
+}
+
+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(),
+ #[cfg(unix)]
+ 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,
+ };
+ 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,
+ }
+ }
+}
+
+impl fmt::Debug for SockAddr {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut f = fmt.debug_struct("SockAddr");
+ #[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 = "openbsd",
+ target_os = "vxworks",
+ ))]
+ f.field("ss_len", &self.storage.ss_len);
+ f.field("ss_family", &self.storage.ss_family)
+ .field("len", &self.len)
+ .finish()
+ }
+}
+
+#[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());
+}
+
+#[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));
+}
diff --git a/src/socket.rs b/src/socket.rs
new file mode 100644
index 0000000..028c4ae
--- /dev/null
+++ b/src/socket.rs
@@ -0,0 +1,1781 @@
+// Copyright 2015 The Rust Project Developers.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::fmt;
+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(unix)]
+use std::os::unix::io::{FromRawFd, IntoRawFd};
+#[cfg(windows)]
+use std::os::windows::io::{FromRawSocket, IntoRawSocket};
+use std::time::Duration;
+
+use crate::sys::{self, c_int, getsockopt, setsockopt, Bool};
+use crate::{Domain, Protocol, SockAddr, TcpKeepalive, Type};
+#[cfg(not(target_os = "redox"))]
+use crate::{MaybeUninitSlice, RecvFlags};
+
+/// Owned wrapper around a system socket.
+///
+/// This type simply wraps an instance of a file descriptor (`c_int`) on Unix
+/// and an instance of `SOCKET` on Windows. This is the main type exported by
+/// this crate and is intended to mirror the raw semantics of sockets on
+/// platforms as closely as possible. Almost all methods correspond to
+/// precisely one libc or OS API call which is essentially just a "Rustic
+/// translation" of what's below.
+///
+/// ## Converting to and from other types
+///
+/// This type can be freely converted into the network primitives provided by
+/// the standard library, such as [`TcpStream`] or [`UdpSocket`], using the
+/// [`From`] trait, see the example below.
+///
+/// [`TcpStream`]: std::net::TcpStream
+/// [`UdpSocket`]: std::net::UdpSocket
+///
+/// # 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
+/// 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.
+///
+/// # Examples
+/// ```no_run
+/// # fn main() -> std::io::Result<()> {
+/// 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)?;
+///
+/// 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();
+/// // ...
+/// # drop(listener);
+/// # Ok(()) }
+/// ```
+pub struct Socket {
+ inner: Inner,
+}
+
+/// Store a `TcpStream` internally to take advantage of its niche optimizations on Unix platforms.
+pub(crate) type Inner = std::net::TcpStream;
+
+impl Socket {
+ /// # Safety
+ ///
+ /// The caller must ensure `raw` is a valid file descriptor/socket. NOTE:
+ /// this should really be marked `unsafe`, but this being an internal
+ /// function, often passed as mapping function, it's makes it very
+ /// inconvenient to mark it as `unsafe`.
+ pub(crate) fn from_raw(raw: sys::Socket) -> Socket {
+ Socket {
+ inner: unsafe {
+ // SAFETY: the caller must ensure that `raw` is a valid file
+ // descriptor, but when it isn't it could return I/O errors, or
+ // potentially close a fd it doesn't own. All of that isn't
+ // memory unsafe, so it's not desired but never memory unsafe or
+ // causes UB.
+ //
+ // However there is one exception. We use `TcpStream` to
+ // represent the `Socket` internally (see `Inner` type),
+ // `TcpStream` has a layout optimisation that doesn't allow for
+ // negative file descriptors (as those are always invalid).
+ // Violating this assumption (fd never negative) causes UB,
+ // something we don't want. So check for that we have this
+ // `assert!`.
+ #[cfg(unix)]
+ assert!(raw >= 0, "tried to create a `Socket` with an invalid fd");
+ sys::socket_from_raw(raw)
+ },
+ }
+ }
+
+ pub(crate) fn as_raw(&self) -> sys::Socket {
+ sys::socket_as_raw(&self.inner)
+ }
+
+ pub(crate) fn into_raw(self) -> sys::Socket {
+ sys::socket_into_raw(self.inner)
+ }
+
+ /// Creates a new socket and sets common flags.
+ ///
+ /// This function corresponds to `socket(2)` on Unix and `WSASocketW` on
+ /// Windows.
+ ///
+ /// On Unix-like systems, the close-on-exec flag is set on the new socket.
+ /// Additionally, on Apple platforms `SOCK_NOSIGPIPE` is set. On Windows,
+ /// the socket is made non-inheritable.
+ ///
+ /// [`Socket::new_raw`] can be used if you don't want these flags to be set.
+ 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)
+ }
+
+ /// Creates a new socket ready to be configured.
+ ///
+ /// 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);
+ sys::socket(domain.0, ty.0, protocol).map(Socket::from_raw)
+ }
+
+ /// Creates a pair of sockets which are connected to each other.
+ ///
+ /// This function corresponds to `socketpair(2)`.
+ ///
+ /// 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)))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", unix))))]
+ pub fn pair(
+ domain: Domain,
+ ty: Type,
+ protocol: Option<Protocol>,
+ ) -> io::Result<(Socket, Socket)> {
+ let ty = set_common_type(ty);
+ let (a, b) = Socket::pair_raw(domain, ty, protocol)?;
+ let a = set_common_flags(a)?;
+ let b = set_common_flags(b)?;
+ Ok((a, b))
+ }
+
+ /// 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_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);
+ sys::socketpair(domain.0, ty.0, protocol)
+ .map(|[a, b]| (Socket::from_raw(a), Socket::from_raw(b)))
+ }
+
+ /// Binds this socket to the specified address.
+ ///
+ /// This function directly corresponds to the `bind(2)` function on Windows
+ /// and Unix.
+ pub fn bind(&self, address: &SockAddr) -> io::Result<()> {
+ sys::bind(self.as_raw(), address)
+ }
+
+ /// Initiate a connection on this socket to the specified address.
+ ///
+ /// This function directly corresponds to the `connect(2)` function on
+ /// Windows and Unix.
+ ///
+ /// An error will be returned if `listen` or `connect` has already been
+ /// called on this builder.
+ ///
+ /// # Notes
+ ///
+ /// When using a non-blocking connect (by setting the socket into
+ /// non-blocking mode before calling this function), socket option can't be
+ /// set *while connecting*. This will cause errors on Windows. Socket
+ /// options can be safely set before and after connecting the socket.
+ pub fn connect(&self, address: &SockAddr) -> io::Result<()> {
+ sys::connect(self.as_raw(), address)
+ }
+
+ /// Initiate a connection on this socket to the specified address, only
+ /// only waiting for a certain period of time for the connection to be
+ /// established.
+ ///
+ /// Unlike many other methods on `Socket`, this does *not* correspond to a
+ /// single C function. It sets the socket to nonblocking mode, connects via
+ /// connect(2), and then waits for the connection to complete with poll(2)
+ /// on Unix and select on Windows. When the connection is complete, the
+ /// socket is set back to blocking mode. On Unix, this will loop over
+ /// `EINTR` errors.
+ ///
+ /// # Warnings
+ ///
+ /// The non-blocking state of the socket is overridden by this function -
+ /// it will be returned in blocking mode on success, and in an indeterminate
+ /// state on failure.
+ ///
+ /// If the connection request times out, it may still be processing in the
+ /// background - a second call to `connect` or `connect_timeout` may fail.
+ pub fn connect_timeout(&self, addr: &SockAddr, timeout: Duration) -> io::Result<()> {
+ self.set_nonblocking(true)?;
+ let res = self.connect(addr);
+ self.set_nonblocking(false)?;
+
+ match res {
+ Ok(()) => return Ok(()),
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {}
+ #[cfg(unix)]
+ Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {}
+ Err(e) => return Err(e),
+ }
+
+ sys::poll_connect(self, timeout)
+ }
+
+ /// Mark a socket as ready to accept incoming connection requests using
+ /// [`Socket::accept()`].
+ ///
+ /// This function directly corresponds to the `listen(2)` function on
+ /// Windows and Unix.
+ ///
+ /// An error will be returned if `listen` or `connect` has already been
+ /// called on this builder.
+ pub fn listen(&self, backlog: c_int) -> io::Result<()> {
+ sys::listen(self.as_raw(), backlog)
+ }
+
+ /// Accept a new incoming connection from this listener.
+ ///
+ /// This function uses `accept4(2)` on platforms that support it and
+ /// `accept(2)` platforms that do not.
+ ///
+ /// 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.
+ pub fn accept(&self) -> io::Result<(Socket, SockAddr)> {
+ // Use `accept4` on platforms that support it.
+ #[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ return self._accept4(libc::SOCK_CLOEXEC);
+
+ // Fall back to `accept` on platforms that do not support `accept4`.
+ #[cfg(not(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ )))]
+ {
+ let (socket, addr) = self.accept_raw()?;
+ let socket = set_common_flags(socket)?;
+ // `set_common_flags` does not disable inheritance on Windows because `Socket::new`
+ // unlike `accept` is able to create the socket with inheritance disabled.
+ #[cfg(windows)]
+ socket._set_no_inherit(true)?;
+ Ok((socket, addr))
+ }
+ }
+
+ /// Accept a new incoming connection from this listener.
+ ///
+ /// This function directly corresponds to the `accept(2)` function on
+ /// Windows and Unix.
+ pub fn accept_raw(&self) -> io::Result<(Socket, SockAddr)> {
+ sys::accept(self.as_raw()).map(|(inner, addr)| (Socket::from_raw(inner), addr))
+ }
+
+ /// Returns the socket address of the local half of this socket.
+ ///
+ /// # Notes
+ ///
+ /// Depending on the OS this may return an error if the socket is not
+ /// [bound].
+ ///
+ /// [bound]: Socket::bind
+ pub fn local_addr(&self) -> io::Result<SockAddr> {
+ sys::getsockname(self.as_raw())
+ }
+
+ /// Returns the socket address of the remote peer of this socket.
+ ///
+ /// # Notes
+ ///
+ /// This returns an error if the socket is not [`connect`ed].
+ ///
+ /// [`connect`ed]: Socket::connect
+ pub fn peer_addr(&self) -> io::Result<SockAddr> {
+ sys::getpeername(self.as_raw())
+ }
+
+ /// Returns the [`Type`] of this socket by checking the `SO_TYPE` option on
+ /// this socket.
+ pub fn r#type(&self) -> io::Result<Type> {
+ unsafe { getsockopt::<c_int>(self.as_raw(), sys::SOL_SOCKET, sys::SO_TYPE).map(Type) }
+ }
+
+ /// Creates a new independently owned handle to the underlying socket.
+ ///
+ /// # Notes
+ ///
+ /// On Unix this uses `F_DUPFD_CLOEXEC` and thus sets the `FD_CLOEXEC` on
+ /// the returned socket.
+ ///
+ /// On Windows this uses `WSA_FLAG_NO_HANDLE_INHERIT` setting inheriting to
+ /// false.
+ ///
+ /// On Windows this can **not** be used function cannot be used on a
+ /// QOS-enabled socket, see
+ /// <https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsaduplicatesocketw>.
+ pub fn try_clone(&self) -> io::Result<Socket> {
+ sys::try_clone(self.as_raw()).map(Socket::from_raw)
+ }
+
+ /// Moves this TCP stream into or out of nonblocking mode.
+ ///
+ /// # Notes
+ ///
+ /// On Unix this corresponds to calling `fcntl` (un)setting `O_NONBLOCK`.
+ ///
+ /// On Windows this corresponds to calling `ioctlsocket` (un)setting
+ /// `FIONBIO`.
+ pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+ sys::set_nonblocking(self.as_raw(), nonblocking)
+ }
+
+ /// Shuts down the read, write, or both halves of this connection.
+ ///
+ /// This function will cause all pending and future I/O on the specified
+ /// portions to return immediately with an appropriate value.
+ pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
+ sys::shutdown(self.as_raw(), how)
+ }
+
+ /// Receives data on the socket from the remote address to which it is
+ /// connected.
+ ///
+ /// The [`connect`] method will connect this socket to a remote address.
+ /// This method might fail if the socket is not connected.
+ ///
+ /// [`connect`]: Socket::connect
+ ///
+ /// # Safety
+ ///
+ /// Normally casting a `&mut [u8]` to `&mut [MaybeUninit<u8>]` would be
+ /// unsound, as that allows us to write uninitialised bytes to the buffer.
+ /// However this implementation promises to not write uninitialised bytes to
+ /// the `buf`fer and passes it directly to `recv(2)` system call. This
+ /// promise ensures that this function can be called using a `buf`fer of
+ /// type `&mut [u8]`.
+ ///
+ /// Note that the [`io::Read::read`] implementation calls this function with
+ /// a `buf`fer of type `&mut [u8]`, allowing initialised buffers to be used
+ /// without using `unsafe`.
+ pub fn recv(&self, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
+ self.recv_with_flags(buf, 0)
+ }
+
+ /// Receives out-of-band (OOB) data on the socket from the remote address to
+ /// which it is connected by setting the `MSG_OOB` flag for this call.
+ ///
+ /// For more information, see [`recv`], [`out_of_band_inline`].
+ ///
+ /// [`recv`]: Socket::recv
+ /// [`out_of_band_inline`]: Socket::out_of_band_inline
+ pub fn recv_out_of_band(&self, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
+ self.recv_with_flags(buf, sys::MSG_OOB)
+ }
+
+ /// Identical to [`recv`] but allows for specification of arbitrary flags to
+ /// the underlying `recv` call.
+ ///
+ /// [`recv`]: Socket::recv
+ pub fn recv_with_flags(
+ &self,
+ buf: &mut [MaybeUninit<u8>],
+ flags: sys::c_int,
+ ) -> io::Result<usize> {
+ sys::recv(self.as_raw(), buf, flags)
+ }
+
+ /// Receives data on the socket from the remote address to which it is
+ /// connected. Unlike [`recv`] this allows passing multiple buffers.
+ ///
+ /// The [`connect`] method will connect this socket to a remote address.
+ /// This method might fail if the socket is not connected.
+ ///
+ /// 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.
+ ///
+ /// [`recv`]: Socket::recv
+ /// [`connect`]: Socket::connect
+ ///
+ /// # Safety
+ ///
+ /// Normally casting a `IoSliceMut` to `MaybeUninitSlice` would be unsound,
+ /// as that allows us to write uninitialised bytes to the buffer. However
+ /// this implementation promises to not write uninitialised bytes to the
+ /// `bufs` and passes it directly to `recvmsg(2)` system call. This promise
+ /// ensures that this function can be called using `bufs` of type `&mut
+ /// [IoSliceMut]`.
+ ///
+ /// Note that the [`io::Read::read_vectored`] implementation calls this
+ /// function with `buf`s of type `&mut [IoSliceMut]`, allowing initialised
+ /// buffers to be used without using `unsafe`.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))]
+ pub fn recv_vectored(
+ &self,
+ bufs: &mut [MaybeUninitSlice<'_>],
+ ) -> io::Result<(usize, RecvFlags)> {
+ self.recv_vectored_with_flags(bufs, 0)
+ }
+
+ /// Identical to [`recv_vectored`] but allows for specification of arbitrary
+ /// flags to the underlying `recvmsg`/`WSARecv` call.
+ ///
+ /// [`recv_vectored`]: Socket::recv_vectored
+ ///
+ /// # Safety
+ ///
+ /// `recv_from_vectored` makes the same safety guarantees regarding `bufs`
+ /// as [`recv_vectored`].
+ ///
+ /// [`recv_vectored`]: Socket::recv_vectored
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))]
+ pub fn recv_vectored_with_flags(
+ &self,
+ bufs: &mut [MaybeUninitSlice<'_>],
+ flags: c_int,
+ ) -> io::Result<(usize, RecvFlags)> {
+ sys::recv_vectored(self.as_raw(), bufs, flags)
+ }
+
+ /// Receives data on the socket from the remote adress to which it is
+ /// connected, without removing that data from the queue. On success,
+ /// returns the number of bytes peeked.
+ ///
+ /// Successive calls return the same data. This is accomplished by passing
+ /// `MSG_PEEK` as a flag to the underlying `recv` system call.
+ ///
+ /// # Safety
+ ///
+ /// `peek` makes the same safety guarantees regarding the `buf`fer as
+ /// [`recv`].
+ ///
+ /// [`recv`]: Socket::recv
+ pub fn peek(&self, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
+ self.recv_with_flags(buf, sys::MSG_PEEK)
+ }
+
+ /// Receives data from the socket. On success, returns the number of bytes
+ /// read and the address from whence the data came.
+ ///
+ /// # Safety
+ ///
+ /// `recv_from` makes the same safety guarantees regarding the `buf`fer as
+ /// [`recv`].
+ ///
+ /// [`recv`]: Socket::recv
+ pub fn recv_from(&self, buf: &mut [MaybeUninit<u8>]) -> io::Result<(usize, SockAddr)> {
+ self.recv_from_with_flags(buf, 0)
+ }
+
+ /// Identical to [`recv_from`] but allows for specification of arbitrary
+ /// flags to the underlying `recvfrom` call.
+ ///
+ /// [`recv_from`]: Socket::recv_from
+ pub fn recv_from_with_flags(
+ &self,
+ buf: &mut [MaybeUninit<u8>],
+ flags: c_int,
+ ) -> io::Result<(usize, SockAddr)> {
+ sys::recv_from(self.as_raw(), buf, flags)
+ }
+
+ /// 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.
+ ///
+ /// [`recv_from`]: Socket::recv_from
+ ///
+ /// # Safety
+ ///
+ /// `recv_from_vectored` makes the same safety guarantees regarding `bufs`
+ /// as [`recv_vectored`].
+ ///
+ /// [`recv_vectored`]: Socket::recv_vectored
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))]
+ pub fn recv_from_vectored(
+ &self,
+ bufs: &mut [MaybeUninitSlice<'_>],
+ ) -> io::Result<(usize, RecvFlags, SockAddr)> {
+ self.recv_from_vectored_with_flags(bufs, 0)
+ }
+
+ /// Identical to [`recv_from_vectored`] but allows for specification of
+ /// arbitrary flags to the underlying `recvmsg`/`WSARecvFrom` call.
+ ///
+ /// [`recv_from_vectored`]: Socket::recv_from_vectored
+ ///
+ /// # Safety
+ ///
+ /// `recv_from_vectored` makes the same safety guarantees regarding `bufs`
+ /// as [`recv_vectored`].
+ ///
+ /// [`recv_vectored`]: Socket::recv_vectored
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))]
+ pub fn recv_from_vectored_with_flags(
+ &self,
+ bufs: &mut [MaybeUninitSlice<'_>],
+ flags: c_int,
+ ) -> io::Result<(usize, RecvFlags, SockAddr)> {
+ sys::recv_from_vectored(self.as_raw(), bufs, flags)
+ }
+
+ /// Receives data from the socket, without removing it from the queue.
+ ///
+ /// Successive calls return the same data. This is accomplished by passing
+ /// `MSG_PEEK` as a flag to the underlying `recvfrom` system call.
+ ///
+ /// On success, returns the number of bytes peeked and the address from
+ /// whence the data came.
+ ///
+ /// # Safety
+ ///
+ /// `peek_from` makes the same safety guarantees regarding the `buf`fer as
+ /// [`recv`].
+ ///
+ /// [`recv`]: Socket::recv
+ pub fn peek_from(&self, buf: &mut [MaybeUninit<u8>]) -> io::Result<(usize, SockAddr)> {
+ self.recv_from_with_flags(buf, sys::MSG_PEEK)
+ }
+
+ /// 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.
+ pub fn send(&self, buf: &[u8]) -> io::Result<usize> {
+ self.send_with_flags(buf, 0)
+ }
+
+ /// Identical to [`send`] but allows for specification of arbitrary flags to the underlying
+ /// `send` call.
+ ///
+ /// [`send`]: #method.send
+ pub fn send_with_flags(&self, buf: &[u8], flags: c_int) -> io::Result<usize> {
+ sys::send(self.as_raw(), buf, flags)
+ }
+
+ /// Send data to the connected peer. Returns the amount of bytes written.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))]
+ pub fn send_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ self.send_vectored_with_flags(bufs, 0)
+ }
+
+ /// Identical to [`send_vectored`] but allows for specification of arbitrary
+ /// flags to the underlying `sendmsg`/`WSASend` call.
+ ///
+ /// [`send_vectored`]: Socket::send_vectored
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))]
+ pub fn send_vectored_with_flags(
+ &self,
+ bufs: &[IoSlice<'_>],
+ flags: c_int,
+ ) -> io::Result<usize> {
+ sys::send_vectored(self.as_raw(), bufs, flags)
+ }
+
+ /// Sends out-of-band (OOB) data on the socket to connected peer
+ /// by setting the `MSG_OOB` flag for this call.
+ ///
+ /// For more information, see [`send`], [`out_of_band_inline`].
+ ///
+ /// [`send`]: #method.send
+ /// [`out_of_band_inline`]: #method.out_of_band_inline
+ pub fn send_out_of_band(&self, buf: &[u8]) -> io::Result<usize> {
+ self.send_with_flags(buf, sys::MSG_OOB)
+ }
+
+ /// Sends data on the socket to the given address. On success, returns the
+ /// number of bytes written.
+ ///
+ /// This is typically used on UDP or datagram-oriented sockets.
+ pub fn send_to(&self, buf: &[u8], addr: &SockAddr) -> io::Result<usize> {
+ self.send_to_with_flags(buf, addr, 0)
+ }
+
+ /// Identical to [`send_to`] but allows for specification of arbitrary flags
+ /// to the underlying `sendto` call.
+ ///
+ /// [`send_to`]: Socket::send_to
+ pub fn send_to_with_flags(
+ &self,
+ buf: &[u8],
+ addr: &SockAddr,
+ flags: c_int,
+ ) -> io::Result<usize> {
+ sys::send_to(self.as_raw(), buf, addr, flags)
+ }
+
+ /// Send data to a peer listening on `addr`. Returns the amount of bytes
+ /// written.
+ #[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> {
+ self.send_to_vectored_with_flags(bufs, addr, 0)
+ }
+
+ /// Identical to [`send_to_vectored`] but allows for specification of
+ /// arbitrary flags to the underlying `sendmsg`/`WSASendTo` call.
+ ///
+ /// [`send_to_vectored`]: Socket::send_to_vectored
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))]
+ pub fn send_to_vectored_with_flags(
+ &self,
+ bufs: &[IoSlice<'_>],
+ addr: &SockAddr,
+ flags: c_int,
+ ) -> io::Result<usize> {
+ sys::send_to_vectored(self.as_raw(), bufs, addr, 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 {
+ // On platforms that support it set `SOCK_CLOEXEC`.
+ #[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ let ty = ty._cloexec();
+
+ // On windows set `NO_HANDLE_INHERIT`.
+ #[cfg(windows)]
+ let ty = ty._no_inherit();
+
+ ty
+}
+
+/// Set `FD_CLOEXEC` and `NOSIGPIPE` on the `socket` for platforms that need it.
+#[inline(always)]
+#[allow(clippy::unnecessary_wraps)]
+fn set_common_flags(socket: Socket) -> io::Result<Socket> {
+ // On platforms that don't have `SOCK_CLOEXEC` use `FD_CLOEXEC`.
+ #[cfg(all(
+ unix,
+ not(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))
+ ))]
+ socket._set_cloexec(true)?;
+
+ // On Apple platforms set `NOSIGPIPE`.
+ #[cfg(target_vendor = "apple")]
+ socket._set_nosigpipe(true)?;
+
+ Ok(socket)
+}
+
+/// A local interface specified by its index or an address assigned to it.
+///
+/// `Index(0)` and `Address(Ipv4Addr::UNSPECIFIED)` are equivalent and indicate
+/// that an appropriate interface should be selected by the system.
+#[cfg(not(any(
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "redox",
+ target_os = "solaris",
+)))]
+#[derive(Debug)]
+pub enum InterfaceIndexOrAddress {
+ /// An interface index.
+ Index(u32),
+ /// An address assigned to an interface.
+ Address(Ipv4Addr),
+}
+
+/// Socket options get/set using `SOL_SOCKET`.
+///
+/// Additional documentation can be found in documentation of the OS.
+/// * Linux: <https://man7.org/linux/man-pages/man7/socket.7.html>
+/// * Windows: <https://docs.microsoft.com/en-us/windows/win32/winsock/sol-socket-socket-options>
+impl Socket {
+ /// Get the value of the `SO_BROADCAST` option for this socket.
+ ///
+ /// For more information about this option, see [`set_broadcast`].
+ ///
+ /// [`set_broadcast`]: Socket::set_broadcast
+ pub fn broadcast(&self) -> io::Result<bool> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), sys::SOL_SOCKET, sys::SO_BROADCAST)
+ .map(|broadcast| broadcast != 0)
+ }
+ }
+
+ /// Set the value of the `SO_BROADCAST` option for this socket.
+ ///
+ /// When enabled, this socket is allowed to send packets to a broadcast
+ /// address.
+ pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ sys::SOL_SOCKET,
+ sys::SO_BROADCAST,
+ broadcast as c_int,
+ )
+ }
+ }
+
+ /// Get the value of the `SO_ERROR` option on this socket.
+ ///
+ /// This will retrieve the stored error in the underlying socket, clearing
+ /// the field in the process. This can be useful for checking errors between
+ /// calls.
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ match unsafe { getsockopt::<c_int>(self.as_raw(), sys::SOL_SOCKET, sys::SO_ERROR) } {
+ Ok(0) => Ok(None),
+ Ok(errno) => Ok(Some(io::Error::from_raw_os_error(errno))),
+ Err(err) => Err(err),
+ }
+ }
+
+ /// Get the value of the `SO_KEEPALIVE` option on this socket.
+ ///
+ /// For more information about this option, see [`set_keepalive`].
+ ///
+ /// [`set_keepalive`]: Socket::set_keepalive
+ pub fn keepalive(&self) -> io::Result<bool> {
+ unsafe {
+ getsockopt::<Bool>(self.as_raw(), sys::SOL_SOCKET, sys::SO_KEEPALIVE)
+ .map(|keepalive| keepalive != 0)
+ }
+ }
+
+ /// Set value for the `SO_KEEPALIVE` option on this socket.
+ ///
+ /// Enable sending of keep-alive messages on connection-oriented sockets.
+ pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ sys::SOL_SOCKET,
+ sys::SO_KEEPALIVE,
+ keepalive as c_int,
+ )
+ }
+ }
+
+ /// Get the value of the `SO_LINGER` option on this socket.
+ ///
+ /// For more information about this option, see [`set_linger`].
+ ///
+ /// [`set_linger`]: Socket::set_linger
+ pub fn linger(&self) -> io::Result<Option<Duration>> {
+ unsafe {
+ getsockopt::<sys::linger>(self.as_raw(), sys::SOL_SOCKET, sys::SO_LINGER)
+ .map(from_linger)
+ }
+ }
+
+ /// Set value for the `SO_LINGER` option on this socket.
+ ///
+ /// If `linger` is not `None`, a close(2) or shutdown(2) will not return
+ /// until all queued messages for the socket have been successfully sent or
+ /// the linger timeout has been reached. Otherwise, the call returns
+ /// immediately and the closing is done in the background. When the socket
+ /// is closed as part of exit(2), it always lingers in the background.
+ ///
+ /// # Notes
+ ///
+ /// On most OSs the duration only has a precision of seconds and will be
+ /// silently truncated.
+ ///
+ /// On Apple platforms (e.g. macOS, iOS, etc) this uses `SO_LINGER_SEC`.
+ pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> {
+ let linger = into_linger(linger);
+ unsafe { setsockopt(self.as_raw(), sys::SOL_SOCKET, sys::SO_LINGER, linger) }
+ }
+
+ /// Get value for the `SO_OOBINLINE` option on this socket.
+ ///
+ /// For more information about this option, see [`set_out_of_band_inline`].
+ ///
+ /// [`set_out_of_band_inline`]: Socket::set_out_of_band_inline
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))]
+ pub fn out_of_band_inline(&self) -> io::Result<bool> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), sys::SOL_SOCKET, sys::SO_OOBINLINE)
+ .map(|oob_inline| oob_inline != 0)
+ }
+ }
+
+ /// Set value for the `SO_OOBINLINE` option on this socket.
+ ///
+ /// If this option is enabled, out-of-band data is directly placed into the
+ /// receive data stream. Otherwise, out-of-band data is passed only when the
+ /// `MSG_OOB` flag is set during receiving. As per RFC6093, TCP sockets
+ /// using the Urgent mechanism are encouraged to set this flag.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))]
+ pub fn set_out_of_band_inline(&self, oob_inline: bool) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ sys::SOL_SOCKET,
+ sys::SO_OOBINLINE,
+ oob_inline as c_int,
+ )
+ }
+ }
+
+ /// Get value for the `SO_RCVBUF` option on this socket.
+ ///
+ /// For more information about this option, see [`set_recv_buffer_size`].
+ ///
+ /// [`set_recv_buffer_size`]: Socket::set_recv_buffer_size
+ pub fn recv_buffer_size(&self) -> io::Result<usize> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), sys::SOL_SOCKET, sys::SO_RCVBUF)
+ .map(|size| size as usize)
+ }
+ }
+
+ /// Set value for the `SO_RCVBUF` option on this socket.
+ ///
+ /// Changes the size of the operating system's receive buffer associated
+ /// with the socket.
+ pub fn set_recv_buffer_size(&self, size: usize) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ sys::SOL_SOCKET,
+ sys::SO_RCVBUF,
+ size as c_int,
+ )
+ }
+ }
+
+ /// Get value for the `SO_RCVTIMEO` option on this socket.
+ ///
+ /// If the returned timeout is `None`, then `read` and `recv` calls will
+ /// block indefinitely.
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ sys::timeout_opt(self.as_raw(), sys::SOL_SOCKET, sys::SO_RCVTIMEO)
+ }
+
+ /// Set value for the `SO_RCVTIMEO` option on this socket.
+ ///
+ /// If `timeout` is `None`, then `read` and `recv` calls will block
+ /// indefinitely.
+ pub fn set_read_timeout(&self, duration: Option<Duration>) -> io::Result<()> {
+ sys::set_timeout_opt(self.as_raw(), sys::SOL_SOCKET, sys::SO_RCVTIMEO, duration)
+ }
+
+ /// Get the value of the `SO_REUSEADDR` option on this socket.
+ ///
+ /// For more information about this option, see [`set_reuse_address`].
+ ///
+ /// [`set_reuse_address`]: Socket::set_reuse_address
+ pub fn reuse_address(&self) -> io::Result<bool> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), sys::SOL_SOCKET, sys::SO_REUSEADDR)
+ .map(|reuse| reuse != 0)
+ }
+ }
+
+ /// Set value for the `SO_REUSEADDR` option on this socket.
+ ///
+ /// This indicates that futher calls to `bind` may allow reuse of local
+ /// addresses. For IPv4 sockets this means that a socket may bind even when
+ /// there's a socket already listening on this port.
+ pub fn set_reuse_address(&self, reuse: bool) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ sys::SOL_SOCKET,
+ sys::SO_REUSEADDR,
+ reuse as c_int,
+ )
+ }
+ }
+
+ /// Get the value of the `SO_SNDBUF` option on this socket.
+ ///
+ /// For more information about this option, see [`set_send_buffer_size`].
+ ///
+ /// [`set_send_buffer_size`]: Socket::set_send_buffer_size
+ pub fn send_buffer_size(&self) -> io::Result<usize> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), sys::SOL_SOCKET, sys::SO_SNDBUF)
+ .map(|size| size as usize)
+ }
+ }
+
+ /// Set value for the `SO_SNDBUF` option on this socket.
+ ///
+ /// Changes the size of the operating system's send buffer associated with
+ /// the socket.
+ pub fn set_send_buffer_size(&self, size: usize) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ sys::SOL_SOCKET,
+ sys::SO_SNDBUF,
+ size as c_int,
+ )
+ }
+ }
+
+ /// Get value for the `SO_SNDTIMEO` option on this socket.
+ ///
+ /// If the returned timeout is `None`, then `write` and `send` calls will
+ /// block indefinitely.
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ sys::timeout_opt(self.as_raw(), sys::SOL_SOCKET, sys::SO_SNDTIMEO)
+ }
+
+ /// Set value for the `SO_SNDTIMEO` option on this socket.
+ ///
+ /// If `timeout` is `None`, then `write` and `send` calls will block
+ /// indefinitely.
+ pub fn set_write_timeout(&self, duration: Option<Duration>) -> io::Result<()> {
+ sys::set_timeout_opt(self.as_raw(), sys::SOL_SOCKET, sys::SO_SNDTIMEO, duration)
+ }
+}
+
+fn from_linger(linger: sys::linger) -> Option<Duration> {
+ if linger.l_onoff == 0 {
+ None
+ } else {
+ Some(Duration::from_secs(linger.l_linger as u64))
+ }
+}
+
+fn into_linger(duration: Option<Duration>) -> sys::linger {
+ match duration {
+ Some(duration) => sys::linger {
+ l_onoff: 1,
+ l_linger: duration.as_secs() as _,
+ },
+ None => sys::linger {
+ l_onoff: 0,
+ l_linger: 0,
+ },
+ }
+}
+
+/// Socket options for IPv4 sockets, get/set using `IPPROTO_IP`.
+///
+/// Additional documentation can be found in documentation of the OS.
+/// * Linux: <https://man7.org/linux/man-pages/man7/ip.7.html>
+/// * Windows: <https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options>
+impl Socket {
+ /// Get the value of the `IP_HDRINCL` option on this 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"))))]
+ pub fn header_included(&self) -> io::Result<bool> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), sys::IPPROTO_IP, sys::IP_HDRINCL)
+ .map(|included| included != 0)
+ }
+ }
+
+ /// Set the value of the `IP_HDRINCL` option on this socket.
+ ///
+ /// If enabled, the user supplies an IP header in front of the user data.
+ /// Valid only for [`SOCK_RAW`] sockets; see [raw(7)] for more information.
+ /// When this flag is enabled, the values set by `IP_OPTIONS`, [`IP_TTL`],
+ /// and [`IP_TOS`] are ignored.
+ ///
+ /// [`SOCK_RAW`]: Type::RAW
+ /// [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"))))]
+ pub fn set_header_included(&self, included: bool) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ sys::IPPROTO_IP,
+ sys::IP_HDRINCL,
+ included as c_int,
+ )
+ }
+ }
+
+ /// Get the value of the `IP_TRANSPARENT` option on this 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_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
+ pub fn ip_transparent(&self) -> io::Result<bool> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), sys::IPPROTO_IP, libc::IP_TRANSPARENT)
+ .map(|transparent| transparent != 0)
+ }
+ }
+
+ /// Set the value of the `IP_TRANSPARENT` option on this socket.
+ ///
+ /// Setting this boolean option enables transparent proxying
+ /// on this socket. This socket option allows the calling
+ /// application to bind to a nonlocal IP address and operate
+ /// both as a client and a server with the foreign address as
+ /// the local endpoint. NOTE: this requires that routing be
+ /// set up in a way that packets going to the foreign address
+ /// are routed through the TProxy box (i.e., the system
+ /// hosting the application that employs the IP_TRANSPARENT
+ /// socket option). Enabling this socket option requires
+ /// superuser privileges (the `CAP_NET_ADMIN` capability).
+ ///
+ /// 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_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
+ pub fn set_ip_transparent(&self, transparent: bool) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ sys::IPPROTO_IP,
+ libc::IP_TRANSPARENT,
+ transparent as c_int,
+ )
+ }
+ }
+
+ /// Join a multicast group using `IP_ADD_MEMBERSHIP` option on this 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
+ /// address of the local interface with which the system should join the
+ /// multicast group. If it's [`Ipv4Addr::UNSPECIFIED`] (`INADDR_ANY`) then
+ /// an appropriate interface is chosen by the system.
+ pub fn join_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> {
+ let mreq = sys::IpMreq {
+ imr_multiaddr: sys::to_in_addr(multiaddr),
+ imr_interface: sys::to_in_addr(interface),
+ };
+ unsafe { setsockopt(self.as_raw(), sys::IPPROTO_IP, sys::IP_ADD_MEMBERSHIP, mreq) }
+ }
+
+ /// Leave a multicast group using `IP_DROP_MEMBERSHIP` option on this socket.
+ ///
+ /// For more information about this option, see [`join_multicast_v4`].
+ ///
+ /// [`join_multicast_v4`]: Socket::join_multicast_v4
+ pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> {
+ let mreq = sys::IpMreq {
+ imr_multiaddr: sys::to_in_addr(multiaddr),
+ imr_interface: sys::to_in_addr(interface),
+ };
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ sys::IPPROTO_IP,
+ sys::IP_DROP_MEMBERSHIP,
+ mreq,
+ )
+ }
+ }
+
+ /// Join a multicast group using `IP_ADD_MEMBERSHIP` option on this socket.
+ ///
+ /// This function specifies a new multicast group for this socket to join.
+ /// The address must be a valid multicast address, and `interface` specifies
+ /// the local interface with which the system should join the multicast
+ /// group. See [`InterfaceIndexOrAddress`].
+ #[cfg(not(any(
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "redox",
+ target_os = "solaris",
+ )))]
+ pub fn join_multicast_v4_n(
+ &self,
+ multiaddr: &Ipv4Addr,
+ interface: &InterfaceIndexOrAddress,
+ ) -> io::Result<()> {
+ let mreqn = sys::to_mreqn(multiaddr, interface);
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ sys::IPPROTO_IP,
+ sys::IP_ADD_MEMBERSHIP,
+ mreqn,
+ )
+ }
+ }
+
+ /// Leave a multicast group using `IP_DROP_MEMBERSHIP` option on this socket.
+ ///
+ /// For more information about this option, see [`join_multicast_v4_n`].
+ ///
+ /// [`join_multicast_v4_n`]: Socket::join_multicast_v4_n
+ #[cfg(not(any(
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "redox",
+ target_os = "solaris",
+ )))]
+ pub fn leave_multicast_v4_n(
+ &self,
+ multiaddr: &Ipv4Addr,
+ interface: &InterfaceIndexOrAddress,
+ ) -> io::Result<()> {
+ let mreqn = sys::to_mreqn(multiaddr, interface);
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ sys::IPPROTO_IP,
+ sys::IP_DROP_MEMBERSHIP,
+ mreqn,
+ )
+ }
+ }
+
+ /// Get the value of the `IP_MULTICAST_IF` option for this socket.
+ ///
+ /// For more information about this option, see [`set_multicast_if_v4`].
+ ///
+ /// [`set_multicast_if_v4`]: Socket::set_multicast_if_v4
+ pub fn multicast_if_v4(&self) -> io::Result<Ipv4Addr> {
+ unsafe {
+ getsockopt(self.as_raw(), sys::IPPROTO_IP, sys::IP_MULTICAST_IF).map(sys::from_in_addr)
+ }
+ }
+
+ /// Set the value of the `IP_MULTICAST_IF` option for this socket.
+ ///
+ /// Specifies the interface to use for routing multicast packets.
+ pub fn set_multicast_if_v4(&self, interface: &Ipv4Addr) -> io::Result<()> {
+ let interface = sys::to_in_addr(interface);
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ sys::IPPROTO_IP,
+ sys::IP_MULTICAST_IF,
+ interface,
+ )
+ }
+ }
+
+ /// Get the value of the `IP_MULTICAST_LOOP` option for this socket.
+ ///
+ /// For more information about this option, see [`set_multicast_loop_v4`].
+ ///
+ /// [`set_multicast_loop_v4`]: Socket::set_multicast_loop_v4
+ pub fn multicast_loop_v4(&self) -> io::Result<bool> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), sys::IPPROTO_IP, sys::IP_MULTICAST_LOOP)
+ .map(|loop_v4| loop_v4 != 0)
+ }
+ }
+
+ /// Set the value of the `IP_MULTICAST_LOOP` option for this socket.
+ ///
+ /// If enabled, multicast packets will be looped back to the local socket.
+ /// Note that this may not have any affect on IPv6 sockets.
+ pub fn set_multicast_loop_v4(&self, loop_v4: bool) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ sys::IPPROTO_IP,
+ sys::IP_MULTICAST_LOOP,
+ loop_v4 as c_int,
+ )
+ }
+ }
+
+ /// Get the value of the `IP_MULTICAST_TTL` option for this socket.
+ ///
+ /// For more information about this option, see [`set_multicast_ttl_v4`].
+ ///
+ /// [`set_multicast_ttl_v4`]: Socket::set_multicast_ttl_v4
+ pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), sys::IPPROTO_IP, sys::IP_MULTICAST_TTL)
+ .map(|ttl| ttl as u32)
+ }
+ }
+
+ /// Set the value of the `IP_MULTICAST_TTL` option for this socket.
+ ///
+ /// Indicates the time-to-live value of outgoing multicast packets for
+ /// this socket. The default value is 1 which means that multicast packets
+ /// don't leave the local network unless explicitly requested.
+ ///
+ /// Note that this may not have any affect on IPv6 sockets.
+ pub fn set_multicast_ttl_v4(&self, ttl: u32) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ sys::IPPROTO_IP,
+ sys::IP_MULTICAST_TTL,
+ ttl as c_int,
+ )
+ }
+ }
+
+ /// Get the value of the `IP_TTL` option for this socket.
+ ///
+ /// For more information about this option, see [`set_ttl`].
+ ///
+ /// [`set_ttl`]: Socket::set_ttl
+ pub fn ttl(&self) -> io::Result<u32> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), sys::IPPROTO_IP, sys::IP_TTL).map(|ttl| ttl as u32)
+ }
+ }
+
+ /// Set the value of the `IP_TTL` option for this socket.
+ ///
+ /// This value sets the time-to-live field that is used in every packet sent
+ /// from this socket.
+ pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
+ unsafe { setsockopt(self.as_raw(), sys::IPPROTO_IP, sys::IP_TTL, ttl as c_int) }
+ }
+
+ /// Set the value of the `IP_TOS` option for this socket.
+ ///
+ /// This value sets the type-of-service field that is used in every packet
+ /// sent from this socket.
+ ///
+ /// NOTE: <https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options>
+ /// documents that not all versions of windows support `IP_TOS`.
+ #[cfg(not(any(
+ target_os = "fuschia",
+ target_os = "redox",
+ target_os = "solaris",
+ target_os = "illumos",
+ )))]
+ pub fn set_tos(&self, tos: u32) -> io::Result<()> {
+ unsafe { setsockopt(self.as_raw(), sys::IPPROTO_IP, sys::IP_TOS, tos as c_int) }
+ }
+
+ /// Get the value of the `IP_TOS` option for this socket.
+ ///
+ /// For more information about this option, see [`set_tos`].
+ ///
+ /// NOTE: <https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options>
+ /// documents that not all versions of windows support `IP_TOS`.
+ ///
+ /// [`set_tos`]: Socket::set_tos
+ #[cfg(not(any(
+ target_os = "fuschia",
+ target_os = "redox",
+ target_os = "solaris",
+ target_os = "illumos",
+ )))]
+ pub fn tos(&self) -> io::Result<u32> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), sys::IPPROTO_IP, sys::IP_TOS).map(|tos| tos as u32)
+ }
+ }
+}
+
+/// Socket options for IPv6 sockets, get/set using `IPPROTO_IPV6`.
+///
+/// Additional documentation can be found in documentation of the OS.
+/// * Linux: <https://man7.org/linux/man-pages/man7/ipv6.7.html>
+/// * Windows: <https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ipv6-socket-options>
+impl Socket {
+ /// Join a multicast group using `IPV6_ADD_MEMBERSHIP` option on this socket.
+ ///
+ /// Some OSs use `IPV6_JOIN_GROUP` for this option.
+ ///
+ /// 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).
+ pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> {
+ let mreq = sys::Ipv6Mreq {
+ ipv6mr_multiaddr: sys::to_in6_addr(multiaddr),
+ // NOTE: some OSs use `c_int`, others use `c_uint`.
+ ipv6mr_interface: interface as _,
+ };
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ sys::IPPROTO_IPV6,
+ sys::IPV6_ADD_MEMBERSHIP,
+ mreq,
+ )
+ }
+ }
+
+ /// Leave a multicast group using `IPV6_DROP_MEMBERSHIP` option on this socket.
+ ///
+ /// Some OSs use `IPV6_LEAVE_GROUP` for this option.
+ ///
+ /// For more information about this option, see [`join_multicast_v6`].
+ ///
+ /// [`join_multicast_v6`]: Socket::join_multicast_v6
+ pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> {
+ let mreq = sys::Ipv6Mreq {
+ ipv6mr_multiaddr: sys::to_in6_addr(multiaddr),
+ // NOTE: some OSs use `c_int`, others use `c_uint`.
+ ipv6mr_interface: interface as _,
+ };
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ sys::IPPROTO_IPV6,
+ sys::IPV6_DROP_MEMBERSHIP,
+ mreq,
+ )
+ }
+ }
+
+ /// Get the value of the `IPV6_MULTICAST_HOPS` option for this socket
+ ///
+ /// For more information about this option, see [`set_multicast_hops_v6`].
+ ///
+ /// [`set_multicast_hops_v6`]: Socket::set_multicast_hops_v6
+ pub fn multicast_hops_v6(&self) -> io::Result<u32> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), sys::IPPROTO_IPV6, sys::IPV6_MULTICAST_HOPS)
+ .map(|hops| hops as u32)
+ }
+ }
+
+ /// Set the value of the `IPV6_MULTICAST_HOPS` option for this socket
+ ///
+ /// Indicates the number of "routers" multicast packets will transit for
+ /// this socket. The default value is 1 which means that multicast packets
+ /// don't leave the local network unless explicitly requested.
+ pub fn set_multicast_hops_v6(&self, hops: u32) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ sys::IPPROTO_IPV6,
+ sys::IPV6_MULTICAST_HOPS,
+ hops as c_int,
+ )
+ }
+ }
+
+ /// Get the value of the `IPV6_MULTICAST_IF` option for this socket.
+ ///
+ /// For more information about this option, see [`set_multicast_if_v6`].
+ ///
+ /// [`set_multicast_if_v6`]: Socket::set_multicast_if_v6
+ pub fn multicast_if_v6(&self) -> io::Result<u32> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), sys::IPPROTO_IPV6, sys::IPV6_MULTICAST_IF)
+ .map(|interface| interface as u32)
+ }
+ }
+
+ /// Set the value of the `IPV6_MULTICAST_IF` option for this socket.
+ ///
+ /// Specifies the interface to use for routing multicast packets. Unlike
+ /// ipv4, this is generally required in ipv6 contexts where network routing
+ /// prefixes may overlap.
+ pub fn set_multicast_if_v6(&self, interface: u32) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ sys::IPPROTO_IPV6,
+ sys::IPV6_MULTICAST_IF,
+ interface as c_int,
+ )
+ }
+ }
+
+ /// Get the value of the `IPV6_MULTICAST_LOOP` option for this socket.
+ ///
+ /// For more information about this option, see [`set_multicast_loop_v6`].
+ ///
+ /// [`set_multicast_loop_v6`]: Socket::set_multicast_loop_v6
+ pub fn multicast_loop_v6(&self) -> io::Result<bool> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), sys::IPPROTO_IPV6, sys::IPV6_MULTICAST_LOOP)
+ .map(|loop_v6| loop_v6 != 0)
+ }
+ }
+
+ /// Set the value of the `IPV6_MULTICAST_LOOP` option for this socket.
+ ///
+ /// Controls whether this socket sees the multicast packets it sends itself.
+ /// Note that this may not have any affect on IPv4 sockets.
+ pub fn set_multicast_loop_v6(&self, loop_v6: bool) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ sys::IPPROTO_IPV6,
+ sys::IPV6_MULTICAST_LOOP,
+ loop_v6 as c_int,
+ )
+ }
+ }
+
+ /// Get the value of the `IPV6_UNICAST_HOPS` option for this socket.
+ ///
+ /// Specifies the hop limit for ipv6 unicast packets
+ pub fn unicast_hops_v6(&self) -> io::Result<u32> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), sys::IPPROTO_IPV6, sys::IPV6_UNICAST_HOPS)
+ .map(|hops| hops as u32)
+ }
+ }
+
+ /// Set the value for the `IPV6_UNICAST_HOPS` option on this socket.
+ ///
+ /// Specifies the hop limit for ipv6 unicast packets
+ pub fn set_unicast_hops_v6(&self, hops: u32) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ sys::IPPROTO_IPV6,
+ sys::IPV6_UNICAST_HOPS,
+ hops as c_int,
+ )
+ }
+ }
+
+ /// Get the value of the `IPV6_V6ONLY` option for this socket.
+ ///
+ /// For more information about this option, see [`set_only_v6`].
+ ///
+ /// [`set_only_v6`]: Socket::set_only_v6
+ pub fn only_v6(&self) -> io::Result<bool> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), sys::IPPROTO_IPV6, sys::IPV6_V6ONLY)
+ .map(|only_v6| only_v6 != 0)
+ }
+ }
+
+ /// Set the value for the `IPV6_V6ONLY` option on this socket.
+ ///
+ /// If this is set to `true` then the socket is restricted to sending and
+ /// receiving IPv6 packets only. In this case two IPv4 and IPv6 applications
+ /// can bind the same port at the same time.
+ ///
+ /// If this is set to `false` then the socket can be used to send and
+ /// receive packets from an IPv4-mapped IPv6 address.
+ pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ sys::IPPROTO_IPV6,
+ sys::IPV6_V6ONLY,
+ only_v6 as c_int,
+ )
+ }
+ }
+}
+
+/// Socket options for TCP sockets, get/set using `IPPROTO_TCP`.
+///
+/// Additional documentation can be found in documentation of the OS.
+/// * Linux: <https://man7.org/linux/man-pages/man7/tcp.7.html>
+/// * Windows: <https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-tcp-socket-options>
+impl Socket {
+ /// Get the value of the `TCP_KEEPIDLE` option on this 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_attr(
+ docsrs,
+ doc(cfg(all(
+ feature = "all",
+ not(any(windows, target_os = "haiku", target_os = "openbsd"))
+ )))
+ )]
+ pub fn keepalive_time(&self) -> io::Result<Duration> {
+ sys::keepalive_time(self.as_raw())
+ }
+
+ /// Get the value of the `TCP_KEEPINTVL` option on this socket.
+ ///
+ /// For more information about this option, see [`set_tcp_keepalive`].
+ ///
+ /// [`set_tcp_keepalive`]: Socket::set_tcp_keepalive
+ #[cfg(all(
+ feature = "all",
+ any(
+ doc,
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_vendor = "apple",
+ )
+ ))]
+ #[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",
+ )
+ )))
+ )]
+ pub fn keepalive_interval(&self) -> io::Result<Duration> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), sys::IPPROTO_TCP, sys::TCP_KEEPINTVL)
+ .map(|secs| Duration::from_secs(secs as u64))
+ }
+ }
+
+ /// Get the value of the `TCP_KEEPCNT` option on this socket.
+ ///
+ /// For more information about this option, see [`set_tcp_keepalive`].
+ ///
+ /// [`set_tcp_keepalive`]: Socket::set_tcp_keepalive
+ #[cfg(all(
+ feature = "all",
+ any(
+ doc,
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_vendor = "apple",
+ )
+ ))]
+ #[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",
+ )
+ )))
+ )]
+ pub fn keepalive_retries(&self) -> io::Result<u32> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), sys::IPPROTO_TCP, sys::TCP_KEEPCNT)
+ .map(|retries| retries as u32)
+ }
+ }
+
+ /// Set parameters configuring TCP keepalive probes for this socket.
+ ///
+ /// The supported parameters depend on the operating system, and are
+ /// configured using the [`TcpKeepalive`] struct. At a minimum, all systems
+ /// support configuring the [keepalive time]: the time after which the OS
+ /// will start sending keepalive messages on an idle connection.
+ ///
+ /// [keepalive time]: TcpKeepalive::with_time
+ ///
+ /// # Notes
+ ///
+ /// * This will enable `SO_KEEPALIVE` on this socket, if it is not already
+ /// enabled.
+ /// * On some platforms, such as Windows, any keepalive parameters *not*
+ /// configured by the `TcpKeepalive` struct passed to this function may be
+ /// overwritten with their default values. Therefore, this function should
+ /// either only be called once per socket, or the same parameters should
+ /// be passed every time it is called.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::time::Duration;
+ ///
+ /// use socket2::{Socket, TcpKeepalive, Domain, Type};
+ ///
+ /// # fn main() -> std::io::Result<()> {
+ /// let socket = Socket::new(Domain::IPV4, Type::STREAM, None)?;
+ /// let keepalive = TcpKeepalive::new()
+ /// .with_time(Duration::from_secs(4));
+ /// // Depending on the target operating system, we may also be able to
+ /// // configure the keepalive probe interval and/or the number of
+ /// // retries here as well.
+ ///
+ /// socket.set_tcp_keepalive(&keepalive)?;
+ /// # Ok(()) }
+ /// ```
+ ///
+ pub fn set_tcp_keepalive(&self, params: &TcpKeepalive) -> io::Result<()> {
+ self.set_keepalive(true)?;
+ sys::set_tcp_keepalive(self.as_raw(), params)
+ }
+
+ /// Get the value of the `TCP_NODELAY` option on this socket.
+ ///
+ /// For more information about this option, see [`set_nodelay`].
+ ///
+ /// [`set_nodelay`]: Socket::set_nodelay
+ pub fn nodelay(&self) -> io::Result<bool> {
+ unsafe {
+ getsockopt::<Bool>(self.as_raw(), sys::IPPROTO_TCP, sys::TCP_NODELAY)
+ .map(|nodelay| nodelay != 0)
+ }
+ }
+
+ /// Set the value of the `TCP_NODELAY` option on this socket.
+ ///
+ /// If set, this option disables the Nagle algorithm. This means that
+ /// segments are always sent as soon as possible, even if there is only a
+ /// small amount of data. When not set, data is buffered until there is a
+ /// sufficient amount to send out, thereby avoiding the frequent sending of
+ /// small packets.
+ pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ sys::IPPROTO_TCP,
+ sys::TCP_NODELAY,
+ nodelay as c_int,
+ )
+ }
+ }
+}
+
+impl Read for Socket {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ // Safety: the `recv` implementation promises not to write uninitialised
+ // bytes to the `buf`fer, so this casting is safe.
+ let buf = unsafe { &mut *(buf as *mut [u8] as *mut [MaybeUninit<u8>]) };
+ self.recv(buf)
+ }
+
+ #[cfg(not(target_os = "redox"))]
+ fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ // Safety: both `IoSliceMut` and `MaybeUninitSlice` promise to have the
+ // same layout, that of `iovec`/`WSABUF`. Furthermore `recv_vectored`
+ // promises to not write unitialised bytes to the `bufs` and pass it
+ // directly to the `recvmsg` system call, so this is safe.
+ let bufs = unsafe { &mut *(bufs as *mut [IoSliceMut<'_>] as *mut [MaybeUninitSlice<'_>]) };
+ self.recv_vectored(bufs).map(|(n, _)| n)
+ }
+}
+
+impl<'a> Read for &'a Socket {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ // Safety: see other `Read::read` impl.
+ let buf = unsafe { &mut *(buf as *mut [u8] as *mut [MaybeUninit<u8>]) };
+ self.recv(buf)
+ }
+
+ #[cfg(not(target_os = "redox"))]
+ fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ // Safety: see other `Read::read` impl.
+ let bufs = unsafe { &mut *(bufs as *mut [IoSliceMut<'_>] as *mut [MaybeUninitSlice<'_>]) };
+ self.recv_vectored(bufs).map(|(n, _)| n)
+ }
+}
+
+impl Write for Socket {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.send(buf)
+ }
+
+ #[cfg(not(target_os = "redox"))]
+ fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ self.send_vectored(bufs)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+impl<'a> Write for &'a Socket {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.send(buf)
+ }
+
+ #[cfg(not(target_os = "redox"))]
+ fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ self.send_vectored(bufs)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+impl fmt::Debug for Socket {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Socket")
+ .field("raw", &self.as_raw())
+ .field("local_addr", &self.local_addr().ok())
+ .field("peer_addr", &self.peer_addr().ok())
+ .finish()
+ }
+}
+
+from!(net::TcpStream, Socket);
+from!(net::TcpListener, Socket);
+from!(net::UdpSocket, Socket);
+from!(Socket, net::TcpStream);
+from!(Socket, net::TcpListener);
+from!(Socket, net::UdpSocket);
diff --git a/src/sockref.rs b/src/sockref.rs
new file mode 100644
index 0000000..257323b
--- /dev/null
+++ b/src/sockref.rs
@@ -0,0 +1,147 @@
+use std::fmt;
+use std::marker::PhantomData;
+use std::mem::ManuallyDrop;
+use std::ops::Deref;
+#[cfg(unix)]
+use std::os::unix::io::{AsRawFd, FromRawFd};
+#[cfg(windows)]
+use std::os::windows::io::{AsRawSocket, FromRawSocket};
+
+use crate::Socket;
+
+/// A reference to a [`Socket`] that can be used to configure socket types other
+/// than the `Socket` type itself.
+///
+/// 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.
+///
+/// [`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
+///
+/// # Examples
+///
+/// Below is an example of converting a [`TcpStream`] into a [`SockRef`].
+///
+/// ```
+/// use std::net::{TcpStream, SocketAddr};
+///
+/// use socket2::SockRef;
+///
+/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+/// // Create `TcpStream` from the standard library.
+/// let address: SocketAddr = "127.0.0.1:1234".parse()?;
+/// # let b1 = std::sync::Arc::new(std::sync::Barrier::new(2));
+/// # let b2 = b1.clone();
+/// # let handle = std::thread::spawn(move || {
+/// # let listener = std::net::TcpListener::bind(address).unwrap();
+/// # b2.wait();
+/// # let (stream, _) = listener.accept().unwrap();
+/// # std::thread::sleep(std::time::Duration::from_millis(10));
+/// # drop(stream);
+/// # });
+/// # b1.wait();
+/// let stream = TcpStream::connect(address)?;
+///
+/// // Create a `SockRef`erence to the stream.
+/// let socket_ref = SockRef::from(&stream);
+/// // Use `Socket::set_nodelay` on the stream.
+/// socket_ref.set_nodelay(true)?;
+/// drop(socket_ref);
+///
+/// assert_eq!(stream.nodelay()?, true);
+/// # handle.join().unwrap();
+/// # 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
+ /// closing itself.
+ socket: ManuallyDrop<Socket>,
+ /// Because we don't own the socket we need to ensure the socket remains
+ /// open while we have a "reference" to it, the lifetime `'s` ensures this.
+ _lifetime: PhantomData<&'s Socket>,
+}
+
+impl<'s> Deref for SockRef<'s> {
+ type Target = Socket;
+
+ fn deref(&self) -> &Self::Target {
+ &self.socket
+ }
+}
+
+/// On Windows, a corresponding `From<&impl AsRawSocket>` implementation exists.
+#[cfg(unix)]
+#[cfg_attr(docsrs, doc(cfg(unix)))]
+impl<'s, S> From<&'s S> for SockRef<'s>
+where
+ S: AsRawFd,
+{
+ /// The caller must ensure `S` is actually a socket.
+ fn from(socket: &'s S) -> Self {
+ let fd = socket.as_raw_fd();
+ assert!(fd >= 0);
+ SockRef {
+ socket: ManuallyDrop::new(unsafe { Socket::from_raw_fd(fd) }),
+ _lifetime: PhantomData,
+ }
+ }
+}
+
+/// On Unix, a corresponding `From<&impl AsRawFd>` implementation exists.
+#[cfg(windows)]
+#[cfg_attr(docsrs, doc(cfg(windows)))]
+impl<'s, S> From<&'s S> for SockRef<'s>
+where
+ S: AsRawSocket,
+{
+ /// See the `From<&impl AsRawFd>` implementation.
+ fn from(socket: &'s S) -> Self {
+ let socket = socket.as_raw_socket();
+ assert!(socket != winapi::um::winsock2::INVALID_SOCKET as _);
+ SockRef {
+ socket: ManuallyDrop::new(unsafe { Socket::from_raw_socket(socket) }),
+ _lifetime: PhantomData,
+ }
+ }
+}
+
+impl fmt::Debug for SockRef<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("SockRef")
+ .field("raw", &self.socket.as_raw())
+ .field("local_addr", &self.socket.local_addr().ok())
+ .field("peer_addr", &self.socket.peer_addr().ok())
+ .finish()
+ }
+}
diff --git a/src/sys/unix.rs b/src/sys/unix.rs
new file mode 100644
index 0000000..873b323
--- /dev/null
+++ b/src/sys/unix.rs
@@ -0,0 +1,2053 @@
+// Copyright 2015 The Rust Project Developers.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::cmp::min;
+#[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"))]
+use std::num::NonZeroU32;
+#[cfg(all(
+ feature = "all",
+ any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_vendor = "apple",
+ )
+))]
+use std::num::NonZeroUsize;
+#[cfg(feature = "all")]
+use std::os::unix::ffi::OsStrExt;
+#[cfg(all(
+ feature = "all",
+ any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_vendor = "apple",
+ )
+))]
+use std::os::unix::io::RawFd;
+use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
+#[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"))]
+use libc::ssize_t;
+use libc::{c_void, in6_addr, in_addr};
+
+#[cfg(not(target_os = "redox"))]
+use crate::RecvFlags;
+use crate::{Domain, Protocol, SockAddr, TcpKeepalive, Type};
+
+pub(crate) use libc::c_int;
+
+// Used in `Domain`.
+pub(crate) use libc::{AF_INET, AF_INET6};
+// Used in `Type`.
+#[cfg(all(feature = "all", not(target_os = "redox")))]
+pub(crate) use libc::SOCK_RAW;
+#[cfg(feature = "all")]
+pub(crate) use libc::SOCK_SEQPACKET;
+pub(crate) use libc::{SOCK_DGRAM, SOCK_STREAM};
+// Used in `Protocol`.
+pub(crate) use libc::{IPPROTO_ICMP, IPPROTO_ICMPV6, IPPROTO_TCP, IPPROTO_UDP};
+// Used in `SockAddr`.
+pub(crate) use libc::{
+ sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, sockaddr_storage, socklen_t,
+};
+// Used in `RecvFlags`.
+#[cfg(not(target_os = "redox"))]
+pub(crate) use libc::{MSG_TRUNC, SO_OOBINLINE};
+// Used in `Socket`.
+#[cfg(all(feature = "all", not(target_os = "redox")))]
+pub(crate) use libc::IP_HDRINCL;
+#[cfg(not(any(
+ target_os = "fuschia",
+ target_os = "redox",
+ target_os = "solaris",
+ target_os = "illumos",
+)))]
+pub(crate) use libc::IP_TOS;
+#[cfg(not(target_vendor = "apple"))]
+pub(crate) use libc::SO_LINGER;
+#[cfg(target_vendor = "apple")]
+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,
+};
+#[cfg(not(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris",
+ target_vendor = "apple"
+)))]
+pub(crate) use libc::{IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP};
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris",
+ target_vendor = "apple",
+))]
+pub(crate) use libc::{
+ IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP, IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP,
+};
+#[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",
+ )
+))]
+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")]
+use libc::TCP_KEEPALIVE as KEEPALIVE_TIME;
+#[cfg(not(any(target_vendor = "apple", target_os = "haiku", target_os = "openbsd")))]
+use libc::TCP_KEEPIDLE as KEEPALIVE_TIME;
+
+/// Helper macro to execute a system call that returns an `io::Result`.
+macro_rules! syscall {
+ ($fn: ident ( $($arg: expr),* $(,)* ) ) => {{
+ #[allow(unused_unsafe)]
+ let res = unsafe { libc::$fn($($arg, )*) };
+ if res == -1 {
+ Err(std::io::Error::last_os_error())
+ } else {
+ Ok(res)
+ }
+ }};
+}
+
+/// 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;
+
+// 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
+// `SSIZE_MAX` the result is "unspecified".
+//
+// On macOS, however, apparently the 64-bit libc is either buggy or
+// 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(
+ all(
+ target_os = "linux",
+ any(
+ target_env = "gnu",
+ all(target_env = "uclibc", target_pointer_width = "64")
+ )
+ ),
+ target_os = "android",
+))]
+type IovLen = usize;
+
+#[cfg(any(
+ all(
+ target_os = "linux",
+ any(
+ target_env = "musl",
+ all(target_env = "uclibc", target_pointer_width = "32")
+ )
+ ),
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris",
+ target_vendor = "apple",
+))]
+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",
+ 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 const PACKET: Domain = Domain(libc::AF_PACKET);
+
+ /// Domain for low-level VSOCK interface, corresponding to `AF_VSOCK`.
+ #[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 const VSOCK: Domain = Domain(libc::AF_VSOCK);
+}
+
+impl_debug!(
+ Domain,
+ libc::AF_INET,
+ libc::AF_INET6,
+ libc::AF_UNIX,
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(
+ docsrs,
+ doc(cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux")))
+ )]
+ libc::AF_PACKET,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(any(target_os = "android", target_os = "linux"))))]
+ libc::AF_VSOCK,
+ libc::AF_UNSPEC, // = 0.
+);
+
+/// Unix only API.
+impl Type {
+ /// Set `SOCK_NONBLOCK` on the `Type`.
+ #[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_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 = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ )
+ )))
+ )]
+ pub const fn nonblocking(self) -> Type {
+ Type(self.0 | libc::SOCK_NONBLOCK)
+ }
+
+ /// Set `SOCK_CLOEXEC` on the `Type`.
+ #[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_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 = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ )
+ )))
+ )]
+ pub const fn cloexec(self) -> Type {
+ self._cloexec()
+ }
+
+ #[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ pub(crate) const fn _cloexec(self) -> Type {
+ Type(self.0 | libc::SOCK_CLOEXEC)
+ }
+}
+
+impl_debug!(
+ Type,
+ libc::SOCK_STREAM,
+ libc::SOCK_DGRAM,
+ #[cfg(not(target_os = "redox"))]
+ libc::SOCK_RAW,
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ libc::SOCK_RDM,
+ libc::SOCK_SEQPACKET,
+ /* TODO: add these optional bit OR-ed flags:
+ #[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ libc::SOCK_NONBLOCK,
+ #[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ libc::SOCK_CLOEXEC,
+ */
+);
+
+impl_debug!(
+ Protocol,
+ libc::IPPROTO_ICMP,
+ libc::IPPROTO_ICMPV6,
+ libc::IPPROTO_TCP,
+ libc::IPPROTO_UDP,
+);
+
+/// Unix-only API.
+#[cfg(not(target_os = "redox"))]
+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.
+ ///
+ /// On Unix this corresponds to the MSG_EOR flag.
+ pub const fn is_end_of_record(self) -> bool {
+ self.0 & libc::MSG_EOR != 0
+ }
+
+ /// Check if the message contains out-of-band data.
+ ///
+ /// 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.
+ pub const fn is_out_of_band(self) -> bool {
+ self.0 & libc::MSG_OOB != 0
+ }
+}
+
+#[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()
+ }
+}
+
+#[repr(transparent)]
+pub struct MaybeUninitSlice<'a> {
+ vec: libc::iovec,
+ _lifetime: PhantomData<&'a mut [MaybeUninit<u8>]>,
+}
+
+unsafe impl<'a> Send for MaybeUninitSlice<'a> {}
+
+unsafe impl<'a> Sync for MaybeUninitSlice<'a> {}
+
+impl<'a> MaybeUninitSlice<'a> {
+ pub(crate) fn new(buf: &'a mut [MaybeUninit<u8>]) -> MaybeUninitSlice<'a> {
+ MaybeUninitSlice {
+ vec: libc::iovec {
+ iov_base: buf.as_mut_ptr().cast(),
+ iov_len: buf.len(),
+ },
+ _lifetime: PhantomData,
+ }
+ }
+
+ pub(crate) fn as_slice(&self) -> &[MaybeUninit<u8>] {
+ unsafe { slice::from_raw_parts(self.vec.iov_base.cast(), self.vec.iov_len) }
+ }
+
+ pub(crate) fn as_mut_slice(&mut self) -> &mut [MaybeUninit<u8>] {
+ unsafe { slice::from_raw_parts_mut(self.vec.iov_base.cast(), self.vec.iov_len) }
+ }
+}
+
+/// 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",
+ ));
+ }
+
+ 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(())
+ })
+ }
+ .map(|(_, addr)| addr)
+ }
+}
+
+impl SockAddr {
+ /// Constructs a `SockAddr` with the family `AF_VSOCK` and the provided CID/port.
+ ///
+ /// # Errors
+ ///
+ /// 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.
+ #[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(())
+ })
+ }
+ .map(|(_, addr)| addr)
+ }
+
+ /// Returns this address VSOCK CID/port if it is in the `AF_VSOCK` family,
+ /// otherwise return `None`.
+ #[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_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) };
+ Some((addr.svm_cid, addr.svm_port))
+ } else {
+ None
+ }
+ }
+}
+
+pub(crate) type Socket = c_int;
+
+pub(crate) unsafe fn socket_from_raw(socket: Socket) -> crate::socket::Inner {
+ crate::socket::Inner::from_raw_fd(socket)
+}
+
+pub(crate) fn socket_as_raw(socket: &crate::socket::Inner) -> Socket {
+ socket.as_raw_fd()
+}
+
+pub(crate) fn socket_into_raw(socket: crate::socket::Inner) -> Socket {
+ socket.into_raw_fd()
+}
+
+pub(crate) fn socket(family: c_int, ty: c_int, protocol: c_int) -> io::Result<Socket> {
+ syscall!(socket(family, ty, protocol))
+}
+
+#[cfg(feature = "all")]
+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)
+}
+
+pub(crate) fn bind(fd: Socket, addr: &SockAddr) -> io::Result<()> {
+ syscall!(bind(fd, addr.as_ptr(), addr.len() as _)).map(|_| ())
+}
+
+pub(crate) fn connect(fd: Socket, addr: &SockAddr) -> io::Result<()> {
+ syscall!(connect(fd, addr.as_ptr(), addr.len())).map(|_| ())
+}
+
+pub(crate) fn poll_connect(socket: &crate::Socket, timeout: Duration) -> io::Result<()> {
+ let start = Instant::now();
+
+ let mut pollfd = libc::pollfd {
+ fd: socket.as_raw(),
+ events: libc::POLLIN | libc::POLLOUT,
+ revents: 0,
+ };
+
+ loop {
+ let elapsed = start.elapsed();
+ if elapsed >= timeout {
+ return Err(io::ErrorKind::TimedOut.into());
+ }
+
+ let timeout = (timeout - elapsed).as_millis();
+ let timeout = clamp(timeout, 1, c_int::max_value() as u128) as c_int;
+
+ match syscall!(poll(&mut pollfd, 1, timeout)) {
+ Ok(0) => return Err(io::ErrorKind::TimedOut.into()),
+ Ok(_) => {
+ // 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(None) => {
+ return Err(io::Error::new(
+ io::ErrorKind::Other,
+ "no error set after POLLHUP",
+ ))
+ }
+ Err(err) => return Err(err),
+ }
+ }
+ return Ok(());
+ }
+ // Got interrupted, try again.
+ Err(ref err) if err.kind() == io::ErrorKind::Interrupted => continue,
+ Err(err) => return Err(err),
+ }
+ }
+}
+
+// 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))) }
+}
+
+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))) }
+ .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))) }
+ .map(|(_, addr)| addr)
+}
+
+pub(crate) fn try_clone(fd: Socket) -> io::Result<Socket> {
+ syscall!(fcntl(fd, libc::F_DUPFD_CLOEXEC, 0))
+}
+
+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)
+ } else {
+ fcntl_remove(fd, libc::F_GETFL, libc::F_SETFL, libc::O_NONBLOCK)
+ }
+}
+
+pub(crate) fn shutdown(fd: Socket, how: Shutdown) -> io::Result<()> {
+ let how = match how {
+ Shutdown::Write => libc::SHUT_WR,
+ Shutdown::Read => libc::SHUT_RD,
+ Shutdown::Both => libc::SHUT_RDWR,
+ };
+ syscall!(shutdown(fd, how)).map(|_| ())
+}
+
+pub(crate) fn recv(fd: Socket, buf: &mut [MaybeUninit<u8>], flags: c_int) -> io::Result<usize> {
+ syscall!(recv(
+ fd,
+ buf.as_mut_ptr().cast(),
+ min(buf.len(), MAX_BUF_LEN),
+ flags,
+ ))
+ .map(|n| n as usize)
+}
+
+pub(crate) fn recv_from(
+ fd: Socket,
+ buf: &mut [MaybeUninit<u8>],
+ flags: c_int,
+) -> io::Result<(usize, SockAddr)> {
+ // Safety: `recvfrom` initialises the `SockAddr` for us.
+ unsafe {
+ SockAddr::init(|addr, addrlen| {
+ syscall!(recvfrom(
+ fd,
+ buf.as_mut_ptr().cast(),
+ min(buf.len(), MAX_BUF_LEN),
+ flags,
+ addr.cast(),
+ addrlen
+ ))
+ .map(|n| n as usize)
+ })
+ }
+}
+
+#[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))
+}
+
+#[cfg(not(target_os = "redox"))]
+pub(crate) fn recv_from_vectored(
+ fd: Socket,
+ bufs: &mut [crate::MaybeUninitSlice<'_>],
+ flags: c_int,
+) -> io::Result<(usize, RecvFlags, SockAddr)> {
+ // 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))
+}
+
+/// Returns the (bytes received, sending address len, `RecvFlags`).
+#[cfg(not(target_os = "redox"))]
+fn recvmsg(
+ fd: Socket,
+ msg_name: *mut sockaddr_storage,
+ bufs: &mut [crate::MaybeUninitSlice<'_>],
+ 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)))
+}
+
+pub(crate) fn send(fd: Socket, buf: &[u8], flags: c_int) -> io::Result<usize> {
+ syscall!(send(
+ fd,
+ buf.as_ptr().cast(),
+ min(buf.len(), MAX_BUF_LEN),
+ flags,
+ ))
+ .map(|n| n as 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)
+}
+
+pub(crate) fn send_to(fd: Socket, buf: &[u8], addr: &SockAddr, flags: c_int) -> io::Result<usize> {
+ syscall!(sendto(
+ fd,
+ buf.as_ptr().cast(),
+ min(buf.len(), MAX_BUF_LEN),
+ flags,
+ addr.as_ptr(),
+ addr.len(),
+ ))
+ .map(|n| n as usize)
+}
+
+#[cfg(not(target_os = "redox"))]
+pub(crate) fn send_to_vectored(
+ fd: Socket,
+ bufs: &[IoSlice<'_>],
+ addr: &SockAddr,
+ flags: c_int,
+) -> io::Result<usize> {
+ sendmsg(fd, addr.as_storage_ptr(), addr.len(), bufs, 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)
+}
+
+/// Wrapper around `getsockopt` to deal with platform specific timeouts.
+pub(crate) fn timeout_opt(fd: Socket, opt: c_int, val: c_int) -> io::Result<Option<Duration>> {
+ unsafe { getsockopt(fd, opt, val).map(from_timeval) }
+}
+
+fn from_timeval(duration: libc::timeval) -> Option<Duration> {
+ if duration.tv_sec == 0 && duration.tv_usec == 0 {
+ None
+ } else {
+ let sec = duration.tv_sec as u64;
+ let nsec = (duration.tv_usec as u32) * 1000;
+ Some(Duration::new(sec, nsec))
+ }
+}
+
+/// Wrapper around `setsockopt` to deal with platform specific timeouts.
+pub(crate) fn set_timeout_opt(
+ fd: Socket,
+ opt: c_int,
+ val: c_int,
+ duration: Option<Duration>,
+) -> io::Result<()> {
+ let duration = into_timeval(duration);
+ unsafe { setsockopt(fd, opt, val, duration) }
+}
+
+fn into_timeval(duration: Option<Duration>) -> libc::timeval {
+ match duration {
+ // 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_usec: duration.subsec_micros() as libc::suseconds_t,
+ },
+ None => libc::timeval {
+ tv_sec: 0,
+ tv_usec: 0,
+ },
+ }
+}
+
+#[cfg(feature = "all")]
+#[cfg(not(any(target_os = "haiku", target_os = "openbsd")))]
+pub(crate) fn keepalive_time(fd: Socket) -> io::Result<Duration> {
+ unsafe {
+ getsockopt::<c_int>(fd, IPPROTO_TCP, KEEPALIVE_TIME)
+ .map(|secs| Duration::from_secs(secs as u64))
+ }
+}
+
+#[allow(unused_variables)]
+pub(crate) fn set_tcp_keepalive(fd: Socket, keepalive: &TcpKeepalive) -> io::Result<()> {
+ #[cfg(not(any(target_os = "haiku", target_os = "openbsd")))]
+ if let Some(time) = keepalive.time {
+ let secs = into_secs(time);
+ unsafe { setsockopt(fd, libc::IPPROTO_TCP, KEEPALIVE_TIME, secs)? }
+ }
+
+ #[cfg(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",
+ ))]
+ {
+ if let Some(interval) = keepalive.interval {
+ let secs = into_secs(interval);
+ unsafe { setsockopt(fd, libc::IPPROTO_TCP, libc::TCP_KEEPINTVL, secs)? }
+ }
+
+ if let Some(retries) = keepalive.retries {
+ unsafe { setsockopt(fd, libc::IPPROTO_TCP, libc::TCP_KEEPCNT, retries as c_int)? }
+ }
+ }
+
+ Ok(())
+}
+
+#[cfg(not(any(target_os = "haiku", target_os = "openbsd")))]
+fn into_secs(duration: Duration) -> c_int {
+ min(duration.as_secs(), c_int::max_value() as u64) as c_int
+}
+
+/// Add `flag` to the current set flags of `F_GETFD`.
+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 new = previous | flag;
+ if new != previous {
+ syscall!(fcntl(fd, set_cmd, new)).map(|_| ())
+ } else {
+ // Flag was already set.
+ Ok(())
+ }
+}
+
+/// Remove `flag` to the current set flags of `F_GETFD`.
+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 new = previous & !flag;
+ if new != previous {
+ syscall!(fcntl(fd, set_cmd, new)).map(|_| ())
+ } else {
+ // Flag was already set.
+ Ok(())
+ }
+}
+
+/// Caller must ensure `T` is the correct type for `opt` and `val`.
+pub(crate) unsafe fn getsockopt<T>(fd: Socket, opt: c_int, val: c_int) -> io::Result<T> {
+ let mut payload: MaybeUninit<T> = MaybeUninit::uninit();
+ let mut len = size_of::<T>() as libc::socklen_t;
+ syscall!(getsockopt(
+ fd,
+ opt,
+ val,
+ payload.as_mut_ptr().cast(),
+ &mut len,
+ ))
+ .map(|_| {
+ debug_assert_eq!(len as usize, size_of::<T>());
+ // Safety: `getsockopt` initialised `payload` for us.
+ payload.assume_init()
+ })
+}
+
+/// Caller must ensure `T` is the correct type for `opt` and `val`.
+pub(crate) unsafe fn setsockopt<T>(
+ fd: Socket,
+ opt: c_int,
+ val: c_int,
+ payload: T,
+) -> io::Result<()> {
+ let payload = &payload as *const T as *const c_void;
+ syscall!(setsockopt(
+ fd,
+ opt,
+ val,
+ payload,
+ mem::size_of::<T>() as libc::socklen_t,
+ ))
+ .map(|_| ())
+}
+
+pub(crate) 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.
+ in_addr {
+ s_addr: u32::from_ne_bytes(addr.octets()),
+ }
+}
+
+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 {
+ in6_addr {
+ s6_addr: addr.octets(),
+ }
+}
+
+pub(crate) fn from_in6_addr(addr: in6_addr) -> Ipv6Addr {
+ Ipv6Addr::from(addr.s6_addr)
+}
+
+#[cfg(not(any(
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "redox",
+ target_os = "solaris",
+)))]
+pub(crate) fn to_mreqn(
+ multiaddr: &Ipv4Addr,
+ interface: &crate::socket::InterfaceIndexOrAddress,
+) -> libc::ip_mreqn {
+ match interface {
+ crate::socket::InterfaceIndexOrAddress::Index(interface) => libc::ip_mreqn {
+ imr_multiaddr: to_in_addr(multiaddr),
+ imr_address: to_in_addr(&Ipv4Addr::UNSPECIFIED),
+ imr_ifindex: *interface as _,
+ },
+ crate::socket::InterfaceIndexOrAddress::Address(interface) => libc::ip_mreqn {
+ imr_multiaddr: to_in_addr(multiaddr),
+ imr_address: to_in_addr(interface),
+ imr_ifindex: 0,
+ },
+ }
+}
+
+/// Unix only API.
+impl crate::Socket {
+ /// Accept a new incoming connection from this listener.
+ ///
+ /// This function directly corresponds to the `accept4(2)` function.
+ ///
+ /// 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.
+ #[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_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 = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ )
+ )))
+ )]
+ pub fn accept4(&self, flags: c_int) -> io::Result<(crate::Socket, SockAddr)> {
+ self._accept4(flags)
+ }
+
+ #[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ 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| {
+ syscall!(accept4(self.as_raw(), storage.cast(), len, flags))
+ .map(crate::Socket::from_raw)
+ })
+ }
+ }
+
+ /// Sets `CLOEXEC` on the socket.
+ ///
+ /// # Notes
+ ///
+ /// On supported platforms you can use [`Type::cloexec`].
+ #[cfg(feature = "all")]
+ #[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)
+ }
+
+ pub(crate) fn _set_cloexec(&self, close_on_exec: bool) -> io::Result<()> {
+ if close_on_exec {
+ fcntl_add(
+ self.as_raw(),
+ libc::F_GETFD,
+ libc::F_SETFD,
+ libc::FD_CLOEXEC,
+ )
+ } else {
+ fcntl_remove(
+ self.as_raw(),
+ libc::F_GETFD,
+ libc::F_SETFD,
+ libc::FD_CLOEXEC,
+ )
+ }
+ }
+
+ /// 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"))))]
+ pub fn set_nosigpipe(&self, nosigpipe: bool) -> io::Result<()> {
+ self._set_nosigpipe(nosigpipe)
+ }
+
+ #[cfg(target_vendor = "apple")]
+ pub(crate) fn _set_nosigpipe(&self, nosigpipe: bool) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ libc::SOL_SOCKET,
+ libc::SO_NOSIGPIPE,
+ nosigpipe as c_int,
+ )
+ }
+ }
+
+ /// Gets the value of the `TCP_MAXSEG` option on this socket.
+ ///
+ /// For more information about this option, see [`set_mss`].
+ ///
+ /// [`set_mss`]: crate::Socket::set_mss
+ #[cfg(all(feature = "all", not(target_os = "redox")))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", unix, not(target_os = "redox")))))]
+ pub fn mss(&self) -> io::Result<u32> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), libc::IPPROTO_TCP, libc::TCP_MAXSEG)
+ .map(|mss| mss as u32)
+ }
+ }
+
+ /// Sets the value of the `TCP_MAXSEG` option on this socket.
+ ///
+ /// The `TCP_MAXSEG` option denotes the TCP Maximum Segment Size and is only
+ /// available on TCP sockets.
+ #[cfg(all(feature = "all", not(target_os = "redox")))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", unix, not(target_os = "redox")))))]
+ pub fn set_mss(&self, mss: u32) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ libc::IPPROTO_TCP,
+ libc::TCP_MAXSEG,
+ mss as c_int,
+ )
+ }
+ }
+
+ /// Returns `true` if `listen(2)` was called on this socket by checking the
+ /// `SO_ACCEPTCONN` option on this socket.
+ #[cfg(all(
+ feature = "all",
+ any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "linux",
+ )
+ ))]
+ #[cfg_attr(
+ docsrs,
+ doc(cfg(all(
+ feature = "all",
+ any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "linux",
+ )
+ )))
+ )]
+ pub fn is_listener(&self) -> io::Result<bool> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_ACCEPTCONN)
+ .map(|v| v != 0)
+ }
+ }
+
+ /// Returns the [`Domain`] of this socket by checking the `SO_DOMAIN` option
+ /// on this socket.
+ #[cfg(all(
+ feature = "all",
+ any(
+ target_os = "android",
+ // TODO: add FreeBSD.
+ // target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "linux",
+ )
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all(
+ feature = "all",
+ any(
+ target_os = "android",
+ // TODO: add FreeBSD.
+ // target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "linux",
+ )
+ ))))]
+ pub fn domain(&self) -> io::Result<Domain> {
+ unsafe { getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_DOMAIN).map(Domain) }
+ }
+
+ /// Returns the [`Protocol`] of this socket by checking the `SO_PROTOCOL`
+ /// option on this socket.
+ #[cfg(all(
+ feature = "all",
+ any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "linux",
+ )
+ ))]
+ #[cfg_attr(
+ docsrs,
+ doc(cfg(all(
+ feature = "all",
+ any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "linux",
+ )
+ )))
+ )]
+ pub fn protocol(&self) -> io::Result<Option<Protocol>> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_PROTOCOL).map(|v| match v
+ {
+ 0 => None,
+ p => Some(Protocol(p)),
+ })
+ }
+ }
+
+ /// Gets the value for the `SO_MARK` option on this socket.
+ ///
+ /// This value gets the socket mark field for each packet sent through
+ /// this socket.
+ ///
+ /// On Linux this function requires the `CAP_NET_ADMIN` capability.
+ #[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 mark(&self) -> io::Result<u32> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_MARK)
+ .map(|mark| mark as u32)
+ }
+ }
+
+ /// Sets the value for the `SO_MARK` option on this socket.
+ ///
+ /// This value sets the socket mark field for each packet sent through
+ /// this socket. Changing the mark can be used for mark-based routing
+ /// without netfilter or for packet filtering.
+ ///
+ /// On Linux this function requires the `CAP_NET_ADMIN` capability.
+ #[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 set_mark(&self, mark: u32) -> io::Result<()> {
+ unsafe {
+ setsockopt::<c_int>(
+ self.as_raw(),
+ libc::SOL_SOCKET,
+ libc::SO_MARK,
+ mark as c_int,
+ )
+ }
+ }
+
+ /// Get the value of the `TCP_CORK` option on this socket.
+ ///
+ /// For more information about this option, see [`set_cork`].
+ ///
+ /// [`set_cork`]: Socket::set_cork
+ #[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 cork(&self) -> io::Result<bool> {
+ unsafe {
+ getsockopt::<Bool>(self.as_raw(), libc::IPPROTO_TCP, libc::TCP_CORK)
+ .map(|cork| cork != 0)
+ }
+ }
+
+ /// Set the value of the `TCP_CORK` option on this socket.
+ ///
+ /// If set, don't send out partial frames. All queued partial frames are
+ /// sent when the option is cleared again. There is a 200 millisecond ceiling on
+ /// the time for which output is corked by `TCP_CORK`. If this ceiling is reached,
+ /// then queued data is automatically transmitted.
+ #[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 set_cork(&self, cork: bool) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ libc::IPPROTO_TCP,
+ libc::TCP_CORK,
+ cork as c_int,
+ )
+ }
+ }
+
+ /// Get the value of the `TCP_QUICKACK` option on this socket.
+ ///
+ /// For more information about this option, see [`set_quickack`].
+ ///
+ /// [`set_quickack`]: Socket::set_quickack
+ #[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 quickack(&self) -> io::Result<bool> {
+ unsafe {
+ getsockopt::<Bool>(self.as_raw(), libc::IPPROTO_TCP, libc::TCP_QUICKACK)
+ .map(|quickack| quickack != 0)
+ }
+ }
+
+ /// Set the value of the `TCP_QUICKACK` option on this socket.
+ ///
+ /// If set, acks are sent immediately, rather than delayed if needed in accordance to normal
+ /// TCP operation. This flag is not permanent, it only enables a switch to or from quickack mode.
+ /// Subsequent operation of the TCP protocol will once again enter/leave quickack mode depending on
+ /// internal protocol processing and factors such as delayed ack timeouts occurring and data transfer.
+ #[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 set_quickack(&self, quickack: bool) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ libc::IPPROTO_TCP,
+ libc::TCP_QUICKACK,
+ quickack as c_int,
+ )
+ }
+ }
+
+ /// Get the value of the `TCP_THIN_LINEAR_TIMEOUTS` option on this socket.
+ ///
+ /// For more information about this option, see [`set_thin_linear_timeouts`].
+ ///
+ /// [`set_thin_linear_timeouts`]: Socket::set_thin_linear_timeouts
+ #[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 thin_linear_timeouts(&self) -> io::Result<bool> {
+ unsafe {
+ getsockopt::<Bool>(
+ self.as_raw(),
+ libc::IPPROTO_TCP,
+ libc::TCP_THIN_LINEAR_TIMEOUTS,
+ )
+ .map(|timeouts| timeouts != 0)
+ }
+ }
+
+ /// Set the value of the `TCP_THIN_LINEAR_TIMEOUTS` option on this socket.
+ ///
+ /// If set, the kernel will dynamically detect a thin-stream connection if there are less than four packets in flight.
+ /// With less than four packets in flight the normal TCP fast retransmission will not be effective.
+ /// The kernel will modify the retransmission to avoid the very high latencies that thin stream suffer because of exponential backoff.
+ #[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 set_thin_linear_timeouts(&self, timeouts: bool) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ libc::IPPROTO_TCP,
+ libc::TCP_THIN_LINEAR_TIMEOUTS,
+ timeouts as c_int,
+ )
+ }
+ }
+
+ /// Gets the value for the `SO_BINDTODEVICE` option on this socket.
+ ///
+ /// This value gets the socket binded device's interface name.
+ #[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 device(&self) -> io::Result<Option<Vec<u8>>> {
+ // TODO: replace with `MaybeUninit::uninit_array` once stable.
+ let mut buf: [MaybeUninit<u8>; libc::IFNAMSIZ] =
+ unsafe { MaybeUninit::uninit().assume_init() };
+ let mut len = buf.len() as libc::socklen_t;
+ unsafe {
+ syscall!(getsockopt(
+ self.as_raw(),
+ libc::SOL_SOCKET,
+ libc::SO_BINDTODEVICE,
+ buf.as_mut_ptr().cast(),
+ &mut len,
+ ))?;
+ }
+ if len == 0 {
+ Ok(None)
+ } else {
+ let buf = &buf[..len as usize - 1];
+ // TODO: use `MaybeUninit::slice_assume_init_ref` once stable.
+ Ok(Some(unsafe { &*(buf as *const [_] as *const [u8]) }.into()))
+ }
+ }
+
+ /// Sets the value for the `SO_BINDTODEVICE` option on this socket.
+ ///
+ /// If a socket is bound to an interface, only packets received from that
+ /// particular interface are processed by the socket. Note that this only
+ /// works for some socket types, particularly `AF_INET` sockets.
+ ///
+ /// If `interface` is `None` or an empty string it removes the binding.
+ #[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 bind_device(&self, interface: Option<&[u8]>) -> io::Result<()> {
+ let (value, len) = if let Some(interface) = interface {
+ (interface.as_ptr(), interface.len())
+ } else {
+ (ptr::null(), 0)
+ };
+ syscall!(setsockopt(
+ self.as_raw(),
+ libc::SOL_SOCKET,
+ libc::SO_BINDTODEVICE,
+ value.cast(),
+ len as libc::socklen_t,
+ ))
+ .map(|_| ())
+ }
+
+ /// 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")))))]
+ pub fn set_fib(&self, fib: u32) -> io::Result<()> {
+ syscall!(setsockopt(
+ self.as_raw(),
+ libc::SOL_SOCKET,
+ libc::SO_SETFIB,
+ (&fib as *const u32).cast(),
+ mem::size_of::<u32>() as libc::socklen_t,
+ ))
+ .map(|_| ())
+ }
+
+ /// Sets the value for `IP_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", 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);
+ unsafe { setsockopt(self.as_raw(), IPPROTO_IP, libc::IP_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>> {
+ let index =
+ unsafe { getsockopt::<libc::c_uint>(self.as_raw(), IPPROTO_IP, libc::IP_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`].
+ ///
+ /// [`set_cpu_affinity`]: crate::Socket::set_cpu_affinity
+ #[cfg(all(feature = "all", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
+ pub fn cpu_affinity(&self) -> io::Result<usize> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_INCOMING_CPU)
+ .map(|cpu| cpu as usize)
+ }
+ }
+
+ /// Set value for the `SO_INCOMING_CPU` option on this socket.
+ ///
+ /// Sets the CPU affinity of the socket.
+ #[cfg(all(feature = "all", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
+ pub fn set_cpu_affinity(&self, cpu: usize) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ libc::SOL_SOCKET,
+ libc::SO_INCOMING_CPU,
+ cpu as c_int,
+ )
+ }
+ }
+
+ /// Get the value of the `SO_REUSEPORT` option on this socket.
+ ///
+ /// For more information about this option, see [`set_reuse_port`].
+ ///
+ /// [`set_reuse_port`]: crate::Socket::set_reuse_port
+ #[cfg(all(
+ feature = "all",
+ not(any(target_os = "solaris", target_os = "illumos"))
+ ))]
+ #[cfg_attr(
+ docsrs,
+ doc(cfg(all(
+ feature = "all",
+ unix,
+ not(any(target_os = "solaris", target_os = "illumos"))
+ )))
+ )]
+ pub fn reuse_port(&self) -> io::Result<bool> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_REUSEPORT)
+ .map(|reuse| reuse != 0)
+ }
+ }
+
+ /// Set value for the `SO_REUSEPORT` option on this socket.
+ ///
+ /// This indicates that further calls to `bind` may allow reuse of local
+ /// addresses. For IPv4 sockets this means that a socket may bind even when
+ /// there's a socket already listening on this port.
+ #[cfg(all(
+ feature = "all",
+ not(any(target_os = "solaris", target_os = "illumos"))
+ ))]
+ #[cfg_attr(
+ docsrs,
+ doc(cfg(all(
+ feature = "all",
+ unix,
+ not(any(target_os = "solaris", target_os = "illumos"))
+ )))
+ )]
+ pub fn set_reuse_port(&self, reuse: bool) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ libc::SOL_SOCKET,
+ libc::SO_REUSEPORT,
+ reuse as c_int,
+ )
+ }
+ }
+
+ /// Get the value of the `IP_FREEBIND` option on this socket.
+ ///
+ /// For more information about this option, see [`set_freebind`].
+ ///
+ /// [`set_freebind`]: crate::Socket::set_freebind
+ #[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 freebind(&self) -> io::Result<bool> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), libc::SOL_IP, libc::IP_FREEBIND)
+ .map(|freebind| freebind != 0)
+ }
+ }
+
+ /// Set value for the `IP_FREEBIND` option on this socket.
+ ///
+ /// If enabled, this boolean option allows binding to an IP address that is
+ /// nonlocal or does not (yet) exist. This permits listening on a socket,
+ /// without requiring the underlying network interface or the specified
+ /// dynamic IP address to be up at the time that the application is trying
+ /// to bind to it.
+ #[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 set_freebind(&self, freebind: bool) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ libc::SOL_IP,
+ libc::IP_FREEBIND,
+ freebind as c_int,
+ )
+ }
+ }
+
+ /// Get the value of the `IPV6_FREEBIND` option on this socket.
+ ///
+ /// This is an IPv6 counterpart of `IP_FREEBIND` socket option on
+ /// Android/Linux. For more information about this option, see
+ /// [`set_freebind`].
+ ///
+ /// [`set_freebind`]: crate::Socket::set_freebind
+ #[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 freebind_ipv6(&self) -> io::Result<bool> {
+ unsafe {
+ getsockopt::<c_int>(self.as_raw(), libc::SOL_IPV6, libc::IPV6_FREEBIND)
+ .map(|freebind| freebind != 0)
+ }
+ }
+
+ /// Set value for the `IPV6_FREEBIND` option on this socket.
+ ///
+ /// This is an IPv6 counterpart of `IP_FREEBIND` socket option on
+ /// Android/Linux. For more information about this option, see
+ /// [`set_freebind`].
+ ///
+ /// [`set_freebind`]: crate::Socket::set_freebind
+ ///
+ /// # Examples
+ ///
+ /// On Linux:
+ ///
+ /// ```
+ /// use socket2::{Domain, Socket, Type};
+ /// use std::io::{self, Error, ErrorKind};
+ ///
+ /// fn enable_freebind(socket: &Socket) -> io::Result<()> {
+ /// match socket.domain()? {
+ /// Domain::IPV4 => socket.set_freebind(true)?,
+ /// Domain::IPV6 => socket.set_freebind_ipv6(true)?,
+ /// _ => return Err(Error::new(ErrorKind::Other, "unsupported domain")),
+ /// };
+ /// Ok(())
+ /// }
+ ///
+ /// # fn main() -> io::Result<()> {
+ /// # let socket = Socket::new(Domain::IPV6, Type::STREAM, None)?;
+ /// # enable_freebind(&socket)
+ /// # }
+ /// ```
+ #[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 set_freebind_ipv6(&self, freebind: bool) -> io::Result<()> {
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ libc::SOL_IPV6,
+ libc::IPV6_FREEBIND,
+ freebind as c_int,
+ )
+ }
+ }
+
+ /// 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
+ /// `write(2)`, which would require transferring data to and from user
+ /// space.
+ ///
+ /// 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.
+ ///
+ /// The `offset` is the absolute offset into the `file` to use as starting
+ /// point.
+ ///
+ /// Depending on the OS this function *may* change the offset of `file`. For
+ /// the best results reset the offset of the file before using it again.
+ ///
+ /// The `length` determines how many bytes to send, where a length of `None`
+ /// means it will try to send all bytes.
+ #[cfg(all(
+ feature = "all",
+ any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_vendor = "apple",
+ )
+ ))]
+ #[cfg_attr(
+ docsrs,
+ doc(cfg(all(
+ feature = "all",
+ any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_vendor = "apple",
+ )
+ )))
+ )]
+ pub fn sendfile<F>(
+ &self,
+ file: &F,
+ offset: usize,
+ length: Option<NonZeroUsize>,
+ ) -> io::Result<usize>
+ where
+ F: AsRawFd,
+ {
+ self._sendfile(file.as_raw_fd(), offset as _, length)
+ }
+
+ #[cfg(all(feature = "all", target_vendor = "apple"))]
+ fn _sendfile(
+ &self,
+ file: RawFd,
+ offset: libc::off_t,
+ length: Option<NonZeroUsize>,
+ ) -> io::Result<usize> {
+ // On macOS `length` is value-result parameter. It determines the number
+ // of bytes to write and returns the number of bytes written.
+ let mut length = match length {
+ Some(n) => n.get() as libc::off_t,
+ // A value of `0` means send all bytes.
+ None => 0,
+ };
+ syscall!(sendfile(
+ file,
+ self.as_raw(),
+ offset,
+ &mut length,
+ ptr::null_mut(),
+ 0,
+ ))
+ .map(|_| length as usize)
+ }
+
+ #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
+ fn _sendfile(
+ &self,
+ file: RawFd,
+ offset: libc::off_t,
+ length: Option<NonZeroUsize>,
+ ) -> io::Result<usize> {
+ let count = match length {
+ Some(n) => n.get() as libc::size_t,
+ // The maximum the Linux kernel will write in a single call.
+ None => 0x7ffff000, // 2,147,479,552 bytes.
+ };
+ let mut offset = offset;
+ syscall!(sendfile(self.as_raw(), file, &mut offset, count)).map(|n| n as usize)
+ }
+
+ #[cfg(all(feature = "all", target_os = "freebsd"))]
+ fn _sendfile(
+ &self,
+ file: RawFd,
+ offset: libc::off_t,
+ length: Option<NonZeroUsize>,
+ ) -> io::Result<usize> {
+ let nbytes = match length {
+ Some(n) => n.get() as libc::size_t,
+ // A value of `0` means send all bytes.
+ None => 0,
+ };
+ let mut sbytes: libc::off_t = 0;
+ syscall!(sendfile(
+ file,
+ self.as_raw(),
+ offset,
+ nbytes,
+ ptr::null_mut(),
+ &mut sbytes,
+ 0,
+ ))
+ .map(|_| sbytes 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
+ /// unacknowledged or buffered data may remain untransmitted before TCP will forcibly close the
+ /// corresponding connection.
+ ///
+ /// Setting `timeout` to `None` or a zero duration causes the system default timeouts to
+ /// be used. If `timeout` in milliseconds is larger than `c_uint::MAX`, the timeout is clamped
+ /// to `c_uint::MAX`. For example, when `c_uint` is a 32-bit value, this limits the timeout to
+ /// approximately 49.71 days.
+ #[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 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);
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ libc::IPPROTO_TCP,
+ libc::TCP_USER_TIMEOUT,
+ timeout,
+ )
+ }
+ }
+
+ /// Get the value of the `TCP_USER_TIMEOUT` option on this socket.
+ ///
+ /// For more information about this option, see [`set_tcp_user_timeout`].
+ ///
+ /// [`set_tcp_user_timeout`]: Socket::set_tcp_user_timeout
+ #[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 tcp_user_timeout(&self) -> io::Result<Option<Duration>> {
+ unsafe {
+ getsockopt::<libc::c_uint>(self.as_raw(), libc::IPPROTO_TCP, libc::TCP_USER_TIMEOUT)
+ .map(|millis| {
+ if millis == 0 {
+ None
+ } else {
+ Some(Duration::from_millis(millis as u64))
+ }
+ })
+ }
+ }
+
+ /// Attach Berkeley Packet Filter(BPF) on this socket.
+ ///
+ /// BPF allows a user-space program to attach a filter onto any socket
+ /// and allow or disallow certain types of data to come through the socket.
+ ///
+ /// For more information about this option, see [filter](https://www.kernel.org/doc/html/v5.12/networking/filter.html)
+ #[cfg(all(feature = "all", any(target_os = "linux", target_os = "android")))]
+ pub fn attach_filter(&self, filters: &[libc::sock_filter]) -> io::Result<()> {
+ let prog = libc::sock_fprog {
+ len: filters.len() as u16,
+ filter: filters.as_ptr() as *mut _,
+ };
+
+ unsafe {
+ setsockopt(
+ self.as_raw(),
+ libc::SOL_SOCKET,
+ libc::SO_ATTACH_FILTER,
+ prog,
+ )
+ }
+ }
+
+ /// Detach Berkeley Packet Filter(BPF) from this socket.
+ ///
+ /// For more information about this option, see [`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) }
+ }
+}
+
+#[cfg_attr(docsrs, doc(cfg(unix)))]
+impl AsRawFd for crate::Socket {
+ fn as_raw_fd(&self) -> c_int {
+ self.as_raw()
+ }
+}
+
+#[cfg_attr(docsrs, doc(cfg(unix)))]
+impl IntoRawFd for crate::Socket {
+ fn into_raw_fd(self) -> c_int {
+ self.into_raw()
+ }
+}
+
+#[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)
+ }
+}
+
+#[cfg(feature = "all")]
+from!(UnixStream, crate::Socket);
+#[cfg(feature = "all")]
+from!(UnixListener, crate::Socket);
+#[cfg(feature = "all")]
+from!(UnixDatagram, crate::Socket);
+#[cfg(feature = "all")]
+from!(crate::Socket, UnixStream);
+#[cfg(feature = "all")]
+from!(crate::Socket, UnixListener);
+#[cfg(feature = "all")]
+from!(crate::Socket, UnixDatagram);
+
+#[test]
+fn in_addr_convertion() {
+ let ip = Ipv4Addr::new(127, 0, 0, 1);
+ let raw = to_in_addr(&ip);
+ // NOTE: `in_addr` is packed on NetBSD and it's unsafe to borrow.
+ let a = raw.s_addr;
+ assert_eq!(a, u32::from_ne_bytes([127, 0, 0, 1]));
+ assert_eq!(from_in_addr(raw), ip);
+
+ let ip = Ipv4Addr::new(127, 34, 4, 12);
+ let raw = to_in_addr(&ip);
+ let a = raw.s_addr;
+ assert_eq!(a, u32::from_ne_bytes([127, 34, 4, 12]));
+ assert_eq!(from_in_addr(raw), ip);
+}
+
+#[test]
+fn in6_addr_convertion() {
+ let ip = Ipv6Addr::new(0x2000, 1, 2, 3, 4, 5, 6, 7);
+ let raw = to_in6_addr(&ip);
+ let want = [32, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7];
+ assert_eq!(raw.s6_addr, want);
+ assert_eq!(from_in6_addr(raw), ip);
+}
diff --git a/src/sys/windows.rs b/src/sys/windows.rs
new file mode 100644
index 0000000..ab59839
--- /dev/null
+++ b/src/sys/windows.rs
@@ -0,0 +1,848 @@
+// Copyright 2015 The Rust Project Developers.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::cmp::min;
+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::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 crate::{RecvFlags, SockAddr, TcpKeepalive, Type};
+
+pub(crate) use winapi::ctypes::c_int;
+
+/// Fake MSG_TRUNC flag for the [`RecvFlags`] struct.
+///
+/// The flag is enabled when a `WSARecv[From]` call returns `WSAEMSGSIZE`. The
+/// value of the flag is defined by us.
+pub(crate) const MSG_TRUNC: c_int = 0x01;
+
+// Used in `Domain`.
+pub(crate) use winapi::shared::ws2def::{AF_INET, AF_INET6};
+// 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};
+// 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;
+// Used in `SockAddr`.
+pub(crate) use winapi::shared::ws2def::{
+ ADDRESS_FAMILY as sa_family_t, SOCKADDR as sockaddr, SOCKADDR_IN as sockaddr_in,
+ 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;
+// 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_DROP_MEMBERSHIP, IP_MREQ as IpMreq, IP_MULTICAST_IF, IP_MULTICAST_LOOP, IP_MULTICAST_TTL,
+ IP_TOS, IP_TTL,
+};
+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;
+
+/// Type used in set/getsockopt to retrieve the `TCP_NODELAY` option.
+///
+/// NOTE: <https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-getsockopt>
+/// documents that options such as `TCP_NODELAY` and `SO_KEEPALIVE` expect a
+/// `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;
+
+/// Maximum size of a buffer passed to system call like `recv` and `send`.
+const MAX_BUF_LEN: usize = <c_int>::max_value() 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, )*) };
+ if $err_test(&res, &$err_value) {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(res)
+ }
+ }};
+}
+
+impl_debug!(
+ crate::Domain,
+ ws2def::AF_INET,
+ ws2def::AF_INET6,
+ ws2def::AF_UNIX,
+ ws2def::AF_UNSPEC, // = 0.
+);
+
+/// Windows only API.
+impl Type {
+ /// Our custom flag to set `WSA_FLAG_NO_HANDLE_INHERIT` on socket creation.
+ /// Trying to mimic `Type::cloexec` on windows.
+ const NO_INHERIT: c_int = 1 << ((size_of::<c_int>() * 8) - 1); // Last bit.
+
+ /// Set `WSA_FLAG_NO_HANDLE_INHERIT` on the socket.
+ #[cfg(feature = "all")]
+ #[cfg_attr(docsrs, doc(cfg(all(windows, feature = "all"))))]
+ pub const fn no_inherit(self) -> Type {
+ self._no_inherit()
+ }
+
+ pub(crate) const fn _no_inherit(self) -> Type {
+ Type(self.0 | Type::NO_INHERIT)
+ }
+}
+
+impl_debug!(
+ crate::Type,
+ ws2def::SOCK_STREAM,
+ ws2def::SOCK_DGRAM,
+ ws2def::SOCK_RAW,
+ ws2def::SOCK_RDM,
+ ws2def::SOCK_SEQPACKET,
+);
+
+impl_debug!(
+ crate::Protocol,
+ self::IPPROTO_ICMP,
+ self::IPPROTO_ICMPV6,
+ self::IPPROTO_TCP,
+ self::IPPROTO_UDP,
+);
+
+impl std::fmt::Debug for RecvFlags {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("RecvFlags")
+ .field("is_truncated", &self.is_truncated())
+ .finish()
+ }
+}
+
+#[repr(transparent)]
+pub struct MaybeUninitSlice<'a> {
+ vec: WSABUF,
+ _lifetime: PhantomData<&'a mut [MaybeUninit<u8>]>,
+}
+
+unsafe impl<'a> Send for MaybeUninitSlice<'a> {}
+
+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);
+ MaybeUninitSlice {
+ vec: WSABUF {
+ len: buf.len() as ULONG,
+ buf: buf.as_mut_ptr().cast(),
+ },
+ _lifetime: PhantomData,
+ }
+ }
+
+ pub fn as_slice(&self) -> &[MaybeUninit<u8>] {
+ unsafe { slice::from_raw_parts(self.vec.buf.cast(), self.vec.len as usize) }
+ }
+
+ pub fn as_mut_slice(&mut self) -> &mut [MaybeUninit<u8>] {
+ unsafe { slice::from_raw_parts_mut(self.vec.buf.cast(), self.vec.len as usize) }
+ }
+}
+
+fn init() {
+ static INIT: Once = Once::new();
+
+ INIT.call_once(|| {
+ // Initialize winsock through the standard library by just creating a
+ // dummy socket. Whether this is successful or not we drop the result as
+ // libstd will be sure to have initialized winsock.
+ let _ = net::UdpSocket::bind("127.0.0.1:34254");
+ });
+}
+
+pub(crate) type Socket = sock::SOCKET;
+
+pub(crate) unsafe fn socket_from_raw(socket: Socket) -> crate::socket::Inner {
+ crate::socket::Inner::from_raw_socket(socket as RawSocket)
+}
+
+pub(crate) fn socket_as_raw(socket: &crate::socket::Inner) -> Socket {
+ socket.as_raw_socket() as Socket
+}
+
+pub(crate) fn socket_into_raw(socket: crate::socket::Inner) -> Socket {
+ socket.into_raw_socket() as Socket
+}
+
+pub(crate) fn socket(family: c_int, mut ty: c_int, protocol: c_int) -> io::Result<Socket> {
+ init();
+
+ // 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
+ } else {
+ 0
+ };
+
+ syscall!(
+ WSASocketW(
+ family,
+ ty,
+ protocol,
+ ptr::null_mut(),
+ 0,
+ sock::WSA_FLAG_OVERLAPPED | flags,
+ ),
+ PartialEq::eq,
+ sock::INVALID_SOCKET
+ )
+}
+
+pub(crate) fn bind(socket: Socket, addr: &SockAddr) -> io::Result<()> {
+ syscall!(bind(socket, addr.as_ptr(), addr.len()), PartialEq::ne, 0).map(|_| ())
+}
+
+pub(crate) fn connect(socket: Socket, addr: &SockAddr) -> io::Result<()> {
+ syscall!(connect(socket, addr.as_ptr(), addr.len()), PartialEq::ne, 0).map(|_| ())
+}
+
+pub(crate) fn poll_connect(socket: &crate::Socket, timeout: Duration) -> io::Result<()> {
+ let start = Instant::now();
+
+ let mut fd_array = WSAPOLLFD {
+ fd: socket.as_raw(),
+ events: POLLRDNORM | POLLWRNORM,
+ revents: 0,
+ };
+
+ loop {
+ let elapsed = start.elapsed();
+ if elapsed >= timeout {
+ return Err(io::ErrorKind::TimedOut.into());
+ }
+
+ let timeout = (timeout - elapsed).as_millis();
+ let timeout = clamp(timeout, 1, c_int::max_value() as u128) as c_int;
+
+ match syscall!(
+ WSAPoll(&mut fd_array, 1, timeout),
+ PartialEq::eq,
+ sock::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 {
+ match socket.take_error() {
+ Ok(Some(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(());
+ }
+ // Got interrupted, try again.
+ Err(ref err) if err.kind() == io::ErrorKind::Interrupted => continue,
+ Err(err) => return Err(err),
+ }
+ }
+}
+
+// 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(socket: Socket, backlog: c_int) -> io::Result<()> {
+ syscall!(listen(socket, backlog), PartialEq::ne, 0).map(|_| ())
+}
+
+pub(crate) fn accept(socket: Socket) -> io::Result<(Socket, SockAddr)> {
+ // Safety: `accept` initialises the `SockAddr` for us.
+ unsafe {
+ SockAddr::init(|storage, len| {
+ syscall!(
+ accept(socket, storage.cast(), len),
+ PartialEq::eq,
+ sock::INVALID_SOCKET
+ )
+ })
+ }
+}
+
+pub(crate) fn getsockname(socket: Socket) -> io::Result<SockAddr> {
+ // Safety: `getsockname` initialises the `SockAddr` for us.
+ unsafe {
+ SockAddr::init(|storage, len| {
+ syscall!(
+ getsockname(socket, storage.cast(), len),
+ PartialEq::eq,
+ sock::SOCKET_ERROR
+ )
+ })
+ }
+ .map(|(_, addr)| addr)
+}
+
+pub(crate) fn getpeername(socket: Socket) -> io::Result<SockAddr> {
+ // Safety: `getpeername` initialises the `SockAddr` for us.
+ unsafe {
+ SockAddr::init(|storage, len| {
+ syscall!(
+ getpeername(socket, storage.cast(), len),
+ PartialEq::eq,
+ sock::SOCKET_ERROR
+ )
+ })
+ }
+ .map(|(_, addr)| addr)
+}
+
+pub(crate) fn try_clone(socket: Socket) -> io::Result<Socket> {
+ let mut info: MaybeUninit<sock::WSAPROTOCOL_INFOW> = MaybeUninit::uninit();
+ syscall!(
+ WSADuplicateSocketW(socket, GetCurrentProcessId(), info.as_mut_ptr()),
+ PartialEq::eq,
+ sock::SOCKET_ERROR
+ )?;
+ // Safety: `WSADuplicateSocketW` intialised `info` for us.
+ let mut info = unsafe { info.assume_init() };
+
+ syscall!(
+ WSASocketW(
+ info.iAddressFamily,
+ info.iSocketType,
+ info.iProtocol,
+ &mut info,
+ 0,
+ sock::WSA_FLAG_OVERLAPPED | sock::WSA_FLAG_NO_HANDLE_INHERIT,
+ ),
+ PartialEq::eq,
+ sock::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)
+}
+
+pub(crate) fn shutdown(socket: Socket, how: Shutdown) -> io::Result<()> {
+ let how = match how {
+ Shutdown::Write => SD_SEND,
+ Shutdown::Read => SD_RECEIVE,
+ Shutdown::Both => SD_BOTH,
+ };
+ syscall!(shutdown(socket, how), PartialEq::eq, sock::SOCKET_ERROR).map(|_| ())
+}
+
+pub(crate) fn recv(socket: Socket, buf: &mut [MaybeUninit<u8>], flags: c_int) -> io::Result<usize> {
+ let res = syscall!(
+ recv(
+ socket,
+ buf.as_mut_ptr().cast(),
+ min(buf.len(), MAX_BUF_LEN) as c_int,
+ flags,
+ ),
+ PartialEq::eq,
+ sock::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(err) => Err(err),
+ }
+}
+
+pub(crate) fn recv_vectored(
+ socket: Socket,
+ bufs: &mut [crate::MaybeUninitSlice<'_>],
+ flags: c_int,
+) -> io::Result<(usize, RecvFlags)> {
+ let mut nread = 0;
+ let mut flags = flags as DWORD;
+ let res = syscall!(
+ WSARecv(
+ socket,
+ bufs.as_mut_ptr().cast(),
+ min(bufs.len(), DWORD::max_value() as usize) as DWORD,
+ &mut nread,
+ &mut flags,
+ ptr::null_mut(),
+ None,
+ ),
+ PartialEq::eq,
+ sock::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) => {
+ Ok((nread as usize, RecvFlags(MSG_TRUNC)))
+ }
+ Err(err) => Err(err),
+ }
+}
+
+pub(crate) fn recv_from(
+ socket: Socket,
+ buf: &mut [MaybeUninit<u8>],
+ flags: c_int,
+) -> io::Result<(usize, SockAddr)> {
+ // Safety: `recvfrom` initialises the `SockAddr` for us.
+ unsafe {
+ SockAddr::init(|storage, addrlen| {
+ let res = syscall!(
+ recvfrom(
+ socket,
+ buf.as_mut_ptr().cast(),
+ min(buf.len(), MAX_BUF_LEN) as c_int,
+ flags,
+ storage.cast(),
+ addrlen,
+ ),
+ PartialEq::eq,
+ sock::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(err) => Err(err),
+ }
+ })
+ }
+}
+
+pub(crate) fn recv_from_vectored(
+ socket: Socket,
+ bufs: &mut [crate::MaybeUninitSlice<'_>],
+ flags: c_int,
+) -> io::Result<(usize, RecvFlags, SockAddr)> {
+ // Safety: `recvfrom` initialises the `SockAddr` for us.
+ unsafe {
+ SockAddr::init(|storage, addrlen| {
+ let mut nread = 0;
+ let mut flags = flags as DWORD;
+ let res = syscall!(
+ WSARecvFrom(
+ socket,
+ bufs.as_mut_ptr().cast(),
+ min(bufs.len(), DWORD::max_value() as usize) as DWORD,
+ &mut nread,
+ &mut flags,
+ storage.cast(),
+ addrlen,
+ ptr::null_mut(),
+ None,
+ ),
+ PartialEq::eq,
+ sock::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((nread as usize, RecvFlags(0)))
+ }
+ Err(ref err) if err.raw_os_error() == Some(sock::WSAEMSGSIZE as i32) => {
+ Ok((nread as usize, RecvFlags(MSG_TRUNC)))
+ }
+ Err(err) => Err(err),
+ }
+ })
+ }
+ .map(|((n, recv_flags), addr)| (n, recv_flags, addr))
+}
+
+pub(crate) fn send(socket: Socket, buf: &[u8], flags: c_int) -> io::Result<usize> {
+ syscall!(
+ send(
+ socket,
+ buf.as_ptr().cast(),
+ min(buf.len(), MAX_BUF_LEN) as c_int,
+ flags,
+ ),
+ PartialEq::eq,
+ sock::SOCKET_ERROR
+ )
+ .map(|n| n as usize)
+}
+
+pub(crate) fn send_vectored(
+ socket: Socket,
+ bufs: &[IoSlice<'_>],
+ flags: c_int,
+) -> io::Result<usize> {
+ let mut nsent = 0;
+ syscall!(
+ WSASend(
+ socket,
+ // FIXME: From the `WSASend` docs [1]:
+ // > For a Winsock application, once the WSASend function is called,
+ // > the system owns these buffers and the application may not
+ // > access them.
+ //
+ // So what we're doing is actually UB as `bufs` needs to be `&mut
+ // [IoSlice<'_>]`.
+ //
+ // Tracking issue: https://github.com/rust-lang/socket2-rs/issues/129.
+ //
+ // NOTE: `send_to_vectored` has the same problem.
+ //
+ // [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,
+ &mut nsent,
+ flags as DWORD,
+ std::ptr::null_mut(),
+ None,
+ ),
+ PartialEq::eq,
+ sock::SOCKET_ERROR
+ )
+ .map(|_| nsent as usize)
+}
+
+pub(crate) fn send_to(
+ socket: Socket,
+ buf: &[u8],
+ addr: &SockAddr,
+ flags: c_int,
+) -> io::Result<usize> {
+ syscall!(
+ sendto(
+ socket,
+ buf.as_ptr().cast(),
+ min(buf.len(), MAX_BUF_LEN) as c_int,
+ flags,
+ addr.as_ptr(),
+ addr.len(),
+ ),
+ PartialEq::eq,
+ sock::SOCKET_ERROR
+ )
+ .map(|n| n as usize)
+}
+
+pub(crate) fn send_to_vectored(
+ socket: Socket,
+ bufs: &[IoSlice<'_>],
+ addr: &SockAddr,
+ flags: c_int,
+) -> io::Result<usize> {
+ let mut nsent = 0;
+ syscall!(
+ WSASendTo(
+ socket,
+ // FIXME: Same problem as in `send_vectored`.
+ bufs.as_ptr() as *mut _,
+ bufs.len().min(DWORD::MAX as usize) as DWORD,
+ &mut nsent,
+ flags as DWORD,
+ addr.as_ptr(),
+ addr.len(),
+ ptr::null_mut(),
+ None,
+ ),
+ PartialEq::eq,
+ sock::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>> {
+ unsafe { getsockopt(fd, lvl, name).map(from_ms) }
+}
+
+fn from_ms(duration: DWORD) -> Option<Duration> {
+ if duration == 0 {
+ None
+ } else {
+ let secs = duration / 1000;
+ let nsec = (duration % 1000) * 1000000;
+ Some(Duration::new(secs as u64, nsec as u32))
+ }
+}
+
+/// Wrapper around `setsockopt` to deal with platform specific timeouts.
+pub(crate) fn set_timeout_opt(
+ fd: Socket,
+ level: c_int,
+ optname: c_int,
+ duration: Option<Duration>,
+) -> io::Result<()> {
+ let duration = into_ms(duration);
+ unsafe { setsockopt(fd, level, optname, duration) }
+}
+
+fn into_ms(duration: Option<Duration>) -> DWORD {
+ // 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:
+ //
+ // * 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)
+}
+
+pub(crate) fn set_tcp_keepalive(socket: Socket, keepalive: &TcpKeepalive) -> io::Result<()> {
+ let mut keepalive = tcp_keepalive {
+ onoff: 1,
+ keepalivetime: into_ms(keepalive.time),
+ keepaliveinterval: into_ms(keepalive.interval),
+ };
+ let mut out = 0;
+ syscall!(
+ WSAIoctl(
+ socket,
+ SIO_KEEPALIVE_VALS,
+ &mut keepalive as *mut _ as *mut _,
+ size_of::<tcp_keepalive>() as _,
+ ptr::null_mut(),
+ 0,
+ &mut out,
+ ptr::null_mut(),
+ None,
+ ),
+ PartialEq::eq,
+ sock::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> {
+ let mut optval: MaybeUninit<T> = MaybeUninit::uninit();
+ let mut optlen = mem::size_of::<T>() as c_int;
+ syscall!(
+ getsockopt(
+ socket,
+ level,
+ optname,
+ optval.as_mut_ptr().cast(),
+ &mut optlen,
+ ),
+ PartialEq::eq,
+ sock::SOCKET_ERROR
+ )
+ .map(|_| {
+ debug_assert_eq!(optlen as usize, mem::size_of::<T>());
+ // Safety: `getsockopt` initialised `optval` for us.
+ optval.assume_init()
+ })
+}
+
+/// Caller must ensure `T` is the correct type for `level` and `optname`.
+pub(crate) unsafe fn setsockopt<T>(
+ socket: Socket,
+ level: c_int,
+ optname: c_int,
+ optval: T,
+) -> io::Result<()> {
+ syscall!(
+ setsockopt(
+ socket,
+ level,
+ optname,
+ (&optval as *const T).cast(),
+ mem::size_of::<T>() as c_int,
+ ),
+ PartialEq::eq,
+ sock::SOCKET_ERROR
+ )
+ .map(|_| ())
+}
+
+fn ioctlsocket(socket: Socket, cmd: c_long, payload: &mut u_long) -> io::Result<()> {
+ syscall!(
+ ioctlsocket(socket, cmd, payload),
+ PartialEq::eq,
+ sock::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 }
+}
+
+pub(crate) fn from_in_addr(in_addr: IN_ADDR) -> Ipv4Addr {
+ 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 from_in6_addr(addr: in6_addr) -> Ipv6Addr {
+ Ipv6Addr::from(*unsafe { addr.u.Byte() })
+}
+
+pub(crate) fn to_mreqn(
+ multiaddr: &Ipv4Addr,
+ interface: &crate::socket::InterfaceIndexOrAddress,
+) -> IpMreq {
+ IpMreq {
+ imr_multiaddr: to_in_addr(multiaddr),
+ // Per https://docs.microsoft.com/en-us/windows/win32/api/ws2ipdef/ns-ws2ipdef-ip_mreq#members:
+ //
+ // imr_interface
+ //
+ // The local IPv4 address of the interface or the interface index on
+ // which the multicast group should be joined or dropped. This value is
+ // in network byte order. If this member specifies an IPv4 address of
+ // 0.0.0.0, the default IPv4 multicast interface is used.
+ //
+ // To use an interface index of 1 would be the same as an IP address of
+ // 0.0.0.1.
+ imr_interface: match interface {
+ crate::socket::InterfaceIndexOrAddress::Index(interface) => {
+ to_in_addr(&(*interface).into())
+ }
+ crate::socket::InterfaceIndexOrAddress::Address(interface) => to_in_addr(interface),
+ },
+ }
+}
+
+/// Windows only API.
+impl crate::Socket {
+ /// Sets `HANDLE_FLAG_INHERIT` using `SetHandleInformation`.
+ #[cfg(feature = "all")]
+ #[cfg_attr(docsrs, doc(cfg(all(windows, feature = "all"))))]
+ pub fn set_no_inherit(&self, no_inherit: bool) -> io::Result<()> {
+ self._set_no_inherit(no_inherit)
+ }
+
+ 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.
+ let res = unsafe {
+ SetHandleInformation(
+ self.as_raw() as HANDLE,
+ winbase::HANDLE_FLAG_INHERIT,
+ !no_inherit as _,
+ )
+ };
+ if res == 0 {
+ // Zero means error.
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(())
+ }
+ }
+}
+
+impl AsRawSocket for crate::Socket {
+ fn as_raw_socket(&self) -> RawSocket {
+ self.as_raw() as RawSocket
+ }
+}
+
+impl IntoRawSocket for crate::Socket {
+ fn into_raw_socket(self) -> RawSocket {
+ self.into_raw() as RawSocket
+ }
+}
+
+impl FromRawSocket for crate::Socket {
+ unsafe fn from_raw_socket(socket: RawSocket) -> crate::Socket {
+ crate::Socket::from_raw(socket as Socket)
+ }
+}
+
+#[test]
+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!(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() },
+ 127 << 0 | 34 << 8 | 4 << 16 | 12 << 24
+ );
+ assert_eq!(from_in_addr(raw), ip);
+}
+
+#[test]
+fn in6_addr_convertion() {
+ let ip = Ipv6Addr::new(0x2000, 1, 2, 3, 4, 5, 6, 7);
+ let raw = to_in6_addr(&ip);
+ let want = [
+ 0x2000u16.to_be(),
+ 1u16.to_be(),
+ 2u16.to_be(),
+ 3u16.to_be(),
+ 4u16.to_be(),
+ 5u16.to_be(),
+ 6u16.to_be(),
+ 7u16.to_be(),
+ ];
+ assert_eq!(unsafe { *raw.u.Word() }, want);
+ assert_eq!(from_in6_addr(raw), ip);
+}