50 short programs in Rust, #4 - raw sockets

November 10, 2019

(I'm writing 50 short programs in Rust, often ones I would normally write in Python, to improve my fluency - starting here).

In my last post, I showed a short C program I'd written to test bind() behaviour with SOCK_RAW sockets. I thought I'd try porting it into Rust:

use nix::sys::socket::*;

fn main() {
    let s = socket(
        AddressFamily::Inet,
        SockType::Raw,
        SockFlag::empty(),
        Some(SockProtocol::Udp),
    )
    .expect("Failed to get socket");
    let addr =
        SockAddr::new_inet(InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 15000));
    bind(s, &addr).expect("Failed to bind");
    println!("Bound successfully!");
}

It's nice that this is shorter and clearer than the C equivalent (no casting between the myriad struct sockaddr variants), but the tradeoff is that it takes longer to compile, especially when pulling the nix library in.

One potential other downside is that it's harder to repro and debug issues in a C/C++ program with a Rust repro - but in this case the issues are really about system call behaviour, and truss/strace can trace them in the same way across languages:

[root@freebsd ~/50-short-programs-in-rust/raw-bind]# PORT=15000 ./target/debug/raw-bind                                      
thread 'main' panicked at 'Failed to bind: Sys(EADDRNOTAVAIL)', src/libcore/result.rs:1084:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
[root@freebsd ~/50-short-programs-in-rust/raw-bind]# PORT=15000 truss ./target/debug/raw-bind 2> >(egrep "^(bind|socket)")
socket(PF_INET,SOCK_RAW,IPPROTO_UDP)             = 3 (0x3)
bind(3,{ AF_INET 127.0.0.1:15000 },16)           ERR#49 'Can't assign requested address'
[root@freebsd ~/50-short-programs-in-rust/raw-bind]# PORT=0 truss ./target/debug/raw-bind 2> >(egrep "^(bind|socket)")
socket(PF_INET,SOCK_RAW,IPPROTO_UDP)             = 3 (0x3)
bind(3,{ AF_INET 127.0.0.1:0 },16)               = 0 (0x0)
Bound successfully!