windows_overlapped_io_sys/socket.rs
1// Copyright (c) 2026 Mike Grier
2//! Safe socket operation adapters, gated behind the `socket` feature.
3//!
4//! These wrappers own the I/O buffer and the `WSABUF` describing it, issue the
5//! single native `WSARecv` / `WSASend` internally, and route completions through
6//! the same [`CompletionPort`] as the handle backends. A socket is owned as a
7//! [`OwnedSocket`] (its destructor is `closesocket`), so it gets its own
8//! endpoint type rather than reusing the handle-based `AssociatedEndpoint`.
9//!
10//! The caller provides a connected, overlapped-capable socket -- every
11//! `std::net` socket qualifies, and owning one keeps Winsock initialized -- so
12//! the crate never calls `WSAStartup` itself.
13
14use std::io;
15use std::os::windows::io::{AsRawSocket, AsSocket, BorrowedSocket, OwnedSocket};
16
17use windows_sys::Win32::Foundation::HANDLE;
18use windows_sys::Win32::Networking::WinSock::{
19 SO_PROTOCOL_INFOW, SOCKET, SOCKET_ERROR, SOL_SOCKET, WSA_INVALID_EVENT, WSA_IO_PENDING, WSABUF,
20 WSACloseEvent, WSACreateEvent, WSAEVENT, WSAGetOverlappedResult, WSAPROTOCOL_INFOW, WSARecv,
21 WSASend, XP1_IFS_HANDLES, getsockopt,
22};
23use windows_sys::Win32::System::IO::{CancelIoEx, CreateIoCompletionPort, OVERLAPPED};
24
25use crate::endpoint::notification_flags;
26use crate::operation::{payload_ptr_from_overlapped, sync_bytes_ptr_from_overlapped};
27use crate::{
28 Completion, CompletionPort, IoBuf, IoBufMut, Issued, NotificationModes, Operation, OperationId,
29 Started, Submitted,
30};
31
32impl CompletionPort {
33 /// Associate an overlapped socket with this port under `key`.
34 ///
35 /// Completions for operations issued on the socket are delivered to this port
36 /// and tagged with `key`. The socket must be overlapped-capable, which every
37 /// `std::net` socket and any `WSASocket` created with `WSA_FLAG_OVERLAPPED`
38 /// is; the association is permanent for the life of the socket.
39 ///
40 /// # Errors
41 ///
42 /// Returns any error from `CreateIoCompletionPort`.
43 pub fn associate_socket(
44 &self,
45 socket: OwnedSocket,
46 key: usize,
47 ) -> io::Result<AssociatedSocket<'_>> {
48 // SAFETY: associating a valid socket handle with a valid port; the
49 // concurrency argument is ignored when an existing port is supplied.
50 let result = unsafe {
51 CreateIoCompletionPort(
52 socket.as_raw_socket() as usize as HANDLE,
53 self.raw(),
54 key,
55 0,
56 )
57 };
58 if result.is_null() {
59 return Err(io::Error::last_os_error());
60 }
61 Ok(AssociatedSocket {
62 port: self,
63 socket,
64 key,
65 modes: NotificationModes::default(),
66 })
67 }
68}
69
70/// An overlapped socket bound to exactly one [`CompletionPort`].
71///
72/// The endpoint owns its socket (closed with `closesocket` on drop) and borrows
73/// the port it is associated with. It is intentionally not `Clone`.
74#[derive(Debug)]
75pub struct AssociatedSocket<'port> {
76 port: &'port CompletionPort,
77 socket: OwnedSocket,
78 key: usize,
79 /// What [`AssociatedSocket::set_notification_modes`] has established.
80 ///
81 /// Read at every submission, because it decides whether a synchronous
82 /// success will be followed by a completion packet.
83 modes: NotificationModes,
84}
85
86impl<'port> AssociatedSocket<'port> {
87 /// Borrow the underlying socket.
88 #[must_use]
89 pub fn socket(&self) -> BorrowedSocket<'_> {
90 self.socket.as_socket()
91 }
92
93 /// The completion key packets from this socket are tagged with.
94 #[must_use]
95 pub fn key(&self) -> usize {
96 self.key
97 }
98
99 /// The completion port this socket is associated with.
100 #[must_use]
101 pub fn port(&self) -> &'port CompletionPort {
102 self.port
103 }
104
105 /// The completion-notification modes established on this socket.
106 #[must_use]
107 pub fn notification_modes(&self) -> NotificationModes {
108 self.modes
109 }
110
111 fn raw_socket(&self) -> SOCKET {
112 self.socket.as_raw_socket() as usize
113 }
114
115 /// Set this socket's completion-notification modes, after checking that its
116 /// provider actually supports them.
117 ///
118 /// The handle side declares its modes *before* association, on
119 /// [`UnassociatedEndpoint::set_notification_modes`](crate::UnassociatedEndpoint::set_notification_modes),
120 /// because there the mode is part of an endpoint's provenance. A socket has
121 /// no unassociated stage to hang that on, so it declares here instead.
122 /// Setting after association is still safe: the flag only takes effect at
123 /// I/O time, and `recv`/`send` take `&self`, so a caller sets the mode once
124 /// against `&mut self` and then submits freely.
125 ///
126 /// Passing every field `false` is a no-op call, not a reset. **A mode cannot
127 /// be removed once set** -- a Win32 property of the handle, not a limitation
128 /// of this wrapper -- so a second call can only ever add modes.
129 ///
130 /// # The capability probe
131 ///
132 /// Win32 restricts [`NotificationModes::skip_completion_port_on_success`] on
133 /// a socket to Layered Service Providers that return IFS handles, and a
134 /// socket wrongly put in that mode reports [`Started::Pending`] for an
135 /// operation whose packet was suppressed -- leaving it outstanding forever
136 /// and wedging [`CompletionPort::run_down`]. So this asks first, reading
137 /// *this* socket's own `WSAPROTOCOL_INFOW` via `SO_PROTOCOL_INFOW` and
138 /// requiring `XP1_IFS_HANDLES`. That is narrower and more accurate than the
139 /// `WSAEnumProtocols` sweep the flag's documentation suggests: it asks about
140 /// the provider that actually created this socket, not about every LSP
141 /// installed on the machine.
142 ///
143 /// `skip_set_event_on_handle` carries no such restriction and is not probed.
144 ///
145 /// # Errors
146 ///
147 /// Returns [`io::ErrorKind::Unsupported`] if skip-on-success was requested
148 /// and this socket's provider does not return IFS handles, or any error from
149 /// `getsockopt` or `SetFileCompletionNotificationModes`.
150 pub fn set_notification_modes(&mut self, modes: NotificationModes) -> io::Result<()> {
151 if modes.skip_completion_port_on_success {
152 require_ifs_handles(self.provider_service_flags()?)?;
153 }
154
155 let mut flags = 0_u8;
156 if modes.skip_completion_port_on_success {
157 flags |= notification_flags::SKIP_COMPLETION_PORT_ON_SUCCESS;
158 }
159 if modes.skip_set_event_on_handle {
160 flags |= notification_flags::SKIP_SET_EVENT_ON_HANDLE;
161 }
162 // SAFETY: a live socket this endpoint owns -- a socket handle is a
163 // kernel handle, which is why this file-named call accepts one -- and a
164 // flags byte built only from the two documented bits. The call sets a
165 // handle attribute and starts no I/O.
166 let ok = unsafe {
167 windows_sys::Win32::Storage::FileSystem::SetFileCompletionNotificationModes(
168 self.raw_socket() as HANDLE,
169 flags,
170 )
171 };
172 if ok == 0 {
173 return Err(io::Error::last_os_error());
174 }
175 // Accumulated, never replaced: Win32 cannot clear a mode, so what this
176 // socket records has to be the union of everything ever set on it.
177 self.modes.skip_completion_port_on_success |= modes.skip_completion_port_on_success;
178 self.modes.skip_set_event_on_handle |= modes.skip_set_event_on_handle;
179 Ok(())
180 }
181
182 /// The `dwServiceFlags1` word of the provider that created this socket.
183 fn provider_service_flags(&self) -> io::Result<u32> {
184 let mut info = std::mem::MaybeUninit::<WSAPROTOCOL_INFOW>::uninit();
185 let mut len = i32::try_from(size_of::<WSAPROTOCOL_INFOW>())
186 .expect("WSAPROTOCOL_INFOW is far smaller than i32::MAX");
187 // SAFETY: a live socket, a documented option pair, and an output buffer
188 // exactly `len` bytes long that Winsock fills before returning success.
189 let ret = unsafe {
190 getsockopt(
191 self.raw_socket(),
192 SOL_SOCKET,
193 SO_PROTOCOL_INFOW,
194 info.as_mut_ptr().cast(),
195 &raw mut len,
196 )
197 };
198 if ret == SOCKET_ERROR {
199 return Err(io::Error::last_os_error());
200 }
201 // SAFETY: `getsockopt` reported success, so it wrote the whole struct.
202 Ok(unsafe { info.assume_init() }.dwServiceFlags1)
203 }
204
205 /// Submit an overlapped receive into `buffer`.
206 ///
207 /// The buffer is any owned [`IoBufMut`] -- handed over for the operation's
208 /// life and returned when it completes, with nothing copied and nothing
209 /// allocated here.
210 ///
211 /// Returns [`Started::Pending`] with a [`SocketIo`] token that recovers the
212 /// buffer and byte count from the operation's completion, or -- only on a
213 /// socket in a skip-on-success completion mode, where a synchronous success
214 /// queues no packet -- [`Started::Completed`] with the buffer already in
215 /// hand.
216 ///
217 /// # Errors
218 ///
219 /// Returns [`io::ErrorKind::InvalidInput`] if the buffer is longer than
220 /// `u32::MAX`, which `WSABUF`'s byte count cannot express, or any immediate
221 /// failure from issuing the receive.
222 #[track_caller]
223 pub fn recv<B: IoBufMut>(&self, buffer: B) -> io::Result<Started<SocketIo<B>, B>> {
224 let socket = self.raw_socket();
225 let skip = self.modes.skip_completion_port_on_success;
226 let operation = Operation::new(recv_payload(buffer)?);
227 // SAFETY: issues exactly one WSARecv into the payload's buffer via its
228 // WSABUF and flags word, both reached through the pinned OVERLAPPED; they
229 // and the byte-count cell live until the completion is claimed.
230 let submitted = unsafe {
231 self.port.submit_with(operation, |overlapped| {
232 let payload = payload_ptr_from_overlapped::<SocketPayload<B>>(overlapped);
233 let bytes = sync_bytes_ptr_from_overlapped(overlapped);
234 let ret = WSARecv(
235 socket,
236 std::ptr::addr_of!((*payload).wsabuf),
237 1,
238 bytes,
239 std::ptr::addr_of_mut!((*payload).flags),
240 overlapped,
241 None,
242 );
243 classify_socket(ret, skip, bytes)
244 })
245 };
246 finish_socket(submitted)
247 }
248
249 /// Submit an overlapped send of `buffer`.
250 ///
251 /// The buffer is any owned [`IoBuf`] -- including a shared `Arc<[u8]>` or a
252 /// `&'static [u8]` -- handed over for the operation's life and returned when
253 /// it completes. Nothing is copied.
254 ///
255 /// Returns [`Started::Pending`] with a [`SocketIo`] token, or
256 /// [`Started::Completed`] with the buffer already in hand when the socket is
257 /// in a skip-on-success completion mode and the send completed
258 /// synchronously.
259 ///
260 /// # Errors
261 ///
262 /// Returns [`io::ErrorKind::InvalidInput`] if the buffer is longer than
263 /// `u32::MAX`, which `WSABUF`'s byte count cannot express, or any immediate
264 /// failure from issuing the send.
265 #[track_caller]
266 pub fn send<B: IoBuf>(&self, buffer: B) -> io::Result<Started<SocketIo<B>, B>> {
267 let socket = self.raw_socket();
268 let skip = self.modes.skip_completion_port_on_success;
269 let operation = Operation::new(send_payload(buffer)?);
270 // SAFETY: issues exactly one WSASend from the payload's buffer via its
271 // WSABUF, reached through the pinned OVERLAPPED; it and the byte-count
272 // cell live until the completion is claimed.
273 let submitted = unsafe {
274 self.port.submit_with(operation, |overlapped| {
275 let payload = payload_ptr_from_overlapped::<SocketPayload<B>>(overlapped);
276 let bytes = sync_bytes_ptr_from_overlapped(overlapped);
277 let ret = WSASend(
278 socket,
279 std::ptr::addr_of!((*payload).wsabuf),
280 1,
281 bytes,
282 0,
283 overlapped,
284 None,
285 );
286 classify_socket(ret, skip, bytes)
287 })
288 };
289 finish_socket(submitted)
290 }
291
292 /// Request cancellation of a single outstanding operation on this socket.
293 ///
294 /// The identity is validated against the port's live operations, and the
295 /// native cancellation happens under the same guard, so an identity retained
296 /// past its operation's completion cannot reach a later operation that was
297 /// given the same storage address.
298 ///
299 /// # Errors
300 ///
301 /// Returns [`io::ErrorKind::NotFound`] if `id` no longer names a live
302 /// operation, or any error from `CancelIoEx`.
303 pub fn cancel(&self, id: OperationId) -> io::Result<()> {
304 // Socket cancellation goes through the same registry as file
305 // cancellation; routing around it would leave the identity guarantee
306 // holding for one endpoint kind and not the other.
307 self.port.live_operations().cancel_if_live(id, || {
308 // SAFETY: cancelling by a valid socket handle and an OVERLAPPED
309 // identity the registry has confirmed still names a live operation,
310 // and which cannot be reissued while the guard is held.
311 let ok = unsafe { CancelIoEx(self.raw_socket() as HANDLE, id.as_ptr()) };
312 if ok == 0 {
313 return Err(io::Error::last_os_error());
314 }
315 Ok(())
316 })
317 }
318
319 /// Request cancellation of every outstanding operation on this socket.
320 ///
321 /// # Errors
322 ///
323 /// Returns any error from `CancelIoEx`.
324 pub fn cancel_all(&self) -> io::Result<()> {
325 // SAFETY: a null OVERLAPPED cancels all operations on the socket handle.
326 let ok = unsafe { CancelIoEx(self.raw_socket() as HANDLE, std::ptr::null()) };
327 if ok == 0 {
328 return Err(io::Error::last_os_error());
329 }
330 Ok(())
331 }
332}
333
334/// The pinned payload for an in-flight socket operation: the buffer, the
335/// `WSABUF` pointing into it, and the receive `flags` word.
336struct SocketPayload<B> {
337 buffer: B,
338 wsabuf: WSABUF,
339 flags: u32,
340}
341
342// SAFETY: `wsabuf.buf` points into `buffer`, which this payload owns; moving the
343// payload moves the whole self-referential unit -- sound because `IoBuf` promises
344// the buffer's address does not move with it -- and it exposes no aliasing
345// access, so it is `Send` whenever the buffer is.
346unsafe impl<B: Send> Send for SocketPayload<B> {}
347
348fn recv_payload<B: IoBufMut>(mut buffer: B) -> io::Result<SocketPayload<B>> {
349 let wsalen = checked_len(buffer.bytes_len(), "receive buffer")?;
350 let wsabuf = WSABUF {
351 len: wsalen,
352 buf: buffer.stable_mut_ptr(),
353 };
354 Ok(SocketPayload {
355 buffer,
356 wsabuf,
357 flags: 0,
358 })
359}
360
361fn send_payload<B: IoBuf>(buffer: B) -> io::Result<SocketPayload<B>> {
362 let wsabuf = WSABUF {
363 len: checked_len(buffer.bytes_len(), "send buffer")?,
364 // `WSABUF` is one type for both directions, so its `buf` is `*mut` even
365 // for a send. `WSASend` only reads through it, which is what makes this
366 // sound for a shared buffer whose pointer carries no write provenance.
367 buf: buffer.stable_ptr().cast_mut(),
368 };
369 Ok(SocketPayload {
370 buffer,
371 wsabuf,
372 flags: 0,
373 })
374}
375
376/// Decide whether a provider's `dwServiceFlags1` permits skip-on-success.
377///
378/// Split from the `getsockopt` that reads the word so the refusal can be tested
379/// directly: every base Winsock provider on a stock Windows returns IFS handles,
380/// so the failing arm is otherwise unreachable without installing a Layered
381/// Service Provider.
382///
383/// # Errors
384///
385/// Returns [`io::ErrorKind::Unsupported`] -- deliberately not a Win32 error,
386/// because nothing failed: the question was asked and answered.
387fn require_ifs_handles(service_flags1: u32) -> io::Result<()> {
388 if service_flags1 & XP1_IFS_HANDLES == 0 {
389 return Err(io::Error::new(
390 io::ErrorKind::Unsupported,
391 "this socket's provider does not return IFS handles, so Win32 does not support \
392 FILE_SKIP_COMPLETION_PORT_ON_SUCCESS on it",
393 ));
394 }
395 Ok(())
396}
397
398/// Map a Winsock call's return value into the submission contract.
399///
400/// [`Issued`] records whether a **completion packet will arrive**, not whether
401/// the call finished synchronously. For an IOCP-bound overlapped socket those
402/// are different facts: a packet is queued for every completed request,
403/// *including* one that succeeded immediately without `WSA_IO_PENDING`.
404///
405/// The single exception is `skip_on_success`, which is why this needs to know
406/// it: on a socket put into `FILE_SKIP_COMPLETION_PORT_ON_SUCCESS` mode by
407/// [`AssociatedSocket::set_notification_modes`] no packet is queued for an
408/// immediate success, so that -- and only that -- is an [`Issued::Completed`].
409/// Getting this backwards in either direction is a bug with teeth: claiming
410/// `Completed` when a packet is coming frees the operation under a live
411/// `OVERLAPPED`, and claiming `Pending` when none is coming leaves the operation
412/// outstanding forever and wedges [`CompletionPort::run_down`].
413///
414/// # Safety
415///
416/// `sync_bytes` must be the byte-count cell of the operation being submitted,
417/// which is live for the whole call.
418unsafe fn classify_socket(
419 ret: i32,
420 skip_on_success: bool,
421 sync_bytes: *mut u32,
422) -> io::Result<Issued> {
423 if ret == 0 {
424 if skip_on_success {
425 // SAFETY: the call reported immediate success, so Winsock has
426 // already written the count and will not write it again.
427 let bytes_transferred = unsafe { *sync_bytes };
428 return Ok(Issued::Completed { bytes_transferred });
429 }
430 return Ok(Issued::Pending);
431 }
432 let error = io::Error::last_os_error();
433 if error.raw_os_error() == Some(WSA_IO_PENDING) {
434 Ok(Issued::Pending)
435 } else {
436 Err(error)
437 }
438}
439
440/// Turn a socket submission outcome into the adapter's two-state outcome.
441fn finish_socket<B: IoBuf>(
442 submitted: Submitted<SocketPayload<B>>,
443) -> io::Result<Started<SocketIo<B>, B>> {
444 match submitted {
445 Submitted::Pending(id) => Ok(Started::Pending(SocketIo {
446 id,
447 buffer: std::marker::PhantomData,
448 })),
449 Submitted::Completed {
450 operation,
451 bytes_transferred,
452 } => Ok(Started::Completed {
453 payload: operation.into_payload().buffer,
454 bytes_transferred: bytes_transferred as usize,
455 }),
456 Submitted::Failed { error, .. } => Err(error),
457 }
458}
459
460/// Convert a buffer length to the `u32` byte count `WSABUF` carries.
461///
462/// Rejects rather than caps, for the same reason as the file and device
463/// helpers: capping would transfer a prefix of the caller's buffer and then
464/// report success for an operation that did something other than what was asked.
465fn checked_len(len: usize, which: &str) -> io::Result<u32> {
466 u32::try_from(len).map_err(|_| {
467 io::Error::new(
468 io::ErrorKind::InvalidInput,
469 format!("a {which} is limited to u32::MAX bytes; {len} does not fit"),
470 )
471 })
472}
473
474/// A pending socket operation submitted through [`AssociatedSocket::recv`] or
475/// [`AssociatedSocket::send`].
476///
477/// The token carries the operation's identity and remembers the buffer type it
478/// was submitted with, so [`SocketIo::claim`] hands back the caller's own buffer
479/// -- the same value, not a copy -- once the matching completion is dequeued.
480#[derive(Debug)]
481pub struct SocketIo<B> {
482 id: OperationId,
483 /// The buffer itself is in the pinned operation, not here; this only keeps
484 /// the token's type tied to it so `claim` cannot be handed the wrong one.
485 buffer: std::marker::PhantomData<fn() -> B>,
486}
487
488impl<B: IoBuf> SocketIo<B> {
489 /// The identity of the in-flight operation, for cancellation or matching.
490 #[must_use]
491 pub fn id(&self) -> OperationId {
492 self.id
493 }
494
495 /// Claim this operation's result from `completion`.
496 ///
497 /// On a match returns `Ok((buffer, result))`: `buffer` is the one the caller
498 /// handed over -- the bytes received (valid up to the byte count), or the
499 /// data sent -- and `result` is the byte count or the operation's error.
500 /// Returns `Err(self)` when `completion` belongs to a different operation.
501 pub fn claim(self, completion: &Completion) -> Result<(B, io::Result<usize>), Self> {
502 if completion.id() != Some(self.id) {
503 return Err(self);
504 }
505 // SAFETY: the full identity -- address *and* generation -- matches, which
506 // an address alone would not: a recycled address can belong to a later
507 // operation of a different payload type. The match therefore proves this
508 // completion is the Operation<SocketPayload<B>> this token submitted, and
509 // the token's own type parameter names that B; claim it exactly once.
510 let operation = unsafe { completion.claim::<SocketPayload<B>>() };
511 let buffer = operation.into_payload().buffer;
512 let result = match completion.error() {
513 Some(error) => Err(io::Error::from_raw_os_error(
514 error.raw_os_error().unwrap_or_default(),
515 )),
516 None => Ok(completion.bytes_transferred() as usize),
517 };
518 Ok((buffer, result))
519 }
520}
521
522/// A connected overlapped socket that completes operations synchronously, one at
523/// a time, via a Winsock completion event.
524///
525/// This is the socket counterpart of the handle blocking backend. It cannot use
526/// `GetOverlappedResult` on the socket handle, so each call creates a
527/// `WSACreateEvent`, issues the operation with that event in `OVERLAPPED.hEvent`,
528/// and blocks on `WSAGetOverlappedResult`.
529#[derive(Debug)]
530pub struct BlockingSocket {
531 socket: OwnedSocket,
532}
533
534impl BlockingSocket {
535 /// Take ownership of a connected overlapped socket for synchronous
536 /// completion.
537 #[must_use]
538 pub fn new(socket: OwnedSocket) -> Self {
539 Self { socket }
540 }
541
542 /// Borrow the underlying socket.
543 #[must_use]
544 pub fn socket(&self) -> BorrowedSocket<'_> {
545 self.socket.as_socket()
546 }
547
548 fn raw_socket(&self) -> SOCKET {
549 self.socket.as_raw_socket() as usize
550 }
551
552 /// Receive into `buffer`, blocking until the receive completes, and return
553 /// the number of bytes received.
554 ///
555 /// Takes a plain `&mut [u8]` and allocates nothing, matching
556 /// [`BlockingSocket::send`]: this call does not return until the operation
557 /// is over, so an ordinary borrow provably covers it.
558 ///
559 /// # Errors
560 ///
561 /// Returns [`io::ErrorKind::InvalidInput`] if `buffer` is longer than
562 /// `u32::MAX`, which `WSABUF`'s byte count cannot express, or any error from
563 /// issuing or completing the receive.
564 pub fn recv(&self, buffer: &mut [u8]) -> io::Result<usize> {
565 let wsalen = checked_len(buffer.len(), "receive buffer")?;
566 let wsabuf = WSABUF {
567 len: wsalen,
568 buf: buffer.as_mut_ptr(),
569 };
570 // SAFETY: issues exactly one WSARecv into `buffer` via `wsabuf`, both of
571 // which stay valid for the whole blocking call.
572 unsafe {
573 self.run(|socket, overlapped| {
574 let mut flags = 0_u32;
575 WSARecv(
576 socket,
577 &wsabuf,
578 1,
579 std::ptr::null_mut(),
580 &mut flags,
581 overlapped,
582 None,
583 )
584 })
585 }
586 }
587
588 /// Send `data`, blocking until the send completes, and return the bytes sent.
589 ///
590 /// # Errors
591 ///
592 /// Returns [`io::ErrorKind::InvalidInput`] if `data` is longer than
593 /// `u32::MAX`, which `WSABUF`'s byte count cannot express, or any error from
594 /// issuing or completing the send.
595 pub fn send(&self, data: &[u8]) -> io::Result<usize> {
596 let wsabuf = WSABUF {
597 len: checked_len(data.len(), "send buffer")?,
598 buf: data.as_ptr().cast_mut(),
599 };
600 // SAFETY: issues exactly one WSASend from `data` via `wsabuf`, both of
601 // which stay valid for the whole blocking call; WSASend does not write
602 // through the buffer pointer.
603 unsafe {
604 self.run(|socket, overlapped| {
605 WSASend(
606 socket,
607 &wsabuf,
608 1,
609 std::ptr::null_mut(),
610 0,
611 overlapped,
612 None,
613 )
614 })
615 }
616 }
617
618 /// Issue one overlapped socket operation with a completion event and block on
619 /// `WSAGetOverlappedResult` until it finishes, returning the bytes transferred.
620 ///
621 /// # Safety
622 ///
623 /// `issue` must start exactly one overlapped operation using the provided
624 /// socket and `OVERLAPPED`, with any buffers valid for the whole call.
625 unsafe fn run<F>(&self, issue: F) -> io::Result<usize>
626 where
627 F: FnOnce(SOCKET, *mut OVERLAPPED) -> i32,
628 {
629 let socket = self.raw_socket();
630 // SAFETY: creates a manual-reset Winsock event; the null handle
631 // `WSA_INVALID_EVENT` signals failure.
632 let event: WSAEVENT = unsafe { WSACreateEvent() };
633 if event == WSA_INVALID_EVENT {
634 return Err(io::Error::last_os_error());
635 }
636
637 // SAFETY: `OVERLAPPED` is plain data; a zeroed value with the event in
638 // `hEvent` is the documented way to wait for a single operation.
639 let mut overlapped: OVERLAPPED = unsafe { std::mem::zeroed() };
640 overlapped.hEvent = event as HANDLE;
641
642 let ret = issue(socket, &mut overlapped);
643 if ret != 0 {
644 let error = io::Error::last_os_error();
645 if error.raw_os_error() != Some(WSA_IO_PENDING) {
646 // SAFETY: `event` was created above and is closed exactly once.
647 unsafe { WSACloseEvent(event) };
648 return Err(error);
649 }
650 }
651
652 let mut transferred = 0_u32;
653 let mut flags = 0_u32;
654 // SAFETY: waits on `overlapped`'s event for this one operation to finish.
655 let ok = unsafe {
656 WSAGetOverlappedResult(
657 socket,
658 &overlapped,
659 &mut transferred,
660 i32::from(true),
661 &mut flags,
662 )
663 };
664 let result = if ok == 0 {
665 Err(io::Error::last_os_error())
666 } else {
667 Ok(transferred as usize)
668 };
669 // SAFETY: `event` was created above and is closed exactly once here.
670 unsafe { WSACloseEvent(event) };
671 result
672 }
673}
674
675#[cfg(test)]
676mod tests;