How do I setsockopt of a TcpStream via libc?

543 views Asked by At

I know TcpStream has stream.set_read_timeout but I need to make it in libc for Windows, but my code don´t works and I believe it is because I can't understand the way to put milliseconds in _value: *const c_char. In Rust I wrote let qtie = [100].as_ptr(); but its wrong. I also don't know how return a c_int in extern "C" fn.

use std::net::{TcpListener, TcpStream};
use std::io::{Read, Write};
use std::thread;
use std::time::Duration;

use libc::c_int;
use libc::c_char;

pub unsafe extern "C" fn setsockopt(
    _socket: c_int,
    _nivel: c_int,
    _nombre: c_int,
    _value: *const c_char,
    _option_len: c_int
) -> c_int {return 0;}

fn al_cliente(mut stream: TcpStream) {
    
    const SOL_SOCKET:i32  = 1; // También 0xffff
    const SO_RCVTIMEO:i32 = 20;
    const SO_SNDTIMEO:i32 = 21;
    
    const tam_buff:usize = 10;
    let mut data = [0 as u8; tam_buff];
    
    loop {
        
        println!("{:?}", stream);
        let buska = format!("{:?}", stream);
        let arrsk:Vec<&str> = buska.split(" ").collect();
        
        let socket = arrsk[7].parse::<i32>().unwrap();
        //let socket = 0;
        println!("{}", socket);
        
        let qtie = [100].as_ptr();
        
        // Ejemplo: int nTimeout = 5000; // 5 seconds
        //          setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&nTimeout, sizeof(int));
        unsafe { setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, qtie, 10); }
        
        //stream.set_read_timeout(Some(Duration::from_secs(10)));
        let ver = stream.read(&mut data).unwrap();
        
        stream.write(&data[0..tam_buff]).unwrap();
        
        let atexto = String::from_utf8_lossy(&data);
        println!("{:?}", atexto);
    }
}


fn main() {
    let listener = TcpListener::bind("0.0.0.0:3333").unwrap();
    println!("Server listening on port 3333");

    for stream in listener.incoming() {
        
        match stream {
            Ok(stream) => {
                println!("Conectado: {}", stream.peer_addr().unwrap());
                
                let _hilo = thread::spawn(move || {
                    al_cliente(stream);
                });
            }
            Err(e) => {
                println!("Error: {}", e);
            }
        }
    }
}
1

There are 1 answers

2
harmic On BEST ANSWER

According to the socket manpage:

Specify the receiving or sending timeouts until reporting an error. The argument is a struct timeval.

Luckily, the libc crate defines that structure so you can do this:


    let sock_timeout = libc::timeval {
        tv_sec: 10,
        tv_usec: 0,
    };

    let result = unsafe {
        libc::setsockopt(
            socket,
            libc::SOL_SOCKET,
            libc::SO_RCVTIMEO,
            &sock_timeout as *const libc::timeval as *const libc::c_void,
            std::mem::size_of::<libc::timeval>() as u32,
        );
    };

In order to get the rust reference into a void * pointer, you need to cast it twice: once to a pointer to the type, then to the void pointer.

Note that libc also defines all the constants you need, so you don't need to define them yourself.

Also don't forget to check the return value from setsockopt. It will return 0 for success and -1 for an error. The error code will be available in errno, which in rust you can access via Error::last_os_error().raw_os_error().