1 // Copyright (c) 2016-2019, The Tor Project, Inc. */
2 // See LICENSE for licensing information */
4 //! FFI functions, only to be called from C.
6 //! Equivalent C versions of this api are in `protover.c`
8 use libc::{c_char, c_int, uint32_t};
12 use tor_allocate::allocate_and_copy_string;
14 use errors::ProtoverError;
17 /// Translate C enums to Rust Proto enums, using the integer value of the C
18 /// enum to map to its associated Rust enum.
20 /// C_RUST_COUPLED: protover.h `protocol_type_t`
21 fn translate_to_rust(c_proto: uint32_t) -> Result<Protocol, ProtoverError> {
23 0 => Ok(Protocol::Link),
24 1 => Ok(Protocol::LinkAuth),
25 2 => Ok(Protocol::Relay),
26 3 => Ok(Protocol::DirCache),
27 4 => Ok(Protocol::HSDir),
28 5 => Ok(Protocol::HSIntro),
29 6 => Ok(Protocol::HSRend),
30 7 => Ok(Protocol::Desc),
31 8 => Ok(Protocol::Microdesc),
32 9 => Ok(Protocol::Cons),
33 10 => Ok(Protocol::Padding),
34 11 => Ok(Protocol::FlowCtrl),
35 _ => Err(ProtoverError::UnknownProtocol),
39 /// Provide an interface for C to translate arguments and return types for
40 /// protover::all_supported
42 pub extern "C" fn protover_all_supported(
43 c_relay_version: *const c_char,
44 missing_out: *mut *mut c_char,
46 if c_relay_version.is_null() {
50 // Require an unsafe block to read the version from a C string. The pointer
51 // is checked above to ensure it is not null.
52 let c_str: &CStr = unsafe { CStr::from_ptr(c_relay_version) };
54 let relay_version = match c_str.to_str() {
59 let relay_proto_entry: UnvalidatedProtoEntry =
60 match UnvalidatedProtoEntry::from_str_any_len(relay_version) {
65 if let Some(unsupported) = relay_proto_entry.all_supported() {
66 if missing_out.is_null() {
69 let ptr = allocate_and_copy_string(&unsupported.to_string());
70 unsafe { *missing_out = ptr };
78 /// Provide an interface for C to translate arguments and return types for
79 /// protover::list_supports_protocol
81 pub extern "C" fn protocol_list_supports_protocol(
82 c_protocol_list: *const c_char,
86 if c_protocol_list.is_null() {
90 // Require an unsafe block to read the version from a C string. The pointer
91 // is checked above to ensure it is not null.
92 let c_str: &CStr = unsafe { CStr::from_ptr(c_protocol_list) };
94 let protocol_list = match c_str.to_str() {
98 let proto_entry: UnvalidatedProtoEntry = match protocol_list.parse() {
102 let protocol: UnknownProtocol = match translate_to_rust(c_protocol) {
106 if proto_entry.supports_protocol(&protocol, &version) {
114 pub extern "C" fn protover_contains_long_protocol_names_(c_protocol_list: *const c_char) -> c_int {
115 if c_protocol_list.is_null() {
119 // Require an unsafe block to read the version from a C string. The pointer
120 // is checked above to ensure it is not null.
121 let c_str: &CStr = unsafe { CStr::from_ptr(c_protocol_list) };
123 let protocol_list = match c_str.to_str() {
128 match protocol_list.parse::<UnvalidatedProtoEntry>() {
134 /// Provide an interface for C to translate arguments and return types for
135 /// protover::list_supports_protocol_or_later
137 pub extern "C" fn protocol_list_supports_protocol_or_later(
138 c_protocol_list: *const c_char,
139 c_protocol: uint32_t,
142 if c_protocol_list.is_null() {
146 // Require an unsafe block to read the version from a C string. The pointer
147 // is checked above to ensure it is not null.
148 let c_str: &CStr = unsafe { CStr::from_ptr(c_protocol_list) };
150 let protocol_list = match c_str.to_str() {
155 let protocol = match translate_to_rust(c_protocol) {
160 let proto_entry: UnvalidatedProtoEntry = match protocol_list.parse() {
165 if proto_entry.supports_protocol_or_later(&protocol.into(), &version) {
171 /// Provide an interface for C to translate arguments and return types for
172 /// protover::get_supported_protocols
174 pub extern "C" fn protover_get_supported_protocols() -> *const c_char {
175 let supported: &'static CStr;
177 supported = get_supported_protocols_cstr();
181 /// Provide an interface for C to translate arguments and return types for
182 /// protover::compute_vote
184 // Why is the threshold a signed integer? —isis
186 pub extern "C" fn protover_compute_vote(list: *const Stringlist, threshold: c_int) -> *mut c_char {
188 return allocate_and_copy_string("");
191 // Dereference of raw pointer requires an unsafe block. The pointer is
192 // checked above to ensure it is not null.
193 let data: Vec<String> = unsafe { (*list).get_list() };
194 let hold: usize = threshold as usize;
195 let mut proto_entries: Vec<UnvalidatedProtoEntry> = Vec::new();
198 let entry: UnvalidatedProtoEntry = match datum.parse() {
202 proto_entries.push(entry);
204 let vote: UnvalidatedProtoEntry = ProtoverVote::compute(&proto_entries, &hold);
206 allocate_and_copy_string(&vote.to_string())
209 /// Provide an interface for C to translate arguments and return types for
210 /// protover::is_supported_here
212 pub extern "C" fn protover_is_supported_here(c_protocol: uint32_t, version: uint32_t) -> c_int {
213 let protocol = match translate_to_rust(c_protocol) {
218 let is_supported = is_supported_here(&protocol, &version);
220 return if is_supported { 1 } else { 0 };
223 /// Provide an interface for C to translate arguments and return types for
224 /// protover::compute_for_old_tor
226 pub extern "C" fn protover_compute_for_old_tor(version: *const c_char) -> *const c_char {
227 let supported: &'static CStr;
228 let empty: &'static CStr;
232 if version.is_null() {
233 return empty.as_ptr();
236 // Require an unsafe block to read the version from a C string. The pointer
237 // is checked above to ensure it is not null.
238 let c_str: &CStr = unsafe { CStr::from_ptr(version) };
240 let version = match c_str.to_str() {
242 Err(_) => return empty.as_ptr(),
245 supported = compute_for_old_tor_cstr(&version);