-
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
15e48c7
commit 8a87d35
Showing
12 changed files
with
248 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
// Based on https://github.com/DNSCrypt/doh-server/blob/master/src/libdoh/src/odoh.rs | ||
use crate::{error::*, log::*}; | ||
use bytes::Bytes; | ||
use odoh_rs::{ | ||
parse, ObliviousDoHConfigContents, ObliviousDoHConfigs, ObliviousDoHMessage, ObliviousDoHMessagePlaintext, OdohSecret, | ||
}; | ||
use rand::{rngs::StdRng, SeedableRng}; | ||
|
||
#[derive(Debug, Clone)] | ||
/// ODoH config | ||
pub struct ODoHConfig { | ||
authority: String, | ||
inner: ObliviousDoHConfigContents, | ||
} | ||
|
||
impl ODoHConfig { | ||
/// Create a new ODoHConfig | ||
pub fn new(authority: &str, configs_vec: &[u8]) -> Result<Self> { | ||
let odoh_configs: ObliviousDoHConfigs = parse(&mut (<&[u8]>::clone(&configs_vec)))?; | ||
info!("[ODoH] Update ODoH configs: {authority}"); | ||
let client_config = match odoh_configs.into_iter().next() { | ||
Some(t) => t, | ||
None => return Err(DapError::ODoHNoClientConfig), | ||
}; | ||
let inner: ObliviousDoHConfigContents = client_config.into(); | ||
|
||
Ok(ODoHConfig { | ||
authority: authority.to_owned(), | ||
inner, | ||
}) | ||
} | ||
|
||
/// Encrypt query | ||
pub fn encrypt_query(&self, plaintext_query: &[u8]) -> Result<(ObliviousDoHMessagePlaintext, Bytes, OdohSecret)> { | ||
debug!("[ODoH] Encrypt query"); | ||
let mut rng = StdRng::from_entropy(); | ||
|
||
// TODO: Padding bytes should be add? Padding be handled by a client issuing plaintext queries. | ||
// add a random padding for testing purpose | ||
// let padding_len = rng.gen_range(0..10); | ||
// let query = ObliviousDoHMessagePlaintext::new(&plaintext_query, padding_len); | ||
// debug!("[ODoH] Encrypting DNS message with {} bytes of padding", padding_len); | ||
let query = ObliviousDoHMessagePlaintext::new(plaintext_query, 0); | ||
let (query_enc, cli_secret) = odoh_rs::encrypt_query(&query, &self.inner, &mut rng)?; | ||
let query_body = odoh_rs::compose(&query_enc)?.freeze(); | ||
Ok((query, query_body, cli_secret)) | ||
} | ||
|
||
/// Decrypt response | ||
pub fn decrypt_response( | ||
&self, | ||
plaintext_query: &ObliviousDoHMessagePlaintext, | ||
encrypted_response: &Bytes, | ||
client_secret: OdohSecret, | ||
) -> Result<Bytes> { | ||
debug!("[ODoH] Decrypt query"); | ||
let response_enc: ObliviousDoHMessage = parse(&mut (encrypted_response.clone()))?; | ||
let response_dec = odoh_rs::decrypt_response(plaintext_query, &response_enc, client_secret)?; | ||
debug!("[ODoH] Successfully decrypted"); | ||
|
||
Ok(response_dec.into_msg()) | ||
} | ||
} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
use super::{odoh::ODoHConfig, path_manage::DoHTarget}; | ||
use crate::{ | ||
constants::{ODOH_CONFIG_PATH, ODOH_CONFIG_WATCH_DELAY}, | ||
error::*, | ||
http_client::HttpClientInner, | ||
log::*, | ||
}; | ||
use rustc_hash::FxHashMap as HashMap; | ||
use std::sync::Arc; | ||
use tokio::{ | ||
sync::{Notify, RwLock}, | ||
time::{sleep, Duration}, | ||
}; | ||
use url::Url; | ||
|
||
#[allow(clippy::complexity)] | ||
/// ODoH config store | ||
pub struct ODoHConfigStore { | ||
inner: Arc<RwLock<HashMap<Arc<DoHTarget>, Arc<Option<ODoHConfig>>>>>, | ||
http_client: Arc<RwLock<HttpClientInner>>, | ||
} | ||
|
||
impl ODoHConfigStore { | ||
/// Create a new ODoHConfigStore | ||
pub async fn new(http_client: Arc<RwLock<HttpClientInner>>, targets: &[Arc<DoHTarget>]) -> Result<Self> { | ||
let inner = targets | ||
.iter() | ||
.map(|target| (target.clone(), Arc::new(None as Option<ODoHConfig>))) | ||
.collect::<HashMap<_, _>>(); | ||
let res = Self { | ||
inner: Arc::new(RwLock::new(inner)), | ||
http_client, | ||
}; | ||
res.update_odoh_config_from_well_known().await?; | ||
Ok(res) | ||
} | ||
|
||
/// Fetch ODoHConfig from target | ||
async fn update_odoh_config_from_well_known(&self) -> Result<()> { | ||
// TODO: Add auth token when fetching config? | ||
// fetch public key from odoh target (/.well-known) | ||
let inner_lock = self.inner.read().await; | ||
let inner = inner_lock.clone(); | ||
drop(inner_lock); | ||
|
||
let futures = inner.keys().map(|target| async { | ||
let mut destination = Url::parse(&format!("{}://{}", target.scheme(), target.authority())).unwrap(); | ||
destination.set_path(ODOH_CONFIG_PATH); | ||
let lock = self.http_client.read().await; | ||
debug!("Fetching ODoH config from {}", destination); | ||
lock.get(destination).send().await | ||
}); | ||
let joined = futures::future::join_all(futures); | ||
let update_futures = joined.await.into_iter().zip(inner).map(|(res, current)| async move { | ||
match res { | ||
Ok(response) => { | ||
if response.status() != reqwest::StatusCode::OK { | ||
error!("Failed to fetch ODoH config!: {:?}", response.status()); | ||
return (current.0.clone(), Arc::new(None as Option<ODoHConfig>)); | ||
} | ||
let Ok(body) = response.bytes().await else { | ||
error!("Failed to parse response body in ODoH config response"); | ||
return (current.0.clone(), Arc::new(None as Option<ODoHConfig>)); | ||
}; | ||
let config = ODoHConfig::new(current.0.authority(), &body).ok(); | ||
(current.0.clone(), Arc::new(config)) | ||
} | ||
Err(e) => { | ||
error!("Failed to fetch ODoH config!: {:?}", e); | ||
(current.0.clone(), Arc::new(None as Option<ODoHConfig>)) | ||
} | ||
} | ||
}); | ||
let update_joined = futures::future::join_all(update_futures) | ||
.await | ||
.into_iter() | ||
.collect::<HashMap<_, _>>(); | ||
let mut inner_lock = self.inner.write().await; | ||
*inner_lock = update_joined; | ||
drop(inner_lock); | ||
Ok(()) | ||
} | ||
|
||
/// start odoh config watch service | ||
pub(super) async fn start_service(&self, term_notify: Option<Arc<Notify>>) -> Result<()> { | ||
info!("Start periodic odoh config watch service"); | ||
match term_notify { | ||
Some(term) => { | ||
tokio::select! { | ||
_ = self.watch_service() => { | ||
warn!("ODoH config watch service is down"); | ||
} | ||
_ = term.notified() => { | ||
info!("ODoH config watch service receives term signal"); | ||
} | ||
} | ||
} | ||
None => { | ||
self.watch_service().await?; | ||
warn!("ODoH config watch service is down."); | ||
} | ||
} | ||
Ok(()) | ||
} | ||
|
||
/// watch service | ||
async fn watch_service(&self) -> Result<()> { | ||
loop { | ||
self.update_odoh_config_from_well_known().await?; | ||
sleep(Duration::from_secs(ODOH_CONFIG_WATCH_DELAY as u64)).await; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters