dns_over_https/
lib.rs

1pub use crate::args::Args;
2pub use crate::error::{BoxError, Error, Result};
3use crate::udp_server::UdpServer;
4#[cfg(target_os = "windows")]
5pub use crate::windows::start_service;
6use futures::stream::StreamExt;
7use std::net::SocketAddr;
8use tokio::net::UdpSocket;
9use tokio_util::sync::CancellationToken;
10
11mod args;
12mod error;
13mod udp_server;
14mod upstream;
15mod windows;
16
17static SHUTTING_DOWN_TOKEN: std::sync::Mutex<Option<CancellationToken>> = std::sync::Mutex::new(None);
18
19pub async fn main_loop(args: &Args) -> Result<()> {
20    let shutdown_token = CancellationToken::new();
21    if let Ok(mut lock) = SHUTTING_DOWN_TOKEN.lock() {
22        if lock.is_some() {
23            return Err("dns-over-https already started".into());
24        }
25        *lock = Some(shutdown_token.clone());
26    }
27
28    let mut tasks = Vec::new();
29
30    for bind in args.bind.clone() {
31        let shutdown_token = shutdown_token.clone();
32        let args = args.clone();
33        let task = tokio::spawn(async move { _main_loop(shutdown_token, bind, &args).await });
34        tasks.push(task);
35    }
36
37    let results = futures::future::join_all(tasks).await;
38
39    for result in results {
40        if let Err(e) = result {
41            log::error!("Error in main loop: {:?}", e);
42        }
43    }
44    Ok(())
45}
46
47/// # Safety
48///
49/// Shutdown the proxy server.
50#[no_mangle]
51pub unsafe extern "C" fn dns_over_https_stop() -> std::ffi::c_int {
52    log::info!("Shutting down...");
53    if let Ok(mut token) = SHUTTING_DOWN_TOKEN.lock() {
54        if let Some(token) = token.take() {
55            token.cancel();
56        }
57    }
58    0
59}
60
61async fn _main_loop(quit: CancellationToken, bind: SocketAddr, args: &Args) -> Result<()> {
62    log::info!("Listening for DNS requests on {}...", bind);
63
64    let socket = UdpSocket::bind(bind).await?;
65
66    let mut server = UdpServer::new(&socket);
67
68    let client = reqwest::Client::new();
69    let upstreams = args.upstreams(&client);
70
71    loop {
72        tokio::select! {
73            _ = quit.cancelled() => {
74                log::info!("Listener on {} is shutting down...", bind);
75                break;
76            }
77            result = server.next() => {
78                match result.ok_or("error during receiving request")? {
79                    Ok(request) => {
80                        for upstream in upstreams.iter() {
81                            match upstream.send(&request).await {
82                                Ok(response) => {
83                                    server.reply(&request, &response).await?;
84                                    break;
85                                }
86                                Err(e) => {
87                                    log::error!("error during sending request: {:?}", e);
88                                    continue;
89                                }
90                            }
91                        }
92                    }
93                    Err(e) => log::trace!("error during DNS request: {:?}", e),
94                }
95            }
96        }
97    }
98    Ok(())
99}