2023-03-26 17:15:41 +02:00
|
|
|
use crate::algorithm::Algorithm;
|
2023-03-26 16:30:53 +02:00
|
|
|
use crate::canonicalization::Canonicalization;
|
2023-03-26 16:07:49 +02:00
|
|
|
use clap::Parser;
|
|
|
|
use std::collections::HashSet;
|
|
|
|
use std::fs::File;
|
|
|
|
use std::io::{BufRead, BufReader};
|
2023-03-26 21:57:01 +02:00
|
|
|
use std::num::NonZeroU64;
|
2023-03-26 16:07:49 +02:00
|
|
|
use std::path::PathBuf;
|
|
|
|
|
|
|
|
#[derive(Parser, Debug)]
|
|
|
|
#[command(author, version, about, long_about = None)]
|
|
|
|
pub struct Config {
|
2023-03-26 17:15:41 +02:00
|
|
|
#[arg(short, long, default_value_t = Algorithm::default())]
|
|
|
|
algorithm: Algorithm,
|
2023-03-26 16:30:53 +02:00
|
|
|
#[arg(short, long, default_value_t = Canonicalization::default())]
|
|
|
|
canonicalization: Canonicalization,
|
2023-03-26 16:07:49 +02:00
|
|
|
#[arg(short, long)]
|
|
|
|
domain: Vec<String>,
|
|
|
|
#[arg(short = 'D', long, value_name = "FILE")]
|
|
|
|
domain_file: Option<PathBuf>,
|
2023-03-26 22:00:02 +02:00
|
|
|
#[arg(short = 'f', long, value_name = "FILE")]
|
|
|
|
revocation_list: Option<PathBuf>,
|
2023-03-26 17:52:15 +02:00
|
|
|
#[arg(short, long)]
|
|
|
|
header: Vec<String>,
|
|
|
|
#[arg(short = 'o', long)]
|
|
|
|
header_optional: Vec<String>,
|
2023-03-26 21:57:01 +02:00
|
|
|
#[arg(short = 'p', long, default_value_t = NonZeroU64::new(15552000).unwrap())]
|
|
|
|
cryptoperiod: NonZeroU64,
|
2023-03-26 22:04:15 +02:00
|
|
|
#[arg(short, long, default_value_t = 1728000)]
|
|
|
|
revocation: u64,
|
2023-03-26 22:09:19 +02:00
|
|
|
#[arg(short = 'u', long)]
|
|
|
|
dns_update_cmd: String,
|
2023-03-26 21:49:34 +02:00
|
|
|
#[arg(short, long, action = clap::ArgAction::Count)]
|
|
|
|
verbose: u8,
|
2023-03-26 22:12:50 +02:00
|
|
|
#[arg(short = 'x', long, default_value_t = 1296000)]
|
|
|
|
expiration: u64,
|
2023-03-26 16:07:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Config {
|
|
|
|
pub fn init() -> Result<Self, String> {
|
|
|
|
let mut cnf = Self::parse();
|
2023-03-26 18:02:20 +02:00
|
|
|
cnf.domain = process_domains(&cnf.domain, &cnf.domain_file)?;
|
2023-03-26 17:52:15 +02:00
|
|
|
cnf.header = process_headers(&cnf.header, crate::DEFAULT_HEADERS);
|
|
|
|
cnf.header_optional = process_headers(&cnf.header_optional, crate::DEFAULT_HEADERS_OPT);
|
2023-03-26 16:07:49 +02:00
|
|
|
Ok(cnf)
|
|
|
|
}
|
|
|
|
|
2023-03-26 17:15:41 +02:00
|
|
|
pub fn algorithm(&self) -> Algorithm {
|
|
|
|
self.algorithm
|
|
|
|
}
|
|
|
|
|
2023-03-26 16:30:53 +02:00
|
|
|
pub fn canonicalization(&self) -> Canonicalization {
|
|
|
|
self.canonicalization
|
|
|
|
}
|
|
|
|
|
2023-03-26 16:07:49 +02:00
|
|
|
pub fn domains(&self) -> &[String] {
|
|
|
|
&self.domain
|
|
|
|
}
|
2023-03-26 17:21:20 +02:00
|
|
|
|
2023-03-26 17:52:15 +02:00
|
|
|
pub fn headers(&self) -> &[String] {
|
|
|
|
&self.header
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn headers_optional(&self) -> &[String] {
|
|
|
|
&self.header_optional
|
|
|
|
}
|
2023-03-26 21:49:34 +02:00
|
|
|
|
2023-03-26 22:12:50 +02:00
|
|
|
pub fn cryptoperiod(&self) -> NonZeroU64 {
|
|
|
|
self.cryptoperiod
|
|
|
|
}
|
|
|
|
|
2023-03-26 22:04:15 +02:00
|
|
|
pub fn revocation(&self) -> u64 {
|
|
|
|
self.revocation
|
|
|
|
}
|
|
|
|
|
2023-03-26 22:12:50 +02:00
|
|
|
pub fn dns_update_cmd(&self) -> &str {
|
|
|
|
&self.dns_update_cmd
|
|
|
|
}
|
|
|
|
|
2023-03-26 21:49:34 +02:00
|
|
|
pub fn verbosity(&self) -> log::LevelFilter {
|
|
|
|
crate::logs::log_level(self.verbose)
|
|
|
|
}
|
2023-03-26 22:12:50 +02:00
|
|
|
|
|
|
|
pub fn expiration(&self) -> Option<u64> {
|
|
|
|
if self.expiration != 0 {
|
|
|
|
Some(self.expiration)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
2023-03-26 17:52:15 +02:00
|
|
|
}
|
|
|
|
|
2023-03-26 18:02:20 +02:00
|
|
|
fn process_domains(lst: &[String], domain_file: &Option<PathBuf>) -> Result<Vec<String>, String> {
|
|
|
|
let mut domain_set: HashSet<String> = lst.iter().map(|e| e.to_string()).collect();
|
|
|
|
if let Some(path) = domain_file {
|
|
|
|
let f = File::open(path).map_err(|e| format!("{}: {e}", path.display()))?;
|
|
|
|
for line in BufReader::new(f).lines() {
|
|
|
|
let line = line.map_err(|e| format!("{}: {e}", path.display()))?;
|
|
|
|
let domain = line.trim();
|
|
|
|
if !domain.is_empty() && !domain.starts_with('#') {
|
|
|
|
domain_set.insert(domain.to_string().to_lowercase());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(domain_set.into_iter().collect::<Vec<_>>())
|
|
|
|
}
|
|
|
|
|
2023-03-26 17:52:15 +02:00
|
|
|
fn process_headers(lst: &[String], default: &str) -> Vec<String> {
|
|
|
|
let ret = if lst.is_empty() {
|
|
|
|
let default_lst = vec![default.to_string()];
|
|
|
|
do_process_headers(&default_lst)
|
|
|
|
} else {
|
|
|
|
do_process_headers(lst)
|
|
|
|
};
|
|
|
|
ret.into_iter().collect::<Vec<_>>()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn do_process_headers(lst: &[String]) -> HashSet<String> {
|
|
|
|
let mut ret = HashSet::with_capacity(128);
|
|
|
|
for input in lst {
|
|
|
|
for h in input.split(':') {
|
|
|
|
ret.insert(h.to_string().to_lowercase());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ret
|
2023-03-26 16:07:49 +02:00
|
|
|
}
|