cdns-rs 1.0.0

A native Sync/Async Rust implementation of client DNS resolver.
Documentation
# CDns-rs

v 1.0.0 

An implementation of client side DNS query library which also is able to look for host name in `/etc/hosts`.  
Also it is able to `/etc/resolv.conf` and uses options from this file to configure itself. So it acts like libc's `gethostbyname(3)` or `gethostbyaddr(3)`. The configuration can be overriden.

This library supports both async and sync code. At the moment async part is not available because:
- it is based on the sync realization because async code is based on sync code and sync code is unstable at the moment
- it requires proper porting from sync, because sync uses `poll(2)` to achieve the parallel name resolution

## Supported

- Sending and receiving responses via TCP/UDP
- Reacting on the message truncated event by trying TCP
- Parsing /etc/hosts (all options)
- Partial parsing /etc/resolve.conf (all options)
- Async and Sync code (separate implementations) 
- Sequential and pipelined requests.
- DNS-over-TLS

# Extension

To use the DNS-over-TLS, the record to system's resolv.conf can be added:
```text
nameserver 1.1.1.1#@853#cloudflare-dns.com
```
All after the # is considered as extension if there is no space between IP address and '#'.

## ToDo
- DNS-over-HTTPS
- Parse /etc/nsswitch.conf
- DNSSEC
- OPT_NO_CHECK_NAMES
- resolv.conf (search, domain, sortlist)


Usage:  

- see ./examples/

### Simple Example:

```Rust
use cdns_rs::sync::{QDns, QuerySetup, QType, request};

fn main()
{
    // a, aaaa
    let res_a = request::resolve_fqdn("protonmail.com", None).unwrap();

    println!("A/AAAA:");
    for a in res_a
    {
        println!("\t{}", a);
    }
}
```

### Custom query:

```Rust
use cdns_rs::sync::{QDns, QuerySetup, QType, request, caches::CACHE};

fn main()
{
    // soa
    let mut dns_req = 
        QDns::make_empty(None, QuerySetup::default()).unwrap();

    dns_req.add_request(QType::SOA, "protonmail.com");

    // sending request and receiving results
    let res = dns_req.query();


    println!("SOA:");
    let soa_res = res.get_result();

    if soa_res.is_err() == true
    {
        println!("{}", soa_res.err().unwrap());
    }
    else
    {
        let soa = soa_res.unwrap();

        if soa.is_empty() == true
        {
            println!("\tNo SOA found!")
        }
        else
        {
            for s in soa
            {
                for i in s.resp
                {
                    println!("\t{}", i)
                }
            }
        }
       
    }
}
```

### Custom resolv.conf use TCP:
```rust
use std::{net::{IpAddr, SocketAddr}, sync::Arc, time::Instant};

use cdns_rs::{cfg_resolv_parser::{OptionFlags, ResolveConfEntry}, common::bind_all, sync::{request, ResolveConfig}};

fn main()
{
    let now = Instant::now();

    let cfg = 
    "nameserver 8.8.8.8 \n\
    options use-vc";

    //  -- or --
    let resolver_ip: IpAddr = "8.8.8.8".parse().unwrap();

    let mut resolve = ResolveConfig::default();

    resolve.nameservers.push(Arc::new(ResolveConfEntry::new(SocketAddr::new(resolver_ip, 53), None, bind_all(resolver_ip)).unwrap()));
    resolve.option_flags = OptionFlags::OPT_USE_VC;

    let cust_parsed = Arc::new(ResolveConfig::custom_config(cfg).unwrap());
    let cust_manual = Arc::new(resolve);

    assert_eq!(cust_parsed, cust_manual);

    // a, aaaa
    let res_a = request::resolve_fqdn("protonmail.com", Some(cust_parsed)).unwrap();

    let elapsed = now.elapsed();
    println!("Elapsed: {:.2?}", elapsed);

    println!("A/AAAA:");
    for a in res_a
    {
        println!("\t{}", a);
    }

    return;
}
```

### Custom resolv.conf use TLS:
```rust
use std::{sync::Arc, time::Instant};

use cdns_rs::sync::{request, ResolveConfig};

fn main()
{
    let now = Instant::now();

    let cfg = "nameserver 1.1.1.1#@853#cloudflare-dns.com \n\
    options use-vc single-request";

    let cust = Arc::new(ResolveConfig::custom_config(cfg).unwrap());

    // a, aaaa
    let res_a = request::resolve_fqdn("google.com", Some(cust.clone())).unwrap();


    let elapsed = now.elapsed();
    println!("Elapsed: {:.2?}", elapsed);

    println!("A/AAAA:");
    for a in res_a
    {
        println!("\t{}", a);
    }

    return;
}

```

### ToSockAddress

```Rust
use std::net::UdpSocket;

use cdns_rs::sync::query::QDnsSockerAddr;



// run in shell `nc -u -l 44444` a "test" should be received
fn main()
{
    let udp = UdpSocket::bind("127.0.0.1:33333").unwrap();
    udp.connect(QDnsSockerAddr::resolve("localhost:44444").unwrap()).unwrap();

    udp.send("test".as_bytes()).unwrap();

    return;
}
```