Skip to content

Commit 532517c

Browse files
committed
Added config validation
1 parent 9b55904 commit 532517c

File tree

3 files changed

+68
-16
lines changed

3 files changed

+68
-16
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,13 @@ tls_dns_name = "1dot1dot1dot1.cloudflare-dns.com"
136136
trust_nx_responses = false
137137
```
138138

139+
## Validation
140+
The config can be validated by running the following command.
141+
142+
`cargo run -- --validate`
143+
144+
This only validates the config, block- and allowlists, and does not start the DNS server. If the validation fails, the program exits with the error code `1`.
145+
139146
## DNSSEC Issues
140147
Due to an upstream issue of [hickory-dns](https://github.com/hickory-dns/hickory-dns/issues/2429), non DNSSEC sites will not be resolved if `validate = true`.
141148
Only DNSSEC capable sites will be resolved with this setting.

src/blocklist.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ impl BlockList {
8888

8989
// block list
9090
for url in adlist {
91-
let (raw_list, mut list_errors) = get_file(url, restore_from_cache).await;
91+
let (raw_list, mut list_errors) = get_file(url, restore_from_cache, true).await;
9292
match raw_list {
9393
None => {
9494
error!("skipp list {url}");
@@ -141,7 +141,7 @@ impl BlockList {
141141
// allow list
142142
for url in allow_list {
143143
info!("load allow list");
144-
let (raw_list, mut list_errors) = get_file(url, restore_from_cache).await;
144+
let (raw_list, mut list_errors) = get_file(url, restore_from_cache, true).await;
145145
match raw_list {
146146
None => error!("skipp list {url}"),
147147
Some(raw_list) => {

src/main.rs

Lines changed: 59 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,10 @@ use reqwest::Client;
2323
use rustls::{Certificate, PrivateKey};
2424
use serde::Deserialize;
2525
use std::{
26-
env::var,
27-
fs::{self, File},
28-
io::BufReader,
29-
iter,
30-
path::{Path, PathBuf},
31-
sync::{
26+
env::var, fs::{self, File}, io::BufReader, iter, path::{Path, PathBuf}, sync::{
3227
atomic::{AtomicU64, Ordering},
3328
Arc
34-
},
35-
time::Duration
29+
}, time::Duration
3630
};
3731
use time::OffsetDateTime;
3832
use tokio::{
@@ -244,10 +238,12 @@ fn load_cert_and_key(
244238
Ok((certificates, key))
245239
}
246240

247-
/// load a text file from url and cache it.
248-
/// If restore_from_cache is true only the cache is used.
249-
/// Return None if an Err has occure.
250-
async fn get_file(url: &Url, restore_from_cache: bool) -> (Option<String>, String) {
241+
/// Load a text file from url and cache it.
242+
/// If restore_from_cache is true, only the cache is used.
243+
/// The first return value is the file content.
244+
/// It will be None if an error has occured.
245+
/// The second value is a combined error message.
246+
async fn get_file(url: &Url, restore_from_cache: bool, cache_file: bool) -> (Option<String>, String) {
251247
if url.scheme() == "file" {
252248
let path = url.path();
253249
info!("load file {path:?}");
@@ -281,12 +277,13 @@ async fn get_file(url: &Url, restore_from_cache: bool) -> (Option<String>, Strin
281277
.error_for_status()?
282278
.text()
283279
.await?;
280+
if cache_file {
284281
if let Err(err) = write(&path, &resp)
285282
.await
286283
.with_context(|| format!("failed to save to {path:?}"))
287284
{
288285
error!("{err:?}");
289-
}
286+
}}
290287
Ok(resp)
291288
}
292289
.await;
@@ -493,6 +490,19 @@ fn main() {
493490
Lazy::force(&CONFIG_PATH);
494491
Lazy::force(&LIST_DIR);
495492

493+
let config = load_config();
494+
495+
if std::env::args().any(|x| x == "--validate") {
496+
if !async_validate_config(config) {
497+
error!("Config validation failed!");
498+
std::process::exit(1);
499+
}
500+
} else {
501+
async_main(config);
502+
}
503+
}
504+
505+
fn load_config() -> Config {
496506
info!("load config from {:?}", &*CONFIG_PATH);
497507
let config = fs::read(&*CONFIG_PATH)
498508
.with_context(|| format!("Failed to read {:?}", CONFIG_PATH.as_path()))
@@ -501,7 +511,42 @@ fn main() {
501511
.with_context(|| "Failed to deserialize config")
502512
.unwrap_or_else(|err| panic!("{err:?}"));
503513
debug!("{:#?}", config);
504-
async_main(config);
514+
515+
config
516+
}
517+
518+
#[tokio::main]
519+
async fn async_validate_config(config: Config) -> bool{
520+
let mut validated = true;
521+
//Allow List
522+
for list in config.blocklist.allow_list {
523+
let (file_content, error_message) = get_file(&list, false, false).await;
524+
if let Some(content) = file_content {
525+
if let Err(err) = parser::Blocklist::parse(list.path(), &content) {
526+
error!("{}", err.msg());
527+
validated = false;
528+
}
529+
} else {
530+
error!("{error_message}");
531+
validated = false;
532+
}
533+
}
534+
535+
//Block List
536+
for list in config.blocklist.lists {
537+
let (file_content, error_message) = get_file(&list, false, false).await;
538+
if let Some(content) = file_content {
539+
if let Err(err) = parser::Blocklist::parse(list.path(), &content) {
540+
error!("{}", err.msg());
541+
validated = false;
542+
}
543+
} else {
544+
error!("{error_message}");
545+
validated = false;
546+
}
547+
}
548+
549+
validated
505550
}
506551

507552
#[cfg(test)]

0 commit comments

Comments
 (0)