nonymous 0.7.0

DNS protocol and algorithm library
Documentation
//! Usage:
//!
//! ```sh
//! $ bore --encode -x 10.42.69.0 | cargo run --example dynrdns --features std | bore --decode
//! ```

extern crate eyre;

use std::io::{stdin, stdout, Read, Write};

use nonymous::{
    core::{Class, Opcode, Rcode, Type},
    emit::name::NameBuilder,
    server::response,
};

fn main() -> eyre::Result<()> {
    let mut query = vec![];
    stdin().lock().read_to_end(&mut query)?;

    let response = rdns_response(&query)?;
    stdout().lock().write_all(&response)?;

    Ok(())
}

fn rdns_response(query: &[u8]) -> eyre::Result<Vec<u8>> {
    let mut result = vec![];

    if let Some((query, response)) = response(&query, &mut result, false)? {
        if query.header().opcode() != Opcode::Query || query.header().qdcount() != 1 {
            response.rcode(Rcode::NotImp).finish()?.finish();
            return Ok(result);
        }

        let question = query.qd().next().unwrap();
        if question.qclass() != Class::IN || question.qtype() != Type::PTR {
            response.rcode(Rcode::Refused).finish()?.finish();
            return Ok(result);
        }

        let qname = question.qname();
        let labels = qname
            .labels_not_null()
            .flat_map(|x| x.value())
            .collect::<Vec<_>>();
        let name = match labels[..] {
            [.., b"in-addr", b"arpa"] => {
                if labels.len() != 4 + 2 {
                    response.rcode(Rcode::Refused).finish()?.finish();
                    return Ok(result);
                }
                let octets = labels
                    .iter()
                    .take(4)
                    .rev()
                    .flat_map(|x| std::str::from_utf8(x))
                    .flat_map(|x| x.parse::<u8>())
                    .map(|x| x.to_string())
                    .collect::<Vec<_>>();
                if octets.len() != 4 {
                    response.rcode(Rcode::Refused).finish()?.finish();
                    return Ok(result);
                }
                let result = octets.join("-");

                format!("dyn-{}.isp.example.", result)
            }
            [.., b"ip6", b"arpa"] => {
                if labels.len() != 32 + 2 {
                    response.rcode(Rcode::Refused).finish()?.finish();
                    return Ok(result);
                }
                let nibbles = labels
                    .iter()
                    .take(32)
                    .rev()
                    .flat_map(|x| std::str::from_utf8(x))
                    .flat_map(|x| u16::from_str_radix(x, 16))
                    .filter(|&x| x <= 0xF)
                    .collect::<Vec<_>>();
                if nibbles.len() != 32 {
                    response.rcode(Rcode::Refused).finish()?.finish();
                    return Ok(result);
                }
                let result = nibbles
                    .chunks_exact(4)
                    .map(|x| x.iter().fold(0, |a, x| a << 4 | x))
                    .map(|x| format!("{:x}", x))
                    .collect::<Vec<_>>()
                    .join("-");

                format!("dyn-{}.isp.example.", result)
            }
            _ => {
                response.rcode(Rcode::Refused).finish()?.finish();
                return Ok(result);
            }
        };

        let mut response = response
            .rcode(Rcode::NoError)
            .copy_question(&question)?
            .into_an()
            .record()?
            .name()?;
        for label in &labels {
            response = response.label(label)?;
        }

        response
            .try_into_rdata()?
            .class(Class::IN)
            .r#type(Type::PTR)
            .build_rdata::<NameBuilder<_>>()?
            .labels(name.as_bytes())?
            .return_to_rdata()?
            .finish()?
            .finish()?
            .finish();
    };

    Ok(result)
}