cdns_rs/
query.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
24use std::collections::LinkedList;
25use std::convert::{TryFrom, TryInto};
26use std::net::SocketAddr;
27use std::fmt;
28use std::time::Duration;
29use std::time::Instant;
30
31
32use crate::error::*;
33use crate::internal_error;
34
35use super::common::*;
36
37
38/// A query override
39#[derive(Debug, Clone)]
40pub struct QuerySetup
41{
42    /// Sets the realtime counter to measure delay
43    pub(crate) measure_time: bool, 
44
45    /// Forces to ignore hosts file
46    pub(crate) ign_hosts: bool, 
47
48    /// Overrides the timeout duration for reply awaiting.
49    pub(crate) timeout: Option<u32>,
50}
51
52impl Default for QuerySetup
53{
54    fn default() -> Self 
55    {
56        return Self { measure_time: false, ign_hosts: false, timeout: None };
57    }
58}
59
60impl QuerySetup
61{
62    /// Turns on/off the time measurment whcih measure how many time went
63    /// since query started for each response.
64    pub 
65    fn set_measure_time(&mut self, flag: bool) -> &mut Self
66    {
67        self.measure_time = flag;
68        
69        return self;
70    }
71
72    /// Turns on/off the option which when set is force to ignore lookup in
73    /// /etc/hosts
74    pub 
75    fn set_ign_hosts(&mut self, flag: bool) -> &mut Self
76    {
77        self.ign_hosts = flag;
78
79        return self;
80    }
81
82    /// Overrides the timeout. Can not be 0.
83    pub 
84    fn set_override_timeout(&mut self, timeout: u32) -> &mut Self
85    {
86        if timeout == 0
87        {
88            return self;
89        }
90
91        self.timeout = Some(timeout);
92
93        return self;
94    }
95
96    /// Resets the override timeout.
97    pub 
98    fn reset_override_timeout(&mut self) -> &mut Self
99    {
100        self.timeout = None;
101
102        return self;
103    }
104
105}
106
107/// A [StatusBits] decoded response status.
108#[derive(Clone, Copy, Debug, PartialEq, Eq)]
109pub enum QDnsQueryRec
110{
111    /// Response is Ok and
112    Ok,
113    /// Server side error
114    ServFail,
115    /// Query does not exist, but meaningfull when `aa` is true
116    NxDomain,
117    /// A name server refuses to perform the specified operation
118    Refused,
119    /// A name server does not support the requested kind of query
120    NotImpl,
121    /// A fromat error
122    FormError,
123}
124
125impl QDnsQueryRec
126{
127    /// Returns true if [QueryMode] is set to continue query other nameservers.  
128    /// Returns false if there is no need to query other nameservers i.e 
129    ///  [QueryMode::DefaultMode] or the response was from authotherative server.
130    pub(crate)
131    fn try_next_nameserver(&self, aa: bool) -> bool
132    {
133        match *self
134        {
135            Self::NxDomain =>
136            {
137                if aa == true
138                {
139                    // the response is from authotherative server
140                    return false;
141                }
142
143                return true;
144            },
145            Self::Ok | Self::Refused | Self::ServFail | Self::NotImpl | Self::FormError =>
146            {
147                return true;
148            }
149        }
150    }
151}
152
153impl TryFrom<StatusBits> for QDnsQueryRec
154{
155    type Error = CDnsError;
156
157    fn try_from(value: StatusBits) -> Result<Self, Self::Error> 
158    {
159        if value.contains(StatusBits::RESP_NOERROR) == true
160        {
161            /*let resps: Vec<QueryRec> = 
162                ans.response.into_iter().map(|a| a.into()).collect();
163            */
164            return Ok(QDnsQueryRec::Ok);
165        }
166        else if value.contains(StatusBits::RESP_FORMERR) == true
167        {
168            return Ok(QDnsQueryRec::FormError);
169        }
170        else if value.contains(StatusBits::RESP_NOT_IMPL) == true
171        {
172            return Ok(QDnsQueryRec::NotImpl);
173        }
174        else if value.contains(StatusBits::RESP_NXDOMAIN) == true
175        {
176            return Ok(QDnsQueryRec::NxDomain);
177        }
178        else if value.contains(StatusBits::RESP_REFUSED) == true
179        {
180            return Ok(QDnsQueryRec::Refused);
181        }
182        else if value.contains(StatusBits::RESP_SERVFAIL) == true
183        {
184            return Ok(QDnsQueryRec::ServFail);
185        }
186        else
187        {
188            internal_error!(CDnsErrorType::DnsResponse, "response status bits unknwon result: '{}'", value.bits());
189        };
190    }
191}
192
193
194impl fmt::Display for QDnsQueryRec
195{
196    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
197    {
198        match *self
199        {
200            Self::Ok => 
201            {
202                writeln!(f, "OK")?;
203            },
204            Self::ServFail =>
205            {
206                writeln!(f, "SERVFAIL")?;
207            },
208            Self::NxDomain =>
209            {
210                writeln!(f, "NXDOMAIN")?;
211            },
212            Self::Refused => 
213            {   
214                writeln!(f, "REFUSED")?;
215            },
216            Self::NotImpl => 
217            {
218                writeln!(f, "NOT IMPLEMENTED")?;
219            },
220            Self::FormError =>
221            {
222                writeln!(f, "FORMAT ERROR")?;
223            }
224        }
225
226        return Ok(());
227    }
228}
229
230/// A structure which describes the query properties and contains the
231/// results.
232#[derive(Clone, Debug)]
233pub struct QDnsQuery
234{
235    /// A realtime time elapsed for query
236    pub elapsed: Option<Duration>,
237    /// Server which performed the response and port number
238    pub server: String,
239    /// Authoritative Answer
240    pub aa: bool,
241    /// Authoratives section
242    pub authoratives: Vec<DnsResponsePayload>,
243    /// Responses
244    pub resp: Vec<DnsResponsePayload>,
245    /// Status
246    pub status: QDnsQueryRec,
247}
248
249impl fmt::Display for QDnsQuery
250{
251    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
252    {
253        write!(f, "Source: {} ", self.server)?;
254        if let Some(ref el) = self.elapsed
255        {
256            write!(f, "{:.2?} ", el)?;
257        }
258
259        if self.aa == true
260        {
261            writeln!(f, "Authoritative answer")?;
262        }
263        else
264        {
265            writeln!(f, "Non-Authoritative answer")?;
266        }
267
268        writeln!(f, "Authoritatives: {}", self.authoratives.len())?;
269
270        if self.authoratives.len() > 0
271        {
272            for a in self.authoratives.iter()
273            {
274                writeln!(f, "{}", a)?;
275            }
276
277            writeln!(f, "")?;
278        }
279
280        writeln!(f, "Status: {}", self.status)?;
281
282        writeln!(f, "Answers: {}", self.resp.len())?;
283
284        if self.resp.len() > 0
285        {
286            for r in self.resp.iter()
287            {
288                writeln!(f, "{}", r)?;
289            }
290
291            writeln!(f, "")?;
292        }
293
294        return Ok(());
295    }
296}
297
298impl QDnsQuery
299{
300    /// Returns true if the response is OK
301    pub 
302    fn is_ok(&self) -> bool
303    {
304        return self.status == QDnsQueryRec::Ok;
305    }
306
307    pub 
308    fn is_authorative(&self) -> bool
309    {
310        return self.aa;
311    }
312
313    pub 
314    fn get_elapsed_time(&self) -> Option<&Duration>
315    {
316        return self.elapsed.as_ref();
317    }
318
319    pub 
320    fn get_server(&self) -> &String
321    {
322        return &self.server;
323    }
324
325    /// Returns the authorative server data if any
326    pub 
327    fn get_authoratives(&self) -> &[DnsResponsePayload]
328    {
329        return self.authoratives.as_slice();
330    }
331
332    /// Returns the responses if any
333    pub 
334    fn get_responses(&self) -> &[DnsResponsePayload]
335    {
336        return self.resp.as_slice();
337    }
338
339    /// Moves the responses from structure
340    pub 
341    fn move_responses(self) -> Vec<DnsResponsePayload>
342    {
343        return self.resp;
344    }
345}
346
347impl QDnsQuery
348{
349    /// Constructs instance like it is from 'local' source but not from DNS server.
350    pub 
351    fn from_local(req_pl: Vec<DnsResponsePayload>, now: Option<&Instant>) -> QDnsQuery
352    {
353        let elapsed = 
354            match now
355            {
356                Some(n) => Some(n.elapsed()),
357                None => None
358            };
359
360        return 
361            Self
362            {
363                elapsed: elapsed,
364                server: HOST_CFG_PATH.to_string(),
365                aa: true,
366                authoratives: Vec::new(),
367                status: QDnsQueryRec::Ok,
368                resp: req_pl
369            };
370    }
371
372    /// Constructs an instance from the remote response.
373    pub 
374    fn from_response(server: &SocketAddr, ans: DnsRequestAnswer, now: Option<&Instant>) -> CDnsResult<Self>
375    {
376        return Ok(
377            Self
378            {
379                elapsed: now.map_or(None, |n| Some(n.elapsed())),
380                server: server.to_string(),
381                aa: ans.header.status.contains(StatusBits::AUTH_ANSWER),
382                authoratives: ans.authoratives,
383                status: ans.header.status.try_into()?,
384                resp: ans.response,
385            }
386        );
387    }
388}
389
390/// The result enum. 
391#[derive(Debug)]
392pub enum QDnsQueriesRes
393{
394    /// Received results
395    DnsOk
396    {
397        res: Vec<QDnsQuery>,
398    },
399    /// All nameservers did not respond 
400    DnsNotAvailable,
401}
402
403impl QDnsQueriesRes
404{ 
405    pub(crate)
406    fn extend(&mut self, other: Self)
407    {
408        if other.is_results() == false
409        {
410            return;
411        }
412
413        match *self
414        {
415            Self::DnsOk{ ref mut res } =>
416            {
417                res.extend(other.into_inner().unwrap());
418            },
419            Self::DnsNotAvailable =>
420            {
421                *self = Self::DnsOk{ res: other.into_inner().unwrap() };
422            }
423        }
424    }
425
426    /// Unwraps the results without panics.
427    pub 
428    fn into_inner(self) -> Option<Vec<QDnsQuery>>
429    {
430        match self
431        {
432            Self::DnsNotAvailable => None,
433            Self::DnsOk{ res } => return Some(res),
434        }
435    }
436
437    /// Checks if the received result contains any results.
438    /// It does not check if results were successfull. It just
439    /// answers on question if any DNS server responded or record 
440    /// in local databases was found.
441    pub 
442    fn is_results(&self) -> bool
443    {
444        match *self
445        {
446            Self::DnsNotAvailable => return false,
447            _ => return true,
448        }
449    }
450
451    /// This function tells how many responses it contains.
452    /// If Self is [QDnsQueriesRes::DnsNotAvailable] then 0 will be returned.
453    pub 
454    fn len(&self) -> usize
455    {
456        if let Self::DnsOk{res} = self
457        {
458            return res.len();
459        }
460        else
461        {
462            return 0;
463        }
464    }
465
466    /// This function returns the results if any.
467    pub 
468    fn list_results(&self) -> Option<std::slice::Iter<'_, QDnsQuery>>
469    {
470        if let Self::DnsOk{res} = self
471        {
472            return Some(res.iter());
473        }
474        else
475        {
476            return None;
477        }
478    }
479}
480
481impl From<QDnsQuery> for QDnsQueriesRes
482{
483    fn from(query: QDnsQuery) -> Self 
484    {
485        return QDnsQueriesRes::DnsOk{ res: vec![query] };
486    }
487}
488
489impl From<LinkedList<QDnsQuery>> for QDnsQueriesRes
490{
491    fn from(responses: LinkedList<QDnsQuery>) -> Self 
492    {
493        if responses.len() > 0
494        {
495            return QDnsQueriesRes::DnsOk{ res: responses.into_iter().map(|h| h).collect() };
496        }
497        else
498        {
499            return QDnsQueriesRes::DnsNotAvailable;
500        }
501    }
502}
503
504impl fmt::Display for QDnsQueriesRes
505{
506    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
507    {
508        match *self
509        {
510            Self::DnsOk{ ref res } =>
511            {
512                for r in res.iter()
513                {
514                    write!(f, "{}", r)?;
515                }
516            },
517            Self::DnsNotAvailable => write!(f, "No DNS server available")?
518        }
519
520        return Ok(());
521    }
522}
523
524