diff --git a/Cargo.toml b/Cargo.toml index ee9b954..ce94546 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "check_ip" -version = "1.0.0" +version = "2.0.0" edition = "2018" [dependencies] @@ -14,3 +14,4 @@ serde = { version = "1.0", features = ["derive"] } log = "0.4" simplelog = "^0.10.0" clokwerk = "0.3" +trust-dns-resolver = {version = "0.20", features = ["serde-config"] } diff --git a/TODO.md b/TODO.md index 686d576..18157ea 100644 --- a/TODO.md +++ b/TODO.md @@ -1,4 +1,5 @@ # TODO - * [ ] Examples de fichiers de config - * [ ] Test à partir de l’adresse IP récupéré par DNS (https://crates.io/crates/trust-dns-resolver) + * [ ] Examples de fichiers de config (config + systemd) + * [ ] N’envoyer qu’un seul mail par test =>_test_domains renvoie Option * [ ] Test de l’IPv6 (https://crates.io/crates/query_external_ip) + * [ ] Rustfmt \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 1b18013..c475369 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ extern crate native_tls; extern crate serde; extern crate simplelog; extern crate toml; +extern crate trust_dns_resolver; use self::native_tls::{Protocol, TlsConnector}; use anyhow::{bail, Result}; @@ -20,8 +21,8 @@ use serde::Deserialize; use simplelog::{ConfigBuilder, LevelFilter, WriteLogger}; use std::fs::{self, OpenOptions}; use std::net::IpAddr; -use std::path::Path; use std::{thread, time::Duration}; +use trust_dns_resolver::{config::*, Name, Resolver}; fn main() -> Result<()> { // Init log @@ -40,103 +41,148 @@ fn main() -> Result<()> { ) .unwrap(); - // Init scheduler - let mut scheduler = Scheduler::new(); - info!("Init"); - scheduler.every(1.day()).at("06:00").run(|| { - if let Err(e) = check() { + // Init tester + info!("Init tester"); + let tester = match Tester::new() { + Err(e) => { error!("Error {}", e); + bail!("Cannot init tester"); } - }); + Ok(tester) => tester, + }; // Initial check - if let Err(e) = check() { + if let Err(e) = tester.check() { error!("Error {}", e); bail!("Cannot get initial IP"); } - // Run - loop { - scheduler.run_pending(); - thread::sleep(Duration::from_secs(60)); + 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 + loop { + scheduler.run_pending(); + thread::sleep(Duration::from_secs(60)); + } } + + Ok(()) } -fn check() -> Result<()> { - 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 { + 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) => { + let msg = format!("Cannot get IP ({})", err); + error!("{}", msg); + self.send_mail("Error on getting IP".to_owned(), msg)?; } } - Err(err) => { - let msg = format!("Cannot get IP ({})", err); - error!("{}", msg); - send_mail("Error on getting IP".to_owned(), msg)?; - } + Ok(()) } - Ok(()) -} -fn get() -> Result<(IpAddr, IpAddr)> { - // Get new IP - let new_ip = match my_internet_ip::get() { - Ok(ip) => ip, - Err(e) => bail!("Could not get IP: {:?}", e), - }; - info!("Current IP is {}", new_ip); + fn get_ip(&self) -> Result { + // Get new IP + let new_ip = match my_internet_ip::get() { + Ok(ip) => ip, + Err(e) => bail!("Could not get IP: {:?}", e), + }; + info!("Current IP is {}", new_ip); - // Get old 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); + Ok(new_ip) + } - // Write new IP - fs::write(path, format!("{}", new_ip))?; + 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(()) + } - Ok((old_ip, new_ip)) -} + fn send_mail(&self, subject: String, body: String) -> Result<()> { + info!("Send mail {}", subject); -fn send_mail(subject: String, body: String) -> Result<()> { - info!("Send mail {}", subject); + if let Some(false) = self.config.test { + let email = EmailBuilder::new() + .to(self.config.mail.from.clone()) + .from(self.config.mail.to.clone()) + .subject(subject) + .text(body) + .build()?; - let config: Config = toml::from_str(&fs::read_to_string("config.toml")?)?; + // Create transport + let creds = Credentials::new( + self.config.mail.username.clone(), + self.config.mail.password.clone(), + ); + let mut tls_builder = TlsConnector::builder(); + tls_builder.min_protocol_version(Some(Protocol::Sslv3)); + let tls_parameters = ClientTlsParameters::new( + self.config.mail.server.clone(), + tls_builder.build().unwrap(), + ); + let mut mailer = SmtpClient::new( + (self.config.mail.server.clone(), self.config.mail.port), + ClientSecurity::Required(tls_parameters), + )? + .credentials(creds) + .transport(); - let email = EmailBuilder::new() - .to(config.mail.from) - .from(config.mail.to) - .subject(subject) - .text(body) - .build()?; + mailer.send(email.into())?; + } else { + println!("subject: {} - body: {}", subject, body); + }; - // Create transport - let creds = Credentials::new(config.mail.username, config.mail.password); - let mut tls_builder = TlsConnector::builder(); - tls_builder.min_protocol_version(Some(Protocol::Sslv3)); - let tls_parameters = - ClientTlsParameters::new(config.mail.server.clone(), tls_builder.build().unwrap()); - let mut mailer = SmtpClient::new( - (config.mail.server, config.mail.port), - ClientSecurity::Required(tls_parameters), - )? - .credentials(creds) - .transport(); - - mailer.send(email.into())?; - - Ok(()) + Ok(()) + } } #[derive(Debug, Deserialize)] struct Config { mail: MailConfig, + server: ServerConfig, + test: Option, +} + +#[derive(Debug, Deserialize)] +struct ServerConfig { + domains: Vec, } #[derive(Debug, Deserialize)]