Rust: Domain Name Validation

Recently, as I work my way through learning bits of rust, I have been thinking that I should in corporate some form of input validation for domains.

Today I will look at two crates.

Initially I thought I would make validation part of the decon-spf crate. Though I think I would prefer to keep the crate simple and clean. To this end I have decided that validation is not the role of the crate itself. The reality is this crate is an experiment and is intended to parse and deconstruct existing SPF records.

Crates

publicsuffix

I am using version 1.5.4 which can be found on crates.io.

Example

use publicsuffix::List;

fn main() {
    let list = List::fetch().unwrap();
    let case1 = "_i-am-not-so-very-long.example.com";
    let items = case1.split(".");
    println!("Case1:");
    for i in items.into_iter() {
        println!("label Name: {}", i);
        println!("label Len : {}", i.len());
    }
    let domain = list.parse_dns_name(case1);
    match domain {
        Err(_) => println!("Invalid Domain"),
        Ok(result) => {
            println!("test: {}", result);
        }
    }
    let case2 = "_i-am-a-very-long-long-string-of-text-that-should-not-be-allowed.example.com";
    let items = case2.split(".");
    println!("Case2:");
    for i in items.into_iter() {
        println!("label Name: {}", i);
        println!("label Len : {}", i.len());
    }
    let domain = list.parse_dns_name(case2);
    match domain {
        Err(_) => println!("Invalid Domain"),
        Ok(result) => {
            println!("test: {}", result);
        }
    }
}

Output

Case1:
label Name: _i-am-not-so-very-long
label Len : 22
label Name: example
label Len : 7
label Name: com
label Len : 3
test: _i-am-not-so-very-long.example.com
Case2:
label Name: _i-am-a-very-long-long-string-of-text-that-should-not-be-allowed
label Len : 64
label Name: example
label Len : 7
label Name: com
label Len : 3
Invalid Domain

addr

I am using version 0.14.0 which can be found on crates.io.

Please also note that I am using parse_dns_name and not parse_domain_name. The difference being parse_domain_name does not allow for addictional characters per RFC1034 which covers host and domain records. There is more flexibity allowed when specifying resource type records. See StackOverFlow.

Example

use addr::parser::DnsName;
use addr::psl::List;

fn main() {
    let domain1 = List.parse_dns_name("_spf.example.com").unwrap();
    println!("Domain1 Valid: {}", domain1);
    let domain2 = "_i-am-a-very-long-long-string-of-text-that-should-not-be-allowed.example.com";
    let validity = List.parse_dns_name(domain2);
    match validity {
        Err(_) => {
            let mut c = 1;
            println!("Invalid Domain");
            let labels = domain2.split(".").into_iter();
            for label in labels {
                if label.len() > 63 {
                    println!("Label exceeds max length of 63!");
                }
                println!("label{}: len {} Str {}", c, label.len(), label);
                c += 1;
            }
        }
        Ok(domain) => println!("valid Domain: {}", domain),
    }
}

Output

Domain1 Valid: _spf.example.com
Invalid Domain
Label exceeds max length of 63!
label1: len 64 Str _i-am-a-very-long-long-string-of-text-that-should-not-be-allowed
label2: len 7 Str example
label3: len 3 Str com

Closing

I hope this information is useful.


See also