Add test through DNS

This commit is contained in:
Rémi BERTHO 2021-10-05 23:37:30 +02:00
parent 7659338106
commit 94050d4af4
Signed by: dalan
GPG key ID: EE3B917931C07B64
3 changed files with 122 additions and 74 deletions

View file

@ -1,6 +1,6 @@
[package] [package]
name = "check_ip" name = "check_ip"
version = "1.0.0" version = "2.0.0"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
@ -14,3 +14,4 @@ serde = { version = "1.0", features = ["derive"] }
log = "0.4" log = "0.4"
simplelog = "^0.10.0" simplelog = "^0.10.0"
clokwerk = "0.3" clokwerk = "0.3"
trust-dns-resolver = {version = "0.20", features = ["serde-config"] }

View file

@ -1,4 +1,5 @@
# TODO # TODO
* [ ] Examples de fichiers de config * [ ] Examples de fichiers de config (config + systemd)
* [ ] Test à partir de ladresse IP récupéré par DNS (https://crates.io/crates/trust-dns-resolver) * [ ] Nenvoyer quun seul mail par test =>_test_domains renvoie Option<String>
* [ ] Test de lIPv6 (https://crates.io/crates/query_external_ip) * [ ] Test de lIPv6 (https://crates.io/crates/query_external_ip)
* [ ] Rustfmt

View file

@ -8,6 +8,7 @@ extern crate native_tls;
extern crate serde; extern crate serde;
extern crate simplelog; extern crate simplelog;
extern crate toml; extern crate toml;
extern crate trust_dns_resolver;
use self::native_tls::{Protocol, TlsConnector}; use self::native_tls::{Protocol, TlsConnector};
use anyhow::{bail, Result}; use anyhow::{bail, Result};
@ -20,8 +21,8 @@ use serde::Deserialize;
use simplelog::{ConfigBuilder, LevelFilter, WriteLogger}; use simplelog::{ConfigBuilder, LevelFilter, WriteLogger};
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::net::IpAddr; use std::net::IpAddr;
use std::path::Path;
use std::{thread, time::Duration}; use std::{thread, time::Duration};
use trust_dns_resolver::{config::*, Name, Resolver};
fn main() -> Result<()> { fn main() -> Result<()> {
// Init log // Init log
@ -40,21 +41,32 @@ fn main() -> Result<()> {
) )
.unwrap(); .unwrap();
// Init scheduler // Init tester
let mut scheduler = Scheduler::new(); info!("Init tester");
info!("Init"); let tester = match Tester::new() {
scheduler.every(1.day()).at("06:00").run(|| { Err(e) => {
if let Err(e) = check() {
error!("Error {}", e); error!("Error {}", e);
bail!("Cannot init tester");
} }
}); Ok(tester) => tester,
};
// Initial check // Initial check
if let Err(e) = check() { if let Err(e) = tester.check() {
error!("Error {}", e); error!("Error {}", e);
bail!("Cannot get initial IP"); bail!("Cannot get initial IP");
} }
if let Some(false) = tester.config.test {
// Init scheduler
info!("Init scheduler");
let mut scheduler = Scheduler::new();
scheduler.every(1.day()).at("06:00").run(move || {
if let Err(e) = tester.check() {
error!("Error {}", e);
}
});
// Run // Run
loop { loop {
scheduler.run_pending(); scheduler.run_pending();
@ -62,26 +74,38 @@ fn main() -> Result<()> {
} }
} }
fn check() -> Result<()> { Ok(())
info!("Check");
match get() {
Ok((old_ip, new_ip)) => {
if old_ip != new_ip {
let msg = format!("IP changed from {} to {}", old_ip, new_ip);
info!("{}", msg);
send_mail("New IP".to_owned(), msg)?;
} }
struct Tester {
config: Config,
dns_resolver: Resolver,
}
impl Tester {
fn new() -> Result<Tester> {
Ok(Tester {
config: toml::from_str(&fs::read_to_string("config.toml")?)?,
dns_resolver: Resolver::new(ResolverConfig::default(), ResolverOpts::default())?,
})
}
fn check(&self) -> Result<()> {
info!("Check");
match self.get_ip() {
Ok(ipv4) => {
self.test_domains(ipv4)?;
} }
Err(err) => { Err(err) => {
let msg = format!("Cannot get IP ({})", err); let msg = format!("Cannot get IP ({})", err);
error!("{}", msg); error!("{}", msg);
send_mail("Error on getting IP".to_owned(), msg)?; self.send_mail("Error on getting IP".to_owned(), msg)?;
} }
} }
Ok(()) Ok(())
} }
fn get() -> Result<(IpAddr, IpAddr)> { fn get_ip(&self) -> Result<IpAddr> {
// Get new IP // Get new IP
let new_ip = match my_internet_ip::get() { let new_ip = match my_internet_ip::get() {
Ok(ip) => ip, Ok(ip) => ip,
@ -89,54 +113,76 @@ fn get() -> Result<(IpAddr, IpAddr)> {
}; };
info!("Current IP is {}", new_ip); info!("Current IP is {}", new_ip);
// Get old IP Ok(new_ip)
let path = Path::new("./old_ip.txt");
let old_ip = if path.exists() {
fs::read_to_string(path)?.parse()?
} else {
new_ip.clone()
};
info!("Old IP is {}", old_ip);
// Write new IP
fs::write(path, format!("{}", new_ip))?;
Ok((old_ip, new_ip))
} }
fn send_mail(subject: String, body: String) -> Result<()> { fn test_domains(&self, ipv4: IpAddr) -> Result<()> {
for domain in self.config.server.domains.iter() {
let address = self.dns_resolver.lookup_ip(domain.clone())?;
for dns_ip in address.iter() {
if dns_ip.is_ipv4() {
if dns_ip != ipv4 {
let msg = format!(
"Wrong IPV4 for {} (DNS: {}, current: {})",
domain, dns_ip, ipv4
);
info!("{}", msg);
self.send_mail("Wrong IP".to_owned(), msg)?;
}
}
}
}
Ok(())
}
fn send_mail(&self, subject: String, body: String) -> Result<()> {
info!("Send mail {}", subject); info!("Send mail {}", subject);
let config: Config = toml::from_str(&fs::read_to_string("config.toml")?)?; if let Some(false) = self.config.test {
let email = EmailBuilder::new() let email = EmailBuilder::new()
.to(config.mail.from) .to(self.config.mail.from.clone())
.from(config.mail.to) .from(self.config.mail.to.clone())
.subject(subject) .subject(subject)
.text(body) .text(body)
.build()?; .build()?;
// Create transport // Create transport
let creds = Credentials::new(config.mail.username, config.mail.password); let creds = Credentials::new(
self.config.mail.username.clone(),
self.config.mail.password.clone(),
);
let mut tls_builder = TlsConnector::builder(); let mut tls_builder = TlsConnector::builder();
tls_builder.min_protocol_version(Some(Protocol::Sslv3)); tls_builder.min_protocol_version(Some(Protocol::Sslv3));
let tls_parameters = let tls_parameters = ClientTlsParameters::new(
ClientTlsParameters::new(config.mail.server.clone(), tls_builder.build().unwrap()); self.config.mail.server.clone(),
tls_builder.build().unwrap(),
);
let mut mailer = SmtpClient::new( let mut mailer = SmtpClient::new(
(config.mail.server, config.mail.port), (self.config.mail.server.clone(), self.config.mail.port),
ClientSecurity::Required(tls_parameters), ClientSecurity::Required(tls_parameters),
)? )?
.credentials(creds) .credentials(creds)
.transport(); .transport();
mailer.send(email.into())?; mailer.send(email.into())?;
} else {
println!("subject: {} - body: {}", subject, body);
};
Ok(()) Ok(())
} }
}
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
struct Config { struct Config {
mail: MailConfig, mail: MailConfig,
server: ServerConfig,
test: Option<bool>,
}
#[derive(Debug, Deserialize)]
struct ServerConfig {
domains: Vec<Name>,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]