~nickbp/kapiti

9d467b7608ee36b9aa75191245108c999bf2600e — Nick Parker a month ago 0dfbb8d
Try including TXT with filter info in filter responses (#20)
M src/codec/encoder.rs => src/codec/encoder.rs +18 -5
@@ 27,6 27,7 @@ impl DNSMessageEncoder {
        orig_header_id: u16,
        orig_question: &Question,
        orig_opt: &Option<OPT>,
        filter_info: &String,
        ip: Option<std::net::IpAddr>,
        udp_size_override: Option<u16>,
        buf: &mut BytesMut,


@@ 51,7 52,7 @@ impl DNSMessageEncoder {

                question_count: 1,
                answer_count: match ip {
                    Some(_) => 1, // A or AAA resource
                    Some(_) => 1, // A or AAAA resource
                    None => 0,
                },
                authority_count: match ip {


@@ 59,8 60,8 @@ impl DNSMessageEncoder {
                    None => 1, // SOA resource
                },
                additional_count: match orig_opt {
                    Some(_) => 1,
                    None => 0,
                    Some(_) => 2,
                    None => 1,
                },
            },
            buf,


@@ 95,10 96,10 @@ impl DNSMessageEncoder {
                                mname: orig_question.name.as_str(),
                                rname: rname.as_str(),
                                serial: 42069,
                                refresh: 6 * 60 * 60,
                                refresh: CACHED_NXDOMAIN_TTL,
                                retry: 60 * 60,
                                expire: 3 * 24 * 60 * 60,
                                minimum: 5 * 60,
                                minimum: CACHED_NXDOMAIN_TTL,
                            }),
                        },
                    },


@@ 113,6 114,18 @@ impl DNSMessageEncoder {
            message::write_opt(&opt, udp_size_override, buf)?;
        }

        message::write_resource_fields(
            &message::ResourceFields {
                name: orig_question.name.as_str(),
                resource_type: IntEnum::Enum(ResourceType::TXT),
                resource_class: orig_question.resource_class,
                ttl: CACHED_NXDOMAIN_TTL,
                rdata: message::RDataFields::TXT(filter_info),
            },
            buf,
            &mut ptr_offsets,
        )?;

        Ok(())
    }


