Rust plugin

Recent experiments have shown that it is possible to bind any language that compile to native code to proxenet, as long as it exports some specific symbols. This relies on one of most powerful advantages of proxenet, that is being 100% written in C.

This page will detail how to write proxenet plugins in the Rust. Rust will use its compiler (rustc) to generate native code that respects its paradigms (multi-threadable, memory and type safe). Since rust allows to generate shared objects, and explicitly exposing some exported symbols, binding it on proxenet did not require any change to the C interface. The challenge consisted in exporting the structure from the C level (and potentially unsafe) to rust structure.

As a result, you can now use the following skeleton for writing your rust plugin for proxenet.

Enjoy!

Plugin skeleton

//
// Dummy Rust plugin for proxenet
// by @_hugsy_
//
// Compile with:
// $ rustc -o MyPlugin.so Myplugin.rs
//

#![crate_type = "dylib"]

use std::mem;


//
// proxenet request hook for Rust
// This function is type and memory safe.
//
fn rust_request_hook(request_id: u32, request: Vec<u8>, uri: String) -> Vec<u8>
{
    // Play your funky Rust here white boy!
    return request;
}


//
// proxenet response hook for Rust
// This function is type and memory safe.
//
fn rust_response_hook(response_id: u32, response: Vec<u8>, uri: String) -> Vec<u8>
{
    return response;
}


//
// Poor man strlen() compat function for Rust
//
fn rust_strlen(src: *const u8) -> usize
{
    let mut i = 0;
    let mut do_loop = true;

    while do_loop {
        let c = unsafe { *src.offset(i) as char };
        if c == '\x00' { do_loop = false; }
        else { i += 1; }
        }

    return i as usize;
}


//
// This function is used to transform and dispatch C compatible data type (unsafe) to
// Rust valid types, dispatch to the right function ({request,response}_hook).
//
fn generic_hook(rid: u32, buf: *mut u8, uri: *mut u8, buflen: *mut usize, is_request: bool) -> *mut u8
{
    unsafe {
        let rust_buf = Vec::from_raw_parts(buf, *buflen, *buflen);
        let rust_urilen = rust_strlen( uri );
        let rust_uri = String::from_raw_parts(uri, rust_urilen, rust_urilen);
        let ret;

        if is_request {
            ret = rust_request_hook (rid, rust_buf, rust_uri);
        } else {
            ret = rust_response_hook(rid, rust_buf, rust_uri);
        }

        *buflen = ret.len();
        return mem::transmute(&ret);
    }
}


#[no_mangle]
pub extern fn proxenet_request_hook(request_id: u32, request: *mut u8, uri: *mut u8, request_len: *mut usize) -> *mut u8
{
    return generic_hook(request_id, request, uri, request_len, true);
}


#[no_mangle]
pub extern fn proxenet_response_hook(response_id: u32, response: *mut u8, uri: *mut u8, response_len: *mut usize) -> *mut u8
{
    return generic_hook(response_id, response, uri, response_len, false);
}

Note: not being a professional rust developper, maybe my approach is sub optimal. If so, shoot me an email with your improvement :)

The C plugin interface of proxenet must be used to load rust compiled plugins. So all the plugins must be compiled first, and the shared library must be suffixed with .so.

$ rustc  -o MyPlugin.so MyPlugin.rs

Move your compiled plugin to proxenet plugin directory, and fire it up.