Search code examples
rustudpclient

How do I `connect` an UDP socket and let the OS choose the port and address


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.

  1. it could fail if I specify a local port that is already in use,

  2. 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.


Solution

  • 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. :-)