cdns_rs/
lib.rs

1/*-
2 * cdns-rs - a simple sync/async DNS query library
3 * 
4 * Copyright (C) 2020  Aleksandr Morozov
5 * 
6 * Copyright 2025 Aleksandr Morozov
7 * 
8 * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
9 * the European Commission - subsequent versions of the EUPL (the "Licence").
10 * 
11 * You may not use this work except in compliance with the Licence.
12 * 
13 * You may obtain a copy of the Licence at:
14 * 
15 *    https://joinup.ec.europa.eu/software/page/eupl
16 * 
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the Licence is distributed on an "AS IS" basis, WITHOUT
19 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
20 * Licence for the specific language governing permissions and limitations
21 * under the Licence.
22 */
23
24/*! cdns-rs
25    # CDns-rs
26
27    v 1.0.0 
28
29    An implementation of client side DNS query library which also is able to look for host name in `/etc/hosts`.  
30    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.
31
32    This library supports both async and sync code. At the moment async part is not available because:
33    - it is based on the sync realization because async code is based on sync code and sync code is unstable at the moment
34    - it requires proper porting from sync, because sync uses `poll(2)` to achieve the parallel name resolution
35
36    ## Supported
37
38    - Sending and receiving responses via TCP/UDP
39    - Reacting on the message truncated event by trying TCP
40    - Parsing /etc/hosts (all options)
41    - Partial parsing /etc/resolve.conf (all options)
42    - Async and Sync code (separate implementations) 
43    - Sequential and pipelined requests.
44    - DNS-over-TLS
45
46    ## ToDo
47    - DNS-over-HTTPS
48    - Parse /etc/nsswitch.conf
49    - DNSSEC
50    - OPT_NO_CHECK_NAMES
51    - resolv.conf (search, domain, sortlist)
52
53
54    Usage:  
55
56    - see ./examples/
57
58    Simple Example:
59
60    ```rust
61    use cdns_rs::sync::{QDns, QuerySetup, QType, request};
62
63    fn main()
64    {
65        // a, aaaa
66        let res_a = request::resolve_fqdn("protonmail.com", None).unwrap();
67
68        println!("A/AAAA:");
69        for a in res_a
70        {
71            println!("\t{}", a);
72        }
73    }
74    ```
75
76    Custom query:
77
78    ```rust
79    use cdns_rs::sync::{QDns, QuerySetup, QType, request, caches::CACHE};
80
81    fn main()
82    {
83        // soa
84        let mut dns_req = 
85            QDns::make_empty(None, QuerySetup::default()).unwrap();
86
87        dns_req.add_request(QType::SOA, "protonmail.com");
88
89        // sending request and receiving results
90        let res = dns_req.query();
91
92
93        println!("SOA:");
94        let soa_res = res.get_result();
95
96        if soa_res.is_err() == true
97        {
98            println!("{}", soa_res.err().unwrap());
99        }
100        else
101        {
102            let soa = soa_res.unwrap();
103
104            if soa.is_empty() == true
105            {
106                println!("\tNo SOA found!")
107            }
108            else
109            {
110                for s in soa
111                {
112                    for i in s.resp
113                    {
114                        println!("\t{}", i)
115                    }
116                }
117            }
118        
119        }
120    }
121    ```
122
123    ### Custom resolv.conf use TCP:
124    ```rust
125    use std::{net::{IpAddr, SocketAddr}, sync::Arc, time::Instant};
126
127    use cdns_rs::{cfg_resolv_parser::{OptionFlags, ResolveConfEntry}, common::bind_all, sync::{request, ResolveConfig}};
128
129    fn main()
130    {
131        let now = Instant::now();
132
133        let cfg = 
134        "nameserver 8.8.8.8 \n\
135        options use-vc";
136
137        //  -- or --
138        let resolver_ip: IpAddr = "8.8.8.8".parse().unwrap();
139
140        let mut resolve = ResolveConfig::default();
141
142        resolve.nameservers.push(Arc::new(ResolveConfEntry::new(SocketAddr::new(resolver_ip, 53), None, bind_all(resolver_ip)).unwrap()));
143        resolve.option_flags = OptionFlags::OPT_USE_VC;
144
145        let cust_parsed = Arc::new(ResolveConfig::custom_config(cfg).unwrap());
146        let cust_manual = Arc::new(resolve);
147
148        assert_eq!(cust_parsed, cust_manual);
149
150        // a, aaaa
151        let res_a = request::resolve_fqdn("protonmail.com", Some(cust_parsed)).unwrap();
152
153        let elapsed = now.elapsed();
154        println!("Elapsed: {:.2?}", elapsed);
155
156        println!("A/AAAA:");
157        for a in res_a
158        {
159            println!("\t{}", a);
160        }
161
162        return;
163    }
164    ```
165
166    ### Custom resolv.conf use TLS:
167    ```rust
168    use std::{sync::Arc, time::Instant};
169
170    use cdns_rs::sync::{request, ResolveConfig};
171
172    fn main()
173    {
174        let now = Instant::now();
175
176        let cfg = "nameserver 1.1.1.1#@853#cloudflare-dns.com \n\
177        options use-vc single-request";
178
179        let cust = Arc::new(ResolveConfig::custom_config(cfg).unwrap());
180
181        // a, aaaa
182        let res_a = request::resolve_fqdn("google.com", Some(cust.clone())).unwrap();
183
184
185        let elapsed = now.elapsed();
186        println!("Elapsed: {:.2?}", elapsed);
187
188        println!("A/AAAA:");
189        for a in res_a
190        {
191            println!("\t{}", a);
192        }
193
194        return;
195    }
196
197    ```
198
199    ToSockAddress
200
201    ```rust
202    use std::net::UdpSocket;
203
204    use cdns_rs::sync::query::QDnsSockerAddr;
205
206
207
208    // run in shell `nc -u -l 44444` a "test" should be received
209    fn main()
210    {
211        let udp = UdpSocket::bind("127.0.0.1:33333").unwrap();
212        udp.connect(QDnsSockerAddr::resolve("localhost:44444").unwrap()).unwrap();
213
214        udp.send("test".as_bytes()).unwrap();
215
216        return;
217    }
218    ```
219 */
220
221
222
223extern crate rand;
224#[macro_use] extern crate bitflags;
225extern crate nix;
226
227#[cfg(feature = "use_sync")]
228extern crate rustls;
229
230#[cfg(feature = "use_async")]
231extern crate tokio_rustls;
232
233#[cfg(any(feature = "use_sync", feature = "use_async"))]
234extern crate webpki;
235#[cfg(any(feature = "use_sync", feature = "use_async"))]
236extern crate webpki_roots;
237//extern crate webrtc_dtls;
238
239
240extern crate httparse;
241
242#[cfg(feature = "use_sync")]
243extern crate byteorder;
244#[cfg(feature = "use_sync")]
245extern crate crossbeam_utils;
246
247
248#[cfg(feature = "use_async")]
249extern crate async_recursion;
250#[cfg(feature = "use_async")]
251extern crate tokio;
252#[cfg(feature = "use_async")]
253extern crate tokio_byteorder;
254#[cfg(feature = "use_async")]
255extern crate async_trait;
256
257
258#[cfg(feature = "use_async")]
259pub mod a_sync;
260
261#[cfg(feature = "use_sync")]
262pub mod sync;
263
264pub mod cfg_host_parser;
265pub mod cfg_resolv_parser;
266mod network_common;
267
268
269mod query_private;
270
271pub mod query;
272pub mod common;
273mod portable;
274#[macro_use] pub mod error;
275mod tokenizer;
276
277pub use error::*;
278pub use common::{QType, DnsResponsePayload, DnsRdata};
279pub use query::{QDnsQueryResult, QDnsQuery, QuerySetup, QDnsQueryRec};
280
281
282
283
284