nslookup/
nslookup.rs

1//! Synchronous implementation of `nslookup`, using `dns-protocol`.
2//!
3//! Only works on Unix.
4
5use std::env;
6use std::fs;
7use std::io::{prelude::*, BufReader};
8use std::net::Ipv4Addr;
9use std::net::{IpAddr, UdpSocket};
10use std::process;
11
12use dns_protocol::Flags;
13use dns_protocol::{Message, Question, ResourceRecord, ResourceType};
14
15fn main() -> Result<(), Box<dyn std::error::Error>> {
16    // The first argument is the name to lookup.
17    let mut args = env::args();
18    let program_name = args.next().unwrap();
19    let name = match args.next() {
20        Some(name) => name,
21        None => {
22            eprintln!("Usage: {} <name>", &program_name);
23            process::exit(1);
24        }
25    };
26
27    // Search in resolv.conf for the nameserver.
28    //
29    // A production-grade implementation would consider multiple nameservers in
30    // resolv.conf, and then poll them all at once.
31    let resolv = BufReader::new(fs::File::open("/etc/resolv.conf")?);
32    let mut nameserver = None;
33
34    for line in resolv.lines() {
35        let line = line?;
36        if line.starts_with("nameserver") {
37            let result = line.split_whitespace().nth(1).unwrap();
38            if let Ok(ns) = result.parse::<IpAddr>() {
39                nameserver = Some(ns);
40                break;
41            }
42        }
43    }
44
45    let nameserver = match nameserver {
46        Some(ns) => ns,
47        None => {
48            eprintln!("No nameserver found in /etc/resolv.conf");
49            process::exit(1);
50        }
51    };
52
53    println!("Nameserver: {}", nameserver);
54
55    // Create the message we need to send.
56    let mut questions = [Question::new(name.as_str(), ResourceType::A, 1)];
57    let message = Message::new(
58        0xFEE7,
59        Flags::standard_query(),
60        &mut questions,
61        &mut [],
62        &mut [],
63        &mut [],
64    );
65
66    // Allocate the buffer that we need to send.
67    let mut buffer = vec![0; message.space_needed()];
68    message.write(&mut buffer)?;
69
70    // Send the packet to our nameserver over UDP.
71    let socket = UdpSocket::bind((Ipv4Addr::from([127, 0, 0, 1]), 0))?;
72    socket.send_to(&buffer, (nameserver, 53))?;
73
74    // Wait for a response.
75    //
76    // A production-grade implementation would respect timeout/attempts settings in
77    // resolv.conf.
78    let mut buffer = vec![0; 1024];
79    let len = socket.recv(&mut buffer)?;
80
81    // Parse the response.
82    let mut answers = [ResourceRecord::default(); 16];
83    let mut authority = [ResourceRecord::default(); 16];
84    let mut additional = [ResourceRecord::default(); 16];
85    let message = Message::read(
86        &buffer[..len],
87        &mut questions,
88        &mut answers,
89        &mut authority,
90        &mut additional,
91    )?;
92
93    println!(";; Got answer: {:?}", message.flags().response_code());
94
95    // Print the answers.
96    for answer in message.answers() {
97        // Determine the IP address.
98        match answer.data().len() {
99            4 => {
100                let mut ip = [0u8; 4];
101                ip.copy_from_slice(answer.data());
102                let ip = Ipv4Addr::from(ip);
103                println!("{} has address {}", answer.name(), ip);
104            }
105            16 => {
106                let mut ip = [0u8; 16];
107                ip.copy_from_slice(answer.data());
108                let ip = IpAddr::from(ip);
109                println!("{} has address {}", answer.name(), ip);
110            }
111            _ => {
112                println!("{} has unknown address type", answer.name());
113            }
114        }
115    }
116
117    Ok(())
118}