Skip to content

Instantly share code, notes, and snippets.

@abonander
Created February 20, 2023 01:04
Show Gist options
  • Save abonander/f1a35cd91a8675a65546e051a0743e62 to your computer and use it in GitHub Desktop.
Save abonander/f1a35cd91a8675a65546e051a0743e62 to your computer and use it in GitHub Desktop.
Demonstration of `recvfrom()` on Windows properly filling the `sockaddr` argument
//! NOTE: compiles only on Windows.
use std::{io, mem};
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
// libc = "0.2.139"
use libc::c_int;
fn main() {
let socket_a = UdpSocket::bind("127.0.0.1:0").expect("failed to bind socket_a");
let socket_b = UdpSocket::bind("127.0.0.1:0").expect("failed to bind socket_b");
let socket_b_addr = socket_b.local_addr().expect("failed to get local_addr from socket_b");
socket_a.send_to(b"Hello, world!", socket_b_addr).expect("failed to send to socket_b");
println!("result: {:?}", peek_sender(&socket_b));
}
fn peek_sender(socket: &UdpSocket) -> io::Result<Option<SocketAddr>> {
// https://github.com/rust-lang/rust/blob/master/library/std/src/sys/windows/c.rs#L239
const MSG_PEEK: c_int = 0x2;
const SOCKET_ERROR: c_int = -1;
const WSAEMSGSIZE: c_int = 10040;
// https://github.com/rust-lang/rust/blob/master/library/std/src/sys/windows/c.rs#L212
const AF_INET: c_int = 2;
use std::os::windows::io::AsRawSocket;
#[allow(non_camel_case_types)]
#[repr(C)]
struct sockaddr_in {
sa_family: u16,
port: u16,
addr: [u8; 4],
sin_zero: [i8; 8],
}
let mut buf = [0i8; 8];
let mut sockaddr = sockaddr_in {
sa_family: 0,
port: 0,
addr: [0u8; 4],
sin_zero: [0i8; 8]
};
let mut len: c_int = mem::size_of_val(&sockaddr) as c_int;
let result = unsafe {
libc::recvfrom(
// `RawSocket` is `u64` but `libc::SOCKET` is `usize` *shrug*
socket.as_raw_socket() as libc::SOCKET,
buf.as_mut_ptr(),
0,
MSG_PEEK,
// SAFETY: this only works because we know we bound the socket as IPv4.
&mut sockaddr as *mut _ as *mut libc::sockaddr,
&mut len,
)
};
assert!(result <= 0, "positive result returned from `recvfrom()` with empty buffer");
// Socket closed
if result == 0 {
return Ok(None);
}
assert_eq!(result, SOCKET_ERROR);
let err = io::Error::last_os_error();
// WSAEMSGSIZE is the expected error since we passed a zero-sized buffer.
if err.raw_os_error() != Some(WSAEMSGSIZE) {
return Err(err);
}
assert_eq!(sockaddr.sa_family as c_int, AF_INET);
Ok(Some(SocketAddrV4::new(
Ipv4Addr::from(sockaddr.addr),
sockaddr.port
).into()))
}
cargo.exe run --color=always --package peek-from-windows --bin peek-from-windows
Finished dev [unoptimized + debuginfo] target(s) in 0.02s
Running `target\debug\peek-from-windows.exe`
result: Ok(Some(127.0.0.1:60358))
Process finished with exit code 0
@abonander
Copy link
Author

I am aware that I forgot to convert from big-endian for port, but this still works for the sake of demonstration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment