cdns_rs/a_sync/network.rs
1/*-
2 * cdns-rs - a simple sync/async DNS query library
3 *
4 * Copyright (C) 2020 Aleksandr Morozov
5 *
6 * Copyright (C) 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::fmt;
25use std::marker::PhantomData;
26use std::net::SocketAddr;
27use std::os::fd::{AsFd, BorrowedFd};
28use std::sync::Arc;
29use std::time::Duration;
30use core::fmt::Debug;
31
32use async_trait::async_trait;
33
34//use rustls::pki_types::ServerName;
35
36use crate::cfg_resolv_parser::ResolveConfEntry;
37use crate::network_common::SocketTapCommon;
38
39use crate::error::*;
40
41/// A common interface to access the realizations for both [TcpStream] and
42/// [UdpSocket]
43#[async_trait]
44#[allow(async_fn_in_trait)]
45pub trait SocketTap<LOC>: SocketTapCommon + Send + Sync + fmt::Debug
46{
47 /// Connects to the remote host.
48 async fn connect(&mut self, conn_timeout: Option<Duration>) -> CDnsResult<()>;
49
50 /// Tells if current instace is `encrypted` i.e [TcpTlsConnection] over [TcpStream].
51 fn is_encrypted(&self) -> bool;
52
53 /// Tells if current instance is [TcpStream] if true, or [UdpSocket] if false
54 fn is_tcp(&self) -> bool;
55
56 fn should_append_len(&self) -> bool;
57
58 async fn poll_read(&self) -> CDnsResult<()>;
59
60 /// Sends data over wire.
61 async fn send(&mut self, sndbuf: &[u8]) -> CDnsResult<usize> ;
62
63 /// Receives data transmitted from remote host.
64 /// In nonblocking mode it should be called only after the event was polled
65 /// In blocking mode it will block until received or timeout.
66 async fn recv(&mut self, rcvbuf: &mut [u8]) -> CDnsResult<usize>;
67}
68
69
70pub type NetworkTapType<LOC> = (dyn SocketTap<LOC> + Send + Sync);
71
72pub trait SocketTaps<LOC>: Clone + fmt::Debug + Send + Sync
73{
74 type TcpSock: AsFd;
75 type UdpSock: AsFd;
76
77 #[cfg(feature = "use_async_tls")]
78 type TlsSock: AsFd;
79 /*type LocalDatagramSock: AsFd;
80 type LocalStreamSock: AsFd;*/
81
82 fn new_tcp_socket(resolver: Arc<ResolveConfEntry>, timeout: Duration) -> CDnsResult<Box<NetworkTapType<LOC>>>;
83 fn new_udp_socket(resolver: Arc<ResolveConfEntry>, timeout: Duration) -> CDnsResult<Box<NetworkTapType<LOC>>>;
84
85 #[cfg(feature = "use_async_tls")]
86 fn new_tls_socket(resolver: Arc<ResolveConfEntry>, timeout: Duration) -> CDnsResult<Box<NetworkTapType<LOC>>>;
87 /*fn new_local_dgram_socket(resolver: Arc<ResolveConfEntry>, timeout: Duration) -> NetworkTap<Self::LocalDatagramSock>;
88 fn new_local_stream_socket(resolver: Arc<ResolveConfEntry>, timeout: Duration) -> NetworkTap<Self::LocalStreamSock>;*/
89}
90
91
92
93
94/// An instance of the socket/stream.
95pub struct NetworkTap<T: AsFd, LOC>
96{
97 /// Channel
98 pub sock: Option<T>,
99 /// Receive timeout
100 pub timeout: Duration,
101 /// Instance's config
102 pub cfg: Arc<ResolveConfEntry>,
103
104 pub _loc: PhantomData<LOC>,
105}
106
107unsafe impl<T: AsFd, LOC> Send for NetworkTap<T, LOC> {}
108unsafe impl<T: AsFd, LOC> Sync for NetworkTap<T, LOC> {}
109
110impl<T: AsFd + Debug, LOC> fmt::Debug for NetworkTap<T, LOC>
111{
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
113 {
114 f.debug_struct("NetworkTap").field("sock", &self.sock).field("timeout", &self.timeout).field("cfg", &self.cfg).finish()
115 }
116}
117
118impl<T: AsFd, LOC> NetworkTap<T, LOC>
119{
120 /// Creates new instance of network which is 'tapped' with [SocketTap].
121 /// A socket is not initially connected and `SocketTap::connect` should be called.
122 ///
123 /// # Arguments
124 ///
125 /// * `resolver_ip` - a ref to [IpAddr] which holds host address of the nameserver.
126 ///
127 /// * `resolver_port` - a port number binded by nameserver
128 ///
129 /// * `bind_addr` - a local address to bind the socket to
130 ///
131 /// # Returns
132 ///
133 /// * [CDnsResult] - Ok with inner type [Box] dyn [SocketTap]
134 ///
135 /// * [CDnsResult] - Err with error description
136 pub
137 fn new(resolver: Arc<ResolveConfEntry>, timeout: Duration) -> CDnsResult<Box<NetworkTapType<LOC>>> where NetworkTap<T, LOC>: SocketTap<LOC> + 'static
138 {
139 /*let domain_name =
140 if let Some(domainname) = resolver.get_tls_domain()
141 {
142 webpki::DnsNameRef::try_from_ascii_str(domainname).map_err(|e| internal_error_map!(CDnsErrorType::InternalError, "{}", e))?
143 //ServerName::try_from(domainname.clone()).map_err(|e| internal_error_map!(CDnsErrorType::InternalError, "{}", e))?
144 }
145 else
146 {
147 internal_error!(CDnsErrorType::InternalError, "no domain is set for TLS conncection");
148 };
149*/
150 let ret =
151 NetworkTap::<T, LOC>
152 {
153 sock: None,
154 // domain_name: domain_name,
155 timeout: timeout,
156 cfg: resolver,
157 _loc: PhantomData
158 };
159
160 return Ok( Box::new(ret) );
161 }
162}
163
164
165impl<T: AsFd, LOC> SocketTapCommon for NetworkTap<T, LOC>
166{
167 fn is_conncected(&self) -> bool
168 {
169 return self.sock.is_some();
170 }
171
172 fn get_fd(&self) -> Option<BorrowedFd>
173 {
174 return self.sock.as_ref().map(|v| v.as_fd());
175 }
176
177 fn get_remote_addr(&self) -> &SocketAddr
178 {
179 return &self.cfg.get_resolver_sa();
180 }
181}
182