1 /*
2 * Copyright (C) 2021, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 use super::dns_https_frontend::DohFrontend;
18 use super::stats::Stats;
19
20 use anyhow::{bail, Result};
21 use libc::c_char;
22 use log::warn;
23 use std::ffi::CStr;
24 use std::net::{IpAddr, SocketAddr};
25 use std::str::FromStr;
26
27 /// Creates a DohFrontend object by the given IP addresss and ports. Returns the pointer of
28 /// the object if the creation succeeds; otherwise, returns a null pointer.
29 ///
30 /// # Safety
31 ///
32 /// The parameters `addr`, `port`, `backend_addr`, and `backend_port` must all point to null
33 /// terminated UTF-8 encoded strings.
34 #[no_mangle]
frontend_new( addr: *const c_char, port: *const c_char, backend_addr: *const c_char, backend_port: *const c_char, ) -> *mut DohFrontend35 pub unsafe extern "C" fn frontend_new(
36 addr: *const c_char,
37 port: *const c_char,
38 backend_addr: *const c_char,
39 backend_port: *const c_char,
40 ) -> *mut DohFrontend {
41 // SAFETY: Our caller promises that addr is a valid C string.
42 let addr = unsafe { CStr::from_ptr(addr) }.to_str().unwrap();
43 // SAFETY: Our caller promises that port is a valid C string.
44 let port = unsafe { CStr::from_ptr(port) }.to_str().unwrap();
45 // SAFETY: Our caller promises that backend_addr is a valid C string.
46 let backend_addr = unsafe { CStr::from_ptr(backend_addr) }.to_str().unwrap();
47 // SAFETY: Our caller promises that backend_port is a valid C string.
48 let backend_port = unsafe { CStr::from_ptr(backend_port) }.to_str().unwrap();
49
50 let socket_addr = to_socket_addr(addr, port).or_else(logging_and_return_err);
51 let backend_socket_addr =
52 to_socket_addr(backend_addr, backend_port).or_else(logging_and_return_err);
53 if socket_addr.is_err() || backend_socket_addr.is_err() {
54 return std::ptr::null_mut();
55 }
56
57 match DohFrontend::new(socket_addr.unwrap(), backend_socket_addr.unwrap()) {
58 Ok(c) => Box::into_raw(c),
59 Err(_) => std::ptr::null_mut(),
60 }
61 }
62
63 /// Starts the `DohFrontend` worker thread. Returns true if the worker thread is spawned
64 /// successfully; otherwise, it returns false.
65 #[no_mangle]
frontend_start(doh: &mut DohFrontend) -> bool66 pub extern "C" fn frontend_start(doh: &mut DohFrontend) -> bool {
67 doh.start().or_else(logging_and_return_err).is_ok()
68 }
69
70 /// Stops the `DohFrontend` worker thread.
71 #[no_mangle]
frontend_stop(doh: &mut DohFrontend) -> bool72 pub extern "C" fn frontend_stop(doh: &mut DohFrontend) -> bool {
73 doh.stop().or_else(logging_and_return_err).is_ok()
74 }
75
76 /// Deletes the `DohFrontend` created from `frontend_new`.
77 /// If the caller has called `frontend_start` to start `DohFrontend`, it has to call
78 /// call `frontend_stop` to stop the worker thread before deleting the object.
79 ///
80 /// The DohFrontend is not set to null pointer, caller needs to do it on its own.
81 ///
82 /// # Safety
83 ///
84 /// `doh` must be a pointer either null or previously returned by `frontend_new`, and not yet passed
85 /// to `frontend_delete`.
86 #[no_mangle]
frontend_delete(doh: *mut DohFrontend)87 pub unsafe extern "C" fn frontend_delete(doh: *mut DohFrontend) {
88 if !doh.is_null() {
89 // SAFETY: Our caller promised that `doh` was either null or previously returned by
90 // `frontend_new`. We just checked that it's not null, so it must have been returned by
91 // `frontend_new`, which obtained it from `Box::into_raw`.
92 drop(unsafe { Box::from_raw(doh) });
93 }
94 }
95
96 /// Sets server certificate to `DohFrontend`.
97 ///
98 /// # Safety
99 ///
100 /// The given certificate must be a null-terminated UTF-8 encoded string.
101 #[no_mangle]
frontend_set_certificate( doh: &mut DohFrontend, certificate: *const c_char, ) -> bool102 pub unsafe extern "C" fn frontend_set_certificate(
103 doh: &mut DohFrontend,
104 certificate: *const c_char,
105 ) -> bool {
106 if certificate.is_null() {
107 return false;
108 }
109 // SAFETY: Our caller promises that certificate is a valid C string.
110 let certificate = unsafe { CStr::from_ptr(certificate) }.to_str().unwrap();
111 doh.set_certificate(certificate).or_else(logging_and_return_err).is_ok()
112 }
113
114 /// Sets server private key to `DohFrontend`.
115 ///
116 /// # Safety
117 ///
118 /// The given private key must be a null-terminated UTF-8 encoded string.
119 #[no_mangle]
frontend_set_private_key( doh: &mut DohFrontend, private_key: *const c_char, ) -> bool120 pub unsafe extern "C" fn frontend_set_private_key(
121 doh: &mut DohFrontend,
122 private_key: *const c_char,
123 ) -> bool {
124 if private_key.is_null() {
125 return false;
126 }
127 // SAFETY: Our caller promises that private_key is a valid C string.
128 let private_key = unsafe { CStr::from_ptr(private_key) }.to_str().unwrap();
129 doh.set_private_key(private_key).or_else(logging_and_return_err).is_ok()
130 }
131
132 /// Configures the `DohFrontend` not to process DoH queries until a given number of DoH queries
133 /// are received. This function works even in the middle of the worker thread.
134 #[no_mangle]
frontend_set_delay_queries(doh: &mut DohFrontend, count: i32) -> bool135 pub extern "C" fn frontend_set_delay_queries(doh: &mut DohFrontend, count: i32) -> bool {
136 doh.set_delay_queries(count).or_else(logging_and_return_err).is_ok()
137 }
138
139 /// Configures the `DohFrontend` to use the given value for max_idle_timeout transport parameter.
140 #[no_mangle]
frontend_set_max_idle_timeout(doh: &mut DohFrontend, value: u64) -> bool141 pub extern "C" fn frontend_set_max_idle_timeout(doh: &mut DohFrontend, value: u64) -> bool {
142 doh.set_max_idle_timeout(value).or_else(logging_and_return_err).is_ok()
143 }
144
145 /// Configures the `DohFrontend` to use the given value for these transport parameters.
146 /// - initial_max_data
147 /// - initial_max_stream_data_bidi_local
148 /// - initial_max_stream_data_bidi_remote
149 /// - initial_max_stream_data_uni
150 #[no_mangle]
frontend_set_max_buffer_size(doh: &mut DohFrontend, value: u64) -> bool151 pub extern "C" fn frontend_set_max_buffer_size(doh: &mut DohFrontend, value: u64) -> bool {
152 doh.set_max_buffer_size(value).or_else(logging_and_return_err).is_ok()
153 }
154
155 /// Configures the `DohFrontend` to use the given value for initial_max_streams_bidi transport
156 /// parameter.
157 #[no_mangle]
frontend_set_max_streams_bidi(doh: &mut DohFrontend, value: u64) -> bool158 pub extern "C" fn frontend_set_max_streams_bidi(doh: &mut DohFrontend, value: u64) -> bool {
159 doh.set_max_streams_bidi(value).or_else(logging_and_return_err).is_ok()
160 }
161
162 /// Sets the `DohFrontend` to block or unblock sending any data.
163 #[no_mangle]
frontend_block_sending(doh: &mut DohFrontend, block: bool) -> bool164 pub extern "C" fn frontend_block_sending(doh: &mut DohFrontend, block: bool) -> bool {
165 doh.block_sending(block).or_else(logging_and_return_err).is_ok()
166 }
167
168 /// If this function is called, the `DohFrontend` will send RESET_STREAM frame as a response
169 /// instead of a DoH answer on the stream |stream_id|. This will make the client fail to receive
170 /// this DoH answer.
171 #[no_mangle]
frontend_set_reset_stream_id(doh: &mut DohFrontend, stream_id: u64) -> bool172 pub extern "C" fn frontend_set_reset_stream_id(doh: &mut DohFrontend, stream_id: u64) -> bool {
173 doh.set_reset_stream_id(stream_id).or_else(logging_and_return_err).is_ok()
174 }
175
176 /// Gets the statistics of the `DohFrontend` and writes the result to |out|.
177 #[no_mangle]
frontend_stats(doh: &mut DohFrontend, out: &mut Stats) -> bool178 pub extern "C" fn frontend_stats(doh: &mut DohFrontend, out: &mut Stats) -> bool {
179 doh.request_stats()
180 .map(|stats| {
181 out.queries_received = stats.queries_received;
182 out.connections_accepted = stats.connections_accepted;
183 out.alive_connections = stats.alive_connections;
184 out.resumed_connections = stats.resumed_connections;
185 out.early_data_connections = stats.early_data_connections;
186 })
187 .or_else(logging_and_return_err)
188 .is_ok()
189 }
190
191 /// Resets `queries_received` field of `Stats` owned by the `DohFrontend`.
192 #[no_mangle]
frontend_stats_clear_queries(doh: &DohFrontend) -> bool193 pub extern "C" fn frontend_stats_clear_queries(doh: &DohFrontend) -> bool {
194 doh.stats_clear_queries().or_else(logging_and_return_err).is_ok()
195 }
196
197 /// Enable Rust debug logging.
198 #[no_mangle]
init_android_logger()199 pub extern "C" fn init_android_logger() {
200 android_logger::init_once(
201 android_logger::Config::default()
202 .with_tag("DohFrontend")
203 .with_max_level(log::LevelFilter::Debug),
204 );
205 }
206
to_socket_addr(addr: &str, port: &str) -> Result<SocketAddr>207 fn to_socket_addr(addr: &str, port: &str) -> Result<SocketAddr> {
208 let socket_addr = SocketAddr::new(IpAddr::from_str(addr)?, port.parse()?);
209 Ok(socket_addr)
210 }
211
logging_and_return_err<T, U: std::fmt::Debug>(e: U) -> Result<T>212 fn logging_and_return_err<T, U: std::fmt::Debug>(e: U) -> Result<T> {
213 warn!("logging_and_return_err: {:?}", e);
214 bail!("{:?}", e)
215 }
216