1use std::{future::Future, net::SocketAddr, sync::Arc, time::Instant};
2
3use crate::{
4 Action, Admission, Context, Error, Handler, ListenerKey, Metadata, Outcome, Protocol, Router,
5 admit, finish_outcome,
6 metrics::{BlockMetrics, Registry},
7 response_limit,
8};
9use pharos_proto::Rcode;
10
11pub trait Chain: Send + Sync + 'static {
13 fn serve(&self, context: &mut Context<'_>) -> impl Future<Output = Outcome> + Send;
14 fn is_empty(&self) -> bool;
15 fn parallel_query(
16 &self,
17 _query: &pharos_proto::Message<'_>,
18 ) -> Option<tokio::sync::watch::Receiver<bool>> {
19 None
20 }
21 fn visit_enabled(&self, _visit: &mut dyn FnMut(&'static str)) {}
22 fn metrics_enabled(&self) -> bool {
23 true
24 }
25 fn stacktrace(&self) -> bool {
27 false
28 }
29}
30
31impl<C: Chain> Chain for Arc<C> {
32 fn serve(&self, context: &mut Context<'_>) -> impl Future<Output = Outcome> + Send {
33 self.as_ref().serve(context)
34 }
35 fn is_empty(&self) -> bool {
36 self.as_ref().is_empty()
37 }
38 fn parallel_query(
39 &self,
40 query: &pharos_proto::Message<'_>,
41 ) -> Option<tokio::sync::watch::Receiver<bool>> {
42 self.as_ref().parallel_query(query)
43 }
44 fn visit_enabled(&self, visit: &mut dyn FnMut(&'static str)) {
45 self.as_ref().visit_enabled(visit);
46 }
47 fn metrics_enabled(&self) -> bool {
48 self.as_ref().metrics_enabled()
49 }
50 fn stacktrace(&self) -> bool {
51 self.as_ref().stacktrace()
52 }
53}
54
55pub struct Server<C> {
57 pub router: Router<C>,
58 pub key: ListenerKey,
59 pub allow_non_in: bool,
60 pub debug: bool,
61 pub udp_workers: usize,
62 pub logger: fn(Error),
63 metrics: Registry,
64 dropped: Option<BlockMetrics>,
65}
66
67impl<C: Chain> Server<C> {
68 pub fn new(router: Router<C>, key: ListenerKey) -> Self {
69 let metrics = Registry::default();
70 Self::with_registry(router, key, metrics)
71 }
72
73 pub fn with_registry(mut router: Router<C>, key: ListenerKey, metrics: Registry) -> Self {
74 let scheme = match key.transport {
75 crate::Transport::Dns => "dns",
76 crate::Transport::Tls => "tls",
77 crate::Transport::Quic => "quic",
78 crate::Transport::Grpc => "grpc",
79 crate::Transport::Https => "https",
80 crate::Transport::Https3 => "https3",
81 };
82 let address = if key.address.ip().is_unspecified() {
83 format!(":{}", key.address.port())
84 } else {
85 key.address.to_string()
86 };
87 let server = format!("{scheme}://{address}");
88 router.prepare_metrics(&key, &metrics, &server);
89 let dropped = router
90 .metrics_enabled(&key)
91 .then(|| metrics.block(&server, "dropped", ""));
92 Self {
93 router,
94 key,
95 allow_non_in: false,
96 debug: false,
97 udp_workers: crate::quota::worker_count(),
98 logger: log_error,
99 metrics,
100 dropped,
101 }
102 }
103
104 pub fn metrics(&self) -> &Registry {
105 &self.metrics
106 }
107}
108
109fn log_error(error: Error) {
110 log_plugin_error("server", error);
111}
112
113fn log_plugin_error(plugin: &str, error: Error) {
114 crate::logging::log("ERROR", plugin, &format!("Plugin error: {error}"));
115}
116
117impl<C: Chain> Handler for Server<C> {
118 fn udp_workers(&self) -> usize {
119 self.udp_workers
120 }
121 fn parallel_query(
122 &self,
123 wire: &[u8],
124 peer: SocketAddr,
125 ) -> Option<tokio::sync::watch::Receiver<bool>> {
126 let Ok(query) = pharos_proto::Message::parse(wire) else {
127 return None;
128 };
129 if query
131 .questions()
132 .next()
133 .is_none_or(|question| question.qtype != pharos_proto::Type::HINFO)
134 {
135 return None;
136 }
137 self.router
138 .route(&self.key, query, peer, Protocol::Udp)
139 .and_then(|block| block.chain())
140 .and_then(|chain| chain.parallel_query(&query))
141 }
142 async fn handle(
143 &self,
144 wire: &[u8],
145 peer: SocketAddr,
146 protocol: Protocol,
147 response: &mut Vec<u8>,
148 ) -> Action {
149 self.handle_with_depth(wire, peer, protocol, response, 0, self)
150 .await
151 }
152
153 async fn handle_shared(
154 self: &Arc<Self>,
155 wire: &[u8],
156 peer: SocketAddr,
157 protocol: Protocol,
158 response: &mut Vec<u8>,
159 ) -> Action {
160 self.handle_with_depth(wire, peer, protocol, response, 0, self)
161 .await
162 }
163}
164
165impl<C: Chain> crate::SelfLookupServer for Server<C> {
166 fn lookup<'a>(
167 &'a self,
168 wire: &'a [u8],
169 peer: SocketAddr,
170 protocol: Protocol,
171 depth: u8,
172 response: &'a mut Vec<u8>,
173 ) -> std::pin::Pin<Box<dyn Future<Output = Action> + Send + 'a>> {
174 Box::pin(self.handle_with_depth(wire, peer, protocol, response, depth, self))
176 }
177}
178
179impl<C: Chain> crate::SelfLookupServer for Arc<Server<C>> {
180 fn lookup<'a>(
181 &'a self,
182 wire: &'a [u8],
183 peer: SocketAddr,
184 protocol: Protocol,
185 depth: u8,
186 response: &'a mut Vec<u8>,
187 ) -> std::pin::Pin<Box<dyn Future<Output = Action> + Send + 'a>> {
188 Box::pin(self.handle_with_depth(wire, peer, protocol, response, depth, self))
189 }
190
191 fn owned(&self) -> Option<Arc<dyn crate::OwnedSelfLookupServer>> {
192 Some(Arc::clone(self) as Arc<dyn crate::OwnedSelfLookupServer>)
193 }
194}
195
196impl<C: Chain> crate::OwnedSelfLookupServer for Server<C> {
197 fn lookup<'a>(
198 self: Arc<Self>,
199 wire: &'a [u8],
200 peer: SocketAddr,
201 protocol: Protocol,
202 depth: u8,
203 response: &'a mut Vec<u8>,
204 ) -> std::pin::Pin<Box<dyn Future<Output = Action> + Send + 'a>> {
205 Box::pin(async move {
206 self.handle_with_depth(wire, peer, protocol, response, depth, &self)
207 .await
208 })
209 }
210}
211
212impl<C: Chain> Server<C> {
213 async fn handle_with_depth(
214 &self,
215 wire: &[u8],
216 peer: SocketAddr,
217 protocol: Protocol,
218 response: &mut Vec<u8>,
219 depth: u8,
220 lookup_server: &dyn crate::SelfLookupServer,
221 ) -> Action {
222 let start = Instant::now();
223 let mut query = match admit(wire, self.allow_non_in, response) {
224 Admission::Query(query) => query,
225 Admission::Respond => {
226 if let Ok(query) = pharos_proto::Message::parse(wire) {
227 let rcode = pharos_proto::Message::parse(response)
228 .map_or(0, |message| message.rcode().0);
229 if rcode != Rcode::BADVERS.0
230 && let Some(dropped) = &self.dropped
231 {
232 dropped.record(
233 query.questions().next().map_or(0, |q| q.qtype.0),
234 protocol == Protocol::Tcp,
235 peer_is_ipv6(peer),
236 query.opt().is_some_and(|opt| opt.dnssec_ok()),
237 wire.len(),
238 response.len(),
239 rcode,
240 start.elapsed().as_secs_f64(),
241 );
242 }
243 }
244 return Action::Respond;
245 }
246 Admission::Drop => return Action::Drop,
247 };
248 let block = self.router.route(&self.key, query, peer, protocol);
249 let chain_start = Instant::now();
250 let mut producer = None;
251 let mut error_plugin = None;
252 let mut error_logged = false;
253 let outcome = if let Some(block) = block {
254 if let Some(chain) = block.chain() {
255 let mut slots = [None; 18];
256 slots[0] = Some((crate::metrics::WRITER_METADATA, ""));
257 slots[1] = Some((crate::logging::PLUGIN_METADATA, ""));
258 slots[2] = Some((crate::logging::ERROR_LOGGED_METADATA, ""));
259 let mut metadata = Metadata::new(&mut slots);
260 metadata.self_lookup = Some(crate::SelfLookupState {
261 server: lookup_server,
262 depth,
263 });
264 let mut context = Context {
265 query,
266 client: peer,
267 local: self.key.address,
268 transport: self.key.transport,
269 protocol,
270 domain: block.domain(),
271 metadata,
272 response,
273 max_response_size: response_limit(query, protocol),
274 };
275 let outcome = crate::panic::contain_panic_with_stacktrace(
276 chain.serve(&mut context),
277 self.debug,
278 chain.stacktrace(),
279 )
280 .await;
281 if let Some(opt) = context.query.opt() {
282 query = query.with_udp_payload_limit(opt.udp_payload_size());
283 }
284 producer = context
285 .metadata
286 .get(crate::metrics::WRITER_METADATA)
287 .and_then(|name| block.plugin_metrics(name));
288 error_plugin = context
289 .metadata
290 .get(crate::logging::PLUGIN_METADATA)
291 .and_then(|name| block.plugin_name(name));
292 error_logged =
293 context.metadata.get(crate::logging::ERROR_LOGGED_METADATA) == Some("true");
294 outcome
295 } else {
296 Outcome::Unanswered(Rcode::REFUSED)
297 }
298 } else {
299 Outcome::Unanswered(Rcode::REFUSED)
300 };
301 let recorded_size = if outcome == Outcome::Response {
302 response.len()
303 } else {
304 0
305 };
306 let recorded_code = match outcome {
307 Outcome::Response => {
308 pharos_proto::Message::parse(response).map_or(0, |message| message.rcode().0)
309 }
310 Outcome::Unanswered(code) => code.0,
311 Outcome::Error(_) => Rcode::SERVFAIL.0,
312 Outcome::Drop | Outcome::Stream(_) => 0,
313 };
314 let duration = chain_start.elapsed().as_secs_f64();
315 let default_logger = std::ptr::fn_addr_eq(self.logger, log_error as fn(Error));
316 let mut first_error = true;
317 let action = finish_outcome(query, protocol, outcome, response, |error| {
318 let suppress = first_error
319 && (error_logged || default_logger)
320 && matches!(outcome, Outcome::Error(e) if e != Error::PluginPanic);
321 first_error = false;
322 if suppress {
323 return;
324 }
325 if let Some(plugin) = error_plugin.filter(|_| default_logger) {
326 log_plugin_error(plugin, error);
327 } else {
328 (self.logger)(error);
329 }
330 });
331 let code = if (block.is_none() || outcome == Outcome::Error(Error::PluginPanic))
332 && action == Action::Respond
333 {
334 pharos_proto::Message::parse(response).map_or(0, |message| message.rcode().0)
335 } else {
336 recorded_code
337 };
338 let qtype = query.questions().next().map_or(0, |q| q.qtype.0);
340 let dnssec = query.opt().is_some_and(|opt| opt.dnssec_ok());
341 let response_len = if (block.is_none() || outcome == Outcome::Error(Error::PluginPanic))
342 && action == Action::Respond
343 {
344 response.len()
345 } else {
346 recorded_size
347 };
348 if let Some(producer) = producer {
349 producer.record(
350 qtype,
351 protocol == Protocol::Tcp,
352 peer_is_ipv6(peer),
353 dnssec,
354 wire.len(),
355 response_len,
356 code,
357 duration,
358 );
359 } else {
360 let metrics = if outcome == Outcome::Error(Error::PluginPanic) {
361 self.dropped.as_ref()
362 } else {
363 block.map_or(self.dropped.as_ref(), |block| block.metrics())
364 };
365 if let Some(metrics) = metrics {
366 metrics.record(
367 qtype,
368 protocol == Protocol::Tcp,
369 peer_is_ipv6(peer),
370 dnssec,
371 wire.len(),
372 response_len,
373 code,
374 duration,
375 );
376 }
377 }
378 action
379 }
380}
381
382fn peer_is_ipv6(peer: SocketAddr) -> bool {
383 match peer.ip() {
384 std::net::IpAddr::V4(_) => false,
385 std::net::IpAddr::V6(ip) => ip.to_ipv4_mapped().is_none(),
386 }
387}