rlibdns 0.1.211

Rust DNS Library
Documentation
use std::collections::HashMap;

pub fn pack_fqdn_compressed(fqdn: &str, compression_data: &mut HashMap<String, usize>, off: usize) -> Vec<u8> {
    if fqdn.is_empty() {
        return vec![0x00];
    }

    let mut buf = Vec::new();
    let mut off = off;

    let parts: Vec<&str> = fqdn.split('.').collect();

    for i in 0..parts.len() {
        let suffix = parts[i..].join(".");

        if let Some(&ptr) = compression_data.get(&suffix) {
            buf.push(0xC0 | ((ptr >> 8) as u8 & 0x3F));
            buf.push((ptr & 0xFF) as u8);
            return buf;
        }

        let label_bytes = parts[i].as_bytes();
        //assert!(label_bytes.len() <= 63, "label too long");
        buf.push(label_bytes.len() as u8);
        buf.extend_from_slice(label_bytes);

        if off <= 0x3FFF {
            compression_data.entry(suffix).or_insert(off);
        }
        off = off.saturating_add(label_bytes.len() + 1);
    }

    buf.push(0x00);
    buf
}

pub fn pack_fqdn(fqdn: &str) -> Vec<u8> {
    if fqdn.is_empty() {
        return vec![0x00];
    }

    let mut buf = Vec::new();

    let parts: Vec<&str> = fqdn.split('.').collect();

    for i in 0..parts.len() {
        let label_bytes = parts[i].as_bytes();
        //assert!(label_bytes.len() <= 63, "label too long");
        buf.push(label_bytes.len() as u8);
        buf.extend_from_slice(label_bytes);
    }

    buf.push(0x00);
    buf
}

pub fn unpack_fqdn(buf: &[u8], off: usize) -> (String, usize) {
    let mut builder: Vec<&str> = Vec::new();
    let mut pos = off;
    let mut jumped = false;
    let mut original_pos = pos;

    while pos < buf.len() {
        let length = buf[pos] as usize;
        pos += 1;

        if length == 0 {
            break;
        }

        if (length & 0xC0) == 0xC0 {
            if pos >= buf.len() {
                break;
            }
            let pointer_offset = ((length & 0x3F) << 8) | buf[pos] as usize;
            pos += 1;

            if !jumped {
                original_pos = pos;
            }
            pos = pointer_offset;
            jumped = true;

        } else {
            if pos + length > buf.len() {
                break;
            }

            builder.push(std::str::from_utf8(&buf[pos..pos + length]).unwrap());
            pos += length;
        }
    }

    let final_pos = if jumped {
        original_pos

    } else {
        pos
    };

    (builder.join("."), final_pos - off)
}

pub fn encode_fqdn(fqdn: &str) -> Vec<u8> {
    if fqdn.is_empty() {
        return vec![0x00];
    }

    let mut buf = Vec::new();

    for part in fqdn.split('.').rev() {
        buf.extend_from_slice(part.as_bytes());
        buf.push(0x00);
    }

    buf
}

pub fn decode_fqdn(buf: &[u8]) -> String {
    if buf == [0x00] {
        return String::new();
    }

    let mut builder: Vec<&str> = Vec::new();
    let mut original_pos = 0;

    for i in 0..buf.len() {
        if buf[i] == 0 {
            if i > original_pos {
                builder.push(std::str::from_utf8(&buf[original_pos..i]).unwrap());
            }
            original_pos = i + 1;
        }
    }

    builder.reverse();
    builder.join(".")
}

pub fn to_fqdn(apex: &str, child: &str) -> String {
    if child.is_empty() {
        return apex.to_string();
    }

    format!("{}.{}", child, apex)
}

pub fn fqdn_to_relative(apex: &str, child: &str) -> Option<String> {
    if apex.eq(child) {
        return Some(String::new());
    }

    if let Some(stripped) = child.strip_suffix(apex) {
        return Some(stripped.trim_end_matches('.').to_string());
    }

    None
}