version: Bump version to 0.4.6.11
[tor.git] / src / rust / protover / ffi.rs
blob2bf8d3a9879d525800350d7e286ca8a87a1c20dd
1 // Copyright (c) 2016-2019, The Tor Project, Inc. */
2 // See LICENSE for licensing information */
4 //! FFI functions, only to be called from C.
5 //!
6 //! Equivalent C versions of this api are in `protover.c`
8 use libc::{c_char, c_int, uint32_t};
9 use std::ffi::CStr;
11 use smartlist::*;
12 use tor_allocate::allocate_and_copy_string;
14 use errors::ProtoverError;
15 use protover::*;
17 /// Translate C enums to Rust Proto enums, using the integer value of the C
18 /// enum to map to its associated Rust enum.
19 ///
20 /// C_RUST_COUPLED: protover.h `protocol_type_t`
21 fn translate_to_rust(c_proto: uint32_t) -> Result<Protocol, ProtoverError> {
22     match c_proto {
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),
36     }
39 /// Provide an interface for C to translate arguments and return types for
40 /// protover::all_supported
41 #[no_mangle]
42 pub extern "C" fn protover_all_supported(
43     c_relay_version: *const c_char,
44     missing_out: *mut *mut c_char,
45 ) -> c_int {
46     if c_relay_version.is_null() {
47         return 1;
48     }
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() {
55         Ok(n) => n,
56         Err(_) => return 1,
57     };
59     let relay_proto_entry: UnvalidatedProtoEntry =
60         match UnvalidatedProtoEntry::from_str_any_len(relay_version) {
61             Ok(n) => n,
62             Err(_) => return 1,
63         };
65     if let Some(unsupported) = relay_proto_entry.all_supported() {
66         if missing_out.is_null() {
67             return 0;
68         }
69         let ptr = allocate_and_copy_string(&unsupported.to_string());
70         unsafe { *missing_out = ptr };
72         return 0;
73     }
75     1
78 /// Provide an interface for C to translate arguments and return types for
79 /// protover::list_supports_protocol
80 #[no_mangle]
81 pub extern "C" fn protocol_list_supports_protocol(
82     c_protocol_list: *const c_char,
83     c_protocol: uint32_t,
84     version: uint32_t,
85 ) -> c_int {
86     if c_protocol_list.is_null() {
87         return 0;
88     }
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() {
95         Ok(n) => n,
96         Err(_) => return 0,
97     };
98     let proto_entry: UnvalidatedProtoEntry = match protocol_list.parse() {
99         Ok(n) => n,
100         Err(_) => return 0,
101     };
102     let protocol: UnknownProtocol = match translate_to_rust(c_protocol) {
103         Ok(n) => n.into(),
104         Err(_) => return 0,
105     };
106     if proto_entry.supports_protocol(&protocol, &version) {
107         1
108     } else {
109         0
110     }
113 #[no_mangle]
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() {
116         return 1;
117     }
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() {
124         Ok(n) => n,
125         Err(_) => return 1,
126     };
128     match protocol_list.parse::<UnvalidatedProtoEntry>() {
129         Ok(_) => 0,
130         Err(_) => 1,
131     }
134 /// Provide an interface for C to translate arguments and return types for
135 /// protover::list_supports_protocol_or_later
136 #[no_mangle]
137 pub extern "C" fn protocol_list_supports_protocol_or_later(
138     c_protocol_list: *const c_char,
139     c_protocol: uint32_t,
140     version: uint32_t,
141 ) -> c_int {
142     if c_protocol_list.is_null() {
143         return 0;
144     }
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() {
151         Ok(n) => n,
152         Err(_) => return 0,
153     };
155     let protocol = match translate_to_rust(c_protocol) {
156         Ok(n) => n,
157         Err(_) => return 0,
158     };
160     let proto_entry: UnvalidatedProtoEntry = match protocol_list.parse() {
161         Ok(n) => n,
162         Err(_) => return 0,
163     };
165     if proto_entry.supports_protocol_or_later(&protocol.into(), &version) {
166         return 1;
167     }
168     0
171 /// Provide an interface for C to translate arguments and return types for
172 /// protover::get_supported_protocols
173 #[no_mangle]
174 pub extern "C" fn protover_get_supported_protocols() -> *const c_char {
175     let supported: &'static CStr;
177     supported = get_supported_protocols_cstr();
178     supported.as_ptr()
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
185 #[no_mangle]
186 pub extern "C" fn protover_compute_vote(list: *const Stringlist, threshold: c_int) -> *mut c_char {
187     if list.is_null() {
188         return allocate_and_copy_string("");
189     }
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();
197     for datum in data {
198         let entry: UnvalidatedProtoEntry = match datum.parse() {
199             Ok(n) => n,
200             Err(_) => continue,
201         };
202         proto_entries.push(entry);
203     }
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
211 #[no_mangle]
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) {
214         Ok(n) => n,
215         Err(_) => return 0,
216     };
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
225 #[no_mangle]
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;
230     empty = cstr!("");
232     if version.is_null() {
233         return empty.as_ptr();
234     }
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() {
241         Ok(n) => n,
242         Err(_) => return empty.as_ptr(),
243     };
245     supported = compute_for_old_tor_cstr(&version);
246     supported.as_ptr()