Skip to content

Commit

Permalink
hostsgen: Provider & misc
Browse files Browse the repository at this point in the history
  • Loading branch information
stackinspector committed Oct 23, 2024
1 parent 648e05f commit e791528
Showing 1 changed file with 65 additions and 9 deletions.
74 changes: 65 additions & 9 deletions hostsgen.rs
Original file line number Diff line number Diff line change
@@ -1,42 +1,98 @@
use std::{io::{self, BufRead}, fs};
use std::{fs, io::{self, BufRead}, net::IpAddr, str::FromStr};
use serde::Deserialize;

#[derive(Deserialize)]
#[serde(rename_all = "PascalCase")]
// TODO not in rfc8427?
struct DnsRes {
status: u32,
// DNS rcode (?), 4bits
status: u8,
answer: Vec<DnsAnswer>,
}

#[derive(Deserialize)]
struct DnsAnswer {
#[serde(rename = "type")]
ty: u32,
// data: std::net::IpAddr,
ty: u16,
// data: IpAddr,
data: String,
}

macro_rules! enum_with_parse {
($vis:vis enum $name:ident { $($variant_str:literal -> $variant_name:ident)* } raises $error_ty:ty) => {
$vis enum $name { $($variant_name,)* }
impl FromStr for $name {
type Err = $error_ty;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
// TODO $(stringify!($variant_name) => Self::$variant_name,)* // needs `stringify!` case convert
$($variant_str => Self::$variant_name,)*
_ => return Err(()),
})
}
}
};
}

enum_with_parse! {
enum Provider {
"cf1" -> Cf1
"cf2" -> Cf2
"dnspod" -> Dnspod
"ali1" -> Ali1
"ali2" -> Ali2
} raises ()
}

impl Provider {
fn ipv4(&self) -> &'static str {
match self {
Provider::Cf1 => "1.1.1.1",
Provider::Cf2 => "1.0.0.1",
Provider::Dnspod => "1.12.12.12",
Provider::Ali1 => "223.5.5.5",
Provider::Ali2 => "223.6.6.6",
}
}

fn word(&self) -> &'static str {
match self {
Provider::Cf1 |
Provider::Cf2 |
Provider::Dnspod => "dns-query",
Provider::Ali1 |
Provider::Ali2 => "resolve",
}
}

// TODO -> &'static str
fn build_url_prefix(&self) -> String {
format!("https://{}/{}?name=", self.ipv4(), self.word())
}
}

fn main() {
// cargo run --release --bin hostsgen -- 1.0.0.1 hosts-list hosts
// cargo run --release --bin hostsgen -- cf2 hosts-list hosts
let mut args = std::env::args_os();
let _ = args.next();
let provider = args.next().unwrap().into_string().unwrap();
let provider: Provider = args.next().unwrap().into_string().unwrap().parse().unwrap();
let mut list = io::BufReader::new(fs::File::open(args.next().unwrap()).unwrap());
let dst = args.next();
let mut dst_h: Box<dyn io::Write> = if let Some(dst_path) = dst {
Box::new(fs::OpenOptions::new().create_new(true).write(true).open(&dst_path).unwrap())
} else {
Box::new(io::stdout().lock())
};
let url_prefix = format!("https://{provider}/dns-query?name=");
let url_prefix = provider.build_url_prefix();
let mut url_buf = String::with_capacity(url_prefix.len());
loop {
url_buf.clear();
url_buf.push_str(&url_prefix);
let read = list.read_line(&mut url_buf).unwrap();
if read == 0 { break; }
// contains \n or \r\n depends on list file
// won't affect http request and end up the same in dst
// won't affect http request (handled by url crate used by ureq) and end up the same in dst
let domain = &url_buf[url_prefix.len()..];
let dns_res = ureq::get(&url_buf)
.set("accept", "application/dns-json")
Expand All @@ -47,7 +103,7 @@ fn main() {
assert_eq!(dns_res.status, 0);
for DnsAnswer { ty, data } in dns_res.answer {
if ty == 1 {
let ip: std::net::IpAddr = data.parse().unwrap();
let ip: IpAddr = data.parse().unwrap();
dst_h.write_fmt(format_args!("{ip} {domain}")).unwrap();
}
}
Expand Down

0 comments on commit e791528

Please sign in to comment.