I want to send an UDP query to a server and receive it's response. In C it is: call socket
then connect
then write
then read
. The operating system takes care of choosing a suitable local IP address and port to use.
I'm trying to do the same in Rust. But I can not find a way to get a UdpSocket
without myself specifying the address and port. The closest I could do was:
fn main() {
use std::net::{Ipv4Addr, UdpSocket};
let socket = UdpSocket::bind((Ipv4Addr::UNSPECIFIED, 12345)).unwrap();
socket.connect("1.1.1.1:53").unwrap();
socket.send(b"\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\
\x07example\x03com\x00\
\x00\x01\x00\x01").unwrap();
let mut buffer = [0; 512];
let len = socket.recv(&mut buffer).unwrap();
for b in &buffer[0..len] {
print!("{:02x} ", b);
}
println!("");
}
This works but has two downsides compared to the C version.
it could fail if I specify a local port that is already in use,
the socket listens on all available addresses while the C version only listen on "suitable" address. This is important with UDP to avoid response spoofing.
For example if I query 127.0.0.1, then the C version will automatically bind to the 127.0.0.1 address. The internet will not be able to spoof answers to my queries. Also, if I have two network interfaces, one connected to the internet, and one to my local network with IP 192.168.0.1, I can query a resolver on the local network, says 172.16.17.18. The C version will bind to the address 192.168.0.1 and I'll be sure that the answer is not comming from the internet.
How would I best do the same as the C version?
Edit: to explain the second point about finding a "suitable" address.
It's not obvious, but if you bind the socket to (UNSPECIFIED, 0)
, then when you connect to a remote address the local address is set to the appropriate interface address. (Port 0
means to auto-allocate a port, just like an unbound socket in C.) You can confirm this by calling local_addr()
on the socket after a successful connection.
I apologize for the complexity of POSIX. :-)