3
votes

Note (May 2015): this question was about the Rust language before it stabilized and 1.0 was released, and is now mostly of historical interest: the code will not compile on stable Rust, etc.


I'm writing a wrapper over libpcap for a project I'm working on, and to learn Rust (which I just started doing). I'm working on writing a safe Rust over around this right now:

#[link(name = "pcap")]
extern {
    fn pcap_lookupdev(errormsg: *mut libc::c_char) -> *const libc::c_char;
}

Here's what I've got so far:

fn lookupdev() -> Result<String, String> {
    // Capacity must be at least PCAP_ERRBUF_SIZE
    let mut errbuf = [0 as c_char, ..256];

    let result = unsafe {
        let ptr = pcap_lookupdev(errbuf.as_mut_ptr() as *mut libc::c_char);
        if ptr.is_null() {
            None
        }
        else {
            Some(CString::new(ptr, false))
        }
    };

    match result {
          Some(result) => Ok(result.as_str().unwrap().to_string()),
          None => Err(unsafe { String::from_raw_buf(errbuf.as_ptr() as *const u8) })
    }
}

This works, but I don't like the use from String::from_raw_buf, which will happily walk all over gigabytes of memory instead of stopping when it reaches the end of errbuf.

I can write something that loops over errbuf manually and converts it to a Rust string (stopping at the end of the buffer), but in my mind this is a C FFI idiom for strings (pass an allocated block of memory, where a string can be written) that should be supported by the Rust standard library. However, I could't find anything in the Rust FFI guide nor in the standard library documentation.

What's the best way of handling this FFI case in Rust? Is there at least a String::from_raw_buf_with_maxlen or a String::from_array?

1

1 Answers

3
votes

The Rust standard library itself doesn't really seem to handle this possibility:

This is probably unintentional, and IMO, a bug should be filed with Rust.

So I don't think there is an easy solution right now. The one I can come up with is this

#![feature(slicing_syntax)]

extern crate libc;

use std::c_str::CString;

fn main() {
    let errbuf = ['o' as libc::c_char; 256];
    if errbuf.iter().any(|c| *c == 0 as libc::c_char) {
        unsafe { CString::new(errbuf.as_ptr(), false).to_string() };
    } else {
        panic!("FFI returned invalid string!");
    }
}