gemachain_net_utils/
lib.rs

1//! The `net_utils` module assists with networking
2#![allow(clippy::integer_arithmetic)]
3use {
4    log::*,
5    rand::{thread_rng, Rng},
6    socket2::{Domain, SockAddr, Socket, Type},
7    std::{
8        collections::{BTreeMap, HashSet},
9        io::{self, Read, Write},
10        net::{IpAddr, SocketAddr, TcpListener, TcpStream, ToSocketAddrs, UdpSocket},
11        sync::{mpsc::channel, Arc, RwLock},
12        time::{Duration, Instant},
13    },
14    url::Url,
15};
16
17mod ip_echo_server;
18pub use ip_echo_server::{ip_echo_server, IpEchoServer, MAX_PORT_COUNT_PER_MESSAGE};
19use ip_echo_server::{IpEchoServerMessage, IpEchoServerResponse};
20
21/// A data type representing a public Udp socket
22pub struct UdpSocketPair {
23    pub addr: SocketAddr,    // Public address of the socket
24    pub receiver: UdpSocket, // Locally bound socket that can receive from the public address
25    pub sender: UdpSocket,   // Locally bound socket to send via public address
26}
27
28pub type PortRange = (u16, u16);
29
30pub(crate) const HEADER_LENGTH: usize = 4;
31pub(crate) const IP_ECHO_SERVER_RESPONSE_LENGTH: usize = HEADER_LENGTH + 23;
32
33fn ip_echo_server_request(
34    ip_echo_server_addr: &SocketAddr,
35    msg: IpEchoServerMessage,
36) -> Result<IpEchoServerResponse, String> {
37    let timeout = Duration::new(5, 0);
38    TcpStream::connect_timeout(ip_echo_server_addr, timeout)
39        .and_then(|mut stream| {
40            // Start with HEADER_LENGTH null bytes to avoid looking like an HTTP GET/POST request
41            let mut bytes = vec![0; HEADER_LENGTH];
42
43            bytes.append(&mut bincode::serialize(&msg).expect("serialize IpEchoServerMessage"));
44
45            // End with '\n' to make this request look HTTP-ish and tickle an error response back
46            // from an HTTP server
47            bytes.push(b'\n');
48
49            stream.set_read_timeout(Some(Duration::new(10, 0)))?;
50            stream.write_all(&bytes)?;
51            stream.shutdown(std::net::Shutdown::Write)?;
52            let mut data = vec![0u8; IP_ECHO_SERVER_RESPONSE_LENGTH];
53            let _ = stream.read(&mut data[..])?;
54            Ok(data)
55        })
56        .and_then(|data| {
57            // It's common for users to accidentally confuse the validator's gossip port and JSON
58            // RPC port.  Attempt to detect when this occurs by looking for the standard HTTP
59            // response header and provide the user with a helpful error message
60            if data.len() < HEADER_LENGTH {
61                return Err(io::Error::new(
62                    io::ErrorKind::Other,
63                    format!("Response too short, received {} bytes", data.len()),
64                ));
65            }
66
67            let response_header: String =
68                data[0..HEADER_LENGTH].iter().map(|b| *b as char).collect();
69            if response_header != "\0\0\0\0" {
70                if response_header == "HTTP" {
71                    let http_response = data.iter().map(|b| *b as char).collect::<String>();
72                    return Err(io::Error::new(
73                        io::ErrorKind::Other,
74                        format!(
75                            "Invalid gossip entrypoint. {} looks to be an HTTP port: {}",
76                            ip_echo_server_addr, http_response
77                        ),
78                    ));
79                }
80                return Err(io::Error::new(
81                    io::ErrorKind::Other,
82                    format!(
83                        "Invalid gossip entrypoint. {} provided an invalid response header: '{}'",
84                        ip_echo_server_addr, response_header
85                    ),
86                ));
87            }
88
89            bincode::deserialize(&data[HEADER_LENGTH..]).map_err(|err| {
90                io::Error::new(
91                    io::ErrorKind::Other,
92                    format!("Failed to deserialize: {:?}", err),
93                )
94            })
95        })
96        .map_err(|err| err.to_string())
97}
98
99/// Determine the public IP address of this machine by asking an ip_echo_server at the given
100/// address
101pub fn get_public_ip_addr(ip_echo_server_addr: &SocketAddr) -> Result<IpAddr, String> {
102    let resp = ip_echo_server_request(ip_echo_server_addr, IpEchoServerMessage::default())?;
103    Ok(resp.address)
104}
105
106pub fn get_cluster_shred_version(ip_echo_server_addr: &SocketAddr) -> Result<u16, String> {
107    let resp = ip_echo_server_request(ip_echo_server_addr, IpEchoServerMessage::default())?;
108    resp.shred_version
109        .ok_or_else(|| String::from("IP echo server does not return a shred-version"))
110}
111
112// Checks if any of the provided TCP/UDP ports are not reachable by the machine at
113// `ip_echo_server_addr`
114const DEFAULT_TIMEOUT_SECS: u64 = 5;
115const DEFAULT_RETRY_COUNT: usize = 5;
116
117fn do_verify_reachable_ports(
118    ip_echo_server_addr: &SocketAddr,
119    tcp_listeners: Vec<(u16, TcpListener)>,
120    udp_sockets: &[&UdpSocket],
121    timeout: u64,
122    udp_retry_count: usize,
123) -> bool {
124    info!(
125        "Checking that tcp ports {:?} are reachable from {:?}",
126        tcp_listeners, ip_echo_server_addr
127    );
128
129    let tcp_ports: Vec<_> = tcp_listeners.iter().map(|(port, _)| *port).collect();
130    let _ = ip_echo_server_request(
131        ip_echo_server_addr,
132        IpEchoServerMessage::new(&tcp_ports, &[]),
133    )
134    .map_err(|err| warn!("ip_echo_server request failed: {}", err));
135
136    let mut ok = true;
137    let timeout = Duration::from_secs(timeout);
138
139    // Wait for a connection to open on each TCP port
140    for (port, tcp_listener) in tcp_listeners {
141        let (sender, receiver) = channel();
142        let listening_addr = tcp_listener.local_addr().unwrap();
143        let thread_handle = std::thread::spawn(move || {
144            debug!("Waiting for incoming connection on tcp/{}", port);
145            match tcp_listener.incoming().next() {
146                Some(_) => sender
147                    .send(())
148                    .unwrap_or_else(|err| warn!("send failure: {}", err)),
149                None => warn!("tcp incoming failed"),
150            }
151        });
152        match receiver.recv_timeout(timeout) {
153            Ok(_) => {
154                info!("tcp/{} is reachable", port);
155            }
156            Err(err) => {
157                error!(
158                    "Received no response at tcp/{}, check your port configuration: {}",
159                    port, err
160                );
161                // Ugh, std rustc doesn't provide acceptng with timeout or restoring original
162                // nonblocking-status of sockets because of lack of getter, only the setter...
163                // So, to close the thread cleanly, just connect from here.
164                // ref: https://github.com/rust-lang/rust/issues/31615
165                TcpStream::connect_timeout(&listening_addr, timeout).unwrap();
166                ok = false;
167            }
168        }
169        // ensure to reap the thread
170        thread_handle.join().unwrap();
171    }
172
173    if !ok {
174        // No retries for TCP, abort on the first failure
175        return ok;
176    }
177
178    let mut udp_ports: BTreeMap<_, _> = BTreeMap::new();
179    udp_sockets.iter().for_each(|udp_socket| {
180        let port = udp_socket.local_addr().unwrap().port();
181        udp_ports
182            .entry(port)
183            .or_insert_with(Vec::new)
184            .push(udp_socket);
185    });
186    let udp_ports: Vec<_> = udp_ports.into_iter().collect();
187
188    info!(
189        "Checking that udp ports {:?} are reachable from {:?}",
190        udp_ports.iter().map(|(port, _)| port).collect::<Vec<_>>(),
191        ip_echo_server_addr
192    );
193
194    'outer: for checked_ports_and_sockets in udp_ports.chunks(MAX_PORT_COUNT_PER_MESSAGE) {
195        ok = false;
196
197        for udp_remaining_retry in (0_usize..udp_retry_count).rev() {
198            let (checked_ports, checked_socket_iter) = (
199                checked_ports_and_sockets
200                    .iter()
201                    .map(|(port, _)| *port)
202                    .collect::<Vec<_>>(),
203                checked_ports_and_sockets
204                    .iter()
205                    .flat_map(|(_, sockets)| sockets),
206            );
207
208            let _ = ip_echo_server_request(
209                ip_echo_server_addr,
210                IpEchoServerMessage::new(&[], &checked_ports),
211            )
212            .map_err(|err| warn!("ip_echo_server request failed: {}", err));
213
214            // Spawn threads at once!
215            let reachable_ports = Arc::new(RwLock::new(HashSet::new()));
216            let thread_handles: Vec<_> = checked_socket_iter
217                .map(|udp_socket| {
218                    let port = udp_socket.local_addr().unwrap().port();
219                    let udp_socket = udp_socket.try_clone().expect("Unable to clone udp socket");
220                    let reachable_ports = reachable_ports.clone();
221                    std::thread::spawn(move || {
222                        let start = Instant::now();
223
224                        let original_read_timeout = udp_socket.read_timeout().unwrap();
225                        udp_socket
226                            .set_read_timeout(Some(Duration::from_millis(250)))
227                            .unwrap();
228                        loop {
229                            if reachable_ports.read().unwrap().contains(&port)
230                                || Instant::now().duration_since(start) >= timeout
231                            {
232                                break;
233                            }
234
235                            let recv_result = udp_socket.recv(&mut [0; 1]);
236                            debug!(
237                                "Waited for incoming datagram on udp/{}: {:?}",
238                                port, recv_result
239                            );
240
241                            if recv_result.is_ok() {
242                                reachable_ports.write().unwrap().insert(port);
243                                break;
244                            }
245                        }
246                        udp_socket.set_read_timeout(original_read_timeout).unwrap();
247                    })
248                })
249                .collect();
250
251            // Now join threads!
252            // Separate from the above by collect()-ing as an intermediately step to make the iterator
253            // eager not lazy so that joining happens here at once after creating bunch of threads
254            // at once.
255            for thread in thread_handles {
256                thread.join().unwrap();
257            }
258
259            let reachable_ports = reachable_ports.read().unwrap().clone();
260            if reachable_ports.len() == checked_ports.len() {
261                info!(
262                    "checked udp ports: {:?}, reachable udp ports: {:?}",
263                    checked_ports, reachable_ports
264                );
265                ok = true;
266                break;
267            } else if udp_remaining_retry > 0 {
268                // Might have lost a UDP packet, retry a couple times
269                error!(
270                    "checked udp ports: {:?}, reachable udp ports: {:?}",
271                    checked_ports, reachable_ports
272                );
273                error!("There are some udp ports with no response!! Retrying...");
274            } else {
275                error!("Maximum retry count is reached....");
276                break 'outer;
277            }
278        }
279    }
280
281    ok
282}
283
284pub fn verify_reachable_ports(
285    ip_echo_server_addr: &SocketAddr,
286    tcp_listeners: Vec<(u16, TcpListener)>,
287    udp_sockets: &[&UdpSocket],
288) -> bool {
289    do_verify_reachable_ports(
290        ip_echo_server_addr,
291        tcp_listeners,
292        udp_sockets,
293        DEFAULT_TIMEOUT_SECS,
294        DEFAULT_RETRY_COUNT,
295    )
296}
297
298pub fn parse_port_or_addr(optstr: Option<&str>, default_addr: SocketAddr) -> SocketAddr {
299    if let Some(addrstr) = optstr {
300        if let Ok(port) = addrstr.parse() {
301            let mut addr = default_addr;
302            addr.set_port(port);
303            addr
304        } else if let Ok(addr) = addrstr.parse() {
305            addr
306        } else {
307            default_addr
308        }
309    } else {
310        default_addr
311    }
312}
313
314pub fn parse_port_range(port_range: &str) -> Option<PortRange> {
315    let ports: Vec<&str> = port_range.split('-').collect();
316    if ports.len() != 2 {
317        return None;
318    }
319
320    let start_port = ports[0].parse();
321    let end_port = ports[1].parse();
322
323    if start_port.is_err() || end_port.is_err() {
324        return None;
325    }
326    let start_port = start_port.unwrap();
327    let end_port = end_port.unwrap();
328    if end_port < start_port {
329        return None;
330    }
331    Some((start_port, end_port))
332}
333
334pub fn parse_host(host: &str) -> Result<IpAddr, String> {
335    // First, check if the host syntax is valid. This check is needed because addresses
336    // such as `("localhost:1234", 0)` will resolve to IPs on some networks.
337    let parsed_url = Url::parse(&format!("http://{}", host)).map_err(|e| e.to_string())?;
338    if parsed_url.port().is_some() {
339        return Err(format!("Expected port in URL: {}", host));
340    }
341
342    // Next, check to see if it resolves to an IP address
343    let ips: Vec<_> = (host, 0)
344        .to_socket_addrs()
345        .map_err(|err| err.to_string())?
346        .map(|socket_address| socket_address.ip())
347        .collect();
348    if ips.is_empty() {
349        Err(format!("Unable to resolve host: {}", host))
350    } else {
351        Ok(ips[0])
352    }
353}
354
355pub fn is_host(string: String) -> Result<(), String> {
356    parse_host(&string).map(|_| ())
357}
358
359pub fn parse_host_port(host_port: &str) -> Result<SocketAddr, String> {
360    let addrs: Vec<_> = host_port
361        .to_socket_addrs()
362        .map_err(|err| format!("Unable to resolve host {}: {}", host_port, err))?
363        .collect();
364    if addrs.is_empty() {
365        Err(format!("Unable to resolve host: {}", host_port))
366    } else {
367        Ok(addrs[0])
368    }
369}
370
371pub fn is_host_port(string: String) -> Result<(), String> {
372    parse_host_port(&string).map(|_| ())
373}
374
375#[cfg(windows)]
376fn udp_socket(_reuseaddr: bool) -> io::Result<Socket> {
377    let sock = Socket::new(Domain::IPV4, Type::DGRAM, None)?;
378    Ok(sock)
379}
380
381#[cfg(not(windows))]
382fn udp_socket(reuseaddr: bool) -> io::Result<Socket> {
383    use nix::sys::socket::setsockopt;
384    use nix::sys::socket::sockopt::{ReuseAddr, ReusePort};
385    use std::os::unix::io::AsRawFd;
386
387    let sock = Socket::new(Domain::IPV4, Type::DGRAM, None)?;
388    let sock_fd = sock.as_raw_fd();
389
390    if reuseaddr {
391        // best effort, i.e. ignore errors here, we'll get the failure in caller
392        setsockopt(sock_fd, ReusePort, &true).ok();
393        setsockopt(sock_fd, ReuseAddr, &true).ok();
394    }
395
396    Ok(sock)
397}
398
399// Find a port in the given range that is available for both TCP and UDP
400pub fn bind_common_in_range(
401    ip_addr: IpAddr,
402    range: PortRange,
403) -> io::Result<(u16, (UdpSocket, TcpListener))> {
404    for port in range.0..range.1 {
405        if let Ok((sock, listener)) = bind_common(ip_addr, port, false) {
406            return Result::Ok((sock.local_addr().unwrap().port(), (sock, listener)));
407        }
408    }
409
410    Err(io::Error::new(
411        io::ErrorKind::Other,
412        format!("No available TCP/UDP ports in {:?}", range),
413    ))
414}
415
416pub fn bind_in_range(ip_addr: IpAddr, range: PortRange) -> io::Result<(u16, UdpSocket)> {
417    let sock = udp_socket(false)?;
418
419    for port in range.0..range.1 {
420        let addr = SocketAddr::new(ip_addr, port);
421
422        if sock.bind(&SockAddr::from(addr)).is_ok() {
423            let sock: UdpSocket = sock.into();
424            return Result::Ok((sock.local_addr().unwrap().port(), sock));
425        }
426    }
427
428    Err(io::Error::new(
429        io::ErrorKind::Other,
430        format!("No available UDP ports in {:?}", range),
431    ))
432}
433
434// binds many sockets to the same port in a range
435pub fn multi_bind_in_range(
436    ip_addr: IpAddr,
437    range: PortRange,
438    mut num: usize,
439) -> io::Result<(u16, Vec<UdpSocket>)> {
440    if cfg!(windows) && num != 1 {
441        // See https://github.com/gemacoin/gemachain/issues/4607
442        warn!(
443            "multi_bind_in_range() only supports 1 socket in windows ({} requested)",
444            num
445        );
446        num = 1;
447    }
448    let mut sockets = Vec::with_capacity(num);
449
450    const NUM_TRIES: usize = 100;
451    let mut port = 0;
452    let mut error = None;
453    for _ in 0..NUM_TRIES {
454        port = {
455            let (port, _) = bind_in_range(ip_addr, range)?;
456            port
457        }; // drop the probe, port should be available... briefly.
458
459        for _ in 0..num {
460            let sock = bind_to(ip_addr, port, true);
461            if let Ok(sock) = sock {
462                sockets.push(sock);
463            } else {
464                error = Some(sock);
465                break;
466            }
467        }
468        if sockets.len() == num {
469            break;
470        } else {
471            sockets.clear();
472        }
473    }
474    if sockets.len() != num {
475        error.unwrap()?;
476    }
477    Ok((port, sockets))
478}
479
480pub fn bind_to(ip_addr: IpAddr, port: u16, reuseaddr: bool) -> io::Result<UdpSocket> {
481    let sock = udp_socket(reuseaddr)?;
482
483    let addr = SocketAddr::new(ip_addr, port);
484
485    sock.bind(&SockAddr::from(addr)).map(|_| sock.into())
486}
487
488// binds both a UdpSocket and a TcpListener
489pub fn bind_common(
490    ip_addr: IpAddr,
491    port: u16,
492    reuseaddr: bool,
493) -> io::Result<(UdpSocket, TcpListener)> {
494    let sock = udp_socket(reuseaddr)?;
495
496    let addr = SocketAddr::new(ip_addr, port);
497    let sock_addr = SockAddr::from(addr);
498    sock.bind(&sock_addr)
499        .and_then(|_| TcpListener::bind(&addr).map(|listener| (sock.into(), listener)))
500}
501
502pub fn find_available_port_in_range(ip_addr: IpAddr, range: PortRange) -> io::Result<u16> {
503    let (start, end) = range;
504    let mut tries_left = end - start;
505    let mut rand_port = thread_rng().gen_range(start, end);
506    loop {
507        match bind_common(ip_addr, rand_port, false) {
508            Ok(_) => {
509                break Ok(rand_port);
510            }
511            Err(err) => {
512                if tries_left == 0 {
513                    return Err(err);
514                }
515            }
516        }
517        rand_port += 1;
518        if rand_port == end {
519            rand_port = start;
520        }
521        tries_left -= 1;
522    }
523}
524
525#[cfg(test)]
526mod tests {
527    use super::*;
528    use std::net::Ipv4Addr;
529
530    #[test]
531    fn test_response_length() {
532        let resp = IpEchoServerResponse {
533            address: IpAddr::from([u16::MAX; 8]), // IPv6 variant
534            shred_version: Some(u16::MAX),
535        };
536        let resp_size = bincode::serialized_size(&resp).unwrap();
537        assert_eq!(
538            IP_ECHO_SERVER_RESPONSE_LENGTH,
539            HEADER_LENGTH + resp_size as usize
540        );
541    }
542
543    // Asserts that an old client can parse the response from a new server.
544    #[test]
545    fn test_backward_compat() {
546        let address = IpAddr::from([
547            525u16, 524u16, 523u16, 522u16, 521u16, 520u16, 519u16, 518u16,
548        ]);
549        let response = IpEchoServerResponse {
550            address,
551            shred_version: Some(42),
552        };
553        let mut data = vec![0u8; IP_ECHO_SERVER_RESPONSE_LENGTH];
554        bincode::serialize_into(&mut data[HEADER_LENGTH..], &response).unwrap();
555        data.truncate(HEADER_LENGTH + 20);
556        assert_eq!(
557            bincode::deserialize::<IpAddr>(&data[HEADER_LENGTH..]).unwrap(),
558            address
559        );
560    }
561
562    // Asserts that a new client can parse the response from an old server.
563    #[test]
564    fn test_forward_compat() {
565        let address = IpAddr::from([
566            525u16, 524u16, 523u16, 522u16, 521u16, 520u16, 519u16, 518u16,
567        ]);
568        let mut data = vec![0u8; IP_ECHO_SERVER_RESPONSE_LENGTH];
569        bincode::serialize_into(&mut data[HEADER_LENGTH..], &address).unwrap();
570        let response: Result<IpEchoServerResponse, _> =
571            bincode::deserialize(&data[HEADER_LENGTH..]);
572        assert_eq!(
573            response.unwrap(),
574            IpEchoServerResponse {
575                address,
576                shred_version: None,
577            }
578        );
579    }
580
581    #[test]
582    fn test_parse_port_or_addr() {
583        let p1 = parse_port_or_addr(Some("9000"), SocketAddr::from(([1, 2, 3, 4], 1)));
584        assert_eq!(p1.port(), 9000);
585        let p2 = parse_port_or_addr(Some("127.0.0.1:7000"), SocketAddr::from(([1, 2, 3, 4], 1)));
586        assert_eq!(p2.port(), 7000);
587        let p2 = parse_port_or_addr(Some("hi there"), SocketAddr::from(([1, 2, 3, 4], 1)));
588        assert_eq!(p2.port(), 1);
589        let p3 = parse_port_or_addr(None, SocketAddr::from(([1, 2, 3, 4], 1)));
590        assert_eq!(p3.port(), 1);
591    }
592
593    #[test]
594    fn test_parse_port_range() {
595        assert_eq!(parse_port_range("garbage"), None);
596        assert_eq!(parse_port_range("1-"), None);
597        assert_eq!(parse_port_range("1-2"), Some((1, 2)));
598        assert_eq!(parse_port_range("1-2-3"), None);
599        assert_eq!(parse_port_range("2-1"), None);
600    }
601
602    #[test]
603    fn test_parse_host() {
604        parse_host("localhost:1234").unwrap_err();
605        parse_host("localhost").unwrap();
606        parse_host("127.0.0.0:1234").unwrap_err();
607        parse_host("127.0.0.0").unwrap();
608    }
609
610    #[test]
611    fn test_parse_host_port() {
612        parse_host_port("localhost:1234").unwrap();
613        parse_host_port("localhost").unwrap_err();
614        parse_host_port("127.0.0.0:1234").unwrap();
615        parse_host_port("127.0.0.0").unwrap_err();
616    }
617
618    #[test]
619    fn test_is_host_port() {
620        assert!(is_host_port("localhost:1234".to_string()).is_ok());
621        assert!(is_host_port("localhost".to_string()).is_err());
622    }
623
624    #[test]
625    fn test_bind() {
626        let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
627        assert_eq!(bind_in_range(ip_addr, (2000, 2001)).unwrap().0, 2000);
628        let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
629        let x = bind_to(ip_addr, 2002, true).unwrap();
630        let y = bind_to(ip_addr, 2002, true).unwrap();
631        assert_eq!(
632            x.local_addr().unwrap().port(),
633            y.local_addr().unwrap().port()
634        );
635        bind_to(ip_addr, 2002, false).unwrap_err();
636        bind_in_range(ip_addr, (2002, 2003)).unwrap_err();
637
638        let (port, v) = multi_bind_in_range(ip_addr, (2010, 2110), 10).unwrap();
639        for sock in &v {
640            assert_eq!(port, sock.local_addr().unwrap().port());
641        }
642    }
643
644    #[test]
645    fn test_bind_in_range_nil() {
646        let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
647        bind_in_range(ip_addr, (2000, 2000)).unwrap_err();
648        bind_in_range(ip_addr, (2000, 1999)).unwrap_err();
649    }
650
651    #[test]
652    fn test_find_available_port_in_range() {
653        let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
654        assert_eq!(
655            find_available_port_in_range(ip_addr, (3000, 3001)).unwrap(),
656            3000
657        );
658        let port = find_available_port_in_range(ip_addr, (3000, 3050)).unwrap();
659        assert!((3000..3050).contains(&port));
660
661        let _socket = bind_to(ip_addr, port, false).unwrap();
662        find_available_port_in_range(ip_addr, (port, port + 1)).unwrap_err();
663    }
664
665    #[test]
666    fn test_bind_common_in_range() {
667        let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
668        let (port, _sockets) = bind_common_in_range(ip_addr, (3100, 3150)).unwrap();
669        assert!((3100..3150).contains(&port));
670
671        bind_common_in_range(ip_addr, (port, port + 1)).unwrap_err();
672    }
673
674    #[test]
675    fn test_get_public_ip_addr_none() {
676        gemachain_logger::setup();
677        let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
678        let (_server_port, (server_udp_socket, server_tcp_listener)) =
679            bind_common_in_range(ip_addr, (3200, 3250)).unwrap();
680
681        let _runtime = ip_echo_server(server_tcp_listener, /*shred_version=*/ Some(42));
682
683        let server_ip_echo_addr = server_udp_socket.local_addr().unwrap();
684        assert_eq!(
685            get_public_ip_addr(&server_ip_echo_addr),
686            parse_host("127.0.0.1"),
687        );
688        assert_eq!(get_cluster_shred_version(&server_ip_echo_addr), Ok(42));
689        assert!(verify_reachable_ports(&server_ip_echo_addr, vec![], &[],));
690    }
691
692    #[test]
693    fn test_get_public_ip_addr_reachable() {
694        gemachain_logger::setup();
695        let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
696        let (_server_port, (server_udp_socket, server_tcp_listener)) =
697            bind_common_in_range(ip_addr, (3200, 3250)).unwrap();
698        let (client_port, (client_udp_socket, client_tcp_listener)) =
699            bind_common_in_range(ip_addr, (3200, 3250)).unwrap();
700
701        let _runtime = ip_echo_server(server_tcp_listener, /*shred_version=*/ Some(65535));
702
703        let ip_echo_server_addr = server_udp_socket.local_addr().unwrap();
704        assert_eq!(
705            get_public_ip_addr(&ip_echo_server_addr),
706            parse_host("127.0.0.1"),
707        );
708        assert_eq!(get_cluster_shred_version(&ip_echo_server_addr), Ok(65535));
709        assert!(verify_reachable_ports(
710            &ip_echo_server_addr,
711            vec![(client_port, client_tcp_listener)],
712            &[&client_udp_socket],
713        ));
714    }
715
716    #[test]
717    fn test_get_public_ip_addr_tcp_unreachable() {
718        gemachain_logger::setup();
719        let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
720        let (_server_port, (server_udp_socket, _server_tcp_listener)) =
721            bind_common_in_range(ip_addr, (3200, 3250)).unwrap();
722
723        // make the socket unreachable by not running the ip echo server!
724
725        let server_ip_echo_addr = server_udp_socket.local_addr().unwrap();
726
727        let (correct_client_port, (_client_udp_socket, client_tcp_listener)) =
728            bind_common_in_range(ip_addr, (3200, 3250)).unwrap();
729
730        assert!(!do_verify_reachable_ports(
731            &server_ip_echo_addr,
732            vec![(correct_client_port, client_tcp_listener)],
733            &[],
734            2,
735            3,
736        ));
737    }
738
739    #[test]
740    fn test_get_public_ip_addr_udp_unreachable() {
741        gemachain_logger::setup();
742        let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
743        let (_server_port, (server_udp_socket, _server_tcp_listener)) =
744            bind_common_in_range(ip_addr, (3200, 3250)).unwrap();
745
746        // make the socket unreachable by not running the ip echo server!
747
748        let server_ip_echo_addr = server_udp_socket.local_addr().unwrap();
749
750        let (_correct_client_port, (client_udp_socket, _client_tcp_listener)) =
751            bind_common_in_range(ip_addr, (3200, 3250)).unwrap();
752
753        assert!(!do_verify_reachable_ports(
754            &server_ip_echo_addr,
755            vec![],
756            &[&client_udp_socket],
757            2,
758            3,
759        ));
760    }
761}