Skip to content

Commit 640895c

Browse files
abonanderThomasdezeeuw
authored andcommitted
Add Socket::peek_sender()
Signed-off-by: Austin Bonander <[email protected]>
1 parent 3fff5c1 commit 640895c

File tree

4 files changed

+81
-0
lines changed

4 files changed

+81
-0
lines changed

src/socket.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,11 +564,36 @@ impl Socket {
564564
/// `peek_from` makes the same safety guarantees regarding the `buf`fer as
565565
/// [`recv`].
566566
///
567+
/// # Note: Datagram Sockets
568+
/// For datagram sockets, the behavior of this method when `buf` is smaller than
569+
/// the datagram at the head of the receive queue differs between Windows and
570+
/// Unix-like platforms (Linux, macOS, BSDs, etc: colloquially termed "*nix").
571+
///
572+
/// On *nix platforms, the datagram is truncated to the length of `buf`.
573+
///
574+
/// On Windows, an error corresponding to `WSAEMSGSIZE` will be returned.
575+
///
576+
/// For consistency between platforms, be sure to provide a sufficiently large buffer to avoid
577+
/// truncation; the exact size required depends on the underlying protocol.
578+
///
579+
/// If you just want to know the sender of the data, try [`peek_sender`].
580+
///
567581
/// [`recv`]: Socket::recv
582+
/// [`peek_sender`]: Socket::peek_sender
568583
pub fn peek_from(&self, buf: &mut [MaybeUninit<u8>]) -> io::Result<(usize, SockAddr)> {
569584
self.recv_from_with_flags(buf, sys::MSG_PEEK)
570585
}
571586

587+
/// Retrieve the sender for the data at the head of the receive queue.
588+
///
589+
/// This is equivalent to calling [`peek_from`] with a zero-sized buffer,
590+
/// but suppresses the `WSAEMSGSIZE` error on Windows.
591+
///
592+
/// [`peek_from`]: Socket::peek_from
593+
pub fn peek_sender(&self) -> io::Result<SockAddr> {
594+
sys::peek_sender(self.as_raw())
595+
}
596+
572597
/// Sends data on the socket to a connected peer.
573598
///
574599
/// This is typically used on TCP sockets or datagram sockets which have

src/sys/unix.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,15 @@ pub(crate) fn recv_from(
752752
}
753753
}
754754

755+
pub(crate) fn peek_sender(fd: Socket) -> io::Result<SockAddr> {
756+
// Unix-like platforms simply truncate the returned data, so this implementation is trivial.
757+
// However, for Windows this requires suppressing the `WSAEMSGSIZE` error,
758+
// so that requires a different approach.
759+
// NOTE: macOS does not populate `sockaddr` if you pass a zero-sized buffer.
760+
let (_, sender) = recv_from(fd, &mut [MaybeUninit::uninit(); 8], MSG_PEEK)?;
761+
Ok(sender)
762+
}
763+
755764
#[cfg(not(target_os = "redox"))]
756765
pub(crate) fn recv_vectored(
757766
fd: Socket,

src/sys/windows.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,38 @@ pub(crate) fn recv_from(
466466
}
467467
}
468468

469+
pub(crate) fn peek_sender(socket: Socket) -> io::Result<SockAddr> {
470+
// Safety: `recvfrom` initialises the `SockAddr` for us.
471+
let ((), sender) = unsafe {
472+
SockAddr::try_init(|storage, addrlen| {
473+
let res = syscall!(
474+
recvfrom(
475+
socket,
476+
// Windows *appears* not to care if you pass a null pointer.
477+
ptr::null_mut(),
478+
0,
479+
MSG_PEEK,
480+
storage.cast(),
481+
addrlen,
482+
),
483+
PartialEq::eq,
484+
SOCKET_ERROR
485+
);
486+
match res {
487+
Ok(_n) => Ok(()),
488+
Err(e) => match e.raw_os_error() {
489+
Some(code) if code == (WSAESHUTDOWN as i32) || code == (WSAEMSGSIZE as i32) => {
490+
Ok(())
491+
}
492+
_ => Err(e),
493+
},
494+
}
495+
})
496+
}?;
497+
498+
Ok(sender)
499+
}
500+
469501
pub(crate) fn recv_from_vectored(
470502
socket: Socket,
471503
bufs: &mut [crate::MaybeUninitSlice<'_>],

tests/socket.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,21 @@ fn out_of_band() {
512512
assert_eq!(unsafe { assume_init(&buf[..n]) }, DATA);
513513
}
514514

515+
#[test]
516+
#[cfg(not(target_os = "redox"))] // cfg of `udp_pair_unconnected()`
517+
fn udp_peek_sender() {
518+
let (socket_a, socket_b) = udp_pair_unconnected();
519+
520+
let socket_a_addr = socket_a.local_addr().unwrap();
521+
let socket_b_addr = socket_b.local_addr().unwrap();
522+
523+
socket_b.send_to(b"Hello, world!", &socket_a_addr).unwrap();
524+
525+
let sender_addr = socket_a.peek_sender().unwrap();
526+
527+
assert_eq!(sender_addr.as_socket(), socket_b_addr.as_socket());
528+
}
529+
515530
#[test]
516531
#[cfg(not(target_os = "redox"))]
517532
fn send_recv_vectored() {

0 commit comments

Comments
 (0)