cdns-rs 1.2.2

A native Sync/Async Rust implementation of client DNS resolver.
Documentation
/*-
 * cdns-rs - a simple sync/async DNS query library
 * 
 * Copyright (C) 2020  Aleksandr Morozov
 * 
 * Copyright (C) 2025  Aleksandr Morozov
 * 
 * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
 * the European Commission - subsequent versions of the EUPL (the "Licence").
 * 
 * You may not use this work except in compliance with the Licence.
 * 
 * You may obtain a copy of the Licence at:
 * 
 *    https://joinup.ec.europa.eu/software/page/eupl
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the Licence is distributed on an "AS IS" basis, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * Licence for the specific language governing permissions and limitations
 * under the Licence.
 */

use std::fmt;
use std::marker::PhantomData;
use std::net::SocketAddr;
use std::os::fd::{AsFd, BorrowedFd};
use std::sync::Arc;
use std::time::Duration;
use core::fmt::Debug;

use async_trait::async_trait;

//use rustls::pki_types::ServerName;

use crate::cfg_resolv_parser::ResolveConfEntry;
use crate::network_common::SocketTapCommon;

use crate::error::*;

/// A common interface to access the realizations for both [TcpStream] and
/// [UdpSocket]
#[async_trait]
#[allow(async_fn_in_trait)]
pub trait SocketTap<LOC>: SocketTapCommon + Send + Sync + fmt::Debug
{
    /// Connects to the remote host.  
    async fn connect(&mut self, conn_timeout: Option<Duration>) -> CDnsResult<()>;

    /// Tells if current instace is `encrypted` i.e [TcpTlsConnection] over [TcpStream].
    fn is_encrypted(&self) -> bool;

    /// Tells if current instance is [TcpStream] if true, or [UdpSocket] if false
    fn is_tcp(&self) -> bool;

    fn should_append_len(&self) -> bool;

    async fn poll_read(&self) -> CDnsResult<()>;

    /// Sends data over wire.  
    async fn send(&mut self, sndbuf: &[u8]) -> CDnsResult<usize> ;

    /// Receives data transmitted from remote host.
    /// In nonblocking mode it should be called only after the event was polled
    /// In blocking mode it will block until received or timeout.
    async fn recv(&mut self, rcvbuf: &mut [u8]) -> CDnsResult<usize>;
}


pub type NetworkTapType<LOC> = (dyn SocketTap<LOC> + Send + Sync);

pub trait SocketTaps<LOC>: Clone + fmt::Debug + Send + Sync
{
    type TcpSock: AsFd;
    type UdpSock: AsFd;

    #[cfg(feature = "use_async_tls")]
    type TlsSock: AsFd;
    /*type LocalDatagramSock: AsFd;
    type LocalStreamSock: AsFd;*/

    fn new_tcp_socket(resolver: Arc<ResolveConfEntry>, timeout: Duration) -> CDnsResult<Box<NetworkTapType<LOC>>>;
    fn new_udp_socket(resolver: Arc<ResolveConfEntry>, timeout: Duration) -> CDnsResult<Box<NetworkTapType<LOC>>>;

    #[cfg(feature = "use_async_tls")]
    fn new_tls_socket(resolver: Arc<ResolveConfEntry>, timeout: Duration) -> CDnsResult<Box<NetworkTapType<LOC>>>;
    /*fn new_local_dgram_socket(resolver: Arc<ResolveConfEntry>, timeout: Duration) -> NetworkTap<Self::LocalDatagramSock>;
    fn new_local_stream_socket(resolver: Arc<ResolveConfEntry>, timeout: Duration) -> NetworkTap<Self::LocalStreamSock>;*/
}




/// An instance of the socket/stream.
pub struct NetworkTap<T: AsFd, LOC>
{
    /// Channel
    pub sock: Option<T>, 
    /// Receive timeout
    pub timeout: Duration,
    /// Instance's config
    pub cfg: Arc<ResolveConfEntry>,

    pub _loc: PhantomData<LOC>,
}

unsafe impl<T: AsFd, LOC> Send for NetworkTap<T, LOC> {}
unsafe impl<T: AsFd, LOC> Sync for NetworkTap<T, LOC> {}

impl<T: AsFd + Debug, LOC> fmt::Debug for NetworkTap<T, LOC>
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
    {
        f.debug_struct("NetworkTap").field("sock", &self.sock).field("timeout", &self.timeout).field("cfg", &self.cfg).finish()
    }
}

impl<T: AsFd, LOC> NetworkTap<T, LOC>
{
    /// Creates new instance of network which is 'tapped' with [SocketTap]. 
    /// A socket is not initially connected and `SocketTap::connect` should be called.
    /// 
    /// # Arguments
    /// 
    /// * `resolver_ip` - a ref to [IpAddr] which holds host address of the nameserver.
    /// 
    /// * `resolver_port` - a port number binded by nameserver
    /// 
    /// * `bind_addr` - a local address to bind the socket to
    /// 
    /// # Returns
    /// 
    /// * [CDnsResult] - Ok with inner type [Box] dyn [SocketTap]
    /// 
    /// * [CDnsResult] - Err with error description
    pub 
    fn new(resolver: Arc<ResolveConfEntry>, timeout: Duration) -> CDnsResult<Box<NetworkTapType<LOC>>> where NetworkTap<T, LOC>: SocketTap<LOC> + 'static
    {
        /*let domain_name = 
            if let Some(domainname) = resolver.get_tls_domain()
            {
                webpki::DnsNameRef::try_from_ascii_str(domainname).map_err(|e| internal_error_map!(CDnsErrorType::InternalError, "{}", e))?
                //ServerName::try_from(domainname.clone()).map_err(|e| internal_error_map!(CDnsErrorType::InternalError, "{}", e))?
            }
            else
            {
                internal_error!(CDnsErrorType::InternalError, "no domain is set for TLS conncection");
            };
*/
        let ret = 
            NetworkTap::<T, LOC>
            { 
                sock: None, 
               // domain_name: domain_name,
                timeout: timeout,
                cfg: resolver,
                _loc: PhantomData
            };

        return Ok( Box::new(ret) );
    }
}


impl<T: AsFd, LOC> SocketTapCommon for NetworkTap<T, LOC>
{
    fn get_remote_addr(&self) -> &SocketAddr
    {
        return &self.cfg.get_resolver_sa();
    }
}