1use capnp::private::capability::{ClientHook, ParamsHook, ResultsHook};
10use capnp::traits::ImbueMut;
11
12use crate::error::{Result, RpcError};
13use crate::io::{AsyncStream, read_message, write_raw};
14use crate::rpc_capnp;
15use crate::tunnelrpc_capnp;
16
17pub const CLOUDFLARED_SERVER_INTERFACE_ID: u64 = 0xf548_cef9_dea2_a4a1;
19pub const SESSION_MANAGER_INTERFACE_ID: u64 = 0x8394_45a5_9fb0_1686;
22pub const CONFIGURATION_MANAGER_INTERFACE_ID: u64 = 0xb48e_dfbd_aa25_db04;
25
26#[derive(Debug, Clone, Default)]
28pub struct UpdateConfigurationResponse {
29 pub latest_applied_version: i32,
31 pub error: String,
33}
34
35#[derive(Debug, Clone, Default)]
37pub struct RegisterUdpSessionResponse {
38 pub error: String,
40 pub spans: Vec<u8>,
42}
43
44pub trait CloudflaredHandler: Send + Sync {
46 fn update_configuration(
48 &self,
49 version: i32,
50 configuration: &[u8],
51 ) -> UpdateConfigurationResponse;
52 fn register_udp_session(
55 &self,
56 _session_identifier: &[u8; 16],
57 _destination_ip: &[u8],
58 _destination_port: u16,
59 ) -> RegisterUdpSessionResponse {
60 RegisterUdpSessionResponse {
61 error: "UDP sessions are not supported".into(),
62 spans: Vec::new(),
63 }
64 }
65 fn unregister_udp_session(&self, _session_identifier: &[u8; 16], _message: &str) {}
67}
68
69pub async fn serve_cloudflared<S, H>(stream: &mut S, handler: &H) -> Result<()>
77where
78 S: AsyncStream + Unpin,
79 H: CloudflaredHandler,
80{
81 loop {
82 let reply: Option<Vec<u8>> = {
84 let reader = match read_message(stream).await {
85 Ok(reader) => reader,
86 Err(RpcError::Eof) => return Ok(()),
87 Err(e) => return Err(e),
88 };
89 let root = reader.get_root::<rpc_capnp::message::Reader>()?;
90 match root.reborrow().which()? {
91 rpc_capnp::message::Bootstrap(b) => {
92 let question = b?.get_question_id();
93 tracing::debug!(question, "edge bootstrapped the RPC stream");
94 Some(build_bootstrap_return(question)?)
95 }
96 rpc_capnp::message::Call(c) => {
97 let call = c?;
98 let question = call.get_question_id();
99 tracing::debug!(
100 question,
101 interface_id = call.get_interface_id(),
102 method_id = call.get_method_id(),
103 "edge called an RPC method"
104 );
105 let reply = match classify(call.get_interface_id(), call.get_method_id()) {
106 Method::UpdateConfiguration => {
107 let parameters = call.reborrow().get_params()?.get_content().get_as::<
108 tunnelrpc_capnp::configuration_manager::update_configuration_params::Reader<'_>,
109 >()?;
110 let response = handler.update_configuration(
111 parameters.get_version(),
112 parameters.get_config()?,
113 );
114 build_update_configuration_return(question, &response)?
115 }
116 Method::RegisterUdpSession => {
117 let parameters = call.reborrow().get_params()?.get_content().get_as::<
118 tunnelrpc_capnp::session_manager::register_udp_session_params::Reader<'_>,
119 >()?;
120 let response = handler.register_udp_session(
121 &session_identifier_bytes(parameters.get_session_id()?),
122 parameters.get_dst_ip()?,
123 parameters.get_dst_port(),
124 );
125 build_register_udp_session_return(question, &response)?
126 }
127 Method::UnregisterUdpSession => {
128 build_unregister_udp_session_return(question)?
129 }
130 Method::Unknown => {
131 tracing::debug!(
132 interface_id = call.get_interface_id(),
133 method_id = call.get_method_id(),
134 "edge called an unknown RPC method"
135 );
136 crate::rpc::build_exception(question, "unimplemented")?
137 }
138 };
139 Some(reply)
140 }
141 rpc_capnp::message::Finish(_) | rpc_capnp::message::Release(_) => None,
142 _ => None,
143 }
144 };
145 if let Some(reply) = reply {
146 write_raw(stream, &reply).await?;
147 }
148 }
149}
150
151#[derive(Debug, Clone, Copy, PartialEq, Eq)]
158enum Method {
159 UpdateConfiguration,
160 RegisterUdpSession,
161 UnregisterUdpSession,
162 Unknown,
163}
164
165fn classify(interface_identifier: u64, method_identifier: u16) -> Method {
166 match (interface_identifier, method_identifier) {
167 (CONFIGURATION_MANAGER_INTERFACE_ID, 0) | (CLOUDFLARED_SERVER_INTERFACE_ID, 2) => {
168 Method::UpdateConfiguration
169 }
170 (SESSION_MANAGER_INTERFACE_ID, 0) | (CLOUDFLARED_SERVER_INTERFACE_ID, 0) => {
171 Method::RegisterUdpSession
172 }
173 (SESSION_MANAGER_INTERFACE_ID, 1) | (CLOUDFLARED_SERVER_INTERFACE_ID, 1) => {
174 Method::UnregisterUdpSession
175 }
176 _ => Method::Unknown,
177 }
178}
179
180fn session_identifier_bytes(data: &[u8]) -> [u8; 16] {
182 let mut identifier = [0u8; 16];
183 let n = data.len().min(16);
184 identifier[..n].copy_from_slice(&data[..n]);
185 identifier
186}
187
188fn build_bootstrap_return(question: u32) -> Result<Vec<u8>> {
192 let mut caps: capnp::private::layout::CapTable = Vec::new();
194 let mut message = capnp::message::Builder::new_default();
195 let root = message.init_root::<rpc_capnp::message::Builder>();
196 let mut ret = root.init_return();
197 ret.set_answer_id(question);
198 let mut results = ret.init_results();
199 let mut payload = results.reborrow();
200 let mut content = payload.reborrow().init_content();
201 content.imbue_mut(&mut caps);
202 content.set_as_capability(Box::new(DummyHook));
203 let mut ctab = payload.reborrow().init_cap_table(1);
204 ctab.reborrow().get(0).set_sender_hosted(0);
205 Ok(crate::io::serialize_message(&message))
206}
207
208fn build_update_configuration_return(
211 question: u32,
212 response: &UpdateConfigurationResponse,
213) -> Result<Vec<u8>> {
214 let mut message = capnp::message::Builder::new_default();
215 let root = message.init_root::<rpc_capnp::message::Builder>();
216 let mut ret = root.init_return();
217 ret.set_answer_id(question);
218 let mut results = ret.init_results();
219 let mut payload = results.reborrow();
220 {
221 let content = payload.reborrow().init_content();
222 let mut results_reader =
223 content.init_as::<tunnelrpc_capnp::configuration_manager::update_configuration_results::Builder>();
224 let mut response_builder = results_reader.reborrow().init_result();
225 response_builder.set_latest_applied_version(response.latest_applied_version);
226 response_builder.set_err(&response.error);
227 }
228 payload.reborrow().init_cap_table(0);
229 Ok(crate::io::serialize_message(&message))
230}
231
232fn build_register_udp_session_return(
234 question: u32,
235 response: &RegisterUdpSessionResponse,
236) -> Result<Vec<u8>> {
237 let mut message = capnp::message::Builder::new_default();
238 let root = message.init_root::<rpc_capnp::message::Builder>();
239 let mut ret = root.init_return();
240 ret.set_answer_id(question);
241 let mut results = ret.init_results();
242 let mut payload = results.reborrow();
243 {
244 let content = payload.reborrow().init_content();
245 let mut results_reader =
246 content
247 .init_as::<tunnelrpc_capnp::session_manager::register_udp_session_results::Builder>(
248 );
249 let mut response_builder = results_reader.reborrow().init_result();
250 response_builder.set_err(&response.error);
251 response_builder.set_spans(&response.spans);
252 }
253 payload.reborrow().init_cap_table(0);
254 Ok(crate::io::serialize_message(&message))
255}
256
257fn build_unregister_udp_session_return(question: u32) -> Result<Vec<u8>> {
259 let mut message = capnp::message::Builder::new_default();
260 let root = message.init_root::<rpc_capnp::message::Builder>();
261 let mut ret = root.init_return();
262 ret.set_answer_id(question);
263 let mut results = ret.init_results();
264 let mut payload = results.reborrow();
265 payload
266 .reborrow()
267 .init_content()
268 .init_as::<tunnelrpc_capnp::session_manager::unregister_udp_session_results::Builder>();
269 payload.reborrow().init_cap_table(0);
270 Ok(crate::io::serialize_message(&message))
271}
272
273struct DummyHook;
279
280impl ClientHook for DummyHook {
281 fn add_ref(&self) -> Box<dyn ClientHook> {
282 Box::new(DummyHook)
283 }
284 fn new_call(
285 &self,
286 _interface_identifier: u64,
287 _method_identifier: u16,
288 _size_hint: Option<capnp::MessageSize>,
289 ) -> capnp::capability::Request<capnp::any_pointer::Owned, capnp::any_pointer::Owned> {
290 unreachable!("dummy hook is never called")
291 }
292 fn call(
293 &self,
294 _interface_identifier: u64,
295 _method_identifier: u16,
296 _params: Box<dyn ParamsHook>,
297 _results: Box<dyn ResultsHook>,
298 ) -> capnp::capability::Promise<(), capnp::Error> {
299 unreachable!("dummy hook is never called")
300 }
301 fn get_brand(&self) -> usize {
302 0
303 }
304 fn get_ptr(&self) -> usize {
305 0
306 }
307 fn get_resolved(&self) -> Option<Box<dyn ClientHook>> {
308 None
309 }
310 fn when_more_resolved(
311 &self,
312 ) -> Option<capnp::capability::Promise<Box<dyn ClientHook>, capnp::Error>> {
313 None
314 }
315 fn when_resolved(&self) -> capnp::capability::Promise<(), capnp::Error> {
316 capnp::capability::Promise::ok(())
317 }
318}