use libc::{c_char, c_int, uint32_t};
use std::ffi::CStr;
use smartlist::*;
use tor_allocate::allocate_and_copy_string;
use errors::ProtoverError;
use protover::*;
fn translate_to_rust(c_proto: uint32_t) -> Result<Protocol, ProtoverError> {
match c_proto {
0 => Ok(Protocol::Link),
1 => Ok(Protocol::LinkAuth),
2 => Ok(Protocol::Relay),
3 => Ok(Protocol::DirCache),
4 => Ok(Protocol::HSDir),
5 => Ok(Protocol::HSIntro),
6 => Ok(Protocol::HSRend),
7 => Ok(Protocol::Desc),
8 => Ok(Protocol::Microdesc),
9 => Ok(Protocol::Cons),
10 => Ok(Protocol::Padding),
11 => Ok(Protocol::FlowCtrl),
_ => Err(ProtoverError::UnknownProtocol),
}
}
#[no_mangle]
pub extern "C" fn protover_all_supported(
c_relay_version: *const c_char,
missing_out: *mut *mut c_char,
) -> c_int {
if c_relay_version.is_null() {
return 1;
}
let c_str: &CStr = unsafe { CStr::from_ptr(c_relay_version) };
let relay_version = match c_str.to_str() {
Ok(n) => n,
Err(_) => return 1,
};
let relay_proto_entry: UnvalidatedProtoEntry =
match UnvalidatedProtoEntry::from_str_any_len(relay_version) {
Ok(n) => n,
Err(_) => return 1,
};
if let Some(unsupported) = relay_proto_entry.all_supported() {
if missing_out.is_null() {
return 0;
}
let ptr = allocate_and_copy_string(&unsupported.to_string());
unsafe { *missing_out = ptr };
return 0;
}
1
}
#[no_mangle]
pub extern "C" fn protocol_list_supports_protocol(
c_protocol_list: *const c_char,
c_protocol: uint32_t,
version: uint32_t,
) -> c_int {
if c_protocol_list.is_null() {
return 0;
}
let c_str: &CStr = unsafe { CStr::from_ptr(c_protocol_list) };
let protocol_list = match c_str.to_str() {
Ok(n) => n,
Err(_) => return 0,
};
let proto_entry: UnvalidatedProtoEntry = match protocol_list.parse() {
Ok(n) => n,
Err(_) => return 0,
};
let protocol: UnknownProtocol = match translate_to_rust(c_protocol) {
Ok(n) => n.into(),
Err(_) => return 0,
};
if proto_entry.supports_protocol(&protocol, &version) {
1
} else {
0
}
}
#[no_mangle]
pub extern "C" fn protover_contains_long_protocol_names_(c_protocol_list: *const c_char) -> c_int {
if c_protocol_list.is_null() {
return 1;
}
let c_str: &CStr = unsafe { CStr::from_ptr(c_protocol_list) };
let protocol_list = match c_str.to_str() {
Ok(n) => n,
Err(_) => return 1,
};
match protocol_list.parse::<UnvalidatedProtoEntry>() {
Ok(_) => 0,
Err(_) => 1,
}
}
#[no_mangle]
pub extern "C" fn protocol_list_supports_protocol_or_later(
c_protocol_list: *const c_char,
c_protocol: uint32_t,
version: uint32_t,
) -> c_int {
if c_protocol_list.is_null() {
return 0;
}
let c_str: &CStr = unsafe { CStr::from_ptr(c_protocol_list) };
let protocol_list = match c_str.to_str() {
Ok(n) => n,
Err(_) => return 0,
};
let protocol = match translate_to_rust(c_protocol) {
Ok(n) => n,
Err(_) => return 0,
};
let proto_entry: UnvalidatedProtoEntry = match protocol_list.parse() {
Ok(n) => n,
Err(_) => return 0,
};
if proto_entry.supports_protocol_or_later(&protocol.into(), &version) {
return 1;
}
0
}
#[no_mangle]
pub extern "C" fn protover_get_supported_protocols() -> *const c_char {
let supported: &'static CStr;
supported = get_supported_protocols_cstr();
supported.as_ptr()
}
#[no_mangle]
pub extern "C" fn protover_compute_vote(list: *const Stringlist, threshold: c_int) -> *mut c_char {
if list.is_null() {
return allocate_and_copy_string("");
}
let data: Vec<String> = unsafe { (*list).get_list() };
let hold: usize = threshold as usize;
let mut proto_entries: Vec<UnvalidatedProtoEntry> = Vec::new();
for datum in data {
let entry: UnvalidatedProtoEntry = match datum.parse() {
Ok(n) => n,
Err(_) => continue,
};
proto_entries.push(entry);
}
let vote: UnvalidatedProtoEntry = ProtoverVote::compute(&proto_entries, &hold);
allocate_and_copy_string(&vote.to_string())
}
#[no_mangle]
pub extern "C" fn protover_is_supported_here(c_protocol: uint32_t, version: uint32_t) -> c_int {
let protocol = match translate_to_rust(c_protocol) {
Ok(n) => n,
Err(_) => return 0,
};
let is_supported = is_supported_here(&protocol, &version);
return if is_supported { 1 } else { 0 };
}
#[no_mangle]
pub extern "C" fn protover_compute_for_old_tor(version: *const c_char) -> *const c_char {
let supported: &'static CStr;
let empty: &'static CStr;
empty = cstr!("");
if version.is_null() {
return empty.as_ptr();
}
let c_str: &CStr = unsafe { CStr::from_ptr(version) };
let version = match c_str.to_str() {
Ok(n) => n,
Err(_) => return empty.as_ptr(),
};
supported = compute_for_old_tor_cstr(&version);
supported.as_ptr()
}