├── .cirrus.yml ├── .github └── workflows │ └── main.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── Makefile ├── README.md ├── src ├── lib.rs ├── sockaddr.rs ├── socket.rs ├── sockref.rs └── sys │ ├── unix.rs │ └── windows.rs └── tests ├── data ├── hello_world.txt └── lorem_ipsum.txt └── socket.rs /.cirrus.yml: -------------------------------------------------------------------------------- 1 | freebsd_instance: 2 | image: freebsd-13-2-release-amd64 3 | 4 | env: 5 | RUST_BACKTRACE: full 6 | 7 | task: 8 | name: FreeBSD 9 | setup_script: 10 | - fetch https://sh.rustup.rs -o rustup.sh 11 | - sh rustup.sh -y --profile minimal 12 | cargo_cache: 13 | folder: $HOME/.cargo/registry 14 | build_script: 15 | - . $HOME/.cargo/env 16 | - cargo build 17 | - cargo build --no-default-features 18 | amd64_test_script: 19 | - . $HOME/.cargo/env 20 | - cargo test --all-features 21 | i386_test_script: 22 | - . $HOME/.cargo/env 23 | - rustup target add i686-unknown-freebsd 24 | - cargo test --target i686-unknown-freebsd --all-features 25 | before_cache_script: 26 | - rm -rf $HOME/.cargo/registry/index 27 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: [ master, "v0.4.x" ] 5 | pull_request: 6 | branches: [ master, "v0.4.x" ] 7 | env: 8 | CARGO_TERM_COLOR: always 9 | RUST_BACKTRACE: full 10 | jobs: 11 | Test: 12 | name: Test 13 | runs-on: ${{ matrix.os }} 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | build: [stable, 1.63.0, beta, nightly, macos, windows] 18 | include: 19 | - build: stable 20 | os: ubuntu-latest 21 | rust: stable 22 | - build: 1.63.0 23 | os: ubuntu-latest 24 | rust: 1.63.0 25 | - build: beta 26 | os: ubuntu-latest 27 | rust: beta 28 | - build: nightly 29 | os: ubuntu-latest 30 | rust: nightly 31 | - build: macos 32 | os: macos-latest 33 | rust: stable 34 | - build: windows 35 | os: windows-latest 36 | rust: stable 37 | steps: 38 | - uses: actions/checkout@v4 39 | - uses: dtolnay/rust-toolchain@master 40 | with: 41 | toolchain: ${{ matrix.rust }} 42 | - uses: taiki-e/install-action@cargo-hack 43 | - name: Run tests 44 | run: cargo hack test --feature-powerset && cargo hack test --feature-powerset --release 45 | Rustfmt: 46 | name: Rustfmt 47 | runs-on: ubuntu-latest 48 | steps: 49 | - uses: actions/checkout@v4 50 | - uses: dtolnay/rust-toolchain@stable 51 | - name: Check formatting 52 | run: cargo fmt --all -- --check 53 | Check: 54 | runs-on: ubuntu-latest 55 | timeout-minutes: 10 56 | strategy: 57 | fail-fast: false 58 | matrix: 59 | target: 60 | - aarch64-apple-darwin 61 | - aarch64-apple-ios 62 | - aarch64-apple-tvos 63 | # Broken, see https://github.com/rust-lang/socket2/issues/538. 64 | #- aarch64-apple-visionos 65 | - aarch64-apple-watchos 66 | - aarch64-linux-android 67 | - aarch64-unknown-freebsd 68 | - aarch64-unknown-linux-gnu 69 | - aarch64-unknown-linux-musl 70 | - aarch64-unknown-linux-ohos 71 | - aarch64-unknown-netbsd 72 | - aarch64-unknown-openbsd 73 | - aarch64-unknown-redox 74 | - arm-linux-androideabi 75 | - arm64_32-apple-watchos 76 | - armv7-linux-androideabi 77 | - armv7-sony-vita-newlibeabihf 78 | - armv7-unknown-linux-ohos 79 | - i686-linux-android 80 | # Broken, see https://github.com/rust-lang/socket2/issues/539. 81 | #- i686-unknown-hurd-gnu 82 | - i686-unknown-linux-gnu 83 | - sparcv9-sun-solaris 84 | - x86_64-apple-darwin 85 | - x86_64-apple-ios 86 | - x86_64-pc-cygwin 87 | - x86_64-pc-solaris 88 | # Fails with: 89 | # `rror calling dlltool 'x86_64-w64-mingw32-dlltool': No such file or 90 | # directory (os error 2)`, build log: 91 | # . 92 | #- x86_64-pc-windows-gnu 93 | - x86_64-pc-windows-msvc 94 | - x86_64-unknown-dragonfly 95 | - x86_64-unknown-freebsd 96 | - x86_64-unknown-fuchsia 97 | - x86_64-unknown-illumos 98 | - x86_64-unknown-linux-gnu 99 | - x86_64-unknown-linux-musl 100 | - x86_64-unknown-linux-ohos 101 | - x86_64-unknown-netbsd 102 | - x86_64-unknown-openbsd 103 | - x86_64-unknown-redox 104 | steps: 105 | - uses: actions/checkout@v4 106 | - uses: dtolnay/rust-toolchain@nightly 107 | with: 108 | components: rust-src 109 | - uses: taiki-e/install-action@cargo-hack 110 | - name: Check build 111 | run: cargo hack check -Z build-std=std,panic_abort --feature-powerset --target ${{ matrix.target }} 112 | - name: Check docs 113 | run: RUSTDOCFLAGS="-D warnings --cfg docsrs" cargo doc -Z build-std=std,panic_abort --no-deps --all-features --target ${{ matrix.target }} 114 | Clippy: 115 | name: Clippy 116 | runs-on: ubuntu-latest 117 | steps: 118 | - uses: actions/checkout@v4 119 | - uses: dtolnay/rust-toolchain@stable 120 | - name: Run Clippy 121 | run: cargo clippy --all-targets --all-features -- -D warnings 122 | CheckExternalTypes: 123 | name: check-external-types (${{ matrix.os }}) 124 | runs-on: ${{ matrix.os }} 125 | strategy: 126 | fail-fast: false 127 | matrix: 128 | os: 129 | - windows-latest 130 | - ubuntu-latest 131 | - macos-latest 132 | rust: 133 | # `check-external-types` requires a specific Rust nightly version. See 134 | # the README for details: https://github.com/awslabs/cargo-check-external-types 135 | - nightly-2024-06-30 136 | steps: 137 | - uses: actions/checkout@v4 138 | - name: Install Rust ${{ matrix.rust }} 139 | uses: dtolnay/rust-toolchain@stable 140 | with: 141 | toolchain: ${{ matrix.rust }} 142 | - name: Install cargo-check-external-types 143 | uses: taiki-e/cache-cargo-install-action@v1 144 | with: 145 | tool: cargo-check-external-types@0.1.13 146 | locked: true 147 | - name: check-external-types 148 | run: cargo check-external-types --all-features 149 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | 5 | .vscode/ 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.5.10 2 | 3 | * Add cygwin support 4 | (https://github.com/rust-lang/socket2/pull/568, 5 | https://github.com/rust-lang/socket2/pull/578). 6 | 7 | # 0.5.9 8 | 9 | * Enable `IP_BOUND_IF` on illumos and Solaris 10 | (https://github.com/rust-lang/socket2/pull/561, 11 | https://github.com/rust-lang/socket2/pull/566). 12 | 13 | # 0.5.8 14 | 15 | * Added `Socket::(set_)header_included_v4` and 16 | `Socket::(set_)header_included_v6` 17 | (https://github.com/rust-lang/socket2/pull/518). 18 | * Added support for `Socket::original_dst` and 19 | `Socket::original_dst_ipv6` on Windows 20 | (https://github.com/rust-lang/socket2/pull/529). 21 | 22 | # 0.5.7 23 | 24 | * Added `Socket::(set_)passcred` 25 | (https://github.com/rust-lang/socket2/pull/506). 26 | * Added `RecvFlags::is_confirm` and `RecvFlags::is_dontroute` 27 | (https://github.com/rust-lang/socket2/pull/499). 28 | * Added `MsgHdrMut::control_len` 29 | (https://github.com/rust-lang/socket2/pull/505). 30 | 31 | # 0.5.6 32 | 33 | * Added `Socket::(set_)multicast_all_v{4,6}` 34 | (https://github.com/rust-lang/socket2/pull/485 and 35 | https://github.com/rust-lang/socket2/pull/486). 36 | * Added support for GNU/Hurd 37 | (https://github.com/rust-lang/socket2/pull/474). 38 | * Fixes compilation on Haiku 39 | (https://github.com/rust-lang/socket2/pull/479 and 40 | https://github.com/rust-lang/socket2/pull/482). 41 | * Fixes compilation on OpenHarmony 42 | (https://github.com/rust-lang/socket2/pull/491). 43 | * Update to window-sys v0.52 44 | (https://github.com/rust-lang/socket2/pull/480). 45 | 46 | # 0.5.5 47 | 48 | * Added support for Vita 49 | (https://github.com/rust-lang/socket2/pull/465). 50 | 51 | # 0.5.4 52 | 53 | * Deprecated `Socket::(bind_)device_by_index`, replaced by 54 | `Socket::(bind_)device_by_index_v4` for IPv4 sockets 55 | (https://github.com/rust-lang/socket2/pull/432). 56 | * Added `Socket::(bind_)device_by_index_v6` 57 | (https://github.com/rust-lang/socket2/pull/432). 58 | * Added experimental support for the ESP-IDF framework 59 | (https://github.com/rust-lang/socket2/pull/452) 60 | * Added `Socket::{send,recv}msg` and `MsgHdr(Mut)` types, wrapping `sendmsg(2)` 61 | and `recvmsg(2)` 62 | (https://github.com/rust-lang/socket2/pull/447). 63 | * Added `Socket::(set_)reuse_port_lb` to retrieve or set `SO_REUSEPORT_LB` on 64 | FreeBSD 65 | (https://github.com/rust-lang/socket2/pull/442). 66 | * Added `Protocol::DIVERT` on FreeBSD and OpenBSD 67 | (https://github.com/rust-lang/socket2/pull/448). 68 | * Added `Socket::protocol` for Windows (using `WSAPROTOCOL_INFOW`) 69 | (https://github.com/rust-lang/socket2/pull/470). 70 | * `From` for `SockAddr ` nows sets `ss_len` on platforms that 71 | have the fields (most BSDs) 72 | (https://github.com/rust-lang/socket2/pull/469). 73 | * Change Windows to use `ADDRESS_FAMILY` for `sa_family_t`, this shouldn't 74 | affect anything in practice 75 | (https://github.com/rust-lang/socket2/pull/463). 76 | 77 | # 0.5.3 78 | 79 | * Added support for two new Android targets `armv7-linux-androideabi` and 80 | `i686-linux-android` (https://github.com/rust-lang/socket2/pull/434). 81 | * Added `Socket::cookie` to retrieve `SO_COOKIE` on Linux 82 | (https://github.com/rust-lang/socket2/pull/437). 83 | 84 | # 0.5.2 85 | 86 | * Added Unix socket methods to `SockAddr` 87 | (https://github.com/rust-lang/socket2/pull/403 and 88 | https://github.com/rust-lang/socket2/pull/429). 89 | * Added `SockAddr::as_storage` 90 | (https://github.com/rust-lang/socket2/pull/417). 91 | * Added `SockAddr::set_length` 92 | (https://github.com/rust-lang/socket2/pull/428). 93 | * Added `Protocol::UDPLITE` 94 | (https://github.com/rust-lang/socket2/pull/427). 95 | * Update windows-sys to 0.48 96 | (https://github.com/rust-lang/socket2/pull/422). 97 | * Fixes Fuchsia target after it changes in 1.68, see 98 | 99 | (https://github.com/rust-lang/socket2/pull/423). 100 | * Fixes musl target and adds it to the CI 101 | (https://github.com/rust-lang/socket2/pull/426). 102 | 103 | # 0.5.1 104 | 105 | ## Added 106 | 107 | * `Type::cloexec` for Redox and Solaris 108 | (https://github.com/rust-lang/socket2/pull/398). 109 | * Generate documentation for more targets on docs.rs 110 | (https://github.com/rust-lang/socket2/pull/398). 111 | 112 | ## Fixed 113 | 114 | * Generatation of documentation on docs.rs 115 | (https://github.com/rust-lang/socket2/pull/398). 116 | 117 | # 0.5.0 118 | 119 | ## Changed 120 | 121 | * **BREAKING** `SockAddr::init` is renamed to `try_init` to indicate it can fail 122 | (https://github.com/rust-lang/socket2/pull/328). 123 | * **BREAKING** Remove the returned `Result` from `SockAddr::vsock`, it can't 124 | fail (https://github.com/rust-lang/socket2/pull/328). 125 | * **BREAKING** `From` is now implemented using the I/O traits `AsFd` and 126 | `AsRawSocket` 127 | (https://github.com/rust-lang/socket2/pull/325): 128 | * **BREAKING** renamed `SockAddr::vsock_addr` `SockAddr::as_vsock_addr` to match 129 | the IPv4 and IPv6 methods 130 | (https://github.com/rust-lang/socket2/pull/334). 131 | * Redox now works on a stable compiler 132 | (https://github.com/rust-lang/socket2/pull/326). 133 | * Remove copy from `From` implementation for `SockAddr` 134 | (https://github.com/rust-lang/socket2/pull/335). 135 | * Marked function as constant where possible. 136 | * Updated to Rust edition 2021 137 | (https://github.com/rust-lang/socket2/pull/393). 138 | 139 | ## Added 140 | 141 | * Links to OS documentation to a lot of methods 142 | (https://github.com/rust-lang/socket2/pull/319). 143 | * I/O-safety traits (https://github.com/rust-lang/socket2/pull/325): 144 | * `AsFd` for `Socket` (Unix only). 145 | * `From` for `Socket` (Unix only). 146 | * `From` for `OwnedFd` (Unix only). 147 | * `AsSocket` for `Socket` (Windows only). 148 | * `From` for `Socket` (Windows only). 149 | * `From` for `OwnedSocket` (Windows only). 150 | * Unix socket support on Windows 151 | (https://github.com/rust-lang/socket2/pull/249). 152 | * `SockAddr::is_ipv{4,6}` and `SockAddr::domain` 153 | (https://github.com/rust-lang/socket2/pull/334). 154 | * `Socket::nonblocking` 155 | (https://github.com/rust-lang/socket2/pull/348). 156 | * `Socket::original_dst(_ipv6)` 157 | (https://github.com/rust-lang/socket2/pull/360). 158 | * `Socket::(set_)recv_tclass_v6` and `Socket::(set_)tclass_v6` 159 | (https://github.com/rust-lang/socket2/pull/364). 160 | * `Socket::(set_)tcp_congestion` 161 | (https://github.com/rust-lang/socket2/pull/371). 162 | * Support for various DCCP socket options in the form of 163 | (https://github.com/rust-lang/socket2/pull/359): 164 | * `Socket::(set_)dccp_service` 165 | * `Socket::dccp_available_ccids` 166 | * `Socket::dccp_qpolicy_txqlen` 167 | * `Socket::dccp_recv_cscov` 168 | * `Socket::dccp_send_cscov` 169 | * `Socket::dccp_server_timewait` 170 | * `Socket::dccp_server_timewait` 171 | * `Socket::dccp_tx_ccid` 172 | * `Socket::dccp_xx_ccid` 173 | * `Socket::set_dccp_ccid` 174 | * `Socket::set_dccp_qpolicy_txqlen` 175 | * `Socket::set_dccp_recv_cscov` 176 | * `Socket::set_dccp_send_cscov` 177 | * `Socket::set_dccp_server_timewait` 178 | * `Socket::dccp_cur_mps` 179 | * `Socket::peek_send` 180 | (https://github.com/rust-lang/socket2/pull/389). 181 | * `Protocol::MPTCP` 182 | (https://github.com/rust-lang/socket2/pull/349). 183 | * `Protocol::SCTP` 184 | (https://github.com/rust-lang/socket2/pull/356). 185 | * `Protocol::DCCP` 186 | (https://github.com/rust-lang/socket2/pull/359). 187 | * `Type::DCCP` 188 | (https://github.com/rust-lang/socket2/pull/359). 189 | * Implement `Eq` and `Hash` for `SockAddr` 190 | (https://github.com/rust-lang/socket2/pull/374). 191 | * Support for QNX Neutrino 192 | (https://github.com/rust-lang/socket2/pull/380). 193 | * Support for AIX 194 | (https://github.com/rust-lang/socket2/pull/351). 195 | 196 | # 0.4.10 197 | 198 | * Fixed compilation with the `all` on QNX Neutrino 199 | (https://github.com/rust-lang/socket2/pull/419). 200 | * Added support for ESP-IDF 201 | (https://github.com/rust-lang/socket2/pull/455). 202 | * Added support for Vita 203 | (https://github.com/rust-lang/socket2/pull/475). 204 | 205 | # 0.4.9 206 | 207 | * Fixed compilation on Windows 208 | (https://github.com/rust-lang/socket2/pull/409). 209 | 210 | # 0.4.8 (yanked) 211 | 212 | This release was broken for Windows. 213 | 214 | * Added `Socket::peek_sender` (backport) 215 | (https://github.com/rust-lang/socket2/pull/404). 216 | 217 | # 0.4.7 218 | 219 | * Fixes compilation on OpenBSD 220 | (https://github.com/rust-lang/socket2/pull/344). 221 | * Fixes compilation on DragonFlyBSD 222 | (https://github.com/rust-lang/socket2/pull/342). 223 | 224 | # 0.4.6 225 | 226 | * Reverted back to the `winapi` dependency as switch to `windows-sys` was a 227 | breaking change (https://github.com/rust-lang/socket2/pull/340). 228 | Note that we'll will switch to `windows-sys` in v0.5 . 229 | * Disable RECVTOS on OpenBSD 230 | (https://github.com/rust-lang/socket2/pull/307). 231 | * Derive Clone for SockAddr 232 | (https://github.com/rust-lang/socket2/pull/311). 233 | * Fixes cfg attributes for Fuchsia 234 | (https://github.com/rust-lang/socket2/pull/314). 235 | 236 | # 0.4.5 (yanked) 237 | 238 | ## Changed 239 | 240 | * Replace `winapi` dependency with `windows-sys` 241 | (https://github.com/rust-lang/socket2/pull/303). 242 | 243 | ## Added 244 | 245 | * `Socket::join_ssm_v4` and `Socket::leave_ssm_v4` 246 | (https://github.com/rust-lang/socket2/pull/298). 247 | * `Socket::set_recv_tos` and `Socket::recv_tos` 248 | (https://github.com/rust-lang/socket2/pull/299). 249 | 250 | ## Fixed 251 | 252 | * OpenBSD build 253 | (https://github.com/rust-lang/socket2/pull/291). 254 | 255 | # 0.4.4 256 | 257 | ## Fixed 258 | 259 | * Libc v0.2.114 fixed an issue where `ip_mreqn` where was not defined for Linux 260 | s390x. 261 | 262 | # 0.4.3 (yanked) 263 | 264 | ## Added 265 | 266 | * `Socket::set_fib`: sets `SO_SETFIB` (https://github.com/rust-lang/socket2/pull/271). 267 | * `Socket::attach_filter`, `SO_ATTACH_FILTER` (https://github.com/rust-lang/socket2/commit/6601ed132b37d6e9d178b34918bfb0b236800232). 268 | * `Socket::detach_filter`, `SO_DETACH_FILTER` (https://github.com/rust-lang/socket2/commit/6601ed132b37d6e9d178b34918bfb0b236800232). 269 | * `Socket::{header_included, set_header_included}`: sets or gets `IP_HDRINCL` 270 | (https://github.com/rust-lang/socket2/commit/f9e882ee53c0b4e89c5043b6d709af95c9db5599). 271 | * `Socket::{cork, set_cork}`: sets or gets `TCP_CORK` 272 | (https://github.com/rust-lang/socket2/commit/50f31f18aac8fd6ef277df2906adeeed9fa391de). 273 | * `Socket::{quickack, set_quickack}`: sets or gets `TCP_QUICKACK` 274 | (https://github.com/rust-lang/socket2/commit/849eee2abc5d5170d2d3bc635386a2ba13b04530). 275 | * `Socket::{thin_linear_timeouts, set_thin_linear_timeouts}`: sets or gets 276 | `TCP_THIN_LINEAR_TIMEOUTS` 277 | (https://github.com/rust-lang/socket2/commit/24c231ca463a17f51e53e7a554c7915a95bdbcc7). 278 | * `Socket::{join_multicast_v4_n, leave_multicast_v4_n}`: extends the existing 279 | multicast API by allowing an index to be used (in addition to an address) 280 | (https://github.com/rust-lang/socket2/commit/750f83618b967c620bbfdf6ca04de7362bdb42b5). 281 | 282 | # 0.4.2 283 | 284 | ## Added 285 | 286 | * `Socket::(set_)freebind_ipv6`, getter and setter for `IPV6_FREEBIND`. 287 | 288 | ## Fixed 289 | 290 | * Compilation on OpenBSD. 291 | * Usage of incorrect flag in `Socket::(set_)freebind`. 292 | 293 | # 0.4.1 294 | 295 | ## Added 296 | 297 | * Added `SockAddr::new` 298 | * Support for `TCP_USER_TIMEOUT`. 299 | * Support for `IP_BOUND_IF`. 300 | * Support for `IP_TRANSPARENT`. 301 | * Enable `Socket::type` on all platforms. 302 | * Support for uclibc (for Haiku support). 303 | * Added DragonFly support for TCP keepalive (`KEEPINTVL`/`KEEPCNT`). 304 | * Documentation for proper use of `SockRef::from`, and the improper use. 305 | * Assertion in `SockRef::from` to ensure the raw socket valid. 306 | 307 | ## Fixed 308 | 309 | * Compilation on Haiku. 310 | * Setting TCP keepalive on Haiku and OpenBSD (by not setting it as it's not 311 | supported). 312 | * Size check for abstract namespaces in `SockAddr::unix`. 313 | * Set noinherit on accepted sockets on Windows when opening sockets. 314 | 315 | # 0.4.0 316 | 317 | ## Added 318 | 319 | * New `all` feature: enables API that is not available on all tier 1 platforms. 320 | * `SockRef` type: used to create a reference to an existing socket, e.g. 321 | `std::net::TcpStream`, making all methods of `Socket` available on it. 322 | * Support for vectored I/O: 323 | * `Socket::recv_vectored`, `Socket::recv_with_flags`. 324 | * `Socket::recv_from_vectored`, `Socket::recv_from_vectored_with_flags`. 325 | * `Socket::send_vectored`, `Socket::send_vectored_with_flags`. 326 | * `Socket::send_to_vectored`, `Socket::send_to_vectored_with_flags`. 327 | * In the `Read` and `Write` implementations. 328 | * `Socket::new_raw`, `Socket::pair_raw` and `Socket::accept_raw` methods 329 | that don't set common flags, such as the close-on-exec flag. 330 | * `Socket::accept4`: `accept4(2)` system call. 331 | * `Socket::sendfile`: the `sendfile(2)` system call. 332 | * `Socket::set_cloexec`: set the close-on-exec flag on Unix. 333 | * `Socket::set_no_inherit`: set inherit handle flag on Windows. 334 | * `Socket::set_nosigpipe`: set `SO_NOSIGPIPE` on Apple targets. 335 | * `Socket::set_mark` and `Socket::mark`, setting/getting the `SO_MARK` socket 336 | option. 337 | * `Socket::set_cpu_affinity` and `Socket::cpu_affinity`, setting/getting the 338 | `SO_INCOMING_CPU` socket option. 339 | * `Socket::set_mss` and `Socket::mss`, setting/getting the `TCP_MAXSEG` socket 340 | option. 341 | * `Socket::set_freebind` and `Socket::freebind`, setting/getting the 342 | `IP_FREEBIND` socket option. 343 | * `Socket::bind_device` and `Socket::device`, setting/getting the 344 | `SO_BINDTODEVICE` socket option. 345 | * Adopted Mio's TCP keepalive API: 346 | * `Socket::keepalive_time`, 347 | * `Socket::keepalive_interval`, 348 | * `Socket::keepalive_retries`, 349 | * `Socket::set_tcp_keepalive`. 350 | * `Socket::is_listener` getting the `SO_ACCEPTCONN` socket option. 351 | * `Socket::domain` getting the `SO_DOMAIN` socket option. 352 | * `Socket::protocol` getting the `SO_PROTOCOL` socket option. 353 | * `Socket::type` getting the `SO_TYPE` socket option. 354 | * `Domain::for_address`: the correct `Domain` for a `std::net::SocketAddr`. 355 | * `Type::nonblocking`: set `SOCK_NONBLOCK`. 356 | * `Type::cloexec`: set `SOCK_CLOEXEC`. 357 | * `Type::no_inherit`: set `HANDLE_FLAG_INHERIT`. 358 | * `SockAddr::init`: initialises a `SockAddr`. 359 | * `MaybeUninitSlice` type: a version of `IoSliceMut` that allows the buffer to 360 | be uninitialised, used in `Socket::recv_vectored` and related functions. 361 | * `RecvFlags` type: provides additional information about incoming messages, 362 | returned by `Socket::recv_vectored` and related functions. 363 | * `TcpKeepalive` type: configuration type for a socket's TCP keepalive 364 | parameters. 365 | 366 | 367 | ## Changed 368 | 369 | * Repository moved to . 370 | * **BREAKING:** Changed constructor functions into constants: 371 | * `Domain::ipv4` => `Domain::IPV4`. 372 | * `Domain::ipv6` => `Domain::IPV4`. 373 | * `Domain::unix` => `Domain::UNIX`. 374 | * `Domain::packet` => `Domain::PACKET`. 375 | * `Type::stream` => `Type::STREAM`. 376 | * `Type::dgram` => `Type::DGRAM`. 377 | * `Type::seqpacket` => `Type::SEQPACKET`. 378 | * `Type::raw` => `Type::RAW`. 379 | * `Protocol::icmpv4` => `Protocol::ICMPV4`. 380 | * `Protocol::icmpv6` => `Protocol::ICMPV6`. 381 | * `Protocol::tcp` => `Protocol::TCP`. 382 | * `Protocol::udp` => `Protocol::UDP`. 383 | * **BREAKING:** Changed the signature of `Socket::recv`, `Socket::recv_vectored` 384 | and related methods to accept unitialised buffers. The `Read` implementation 385 | can be used to read into initialised buffers. 386 | * **BREAKING:** Renamed `SockAddr::as_std` to `as_socket`. 387 | * **BREAKING:** Renamed `SockAddr::as_inet` to `as_socket_ipv4`. 388 | * **BREAKING:** Renamed `SockAddr::as_inet6` to `as_socket_ipv6`. 389 | * **BREAKING:** Replace all previously existing features (reuseport, pair, unix) 390 | with a new all features (see above for description of the all feature). 391 | * Use `accept4(2)` with `SOCK_CLOEXEC` in `Socket::accept`, reducing the amount 392 | of system calls required. 393 | * Marked many functions as constant. 394 | * The `Read` implementation now calls `recv(2)` rather than `read(2)`. 395 | * Split the `impl` block for the `Socket` type to create groupings for setting 396 | and getting different level socket options using 397 | `setsockopt(2)`/`getsockopt(2)`. 398 | * Updated `winapi` depdency to version 0.3.9 and dropped unused features. 399 | 400 | ## Removed 401 | 402 | * Removed the `-rs` suffix from the repository name. 403 | * **BREAKING:** Removed `SockAddr::from_raw_parts`, use `SockAddr::init` instead. 404 | * **BREAKING:** Removed `Socket::into_*` functions and replaced them with a `From` 405 | implementation: 406 | * `Socket::into_tcp_stream` => `TcpStream::from(socket)`. 407 | * `Socket::into_tcp_listener` => `TcpListener::from(socket)`. 408 | * `Socket::into_udp_socket` => `UdpSocket::from(socket)`. 409 | * `Socket::into_unix_stream` => `UnixStream::from(socket)`. 410 | * `Socket::into_unix_listener` => `UnixListener::from(socket)`. 411 | * `Socket::into_unix_datagram` => `UnixDatagram::from(socket)`. 412 | * Removed `cfg-if` dependency. 413 | * Remove `redox_syscall` depdency. 414 | 415 | ## Fixes 416 | 417 | * Fixes the Andoid, Fuchsia, Haiku, iOS, illumos, NetBSD and Redox (nightly 418 | only) targets. 419 | * Correctly call `recv_from` in `Socket::recv_from_with_flags` (called `recv` 420 | previously). 421 | * Correctly call `send_to` in `Socket::send_to_with_flags` (called `recv` 422 | previously). 423 | * Use correct inmutable references in `Socket::send_with_flags` and 424 | `Socket::send_out_of_band`. 425 | * Use `IPPROTO_IPV6` in `Socket::join_multicast_v6` on Windows. 426 | * Use `c_int` instead of `i32` where appropriate. 427 | 428 | ## From v0.4.0-alpha.1 to v0.4.0-alpha.2 429 | 430 | * Fixes the Fuchsia target. 431 | * `Socket::device` now returns a `Vec` rather than `CString`. 432 | * `Socket::bind_device` now accepts a `&[u8]` rather than `&CStr`. 433 | 434 | ## From v0.4.0-alpha.2 to v0.4.0-alpha.3 435 | 436 | * `Socket::connect_timeout` was added back. 437 | 438 | ## From v0.4.0-alpha.4 to v0.4.0-alpha.5 439 | 440 | * Changed `Socket::set_cpu_affinity` and `Socket::cpu_affinity` to use an 441 | immutable reference. 442 | 443 | ## From v0.4.0-alpha.5 to v0.4.0 444 | 445 | * Use `SO_LINGER_SEC` on macOS for `Socket::get/set_linger`. 446 | 447 | # 0.3.16 448 | 449 | * Don't assume the memory layout of `std::net::SocketAddr`. 450 | * Other changes omited 451 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Socket2 2 | 3 | There are many ways to contribute to Socket2, including (but not limited to) 4 | answering questions, adding new features, fixing bugs or expanding the 5 | documentation. This document will focus on adding new features and fixing bugs. 6 | 7 | If you're adding a new feature please first [open an issue] laying out what 8 | would be added and a design you're proposing. Doing this before actually writing 9 | the code will save you time if others suggest improvements to the design. Once 10 | there is some consensus on 1) the feature is a good addition to Socket2 and 2) 11 | the proposed design is the right one, open a [pull request] with the changes. 12 | 13 | If you're working on fixing a bug please say so on the specific issue so that 14 | two people don't work on fixing the same bug. For more complex bugs or fixes 15 | please also share your proposed design on the issue tracker, same as for new 16 | features. 17 | 18 | Once you're working on the code it's useful to understand where the code is (or 19 | should) be located, the [code structure section] describes how the code of 20 | Socket2 is organised. 21 | 22 | To make sure we don't create the same bug again, or to ensure that new features 23 | keep working, please add (regression) test for the changes you've made to the 24 | code. The [testing section] below describes how to run the tests and where to 25 | add new tests. 26 | 27 | [open an issue]: https://github.com/rust-lang/socket2/issues/new 28 | [pull request]: https://github.com/rust-lang/socket2/compare 29 | [code structure section]: #code-structure 30 | [testing section]: #testing 31 | 32 | # Code structure 33 | 34 | All types and methods that are available on all tier 1 platforms are defined in 35 | the first level of the source, i.e. `src/*.rs` files. Additional API that is 36 | platform specific, e.g. `Domain::VSOCK`, is defined in `src/sys/*.rs` and only 37 | for the platforms that support it. For API that is not available on all tier 1 38 | platforms the `all` feature is used, to indicate to the user that they're using 39 | API that might is not available on all platforms. 40 | 41 | The main `Socket` type is defined in `src/socket.rs` with additional methods 42 | defined in the `src/sys/*.rs` files, as per above. The methods on 43 | `Socket` are split into multiple `impl` blocks. The first `impl` block contains 44 | a collection of system calls for creating and using the socket, e.g. 45 | `socket(2)`, `bind(2)`, `listen(2)`, etc. The other implementation blocks are 46 | for getting and setting socket options on various levels, e.g. `SOL_SOCKET`, 47 | where each block contains a single level. The methods in these block are sorted 48 | based on the option name, e.g. `IP_ADD_MEMBERSHIP` rather than 49 | `join_multicast_v4`. Finally the last block contains platforms specific methods 50 | such as `Socket::freebind` which is (at the time of writing) only available on 51 | Android, Linux and Fuchsia, which is defined in the `src/sys/*.rs` files. 52 | 53 | Other types are mostly defined in `src/lib.rs`, except for `SockAddr` and 54 | `SockRef` which have their own file. These types follow the same structure as 55 | `Socket`, where OS specific methods are defined in `src/sys/*.rs`, e.g. 56 | `Type::cloexec`. 57 | 58 | # Testing 59 | 60 | Testing Socket2 is as simple as running `cargo test --all-features`. 61 | 62 | However Socket2 supports a good number of OSs and features. If you want to 63 | test/check all those combinations it's easier to use the [Makefile]. Using `make 64 | test_all` it will check all supported OS targets and all combinations of 65 | supported features. Note that this requires [cargo-hack] and various rustup 66 | targets to be installed. Cargo-hack must be installed manually, the various 67 | targets can be installed automatically using `make install_targets` (which uses 68 | [rustup]). 69 | 70 | [Makefile]: ./Makefile 71 | [cargo-hack]: https://crates.io/crates/cargo-hack 72 | [rustup]: https://rustup.rs 73 | 74 | ## Adding a test 75 | 76 | Tests should be added to `tests/socket.rs`, following (roughly) the same order 77 | in which the methods are defined on a type. At the bottom of this file it has a 78 | macro to create a simple get/set socket option test, more complex API however 79 | needs a manually written test. 80 | 81 | Tests that need to use internal API can be defined directly at the bottom of the 82 | source file. No need for a test module since we intend on keeping the number of 83 | internal tests low. 84 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "socket2" 3 | version = "0.6.0" 4 | publish = false # In development. 5 | authors = [ 6 | "Alex Crichton ", 7 | "Thomas de Zeeuw " 8 | ] 9 | license = "MIT OR Apache-2.0" 10 | readme = "README.md" 11 | repository = "https://github.com/rust-lang/socket2" 12 | homepage = "https://github.com/rust-lang/socket2" 13 | documentation = "https://docs.rs/socket2" 14 | description = """ 15 | Utilities for handling networking sockets with a maximal amount of configuration 16 | possible intended. 17 | """ 18 | keywords = ["io", "socket", "network"] 19 | categories = ["api-bindings", "network-programming"] 20 | edition = "2021" 21 | rust-version = "1.63" 22 | include = [ 23 | "Cargo.toml", 24 | "LICENSE-APACHE", 25 | "LICENSE-MIT", 26 | "README.md", 27 | "src/**/*.rs", 28 | ] 29 | 30 | [package.metadata.docs.rs] 31 | all-features = true 32 | default-target = "x86_64-unknown-linux-gnu" 33 | targets = [ 34 | "aarch64-apple-ios", 35 | "aarch64-linux-android", 36 | "armv7-linux-androideabi", 37 | "i686-linux-android", 38 | "x86_64-apple-darwin", 39 | "x86_64-pc-solaris", 40 | "x86_64-pc-windows-msvc", 41 | "x86_64-unknown-freebsd", 42 | "x86_64-unknown-fuchsia", 43 | "x86_64-unknown-illumos", 44 | "x86_64-unknown-linux-gnu", 45 | "x86_64-unknown-linux-musl", 46 | "x86_64-unknown-netbsd", 47 | "x86_64-unknown-redox", 48 | ] 49 | 50 | [package.metadata.playground] 51 | features = ["all"] 52 | 53 | [target."cfg(unix)".dependencies] 54 | libc = "0.2.172" 55 | 56 | [target.'cfg(windows)'.dependencies.windows-sys] 57 | version = "0.59" 58 | features = [ 59 | "Win32_Foundation", 60 | "Win32_Networking_WinSock", 61 | "Win32_System_IO", 62 | "Win32_System_Threading", 63 | "Win32_System_WindowsProgramming", 64 | ] 65 | 66 | [features] 67 | # Enable all API, even ones not available on all OSs. 68 | all = [] 69 | 70 | [package.metadata.cargo_check_external_types] 71 | allowed_external_types = [ 72 | # Referenced via a type alias. 73 | "libc::c_int", 74 | "libc::primitives::c_int", # libc::c_int isn't always detected. 75 | "libc::socklen_t", 76 | "libc::*::socklen_t", # libc::socklen_t isn't always detected. 77 | "libc::sa_family_t", 78 | "libc::*::sa_family_t", # libc::sa_family_t is always detected. 79 | "windows_sys::Win32::Networking::WinSock::socklen_t", 80 | "windows_sys::Win32::Networking::WinSock::ADDRESS_FAMILY", 81 | ] 82 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Alex Crichton 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Targets available via Rustup that are supported. 2 | # NOTE: keep in sync with the CI and docs.rs targets. 3 | TARGETS ?= "aarch64-apple-ios" "aarch64-linux-android" "x86_64-apple-darwin" "x86_64-unknown-fuchsia" "x86_64-pc-windows-msvc" "x86_64-pc-solaris" "x86_64-unknown-freebsd" "x86_64-unknown-illumos" "x86_64-unknown-linux-gnu" "x86_64-unknown-linux-musl" "x86_64-unknown-netbsd" "x86_64-unknown-redox" "armv7-linux-androideabi" "i686-linux-android" 4 | 5 | test: 6 | cargo test --all-features 7 | 8 | # Test everything for the current OS/architecture and check all targets in 9 | # $TARGETS. 10 | test_all: check_all_targets 11 | cargo hack test --feature-powerset 12 | cargo hack test --feature-powerset --release 13 | 14 | # Check all targets using all features. 15 | check_all_targets: $(TARGETS) 16 | $(TARGETS): 17 | cargo hack check --feature-powerset --all-targets --examples --bins --tests --target $@ 18 | 19 | # Installs all required targets for `check_all_targets`. 20 | install_targets: 21 | rustup target add $(TARGETS) 22 | 23 | # NOTE: when using this command you might want to change the `test` target to 24 | # only run a subset of the tests you're actively working on. 25 | dev: 26 | find src/ tests/ Makefile Cargo.toml | entr -d -c $(MAKE) test 27 | 28 | clean: 29 | cargo clean 30 | 31 | .PHONY: test test_all check_all_targets $(TARGETS) install_targets dev clean 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Socket2 2 | 3 | Socket2 is a crate that provides utilities for creating and using sockets. 4 | 5 | The goal of this crate is to create and use a socket using advanced 6 | configuration options (those that are not available in the types in the standard 7 | library) without using any unsafe code. 8 | 9 | This crate provides as direct as possible access to the system's functionality 10 | for sockets, this means little effort to provide cross-platform utilities. It is 11 | up to the user to know how to use sockets when using this crate. *If you don't 12 | know how to create a socket using libc/system calls then this crate is not for 13 | you*. Most, if not all, functions directly relate to the equivalent system call 14 | with no error handling applied, so no handling errors such as `EINTR`. As a 15 | result using this crate can be a little wordy, but it should give you maximal 16 | flexibility over configuration of sockets. 17 | 18 | See the [API documentation] for more. 19 | 20 | [API documentation]: https://docs.rs/socket2 21 | 22 | # Branches 23 | 24 | Currently Socket2 supports two versions: v0.5 and v0.4. Version 0.5 is being 25 | developed in the master branch. Version 0.4 is developed in the [v0.4.x branch] 26 | branch. 27 | 28 | [v0.4.x branch]: https://github.com/rust-lang/socket2/tree/v0.4.x 29 | 30 | # OS support 31 | 32 | Socket2 attempts to support the same OS/architectures as Rust does, see 33 | https://doc.rust-lang.org/nightly/rustc/platform-support.html. However this is 34 | not always possible, below is current list of support OSs. 35 | 36 | *If your favorite OS is not on the list consider contributing it! See [issue 37 | #78].* 38 | 39 | [issue #78]: https://github.com/rust-lang/socket2/issues/78 40 | 41 | ### Tier 1 42 | 43 | These OSs are tested with each commit in the CI and must always pass the tests. 44 | All functions/types/etc., excluding ones behind the `all` feature, must work on 45 | these OSs. 46 | 47 | * Linux 48 | * macOS 49 | * Windows 50 | 51 | ### Tier 2 52 | 53 | These OSs are currently build in the CI, but not tested. Not all 54 | functions/types/etc. may work on these OSs, even ones **not** behind the `all` 55 | feature flag. 56 | 57 | * Android 58 | * FreeBSD 59 | * Fuchsia 60 | * iOS 61 | * illumos 62 | * NetBSD 63 | * Redox 64 | * Solaris 65 | * OpenHarmony 66 | 67 | # Minimum Supported Rust Version (MSRV) 68 | 69 | Socket2 uses 1.63.0 as MSRV. 70 | 71 | # License 72 | 73 | This project is licensed under either of 74 | 75 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or 76 | https://www.apache.org/licenses/LICENSE-2.0) 77 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or 78 | https://opensource.org/licenses/MIT) 79 | 80 | at your option. 81 | 82 | ### Contribution 83 | 84 | Unless you explicitly state otherwise, any contribution intentionally submitted 85 | for inclusion in this project by you, as defined in the Apache-2.0 license, 86 | shall be dual licensed as above, without any additional terms or conditions. 87 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Rust Project Developers. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | #![allow(clippy::needless_lifetimes)] 10 | 11 | //! Utilities for creating and using sockets. 12 | //! 13 | //! The goal of this crate is to create and use a socket using advanced 14 | //! configuration options (those that are not available in the types in the 15 | //! standard library) without using any unsafe code. 16 | //! 17 | //! This crate provides as direct as possible access to the system's 18 | //! functionality for sockets, this means little effort to provide 19 | //! cross-platform utilities. It is up to the user to know how to use sockets 20 | //! when using this crate. *If you don't know how to create a socket using 21 | //! libc/system calls then this crate is not for you*. Most, if not all, 22 | //! functions directly relate to the equivalent system call with no error 23 | //! handling applied, so no handling errors such as [`EINTR`]. As a result using 24 | //! this crate can be a little wordy, but it should give you maximal flexibility 25 | //! over configuration of sockets. 26 | //! 27 | //! [`EINTR`]: std::io::ErrorKind::Interrupted 28 | //! 29 | //! # Examples 30 | //! 31 | //! ```no_run 32 | //! # fn main() -> std::io::Result<()> { 33 | //! use std::net::{SocketAddr, TcpListener}; 34 | //! use socket2::{Socket, Domain, Type}; 35 | //! 36 | //! // Create a TCP listener bound to two addresses. 37 | //! let socket = Socket::new(Domain::IPV6, Type::STREAM, None)?; 38 | //! 39 | //! socket.set_only_v6(false)?; 40 | //! let address: SocketAddr = "[::1]:12345".parse().unwrap(); 41 | //! socket.bind(&address.into())?; 42 | //! socket.listen(128)?; 43 | //! 44 | //! let listener: TcpListener = socket.into(); 45 | //! // ... 46 | //! # drop(listener); 47 | //! # Ok(()) } 48 | //! ``` 49 | //! 50 | //! ## Features 51 | //! 52 | //! This crate has a single feature `all`, which enables all functions even ones 53 | //! that are not available on all OSs. 54 | 55 | #![deny(missing_docs, missing_debug_implementations, rust_2018_idioms)] 56 | // Automatically generate required OS/features for docs.rs. 57 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 58 | // Disallow warnings when running tests. 59 | #![cfg_attr(test, deny(warnings))] 60 | // Disallow warnings in examples. 61 | #![doc(test(attr(deny(warnings))))] 62 | 63 | use std::fmt; 64 | #[cfg(not(target_os = "redox"))] 65 | use std::io::IoSlice; 66 | #[cfg(not(target_os = "redox"))] 67 | use std::marker::PhantomData; 68 | #[cfg(not(target_os = "redox"))] 69 | use std::mem; 70 | use std::mem::MaybeUninit; 71 | use std::net::SocketAddr; 72 | use std::ops::{Deref, DerefMut}; 73 | use std::time::Duration; 74 | 75 | /// Macro to implement `fmt::Debug` for a type, printing the constant names 76 | /// rather than a number. 77 | /// 78 | /// Note this is used in the `sys` module and thus must be defined before 79 | /// defining the modules. 80 | macro_rules! impl_debug { 81 | ( 82 | // Type name for which to implement `fmt::Debug`. 83 | $type: path, 84 | $( 85 | $(#[$target: meta])* 86 | // The flag(s) to check. 87 | // Need to specific the libc crate because Windows doesn't use 88 | // `libc` but `windows_sys`. 89 | $libc: ident :: $flag: ident 90 | ),+ $(,)* 91 | ) => { 92 | impl std::fmt::Debug for $type { 93 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 94 | let string = match self.0 { 95 | $( 96 | $(#[$target])* 97 | $libc :: $flag => stringify!($flag), 98 | )+ 99 | n => return write!(f, "{n}"), 100 | }; 101 | f.write_str(string) 102 | } 103 | } 104 | }; 105 | } 106 | 107 | /// Macro to convert from one network type to another. 108 | macro_rules! from { 109 | ($from: ty, $for: ty) => { 110 | impl From<$from> for $for { 111 | fn from(socket: $from) -> $for { 112 | #[cfg(unix)] 113 | unsafe { 114 | <$for>::from_raw_fd(socket.into_raw_fd()) 115 | } 116 | #[cfg(windows)] 117 | unsafe { 118 | <$for>::from_raw_socket(socket.into_raw_socket()) 119 | } 120 | } 121 | } 122 | }; 123 | } 124 | 125 | /// Link to online documentation for (almost) all supported OSs. 126 | #[rustfmt::skip] 127 | macro_rules! man_links { 128 | // Links to all OSs. 129 | ($syscall: tt ( $section: tt ) ) => { 130 | concat!( 131 | man_links!(__ intro), 132 | man_links!(__ unix $syscall($section)), 133 | man_links!(__ windows $syscall($section)), 134 | ) 135 | }; 136 | // Links to Unix-like OSs. 137 | (unix: $syscall: tt ( $section: tt ) ) => { 138 | concat!( 139 | man_links!(__ intro), 140 | man_links!(__ unix $syscall($section)), 141 | ) 142 | }; 143 | // Links to Windows only. 144 | (windows: $syscall: tt ( $section: tt ) ) => { 145 | concat!( 146 | man_links!(__ intro), 147 | man_links!(__ windows $syscall($section)), 148 | ) 149 | }; 150 | // Internals. 151 | (__ intro) => { 152 | "\n\nAdditional documentation can be found in manual of the OS:\n\n" 153 | }; 154 | // List for Unix-like OSs. 155 | (__ unix $syscall: tt ( $section: tt ) ) => { 156 | concat!( 157 | " * DragonFly BSD: \n", 158 | " * FreeBSD: \n", 159 | " * Linux: \n", 160 | " * macOS: (archived, actually for iOS)\n", 161 | " * NetBSD: \n", 162 | " * OpenBSD: \n", 163 | " * iOS: (archived)\n", 164 | " * illumos: \n", 165 | ) 166 | }; 167 | // List for Window (so just Windows). 168 | (__ windows $syscall: tt ( $section: tt ) ) => { 169 | concat!( 170 | " * Windows: \n", 171 | ) 172 | }; 173 | } 174 | 175 | mod sockaddr; 176 | mod socket; 177 | mod sockref; 178 | 179 | #[cfg_attr(unix, path = "sys/unix.rs")] 180 | #[cfg_attr(windows, path = "sys/windows.rs")] 181 | mod sys; 182 | 183 | #[cfg(not(any(windows, unix)))] 184 | compile_error!("Socket2 doesn't support the compile target"); 185 | 186 | use sys::c_int; 187 | 188 | pub use sockaddr::{sa_family_t, socklen_t, SockAddr, SockAddrStorage}; 189 | #[cfg(not(any( 190 | target_os = "haiku", 191 | target_os = "illumos", 192 | target_os = "netbsd", 193 | target_os = "redox", 194 | target_os = "solaris", 195 | )))] 196 | pub use socket::InterfaceIndexOrAddress; 197 | pub use socket::Socket; 198 | pub use sockref::SockRef; 199 | #[cfg(all(feature = "all", target_os = "linux"))] 200 | pub use sys::CcidEndpoints; 201 | #[cfg(all(feature = "all", any(target_os = "linux", target_os = "android")))] 202 | pub use sys::SockFilter; 203 | 204 | /// Specification of the communication domain for a socket. 205 | /// 206 | /// This is a newtype wrapper around an integer which provides a nicer API in 207 | /// addition to an injection point for documentation. Convenience constants such 208 | /// as [`Domain::IPV4`], [`Domain::IPV6`], etc, are provided to avoid reaching 209 | /// into libc for various constants. 210 | /// 211 | /// This type is freely interconvertible with C's `int` type, however, if a raw 212 | /// value needs to be provided. 213 | #[derive(Copy, Clone, Eq, PartialEq)] 214 | pub struct Domain(c_int); 215 | 216 | impl Domain { 217 | /// Domain for IPv4 communication, corresponding to `AF_INET`. 218 | pub const IPV4: Domain = Domain(sys::AF_INET); 219 | 220 | /// Domain for IPv6 communication, corresponding to `AF_INET6`. 221 | pub const IPV6: Domain = Domain(sys::AF_INET6); 222 | 223 | /// Domain for Unix socket communication, corresponding to `AF_UNIX`. 224 | pub const UNIX: Domain = Domain(sys::AF_UNIX); 225 | 226 | /// Returns the correct domain for `address`. 227 | pub const fn for_address(address: SocketAddr) -> Domain { 228 | match address { 229 | SocketAddr::V4(_) => Domain::IPV4, 230 | SocketAddr::V6(_) => Domain::IPV6, 231 | } 232 | } 233 | } 234 | 235 | impl From for Domain { 236 | fn from(d: c_int) -> Domain { 237 | Domain(d) 238 | } 239 | } 240 | 241 | impl From for c_int { 242 | fn from(d: Domain) -> c_int { 243 | d.0 244 | } 245 | } 246 | 247 | /// Specification of communication semantics on a socket. 248 | /// 249 | /// This is a newtype wrapper around an integer which provides a nicer API in 250 | /// addition to an injection point for documentation. Convenience constants such 251 | /// as [`Type::STREAM`], [`Type::DGRAM`], etc, are provided to avoid reaching 252 | /// into libc for various constants. 253 | /// 254 | /// This type is freely interconvertible with C's `int` type, however, if a raw 255 | /// value needs to be provided. 256 | #[derive(Copy, Clone, Eq, PartialEq)] 257 | pub struct Type(c_int); 258 | 259 | impl Type { 260 | /// Type corresponding to `SOCK_STREAM`. 261 | /// 262 | /// Used for protocols such as TCP. 263 | pub const STREAM: Type = Type(sys::SOCK_STREAM); 264 | 265 | /// Type corresponding to `SOCK_DGRAM`. 266 | /// 267 | /// Used for protocols such as UDP. 268 | pub const DGRAM: Type = Type(sys::SOCK_DGRAM); 269 | 270 | /// Type corresponding to `SOCK_DCCP`. 271 | /// 272 | /// Used for the DCCP protocol. 273 | #[cfg(all(feature = "all", target_os = "linux"))] 274 | pub const DCCP: Type = Type(sys::SOCK_DCCP); 275 | 276 | /// Type corresponding to `SOCK_SEQPACKET`. 277 | #[cfg(all(feature = "all", not(target_os = "espidf")))] 278 | pub const SEQPACKET: Type = Type(sys::SOCK_SEQPACKET); 279 | 280 | /// Type corresponding to `SOCK_RAW`. 281 | #[cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))] 282 | pub const RAW: Type = Type(sys::SOCK_RAW); 283 | } 284 | 285 | impl From for Type { 286 | fn from(t: c_int) -> Type { 287 | Type(t) 288 | } 289 | } 290 | 291 | impl From for c_int { 292 | fn from(t: Type) -> c_int { 293 | t.0 294 | } 295 | } 296 | 297 | /// Protocol specification used for creating sockets via `Socket::new`. 298 | /// 299 | /// This is a newtype wrapper around an integer which provides a nicer API in 300 | /// addition to an injection point for documentation. 301 | /// 302 | /// This type is freely interconvertible with C's `int` type, however, if a raw 303 | /// value needs to be provided. 304 | #[derive(Copy, Clone, Eq, PartialEq)] 305 | pub struct Protocol(c_int); 306 | 307 | impl Protocol { 308 | /// Protocol corresponding to `ICMPv4`. 309 | pub const ICMPV4: Protocol = Protocol(sys::IPPROTO_ICMP); 310 | 311 | /// Protocol corresponding to `ICMPv6`. 312 | pub const ICMPV6: Protocol = Protocol(sys::IPPROTO_ICMPV6); 313 | 314 | /// Protocol corresponding to `TCP`. 315 | pub const TCP: Protocol = Protocol(sys::IPPROTO_TCP); 316 | 317 | /// Protocol corresponding to `UDP`. 318 | pub const UDP: Protocol = Protocol(sys::IPPROTO_UDP); 319 | 320 | #[cfg(target_os = "linux")] 321 | /// Protocol corresponding to `MPTCP`. 322 | pub const MPTCP: Protocol = Protocol(sys::IPPROTO_MPTCP); 323 | 324 | /// Protocol corresponding to `DCCP`. 325 | #[cfg(all(feature = "all", target_os = "linux"))] 326 | pub const DCCP: Protocol = Protocol(sys::IPPROTO_DCCP); 327 | 328 | /// Protocol corresponding to `SCTP`. 329 | #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))] 330 | pub const SCTP: Protocol = Protocol(sys::IPPROTO_SCTP); 331 | 332 | /// Protocol corresponding to `UDPLITE`. 333 | #[cfg(all( 334 | feature = "all", 335 | any( 336 | target_os = "android", 337 | target_os = "freebsd", 338 | target_os = "fuchsia", 339 | target_os = "linux", 340 | ) 341 | ))] 342 | pub const UDPLITE: Protocol = Protocol(sys::IPPROTO_UDPLITE); 343 | 344 | /// Protocol corresponding to `DIVERT`. 345 | #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "openbsd")))] 346 | pub const DIVERT: Protocol = Protocol(sys::IPPROTO_DIVERT); 347 | } 348 | 349 | impl From for Protocol { 350 | fn from(p: c_int) -> Protocol { 351 | Protocol(p) 352 | } 353 | } 354 | 355 | impl From for c_int { 356 | fn from(p: Protocol) -> c_int { 357 | p.0 358 | } 359 | } 360 | 361 | /// Flags for incoming messages. 362 | /// 363 | /// Flags provide additional information about incoming messages. 364 | #[cfg(not(target_os = "redox"))] 365 | #[derive(Copy, Clone, Eq, PartialEq)] 366 | pub struct RecvFlags(c_int); 367 | 368 | #[cfg(not(target_os = "redox"))] 369 | impl RecvFlags { 370 | /// Check if the message contains a truncated datagram. 371 | /// 372 | /// This flag is only used for datagram-based sockets, 373 | /// not for stream sockets. 374 | /// 375 | /// On Unix this corresponds to the `MSG_TRUNC` flag. 376 | /// On Windows this corresponds to the `WSAEMSGSIZE` error code. 377 | #[cfg(not(target_os = "espidf"))] 378 | pub const fn is_truncated(self) -> bool { 379 | self.0 & sys::MSG_TRUNC != 0 380 | } 381 | } 382 | 383 | /// A version of [`IoSliceMut`] that allows the buffer to be uninitialised. 384 | /// 385 | /// [`IoSliceMut`]: std::io::IoSliceMut 386 | #[repr(transparent)] 387 | pub struct MaybeUninitSlice<'a>(sys::MaybeUninitSlice<'a>); 388 | 389 | impl<'a> fmt::Debug for MaybeUninitSlice<'a> { 390 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 391 | fmt::Debug::fmt(self.0.as_slice(), fmt) 392 | } 393 | } 394 | 395 | impl<'a> MaybeUninitSlice<'a> { 396 | /// Creates a new `MaybeUninitSlice` wrapping a byte slice. 397 | /// 398 | /// # Panics 399 | /// 400 | /// Panics on Windows if the slice is larger than 4GB. 401 | pub fn new(buf: &'a mut [MaybeUninit]) -> MaybeUninitSlice<'a> { 402 | MaybeUninitSlice(sys::MaybeUninitSlice::new(buf)) 403 | } 404 | } 405 | 406 | impl<'a> Deref for MaybeUninitSlice<'a> { 407 | type Target = [MaybeUninit]; 408 | 409 | fn deref(&self) -> &[MaybeUninit] { 410 | self.0.as_slice() 411 | } 412 | } 413 | 414 | impl<'a> DerefMut for MaybeUninitSlice<'a> { 415 | fn deref_mut(&mut self) -> &mut [MaybeUninit] { 416 | self.0.as_mut_slice() 417 | } 418 | } 419 | 420 | /// Configures a socket's TCP keepalive parameters. 421 | /// 422 | /// See [`Socket::set_tcp_keepalive`]. 423 | #[derive(Debug, Clone)] 424 | pub struct TcpKeepalive { 425 | #[cfg_attr( 426 | any(target_os = "openbsd", target_os = "haiku", target_os = "vita"), 427 | allow(dead_code) 428 | )] 429 | time: Option, 430 | #[cfg(not(any( 431 | target_os = "openbsd", 432 | target_os = "redox", 433 | target_os = "solaris", 434 | target_os = "nto", 435 | target_os = "espidf", 436 | target_os = "vita", 437 | target_os = "haiku", 438 | )))] 439 | interval: Option, 440 | #[cfg(not(any( 441 | target_os = "openbsd", 442 | target_os = "redox", 443 | target_os = "solaris", 444 | target_os = "nto", 445 | target_os = "espidf", 446 | target_os = "vita", 447 | target_os = "haiku", 448 | )))] 449 | retries: Option, 450 | } 451 | 452 | impl TcpKeepalive { 453 | /// Returns a new, empty set of TCP keepalive parameters. 454 | #[allow(clippy::new_without_default)] 455 | pub const fn new() -> TcpKeepalive { 456 | TcpKeepalive { 457 | time: None, 458 | #[cfg(not(any( 459 | target_os = "openbsd", 460 | target_os = "redox", 461 | target_os = "solaris", 462 | target_os = "nto", 463 | target_os = "espidf", 464 | target_os = "vita", 465 | target_os = "haiku", 466 | )))] 467 | interval: None, 468 | #[cfg(not(any( 469 | target_os = "openbsd", 470 | target_os = "redox", 471 | target_os = "solaris", 472 | target_os = "nto", 473 | target_os = "espidf", 474 | target_os = "vita", 475 | target_os = "haiku", 476 | )))] 477 | retries: None, 478 | } 479 | } 480 | 481 | /// Set the amount of time after which TCP keepalive probes will be sent on 482 | /// idle connections. 483 | /// 484 | /// This will set `TCP_KEEPALIVE` on macOS and iOS, and 485 | /// `TCP_KEEPIDLE` on all other Unix operating systems, except 486 | /// OpenBSD and Haiku which don't support any way to set this 487 | /// option. On Windows, this sets the value of the `tcp_keepalive` 488 | /// struct's `keepalivetime` field. 489 | /// 490 | /// Some platforms specify this value in seconds, so sub-second 491 | /// specifications may be omitted. 492 | pub const fn with_time(self, time: Duration) -> Self { 493 | Self { 494 | time: Some(time), 495 | ..self 496 | } 497 | } 498 | 499 | /// Set the value of the `TCP_KEEPINTVL` option. On Windows, this sets the 500 | /// value of the `tcp_keepalive` struct's `keepaliveinterval` field. 501 | /// 502 | /// Sets the time interval between TCP keepalive probes. 503 | /// 504 | /// Some platforms specify this value in seconds, so sub-second 505 | /// specifications may be omitted. 506 | #[cfg(any( 507 | target_os = "android", 508 | target_os = "dragonfly", 509 | target_os = "freebsd", 510 | target_os = "fuchsia", 511 | target_os = "illumos", 512 | target_os = "ios", 513 | target_os = "visionos", 514 | target_os = "linux", 515 | target_os = "macos", 516 | target_os = "netbsd", 517 | target_os = "tvos", 518 | target_os = "watchos", 519 | target_os = "windows", 520 | target_os = "cygwin", 521 | ))] 522 | pub const fn with_interval(self, interval: Duration) -> Self { 523 | Self { 524 | interval: Some(interval), 525 | ..self 526 | } 527 | } 528 | 529 | /// Set the value of the `TCP_KEEPCNT` option. 530 | /// 531 | /// Set the maximum number of TCP keepalive probes that will be sent before 532 | /// dropping a connection, if TCP keepalive is enabled on this socket. 533 | #[cfg(all( 534 | feature = "all", 535 | any( 536 | target_os = "android", 537 | target_os = "dragonfly", 538 | target_os = "freebsd", 539 | target_os = "fuchsia", 540 | target_os = "illumos", 541 | target_os = "ios", 542 | target_os = "visionos", 543 | target_os = "linux", 544 | target_os = "macos", 545 | target_os = "netbsd", 546 | target_os = "tvos", 547 | target_os = "watchos", 548 | target_os = "cygwin", 549 | target_os = "windows", 550 | ) 551 | ))] 552 | pub const fn with_retries(self, retries: u32) -> Self { 553 | Self { 554 | retries: Some(retries), 555 | ..self 556 | } 557 | } 558 | } 559 | 560 | /// Configuration of a `sendmsg(2)` system call. 561 | /// 562 | /// This wraps `msghdr` on Unix and `WSAMSG` on Windows. Also see [`MsgHdrMut`] 563 | /// for the variant used by `recvmsg(2)`. 564 | #[cfg(not(target_os = "redox"))] 565 | pub struct MsgHdr<'addr, 'bufs, 'control> { 566 | inner: sys::msghdr, 567 | #[allow(clippy::type_complexity)] 568 | _lifetimes: PhantomData<(&'addr SockAddr, &'bufs IoSlice<'bufs>, &'control [u8])>, 569 | } 570 | 571 | #[cfg(not(target_os = "redox"))] 572 | impl<'addr, 'bufs, 'control> MsgHdr<'addr, 'bufs, 'control> { 573 | /// Create a new `MsgHdr` with all empty/zero fields. 574 | #[allow(clippy::new_without_default)] 575 | pub fn new() -> MsgHdr<'addr, 'bufs, 'control> { 576 | // SAFETY: all zero is valid for `msghdr` and `WSAMSG`. 577 | MsgHdr { 578 | inner: unsafe { mem::zeroed() }, 579 | _lifetimes: PhantomData, 580 | } 581 | } 582 | 583 | /// Set the address (name) of the message. 584 | /// 585 | /// Corresponds to setting `msg_name` and `msg_namelen` on Unix and `name` 586 | /// and `namelen` on Windows. 587 | pub fn with_addr(mut self, addr: &'addr SockAddr) -> Self { 588 | sys::set_msghdr_name(&mut self.inner, addr); 589 | self 590 | } 591 | 592 | /// Set the buffer(s) of the message. 593 | /// 594 | /// Corresponds to setting `msg_iov` and `msg_iovlen` on Unix and `lpBuffers` 595 | /// and `dwBufferCount` on Windows. 596 | pub fn with_buffers(mut self, bufs: &'bufs [IoSlice<'_>]) -> Self { 597 | let ptr = bufs.as_ptr() as *mut _; 598 | sys::set_msghdr_iov(&mut self.inner, ptr, bufs.len()); 599 | self 600 | } 601 | 602 | /// Set the control buffer of the message. 603 | /// 604 | /// Corresponds to setting `msg_control` and `msg_controllen` on Unix and 605 | /// `Control` on Windows. 606 | pub fn with_control(mut self, buf: &'control [u8]) -> Self { 607 | let ptr = buf.as_ptr() as *mut _; 608 | sys::set_msghdr_control(&mut self.inner, ptr, buf.len()); 609 | self 610 | } 611 | 612 | /// Set the flags of the message. 613 | /// 614 | /// Corresponds to setting `msg_flags` on Unix and `dwFlags` on Windows. 615 | pub fn with_flags(mut self, flags: sys::c_int) -> Self { 616 | sys::set_msghdr_flags(&mut self.inner, flags); 617 | self 618 | } 619 | } 620 | 621 | #[cfg(not(target_os = "redox"))] 622 | impl<'name, 'bufs, 'control> fmt::Debug for MsgHdr<'name, 'bufs, 'control> { 623 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 624 | "MsgHdr".fmt(fmt) 625 | } 626 | } 627 | 628 | /// Configuration of a `recvmsg(2)` system call. 629 | /// 630 | /// This wraps `msghdr` on Unix and `WSAMSG` on Windows. Also see [`MsgHdr`] for 631 | /// the variant used by `sendmsg(2)`. 632 | #[cfg(not(target_os = "redox"))] 633 | pub struct MsgHdrMut<'addr, 'bufs, 'control> { 634 | inner: sys::msghdr, 635 | #[allow(clippy::type_complexity)] 636 | _lifetimes: PhantomData<( 637 | &'addr mut SockAddr, 638 | &'bufs mut MaybeUninitSlice<'bufs>, 639 | &'control mut [u8], 640 | )>, 641 | } 642 | 643 | #[cfg(not(target_os = "redox"))] 644 | impl<'addr, 'bufs, 'control> MsgHdrMut<'addr, 'bufs, 'control> { 645 | /// Create a new `MsgHdrMut` with all empty/zero fields. 646 | #[allow(clippy::new_without_default)] 647 | pub fn new() -> MsgHdrMut<'addr, 'bufs, 'control> { 648 | // SAFETY: all zero is valid for `msghdr` and `WSAMSG`. 649 | MsgHdrMut { 650 | inner: unsafe { mem::zeroed() }, 651 | _lifetimes: PhantomData, 652 | } 653 | } 654 | 655 | /// Set the mutable address (name) of the message. 656 | /// 657 | /// Corresponds to setting `msg_name` and `msg_namelen` on Unix and `name` 658 | /// and `namelen` on Windows. 659 | #[allow(clippy::needless_pass_by_ref_mut)] 660 | pub fn with_addr(mut self, addr: &'addr mut SockAddr) -> Self { 661 | sys::set_msghdr_name(&mut self.inner, addr); 662 | self 663 | } 664 | 665 | /// Set the mutable buffer(s) of the message. 666 | /// 667 | /// Corresponds to setting `msg_iov` and `msg_iovlen` on Unix and `lpBuffers` 668 | /// and `dwBufferCount` on Windows. 669 | pub fn with_buffers(mut self, bufs: &'bufs mut [MaybeUninitSlice<'_>]) -> Self { 670 | sys::set_msghdr_iov(&mut self.inner, bufs.as_mut_ptr().cast(), bufs.len()); 671 | self 672 | } 673 | 674 | /// Set the mutable control buffer of the message. 675 | /// 676 | /// Corresponds to setting `msg_control` and `msg_controllen` on Unix and 677 | /// `Control` on Windows. 678 | pub fn with_control(mut self, buf: &'control mut [MaybeUninit]) -> Self { 679 | sys::set_msghdr_control(&mut self.inner, buf.as_mut_ptr().cast(), buf.len()); 680 | self 681 | } 682 | 683 | /// Returns the flags of the message. 684 | pub fn flags(&self) -> RecvFlags { 685 | sys::msghdr_flags(&self.inner) 686 | } 687 | 688 | /// Gets the length of the control buffer. 689 | /// 690 | /// Can be used to determine how much, if any, of the control buffer was filled by `recvmsg`. 691 | /// 692 | /// Corresponds to `msg_controllen` on Unix and `Control.len` on Windows. 693 | pub fn control_len(&self) -> usize { 694 | sys::msghdr_control_len(&self.inner) 695 | } 696 | } 697 | 698 | #[cfg(not(target_os = "redox"))] 699 | impl<'name, 'bufs, 'control> fmt::Debug for MsgHdrMut<'name, 'bufs, 'control> { 700 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 701 | "MsgHdrMut".fmt(fmt) 702 | } 703 | } 704 | -------------------------------------------------------------------------------- /src/sockaddr.rs: -------------------------------------------------------------------------------- 1 | use std::hash::Hash; 2 | use std::mem::{self, size_of}; 3 | use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; 4 | use std::path::Path; 5 | use std::{fmt, io, ptr}; 6 | 7 | #[cfg(windows)] 8 | use windows_sys::Win32::Networking::WinSock::SOCKADDR_IN6_0; 9 | 10 | use crate::sys::{c_int, sockaddr_in, sockaddr_in6, sockaddr_storage, AF_INET, AF_INET6, AF_UNIX}; 11 | use crate::Domain; 12 | 13 | /// The integer type used with `getsockname` on this platform. 14 | #[allow(non_camel_case_types)] 15 | pub type socklen_t = crate::sys::socklen_t; 16 | 17 | /// The integer type for the `ss_family` field on this platform. 18 | #[allow(non_camel_case_types)] 19 | pub type sa_family_t = crate::sys::sa_family_t; 20 | 21 | /// Rust version of the [`sockaddr_storage`] type. 22 | /// 23 | /// This type is intended to be used with with direct calls to the `getsockname` syscall. See the 24 | /// documentation of [`SockAddr::new`] for examples. 25 | /// 26 | /// This crate defines its own `sockaddr_storage` type to avoid semver concerns with upgrading 27 | /// `windows-sys`. 28 | #[repr(transparent)] 29 | pub struct SockAddrStorage { 30 | storage: sockaddr_storage, 31 | } 32 | 33 | impl SockAddrStorage { 34 | /// Construct a new storage containing all zeros. 35 | #[inline] 36 | pub fn zeroed() -> Self { 37 | // SAFETY: All zeros is valid for this type. 38 | unsafe { mem::zeroed() } 39 | } 40 | 41 | /// Returns the size of this storage. 42 | #[inline] 43 | pub fn size_of(&self) -> socklen_t { 44 | size_of::() as socklen_t 45 | } 46 | 47 | /// View this type as another type. 48 | /// 49 | /// # Safety 50 | /// 51 | /// The type `T` must be one of the `sockaddr_*` types defined by this platform. 52 | #[inline] 53 | pub unsafe fn view_as(&mut self) -> &mut T { 54 | assert!(size_of::() <= size_of::()); 55 | // SAFETY: This type is repr(transparent) over `sockaddr_storage` and `T` is one of the 56 | // `sockaddr_*` types defined by this platform. 57 | &mut *(self as *mut Self as *mut T) 58 | } 59 | } 60 | 61 | impl std::fmt::Debug for SockAddrStorage { 62 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 63 | f.debug_struct("sockaddr_storage") 64 | .field("ss_family", &self.storage.ss_family) 65 | .finish_non_exhaustive() 66 | } 67 | } 68 | 69 | /// The address of a socket. 70 | /// 71 | /// `SockAddr`s may be constructed directly to and from the standard library 72 | /// [`SocketAddr`], [`SocketAddrV4`], and [`SocketAddrV6`] types. 73 | #[derive(Clone)] 74 | pub struct SockAddr { 75 | storage: sockaddr_storage, 76 | len: socklen_t, 77 | } 78 | 79 | #[allow(clippy::len_without_is_empty)] 80 | impl SockAddr { 81 | /// Create a `SockAddr` from the underlying storage and its length. 82 | /// 83 | /// # Safety 84 | /// 85 | /// Caller must ensure that the address family and length match the type of 86 | /// storage address. For example if `storage.ss_family` is set to `AF_INET` 87 | /// the `storage` must be initialised as `sockaddr_in`, setting the content 88 | /// and length appropriately. 89 | /// 90 | /// # Examples 91 | /// 92 | /// ``` 93 | /// # fn main() -> std::io::Result<()> { 94 | /// # #[cfg(unix)] { 95 | /// use std::io; 96 | /// use std::os::unix::io::AsRawFd; 97 | /// 98 | /// use socket2::{SockAddr, SockAddrStorage, Socket, Domain, Type}; 99 | /// 100 | /// let socket = Socket::new(Domain::IPV4, Type::STREAM, None)?; 101 | /// 102 | /// // Initialise a `SocketAddr` byte calling `getsockname(2)`. 103 | /// let mut addr_storage = SockAddrStorage::zeroed(); 104 | /// let mut len = addr_storage.size_of(); 105 | /// 106 | /// // The `getsockname(2)` system call will intiliase `storage` for 107 | /// // us, setting `len` to the correct length. 108 | /// let res = unsafe { 109 | /// libc::getsockname( 110 | /// socket.as_raw_fd(), 111 | /// addr_storage.view_as(), 112 | /// &mut len, 113 | /// ) 114 | /// }; 115 | /// if res == -1 { 116 | /// return Err(io::Error::last_os_error()); 117 | /// } 118 | /// 119 | /// let address = unsafe { SockAddr::new(addr_storage, len) }; 120 | /// # drop(address); 121 | /// # } 122 | /// # Ok(()) 123 | /// # } 124 | /// ``` 125 | pub const unsafe fn new(storage: SockAddrStorage, len: socklen_t) -> SockAddr { 126 | SockAddr { 127 | storage: storage.storage, 128 | len: len as socklen_t, 129 | } 130 | } 131 | 132 | /// Initialise a `SockAddr` by calling the function `init`. 133 | /// 134 | /// The type of the address storage and length passed to the function `init` 135 | /// is OS/architecture specific. 136 | /// 137 | /// The address is zeroed before `init` is called and is thus valid to 138 | /// dereference and read from. The length initialised to the maximum length 139 | /// of the storage. 140 | /// 141 | /// # Safety 142 | /// 143 | /// Caller must ensure that the address family and length match the type of 144 | /// storage address. For example if `storage.ss_family` is set to `AF_INET` 145 | /// the `storage` must be initialised as `sockaddr_in`, setting the content 146 | /// and length appropriately. 147 | /// 148 | /// # Examples 149 | /// 150 | /// ``` 151 | /// # fn main() -> std::io::Result<()> { 152 | /// # #[cfg(unix)] { 153 | /// use std::io; 154 | /// use std::os::unix::io::AsRawFd; 155 | /// 156 | /// use socket2::{SockAddr, Socket, Domain, Type}; 157 | /// 158 | /// let socket = Socket::new(Domain::IPV4, Type::STREAM, None)?; 159 | /// 160 | /// // Initialise a `SocketAddr` byte calling `getsockname(2)`. 161 | /// let (_, address) = unsafe { 162 | /// SockAddr::try_init(|addr_storage, len| { 163 | /// // The `getsockname(2)` system call will intiliase `storage` for 164 | /// // us, setting `len` to the correct length. 165 | /// if libc::getsockname(socket.as_raw_fd(), addr_storage.cast(), len) == -1 { 166 | /// Err(io::Error::last_os_error()) 167 | /// } else { 168 | /// Ok(()) 169 | /// } 170 | /// }) 171 | /// }?; 172 | /// # drop(address); 173 | /// # } 174 | /// # Ok(()) 175 | /// # } 176 | /// ``` 177 | pub unsafe fn try_init(init: F) -> io::Result<(T, SockAddr)> 178 | where 179 | F: FnOnce(*mut SockAddrStorage, *mut socklen_t) -> io::Result, 180 | { 181 | const STORAGE_SIZE: socklen_t = size_of::() as socklen_t; 182 | // NOTE: `SockAddr::unix` depends on the storage being zeroed before 183 | // calling `init`. 184 | // NOTE: calling `recvfrom` with an empty buffer also depends on the 185 | // storage being zeroed before calling `init` as the OS might not 186 | // initialise it. 187 | let mut storage = SockAddrStorage::zeroed(); 188 | let mut len = STORAGE_SIZE; 189 | init(&mut storage, &mut len).map(|res| { 190 | debug_assert!(len <= STORAGE_SIZE, "overflown address storage"); 191 | (res, SockAddr::new(storage, len)) 192 | }) 193 | } 194 | 195 | /// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path. 196 | /// 197 | /// Returns an error if the path is longer than `SUN_LEN`. 198 | pub fn unix

