cdns_rs/sync/
network.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/// This file contains a networking code.
25
26use std::io::{ErrorKind, Write};
27use std::io::prelude::*;
28use std::net::{IpAddr, SocketAddr};
29use std::net::UdpSocket;
30use std::os::fd::{AsFd, BorrowedFd};
31use std::os::unix::prelude::AsRawFd;
32use std::time::Duration;
33use core::fmt::Debug;
34
35use std::net::TcpStream;
36
37
38
39use socket2::{Socket, Domain, Type, Protocol, SockAddr};
40
41use crate::{internal_error, internal_error_map};
42
43use crate::error::*;
44
45
46/// A common interface to access the realizations for both [TcpStream] and
47/// [UdpSocket]
48pub trait SocketTap
49{
50    /// Connects to the remote host.  
51    fn connect(&mut self, timeout: Option<Duration>) -> CDnsResult<()>;
52
53    /// Tells if current instance is [TcpStream] if true, or [UdpSocket] if false
54    fn is_tcp(&self) -> bool;
55
56    /// Tells if socket/stream is connected to remote host
57    fn is_conncected(&self) -> bool;
58
59    /// Returns the raw fd of the socket.
60    fn get_fd(&self) -> Option<BorrowedFd>;
61
62    /// Returns the remote host Ip and port.
63    fn get_remote_addr(&self) -> &SocketAddr;
64
65    /// Sends data over wire.  
66    fn send(&mut self, sndbuf: &[u8]) -> CDnsResult<usize> ;
67
68    /// Receives data transmitted from remote host.
69    /// In nonblocking mode it should be called only after the event was polled
70    /// In blocking mode it will block until received or timeout.
71    fn recv(&mut self, rcvbuf: &mut [u8]) -> CDnsResult<usize>;
72}
73
74
75impl Debug for dyn SocketTap 
76{
77    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result 
78    {
79        write!(f, "{:?}", self)
80    }
81}
82
83/// An instance of the socket/stream.
84#[derive(Debug)]
85pub struct NetworkTap<T: AsRawFd>
86{
87    /// Channel
88    sock: Option<T>, 
89    /// Nameserver address and port
90    remote_addr: SocketAddr, 
91    /// Local addriss to use to bind socket
92    bind_addr: SocketAddr,
93    /// Connection timeout
94    timeout: Option<Duration>,
95}
96
97impl SocketTap for NetworkTap<UdpSocket>
98{
99    fn connect(&mut self, timeout: Option<Duration>) -> CDnsResult<()>
100    {
101        if self.sock.is_some() == true
102        {
103            // ignore
104            return Ok(());
105        }
106
107        let socket = 
108            UdpSocket::bind(self.bind_addr)
109                .map_err(|e| internal_error_map!(CDnsErrorType::InternalError, "{}", e))?;
110
111        // set mode
112        socket
113            .set_nonblocking(timeout.is_none())
114            .map_err(|e| internal_error_map!(CDnsErrorType::IoError, "{}", e))?;
115
116        // set timeout
117        socket
118            .set_read_timeout(timeout.clone())
119            .map_err(|e| internal_error_map!(CDnsErrorType::IoError, "{}", e))?;
120        
121        self.timeout = timeout;
122
123        socket
124            .connect(&self.remote_addr)
125            .map_err(|e| internal_error_map!(CDnsErrorType::IoError, "{}", e))?;
126
127        self.sock = Some(socket);
128
129        return Ok(());
130    }
131
132    fn is_tcp(&self) -> bool 
133    {
134        return false;
135    }
136
137    fn is_conncected(&self) -> bool 
138    {
139        return self.sock.is_some();
140    }
141
142    fn get_fd(&self) -> Option<BorrowedFd>
143    {
144        return self.sock.as_ref().map(|v| v.as_fd());
145    }
146
147    fn get_remote_addr(&self) -> &SocketAddr
148    {
149        return &self.remote_addr;
150    }
151
152    fn send(&mut self, sndbuf: &[u8]) -> CDnsResult<usize> 
153    {
154        return 
155            self
156                .sock
157                .as_mut()
158                .unwrap()
159                .send(sndbuf)
160                .map_err(|e| internal_error_map!(CDnsErrorType::IoError, "{}", e));
161    }
162
163    fn recv(&mut self, rcvbuf: &mut [u8]) -> CDnsResult<usize> 
164    {
165        let mut retry: u64 = 5;
166        loop
167        {
168            match self.sock.as_mut().unwrap().recv_from(rcvbuf)
169            {
170                Ok((rcv_len, rcv_src)) =>
171                {
172                    // this should not fail because socket is "connected"
173                    if rcv_src != self.remote_addr
174                    {
175                        internal_error!(
176                            CDnsErrorType::DnsResponse, 
177                            "received answer from unknown host: '{}' exp: '{}'", 
178                            self.remote_addr, 
179                            rcv_src
180                        );
181                    }
182
183                    return Ok(rcv_len);
184                },
185                Err(ref e) if e.kind() == ErrorKind::WouldBlock =>
186                {
187                    // timeout
188                    if self.timeout.is_some() == true
189                    {
190                        // blocking mode
191                        internal_error!(CDnsErrorType::RequestTimeout, "request timeout from: '{}'", self.remote_addr); 
192                    }
193                    else
194                    {
195                        // non blocking mode
196                        if retry == 0
197                        {
198                            internal_error!(CDnsErrorType::IoError, "can not receive from: '{}'", self.remote_addr); 
199                        }
200
201                        retry -= 1;
202                        continue;
203                    }
204                },
205                Err(ref e) if e.kind() == ErrorKind::Interrupted =>
206                {
207                    continue;
208                },
209                Err(e) =>
210                {
211                    internal_error!(CDnsErrorType::IoError, "{}", e); 
212                }
213            } // match
214        } // loop
215    }
216}
217
218impl SocketTap for NetworkTap<TcpStream>
219{
220    fn connect(&mut self, timeout: Option<Duration>) -> CDnsResult<()> 
221    {
222        if self.sock.is_some() == true
223        {
224            // ignore
225            return Ok(());
226        }
227
228        // create socket
229        let socket = 
230            Socket::new(Domain::for_address(self.remote_addr), Type::STREAM, Some(Protocol::TCP))
231                .map_err(|e| internal_error_map!(CDnsErrorType::IoError, "{}", e))?;
232
233        // bind socket
234        socket.bind(&SockAddr::from(self.bind_addr))
235            .map_err(|e| internal_error_map!(CDnsErrorType::IoError, "{}", e))?;
236
237        // connect
238        socket.connect(&SockAddr::from(self.remote_addr))
239            .map_err(|e| internal_error_map!(CDnsErrorType::IoError, "{}", e))?;
240
241        // convert to TcpStream
242        let socket: TcpStream = socket.into();
243
244        // set mode
245        socket.set_nonblocking(timeout.is_none())
246            .map_err(|e| internal_error_map!(CDnsErrorType::IoError, "{}", e))?;
247
248        // set timeout
249        socket.set_read_timeout(timeout.clone())
250            .map_err(|e| internal_error_map!(CDnsErrorType::IoError, "{}", e))?;
251        
252        self.timeout = timeout;
253
254        self.sock = Some(socket);
255
256        return Ok(());
257    }
258
259    fn is_tcp(&self) -> bool 
260    {
261        return true;
262    }
263
264    fn is_conncected(&self) -> bool 
265    {
266        return self.sock.is_some();
267    }
268
269    fn get_fd(&self) -> Option<BorrowedFd>
270    {
271        return self.sock.as_ref().map(|v| v.as_fd());
272    }
273
274    fn get_remote_addr(&self) -> &SocketAddr
275    {
276        return &self.remote_addr;
277    }
278
279    fn send(&mut self, sndbuf: &[u8]) -> CDnsResult<usize>  
280    {
281        return 
282            self
283                .sock
284                .as_mut()
285                .unwrap()
286                .write(sndbuf)
287                .map_err(|e| internal_error_map!(CDnsErrorType::IoError, "{}", e));
288    }
289
290    fn recv(&mut self, rcvbuf: &mut [u8]) -> CDnsResult<usize> 
291    {
292        let mut retry: u64 = 5;
293        loop
294        {
295            match self.sock.as_ref().unwrap().read(rcvbuf)
296            {
297                Ok(n) => 
298                {
299                    return Ok(n);
300                },
301                Err(ref e) if e.kind() == ErrorKind::WouldBlock =>
302                {
303                    // timeout
304                    if self.timeout.is_some() == true
305                    {
306                        // blocking mode
307                        internal_error!(CDnsErrorType::RequestTimeout, "request timeout from: '{}'", self.remote_addr); 
308                    }
309                    else
310                    {
311                        // non blocking mode
312                        if retry == 0
313                        {
314                            internal_error!(CDnsErrorType::IoError, "can not receive from: '{}'", self.remote_addr); 
315                        }
316
317                        retry -= 1;
318                        continue;
319                    }
320                },
321                Err(ref e) if e.kind() == ErrorKind::Interrupted =>
322                {
323                    continue;
324                },
325                Err(e) =>
326                {
327                    internal_error!(CDnsErrorType::IoError, "{}", e); 
328                }
329            }
330        }
331    }
332}
333
334impl<T: AsRawFd> NetworkTap<T>
335{
336    
337}
338
339
340/// Creates new instance of [UdpSocket]. 
341/// The socket is not connected and [SocketTap::connect] should be called.
342/// 
343/// # Arguments
344/// 
345/// * `resolver_ip` - a ref to [IpAddr] which holds host address of the nameserver.
346/// 
347/// * `resolver_port` - a port number binded by nameserver
348/// 
349/// * `bind_addr` - a local address to bind the socket to
350/// 
351/// # Returns
352/// 
353/// * [CDnsResult] - Ok with inner type [Box] dyn [SocketTap]
354/// 
355/// * [CDnsResult] - Err with error description
356pub 
357fn new_udp(
358    resolver_ip: &IpAddr, 
359    resolver_port: u16, 
360    bind_addr: &SocketAddr
361) -> CDnsResult<Box<dyn SocketTap>>
362{
363    // setting address and port
364    let remote_dns_host = SocketAddr::from((resolver_ip.clone(), resolver_port));
365
366    let ret = 
367        NetworkTap::<UdpSocket>
368        { 
369            sock: None, 
370            remote_addr: remote_dns_host, 
371            bind_addr: bind_addr.clone(),
372            timeout: None
373        };
374    
375    return Ok( Box::new(ret) );
376}
377
378/// Creates new instance of [TcpStream]. 
379/// The stream is not connected and [SocketTap::connect] should be called.
380/// 
381/// # Arguments
382/// 
383/// * `resolver_ip` - a ref to [IpAddr] which holds host address of the nameserver.
384/// 
385/// * `resolver_port` - a port number binded by nameserver
386/// 
387/// * `bind_addr` - a local address to bind the socket to
388/// 
389/// # Returns
390/// 
391/// * [CDnsResult] - Ok with inner type [Box] dyn [SocketTap]
392/// 
393/// * [CDnsResult] - Err with error description
394pub 
395fn new_tcp(
396    resolver_ip: &IpAddr, 
397    resolver_port: u16, 
398    bind_addr: &SocketAddr
399) -> CDnsResult<Box<dyn SocketTap>>
400{
401    // setting address and port
402    let remote_dns_host = SocketAddr::from((resolver_ip.clone(), resolver_port));
403
404    let ret = 
405        NetworkTap::<TcpStream>
406        { 
407            sock: None, 
408            remote_addr: remote_dns_host,
409            bind_addr: bind_addr.clone(),
410            timeout: None
411        };
412
413    return Ok( Box::new(ret) );
414}
415
416
417
418
419#[test]
420fn test_struct()
421{
422    use super::common::IPV4_BIND_ALL;
423    
424    let ip0: IpAddr = "127.0.0.1".parse().unwrap();
425    let bind =  SocketAddr::from((IPV4_BIND_ALL, 0));
426    let res = new_udp(&ip0, 53, &bind);
427
428    assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
429
430    let _res = res.unwrap();
431}