crtin 0.1.0

Certificate introspection tool
use std::io::{Read, Write};
use std::net::TcpStream;
use std::time::Duration;
use dns_lookup;

pub fn lookup(domain: &str) -> Result<String, String> {
    let tld = domain.split('.').last().unwrap_or("com");
    let whois_server = match tld {
        "com" | "net" => "whois.verisign-grs.com",
        "org" => "whois.pir.org",
        "io" => "whois.nic.io",
        "co" => "whois.nic.co",
        "uk" | "co.uk" | "org.uk" => "whois.nic.uk",
        "de" => "whois.denic.de",
        "ru" => "whois.tcinet.ru",
        "info" => "whois.afilias.net",
        "biz" => "whois.nic.biz",
        _ => return fallback_lookup(domain),
    };

    let response = query_server(whois_server, domain)?;
    Ok(response)
}

fn query_server(server: &str, domain: &str) -> Result<String, String> {
    let resolved = dns_lookup::lookup_host(server)
        .map_err(|e| format!("DNS resolution failed for {}: {}", server, e))?;
    let addr = resolved
        .iter()
        .next()
        .ok_or_else(|| format!("No addresses found for {}", server))?;
    let sock_addr = std::net::SocketAddr::new(*addr, 43);
    let mut stream = TcpStream::connect_timeout(
        &sock_addr,
        Duration::from_secs(10),
    )
    .map_err(|e| format!("Failed to connect to {}: {}", server, e))?;

    stream
        .set_read_timeout(Some(Duration::from_secs(10)))
        .map_err(|e| format!("Failed to set timeout: {}", e))?;
    stream
        .set_write_timeout(Some(Duration::from_secs(10)))
        .map_err(|e| format!("Failed to set timeout: {}", e))?;

    let query = format!("{}\r\n", domain);
    stream
        .write_all(query.as_bytes())
        .map_err(|e| format!("Failed to send query: {}", e))?;

    let mut response = String::new();
    stream
        .read_to_string(&mut response)
        .map_err(|e| format!("Failed to read response: {}", e))?;

    Ok(response)
}

fn fallback_lookup(domain: &str) -> Result<String, String> {
    let iana = query_server("whois.iana.org", domain)?;
    for line in iana.lines() {
        let trimmed = line.trim().to_lowercase();
        if trimmed.starts_with("whois:") {
            let server = trimmed
                .strip_prefix("whois:")
                .unwrap_or("")
                .trim();
            if !server.is_empty() && server != "whois.iana.org" {
                return query_server(server, domain);
            }
        }
    }
    Ok(iana)
}