(path: P) -> io::Result 199 | where 200 | P: AsRef, 201 | { 202 | crate::sys::unix_sockaddr(path.as_ref()) 203 | } 204 | 205 | /// Set the length of the address. 206 | /// 207 | /// # Safety 208 | /// 209 | /// Caller must ensure that the address up to `length` bytes are properly 210 | /// initialised. 211 | pub unsafe fn set_length(&mut self, length: socklen_t) { 212 | self.len = length; 213 | } 214 | 215 | /// Returns this address's family. 216 | pub const fn family(&self) -> sa_family_t { 217 | self.storage.ss_family 218 | } 219 | 220 | /// Returns this address's `Domain`. 221 | pub const fn domain(&self) -> Domain { 222 | Domain(self.storage.ss_family as c_int) 223 | } 224 | 225 | /// Returns the size of this address in bytes. 226 | pub const fn len(&self) -> socklen_t { 227 | self.len 228 | } 229 | 230 | /// Returns a raw pointer to the address. 231 | pub const fn as_ptr(&self) -> *const SockAddrStorage { 232 | &self.storage as *const sockaddr_storage as *const SockAddrStorage 233 | } 234 | 235 | /// Retuns the address as the storage. 236 | pub const fn as_storage(self) -> SockAddrStorage { 237 | SockAddrStorage { 238 | storage: self.storage, 239 | } 240 | } 241 | 242 | /// Returns true if this address is in the `AF_INET` (IPv4) family, false otherwise. 243 | pub const fn is_ipv4(&self) -> bool { 244 | self.storage.ss_family == AF_INET as sa_family_t 245 | } 246 | 247 | /// Returns true if this address is in the `AF_INET6` (IPv6) family, false 248 | /// otherwise. 249 | pub const fn is_ipv6(&self) -> bool { 250 | self.storage.ss_family == AF_INET6 as sa_family_t 251 | } 252 | 253 | /// Returns true if this address is of a unix socket (for local interprocess communication), 254 | /// i.e. it is from the `AF_UNIX` family, false otherwise. 255 | pub fn is_unix(&self) -> bool { 256 | self.storage.ss_family == AF_UNIX as sa_family_t 257 | } 258 | 259 | /// Returns this address as a `SocketAddr` if it is in the `AF_INET` (IPv4) 260 | /// or `AF_INET6` (IPv6) family, otherwise returns `None`. 261 | pub fn as_socket(&self) -> Option { 262 | if self.storage.ss_family == AF_INET as sa_family_t { 263 | // SAFETY: if the `ss_family` field is `AF_INET` then storage must 264 | // be a `sockaddr_in`. 265 | let addr = unsafe { &*(ptr::addr_of!(self.storage).cast::()) }; 266 | let ip = crate::sys::from_in_addr(addr.sin_addr); 267 | let port = u16::from_be(addr.sin_port); 268 | Some(SocketAddr::V4(SocketAddrV4::new(ip, port))) 269 | } else if self.storage.ss_family == AF_INET6 as sa_family_t { 270 | // SAFETY: if the `ss_family` field is `AF_INET6` then storage must 271 | // be a `sockaddr_in6`. 272 | let addr = unsafe { &*(ptr::addr_of!(self.storage).cast::()) }; 273 | let ip = crate::sys::from_in6_addr(addr.sin6_addr); 274 | let port = u16::from_be(addr.sin6_port); 275 | Some(SocketAddr::V6(SocketAddrV6::new( 276 | ip, 277 | port, 278 | addr.sin6_flowinfo, 279 | #[cfg(unix)] 280 | addr.sin6_scope_id, 281 | #[cfg(windows)] 282 | unsafe { 283 | addr.Anonymous.sin6_scope_id 284 | }, 285 | ))) 286 | } else { 287 | None 288 | } 289 | } 290 | 291 | /// Returns this address as a [`SocketAddrV4`] if it is in the `AF_INET` 292 | /// family. 293 | pub fn as_socket_ipv4(&self) -> Option { 294 | match self.as_socket() { 295 | Some(SocketAddr::V4(addr)) => Some(addr), 296 | _ => None, 297 | } 298 | } 299 | 300 | /// Returns this address as a [`SocketAddrV6`] if it is in the `AF_INET6` 301 | /// family. 302 | pub fn as_socket_ipv6(&self) -> Option { 303 | match self.as_socket() { 304 | Some(SocketAddr::V6(addr)) => Some(addr), 305 | _ => None, 306 | } 307 | } 308 | 309 | /// Returns the initialised storage bytes. 310 | fn as_bytes(&self) -> &[u8] { 311 | // SAFETY: `self.storage` is a C struct which can always be treated a 312 | // slice of bytes. Furthermore, we ensure we don't read any unitialised 313 | // bytes by using `self.len`. 314 | unsafe { std::slice::from_raw_parts(self.as_ptr().cast(), self.len as usize) } 315 | } 316 | } 317 | 318 | impl From for SockAddr { 319 | fn from(addr: SocketAddr) -> SockAddr { 320 | match addr { 321 | SocketAddr::V4(addr) => addr.into(), 322 | SocketAddr::V6(addr) => addr.into(), 323 | } 324 | } 325 | } 326 | 327 | impl From for SockAddr { 328 | fn from(addr: SocketAddrV4) -> SockAddr { 329 | // SAFETY: a `sockaddr_storage` of all zeros is valid. 330 | let mut storage = unsafe { mem::zeroed::() }; 331 | let len = { 332 | let storage = unsafe { &mut *ptr::addr_of_mut!(storage).cast::() }; 333 | storage.sin_family = AF_INET as sa_family_t; 334 | storage.sin_port = addr.port().to_be(); 335 | storage.sin_addr = crate::sys::to_in_addr(addr.ip()); 336 | storage.sin_zero = Default::default(); 337 | mem::size_of::() as socklen_t 338 | }; 339 | #[cfg(any( 340 | target_os = "dragonfly", 341 | target_os = "freebsd", 342 | target_os = "haiku", 343 | target_os = "hermit", 344 | target_os = "ios", 345 | target_os = "visionos", 346 | target_os = "macos", 347 | target_os = "netbsd", 348 | target_os = "nto", 349 | target_os = "openbsd", 350 | target_os = "tvos", 351 | target_os = "vxworks", 352 | target_os = "watchos", 353 | ))] 354 | { 355 | storage.ss_len = len as u8; 356 | } 357 | SockAddr { storage, len } 358 | } 359 | } 360 | 361 | impl From for SockAddr { 362 | fn from(addr: SocketAddrV6) -> SockAddr { 363 | // SAFETY: a `sockaddr_storage` of all zeros is valid. 364 | let mut storage = unsafe { mem::zeroed::() }; 365 | let len = { 366 | let storage = unsafe { &mut *ptr::addr_of_mut!(storage).cast::() }; 367 | storage.sin6_family = AF_INET6 as sa_family_t; 368 | storage.sin6_port = addr.port().to_be(); 369 | storage.sin6_addr = crate::sys::to_in6_addr(addr.ip()); 370 | storage.sin6_flowinfo = addr.flowinfo(); 371 | #[cfg(unix)] 372 | { 373 | storage.sin6_scope_id = addr.scope_id(); 374 | } 375 | #[cfg(windows)] 376 | { 377 | storage.Anonymous = SOCKADDR_IN6_0 { 378 | sin6_scope_id: addr.scope_id(), 379 | }; 380 | } 381 | mem::size_of::() as socklen_t 382 | }; 383 | #[cfg(any( 384 | target_os = "dragonfly", 385 | target_os = "freebsd", 386 | target_os = "haiku", 387 | target_os = "hermit", 388 | target_os = "ios", 389 | target_os = "visionos", 390 | target_os = "macos", 391 | target_os = "netbsd", 392 | target_os = "nto", 393 | target_os = "openbsd", 394 | target_os = "tvos", 395 | target_os = "vxworks", 396 | target_os = "watchos", 397 | ))] 398 | { 399 | storage.ss_len = len as u8; 400 | } 401 | SockAddr { storage, len } 402 | } 403 | } 404 | 405 | impl fmt::Debug for SockAddr { 406 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 407 | let mut f = fmt.debug_struct("SockAddr"); 408 | #[cfg(any( 409 | target_os = "dragonfly", 410 | target_os = "freebsd", 411 | target_os = "haiku", 412 | target_os = "hermit", 413 | target_os = "ios", 414 | target_os = "visionos", 415 | target_os = "macos", 416 | target_os = "netbsd", 417 | target_os = "nto", 418 | target_os = "openbsd", 419 | target_os = "tvos", 420 | target_os = "vxworks", 421 | target_os = "watchos", 422 | ))] 423 | f.field("ss_len", &self.storage.ss_len); 424 | f.field("ss_family", &self.storage.ss_family) 425 | .field("len", &self.len) 426 | .finish() 427 | } 428 | } 429 | 430 | impl PartialEq for SockAddr { 431 | fn eq(&self, other: &Self) -> bool { 432 | self.as_bytes() == other.as_bytes() 433 | } 434 | } 435 | 436 | impl Eq for SockAddr {} 437 | 438 | impl Hash for SockAddr { 439 | fn hash(&self, state: &mut H) { 440 | self.as_bytes().hash(state); 441 | } 442 | } 443 | 444 | #[cfg(test)] 445 | mod tests { 446 | use super::*; 447 | 448 | #[test] 449 | fn ipv4() { 450 | use std::net::Ipv4Addr; 451 | let std = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876); 452 | let addr = SockAddr::from(std); 453 | assert!(addr.is_ipv4()); 454 | assert!(!addr.is_ipv6()); 455 | assert!(!addr.is_unix()); 456 | assert_eq!(addr.family(), AF_INET as sa_family_t); 457 | assert_eq!(addr.domain(), Domain::IPV4); 458 | assert_eq!(addr.len(), size_of::() as socklen_t); 459 | assert_eq!(addr.as_socket(), Some(SocketAddr::V4(std))); 460 | assert_eq!(addr.as_socket_ipv4(), Some(std)); 461 | assert!(addr.as_socket_ipv6().is_none()); 462 | 463 | let addr = SockAddr::from(SocketAddr::from(std)); 464 | assert_eq!(addr.family(), AF_INET as sa_family_t); 465 | assert_eq!(addr.len(), size_of::() as socklen_t); 466 | assert_eq!(addr.as_socket(), Some(SocketAddr::V4(std))); 467 | assert_eq!(addr.as_socket_ipv4(), Some(std)); 468 | assert!(addr.as_socket_ipv6().is_none()); 469 | #[cfg(unix)] 470 | { 471 | assert!(addr.as_pathname().is_none()); 472 | assert!(addr.as_abstract_namespace().is_none()); 473 | } 474 | } 475 | 476 | #[test] 477 | fn ipv6() { 478 | use std::net::Ipv6Addr; 479 | let std = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12); 480 | let addr = SockAddr::from(std); 481 | assert!(addr.is_ipv6()); 482 | assert!(!addr.is_ipv4()); 483 | assert!(!addr.is_unix()); 484 | assert_eq!(addr.family(), AF_INET6 as sa_family_t); 485 | assert_eq!(addr.domain(), Domain::IPV6); 486 | assert_eq!(addr.len(), size_of::() as socklen_t); 487 | assert_eq!(addr.as_socket(), Some(SocketAddr::V6(std))); 488 | assert!(addr.as_socket_ipv4().is_none()); 489 | assert_eq!(addr.as_socket_ipv6(), Some(std)); 490 | 491 | let addr = SockAddr::from(SocketAddr::from(std)); 492 | assert_eq!(addr.family(), AF_INET6 as sa_family_t); 493 | assert_eq!(addr.len(), size_of::() as socklen_t); 494 | assert_eq!(addr.as_socket(), Some(SocketAddr::V6(std))); 495 | assert!(addr.as_socket_ipv4().is_none()); 496 | assert_eq!(addr.as_socket_ipv6(), Some(std)); 497 | #[cfg(unix)] 498 | { 499 | assert!(addr.as_pathname().is_none()); 500 | assert!(addr.as_abstract_namespace().is_none()); 501 | } 502 | } 503 | 504 | #[test] 505 | fn ipv4_eq() { 506 | use std::net::Ipv4Addr; 507 | 508 | let std1 = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876); 509 | let std2 = SocketAddrV4::new(Ipv4Addr::new(5, 6, 7, 8), 8765); 510 | 511 | test_eq( 512 | SockAddr::from(std1), 513 | SockAddr::from(std1), 514 | SockAddr::from(std2), 515 | ); 516 | } 517 | 518 | #[test] 519 | fn ipv4_hash() { 520 | use std::net::Ipv4Addr; 521 | 522 | let std1 = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876); 523 | let std2 = SocketAddrV4::new(Ipv4Addr::new(5, 6, 7, 8), 8765); 524 | 525 | test_hash( 526 | SockAddr::from(std1), 527 | SockAddr::from(std1), 528 | SockAddr::from(std2), 529 | ); 530 | } 531 | 532 | #[test] 533 | fn ipv6_eq() { 534 | use std::net::Ipv6Addr; 535 | 536 | let std1 = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12); 537 | let std2 = SocketAddrV6::new(Ipv6Addr::new(3, 4, 5, 6, 7, 8, 9, 0), 7654, 13, 14); 538 | 539 | test_eq( 540 | SockAddr::from(std1), 541 | SockAddr::from(std1), 542 | SockAddr::from(std2), 543 | ); 544 | } 545 | 546 | #[test] 547 | fn ipv6_hash() { 548 | use std::net::Ipv6Addr; 549 | 550 | let std1 = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12); 551 | let std2 = SocketAddrV6::new(Ipv6Addr::new(3, 4, 5, 6, 7, 8, 9, 0), 7654, 13, 14); 552 | 553 | test_hash( 554 | SockAddr::from(std1), 555 | SockAddr::from(std1), 556 | SockAddr::from(std2), 557 | ); 558 | } 559 | 560 | #[test] 561 | fn ipv4_ipv6_eq() { 562 | use std::net::Ipv4Addr; 563 | use std::net::Ipv6Addr; 564 | 565 | let std1 = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876); 566 | let std2 = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12); 567 | 568 | test_eq( 569 | SockAddr::from(std1), 570 | SockAddr::from(std1), 571 | SockAddr::from(std2), 572 | ); 573 | 574 | test_eq( 575 | SockAddr::from(std2), 576 | SockAddr::from(std2), 577 | SockAddr::from(std1), 578 | ); 579 | } 580 | 581 | #[test] 582 | fn ipv4_ipv6_hash() { 583 | use std::net::Ipv4Addr; 584 | use std::net::Ipv6Addr; 585 | 586 | let std1 = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876); 587 | let std2 = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12); 588 | 589 | test_hash( 590 | SockAddr::from(std1), 591 | SockAddr::from(std1), 592 | SockAddr::from(std2), 593 | ); 594 | 595 | test_hash( 596 | SockAddr::from(std2), 597 | SockAddr::from(std2), 598 | SockAddr::from(std1), 599 | ); 600 | } 601 | 602 | #[allow(clippy::eq_op)] // allow a0 == a0 check 603 | fn test_eq(a0: SockAddr, a1: SockAddr, b: SockAddr) { 604 | assert!(a0 == a0); 605 | assert!(a0 == a1); 606 | assert!(a1 == a0); 607 | assert!(a0 != b); 608 | assert!(b != a0); 609 | } 610 | 611 | fn test_hash(a0: SockAddr, a1: SockAddr, b: SockAddr) { 612 | assert!(calculate_hash(&a0) == calculate_hash(&a0)); 613 | assert!(calculate_hash(&a0) == calculate_hash(&a1)); 614 | // technically unequal values can have the same hash, in this case x != z and both have different hashes 615 | assert!(calculate_hash(&a0) != calculate_hash(&b)); 616 | } 617 | 618 | fn calculate_hash(x: &SockAddr) -> u64 { 619 | use std::collections::hash_map::DefaultHasher; 620 | use std::hash::Hasher; 621 | 622 | let mut hasher = DefaultHasher::new(); 623 | x.hash(&mut hasher); 624 | hasher.finish() 625 | } 626 | } 627 | -------------------------------------------------------------------------------- /src/sockref.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::marker::PhantomData; 3 | use std::mem::ManuallyDrop; 4 | use std::ops::Deref; 5 | #[cfg(unix)] 6 | use std::os::unix::io::{AsFd, AsRawFd, FromRawFd}; 7 | #[cfg(windows)] 8 | use std::os::windows::io::{AsRawSocket, AsSocket, FromRawSocket}; 9 | 10 | use crate::Socket; 11 | 12 | /// A reference to a [`Socket`] that can be used to configure socket types other 13 | /// than the `Socket` type itself. 14 | /// 15 | /// This allows for example a [`TcpStream`], found in the standard library, to 16 | /// be configured using all the additional methods found in the [`Socket`] API. 17 | /// 18 | /// `SockRef` can be created from any socket type that implements [`AsFd`] 19 | /// (Unix) or [`AsSocket`] (Windows) using the [`From`] implementation. 20 | /// 21 | /// [`TcpStream`]: std::net::TcpStream 22 | // Don't use intra-doc links because they won't build on every platform. 23 | /// [`AsFd`]: https://doc.rust-lang.org/stable/std/os/fd/trait.AsFd.html 24 | /// [`AsSocket`]: https://doc.rust-lang.org/stable/std/os/windows/io/trait.AsSocket.html 25 | /// 26 | /// # Examples 27 | /// 28 | /// Below is an example of converting a [`TcpStream`] into a [`SockRef`]. 29 | /// 30 | /// ``` 31 | /// use std::net::{TcpStream, SocketAddr}; 32 | /// 33 | /// use socket2::SockRef; 34 | /// 35 | /// # fn main() -> Result<(), Box> { 36 | /// // Create `TcpStream` from the standard library. 37 | /// let address: SocketAddr = "127.0.0.1:1234".parse()?; 38 | /// # let b1 = std::sync::Arc::new(std::sync::Barrier::new(2)); 39 | /// # let b2 = b1.clone(); 40 | /// # let handle = std::thread::spawn(move || { 41 | /// # let listener = std::net::TcpListener::bind(address).unwrap(); 42 | /// # b2.wait(); 43 | /// # let (stream, _) = listener.accept().unwrap(); 44 | /// # std::thread::sleep(std::time::Duration::from_millis(10)); 45 | /// # drop(stream); 46 | /// # }); 47 | /// # b1.wait(); 48 | /// let stream = TcpStream::connect(address)?; 49 | /// 50 | /// // Create a `SockRef`erence to the stream. 51 | /// let socket_ref = SockRef::from(&stream); 52 | /// // Use `Socket::set_nodelay` on the stream. 53 | /// socket_ref.set_nodelay(true)?; 54 | /// drop(socket_ref); 55 | /// 56 | /// assert_eq!(stream.nodelay()?, true); 57 | /// # handle.join().unwrap(); 58 | /// # Ok(()) 59 | /// # } 60 | /// ``` 61 | pub struct SockRef<'s> { 62 | /// Because this is a reference we don't own the `Socket`, however `Socket` 63 | /// closes itself when dropped, so we use `ManuallyDrop` to prevent it from 64 | /// closing itself. 65 | socket: ManuallyDrop, 66 | /// Because we don't own the socket we need to ensure the socket remains 67 | /// open while we have a "reference" to it, the lifetime `'s` ensures this. 68 | _lifetime: PhantomData<&'s Socket>, 69 | } 70 | 71 | impl<'s> Deref for SockRef<'s> { 72 | type Target = Socket; 73 | 74 | fn deref(&self) -> &Self::Target { 75 | &self.socket 76 | } 77 | } 78 | 79 | /// On Windows, a corresponding `From<&impl AsSocket>` implementation exists. 80 | #[cfg(unix)] 81 | impl<'s, S> From<&'s S> for SockRef<'s> 82 | where 83 | S: AsFd, 84 | { 85 | /// The caller must ensure `S` is actually a socket. 86 | fn from(socket: &'s S) -> Self { 87 | let fd = socket.as_fd().as_raw_fd(); 88 | assert!(fd >= 0); 89 | SockRef { 90 | socket: ManuallyDrop::new(unsafe { Socket::from_raw_fd(fd) }), 91 | _lifetime: PhantomData, 92 | } 93 | } 94 | } 95 | 96 | /// On Unix, a corresponding `From<&impl AsFd>` implementation exists. 97 | #[cfg(windows)] 98 | impl<'s, S> From<&'s S> for SockRef<'s> 99 | where 100 | S: AsSocket, 101 | { 102 | /// See the `From<&impl AsFd>` implementation. 103 | fn from(socket: &'s S) -> Self { 104 | let socket = socket.as_socket().as_raw_socket(); 105 | assert!(socket != windows_sys::Win32::Networking::WinSock::INVALID_SOCKET as _); 106 | SockRef { 107 | socket: ManuallyDrop::new(unsafe { Socket::from_raw_socket(socket) }), 108 | _lifetime: PhantomData, 109 | } 110 | } 111 | } 112 | 113 | impl fmt::Debug for SockRef<'_> { 114 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 115 | f.debug_struct("SockRef") 116 | .field("raw", &self.socket.as_raw()) 117 | .field("local_addr", &self.socket.local_addr().ok()) 118 | .field("peer_addr", &self.socket.peer_addr().ok()) 119 | .finish() 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/sys/windows.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Rust Project Developers. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use std::cmp::min; 10 | use std::io::{self, IoSlice}; 11 | use std::marker::PhantomData; 12 | use std::mem::{self, size_of, MaybeUninit}; 13 | use std::net::{self, Ipv4Addr, Ipv6Addr, Shutdown}; 14 | use std::os::windows::io::{ 15 | AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket, 16 | }; 17 | use std::path::Path; 18 | use std::sync::Once; 19 | use std::time::{Duration, Instant}; 20 | use std::{process, ptr, slice}; 21 | 22 | use windows_sys::Win32::Foundation::{SetHandleInformation, HANDLE, HANDLE_FLAG_INHERIT}; 23 | use windows_sys::Win32::Networking::WinSock::{ 24 | self, tcp_keepalive, FIONBIO, IN6_ADDR, IN6_ADDR_0, INVALID_SOCKET, IN_ADDR, IN_ADDR_0, 25 | POLLERR, POLLHUP, POLLRDNORM, POLLWRNORM, SD_BOTH, SD_RECEIVE, SD_SEND, SIO_KEEPALIVE_VALS, 26 | SOCKET_ERROR, WSABUF, WSAEMSGSIZE, WSAESHUTDOWN, WSAPOLLFD, WSAPROTOCOL_INFOW, 27 | WSA_FLAG_NO_HANDLE_INHERIT, WSA_FLAG_OVERLAPPED, 28 | }; 29 | #[cfg(feature = "all")] 30 | use windows_sys::Win32::Networking::WinSock::{ 31 | IP6T_SO_ORIGINAL_DST, SOL_IP, SO_ORIGINAL_DST, SO_PROTOCOL_INFOW, 32 | }; 33 | use windows_sys::Win32::System::Threading::INFINITE; 34 | 35 | use crate::{MsgHdr, RecvFlags, SockAddr, SockAddrStorage, TcpKeepalive, Type}; 36 | 37 | #[allow(non_camel_case_types)] 38 | pub(crate) type c_int = std::os::raw::c_int; 39 | 40 | /// Fake MSG_TRUNC flag for the [`RecvFlags`] struct. 41 | /// 42 | /// The flag is enabled when a `WSARecv[From]` call returns `WSAEMSGSIZE`. The 43 | /// value of the flag is defined by us. 44 | pub(crate) const MSG_TRUNC: c_int = 0x01; 45 | 46 | // Used in `Domain`. 47 | pub(crate) const AF_INET: c_int = windows_sys::Win32::Networking::WinSock::AF_INET as c_int; 48 | pub(crate) const AF_INET6: c_int = windows_sys::Win32::Networking::WinSock::AF_INET6 as c_int; 49 | pub(crate) const AF_UNIX: c_int = windows_sys::Win32::Networking::WinSock::AF_UNIX as c_int; 50 | pub(crate) const AF_UNSPEC: c_int = windows_sys::Win32::Networking::WinSock::AF_UNSPEC as c_int; 51 | // Used in `Type`. 52 | pub(crate) const SOCK_STREAM: c_int = windows_sys::Win32::Networking::WinSock::SOCK_STREAM as c_int; 53 | pub(crate) const SOCK_DGRAM: c_int = windows_sys::Win32::Networking::WinSock::SOCK_DGRAM as c_int; 54 | pub(crate) const SOCK_RAW: c_int = windows_sys::Win32::Networking::WinSock::SOCK_RAW as c_int; 55 | const SOCK_RDM: c_int = windows_sys::Win32::Networking::WinSock::SOCK_RDM as c_int; 56 | pub(crate) const SOCK_SEQPACKET: c_int = 57 | windows_sys::Win32::Networking::WinSock::SOCK_SEQPACKET as c_int; 58 | // Used in `Protocol`. 59 | pub(crate) use windows_sys::Win32::Networking::WinSock::{ 60 | IPPROTO_ICMP, IPPROTO_ICMPV6, IPPROTO_TCP, IPPROTO_UDP, 61 | }; 62 | 63 | #[cfg(feature = "all")] 64 | pub(crate) use windows_sys::Win32::Networking::WinSock::TCP_KEEPCNT; 65 | // Used in `SockAddr`. 66 | pub(crate) use windows_sys::Win32::Networking::WinSock::{ 67 | SOCKADDR as sockaddr, SOCKADDR_IN as sockaddr_in, SOCKADDR_IN6 as sockaddr_in6, 68 | SOCKADDR_STORAGE as sockaddr_storage, 69 | }; 70 | #[allow(non_camel_case_types)] 71 | pub(crate) type sa_family_t = windows_sys::Win32::Networking::WinSock::ADDRESS_FAMILY; 72 | #[allow(non_camel_case_types)] 73 | pub(crate) type socklen_t = windows_sys::Win32::Networking::WinSock::socklen_t; 74 | // Used in `Socket`. 75 | #[cfg(feature = "all")] 76 | pub(crate) use windows_sys::Win32::Networking::WinSock::IP_HDRINCL; 77 | pub(crate) use windows_sys::Win32::Networking::WinSock::{ 78 | IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_MREQ as Ipv6Mreq, 79 | IPV6_MULTICAST_HOPS, IPV6_MULTICAST_IF, IPV6_MULTICAST_LOOP, IPV6_RECVTCLASS, 80 | IPV6_UNICAST_HOPS, IPV6_V6ONLY, IP_ADD_MEMBERSHIP, IP_ADD_SOURCE_MEMBERSHIP, 81 | IP_DROP_MEMBERSHIP, IP_DROP_SOURCE_MEMBERSHIP, IP_MREQ as IpMreq, 82 | IP_MREQ_SOURCE as IpMreqSource, IP_MULTICAST_IF, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, 83 | IP_RECVTOS, IP_TOS, IP_TTL, LINGER as linger, MSG_OOB, MSG_PEEK, SO_BROADCAST, SO_ERROR, 84 | SO_KEEPALIVE, SO_LINGER, SO_OOBINLINE, SO_RCVBUF, SO_RCVTIMEO, SO_REUSEADDR, SO_SNDBUF, 85 | SO_SNDTIMEO, SO_TYPE, TCP_NODELAY, 86 | }; 87 | pub(crate) const IPPROTO_IP: c_int = windows_sys::Win32::Networking::WinSock::IPPROTO_IP as c_int; 88 | pub(crate) const SOL_SOCKET: c_int = windows_sys::Win32::Networking::WinSock::SOL_SOCKET as c_int; 89 | 90 | /// Type used in set/getsockopt to retrieve the `TCP_NODELAY` option. 91 | /// 92 | /// NOTE: 93 | /// documents that options such as `TCP_NODELAY` and `SO_KEEPALIVE` expect a 94 | /// `BOOL` (alias for `c_int`, 4 bytes), however in practice this turns out to 95 | /// be false (or misleading) as a `BOOLEAN` (`c_uchar`, 1 byte) is returned by 96 | /// `getsockopt`. 97 | pub(crate) type Bool = windows_sys::Win32::Foundation::BOOLEAN; 98 | 99 | /// Maximum size of a buffer passed to system call like `recv` and `send`. 100 | const MAX_BUF_LEN: usize = c_int::MAX as usize; 101 | 102 | /// Helper macro to execute a system call that returns an `io::Result`. 103 | macro_rules! syscall { 104 | ($fn: ident ( $($arg: expr),* $(,)* ), $err_test: path, $err_value: expr) => {{ 105 | #[allow(unused_unsafe)] 106 | let res = unsafe { windows_sys::Win32::Networking::WinSock::$fn($($arg, )*) }; 107 | if $err_test(&res, &$err_value) { 108 | Err(io::Error::last_os_error()) 109 | } else { 110 | Ok(res) 111 | } 112 | }}; 113 | } 114 | 115 | impl_debug!( 116 | crate::Domain, 117 | self::AF_INET, 118 | self::AF_INET6, 119 | self::AF_UNIX, 120 | self::AF_UNSPEC, 121 | ); 122 | 123 | /// Windows only API. 124 | impl Type { 125 | /// Our custom flag to set `WSA_FLAG_NO_HANDLE_INHERIT` on socket creation. 126 | /// Trying to mimic `Type::cloexec` on windows. 127 | const NO_INHERIT: c_int = 1 << ((size_of::() * 8) - 1); // Last bit. 128 | 129 | /// Set `WSA_FLAG_NO_HANDLE_INHERIT` on the socket. 130 | #[cfg(feature = "all")] 131 | pub const fn no_inherit(self) -> Type { 132 | self._no_inherit() 133 | } 134 | 135 | pub(crate) const fn _no_inherit(self) -> Type { 136 | Type(self.0 | Type::NO_INHERIT) 137 | } 138 | } 139 | 140 | impl_debug!( 141 | crate::Type, 142 | self::SOCK_STREAM, 143 | self::SOCK_DGRAM, 144 | self::SOCK_RAW, 145 | self::SOCK_RDM, 146 | self::SOCK_SEQPACKET, 147 | ); 148 | 149 | impl_debug!( 150 | crate::Protocol, 151 | WinSock::IPPROTO_ICMP, 152 | WinSock::IPPROTO_ICMPV6, 153 | WinSock::IPPROTO_TCP, 154 | WinSock::IPPROTO_UDP, 155 | ); 156 | 157 | impl std::fmt::Debug for RecvFlags { 158 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 159 | f.debug_struct("RecvFlags") 160 | .field("is_truncated", &self.is_truncated()) 161 | .finish() 162 | } 163 | } 164 | 165 | #[repr(transparent)] 166 | pub struct MaybeUninitSlice<'a> { 167 | vec: WSABUF, 168 | _lifetime: PhantomData<&'a mut [MaybeUninit]>, 169 | } 170 | 171 | unsafe impl<'a> Send for MaybeUninitSlice<'a> {} 172 | 173 | unsafe impl<'a> Sync for MaybeUninitSlice<'a> {} 174 | 175 | impl<'a> MaybeUninitSlice<'a> { 176 | pub fn new(buf: &'a mut [MaybeUninit]) -> MaybeUninitSlice<'a> { 177 | assert!(buf.len() <= u32::MAX as usize); 178 | MaybeUninitSlice { 179 | vec: WSABUF { 180 | len: buf.len() as u32, 181 | buf: buf.as_mut_ptr().cast(), 182 | }, 183 | _lifetime: PhantomData, 184 | } 185 | } 186 | 187 | pub fn as_slice(&self) -> &[MaybeUninit] { 188 | unsafe { slice::from_raw_parts(self.vec.buf.cast(), self.vec.len as usize) } 189 | } 190 | 191 | pub fn as_mut_slice(&mut self) -> &mut [MaybeUninit] { 192 | unsafe { slice::from_raw_parts_mut(self.vec.buf.cast(), self.vec.len as usize) } 193 | } 194 | } 195 | 196 | // Used in `MsgHdr`. 197 | pub(crate) use windows_sys::Win32::Networking::WinSock::WSAMSG as msghdr; 198 | 199 | pub(crate) fn set_msghdr_name(msg: &mut msghdr, name: &SockAddr) { 200 | msg.name = name.as_ptr() as *mut _; 201 | msg.namelen = name.len(); 202 | } 203 | 204 | pub(crate) fn set_msghdr_iov(msg: &mut msghdr, ptr: *mut WSABUF, len: usize) { 205 | msg.lpBuffers = ptr; 206 | msg.dwBufferCount = min(len, u32::MAX as usize) as u32; 207 | } 208 | 209 | pub(crate) fn set_msghdr_control(msg: &mut msghdr, ptr: *mut u8, len: usize) { 210 | msg.Control.buf = ptr; 211 | msg.Control.len = len as u32; 212 | } 213 | 214 | pub(crate) fn set_msghdr_flags(msg: &mut msghdr, flags: c_int) { 215 | msg.dwFlags = flags as u32; 216 | } 217 | 218 | pub(crate) fn msghdr_flags(msg: &msghdr) -> RecvFlags { 219 | RecvFlags(msg.dwFlags as c_int) 220 | } 221 | 222 | pub(crate) fn msghdr_control_len(msg: &msghdr) -> usize { 223 | msg.Control.len as _ 224 | } 225 | 226 | fn init() { 227 | static INIT: Once = Once::new(); 228 | 229 | INIT.call_once(|| { 230 | // Initialize winsock through the standard library by just creating a 231 | // dummy socket. Whether this is successful or not we drop the result as 232 | // libstd will be sure to have initialized winsock. 233 | let _ = net::UdpSocket::bind("127.0.0.1:34254"); 234 | }); 235 | } 236 | 237 | pub(crate) type Socket = windows_sys::Win32::Networking::WinSock::SOCKET; 238 | 239 | pub(crate) unsafe fn socket_from_raw(socket: Socket) -> crate::socket::Inner { 240 | crate::socket::Inner::from_raw_socket(socket as RawSocket) 241 | } 242 | 243 | pub(crate) fn socket_as_raw(socket: &crate::socket::Inner) -> Socket { 244 | socket.as_raw_socket() as Socket 245 | } 246 | 247 | pub(crate) fn socket_into_raw(socket: crate::socket::Inner) -> Socket { 248 | socket.into_raw_socket() as Socket 249 | } 250 | 251 | pub(crate) fn socket(family: c_int, mut ty: c_int, protocol: c_int) -> io::Result { 252 | init(); 253 | 254 | // Check if we set our custom flag. 255 | let flags = if ty & Type::NO_INHERIT != 0 { 256 | ty = ty & !Type::NO_INHERIT; 257 | WSA_FLAG_NO_HANDLE_INHERIT 258 | } else { 259 | 0 260 | }; 261 | 262 | syscall!( 263 | WSASocketW( 264 | family, 265 | ty, 266 | protocol, 267 | ptr::null_mut(), 268 | 0, 269 | WSA_FLAG_OVERLAPPED | flags, 270 | ), 271 | PartialEq::eq, 272 | INVALID_SOCKET 273 | ) 274 | } 275 | 276 | pub(crate) fn bind(socket: Socket, addr: &SockAddr) -> io::Result<()> { 277 | syscall!( 278 | bind(socket, addr.as_ptr().cast::(), addr.len()), 279 | PartialEq::ne, 280 | 0 281 | ) 282 | .map(|_| ()) 283 | } 284 | 285 | pub(crate) fn connect(socket: Socket, addr: &SockAddr) -> io::Result<()> { 286 | syscall!( 287 | connect(socket, addr.as_ptr().cast::(), addr.len()), 288 | PartialEq::ne, 289 | 0 290 | ) 291 | .map(|_| ()) 292 | } 293 | 294 | pub(crate) fn poll_connect(socket: &crate::Socket, timeout: Duration) -> io::Result<()> { 295 | let start = Instant::now(); 296 | 297 | let mut fd_array = WSAPOLLFD { 298 | fd: socket.as_raw(), 299 | events: (POLLRDNORM | POLLWRNORM) as i16, 300 | revents: 0, 301 | }; 302 | 303 | loop { 304 | let elapsed = start.elapsed(); 305 | if elapsed >= timeout { 306 | return Err(io::ErrorKind::TimedOut.into()); 307 | } 308 | 309 | let timeout = (timeout - elapsed).as_millis(); 310 | let timeout = clamp(timeout, 1, c_int::MAX as u128) as c_int; 311 | 312 | match syscall!( 313 | WSAPoll(&mut fd_array, 1, timeout), 314 | PartialEq::eq, 315 | SOCKET_ERROR 316 | ) { 317 | Ok(0) => return Err(io::ErrorKind::TimedOut.into()), 318 | Ok(_) => { 319 | // Error or hang up indicates an error (or failure to connect). 320 | if (fd_array.revents & POLLERR as i16) != 0 321 | || (fd_array.revents & POLLHUP as i16) != 0 322 | { 323 | match socket.take_error() { 324 | Ok(Some(err)) => return Err(err), 325 | Ok(None) => { 326 | return Err(io::Error::new( 327 | io::ErrorKind::Other, 328 | "no error set after POLLHUP", 329 | )) 330 | } 331 | Err(err) => return Err(err), 332 | } 333 | } 334 | return Ok(()); 335 | } 336 | // Got interrupted, try again. 337 | Err(ref err) if err.kind() == io::ErrorKind::Interrupted => continue, 338 | Err(err) => return Err(err), 339 | } 340 | } 341 | } 342 | 343 | // TODO: use clamp from std lib, stable since 1.50. 344 | fn clamp(value: T, min: T, max: T) -> T 345 | where 346 | T: Ord, 347 | { 348 | if value <= min { 349 | min 350 | } else if value >= max { 351 | max 352 | } else { 353 | value 354 | } 355 | } 356 | 357 | pub(crate) fn listen(socket: Socket, backlog: c_int) -> io::Result<()> { 358 | syscall!(listen(socket, backlog), PartialEq::ne, 0).map(|_| ()) 359 | } 360 | 361 | pub(crate) fn accept(socket: Socket) -> io::Result<(Socket, SockAddr)> { 362 | // Safety: `accept` initialises the `SockAddr` for us. 363 | unsafe { 364 | SockAddr::try_init(|storage, len| { 365 | syscall!( 366 | accept(socket, storage.cast(), len), 367 | PartialEq::eq, 368 | INVALID_SOCKET 369 | ) 370 | }) 371 | } 372 | } 373 | 374 | pub(crate) fn getsockname(socket: Socket) -> io::Result { 375 | // Safety: `getsockname` initialises the `SockAddr` for us. 376 | unsafe { 377 | SockAddr::try_init(|storage, len| { 378 | syscall!( 379 | getsockname(socket, storage.cast(), len), 380 | PartialEq::eq, 381 | SOCKET_ERROR 382 | ) 383 | }) 384 | } 385 | .map(|(_, addr)| addr) 386 | } 387 | 388 | pub(crate) fn getpeername(socket: Socket) -> io::Result { 389 | // Safety: `getpeername` initialises the `SockAddr` for us. 390 | unsafe { 391 | SockAddr::try_init(|storage, len| { 392 | syscall!( 393 | getpeername(socket, storage.cast(), len), 394 | PartialEq::eq, 395 | SOCKET_ERROR 396 | ) 397 | }) 398 | } 399 | .map(|(_, addr)| addr) 400 | } 401 | 402 | pub(crate) fn try_clone(socket: Socket) -> io::Result { 403 | let mut info: MaybeUninit = MaybeUninit::uninit(); 404 | syscall!( 405 | // NOTE: `process.id` is the same as `GetCurrentProcessId`. 406 | WSADuplicateSocketW(socket, process::id(), info.as_mut_ptr()), 407 | PartialEq::eq, 408 | SOCKET_ERROR 409 | )?; 410 | // Safety: `WSADuplicateSocketW` intialised `info` for us. 411 | let mut info = unsafe { info.assume_init() }; 412 | 413 | syscall!( 414 | WSASocketW( 415 | info.iAddressFamily, 416 | info.iSocketType, 417 | info.iProtocol, 418 | &mut info, 419 | 0, 420 | WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT, 421 | ), 422 | PartialEq::eq, 423 | INVALID_SOCKET 424 | ) 425 | } 426 | 427 | pub(crate) fn set_nonblocking(socket: Socket, nonblocking: bool) -> io::Result<()> { 428 | let mut nonblocking = if nonblocking { 1 } else { 0 }; 429 | ioctlsocket(socket, FIONBIO, &mut nonblocking) 430 | } 431 | 432 | pub(crate) fn shutdown(socket: Socket, how: Shutdown) -> io::Result<()> { 433 | let how = match how { 434 | Shutdown::Write => SD_SEND, 435 | Shutdown::Read => SD_RECEIVE, 436 | Shutdown::Both => SD_BOTH, 437 | } as i32; 438 | syscall!(shutdown(socket, how), PartialEq::eq, SOCKET_ERROR).map(|_| ()) 439 | } 440 | 441 | pub(crate) fn recv(socket: Socket, buf: &mut [MaybeUninit], flags: c_int) -> io::Result { 442 | let res = syscall!( 443 | recv( 444 | socket, 445 | buf.as_mut_ptr().cast(), 446 | min(buf.len(), MAX_BUF_LEN) as c_int, 447 | flags, 448 | ), 449 | PartialEq::eq, 450 | SOCKET_ERROR 451 | ); 452 | match res { 453 | Ok(n) => Ok(n as usize), 454 | Err(ref err) if err.raw_os_error() == Some(WSAESHUTDOWN as i32) => Ok(0), 455 | Err(err) => Err(err), 456 | } 457 | } 458 | 459 | pub(crate) fn recv_vectored( 460 | socket: Socket, 461 | bufs: &mut [crate::MaybeUninitSlice<'_>], 462 | flags: c_int, 463 | ) -> io::Result<(usize, RecvFlags)> { 464 | let mut nread = 0; 465 | let mut flags = flags as u32; 466 | let res = syscall!( 467 | WSARecv( 468 | socket, 469 | bufs.as_mut_ptr().cast(), 470 | min(bufs.len(), u32::MAX as usize) as u32, 471 | &mut nread, 472 | &mut flags, 473 | ptr::null_mut(), 474 | None, 475 | ), 476 | PartialEq::eq, 477 | SOCKET_ERROR 478 | ); 479 | match res { 480 | Ok(_) => Ok((nread as usize, RecvFlags(0))), 481 | Err(ref err) if err.raw_os_error() == Some(WSAESHUTDOWN as i32) => Ok((0, RecvFlags(0))), 482 | Err(ref err) if err.raw_os_error() == Some(WSAEMSGSIZE as i32) => { 483 | Ok((nread as usize, RecvFlags(MSG_TRUNC))) 484 | } 485 | Err(err) => Err(err), 486 | } 487 | } 488 | 489 | pub(crate) fn recv_from( 490 | socket: Socket, 491 | buf: &mut [MaybeUninit], 492 | flags: c_int, 493 | ) -> io::Result<(usize, SockAddr)> { 494 | // Safety: `recvfrom` initialises the `SockAddr` for us. 495 | unsafe { 496 | SockAddr::try_init(|storage, addrlen| { 497 | let res = syscall!( 498 | recvfrom( 499 | socket, 500 | buf.as_mut_ptr().cast(), 501 | min(buf.len(), MAX_BUF_LEN) as c_int, 502 | flags, 503 | storage.cast(), 504 | addrlen, 505 | ), 506 | PartialEq::eq, 507 | SOCKET_ERROR 508 | ); 509 | match res { 510 | Ok(n) => Ok(n as usize), 511 | Err(ref err) if err.raw_os_error() == Some(WSAESHUTDOWN as i32) => Ok(0), 512 | Err(err) => Err(err), 513 | } 514 | }) 515 | } 516 | } 517 | 518 | pub(crate) fn peek_sender(socket: Socket) -> io::Result { 519 | // Safety: `recvfrom` initialises the `SockAddr` for us. 520 | let ((), sender) = unsafe { 521 | SockAddr::try_init(|storage, addrlen| { 522 | let res = syscall!( 523 | recvfrom( 524 | socket, 525 | // Windows *appears* not to care if you pass a null pointer. 526 | ptr::null_mut(), 527 | 0, 528 | MSG_PEEK, 529 | storage.cast(), 530 | addrlen, 531 | ), 532 | PartialEq::eq, 533 | SOCKET_ERROR 534 | ); 535 | match res { 536 | Ok(_n) => Ok(()), 537 | Err(e) => match e.raw_os_error() { 538 | Some(code) if code == (WSAESHUTDOWN as i32) || code == (WSAEMSGSIZE as i32) => { 539 | Ok(()) 540 | } 541 | _ => Err(e), 542 | }, 543 | } 544 | }) 545 | }?; 546 | 547 | Ok(sender) 548 | } 549 | 550 | pub(crate) fn recv_from_vectored( 551 | socket: Socket, 552 | bufs: &mut [crate::MaybeUninitSlice<'_>], 553 | flags: c_int, 554 | ) -> io::Result<(usize, RecvFlags, SockAddr)> { 555 | // Safety: `recvfrom` initialises the `SockAddr` for us. 556 | unsafe { 557 | SockAddr::try_init(|storage, addrlen| { 558 | let mut nread = 0; 559 | let mut flags = flags as u32; 560 | let res = syscall!( 561 | WSARecvFrom( 562 | socket, 563 | bufs.as_mut_ptr().cast(), 564 | min(bufs.len(), u32::MAX as usize) as u32, 565 | &mut nread, 566 | &mut flags, 567 | storage.cast(), 568 | addrlen, 569 | ptr::null_mut(), 570 | None, 571 | ), 572 | PartialEq::eq, 573 | SOCKET_ERROR 574 | ); 575 | match res { 576 | Ok(_) => Ok((nread as usize, RecvFlags(0))), 577 | Err(ref err) if err.raw_os_error() == Some(WSAESHUTDOWN as i32) => { 578 | Ok((nread as usize, RecvFlags(0))) 579 | } 580 | Err(ref err) if err.raw_os_error() == Some(WSAEMSGSIZE as i32) => { 581 | Ok((nread as usize, RecvFlags(MSG_TRUNC))) 582 | } 583 | Err(err) => Err(err), 584 | } 585 | }) 586 | } 587 | .map(|((n, recv_flags), addr)| (n, recv_flags, addr)) 588 | } 589 | 590 | pub(crate) fn send(socket: Socket, buf: &[u8], flags: c_int) -> io::Result { 591 | syscall!( 592 | send( 593 | socket, 594 | buf.as_ptr().cast(), 595 | min(buf.len(), MAX_BUF_LEN) as c_int, 596 | flags, 597 | ), 598 | PartialEq::eq, 599 | SOCKET_ERROR 600 | ) 601 | .map(|n| n as usize) 602 | } 603 | 604 | pub(crate) fn send_vectored( 605 | socket: Socket, 606 | bufs: &[IoSlice<'_>], 607 | flags: c_int, 608 | ) -> io::Result { 609 | let mut nsent = 0; 610 | syscall!( 611 | WSASend( 612 | socket, 613 | // FIXME: From the `WSASend` docs [1]: 614 | // > For a Winsock application, once the WSASend function is called, 615 | // > the system owns these buffers and the application may not 616 | // > access them. 617 | // 618 | // So what we're doing is actually UB as `bufs` needs to be `&mut 619 | // [IoSlice<'_>]`. 620 | // 621 | // Tracking issue: https://github.com/rust-lang/socket2-rs/issues/129. 622 | // 623 | // NOTE: `send_to_vectored` has the same problem. 624 | // 625 | // [1] https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasend 626 | bufs.as_ptr() as *mut _, 627 | min(bufs.len(), u32::MAX as usize) as u32, 628 | &mut nsent, 629 | flags as u32, 630 | std::ptr::null_mut(), 631 | None, 632 | ), 633 | PartialEq::eq, 634 | SOCKET_ERROR 635 | ) 636 | .map(|_| nsent as usize) 637 | } 638 | 639 | pub(crate) fn send_to( 640 | socket: Socket, 641 | buf: &[u8], 642 | addr: &SockAddr, 643 | flags: c_int, 644 | ) -> io::Result { 645 | syscall!( 646 | sendto( 647 | socket, 648 | buf.as_ptr().cast(), 649 | min(buf.len(), MAX_BUF_LEN) as c_int, 650 | flags, 651 | addr.as_ptr().cast::(), 652 | addr.len(), 653 | ), 654 | PartialEq::eq, 655 | SOCKET_ERROR 656 | ) 657 | .map(|n| n as usize) 658 | } 659 | 660 | pub(crate) fn send_to_vectored( 661 | socket: Socket, 662 | bufs: &[IoSlice<'_>], 663 | addr: &SockAddr, 664 | flags: c_int, 665 | ) -> io::Result { 666 | let mut nsent = 0; 667 | syscall!( 668 | WSASendTo( 669 | socket, 670 | // FIXME: Same problem as in `send_vectored`. 671 | bufs.as_ptr() as *mut _, 672 | bufs.len().min(u32::MAX as usize) as u32, 673 | &mut nsent, 674 | flags as u32, 675 | addr.as_ptr().cast::(), 676 | addr.len(), 677 | ptr::null_mut(), 678 | None, 679 | ), 680 | PartialEq::eq, 681 | SOCKET_ERROR 682 | ) 683 | .map(|_| nsent as usize) 684 | } 685 | 686 | pub(crate) fn sendmsg(socket: Socket, msg: &MsgHdr<'_, '_, '_>, flags: c_int) -> io::Result { 687 | let mut nsent = 0; 688 | syscall!( 689 | WSASendMsg( 690 | socket, 691 | &msg.inner, 692 | flags as u32, 693 | &mut nsent, 694 | ptr::null_mut(), 695 | None, 696 | ), 697 | PartialEq::eq, 698 | SOCKET_ERROR 699 | ) 700 | .map(|_| nsent as usize) 701 | } 702 | 703 | /// Wrapper around `getsockopt` to deal with platform specific timeouts. 704 | pub(crate) fn timeout_opt(fd: Socket, lvl: c_int, name: i32) -> io::Result> { 705 | unsafe { getsockopt(fd, lvl, name).map(from_ms) } 706 | } 707 | 708 | fn from_ms(duration: u32) -> Option { 709 | if duration == 0 { 710 | None 711 | } else { 712 | let secs = duration / 1000; 713 | let nsec = (duration % 1000) * 1000000; 714 | Some(Duration::new(secs as u64, nsec as u32)) 715 | } 716 | } 717 | 718 | /// Wrapper around `setsockopt` to deal with platform specific timeouts. 719 | pub(crate) fn set_timeout_opt( 720 | socket: Socket, 721 | level: c_int, 722 | optname: i32, 723 | duration: Option, 724 | ) -> io::Result<()> { 725 | let duration = into_ms(duration); 726 | unsafe { setsockopt(socket, level, optname, duration) } 727 | } 728 | 729 | fn into_ms(duration: Option) -> u32 { 730 | // Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the 731 | // timeouts in windows APIs are typically u32 milliseconds. To translate, we 732 | // have two pieces to take care of: 733 | // 734 | // * Nanosecond precision is rounded up 735 | // * Greater than u32::MAX milliseconds (50 days) is rounded up to 736 | // INFINITE (never time out). 737 | duration.map_or(0, |duration| { 738 | min(duration.as_millis(), INFINITE as u128) as u32 739 | }) 740 | } 741 | 742 | pub(crate) fn set_tcp_keepalive(socket: Socket, keepalive: &TcpKeepalive) -> io::Result<()> { 743 | let mut tcp_keepalive = tcp_keepalive { 744 | onoff: 1, 745 | keepalivetime: into_ms(keepalive.time), 746 | keepaliveinterval: into_ms(keepalive.interval), 747 | }; 748 | 749 | let mut out = 0; 750 | syscall!( 751 | WSAIoctl( 752 | socket, 753 | SIO_KEEPALIVE_VALS, 754 | &mut tcp_keepalive as *mut _ as *mut _, 755 | size_of::() as _, 756 | ptr::null_mut(), 757 | 0, 758 | &mut out, 759 | ptr::null_mut(), 760 | None, 761 | ), 762 | PartialEq::eq, 763 | SOCKET_ERROR 764 | )?; 765 | if let Some(retries) = keepalive.retries { 766 | unsafe { 767 | setsockopt( 768 | socket, 769 | WinSock::IPPROTO_TCP, 770 | WinSock::TCP_KEEPCNT, 771 | retries as c_int, 772 | )? 773 | } 774 | } 775 | Ok(()) 776 | } 777 | 778 | /// Caller must ensure `T` is the correct type for `level` and `optname`. 779 | // NOTE: `optname` is actually `i32`, but all constants are `u32`. 780 | pub(crate) unsafe fn getsockopt(socket: Socket, level: c_int, optname: i32) -> io::Result { 781 | let mut optval: MaybeUninit = MaybeUninit::uninit(); 782 | let mut optlen = mem::size_of::() as c_int; 783 | syscall!( 784 | getsockopt( 785 | socket, 786 | level as i32, 787 | optname, 788 | optval.as_mut_ptr().cast(), 789 | &mut optlen, 790 | ), 791 | PartialEq::eq, 792 | SOCKET_ERROR 793 | ) 794 | .map(|_| { 795 | debug_assert_eq!(optlen as usize, mem::size_of::()); 796 | // Safety: `getsockopt` initialised `optval` for us. 797 | optval.assume_init() 798 | }) 799 | } 800 | 801 | /// Caller must ensure `T` is the correct type for `level` and `optname`. 802 | // NOTE: `optname` is actually `i32`, but all constants are `u32`. 803 | pub(crate) unsafe fn setsockopt( 804 | socket: Socket, 805 | level: c_int, 806 | optname: i32, 807 | optval: T, 808 | ) -> io::Result<()> { 809 | syscall!( 810 | setsockopt( 811 | socket, 812 | level as i32, 813 | optname, 814 | (&optval as *const T).cast(), 815 | mem::size_of::() as c_int, 816 | ), 817 | PartialEq::eq, 818 | SOCKET_ERROR 819 | ) 820 | .map(|_| ()) 821 | } 822 | 823 | fn ioctlsocket(socket: Socket, cmd: i32, payload: &mut u32) -> io::Result<()> { 824 | syscall!( 825 | ioctlsocket(socket, cmd, payload), 826 | PartialEq::eq, 827 | SOCKET_ERROR 828 | ) 829 | .map(|_| ()) 830 | } 831 | 832 | pub(crate) fn to_in_addr(addr: &Ipv4Addr) -> IN_ADDR { 833 | IN_ADDR { 834 | S_un: IN_ADDR_0 { 835 | // `S_un` is stored as BE on all machines, and the array is in BE 836 | // order. So the native endian conversion method is used so that 837 | // it's never swapped. 838 | S_addr: u32::from_ne_bytes(addr.octets()), 839 | }, 840 | } 841 | } 842 | 843 | pub(crate) fn from_in_addr(in_addr: IN_ADDR) -> Ipv4Addr { 844 | Ipv4Addr::from(unsafe { in_addr.S_un.S_addr }.to_ne_bytes()) 845 | } 846 | 847 | pub(crate) fn to_in6_addr(addr: &Ipv6Addr) -> IN6_ADDR { 848 | IN6_ADDR { 849 | u: IN6_ADDR_0 { 850 | Byte: addr.octets(), 851 | }, 852 | } 853 | } 854 | 855 | pub(crate) fn from_in6_addr(addr: IN6_ADDR) -> Ipv6Addr { 856 | Ipv6Addr::from(unsafe { addr.u.Byte }) 857 | } 858 | 859 | pub(crate) fn to_mreqn( 860 | multiaddr: &Ipv4Addr, 861 | interface: &crate::socket::InterfaceIndexOrAddress, 862 | ) -> IpMreq { 863 | IpMreq { 864 | imr_multiaddr: to_in_addr(multiaddr), 865 | // Per https://docs.microsoft.com/en-us/windows/win32/api/ws2ipdef/ns-ws2ipdef-ip_mreq#members: 866 | // 867 | // imr_interface 868 | // 869 | // The local IPv4 address of the interface or the interface index on 870 | // which the multicast group should be joined or dropped. This value is 871 | // in network byte order. If this member specifies an IPv4 address of 872 | // 0.0.0.0, the default IPv4 multicast interface is used. 873 | // 874 | // To use an interface index of 1 would be the same as an IP address of 875 | // 0.0.0.1. 876 | imr_interface: match interface { 877 | crate::socket::InterfaceIndexOrAddress::Index(interface) => { 878 | to_in_addr(&(*interface).into()) 879 | } 880 | crate::socket::InterfaceIndexOrAddress::Address(interface) => to_in_addr(interface), 881 | }, 882 | } 883 | } 884 | 885 | #[cfg(feature = "all")] 886 | pub(crate) fn original_dst(socket: Socket) -> io::Result { 887 | unsafe { 888 | SockAddr::try_init(|storage, len| { 889 | syscall!( 890 | getsockopt( 891 | socket, 892 | SOL_IP as i32, 893 | SO_ORIGINAL_DST as i32, 894 | storage.cast(), 895 | len, 896 | ), 897 | PartialEq::eq, 898 | SOCKET_ERROR 899 | ) 900 | }) 901 | } 902 | .map(|(_, addr)| addr) 903 | } 904 | 905 | #[cfg(feature = "all")] 906 | pub(crate) fn original_dst_ipv6(socket: Socket) -> io::Result { 907 | unsafe { 908 | SockAddr::try_init(|storage, len| { 909 | syscall!( 910 | getsockopt( 911 | socket, 912 | SOL_IP as i32, 913 | IP6T_SO_ORIGINAL_DST as i32, 914 | storage.cast(), 915 | len, 916 | ), 917 | PartialEq::eq, 918 | SOCKET_ERROR 919 | ) 920 | }) 921 | } 922 | .map(|(_, addr)| addr) 923 | } 924 | 925 | #[allow(unsafe_op_in_unsafe_fn)] 926 | pub(crate) fn unix_sockaddr(path: &Path) -> io::Result { 927 | let mut storage = SockAddrStorage::zeroed(); 928 | let len = { 929 | let storage = 930 | unsafe { storage.view_as::() }; 931 | 932 | // Windows expects a UTF-8 path here even though Windows paths are 933 | // usually UCS-2 encoded. If Rust exposed OsStr's Wtf8 encoded 934 | // buffer, this could be used directly, relying on Windows to 935 | // validate the path, but Rust hides this implementation detail. 936 | // 937 | // See . 938 | let bytes = path 939 | .to_str() 940 | .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "path must be valid UTF-8"))? 941 | .as_bytes(); 942 | 943 | // Windows appears to allow non-null-terminated paths, but this is 944 | // not documented, so do not rely on it yet. 945 | // 946 | // See . 947 | if bytes.len() >= storage.sun_path.len() { 948 | return Err(io::Error::new( 949 | io::ErrorKind::InvalidInput, 950 | "path must be shorter than SUN_LEN", 951 | )); 952 | } 953 | 954 | storage.sun_family = crate::sys::AF_UNIX as sa_family_t; 955 | // SAFETY: casting `[u8]` to `[i8]` is safe. 956 | let b = unsafe { &*(bytes as *const [u8] as *const [i8]) }; 957 | // `storage` was initialized to zero above, so the path is 958 | // already null terminated. 959 | storage.sun_path[..bytes.len()].copy_from_slice(b); 960 | 961 | let base = storage as *const _ as usize; 962 | let path = &storage.sun_path as *const _ as usize; 963 | let sun_path_offset = path - base; 964 | sun_path_offset + bytes.len() + 1 965 | }; 966 | Ok(unsafe { SockAddr::new(storage, len as socklen_t) }) 967 | } 968 | 969 | /// Windows only API. 970 | impl crate::Socket { 971 | /// Sets `HANDLE_FLAG_INHERIT` using `SetHandleInformation`. 972 | #[cfg(feature = "all")] 973 | pub fn set_no_inherit(&self, no_inherit: bool) -> io::Result<()> { 974 | self._set_no_inherit(no_inherit) 975 | } 976 | 977 | pub(crate) fn _set_no_inherit(&self, no_inherit: bool) -> io::Result<()> { 978 | // NOTE: can't use `syscall!` because it expects the function in the 979 | // `windows_sys::Win32::Networking::WinSock::` path. 980 | let res = unsafe { 981 | SetHandleInformation( 982 | self.as_raw() as HANDLE, 983 | HANDLE_FLAG_INHERIT, 984 | !no_inherit as _, 985 | ) 986 | }; 987 | if res == 0 { 988 | // Zero means error. 989 | Err(io::Error::last_os_error()) 990 | } else { 991 | Ok(()) 992 | } 993 | } 994 | 995 | /// Returns the [`Protocol`] of this socket by checking the `SO_PROTOCOL_INFOW` 996 | /// option on this socket. 997 | /// 998 | /// [`Protocol`]: crate::Protocol 999 | #[cfg(feature = "all")] 1000 | pub fn protocol(&self) -> io::Result> { 1001 | let info = unsafe { 1002 | getsockopt::(self.as_raw(), SOL_SOCKET, SO_PROTOCOL_INFOW)? 1003 | }; 1004 | match info.iProtocol { 1005 | 0 => Ok(None), 1006 | p => Ok(Some(crate::Protocol::from(p))), 1007 | } 1008 | } 1009 | } 1010 | 1011 | impl AsSocket for crate::Socket { 1012 | fn as_socket(&self) -> BorrowedSocket<'_> { 1013 | // SAFETY: lifetime is bound by self. 1014 | unsafe { BorrowedSocket::borrow_raw(self.as_raw() as RawSocket) } 1015 | } 1016 | } 1017 | 1018 | impl AsRawSocket for crate::Socket { 1019 | fn as_raw_socket(&self) -> RawSocket { 1020 | self.as_raw() as RawSocket 1021 | } 1022 | } 1023 | 1024 | impl From for OwnedSocket { 1025 | fn from(sock: crate::Socket) -> OwnedSocket { 1026 | // SAFETY: sock.into_raw() always returns a valid fd. 1027 | unsafe { OwnedSocket::from_raw_socket(sock.into_raw() as RawSocket) } 1028 | } 1029 | } 1030 | 1031 | impl IntoRawSocket for crate::Socket { 1032 | fn into_raw_socket(self) -> RawSocket { 1033 | self.into_raw() as RawSocket 1034 | } 1035 | } 1036 | 1037 | impl From for crate::Socket { 1038 | fn from(fd: OwnedSocket) -> crate::Socket { 1039 | // SAFETY: `OwnedFd` ensures the fd is valid. 1040 | unsafe { crate::Socket::from_raw_socket(fd.into_raw_socket()) } 1041 | } 1042 | } 1043 | 1044 | impl FromRawSocket for crate::Socket { 1045 | unsafe fn from_raw_socket(socket: RawSocket) -> crate::Socket { 1046 | crate::Socket::from_raw(socket as Socket) 1047 | } 1048 | } 1049 | 1050 | #[test] 1051 | fn in_addr_convertion() { 1052 | let ip = Ipv4Addr::new(127, 0, 0, 1); 1053 | let raw = to_in_addr(&ip); 1054 | assert_eq!(unsafe { raw.S_un.S_addr }, 127 << 0 | 1 << 24); 1055 | assert_eq!(from_in_addr(raw), ip); 1056 | 1057 | let ip = Ipv4Addr::new(127, 34, 4, 12); 1058 | let raw = to_in_addr(&ip); 1059 | assert_eq!( 1060 | unsafe { raw.S_un.S_addr }, 1061 | 127 << 0 | 34 << 8 | 4 << 16 | 12 << 24 1062 | ); 1063 | assert_eq!(from_in_addr(raw), ip); 1064 | } 1065 | 1066 | #[test] 1067 | fn in6_addr_convertion() { 1068 | let ip = Ipv6Addr::new(0x2000, 1, 2, 3, 4, 5, 6, 7); 1069 | let raw = to_in6_addr(&ip); 1070 | let want = [ 1071 | 0x2000u16.to_be(), 1072 | 1u16.to_be(), 1073 | 2u16.to_be(), 1074 | 3u16.to_be(), 1075 | 4u16.to_be(), 1076 | 5u16.to_be(), 1077 | 6u16.to_be(), 1078 | 7u16.to_be(), 1079 | ]; 1080 | assert_eq!(unsafe { raw.u.Word }, want); 1081 | assert_eq!(from_in6_addr(raw), ip); 1082 | } 1083 | -------------------------------------------------------------------------------- /tests/data/hello_world.txt: -------------------------------------------------------------------------------- 1 | Hello world! 2 | -------------------------------------------------------------------------------- /tests/data/lorem_ipsum.txt: -------------------------------------------------------------------------------- 1 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec blandit ex vel turpis mollis, sed pretium purus ultricies. Nam sagittis sapien in nisl bibendum pulvinar. Proin sit amet luctus est. Fusce id elit laoreet, varius nunc in, semper massa. Mauris viverra turpis sed metus tincidunt, eu interdum elit ultrices. Ut ut sapien vitae velit aliquet venenatis ut volutpat odio. Aenean pellentesque odio at tellus iaculis porta. Vivamus est orci, luctus eget sollicitudin at, mattis accumsan risus. Donec iaculis a augue sed tristique. Vivamus iaculis ac quam eu tincidunt. Nam in nunc mollis, gravida mauris eu, lobortis nibh. Maecenas lacinia est in purus elementum efficitur. Proin tincidunt bibendum ante nec venenatis. Morbi at orci varius, efficitur nibh a, commodo libero. Pellentesque eget velit maximus, rutrum quam ac, tincidunt enim. Quisque euismod felis non sagittis porta. 2 | 3 | Nulla facilisi. Nullam ac consequat mi, quis eleifend risus. Duis tincidunt, eros in dapibus aliquam, erat metus mollis purus, sit amet lacinia enim velit vitae augue. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus laoreet sagittis justo. Nullam luctus metus facilisis erat euismod, ac dapibus urna feugiat. Mauris varius euismod porta. Donec vestibulum elit id diam tempor, ac accumsan risus accumsan. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Donec sit amet arcu ut libero efficitur tincidunt sed ut sem. Nam in cursus lectus, quis faucibus lacus. Aliquam elit urna, hendrerit at leo quis, rutrum porta velit. Fusce quam dolor, lacinia a tristique sit amet, imperdiet sed mi. Mauris augue nulla, bibendum hendrerit congue id, vulputate ut quam. Nam tempor vel arcu nec volutpat. 4 | 5 | Sed ac risus sagittis, dictum augue at, elementum diam. Fusce nulla velit, auctor sit amet dapibus quis, lobortis a nisi. Quisque orci eros, fermentum et est at, tincidunt lacinia eros. Aenean quis ultrices nunc. Etiam ac nisi sapien. Sed eu metus nec nisl faucibus mattis. Phasellus pharetra arcu nec lorem consequat, at ullamcorper tellus fringilla. Integer lectus ante, mollis a felis et, fringilla pellentesque mi. 6 | 7 | Morbi lorem odio, bibendum quis tortor eget, mattis consequat nisi. Nunc ut justo turpis. Praesent ultricies mi ac dui vestibulum hendrerit ut et nulla. Nulla porta neque arcu, sit amet suscipit nulla iaculis et. Sed hendrerit massa ac nisl ultricies, id mattis metus maximus. Ut suscipit, urna vitae rutrum placerat, sapien ex semper nulla, eu finibus purus magna accumsan dui. Ut sed dolor pellentesque, tempus lorem ut, mollis dui. Donec mi sapien, dignissim ac ultrices a, ullamcorper ut augue. In sagittis leo a nunc pharetra porttitor. Aliquam magna libero, finibus vel augue et, venenatis venenatis sapien. Nullam consequat mi non lectus lacinia, a varius est tempus. Fusce sit amet tellus orci. Curabitur ut risus maximus, pellentesque leo vitae, porttitor ex. Nam eleifend porta nibh vitae luctus. Nulla quis sapien hendrerit metus eleifend condimentum sed non massa. 8 | 9 | Nulla dictum urna justo, et iaculis sem gravida sit amet. Pellentesque in metus quis nibh hendrerit fringilla. Aliquam varius vitae mi vitae fringilla. Donec et ligula non dui suscipit porta vel ac lectus. Fusce in sodales augue. Praesent mattis tincidunt elit. Etiam venenatis finibus ligula, eget viverra lacus dapibus eu. Proin pretium mi sed lacus tincidunt pretium. Integer rhoncus arcu dui. In id lacus volutpat, tristique nulla nec, euismod nulla. Vestibulum rhoncus mauris tincidunt pellentesque auctor. Maecenas nunc ante, tempor ut ipsum et, viverra pretium ligula. Quisque ullamcorper turpis libero, eu convallis velit malesuada sed. 10 | 11 | Nulla libero nulla, dapibus vitae est non, congue rhoncus risus. Aliquam eleifend lectus eu nulla commodo, nec consectetur risus volutpat. Mauris dapibus risus eu nunc semper dignissim. Mauris a dignissim nibh. Donec maximus, est sed varius vestibulum, diam ligula porttitor nibh, a sagittis mauris augue ut est. Aenean sed enim magna. Nam cursus tellus lacus, ac aliquam arcu sollicitudin congue. Etiam a purus quis nisi congue tempor vel vitae quam. Pellentesque at placerat nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris et iaculis leo, ut tempor elit. Donec accumsan gravida vulputate. Praesent consectetur diam tristique, finibus libero eget, elementum erat. Aenean cursus suscipit tristique. 12 | 13 | Praesent ante sapien, pellentesque quis mauris id, pharetra mattis diam. Quisque quis elementum purus. Curabitur in sem molestie, hendrerit sem vehicula, vulputate risus. Mauris sit amet leo condimentum, consequat arcu nec, molestie nisl. Fusce eleifend sit amet dui a porta. Nullam et eleifend sem, a porttitor lectus. Duis nec porttitor velit. 14 | 15 | Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Suspendisse in interdum tortor. Proin at justo est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed pulvinar ullamcorper congue. Donec ultricies aliquam turpis, sollicitudin bibendum dui. Nam volutpat, neque et consequat porttitor, orci elit aliquet est, a sagittis risus magna ac nunc. 16 | 17 | Pellentesque ut pretium leo, quis bibendum erat. Nulla ornare diam libero, vel lobortis orci lobortis ac. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vestibulum bibendum id libero at egestas. Suspendisse a mollis sem. Nunc gravida massa sed dui posuere lobortis. Aenean eget malesuada justo, eget ullamcorper ipsum. Sed fringilla enim vel sapien condimentum fringilla. Vivamus a massa sapien. Sed consequat ipsum orci, nec ultricies felis aliquet ac. Mauris mattis velit at urna pharetra tristique. 18 | 19 | Praesent urna tellus, pellentesque eu molestie sed, suscipit id felis. Quisque euismod odio vel imperdiet facilisis. Mauris mauris urna, vehicula eget tempus vel, aliquet sed ex. Donec orci mauris, mattis sit amet augue sit amet, molestie maximus lacus. Fusce a dapibus lectus, eu placerat ligula. Sed nec interdum mauris. Nullam auctor vel tortor in suscipit. Mauris dui orci, interdum ac laoreet eget, fringilla a orci. Duis mollis viverra metus, vitae finibus dolor aliquet quis. Aliquam erat volutpat. Aenean in mi scelerisque leo varius facilisis. 20 | 21 | Proin tincidunt mauris sed enim fringilla, quis ullamcorper nibh interdum. Nam quis libero in nunc gravida sodales ut dapibus libero. Vivamus leo augue, mattis ut turpis sit amet, vestibulum semper sapien. Quisque erat purus, placerat viverra sem quis, cursus volutpat nisi. Proin a lectus in elit luctus mattis. Vivamus metus diam, vulputate nec blandit sed, ultricies quis lectus. Donec ante dui, consectetur vitae justo ut, efficitur scelerisque ante. Aenean aliquet ut neque non dictum. Nunc at gravida arcu. 22 | 23 | Pellentesque dui neque, malesuada sit amet porta ac, lobortis id felis. Donec viverra erat quis ante finibus, a euismod orci bibendum. Sed vel risus non lacus fermentum egestas vitae at nisi. Pellentesque ullamcorper vestibulum fermentum. Mauris ante mi, fringilla non dui at, dignissim posuere velit. Duis dictum nibh quis massa sollicitudin convallis. Phasellus dapibus turpis convallis, pretium arcu sit amet, fringilla turpis. Vivamus ut risus tincidunt, blandit tellus at, fermentum mi. Etiam ut metus ut lorem consectetur sagittis. 24 | 25 | Praesent ut velit ut purus dapibus eleifend id et lectus. Sed pharetra in magna lacinia pharetra. Praesent tincidunt sem a gravida eleifend. Maecenas suscipit mi ut nisi viverra placerat. Cras sed faucibus massa. Duis sit amet imperdiet odio. Nulla non magna cursus, pharetra lacus sed, lacinia velit. Nunc luctus pretium elit quis tempor. Aenean nec felis nec ante facilisis eleifend. Nulla faucibus commodo fermentum. 26 | 27 | Proin convallis imperdiet facilisis. Ut nec dapibus leo. Pellentesque aliquam hendrerit ipsum mollis convallis. Sed laoreet vehicula urna, ac ornare risus egestas non. Aenean dapibus commodo massa, at aliquam elit consectetur eu. In libero risus, mollis vel dui vel, bibendum bibendum lorem. Sed a odio ac est sollicitudin pharetra fermentum eleifend dui. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eget justo pellentesque tellus pulvinar porta. Aenean accumsan orci id lorem malesuada ultricies. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aliquam eleifend elementum lacus, et hendrerit nisi tempus ut. Vestibulum id tincidunt ante. Nulla luctus consectetur hendrerit. Praesent et bibendum ipsum, a volutpat purus. Suspendisse quis tellus velit. 28 | 29 | Aliquam erat volutpat. Vestibulum in felis posuere, sodales turpis in, mollis augue. Quisque placerat pretium tortor, nec vulputate erat dapibus sit amet. Morbi vehicula fringilla finibus. Ut congue ligula tellus, et posuere purus rhoncus ut. Proin fermentum ligula eget augue ullamcorper mollis. Vivamus venenatis fringilla odio, sit amet tempus metus porta at. Nunc eget libero tincidunt, viverra lorem sed, congue nibh. Aliquam id dolor mollis, rutrum mi ac, lobortis nunc. Interdum et malesuada fames ac ante ipsum primis in faucibus. 30 | 31 | Pellentesque quis pellentesque arcu. Duis iaculis diam in neque mattis, at rutrum nulla egestas. Curabitur feugiat efficitur convallis. Donec sit amet risus ex. Donec vel tortor at quam aliquet porttitor sagittis gravida justo. Morbi at mattis eros, non condimentum diam. Maecenas dictum pharetra ex. Sed in arcu eu purus vehicula placerat nec blandit purus. In ex augue, dignissim in velit eget, ornare convallis mi. Cras ut orci quis neque vulputate rutrum id id quam. 32 | 33 | In molestie orci id est mollis, eu varius odio tempor. Nulla sapien tellus, tempus volutpat lacus volutpat, dapibus varius orci. Aliquam risus felis, varius sed vehicula quis, elementum et urna. Quisque purus nunc, hendrerit eget imperdiet euismod, eleifend sit amet erat. Integer sed lacus quis sapien hendrerit ultrices. Fusce vehicula nulla nisl, sed maximus est scelerisque id. Pellentesque mattis, sapien quis accumsan rutrum, lacus odio blandit est, non malesuada velit diam sed enim. 34 | 35 | Mauris scelerisque laoreet ex, sed eleifend justo fringilla in. Nullam ligula urna, imperdiet sit amet luctus sed, placerat sed est. In maximus sem vitae est lacinia pellentesque. Ut congue dolor at ligula porta, egestas pulvinar nulla dignissim. Ut accumsan viverra mauris, eget eleifend nibh aliquet imperdiet. Donec in pretium turpis. Nam accumsan ligula lectus, consectetur eleifend arcu tempus dignissim. Nam malesuada vulputate mauris, et lobortis dui feugiat sit amet. Pellentesque pulvinar mauris iaculis urna lobortis rutrum. Integer tristique laoreet accumsan. Suspendisse potenti. Quisque finibus eleifend eleifend. Nam sagittis, enim et sodales placerat, quam sapien pulvinar libero, et mattis est purus id augue. Praesent vitae faucibus diam. In elementum vel mi eget finibus. Morbi non nisi et erat auctor pretium. 36 | 37 | Quisque ac tortor nulla. Suspendisse imperdiet magna vel elit dapibus aliquet. Fusce massa arcu, semper vitae orci quis, facilisis semper leo. Quisque eget scelerisque magna. Sed et neque ut quam semper cursus quis a sem. Nulla in erat eu nibh dapibus venenatis. Vestibulum tincidunt lacinia felis, at vulputate risus mollis at. Suspendisse vitae justo dapibus, auctor massa quis, consequat felis. Proin id enim urna. Mauris sed eleifend tortor. Cras semper mi sem. 38 | 39 | Sed quis eros et nibh pretium malesuada. Etiam ac lacus sit amet mi placerat lobortis. Proin dignissim lacus non nulla dictum, sed lobortis quam vehicula. Ut quis gravida urna, facilisis pretium quam. Quisque at tristique lacus. Duis tempus augue eu eleifend porttitor. Nulla diam sapien, posuere non pretium imperdiet, egestas nec purus. Ut neque purus, cursus a augue nec, sollicitudin ultrices erat. Vestibulum hendrerit bibendum massa sed egestas. Aenean rhoncus tincidunt nibh, vitae finibus mi porttitor sed. Proin elementum quis nunc at pretium. Suspendisse potenti. 40 | -------------------------------------------------------------------------------- /tests/socket.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::bool_assert_comparison)] 2 | #[cfg(all( 3 | feature = "all", 4 | any( 5 | target_os = "android", 6 | target_os = "freebsd", 7 | target_os = "ios", 8 | target_os = "visionos", 9 | target_os = "linux", 10 | target_os = "macos", 11 | target_os = "tvos", 12 | target_os = "watchos", 13 | ) 14 | ))] 15 | use std::fs::File; 16 | use std::io; 17 | #[cfg(not(any(target_os = "redox", target_os = "vita")))] 18 | use std::io::IoSlice; 19 | use std::io::Read; 20 | use std::io::Write; 21 | #[cfg(not(target_os = "vita"))] 22 | use std::mem::MaybeUninit; 23 | use std::mem::{self}; 24 | use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpStream}; 25 | #[cfg(not(any(target_os = "redox", target_os = "vita")))] 26 | use std::net::{Ipv6Addr, SocketAddrV6}; 27 | #[cfg(all( 28 | feature = "all", 29 | any( 30 | target_os = "android", 31 | target_os = "freebsd", 32 | target_os = "ios", 33 | target_os = "visionos", 34 | target_os = "linux", 35 | target_os = "macos", 36 | target_os = "tvos", 37 | target_os = "watchos", 38 | ) 39 | ))] 40 | use std::num::NonZeroUsize; 41 | #[cfg(unix)] 42 | use std::os::unix::io::AsRawFd; 43 | #[cfg(windows)] 44 | use std::os::windows::io::AsRawSocket; 45 | #[cfg(unix)] 46 | use std::path::Path; 47 | use std::str; 48 | #[cfg(not(target_os = "vita"))] 49 | use std::thread; 50 | use std::time::Duration; 51 | use std::{env, fs}; 52 | 53 | #[cfg(windows)] 54 | use windows_sys::Win32::Foundation::{GetHandleInformation, HANDLE_FLAG_INHERIT}; 55 | 56 | #[cfg(not(any(target_os = "redox", target_os = "vita")))] 57 | use socket2::MaybeUninitSlice; 58 | #[cfg(not(target_os = "vita"))] 59 | use socket2::TcpKeepalive; 60 | use socket2::{Domain, Protocol, SockAddr, Socket, Type}; 61 | 62 | #[test] 63 | fn domain_for_address() { 64 | let ipv4: SocketAddr = "127.0.0.1:8080".parse().unwrap(); 65 | assert!(ipv4.is_ipv4()); 66 | let ipv6: SocketAddr = "[::1]:8080".parse().unwrap(); 67 | assert!(ipv6.is_ipv6()); 68 | 69 | assert_eq!(Domain::for_address(ipv4), Domain::IPV4); 70 | assert_eq!(Domain::for_address(ipv6), Domain::IPV6); 71 | } 72 | 73 | #[test] 74 | fn domain_fmt_debug() { 75 | let tests = &[ 76 | (Domain::IPV4, "AF_INET"), 77 | (Domain::IPV6, "AF_INET6"), 78 | (Domain::UNIX, "AF_UNIX"), 79 | #[cfg(all(feature = "all", any(target_os = "fuchsia", target_os = "linux")))] 80 | (Domain::PACKET, "AF_PACKET"), 81 | #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))] 82 | (Domain::VSOCK, "AF_VSOCK"), 83 | (0.into(), "AF_UNSPEC"), 84 | (500.into(), "500"), 85 | ]; 86 | 87 | let mut buf = Vec::new(); 88 | for (input, want) in tests { 89 | buf.clear(); 90 | write!(buf, "{input:?}").unwrap(); 91 | let got = str::from_utf8(&buf).unwrap(); 92 | assert_eq!(got, *want); 93 | } 94 | } 95 | 96 | #[test] 97 | fn type_fmt_debug() { 98 | let tests = &[ 99 | (Type::STREAM, "SOCK_STREAM"), 100 | (Type::DGRAM, "SOCK_DGRAM"), 101 | #[cfg(feature = "all")] 102 | (Type::SEQPACKET, "SOCK_SEQPACKET"), 103 | #[cfg(all(feature = "all", not(target_os = "redox")))] 104 | (Type::RAW, "SOCK_RAW"), 105 | (500.into(), "500"), 106 | ]; 107 | 108 | let mut buf = Vec::new(); 109 | for (input, want) in tests { 110 | buf.clear(); 111 | write!(buf, "{input:?}").unwrap(); 112 | let got = str::from_utf8(&buf).unwrap(); 113 | assert_eq!(got, *want); 114 | } 115 | } 116 | 117 | #[test] 118 | fn protocol_fmt_debug() { 119 | let tests = &[ 120 | (Protocol::ICMPV4, "IPPROTO_ICMP"), 121 | (Protocol::ICMPV6, "IPPROTO_ICMPV6"), 122 | (Protocol::TCP, "IPPROTO_TCP"), 123 | (Protocol::UDP, "IPPROTO_UDP"), 124 | #[cfg(target_os = "linux")] 125 | (Protocol::MPTCP, "IPPROTO_MPTCP"), 126 | #[cfg(all(feature = "all", target_os = "linux"))] 127 | (Protocol::DCCP, "IPPROTO_DCCP"), 128 | #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))] 129 | (Protocol::SCTP, "IPPROTO_SCTP"), 130 | (500.into(), "500"), 131 | ]; 132 | 133 | let mut buf = Vec::new(); 134 | for (input, want) in tests { 135 | buf.clear(); 136 | write!(buf, "{input:?}").unwrap(); 137 | let got = str::from_utf8(&buf).unwrap(); 138 | assert_eq!(got, *want); 139 | } 140 | } 141 | 142 | #[test] 143 | #[should_panic = "tried to create a `Socket` with an invalid fd"] 144 | #[cfg(unix)] 145 | fn from_invalid_raw_fd_should_panic() { 146 | use std::os::unix::io::FromRawFd; 147 | let _socket = unsafe { Socket::from_raw_fd(-1) }; 148 | } 149 | 150 | #[test] 151 | fn socket_address_unix() { 152 | let string = "/tmp/socket"; 153 | let addr = SockAddr::unix(string).unwrap(); 154 | assert!(addr.as_socket_ipv4().is_none()); 155 | assert!(addr.as_socket_ipv6().is_none()); 156 | assert!(!addr.is_ipv4()); 157 | assert!(!addr.is_ipv6()); 158 | assert!(addr.is_unix()); 159 | assert_eq!(addr.domain(), Domain::UNIX); 160 | #[cfg(unix)] 161 | { 162 | assert!(!addr.is_unnamed()); 163 | assert_eq!(addr.as_pathname(), Some(Path::new(string))); 164 | assert_eq!(addr.as_abstract_namespace(), None); 165 | let unix = addr.as_unix().unwrap(); 166 | assert_eq!(addr.as_pathname(), unix.as_pathname()); 167 | } 168 | } 169 | 170 | #[test] 171 | fn socket_address_unix_unnamed() { 172 | let addr = SockAddr::unix("").unwrap(); 173 | assert!(addr.as_socket_ipv4().is_none()); 174 | assert!(addr.as_socket_ipv6().is_none()); 175 | assert!(!addr.is_ipv4()); 176 | assert!(!addr.is_ipv6()); 177 | assert!(addr.is_unix()); 178 | assert_eq!(addr.domain(), Domain::UNIX); 179 | #[cfg(unix)] 180 | { 181 | assert!(addr.is_unnamed()); 182 | assert_eq!(addr.as_pathname(), None); 183 | assert_eq!(addr.as_abstract_namespace(), None); 184 | assert!(addr.as_unix().is_none()); 185 | } 186 | } 187 | 188 | #[test] 189 | #[cfg(all( 190 | any(target_os = "linux", target_os = "android", target_os = "cygwin"), 191 | feature = "all", 192 | ))] 193 | fn socket_address_unix_abstract_namespace() { 194 | let path = "\0h".repeat(108 / 2); 195 | let addr = SockAddr::unix(&path).unwrap(); 196 | assert_eq!( 197 | addr.len() as usize, 198 | std::mem::size_of::() 199 | ); 200 | assert!(!addr.is_unnamed()); 201 | // The first byte is the opening null bytes of an abstract address, should not be included. 202 | assert_eq!(addr.as_abstract_namespace(), Some(&path.as_bytes()[1..])); 203 | assert!(addr.as_pathname().is_none()); 204 | assert!(!addr.is_unnamed()); 205 | } 206 | 207 | #[test] 208 | #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))] 209 | fn socket_address_vsock() { 210 | let addr = SockAddr::vsock(1, 9999); 211 | assert!(addr.as_socket_ipv4().is_none()); 212 | assert!(addr.as_socket_ipv6().is_none()); 213 | assert_eq!(addr.as_vsock_address().unwrap(), (1, 9999)); 214 | } 215 | 216 | #[test] 217 | fn set_nonblocking() { 218 | let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); 219 | assert_nonblocking(&socket, false); 220 | 221 | socket.set_nonblocking(true).unwrap(); 222 | assert_nonblocking(&socket, true); 223 | 224 | socket.set_nonblocking(false).unwrap(); 225 | assert_nonblocking(&socket, false); 226 | } 227 | 228 | fn assert_common_flags(socket: &Socket, expected: bool) { 229 | #[cfg(all(unix, not(target_os = "vita")))] 230 | assert_close_on_exec(socket, expected); 231 | #[cfg(any( 232 | target_os = "ios", 233 | target_os = "visionos", 234 | target_os = "macos", 235 | target_os = "tvos", 236 | target_os = "watchos", 237 | ))] 238 | assert_flag_no_sigpipe(socket, expected); 239 | #[cfg(windows)] 240 | assert_flag_no_inherit(socket, expected); 241 | 242 | // Vita does not have process API, so neither SO_NOSIGPIPE nor FD_CLOEXEC are supported on this platform 243 | #[cfg(target_os = "vita")] 244 | { 245 | let _ = socket; 246 | let _ = expected; 247 | } 248 | } 249 | 250 | #[test] 251 | fn common_flags() { 252 | let listener = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); 253 | assert_common_flags(&listener, true); 254 | 255 | listener.bind(&any_ipv4()).unwrap(); 256 | listener.listen(1).unwrap(); 257 | 258 | let client = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); 259 | client.connect(&listener.local_addr().unwrap()).unwrap(); 260 | 261 | let accepted = listener.accept().unwrap().0; 262 | assert_common_flags(&accepted, true); 263 | } 264 | 265 | #[test] 266 | fn no_common_flags() { 267 | let listener = Socket::new_raw(Domain::IPV4, Type::STREAM, None).unwrap(); 268 | assert_common_flags(&listener, false); 269 | 270 | listener.bind(&any_ipv4()).unwrap(); 271 | listener.listen(1).unwrap(); 272 | 273 | let client = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); 274 | client.connect(&listener.local_addr().unwrap()).unwrap(); 275 | 276 | let accepted = listener.accept_raw().unwrap().0; 277 | assert_common_flags(&accepted, false); 278 | } 279 | 280 | #[cfg(all( 281 | feature = "all", 282 | any( 283 | target_os = "android", 284 | target_os = "dragonfly", 285 | target_os = "freebsd", 286 | target_os = "fuchsia", 287 | target_os = "linux", 288 | target_os = "netbsd", 289 | target_os = "openbsd" 290 | ) 291 | ))] 292 | #[test] 293 | fn type_nonblocking() { 294 | let ty = Type::STREAM.nonblocking(); 295 | let socket = Socket::new(Domain::IPV4, ty, None).unwrap(); 296 | assert_nonblocking(&socket, true); 297 | } 298 | 299 | /// Assert that `NONBLOCK` is set on `socket`. 300 | #[cfg(unix)] 301 | #[track_caller] 302 | pub fn assert_nonblocking(socket: &Socket, want: bool) { 303 | #[cfg(all(feature = "all", unix))] 304 | assert_eq!(socket.nonblocking().unwrap(), want, "non-blocking option"); 305 | 306 | #[cfg(not(any(all(feature = "all", unix), target_os = "vita")))] 307 | { 308 | let flags = unsafe { libc::fcntl(socket.as_raw_fd(), libc::F_GETFL) }; 309 | assert_eq!(flags & libc::O_NONBLOCK != 0, want, "non-blocking option"); 310 | } 311 | 312 | #[cfg(all(target_os = "vita", not(feature = "all")))] 313 | { 314 | let mut optval: libc::c_int = 0; 315 | let mut optlen = std::mem::size_of::() as libc::socklen_t; 316 | 317 | let res = unsafe { 318 | libc::getsockopt( 319 | socket.as_raw_fd(), 320 | libc::SOL_SOCKET, 321 | libc::SO_NONBLOCK, 322 | &mut optval as *mut libc::c_int as _, 323 | &mut optlen, 324 | ) 325 | }; 326 | assert_eq!(res, 0, "unable to get non-blocing option"); 327 | assert_eq!(optval > 0, want, "non-blocking option"); 328 | } 329 | } 330 | 331 | #[cfg(windows)] 332 | #[track_caller] 333 | pub fn assert_nonblocking(_: &Socket, _: bool) { 334 | // No way to get this information... 335 | } 336 | 337 | #[cfg(all(unix, feature = "all", not(target_os = "vita")))] 338 | #[test] 339 | fn set_cloexec() { 340 | let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); 341 | assert_close_on_exec(&socket, true); 342 | 343 | socket.set_cloexec(false).unwrap(); 344 | assert_close_on_exec(&socket, false); 345 | 346 | socket.set_cloexec(true).unwrap(); 347 | assert_close_on_exec(&socket, true); 348 | } 349 | 350 | #[cfg(all( 351 | feature = "all", 352 | any( 353 | target_os = "android", 354 | target_os = "dragonfly", 355 | target_os = "freebsd", 356 | target_os = "fuchsia", 357 | target_os = "hurd", 358 | target_os = "linux", 359 | target_os = "netbsd", 360 | target_os = "openbsd" 361 | ) 362 | ))] 363 | #[test] 364 | fn type_cloexec() { 365 | let ty = Type::STREAM.cloexec(); 366 | let socket = Socket::new(Domain::IPV4, ty, None).unwrap(); 367 | assert_close_on_exec(&socket, true); 368 | } 369 | 370 | /// Assert that `CLOEXEC` is set on `socket`. 371 | #[cfg(unix)] 372 | #[track_caller] 373 | pub fn assert_close_on_exec(socket: &S, want: bool) 374 | where 375 | S: AsRawFd, 376 | { 377 | let flags = unsafe { libc::fcntl(socket.as_raw_fd(), libc::F_GETFD) }; 378 | assert_eq!(flags & libc::FD_CLOEXEC != 0, want, "CLOEXEC option"); 379 | } 380 | 381 | #[cfg(all(feature = "all", windows))] 382 | #[test] 383 | fn set_no_inherit() { 384 | let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); 385 | assert_flag_no_inherit(&socket, true); 386 | 387 | socket.set_no_inherit(false).unwrap(); 388 | assert_flag_no_inherit(&socket, false); 389 | 390 | socket.set_no_inherit(true).unwrap(); 391 | assert_flag_no_inherit(&socket, true); 392 | } 393 | 394 | #[cfg(all(feature = "all", windows))] 395 | #[test] 396 | fn type_no_inherit() { 397 | let ty = Type::STREAM.no_inherit(); 398 | let socket = Socket::new(Domain::IPV4, ty, None).unwrap(); 399 | assert_flag_no_inherit(&socket, true); 400 | } 401 | 402 | /// Assert that `FLAG_INHERIT` is not set on `socket`. 403 | #[cfg(windows)] 404 | #[track_caller] 405 | pub fn assert_flag_no_inherit(socket: &S, want: bool) 406 | where 407 | S: AsRawSocket, 408 | { 409 | let mut flags = 0; 410 | if unsafe { GetHandleInformation(socket.as_raw_socket() as _, &mut flags) } == 0 { 411 | let err = io::Error::last_os_error(); 412 | panic!("unexpected error: {err}"); 413 | } 414 | assert_eq!( 415 | flags & HANDLE_FLAG_INHERIT != 0, 416 | !want, 417 | "FLAG_INHERIT option" 418 | ); 419 | } 420 | 421 | #[cfg(all( 422 | feature = "all", 423 | any( 424 | target_os = "ios", 425 | target_os = "visionos", 426 | target_os = "macos", 427 | target_os = "tvos", 428 | target_os = "watchos", 429 | ) 430 | ))] 431 | #[test] 432 | fn set_nosigpipe() { 433 | let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); 434 | assert_flag_no_sigpipe(&socket, true); 435 | 436 | socket.set_nosigpipe(false).unwrap(); 437 | assert_flag_no_sigpipe(&socket, false); 438 | 439 | socket.set_nosigpipe(true).unwrap(); 440 | assert_flag_no_sigpipe(&socket, true); 441 | } 442 | 443 | /// Assert that `SO_NOSIGPIPE` is set on `socket`. 444 | #[cfg(any( 445 | target_os = "ios", 446 | target_os = "visionos", 447 | target_os = "macos", 448 | target_os = "tvos", 449 | target_os = "watchos", 450 | ))] 451 | #[track_caller] 452 | pub fn assert_flag_no_sigpipe(socket: &S, want: bool) 453 | where 454 | S: AsRawFd, 455 | { 456 | use std::mem::size_of; 457 | let mut flags: libc::c_int = 0; 458 | let mut length = size_of::() as libc::socklen_t; 459 | let res = unsafe { 460 | libc::getsockopt( 461 | socket.as_raw_fd(), 462 | libc::SOL_SOCKET, 463 | libc::SO_NOSIGPIPE, 464 | &mut flags as *mut _ as *mut _, 465 | &mut length, 466 | ) 467 | }; 468 | if res != 0 { 469 | panic!("unexpected error: {}", io::Error::last_os_error()); 470 | } 471 | assert_eq!(length as usize, size_of::()); 472 | assert_eq!(flags, want as _, "non-blocking option"); 473 | } 474 | 475 | const DATA: &[u8] = b"hello world"; 476 | 477 | #[test] 478 | fn connect_timeout_unrouteable() { 479 | // This IP is unroutable, so connections should always time out. 480 | let addr = "10.255.255.1:80".parse::().unwrap().into(); 481 | 482 | let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); 483 | match socket.connect_timeout(&addr, Duration::from_millis(250)) { 484 | Ok(_) => panic!("unexpected success"), 485 | Err(ref err) if err.kind() == io::ErrorKind::TimedOut => {} 486 | Err(err) => panic!("unexpected error {}", err), 487 | } 488 | } 489 | 490 | #[test] 491 | #[cfg(not(target_os = "vita"))] // Loopback has special behavior on vita 492 | fn connect_timeout_unbound() { 493 | // Bind and drop a socket to track down a "probably unassigned" port. 494 | let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); 495 | let addr = "127.0.0.1:0".parse::().unwrap().into(); 496 | socket.bind(&addr).unwrap(); 497 | let addr = socket.local_addr().unwrap(); 498 | drop(socket); 499 | 500 | let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); 501 | match socket.connect_timeout(&addr, Duration::from_millis(250)) { 502 | Ok(_) => panic!("unexpected success"), 503 | Err(ref err) 504 | if err.kind() == io::ErrorKind::ConnectionRefused 505 | || err.kind() == io::ErrorKind::TimedOut => {} 506 | Err(err) => panic!("unexpected error {}", err), 507 | } 508 | } 509 | 510 | #[test] 511 | fn connect_timeout_valid() { 512 | let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); 513 | socket 514 | .bind(&"127.0.0.1:0".parse::().unwrap().into()) 515 | .unwrap(); 516 | socket.listen(128).unwrap(); 517 | 518 | let addr = socket.local_addr().unwrap(); 519 | 520 | let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); 521 | socket 522 | .connect_timeout(&addr, Duration::from_millis(250)) 523 | .unwrap(); 524 | } 525 | 526 | #[test] 527 | #[cfg(all(feature = "all", unix))] 528 | fn pair() { 529 | let (mut a, mut b) = Socket::pair(Domain::UNIX, Type::STREAM, None).unwrap(); 530 | let _ = a.write(DATA).unwrap(); 531 | let mut buf = [0; DATA.len() + 1]; 532 | let n = b.read(&mut buf).unwrap(); 533 | assert_eq!(n, DATA.len()); 534 | assert_eq!(&buf[..n], DATA); 535 | } 536 | 537 | fn unix_sockets_supported() -> bool { 538 | #[cfg(windows)] 539 | { 540 | // Only some versions of Windows support Unix sockets. 541 | match Socket::new(Domain::UNIX, Type::STREAM, None) { 542 | Ok(_) => {} 543 | Err(err) 544 | if err.raw_os_error() 545 | == Some(windows_sys::Win32::Networking::WinSock::WSAEAFNOSUPPORT as i32) => 546 | { 547 | return false; 548 | } 549 | Err(err) => panic!("socket error: {err}"), 550 | } 551 | } 552 | 553 | #[cfg(target_os = "vita")] 554 | return false; 555 | 556 | #[cfg(not(target_os = "vita"))] 557 | true 558 | } 559 | 560 | #[test] 561 | fn unix() { 562 | if !unix_sockets_supported() { 563 | return; 564 | } 565 | let mut path = env::temp_dir(); 566 | path.push("socket2"); 567 | let _ = fs::remove_dir_all(&path); 568 | fs::create_dir_all(&path).unwrap(); 569 | path.push("unix"); 570 | 571 | let addr = SockAddr::unix(path).unwrap(); 572 | 573 | let listener = Socket::new(Domain::UNIX, Type::STREAM, None).unwrap(); 574 | #[cfg(target_os = "cygwin")] 575 | listener.set_no_peercred().unwrap(); 576 | listener.bind(&addr).unwrap(); 577 | listener.listen(10).unwrap(); 578 | 579 | let mut a = Socket::new(Domain::UNIX, Type::STREAM, None).unwrap(); 580 | #[cfg(target_os = "cygwin")] 581 | a.set_no_peercred().unwrap(); 582 | a.connect(&addr).unwrap(); 583 | let mut b = listener.accept().unwrap().0; 584 | 585 | let _ = a.write(DATA).unwrap(); 586 | let mut buf = [0; DATA.len() + 1]; 587 | let n = b.read(&mut buf).unwrap(); 588 | assert_eq!(n, DATA.len()); 589 | assert_eq!(&buf[..n], DATA); 590 | } 591 | 592 | #[test] 593 | #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))] 594 | #[ignore = "using VSOCK family requires optional kernel support (works when enabled)"] 595 | fn vsock() { 596 | let addr = SockAddr::vsock(libc::VMADDR_CID_LOCAL, libc::VMADDR_PORT_ANY); 597 | 598 | let listener = Socket::new(Domain::VSOCK, Type::STREAM, None).unwrap(); 599 | listener.bind(&addr).unwrap(); 600 | listener.listen(10).unwrap(); 601 | 602 | let (_, port) = listener.local_addr().unwrap().as_vsock_address().unwrap(); 603 | let addr = SockAddr::vsock(libc::VMADDR_CID_LOCAL, port); 604 | let mut a = Socket::new(Domain::VSOCK, Type::STREAM, None).unwrap(); 605 | a.connect(&addr).unwrap(); 606 | let mut b = listener.accept().unwrap().0; 607 | 608 | let _ = a.write(DATA).unwrap(); 609 | let mut buf = [0; DATA.len() + 1]; 610 | let n = b.read(&mut buf).unwrap(); 611 | assert_eq!(n, DATA.len()); 612 | assert_eq!(&buf[..n], DATA); 613 | } 614 | 615 | #[test] 616 | #[cfg(not(target_os = "vita"))] // Vita does not support OOB 617 | fn out_of_band() { 618 | let listener = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); 619 | listener.bind(&any_ipv4()).unwrap(); 620 | listener.listen(1).unwrap(); 621 | 622 | let sender = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); 623 | sender.bind(&any_ipv4()).unwrap(); 624 | sender.connect(&listener.local_addr().unwrap()).unwrap(); 625 | 626 | let (receiver, _) = listener.accept().unwrap(); 627 | 628 | sender.send(DATA).unwrap(); 629 | 630 | const FIRST: &[u8] = b"!"; 631 | assert_eq!(sender.send_out_of_band(FIRST).unwrap(), FIRST.len()); 632 | // On macOS if no `MSG_OOB` is available it will return `EINVAL`, to prevent 633 | // this from happening we'll sleep to ensure the data is present. 634 | thread::sleep(Duration::from_millis(10)); 635 | 636 | let mut buf = [MaybeUninit::new(1); DATA.len() + 1]; 637 | let n = receiver.recv_out_of_band(&mut buf).unwrap(); 638 | assert_eq!(n, FIRST.len()); 639 | assert_eq!(unsafe { assume_init(&buf[..n]) }, FIRST); 640 | 641 | let n = receiver.recv(&mut buf).unwrap(); 642 | assert_eq!(n, DATA.len()); 643 | assert_eq!(unsafe { assume_init(&buf[..n]) }, DATA); 644 | } 645 | 646 | #[test] 647 | #[cfg(not(any(target_os = "redox", target_os = "vita")))] 648 | fn udp_peek_sender() { 649 | let (socket_a, socket_b) = udp_pair_unconnected(); 650 | 651 | let socket_a_addr = socket_a.local_addr().unwrap(); 652 | let socket_b_addr = socket_b.local_addr().unwrap(); 653 | 654 | socket_b.send_to(b"Hello, world!", &socket_a_addr).unwrap(); 655 | 656 | let sender_addr = socket_a.peek_sender().unwrap(); 657 | 658 | assert_eq!(sender_addr.as_socket(), socket_b_addr.as_socket()); 659 | } 660 | 661 | #[test] 662 | #[cfg(not(any(target_os = "redox", target_os = "vita")))] 663 | fn send_recv_vectored() { 664 | let (socket_a, socket_b) = udp_pair_connected(); 665 | 666 | let sent = socket_a 667 | .send_vectored(&[ 668 | IoSlice::new(b"the"), 669 | IoSlice::new(b"weeknight"), 670 | IoSlice::new(b"would"), 671 | IoSlice::new(b"yellow"), 672 | ]) 673 | .unwrap(); 674 | assert_eq!(sent, 23); 675 | 676 | let mut the = [MaybeUninit::new(1); 3]; 677 | let mut wee = [MaybeUninit::new(2); 3]; 678 | let mut knight = [MaybeUninit::new(3); 6]; 679 | let mut would = [MaybeUninit::new(4); 5]; 680 | let mut yell = [MaybeUninit::new(5); 4]; 681 | let mut ow = [MaybeUninit::new(6); 2]; 682 | 683 | let (received, flags) = socket_b 684 | .recv_vectored(&mut [ 685 | MaybeUninitSlice::new(&mut the), 686 | MaybeUninitSlice::new(&mut wee), 687 | MaybeUninitSlice::new(&mut knight), 688 | MaybeUninitSlice::new(&mut would), 689 | MaybeUninitSlice::new(&mut yell), 690 | MaybeUninitSlice::new(&mut ow), 691 | ]) 692 | .unwrap(); 693 | assert_eq!(received, 23); 694 | #[cfg(all(unix, not(target_os = "redox")))] 695 | assert_eq!(flags.is_end_of_record(), false); 696 | #[cfg(all(unix, not(target_os = "redox")))] 697 | assert_eq!(flags.is_out_of_band(), false); 698 | assert_eq!(flags.is_truncated(), false); 699 | 700 | assert_eq!(unsafe { assume_init(&the) }, b"the"); 701 | assert_eq!(unsafe { assume_init(&wee) }, b"wee"); 702 | assert_eq!(unsafe { assume_init(&knight) }, b"knight"); 703 | assert_eq!(unsafe { assume_init(&would) }, b"would"); 704 | assert_eq!(unsafe { assume_init(&yell) }, b"yell"); 705 | assert_eq!(unsafe { assume_init(&ow) }, b"ow"); 706 | } 707 | 708 | #[test] 709 | #[cfg(not(any(target_os = "redox", target_os = "vita")))] 710 | fn send_from_recv_to_vectored() { 711 | let (socket_a, socket_b) = udp_pair_unconnected(); 712 | let addr_a = socket_a.local_addr().unwrap(); 713 | let addr_b = socket_b.local_addr().unwrap(); 714 | 715 | let sent = socket_a 716 | .send_to_vectored( 717 | &[ 718 | IoSlice::new(b"surgeon"), 719 | IoSlice::new(b"has"), 720 | IoSlice::new(b"menswear"), 721 | ], 722 | &addr_b, 723 | ) 724 | .unwrap(); 725 | assert_eq!(sent, 18); 726 | 727 | let mut surgeon = [MaybeUninit::new(10); 7]; 728 | let mut has = [MaybeUninit::new(11); 3]; 729 | let mut men = [MaybeUninit::new(12); 3]; 730 | let mut swear = [MaybeUninit::new(13); 5]; 731 | let (received, flags, addr) = socket_b 732 | .recv_from_vectored(&mut [ 733 | MaybeUninitSlice::new(&mut surgeon), 734 | MaybeUninitSlice::new(&mut has), 735 | MaybeUninitSlice::new(&mut men), 736 | MaybeUninitSlice::new(&mut swear), 737 | ]) 738 | .unwrap(); 739 | 740 | assert_eq!(received, 18); 741 | #[cfg(all(unix, not(target_os = "redox")))] 742 | assert_eq!(flags.is_end_of_record(), false); 743 | #[cfg(all(unix, not(target_os = "redox")))] 744 | assert_eq!(flags.is_out_of_band(), false); 745 | assert_eq!(flags.is_truncated(), false); 746 | #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))] 747 | assert_eq!(flags.is_confirm(), false); 748 | #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))] 749 | assert_eq!(flags.is_dontroute(), false); 750 | assert_eq!( 751 | addr.as_socket_ipv6().unwrap(), 752 | addr_a.as_socket_ipv6().unwrap() 753 | ); 754 | 755 | assert_eq!(unsafe { assume_init(&surgeon) }, b"surgeon"); 756 | assert_eq!(unsafe { assume_init(&has) }, b"has"); 757 | assert_eq!(unsafe { assume_init(&men) }, b"men"); 758 | assert_eq!(unsafe { assume_init(&swear) }, b"swear"); 759 | } 760 | 761 | #[test] 762 | #[cfg(not(any(target_os = "redox", target_os = "vita")))] 763 | fn sendmsg() { 764 | let (socket_a, socket_b) = udp_pair_unconnected(); 765 | 766 | const DATA: &[u8] = b"Hello, World!"; 767 | 768 | let bufs = &[IoSlice::new(DATA)]; 769 | let addr_b = socket_b.local_addr().unwrap(); 770 | let msg = socket2::MsgHdr::new().with_addr(&addr_b).with_buffers(bufs); 771 | let sent = socket_a.sendmsg(&msg, 0).unwrap(); 772 | assert_eq!(sent, DATA.len()); 773 | 774 | let mut buf = Vec::with_capacity(DATA.len() + 1); 775 | let received = socket_b.recv(buf.spare_capacity_mut()).unwrap(); 776 | assert_eq!(received, DATA.len()); 777 | } 778 | 779 | #[test] 780 | #[cfg(not(any(target_os = "redox", target_os = "vita")))] 781 | fn recv_vectored_truncated() { 782 | let (socket_a, socket_b) = udp_pair_connected(); 783 | 784 | let sent = socket_a 785 | .send(b"do not feed the gremlins after midnight") 786 | .unwrap(); 787 | assert_eq!(sent, 39); 788 | 789 | let mut buffer = [MaybeUninit::new(20); 24]; 790 | 791 | let (received, flags) = socket_b 792 | .recv_vectored(&mut [MaybeUninitSlice::new(&mut buffer)]) 793 | .unwrap(); 794 | assert_eq!(received, 24); 795 | assert_eq!(flags.is_truncated(), true); 796 | assert_eq!(unsafe { assume_init(&buffer) }, b"do not feed the gremlins"); 797 | } 798 | 799 | #[test] 800 | #[cfg(not(any(target_os = "redox", target_os = "vita")))] 801 | fn recv_from_vectored_truncated() { 802 | let (socket_a, socket_b) = udp_pair_unconnected(); 803 | let addr_a = socket_a.local_addr().unwrap(); 804 | let addr_b = socket_b.local_addr().unwrap(); 805 | 806 | let sent = socket_a 807 | .send_to(b"do not feed the gremlins after midnight", &addr_b) 808 | .unwrap(); 809 | assert_eq!(sent, 39); 810 | 811 | let mut buffer = [MaybeUninit::new(30); 24]; 812 | 813 | let (received, flags, addr) = socket_b 814 | .recv_from_vectored(&mut [MaybeUninitSlice::new(&mut buffer)]) 815 | .unwrap(); 816 | assert_eq!(received, 24); 817 | assert_eq!(flags.is_truncated(), true); 818 | assert_eq!( 819 | addr.as_socket_ipv6().unwrap(), 820 | addr_a.as_socket_ipv6().unwrap() 821 | ); 822 | assert_eq!(unsafe { assume_init(&buffer) }, b"do not feed the gremlins"); 823 | } 824 | 825 | /// Create a pair of non-connected UDP sockets suitable for unit tests. 826 | #[cfg(not(any(target_os = "redox", target_os = "vita")))] 827 | fn udp_pair_unconnected() -> (Socket, Socket) { 828 | // Use ephemeral ports assigned by the OS. 829 | let unspecified_addr = SocketAddrV6::new(Ipv6Addr::LOCALHOST, 0, 0, 0); 830 | let socket_a = Socket::new(Domain::IPV6, Type::DGRAM, None).unwrap(); 831 | let socket_b = Socket::new(Domain::IPV6, Type::DGRAM, None).unwrap(); 832 | 833 | socket_a.bind(&unspecified_addr.into()).unwrap(); 834 | socket_b.bind(&unspecified_addr.into()).unwrap(); 835 | 836 | // Set low timeouts to prevent the tests from blocking. 837 | socket_a 838 | .set_read_timeout(Some(std::time::Duration::from_millis(10))) 839 | .unwrap(); 840 | socket_b 841 | .set_read_timeout(Some(std::time::Duration::from_millis(10))) 842 | .unwrap(); 843 | socket_a 844 | .set_write_timeout(Some(std::time::Duration::from_millis(10))) 845 | .unwrap(); 846 | socket_b 847 | .set_write_timeout(Some(std::time::Duration::from_millis(10))) 848 | .unwrap(); 849 | 850 | (socket_a, socket_b) 851 | } 852 | 853 | /// Create a pair of connected UDP sockets suitable for unit tests. 854 | #[cfg(not(any(target_os = "redox", target_os = "vita")))] 855 | fn udp_pair_connected() -> (Socket, Socket) { 856 | let (socket_a, socket_b) = udp_pair_unconnected(); 857 | 858 | let addr_a = socket_a.local_addr().unwrap(); 859 | let addr_b = socket_b.local_addr().unwrap(); 860 | socket_a.connect(&addr_b).unwrap(); 861 | socket_b.connect(&addr_a).unwrap(); 862 | 863 | (socket_a, socket_b) 864 | } 865 | 866 | #[test] 867 | #[cfg(not(target_os = "vita"))] 868 | fn tcp_keepalive() { 869 | let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); 870 | let params = TcpKeepalive::new().with_time(Duration::from_secs(200)); 871 | 872 | #[cfg(all( 873 | feature = "all", 874 | any( 875 | target_os = "dragonfly", 876 | target_os = "freebsd", 877 | target_os = "fuchsia", 878 | target_os = "ios", 879 | target_os = "visionos", 880 | target_os = "linux", 881 | target_os = "macos", 882 | target_os = "netbsd", 883 | target_os = "tvos", 884 | target_os = "watchos", 885 | target_os = "windows", 886 | ) 887 | ))] 888 | let params = params.with_interval(Duration::from_secs(30)); 889 | 890 | #[cfg(all( 891 | feature = "all", 892 | any( 893 | target_os = "dragonfly", 894 | target_os = "freebsd", 895 | target_os = "fuchsia", 896 | target_os = "ios", 897 | target_os = "visionos", 898 | target_os = "linux", 899 | target_os = "macos", 900 | target_os = "netbsd", 901 | target_os = "tvos", 902 | target_os = "watchos", 903 | target_os = "windows" 904 | ) 905 | ))] 906 | let params = params.with_retries(10); 907 | 908 | // Set the parameters. 909 | socket.set_tcp_keepalive(¶ms).unwrap(); 910 | 911 | #[cfg(all( 912 | feature = "all", 913 | not(any(windows, target_os = "haiku", target_os = "openbsd")) 914 | ))] 915 | assert_eq!(socket.keepalive_time().unwrap(), Duration::from_secs(200)); 916 | 917 | #[cfg(all( 918 | feature = "all", 919 | any( 920 | target_os = "android", 921 | target_os = "dragonfly", 922 | target_os = "freebsd", 923 | target_os = "fuchsia", 924 | target_os = "illumos", 925 | target_os = "ios", 926 | target_os = "visionos", 927 | target_os = "linux", 928 | target_os = "macos", 929 | target_os = "netbsd", 930 | target_os = "tvos", 931 | target_os = "watchos", 932 | ) 933 | ))] 934 | assert_eq!( 935 | socket.keepalive_interval().unwrap(), 936 | Duration::from_secs(30) 937 | ); 938 | 939 | #[cfg(all( 940 | feature = "all", 941 | any( 942 | target_os = "android", 943 | target_os = "dragonfly", 944 | target_os = "freebsd", 945 | target_os = "fuchsia", 946 | target_os = "illumos", 947 | target_os = "ios", 948 | target_os = "visionos", 949 | target_os = "linux", 950 | target_os = "macos", 951 | target_os = "netbsd", 952 | target_os = "tvos", 953 | target_os = "watchos", 954 | target_os = "windows", 955 | ) 956 | ))] 957 | assert_eq!(socket.keepalive_retries().unwrap(), 10); 958 | } 959 | 960 | #[cfg(all(feature = "all", any(target_os = "fuchsia", target_os = "linux")))] 961 | #[test] 962 | #[ignore = "setting `SO_BINDTODEVICE` requires the `CAP_NET_RAW` capability (works when running as root)"] 963 | fn device() { 964 | // Some common network interface on Linux. 965 | const INTERFACES: &[&str] = &["lo", "lo0", "eth0", "wlan0"]; 966 | 967 | let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); 968 | assert_eq!(socket.device().unwrap(), None); 969 | 970 | for interface in INTERFACES.iter() { 971 | if let Err(err) = socket.bind_device(Some(interface.as_bytes())) { 972 | // Network interface is not available try another. 973 | if matches!(err.raw_os_error(), Some(libc::ENODEV)) { 974 | eprintln!("error binding to device (`{interface}`): {err}"); 975 | continue; 976 | } else { 977 | panic!("unexpected error binding device: {}", err); 978 | } 979 | } 980 | assert_eq!( 981 | socket.device().unwrap().as_deref(), 982 | Some(interface.as_bytes()) 983 | ); 984 | 985 | socket.bind_device(None).unwrap(); 986 | assert_eq!(socket.device().unwrap(), None); 987 | // Just need to do it with one interface. 988 | return; 989 | } 990 | 991 | panic!("failed to bind to any device."); 992 | } 993 | 994 | #[cfg(all( 995 | feature = "all", 996 | any( 997 | target_os = "ios", 998 | target_os = "visionos", 999 | target_os = "macos", 1000 | target_os = "tvos", 1001 | target_os = "watchos", 1002 | target_os = "solaris", 1003 | target_os = "illumos", 1004 | ) 1005 | ))] 1006 | #[test] 1007 | fn device() { 1008 | // Some common network interface on macOS. 1009 | const INTERFACES: &[&str] = &["lo\0", "lo0\0", "en0\0"]; 1010 | 1011 | let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); 1012 | assert_eq!(socket.device_index_v4().unwrap(), None); 1013 | 1014 | for interface in INTERFACES.iter() { 1015 | let iface_index = std::num::NonZeroU32::new(unsafe { 1016 | libc::if_nametoindex(interface.as_ptr() as *const _) 1017 | }); 1018 | // If no index is returned, try another interface alias 1019 | if iface_index.is_none() { 1020 | continue; 1021 | } 1022 | if let Err(err) = socket.bind_device_by_index_v4(iface_index) { 1023 | // Network interface is not available try another. 1024 | if matches!(err.raw_os_error(), Some(libc::ENODEV)) { 1025 | eprintln!("error binding to device (`{interface}`): {err}"); 1026 | continue; 1027 | } else { 1028 | panic!("unexpected error binding device: {}", err); 1029 | } 1030 | } 1031 | assert_eq!(socket.device_index_v4().unwrap(), iface_index); 1032 | 1033 | socket.bind_device_by_index_v4(None).unwrap(); 1034 | assert_eq!(socket.device_index_v4().unwrap(), None); 1035 | // Just need to do it with one interface. 1036 | return; 1037 | } 1038 | 1039 | panic!("failed to bind to any device."); 1040 | } 1041 | 1042 | #[cfg(all( 1043 | feature = "all", 1044 | any( 1045 | target_os = "ios", 1046 | target_os = "visionos", 1047 | target_os = "macos", 1048 | target_os = "tvos", 1049 | target_os = "watchos", 1050 | target_os = "solaris", 1051 | target_os = "illumos", 1052 | ) 1053 | ))] 1054 | #[test] 1055 | fn device_v6() { 1056 | // Some common network interface on macOS. 1057 | const INTERFACES: &[&str] = &["lo\0", "lo0\0", "en0\0"]; 1058 | 1059 | let socket = Socket::new(Domain::IPV6, Type::STREAM, None).unwrap(); 1060 | assert_eq!(socket.device_index_v6().unwrap(), None); 1061 | 1062 | for interface in INTERFACES.iter() { 1063 | let iface_index = std::num::NonZeroU32::new(unsafe { 1064 | libc::if_nametoindex(interface.as_ptr() as *const _) 1065 | }); 1066 | // If no index is returned, try another interface alias 1067 | if iface_index.is_none() { 1068 | continue; 1069 | } 1070 | if let Err(err) = socket.bind_device_by_index_v6(iface_index) { 1071 | // Network interface is not available try another. 1072 | if matches!(err.raw_os_error(), Some(libc::ENODEV)) { 1073 | eprintln!("error binding to device (`{interface}`): {err}"); 1074 | continue; 1075 | } else { 1076 | panic!("unexpected error binding device: {}", err); 1077 | } 1078 | } 1079 | assert_eq!(socket.device_index_v6().unwrap(), iface_index); 1080 | 1081 | socket.bind_device_by_index_v6(None).unwrap(); 1082 | assert_eq!(socket.device_index_v6().unwrap(), None); 1083 | // Just need to do it with one interface. 1084 | return; 1085 | } 1086 | 1087 | panic!("failed to bind to any device."); 1088 | } 1089 | 1090 | #[cfg(all( 1091 | feature = "all", 1092 | any( 1093 | target_os = "android", 1094 | target_os = "freebsd", 1095 | target_os = "ios", 1096 | target_os = "visionos", 1097 | target_os = "linux", 1098 | target_os = "macos", 1099 | target_os = "tvos", 1100 | target_os = "watchos", 1101 | ) 1102 | ))] 1103 | #[test] 1104 | fn sendfile() { 1105 | #[derive(Debug)] 1106 | struct TestFile { 1107 | path: &'static str, 1108 | data: &'static [u8], 1109 | } 1110 | 1111 | const HELLO_WORLD: TestFile = TestFile { 1112 | path: "tests/data/hello_world.txt", 1113 | data: include_bytes!("data/hello_world.txt"), 1114 | }; 1115 | 1116 | const LOREM: TestFile = TestFile { 1117 | path: "tests/data/lorem_ipsum.txt", 1118 | data: include_bytes!("data/lorem_ipsum.txt"), 1119 | }; 1120 | 1121 | let listener = Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP)).unwrap(); 1122 | listener.bind(&any_ipv4()).unwrap(); 1123 | listener.listen(1).unwrap(); 1124 | let address = listener.local_addr().unwrap(); 1125 | 1126 | let sender = Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP)).unwrap(); 1127 | sender.connect(&address).unwrap(); 1128 | 1129 | let (receiver, _) = listener.accept().unwrap(); 1130 | 1131 | // Send a simple hello world file. 1132 | { 1133 | let file = File::open(HELLO_WORLD.path).unwrap(); 1134 | let n = sender.sendfile(&file, 0, None).unwrap(); 1135 | assert_eq!(n, HELLO_WORLD.data.len()); 1136 | 1137 | let mut buf = Vec::with_capacity(HELLO_WORLD.data.len() + 1); 1138 | let n = receiver.recv(buf.spare_capacity_mut()).unwrap(); 1139 | assert_eq!(n, HELLO_WORLD.data.len()); 1140 | unsafe { buf.set_len(n) }; 1141 | assert_eq!(buf, HELLO_WORLD.data); 1142 | } 1143 | 1144 | // Send a larger file in two calls. 1145 | { 1146 | let file = File::open(LOREM.path).unwrap(); 1147 | let n = sender 1148 | .sendfile(&file, 0, NonZeroUsize::new(LOREM.data.len() / 2)) 1149 | .unwrap(); 1150 | assert_eq!(n, LOREM.data.len() / 2); 1151 | 1152 | let offset = n; 1153 | let n = sender.sendfile(&file, offset, None).unwrap(); 1154 | assert_eq!(offset + n, LOREM.data.len()); 1155 | 1156 | let mut buf = Vec::with_capacity(LOREM.data.len() + 1); 1157 | let mut total = 0; 1158 | while total < LOREM.data.len() { 1159 | let n = receiver.recv(buf.spare_capacity_mut()).unwrap(); 1160 | unsafe { buf.set_len(buf.len() + n) }; 1161 | total += n; 1162 | } 1163 | assert_eq!(total, LOREM.data.len()); 1164 | assert_eq!(buf, LOREM.data); 1165 | } 1166 | } 1167 | 1168 | #[cfg(all( 1169 | feature = "all", 1170 | any( 1171 | target_os = "android", 1172 | target_os = "freebsd", 1173 | target_os = "fuchsia", 1174 | target_os = "linux", 1175 | ) 1176 | ))] 1177 | #[test] 1178 | fn is_listener() { 1179 | let socket = Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP)).unwrap(); 1180 | assert_eq!(socket.is_listener().unwrap(), false); 1181 | 1182 | socket.listen(1).unwrap(); 1183 | assert_eq!(socket.is_listener().unwrap(), true); 1184 | } 1185 | 1186 | #[cfg(all( 1187 | feature = "all", 1188 | any( 1189 | target_os = "android", 1190 | // TODO: add FreeBSD. 1191 | // target_os = "freebsd", 1192 | target_os = "fuchsia", 1193 | target_os = "linux", 1194 | ) 1195 | ))] 1196 | #[test] 1197 | fn domain() { 1198 | let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); 1199 | assert_eq!(socket.domain().unwrap(), Domain::IPV4); 1200 | 1201 | let socket = Socket::new(Domain::IPV6, Type::STREAM, None).unwrap(); 1202 | assert_eq!(socket.domain().unwrap(), Domain::IPV6); 1203 | 1204 | let socket = Socket::new(Domain::UNIX, Type::STREAM, None).unwrap(); 1205 | assert_eq!(socket.domain().unwrap(), Domain::UNIX); 1206 | } 1207 | 1208 | #[cfg(all( 1209 | feature = "all", 1210 | any( 1211 | target_os = "android", 1212 | target_os = "freebsd", 1213 | target_os = "fuchsia", 1214 | target_os = "linux", 1215 | target_os = "windows", 1216 | ) 1217 | ))] 1218 | #[test] 1219 | fn protocol() { 1220 | let socket = Socket::new(Domain::UNIX, Type::STREAM, None).unwrap(); 1221 | assert_eq!(socket.protocol().unwrap(), None); 1222 | 1223 | /* Don't have permission for this on CI. 1224 | let socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::ICMPV4)).unwrap(); 1225 | assert_eq!(socket.protocol().unwrap(), Some(Protocol::ICMPV4)); 1226 | 1227 | let socket = Socket::new(Domain::IPV6, Type::DGRAM, Some(Protocol::ICMPV6)).unwrap(); 1228 | assert_eq!(socket.protocol().unwrap(), Some(Protocol::ICMPV6)); 1229 | */ 1230 | 1231 | let socket = Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP)).unwrap(); 1232 | assert_eq!(socket.protocol().unwrap(), Some(Protocol::TCP)); 1233 | 1234 | let socket = Socket::new(Domain::IPV6, Type::DGRAM, Some(Protocol::UDP)).unwrap(); 1235 | assert_eq!(socket.protocol().unwrap(), Some(Protocol::UDP)); 1236 | } 1237 | 1238 | #[test] 1239 | fn r#type() { 1240 | let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); 1241 | assert_eq!(socket.r#type().unwrap(), Type::STREAM); 1242 | 1243 | #[cfg(not(target_os = "vita"))] 1244 | { 1245 | let socket = Socket::new(Domain::IPV6, Type::DGRAM, None).unwrap(); 1246 | assert_eq!(socket.r#type().unwrap(), Type::DGRAM); 1247 | } 1248 | 1249 | // macos doesn't support seqpacket 1250 | #[cfg(all( 1251 | unix, 1252 | not(any( 1253 | target_os = "ios", 1254 | target_os = "visionos", 1255 | target_os = "macos", 1256 | target_os = "tvos", 1257 | target_os = "watchos", 1258 | target_os = "vita", 1259 | target_os = "cygwin", 1260 | )), 1261 | feature = "all", 1262 | ))] 1263 | { 1264 | let socket = Socket::new(Domain::UNIX, Type::SEQPACKET, None).unwrap(); 1265 | assert_eq!(socket.r#type().unwrap(), Type::SEQPACKET); 1266 | } 1267 | } 1268 | 1269 | #[cfg(all(feature = "all", target_os = "linux"))] 1270 | #[test] 1271 | fn cpu_affinity() { 1272 | let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); 1273 | 1274 | // NOTE: This requires at least 2 CPU cores. 1275 | let cpu = socket.cpu_affinity().unwrap(); 1276 | let want = usize::from(cpu == 0); 1277 | 1278 | socket.set_cpu_affinity(want).unwrap(); 1279 | assert_eq!(socket.cpu_affinity().unwrap(), want); 1280 | } 1281 | 1282 | #[test] 1283 | fn niche() { 1284 | if mem::size_of::>() == mem::size_of::() { 1285 | assert_eq!(mem::size_of::>(), mem::size_of::()); 1286 | } 1287 | } 1288 | 1289 | fn any_ipv4() -> SockAddr { 1290 | SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0).into() 1291 | } 1292 | 1293 | /// Assume the `buf`fer to be initialised. 1294 | // TODO: replace with `MaybeUninit::slice_assume_init_ref` once stable. 1295 | #[cfg(not(target_os = "vita"))] // Loopback has special behavior on vita 1296 | unsafe fn assume_init(buf: &[MaybeUninit]) -> &[u8] { 1297 | &*(buf as *const [MaybeUninit] as *const [u8]) 1298 | } 1299 | 1300 | /// Macro to create a simple test to set and get a socket option. 1301 | macro_rules! test { 1302 | // Test using the `arg`ument as expected return value. 1303 | ($( #[ $attr: meta ] )* $get_fn: ident, $set_fn: ident ( $arg: expr ) ) => { 1304 | test!($( #[$attr] )* $get_fn, $set_fn($arg), $arg); 1305 | }; 1306 | ($( #[ $attr: meta ] )* $get_fn: ident, $set_fn: ident ( $arg: expr ), $expected: expr ) => { 1307 | #[test] 1308 | $( #[$attr] )* 1309 | fn $get_fn() { 1310 | test!(__ Domain::IPV4, $get_fn, $set_fn($arg), $expected); 1311 | #[cfg(not(target_os = "vita"))] 1312 | test!(__ Domain::IPV6, $get_fn, $set_fn($arg), $expected); 1313 | } 1314 | }; 1315 | // Only test using a IPv4 socket. 1316 | (IPv4 $get_fn: ident, $set_fn: ident ( $arg: expr ) ) => { 1317 | #[test] 1318 | fn $get_fn() { 1319 | test!(__ Domain::IPV4, $get_fn, $set_fn($arg), $arg); 1320 | } 1321 | }; 1322 | // Only test using a IPv6 socket. 1323 | (IPv6 $get_fn: ident, $set_fn: ident ( $arg: expr ) ) => { 1324 | #[test] 1325 | fn $get_fn() { 1326 | test!(__ Domain::IPV6, $get_fn, $set_fn($arg), $arg); 1327 | } 1328 | }; 1329 | 1330 | // Internal to this macro. 1331 | (__ $ty: expr, $get_fn: ident, $set_fn: ident ( $arg: expr ), $expected: expr ) => { 1332 | let socket = Socket::new($ty, Type::STREAM, None).expect("failed to create `Socket`"); 1333 | 1334 | let initial = socket.$get_fn().expect("failed to get initial value"); 1335 | let arg = $arg; 1336 | assert_ne!(initial, arg, "initial value and argument are the same"); 1337 | 1338 | socket.$set_fn(arg).expect("failed to set option"); 1339 | let got = socket.$get_fn().expect("failed to get value"); 1340 | let expected = $expected; 1341 | assert_eq!(got, expected, "set and get values differ"); 1342 | }; 1343 | } 1344 | 1345 | const SET_BUF_SIZE: usize = 4096; 1346 | // Linux doubles the buffer size for kernel usage, and exposes that when 1347 | // retrieving the buffer size. 1348 | #[cfg(not(target_os = "linux"))] 1349 | const GET_BUF_SIZE: usize = SET_BUF_SIZE; 1350 | #[cfg(target_os = "linux")] 1351 | const GET_BUF_SIZE: usize = 2 * SET_BUF_SIZE; 1352 | 1353 | test!(nodelay, set_nodelay(true)); 1354 | test!( 1355 | recv_buffer_size, 1356 | set_recv_buffer_size(SET_BUF_SIZE), 1357 | GET_BUF_SIZE 1358 | ); 1359 | test!( 1360 | send_buffer_size, 1361 | set_send_buffer_size(SET_BUF_SIZE), 1362 | GET_BUF_SIZE 1363 | ); 1364 | #[cfg(not(target_os = "redox"))] 1365 | test!(out_of_band_inline, set_out_of_band_inline(true)); 1366 | test!(reuse_address, set_reuse_address(true)); 1367 | #[cfg(all( 1368 | feature = "all", 1369 | not(any( 1370 | windows, 1371 | target_os = "solaris", 1372 | target_os = "illumos", 1373 | target_os = "cygwin", 1374 | )) 1375 | ))] 1376 | test!(reuse_port, set_reuse_port(true)); 1377 | #[cfg(all(feature = "all", target_os = "freebsd"))] 1378 | test!(reuse_port_lb, set_reuse_port_lb(true)); 1379 | #[cfg(all( 1380 | feature = "all", 1381 | unix, 1382 | not(any(target_os = "redox", target_os = "cygwin")), 1383 | ))] 1384 | test!( 1385 | #[cfg_attr(target_os = "linux", ignore = "Different value returned")] 1386 | mss, 1387 | set_mss(256) 1388 | ); 1389 | #[cfg(all(feature = "all", target_os = "linux"))] 1390 | test!( 1391 | #[ignore = "setting `IP_TRANSPARENT` requires the `CAP_NET_ADMIN` capability (works when running as root)"] 1392 | ip_transparent_v4, 1393 | set_ip_transparent_v4(true) 1394 | ); 1395 | #[cfg(all(feature = "all", any(target_os = "fuchsia", target_os = "linux")))] 1396 | test!( 1397 | #[ignore = "setting `SO_MARK` requires the `CAP_NET_ADMIN` capability (works when running as root)"] 1398 | mark, 1399 | set_mark(123) 1400 | ); 1401 | #[cfg(all( 1402 | feature = "all", 1403 | any(target_os = "android", target_os = "fuchsia", target_os = "linux") 1404 | ))] 1405 | test!(cork, set_cork(true)); 1406 | #[cfg(all( 1407 | feature = "all", 1408 | any(target_os = "android", target_os = "fuchsia", target_os = "linux") 1409 | ))] 1410 | test!(quickack, set_quickack(false)); 1411 | #[cfg(all( 1412 | feature = "all", 1413 | any(target_os = "android", target_os = "fuchsia", target_os = "linux") 1414 | ))] 1415 | test!(thin_linear_timeouts, set_thin_linear_timeouts(true)); 1416 | test!(linger, set_linger(Some(Duration::from_secs(10)))); 1417 | test!( 1418 | read_timeout, 1419 | set_read_timeout(Some(Duration::from_secs(10))) 1420 | ); 1421 | test!(keepalive, set_keepalive(true)); 1422 | #[cfg(all(feature = "all", any(target_os = "fuchsia", target_os = "linux")))] 1423 | test!(freebind, set_freebind(true)); 1424 | #[cfg(all(feature = "all", target_os = "linux"))] 1425 | test!(IPv6 freebind_ipv6, set_freebind_ipv6(true)); 1426 | 1427 | test!(IPv4 ttl_v4, set_ttl_v4(40)); 1428 | 1429 | #[cfg(not(any( 1430 | target_os = "fuchsia", 1431 | target_os = "redox", 1432 | target_os = "solaris", 1433 | target_os = "illumos", 1434 | target_os = "haiku", 1435 | target_os = "cygwin", 1436 | )))] 1437 | test!(IPv4 tos_v4, set_tos_v4(96)); 1438 | 1439 | #[cfg(not(any( 1440 | target_os = "dragonfly", 1441 | target_os = "fuchsia", 1442 | target_os = "hurd", 1443 | target_os = "illumos", 1444 | target_os = "netbsd", 1445 | target_os = "openbsd", 1446 | target_os = "redox", 1447 | target_os = "solaris", 1448 | target_os = "windows", 1449 | target_os = "vita", 1450 | target_os = "haiku", 1451 | target_os = "cygwin", 1452 | )))] 1453 | test!(IPv4 recv_tos_v4, set_recv_tos_v4(true)); 1454 | 1455 | #[cfg(not(any(windows, target_os = "cygwin")))] // TODO: returns `WSAENOPROTOOPT` (10042) on Windows. 1456 | test!(IPv4 broadcast, set_broadcast(true)); 1457 | 1458 | #[cfg(not(target_os = "vita"))] 1459 | test!(IPv6 unicast_hops_v6, set_unicast_hops_v6(20)); 1460 | 1461 | #[cfg(not(any( 1462 | windows, 1463 | target_os = "dragonfly", 1464 | target_os = "freebsd", 1465 | target_os = "openbsd", 1466 | target_os = "vita", 1467 | target_os = "cygwin", 1468 | )))] 1469 | test!(IPv6 only_v6, set_only_v6(true)); 1470 | // IPv6 socket are already IPv6 only on FreeBSD and Windows. 1471 | #[cfg(any(windows, target_os = "freebsd", target_os = "cygwin"))] 1472 | test!(IPv6 only_v6, set_only_v6(false)); 1473 | 1474 | #[cfg(all( 1475 | feature = "all", 1476 | any( 1477 | target_os = "android", 1478 | target_os = "dragonfly", 1479 | target_os = "freebsd", 1480 | target_os = "fuchsia", 1481 | target_os = "linux", 1482 | target_os = "macos", 1483 | target_os = "netbsd", 1484 | target_os = "openbsd" 1485 | ) 1486 | ))] 1487 | test!(IPv6 tclass_v6, set_tclass_v6(96)); 1488 | 1489 | #[cfg(not(any( 1490 | target_os = "dragonfly", 1491 | target_os = "fuchsia", 1492 | target_os = "hurd", 1493 | target_os = "illumos", 1494 | target_os = "netbsd", 1495 | target_os = "openbsd", 1496 | target_os = "redox", 1497 | target_os = "solaris", 1498 | target_os = "windows", 1499 | target_os = "vita", 1500 | target_os = "haiku", 1501 | target_os = "cygwin", 1502 | )))] 1503 | test!(IPv6 recv_tclass_v6, set_recv_tclass_v6(true)); 1504 | 1505 | #[cfg(all( 1506 | feature = "all", 1507 | not(any( 1508 | target_os = "dragonfly", 1509 | target_os = "fuchsia", 1510 | target_os = "hurd", 1511 | target_os = "illumos", 1512 | target_os = "netbsd", 1513 | target_os = "openbsd", 1514 | target_os = "redox", 1515 | target_os = "solaris", 1516 | target_os = "windows", 1517 | target_os = "vita", 1518 | target_os = "haiku", 1519 | target_os = "cygwin", 1520 | )) 1521 | ))] 1522 | test!(IPv6 recv_hoplimit_v6, set_recv_hoplimit_v6(true)); 1523 | 1524 | #[cfg(all( 1525 | feature = "all", 1526 | any(target_os = "android", target_os = "fuchsia", target_os = "linux") 1527 | ))] 1528 | test!( 1529 | tcp_user_timeout, 1530 | set_tcp_user_timeout(Some(Duration::from_secs(10))) 1531 | ); 1532 | 1533 | #[cfg(all(feature = "all", target_os = "linux"))] 1534 | test!(IPv4 multicast_all_v4, set_multicast_all_v4(false)); 1535 | #[cfg(all(feature = "all", target_os = "linux"))] 1536 | test!(IPv6 multicast_all_v6, set_multicast_all_v6(false)); 1537 | 1538 | #[test] 1539 | #[cfg(not(any( 1540 | target_os = "haiku", 1541 | target_os = "illumos", 1542 | target_os = "netbsd", 1543 | target_os = "openbsd", 1544 | target_os = "redox", 1545 | target_os = "solaris", 1546 | target_os = "vita", 1547 | target_os = "cygwin", 1548 | )))] 1549 | fn join_leave_multicast_v4_n() { 1550 | let socket = Socket::new(Domain::IPV4, Type::DGRAM, None).unwrap(); 1551 | let multiaddr = Ipv4Addr::new(224, 0, 1, 1); 1552 | let interface = socket2::InterfaceIndexOrAddress::Index(0); 1553 | match socket.leave_multicast_v4_n(&multiaddr, &interface) { 1554 | Ok(()) => panic!("leaving an unjoined group should fail"), 1555 | Err(err) => { 1556 | assert_eq!(err.kind(), io::ErrorKind::AddrNotAvailable); 1557 | #[cfg(unix)] 1558 | assert_eq!(err.raw_os_error(), Some(libc::EADDRNOTAVAIL)); 1559 | } 1560 | }; 1561 | socket 1562 | .join_multicast_v4_n(&multiaddr, &interface) 1563 | .expect("join multicast group"); 1564 | socket 1565 | .leave_multicast_v4_n(&multiaddr, &interface) 1566 | .expect("leave multicast group"); 1567 | } 1568 | 1569 | #[test] 1570 | #[cfg(not(any( 1571 | target_os = "dragonfly", 1572 | target_os = "haiku", 1573 | target_os = "hurd", 1574 | target_os = "netbsd", 1575 | target_os = "openbsd", 1576 | target_os = "redox", 1577 | target_os = "fuchsia", 1578 | target_os = "vita", 1579 | )))] 1580 | fn join_leave_ssm_v4() { 1581 | let socket = Socket::new(Domain::IPV4, Type::DGRAM, None).unwrap(); 1582 | let g = Ipv4Addr::new(232, 123, 52, 36); 1583 | let s = Ipv4Addr::new(62, 40, 109, 31); 1584 | let interface = Ipv4Addr::new(0, 0, 0, 0); 1585 | socket.join_ssm_v4(&s, &g, &interface).expect("Joined SSM"); 1586 | socket.leave_ssm_v4(&s, &g, &interface).expect("Left SSM"); 1587 | } 1588 | 1589 | #[test] 1590 | #[cfg(all(feature = "all", not(target_os = "redox")))] 1591 | fn header_included() { 1592 | let socket = match Socket::new(Domain::IPV4, Type::RAW, None) { 1593 | Ok(socket) => socket, 1594 | // Need certain permissions to create a raw sockets. 1595 | Err(ref err) if err.kind() == io::ErrorKind::PermissionDenied => return, 1596 | #[cfg(unix)] 1597 | Err(ref err) if err.raw_os_error() == Some(libc::EPROTONOSUPPORT) => return, 1598 | Err(err) => panic!("unexpected error creating socket: {}", err), 1599 | }; 1600 | 1601 | let initial = socket 1602 | .header_included_v4() 1603 | .expect("failed to get initial value"); 1604 | assert_eq!(initial, false, "initial value and argument are the same"); 1605 | 1606 | socket 1607 | .set_header_included_v4(true) 1608 | .expect("failed to set option"); 1609 | let got = socket.header_included_v4().expect("failed to get value"); 1610 | assert_eq!(got, true, "set and get values differ"); 1611 | } 1612 | 1613 | #[test] 1614 | #[cfg(all( 1615 | feature = "all", 1616 | not(any( 1617 | target_os = "redox", 1618 | target_os = "espidf", 1619 | target_os = "openbsd", 1620 | target_os = "freebsd", 1621 | target_os = "dragonfly", 1622 | target_os = "netbsd" 1623 | )) 1624 | ))] 1625 | fn header_included_ipv6() { 1626 | let socket = match Socket::new(Domain::IPV6, Type::RAW, None) { 1627 | Ok(socket) => socket, 1628 | // Need certain permissions to create a raw sockets. 1629 | Err(ref err) if err.kind() == io::ErrorKind::PermissionDenied => return, 1630 | #[cfg(unix)] 1631 | Err(ref err) if err.raw_os_error() == Some(libc::EPROTONOSUPPORT) => return, 1632 | Err(err) => panic!("unexpected error creating socket: {}", err), 1633 | }; 1634 | 1635 | let initial = socket 1636 | .header_included_v6() 1637 | .expect("failed to get initial value"); 1638 | assert_eq!(initial, false, "initial value and argument are the same"); 1639 | 1640 | socket 1641 | .set_header_included_v6(true) 1642 | .expect("failed to set option"); 1643 | let got = socket.header_included_v6().expect("failed to get value"); 1644 | assert_eq!(got, true, "set and get values differ"); 1645 | } 1646 | 1647 | #[test] 1648 | #[cfg(all( 1649 | feature = "all", 1650 | any( 1651 | target_os = "android", 1652 | target_os = "fuchsia", 1653 | target_os = "linux", 1654 | target_os = "windows" 1655 | ) 1656 | ))] 1657 | fn original_dst() { 1658 | let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); 1659 | #[cfg(not(target_os = "windows"))] 1660 | let expected = Some(libc::ENOENT); 1661 | #[cfg(target_os = "windows")] 1662 | let expected = Some(windows_sys::Win32::Networking::WinSock::WSAEINVAL); 1663 | 1664 | match socket.original_dst() { 1665 | Ok(_) => panic!("original_dst on non-redirected socket should fail"), 1666 | Err(err) => assert_eq!(err.raw_os_error(), expected), 1667 | } 1668 | 1669 | let socket = Socket::new(Domain::IPV6, Type::STREAM, None).unwrap(); 1670 | match socket.original_dst() { 1671 | Ok(_) => panic!("original_dst on non-redirected socket should fail"), 1672 | Err(err) => assert_eq!(err.raw_os_error(), expected), 1673 | } 1674 | } 1675 | 1676 | #[test] 1677 | #[cfg(all( 1678 | feature = "all", 1679 | any(target_os = "android", target_os = "fuchsia", target_os = "linux") 1680 | ))] 1681 | fn original_dst_ipv6() { 1682 | let socket = Socket::new(Domain::IPV6, Type::STREAM, None).unwrap(); 1683 | #[cfg(not(target_os = "windows"))] 1684 | let expected = Some(libc::ENOENT); 1685 | #[cfg(target_os = "windows")] 1686 | let expected = Some(windows_sys::Win32::Networking::WinSock::WSAEINVAL); 1687 | #[cfg(not(target_os = "windows"))] 1688 | let expected_v4 = Some(libc::EOPNOTSUPP); 1689 | #[cfg(target_os = "windows")] 1690 | let expected_v4 = Some(windows_sys::Win32::Networking::WinSock::WSAEINVAL); 1691 | match socket.original_dst_ipv6() { 1692 | Ok(_) => panic!("original_dst_ipv6 on non-redirected socket should fail"), 1693 | Err(err) => assert_eq!(err.raw_os_error(), expected), 1694 | } 1695 | 1696 | // Not supported on IPv4 socket. 1697 | let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); 1698 | match socket.original_dst_ipv6() { 1699 | Ok(_) => panic!("original_dst_ipv6 on non-redirected socket should fail"), 1700 | Err(err) => assert_eq!(err.raw_os_error(), expected_v4), 1701 | } 1702 | } 1703 | 1704 | #[test] 1705 | #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))] 1706 | fn tcp_congestion() { 1707 | let socket: Socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); 1708 | // Get and set current tcp_ca 1709 | let origin_tcp_ca = socket 1710 | .tcp_congestion() 1711 | .expect("failed to get tcp congestion algorithm"); 1712 | socket 1713 | .set_tcp_congestion(&origin_tcp_ca) 1714 | .expect("failed to set tcp congestion algorithm"); 1715 | // Return a Err when set a non-exist tcp_ca 1716 | socket 1717 | .set_tcp_congestion(b"tcp_congestion_does_not_exist") 1718 | .unwrap_err(); 1719 | let cur_tcp_ca = socket.tcp_congestion().unwrap(); 1720 | assert_eq!( 1721 | cur_tcp_ca, origin_tcp_ca, 1722 | "expected {origin_tcp_ca:?} but get {cur_tcp_ca:?}" 1723 | ); 1724 | let cur_tcp_ca = cur_tcp_ca.splitn(2, |num| *num == 0).next().unwrap(); 1725 | const OPTIONS: [&[u8]; 2] = [ 1726 | b"cubic", 1727 | #[cfg(target_os = "linux")] 1728 | b"reno", 1729 | #[cfg(target_os = "freebsd")] 1730 | b"newreno", 1731 | ]; 1732 | // Set a new tcp ca 1733 | #[cfg(target_os = "linux")] 1734 | let new_tcp_ca = if cur_tcp_ca == OPTIONS[0] { 1735 | OPTIONS[1] 1736 | } else { 1737 | OPTIONS[0] 1738 | }; 1739 | #[cfg(target_os = "freebsd")] 1740 | let new_tcp_ca = OPTIONS[1]; 1741 | socket.set_tcp_congestion(new_tcp_ca).unwrap(); 1742 | // Check if new tcp ca is successfully set 1743 | let cur_tcp_ca = socket.tcp_congestion().unwrap(); 1744 | assert_eq!( 1745 | cur_tcp_ca.splitn(2, |num| *num == 0).next().unwrap(), 1746 | new_tcp_ca, 1747 | ); 1748 | } 1749 | 1750 | #[test] 1751 | #[ignore = "DCCP support is not enabled in all kernels of majors Linux distros"] 1752 | #[cfg(all(feature = "all", target_os = "linux"))] 1753 | fn dccp() { 1754 | let listener = Socket::new(Domain::IPV4, Type::DCCP, Some(Protocol::DCCP)).unwrap(); 1755 | let addr = "127.0.0.1:0".parse::().unwrap().into(); 1756 | listener.set_dccp_service(45).unwrap(); 1757 | assert!(listener.dccp_service().unwrap() == 45); 1758 | assert!(listener.dccp_cur_mps().unwrap() > 0); 1759 | assert!(listener.dccp_available_ccids::<4>().unwrap().len() >= 3); 1760 | assert!( 1761 | listener.dccp_send_cscov().unwrap() == 0, 1762 | "sender cscov should be zero by default" 1763 | ); 1764 | listener.set_dccp_ccid(2).unwrap(); 1765 | listener.set_dccp_qpolicy_txqlen(6).unwrap(); 1766 | assert!(listener.dccp_qpolicy_txqlen().unwrap() == 6); 1767 | listener.bind(&addr).unwrap(); 1768 | listener.listen(10).unwrap(); 1769 | 1770 | let mut client = Socket::new(Domain::IPV4, Type::DCCP, Some(Protocol::DCCP)).unwrap(); 1771 | client.set_dccp_service(45).unwrap(); 1772 | client.connect(&addr).unwrap(); 1773 | 1774 | let (mut accepted, _) = listener.accept().unwrap(); 1775 | let msg = "Hello World!"; 1776 | assert!(client.write(msg.as_bytes()).unwrap() == msg.len()); 1777 | let mut recv_buf = [0_u8; 64]; 1778 | assert!(accepted.read(&mut recv_buf).unwrap() == msg.len()); 1779 | } 1780 | 1781 | #[test] 1782 | #[cfg(all(feature = "all", target_os = "linux"))] 1783 | fn cookie() { 1784 | let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); 1785 | let first_socket_cookie = socket.cookie(); 1786 | match first_socket_cookie { 1787 | Ok(_) => {} 1788 | Err(err) => panic!("Could not get socket cookie, err: {err}"), 1789 | } 1790 | 1791 | //Fetch cookie again and make sure it's the same value (ALWAYS should be, smoke test) 1792 | let second_socket_cookie = socket.cookie(); 1793 | match second_socket_cookie { 1794 | Ok(cookie) => assert_eq!(cookie, first_socket_cookie.unwrap()), 1795 | Err(err) => panic!("Could not get socket cookie a second time, err: {err}"), 1796 | } 1797 | } 1798 | 1799 | #[cfg(all(unix, target_os = "linux"))] 1800 | #[test] 1801 | fn set_passcred() { 1802 | let socket = Socket::new(Domain::UNIX, Type::DGRAM, None).unwrap(); 1803 | assert!(!socket.passcred().unwrap()); 1804 | 1805 | socket.set_passcred(true).unwrap(); 1806 | assert!(socket.passcred().unwrap()); 1807 | 1808 | let socket = Socket::new(Domain::UNIX, Type::STREAM, None).unwrap(); 1809 | assert!(!socket.passcred().unwrap()); 1810 | 1811 | socket.set_passcred(true).unwrap(); 1812 | assert!(socket.passcred().unwrap()); 1813 | } 1814 | 1815 | #[cfg(all(feature = "all", target_os = "linux"))] 1816 | #[test] 1817 | fn set_priority() { 1818 | let socket = Socket::new(Domain::UNIX, Type::DGRAM, None).unwrap(); 1819 | assert!(socket.priority().unwrap() == 0); 1820 | 1821 | // test priorities 6 .. 0; values above 6 require additional priviledges 1822 | for i in (0..=6).rev() { 1823 | socket.set_priority(i).unwrap(); 1824 | assert!(socket.priority().unwrap() == i); 1825 | } 1826 | } 1827 | --------------------------------------------------------------------------------