rustudprecvfrom

UdpSocket.recv_from fails with "end of file" but I can see the incoming package in Wireshark


Editor's note: This code example is from a version of Rust prior to 1.0 and is not valid Rust 1.0 code. The concepts discussed in the question are still valid.

I'm experimenting with torrent scraping using Rust. I can see the incoming package in Wireshark, but my recv_from calls always return Error("End of file"). Here's my program:

use std::io::net::ip::{Ipv4Addr, SocketAddr};
use std::io::net::udp::UdpSocket;
use std::rand;

use std::io::MemWriter;

fn main() {
    let addr = SocketAddr { ip: Ipv4Addr(0, 0, 0, 0), port: 35000 };
    let mut socket = match UdpSocket::bind(addr) {
        Ok(s) => s,
        Err(e) => panic!("couldn't bind socket: {}", e),
    };

    let mut buf: Vec<u8> = Vec::with_capacity(1000);

    let transaction_id: u32 = rand::random();
    let mut req_data = MemWriter::with_capacity(16);
    req_data.write_be_u64(0x41727101980).unwrap(); // connection_id, identifies the protocol.
    req_data.write_be_u32(0).unwrap(); // action: connect
    req_data.write_be_u32(transaction_id).unwrap();

    println!("{}", socket.send_to(req_data.get_ref(), ("31.172.63.252", 80)));

    match socket.recv_from(buf.as_mut_slice()) {
        Ok((amt, src)) => {
            println!("Got {} bytes from {}.", amt, src);
        },
        Err(err) => println!("Can't recv_from: {}", err)
    }
}

The output is always:

āžœ  udp-bug git:(master) āœ— cargo run
   Compiling udp-bug v0.0.1 (file:///home/omer/rust/udp-bug)
     Running `target/udp-bug`
Ok(())
Can't recv_from: end of file

However, I can see the expected response coming in Wireshark:

20235   3512.148636000  31.172.63.252   192.168.1.4 QUIC    60  CID: 0, Seq: 0

This package has a 16-byte payload, exactly what I expect. What's going wrong?


Solution

  • Editor's note: This code example is from a version of Rust prior to 1.0 and is not valid Rust 1.0 code. The concepts discussed in the answer are still valid.

    I think your problem is that you're using Vec::with_capacity() as a mutable slice. Vec::with_capacity() only creates a vector with the specified capacity (naturally), but its length is zero. Consequently, the length of the slice taken from the vector will also be zero:

    let v = Vec::with_capacity(128);
    println!("{}", v.as_mut_slice().len());  // prints 0
    

    Slices can't grow, so recv_from() has no space to write to and it fails with the error.

    You have essentially two options here. First one is to use unsafe set_len() method:

    let mut buf: Vec<u8> = Vec::with_capacity(1000);
    unsafe { buf.set_len(1000); }
    

    This way the buffer will have the correct length but its contents will likely be just garbage. This is not very important for this use case, however, as long as you only access the correct amount of bytes (using the information returned by recv_from()).

    There is a better way, however. You can use stack-allocated fixed-size array:

    let mut buf = [0u8, ..1000];
    
    // ...
    
    match socket.recv_from(buf.as_mut_slice()) {
        // ...
    }
    

    Same thing goes for your req_data: you can use a statically sized array and a BufWriter:

    let transaction_id: u32 = rand::random();
    let mut req_data_buf = [0u8, ..16];
    let mut req_data = BufWriter::new(req_data_buf);
    req_data.write_be_u64(0x41727101980).unwrap(); // connection_id, identifies the protocol.
    req_data.write_be_u32(0).unwrap(); // action: connect
    req_data.write_be_u32(transaction_id).unwrap();
    
    println!("{}", socket.send_to(req_data_buf, ("31.172.63.252", 80)));
    

    This will only work with fixed-size buffers though. If you don't know the size of the buffer, you will still need a Vec.