cdns-rs 2.0.0-next.0

A native Sync/Async Rust implementation of client DNS resolver.
Documentation
/*-
 * cdns-rs - a simple sync/async DNS query library
 * 
 * Copyright (C) 2021  Aleksandr Morozov
 * Copyright (C) 2025  Aleksandr Morozov
 * Copyright (C) 2026  Aleksandr Morozov, 4neko.org
 * 
 * The syslog-rs crate can be redistributed and/or modified
 * under the terms of either of the following licenses:
 *
 *   1. the Mozilla Public License Version 2.0 (the “MPL”) OR
 *
 *   2. The MIT License (MIT)
 *                     
 *   3. EUROPEAN UNION PUBLIC LICENCE v. 1.2 EUPL © the European Union 2007, 2016
 */


/*! 
# cdns-rs

v 2.0.0

<img src="https://cdn.4neko.org/cdns_rs_logo.webp" width="280"/> <img src="https://cdn.4neko.org/source_avail.webp" width="280"/> <img src="https://cdn.4neko.org/mit_mpl_eupl_2.webp" width="280"/>

Implementation of a library for performing client-side DNS queries that is also capable of looking up a hostname in the `/etc/hosts` file. It loads settings from either `/etc/resolv.conf` or user input.
So it acts like libc's `gethostbyname(3)` or `gethostbyaddr(3)`. The configuration can be overriden.

This library supports ~~both~~ `sync` ~~and `async`~~ modes.

Also the experimental IDN (international domain names) were added.

From `resolv.conf` the crate recognises:

- timeout
- rotate
- inet6
- single-request
- single-request-reopen
- use-vc

Todo:
- trust-ad
- no-reload
- edns0
- ip6-bytestring
- attempts
- ndots

## Functionality 
- 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)
- Sequential and pipelined requests.
- DNS-over-TLS
- IDN international domain names (experimental)

## Supported OSes
- GNU/Linux (in general)
- FreeBSD
- OpenBSD
- NetBSD
- DragonflyBSD
- other UNIX alike OSes

# 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 text after the # is considered as extension if there is no `space` between IP address and '#' (as in example above).

# Features

`enable_IDN_support` - (enabled by default) allows to resolve IDN
`use_sync` - enabled a sync code base
`use_sync_tls` - enables a TLS support (HTTPS is not yet functional)
`no_error_output` - does not output any errors to stderr

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

Usage:  

- see ./examples/

Simple Example:

```ignore
fn main()
{
    // init global config (resolv.conf, hosts)
    DnsConfigs::<DnsConfigGlobal>::init_global_config(None).unwrap();

    let qse = QuerySetup::default().measure_time(true);

    let mut res0 = 
        Resolver::<ResolverStdErr>::new(GLOBAL_CONFIG.get().unwrap(), ResolverStdErr);

    let req0 = 
        QDnsRequests::resolve_a_aaaa_request(1, 2, ResolveConfigFamily::INET4, "4neko.org", qse)
            .unwrap();

    let res = res0.query(&req0).unwrap();

    println!("{}", res);
}
```

ToSockAddr

```ignore
// run in shell `nc -u -l 44444` a "test" should be received
fn main()
{
    let udp = UdpSocket::bind("127.0.0.1:33333").unwrap();
    
    // init config
    DnsConfigs::<DnsConfigGlobal>::init_global_config(None).unwrap();

    udp.connect(QDnsSockerAddr::resolve("localhost:44444").unwrap()).unwrap();

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

    return;
}
```

### Custom resolv.conf use TCP:
```ignore
fn main()
{
    let cfg = 
      "nameserver 127.0.0.53 \n \
      options edns0 trust-ad single-request \n \
      search .";

    let hosts = DnsConfigs::<DnsConfigUser>::load_host(Path::new("/etc/hosts")).unwrap();

    let cfg = 
        DnsConfigs
            ::<DnsConfigUser>
            ::new_custom(
                DnsConfigSource::SourceInstance(hosts), 
                DnsConfigSource::SourceText(cfg)
            )
            .unwrap();

    let mut res0 = 
        Resolver::<ResolverStdErr>::new(&cfg, ResolverStdErr);

    let qse = QuerySetup::default().measure_time(true);

    let req0 = 
        QDnsRequests::resolve_a_aaaa_request(1, 2, ResolveConfigFamily::INET4_INET6, "4neko.org", qse)
            .unwrap();

    let res = res0.query(&req0).unwrap();

    println!("{}", res);
}
```

### Custom resolv.conf use TLS:
```ignore
fn main()
{
    let cfg = 
      "nameserver 1.1.1.1#@853#cloudflare-dns.com \n \
      options single-request \n \
      search .";

    let hosts = 
        DnsConfigs::<DnsConfigUser>::load_host(Path::new("/etc/hosts")).unwrap();

    let cfg = 
        DnsConfigs
            ::<DnsConfigUser>
            ::new_custom(
                DnsConfigSource::SourceInstance(hosts), 
                DnsConfigSource::SourceText(cfg)
            )
            .unwrap();

    let mut res0 = 
            Resolver::<ResolverStdErr>::new(&cfg, ResolverStdErr);

    let qse = QuerySetup::default().measure_time(true);

    let mut req0 = 
        QDnsRequests::resolve_a_aaaa_request(1, 2, ResolveConfigFamily::INET4_INET6, "4neko.org", qse)
            .unwrap();

    req0.add_request(3, QType::MX, "4neko.org").unwrap();

    let res = res0.query(&req0).unwrap();

    println!("{}", res);
}

```
 */



extern crate rand;
#[macro_use] extern crate bitflags;

extern crate nix;

#[cfg(any(feature = "use_sync_tls"))]
extern crate rustls;

#[cfg(any(feature = "use_sync_tls"))]
extern crate webpki;
#[cfg(any(feature = "use_sync_tls"))]
extern crate webpki_roots;
//extern crate webrtc_dtls;

extern crate instance_copy_on_write;

#[cfg(feature = "use_sync")]
extern crate byteorder;
#[cfg(feature = "use_sync")]
extern crate crossbeam_utils;

/*
#[cfg(feature = "use_async")]
extern crate async_recursion;

#[cfg(feature = "use_async")]
extern crate async_trait;
*/

//#[cfg(feature = "use_async")]
//pub mod async_interface;

/// An embedded external code under the 3rd party license.
pub mod external;

/// Config parsers.
mod parsers;

/// Networking code
mod network;

/// A public items for query.
pub mod query;

/// A common functions (shared).
pub mod common;

/// Configuration managment.
pub mod configuration;

/// A resolver code.
pub mod resolver;

/// A portable code which is different for the OSes.
mod portable;

/// Error handling.
#[macro_use] pub mod error;

pub use configuration::
{
    GLOBAL_CONFIG, 
    DnsConfigUpdater, 
    DnsConfig, 
    DnsConfigSource, 
    DnsConfigs, 
    DnsConfigUser, 
    DnsConfigGlobal
};

pub use query::QDnsRequests;

pub use error::*;
pub use common::{QType, DnsResponsePayload, DnsRdata, QDnsName};
pub use query::{QDnsQueryResult, QDnsQuery, QuerySetup, QDnsQueryRec};

pub use resolver::{ResolverGlobal, QDnsSockerAddr};
pub use parsers::cfg_host_parser::HostConfig;
pub use parsers::cfg_resolv_parser::{ResolveConfig, ResolveConfigFamily};