M src/codec/message.rs => src/codec/message.rs +2 -0
@@ 471,6 471,7 @@ pub enum RDataFields<'a> {
    RDATA(&'a ResourceData),
    IP(IpAddr),
    SOA(rdata::SOAFields<'a>),
    TXT(&'a String),
}

/// Values to be used when writing a Resource, instead of using what's in the cached version.


@@ 548,6 549,7 @@ pub fn write_resource_fields(
        RDataFields::IP(IpAddr::V4(ip)) => rdata::write_a_ip(&ip, buf)?,
        RDataFields::IP(IpAddr::V6(ip)) => rdata::write_aaaa_ip(&ip, buf)?,
        RDataFields::SOA(soa) => rdata::write_soa_fields(&soa, buf, ptr_offsets)?,
        RDataFields::TXT(txt) => rdata::write_txt_entry(txt.as_bytes(), buf)?,
    }

    if buf.len() > rdata_offset {

M src/codec/rdata.rs => src/codec/rdata.rs +5 -1
@@ 559,7 559,7 @@ fn read_mx(buf: &[u8], mut rdata_offset: usize, rdata_len: usize) -> Result<rdat
fn write_txt(rdata: &ResourceData, buf: &mut BytesMut) -> Result<()> {
    if let ResourceData::TXT(txt) = rdata {
        for entry in &txt.entries {
            character_string::write(entry.data.as_slice(), buf, "rdata.txt.entries[]")?;
            write_txt_entry(entry.data.as_slice(), buf)?;
        }
        Ok(())
    } else {


@@ 567,6 567,10 @@ fn write_txt(rdata: &ResourceData, buf: &mut BytesMut) -> Result<()> {
    }
}

pub fn write_txt_entry(entry: &[u8], buf: &mut BytesMut) -> Result<()> {
    character_string::write(entry, buf, "rdata.txt.entries[]")
}

fn read_txt(buf: &[u8], rdata_offset: usize, rdata_len: usize) -> Result<rdata::TXT> {
    let mut total_consumed: usize = 0;
    let mut entries = vec::Vec::new();

M src/filter/filter.rs => src/filter/filter.rs +0 -1
@@ 5,7 5,6 @@ use std::vec::Vec;
use anyhow::{Context, Result};
use hyper::{Client, Uri};
use sha2::{Digest, Sha256};
use tracing::debug;

use crate::filter::{downloader, path, reader};
use crate::{http, hyper_smol};

M src/filter/reader.rs => src/filter/reader.rs +4 -4
@@ 279,12 279,12 @@ fn validate_filter<T: Display>(
/// HOWEVER, in reality both of these seem to be valid:
/// - hostnames that start with a number (in the subdomain)
/// - hostnames that contain '_'
fn validate_host(host: &str, file_info: &FileInfo, line_num: usize) -> Result<String> {
fn validate_host(host: &str, source_info: &FileInfo, line_num: usize) -> Result<String> {
    if host.len() < 2 {
        bail!(
            "Invalid host of length {} in {:?} line {}: {}",
            host.len(),
            file_info,
            source_info,
            line_num,
            host
        );


@@ 295,7 295,7 @@ fn validate_host(host: &str, file_info: &FileInfo, line_num: usize) -> Result<St
            if !c.is_ascii_alphanumeric() {
                bail!(
                    "Invalid host in {:?} line {}: Last char must be alphanumeric: {}",
                    file_info,
                    source_info,
                    line_num,
                    host
                );


@@ 305,7 305,7 @@ fn validate_host(host: &str, file_info: &FileInfo, line_num: usize) -> Result<St
            if !c.is_ascii_alphanumeric() && c != '-' && c != '_' && c != '.' {
                bail!(
                    "Invalid host in {:?} line {}, middle chars must be alphanumeric, '-', or '.': {}",
                    file_info, line_num, host
                    source_info, line_num, host
                );
            }
        }

M src/lookup.rs => src/lookup.rs +12 -1
@@ 19,6 19,9 @@ lazy_static! {

    /// Encoder instance, currently doesn't have state
    static ref ENCODER: DNSMessageEncoder = DNSMessageEncoder::new();

    /// "File name" name use in filtered info responses about hardcoded targets
    static ref HARDCODED_SOURCE_NAME: String = "hardcoded".to_string();
}

pub struct Lookup {


@@ 51,7 54,7 @@ impl Lookup {
                                if let Some(f) = file_info {
                                    (f.source_path.clone(), (*file_entry).clone())
                                } else {
                                    (String::new(), (*file_entry).clone())
                                    (HARDCODED_SOURCE_NAME.clone(), (*file_entry).clone())
                                }
                            });
                    }


@@ 110,6 113,10 @@ fn write_filter_response(
    filter_source: &String,
    entry: &reader::FilterEntry,
) -> Result<()> {
    let filter_info = match entry.line_num {
        Some(line_num) => format!("{}:{}", filter_source, line_num),
        None => filter_source.to_string(),
    };
    if let (None, None) = (entry.dest_ipv4, entry.dest_ipv6) {
        // Return blocked domain
        debug!(


@@ 121,6 128,7 @@ fn write_filter_response(
            request_info.received_request_id,
            question,
            opt,
            &filter_info,
            None,
            Some(request_info.requested_udp_size),
            packet_buffer,


@@ 140,6 148,7 @@ fn write_filter_response(
            request_info.received_request_id,
            question,
            opt,
            &filter_info,
            entry.dest_ipv4.map(|ip| IpAddr::V4(ip)),
            Some(request_info.requested_udp_size),
            packet_buffer,


@@ 159,6 168,7 @@ fn write_filter_response(
            request_info.received_request_id,
            question,
            opt,
            &filter_info,
            entry.dest_ipv6.map(|ip| IpAddr::V6(ip)),
            Some(request_info.requested_udp_size),
            packet_buffer,


@@ 177,6 187,7 @@ fn write_filter_response(
            request_info.received_request_id,
            question,
            opt,
            &filter_info,
            None,
            Some(request_info.requested_udp_size),
            packet_buffer,