solana_net_utils/
lib.rs

1//! The `net_utils` module assists with networking
2
3// Activate some of the Rust 2024 lints to make the future migration easier.
4#![warn(if_let_rescope)]
5#![warn(keyword_idents_2024)]
6#![warn(missing_unsafe_on_extern)]
7#![warn(rust_2024_guarded_string_incompatible_syntax)]
8#![warn(rust_2024_incompatible_pat)]
9#![warn(tail_expr_drop_order)]
10#![warn(unsafe_attr_outside_unsafe)]
11#![warn(unsafe_op_in_unsafe_fn)]
12
13mod ip_echo_client;
14mod ip_echo_server;
15pub mod multihomed_sockets;
16pub mod sockets;
17
18#[cfg(feature = "dev-context-only-utils")]
19pub mod tooling_for_tests;
20
21pub use ip_echo_server::{
22    ip_echo_server, IpEchoServer, DEFAULT_IP_ECHO_SERVER_THREADS, MAX_PORT_COUNT_PER_MESSAGE,
23    MINIMUM_IP_ECHO_SERVER_THREADS,
24};
25use {
26    crate::sockets::{udp_socket_with_config, PLATFORM_SUPPORTS_SOCKET_CONFIGS},
27    ip_echo_client::{ip_echo_server_request, ip_echo_server_request_with_binding},
28    ip_echo_server::IpEchoServerMessage,
29    log::*,
30    rand::{thread_rng, Rng},
31    socket2::SockAddr,
32    std::{
33        io::{self},
34        net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener, ToSocketAddrs, UdpSocket},
35    },
36    url::Url,
37};
38
39/// A data type representing a public Udp socket
40pub struct UdpSocketPair {
41    pub addr: SocketAddr,    // Public address of the socket
42    pub receiver: UdpSocket, // Locally bound socket that can receive from the public address
43    pub sender: UdpSocket,   // Locally bound socket to send via public address
44}
45
46pub type PortRange = (u16, u16);
47
48#[cfg(not(debug_assertions))]
49/// Port range available to validator by default
50pub const VALIDATOR_PORT_RANGE: PortRange = (8000, 10_000);
51
52// Sets the port range outside of the region used by other tests to avoid interference
53// This arrangement is not ideal, but can be removed once ConnectionCache is deprecated
54#[cfg(debug_assertions)]
55pub const VALIDATOR_PORT_RANGE: PortRange = (
56    crate::sockets::UNIQUE_ALLOC_BASE_PORT - 512,
57    crate::sockets::UNIQUE_ALLOC_BASE_PORT,
58);
59
60pub const MINIMUM_VALIDATOR_PORT_RANGE_WIDTH: u16 = 25; // VALIDATOR_PORT_RANGE must be at least this wide
61
62pub(crate) const HEADER_LENGTH: usize = 4;
63pub(crate) const IP_ECHO_SERVER_RESPONSE_LENGTH: usize = HEADER_LENGTH + 23;
64
65/// Determine the public IP address of this machine by asking an ip_echo_server at the given
66/// address.
67#[deprecated(
68    since = "3.0.0",
69    note = "Use `get_public_ip_addr_with_binding` instead"
70)]
71pub fn get_public_ip_addr(ip_echo_server_addr: &SocketAddr) -> Result<IpAddr, String> {
72    let fut = ip_echo_server_request(*ip_echo_server_addr, IpEchoServerMessage::default());
73    let rt = tokio::runtime::Builder::new_current_thread()
74        .enable_all()
75        .build()
76        .map_err(|e| e.to_string())?;
77    let resp = rt.block_on(fut).map_err(|e| e.to_string())?;
78    Ok(resp.address)
79}
80
81/// Determine the public IP address of this machine by asking an ip_echo_server at the given
82/// address. This function will bind to the provided bind_addreess.
83pub fn get_public_ip_addr_with_binding(
84    ip_echo_server_addr: &SocketAddr,
85    bind_address: IpAddr,
86) -> anyhow::Result<IpAddr> {
87    let fut = ip_echo_server_request_with_binding(
88        *ip_echo_server_addr,
89        IpEchoServerMessage::default(),
90        bind_address,
91    );
92    let rt = tokio::runtime::Builder::new_current_thread()
93        .enable_all()
94        .build()?;
95    let resp = rt.block_on(fut)?;
96    Ok(resp.address)
97}
98
99/// Retrieves cluster shred version from Entrypoint address provided.
100pub fn get_cluster_shred_version(ip_echo_server_addr: &SocketAddr) -> Result<u16, String> {
101    let fut = ip_echo_server_request(*ip_echo_server_addr, IpEchoServerMessage::default());
102    let rt = tokio::runtime::Builder::new_current_thread()
103        .enable_all()
104        .build()
105        .map_err(|e| e.to_string())?;
106    let resp = rt.block_on(fut).map_err(|e| e.to_string())?;
107    resp.shred_version
108        .ok_or_else(|| "IP echo server does not return a shred-version".to_owned())
109}
110
111/// Retrieves cluster shred version from Entrypoint address provided,
112/// binds client-side socket to the IP provided.
113pub fn get_cluster_shred_version_with_binding(
114    ip_echo_server_addr: &SocketAddr,
115    bind_address: IpAddr,
116) -> anyhow::Result<u16> {
117    let fut = ip_echo_server_request_with_binding(
118        *ip_echo_server_addr,
119        IpEchoServerMessage::default(),
120        bind_address,
121    );
122    let rt = tokio::runtime::Builder::new_current_thread()
123        .enable_all()
124        .build()?;
125    let resp = rt.block_on(fut)?;
126    resp.shred_version
127        .ok_or_else(|| anyhow::anyhow!("IP echo server does not return a shred-version"))
128}
129
130// Limit the maximum number of port verify threads to something reasonable
131// in case the port ranges provided are very large.
132const MAX_PORT_VERIFY_THREADS: usize = 64;
133
134/// Checks if all of the provided UDP ports are reachable by the machine at
135/// `ip_echo_server_addr`. Tests must complete within timeout provided.
136/// Tests will run concurrently when possible, using up to 64 threads for IO.
137/// This function assumes that all sockets are bound to the same IP, and will panic otherwise
138pub fn verify_all_reachable_udp(
139    ip_echo_server_addr: &SocketAddr,
140    udp_sockets: &[&UdpSocket],
141) -> bool {
142    let rt = tokio::runtime::Builder::new_current_thread()
143        .enable_all()
144        .max_blocking_threads(MAX_PORT_VERIFY_THREADS)
145        .build()
146        .expect("Tokio builder should be able to reliably create a current thread runtime");
147    let fut = ip_echo_client::verify_all_reachable_udp(
148        *ip_echo_server_addr,
149        udp_sockets,
150        ip_echo_client::TIMEOUT,
151        ip_echo_client::DEFAULT_RETRY_COUNT,
152    );
153    rt.block_on(fut)
154}
155
156/// Checks if all of the provided TCP ports are reachable by the machine at
157/// `ip_echo_server_addr`. Tests must complete within timeout provided.
158/// Tests will run concurrently when possible, using up to 64 threads for IO.
159/// This function assumes that all sockets are bound to the same IP, and will panic otherwise.
160pub fn verify_all_reachable_tcp(
161    ip_echo_server_addr: &SocketAddr,
162    tcp_listeners: Vec<TcpListener>,
163) -> bool {
164    let rt = tokio::runtime::Builder::new_current_thread()
165        .enable_all()
166        .max_blocking_threads(MAX_PORT_VERIFY_THREADS)
167        .build()
168        .expect("Tokio builder should be able to reliably create a current thread runtime");
169    let fut = ip_echo_client::verify_all_reachable_tcp(
170        *ip_echo_server_addr,
171        tcp_listeners,
172        ip_echo_client::TIMEOUT,
173    );
174    rt.block_on(fut)
175}
176
177pub fn parse_port_or_addr(optstr: Option<&str>, default_addr: SocketAddr) -> SocketAddr {
178    if let Some(addrstr) = optstr {
179        if let Ok(port) = addrstr.parse() {
180            let mut addr = default_addr;
181            addr.set_port(port);
182            addr
183        } else if let Ok(addr) = addrstr.parse() {
184            addr
185        } else {
186            default_addr
187        }
188    } else {
189        default_addr
190    }
191}
192
193pub fn parse_port_range(port_range: &str) -> Option<PortRange> {
194    let ports: Vec<&str> = port_range.split('-').collect();
195    if ports.len() != 2 {
196        return None;
197    }
198
199    let start_port = ports[0].parse();
200    let end_port = ports[1].parse();
201
202    if start_port.is_err() || end_port.is_err() {
203        return None;
204    }
205    let start_port = start_port.unwrap();
206    let end_port = end_port.unwrap();
207    if end_port < start_port {
208        return None;
209    }
210    Some((start_port, end_port))
211}
212
213pub fn parse_host(host: &str) -> Result<IpAddr, String> {
214    // First, check if the host syntax is valid. This check is needed because addresses
215    // such as `("localhost:1234", 0)` will resolve to IPs on some networks.
216    let parsed_url = Url::parse(&format!("http://{host}")).map_err(|e| e.to_string())?;
217    if parsed_url.port().is_some() {
218        return Err(format!("Expected port in URL: {host}"));
219    }
220
221    // Next, check to see if it resolves to an IP address
222    let ips: Vec<_> = (host, 0)
223        .to_socket_addrs()
224        .map_err(|err| err.to_string())?
225        .map(|socket_address| socket_address.ip())
226        .collect();
227    if ips.is_empty() {
228        Err(format!("Unable to resolve host: {host}"))
229    } else {
230        Ok(ips[0])
231    }
232}
233
234pub fn is_host(string: String) -> Result<(), String> {
235    parse_host(&string).map(|_| ())
236}
237
238pub fn parse_host_port(host_port: &str) -> Result<SocketAddr, String> {
239    let addrs: Vec<_> = host_port
240        .to_socket_addrs()
241        .map_err(|err| format!("Unable to resolve host {host_port}: {err}"))?
242        .collect();
243    if addrs.is_empty() {
244        Err(format!("Unable to resolve host: {host_port}"))
245    } else {
246        Ok(addrs[0])
247    }
248}
249
250pub fn is_host_port(string: String) -> Result<(), String> {
251    parse_host_port(&string).map(|_| ())
252}
253
254#[deprecated(
255    since = "3.0.0",
256    note = "Please use the equivalent struct from solana-net-utils::sockets"
257)]
258#[derive(Clone, Copy, Debug, Default)]
259pub struct SocketConfig {
260    reuseport: bool,
261    recv_buffer_size: Option<usize>,
262    send_buffer_size: Option<usize>,
263}
264
265#[allow(deprecated)]
266impl SocketConfig {
267    pub fn reuseport(mut self, reuseport: bool) -> Self {
268        self.reuseport = reuseport;
269        self
270    }
271
272    /// Sets the receive buffer size for the socket (no effect on windows/ios).
273    ///
274    /// **Note:** On Linux the kernel will double the value you specify.
275    /// For example, if you specify `16MB`, the kernel will configure the
276    /// socket to use `32MB`.
277    /// See: https://man7.org/linux/man-pages/man7/socket.7.html: SO_RCVBUF
278    pub fn recv_buffer_size(mut self, size: usize) -> Self {
279        self.recv_buffer_size = Some(size);
280        self
281    }
282
283    /// Sets the send buffer size for the socket (no effect on windows/ios)
284    ///
285    /// **Note:** On Linux the kernel will double the value you specify.
286    /// For example, if you specify `16MB`, the kernel will configure the
287    /// socket to use `32MB`.
288    /// See: https://man7.org/linux/man-pages/man7/socket.7.html: SO_SNDBUF
289    pub fn send_buffer_size(mut self, size: usize) -> Self {
290        self.send_buffer_size = Some(size);
291        self
292    }
293}
294
295#[deprecated(
296    since = "3.0.0",
297    note = "Please use the equivalent from solana-net-utils::sockets"
298)]
299#[allow(deprecated)]
300/// Find a port in the given range with a socket config that is available for both TCP and UDP
301pub fn bind_common_in_range_with_config(
302    ip_addr: IpAddr,
303    range: PortRange,
304    config: SocketConfig,
305) -> io::Result<(u16, (UdpSocket, TcpListener))> {
306    for port in range.0..range.1 {
307        if let Ok((sock, listener)) = bind_common_with_config(ip_addr, port, config) {
308            return Result::Ok((sock.local_addr().unwrap().port(), (sock, listener)));
309        }
310    }
311
312    Err(io::Error::other(format!(
313        "No available TCP/UDP ports in {range:?}"
314    )))
315}
316
317pub fn bind_in_range(ip_addr: IpAddr, range: PortRange) -> io::Result<(u16, UdpSocket)> {
318    let config = sockets::SocketConfiguration::default();
319    sockets::bind_in_range_with_config(ip_addr, range, config)
320}
321
322#[deprecated(
323    since = "3.0.0",
324    note = "Please use the equivalent from solana-net-utils::sockets"
325)]
326#[allow(deprecated)]
327pub fn bind_in_range_with_config(
328    ip_addr: IpAddr,
329    range: PortRange,
330    config: SocketConfig,
331) -> io::Result<(u16, UdpSocket)> {
332    let socket = udp_socket_with_config(config.into())?;
333
334    for port in range.0..range.1 {
335        let addr = SocketAddr::new(ip_addr, port);
336
337        if socket.bind(&SockAddr::from(addr)).is_ok() {
338            let udp_socket: UdpSocket = socket.into();
339            return Result::Ok((udp_socket.local_addr().unwrap().port(), udp_socket));
340        }
341    }
342
343    Err(io::Error::other(format!(
344        "No available UDP ports in {range:?}"
345    )))
346}
347
348#[deprecated(
349    since = "3.0.0",
350    note = "Please use the equivalent from solana-net-utils::sockets"
351)]
352#[allow(deprecated)]
353pub fn bind_with_any_port_with_config(
354    ip_addr: IpAddr,
355    config: SocketConfig,
356) -> io::Result<UdpSocket> {
357    let sock = udp_socket_with_config(config.into())?;
358    let addr = SocketAddr::new(ip_addr, 0);
359    let bind = sock.bind(&SockAddr::from(addr));
360    match bind {
361        Ok(_) => Result::Ok(sock.into()),
362        Err(err) => Err(io::Error::other(format!("No available UDP port: {err}"))),
363    }
364}
365
366#[deprecated(
367    since = "3.0.0",
368    note = "Please use the equivalent from solana-net-utils::sockets"
369)]
370#[allow(deprecated)]
371/// binds num sockets to the same port in a range with config
372pub fn multi_bind_in_range_with_config(
373    ip_addr: IpAddr,
374    range: PortRange,
375    config: SocketConfig,
376    mut num: usize,
377) -> io::Result<(u16, Vec<UdpSocket>)> {
378    if !PLATFORM_SUPPORTS_SOCKET_CONFIGS && num != 1 {
379        // See https://github.com/solana-labs/solana/issues/4607
380        warn!(
381            "multi_bind_in_range_with_config() only supports 1 socket on this platform ({num} \
382             requested)"
383        );
384        num = 1;
385    }
386    let (port, socket) = bind_in_range_with_config(ip_addr, range, config)?;
387    let sockets = bind_more_with_config(socket, num, config)?;
388    Ok((port, sockets))
389}
390
391#[deprecated(
392    since = "3.0.0",
393    note = "Please use the eqiuvalent from solana-net-utils::sockets"
394)]
395#[allow(deprecated)]
396pub fn bind_to(ip_addr: IpAddr, port: u16, reuseport: bool) -> io::Result<UdpSocket> {
397    let config = SocketConfig {
398        reuseport,
399        ..Default::default()
400    };
401    bind_to_with_config(ip_addr, port, config)
402}
403
404pub fn bind_to_localhost() -> io::Result<UdpSocket> {
405    let config = sockets::SocketConfiguration::default();
406    sockets::bind_to_with_config(IpAddr::V4(Ipv4Addr::LOCALHOST), 0, config)
407}
408
409pub fn bind_to_unspecified() -> io::Result<UdpSocket> {
410    let config = sockets::SocketConfiguration::default();
411    sockets::bind_to_with_config(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0, config)
412}
413
414#[deprecated(
415    since = "3.0.0",
416    note = "Please avoid this function in favor of sockets::bind_to_with_config"
417)]
418#[allow(deprecated)]
419pub fn bind_to_with_config(
420    ip_addr: IpAddr,
421    port: u16,
422    config: SocketConfig,
423) -> io::Result<UdpSocket> {
424    let sock = udp_socket_with_config(config.into())?;
425    let addr = SocketAddr::new(ip_addr, port);
426    sock.bind(&SockAddr::from(addr)).map(|_| sock.into())
427}
428
429#[deprecated(
430    since = "3.0.0",
431    note = "Please avoid this function, it is easy to misuse"
432)]
433#[allow(deprecated)]
434pub fn bind_to_with_config_non_blocking(
435    ip_addr: IpAddr,
436    port: u16,
437    config: SocketConfig,
438) -> io::Result<UdpSocket> {
439    let sock = udp_socket_with_config(config.into())?;
440
441    let addr = SocketAddr::new(ip_addr, port);
442
443    sock.bind(&SockAddr::from(addr))?;
444    sock.set_nonblocking(true)?;
445    Ok(sock.into())
446}
447
448#[deprecated(
449    since = "3.0.0",
450    note = "Please avoid this function in favor of sockets::bind_common_with_config"
451)]
452/// binds both a UdpSocket and a TcpListener
453pub fn bind_common(ip_addr: IpAddr, port: u16) -> io::Result<(UdpSocket, TcpListener)> {
454    let config = sockets::SocketConfiguration::default();
455    sockets::bind_common_with_config(ip_addr, port, config)
456}
457
458#[deprecated(
459    since = "3.0.0",
460    note = "Please avoid this function in favor of sockets::bind_common_with_config"
461)]
462#[allow(deprecated)]
463/// binds both a UdpSocket and a TcpListener on the same port
464pub fn bind_common_with_config(
465    ip_addr: IpAddr,
466    port: u16,
467    config: SocketConfig,
468) -> io::Result<(UdpSocket, TcpListener)> {
469    let sock = udp_socket_with_config(config.into())?;
470
471    let addr = SocketAddr::new(ip_addr, port);
472    let sock_addr = SockAddr::from(addr);
473    sock.bind(&sock_addr)
474        .and_then(|_| TcpListener::bind(addr).map(|listener| (sock.into(), listener)))
475}
476
477#[deprecated(
478    since = "3.0.0",
479    note = "Please avoid this function, in favor of \
480            sockets::bind_two_in_range_with_offset_and_config"
481)]
482#[allow(deprecated)]
483pub fn bind_two_in_range_with_offset(
484    ip_addr: IpAddr,
485    range: PortRange,
486    offset: u16,
487) -> io::Result<((u16, UdpSocket), (u16, UdpSocket))> {
488    let sock_config = sockets::SocketConfiguration::default();
489    sockets::bind_two_in_range_with_offset_and_config(
490        ip_addr,
491        range,
492        offset,
493        sock_config,
494        sock_config,
495    )
496}
497
498#[deprecated(
499    since = "3.0.0",
500    note = "Please avoid this function, in favor of \
501            sockets::bind_two_in_range_with_offset_and_config"
502)]
503#[allow(deprecated)]
504pub fn bind_two_in_range_with_offset_and_config(
505    ip_addr: IpAddr,
506    range: PortRange,
507    offset: u16,
508    sock1_config: SocketConfig,
509    sock2_config: SocketConfig,
510) -> io::Result<((u16, UdpSocket), (u16, UdpSocket))> {
511    if range.1.saturating_sub(range.0) < offset {
512        return Err(io::Error::other(
513            "range too small to find two ports with the correct offset".to_string(),
514        ));
515    }
516
517    for port in range.0..range.1 {
518        let first_bind = bind_to_with_config(ip_addr, port, sock1_config);
519        if let Ok(first_bind) = first_bind {
520            if range.1.saturating_sub(port) >= offset {
521                let second_bind =
522                    bind_to_with_config(ip_addr, port.saturating_add(offset), sock2_config);
523                if let Ok(second_bind) = second_bind {
524                    return Ok((
525                        (first_bind.local_addr().unwrap().port(), first_bind),
526                        (second_bind.local_addr().unwrap().port(), second_bind),
527                    ));
528                }
529            } else {
530                break;
531            }
532        }
533    }
534    Err(io::Error::other(
535        "couldn't find two ports with the correct offset in range".to_string(),
536    ))
537}
538
539/// Searches for an open port on a given binding ip_addr in the provided range.
540///
541/// This will start at a random point in the range provided, and search sequenctially.
542/// If it can not find anything, an Error is returned.
543///
544/// Keep in mind this will not reserve the port for you, only find one that is empty.
545pub fn find_available_port_in_range(ip_addr: IpAddr, range: PortRange) -> io::Result<u16> {
546    let [port] = find_available_ports_in_range(ip_addr, range)?;
547    Ok(port)
548}
549
550/// Searches for several ports on a given binding ip_addr in the provided range.
551///
552/// This will start at a random point in the range provided, and search sequentially.
553/// If it can not find anything, an Error is returned.
554pub fn find_available_ports_in_range<const N: usize>(
555    ip_addr: IpAddr,
556    range: PortRange,
557) -> io::Result<[u16; N]> {
558    let mut result = [0u16; N];
559    let range = range.0..range.1;
560    let mut next_port_to_try = range
561        .clone()
562        .cycle() // loop over the end of the range
563        .skip(thread_rng().gen_range(range.clone()) as usize) // skip to random position
564        .take(range.len()) // never take the same value twice
565        .peekable();
566    let mut num = 0;
567    let config = sockets::SocketConfiguration::default();
568    while num < N {
569        let port_to_try = next_port_to_try.next().unwrap(); // this unwrap never fails since we exit earlier
570        let bind = sockets::bind_common_with_config(ip_addr, port_to_try, config);
571        match bind {
572            Ok(_) => {
573                result[num] = port_to_try;
574                num = num.saturating_add(1);
575            }
576            Err(err) => {
577                if next_port_to_try.peek().is_none() {
578                    return Err(err);
579                }
580            }
581        }
582    }
583    Ok(result)
584}
585
586#[deprecated(
587    since = "3.0.0",
588    note = "Please avoid this function, in favor of sockets::bind_more_with_config"
589)]
590#[allow(deprecated)]
591pub fn bind_more_with_config(
592    socket: UdpSocket,
593    num: usize,
594    config: SocketConfig,
595) -> io::Result<Vec<UdpSocket>> {
596    if !PLATFORM_SUPPORTS_SOCKET_CONFIGS {
597        if num > 1 {
598            warn!(
599                "bind_more_with_config() only supports 1 socket on this platform ({num} requested)"
600            );
601        }
602        Ok(vec![socket])
603    } else {
604        let addr = socket.local_addr().unwrap();
605        let ip = addr.ip();
606        let port = addr.port();
607        std::iter::once(Ok(socket))
608            .chain((1..num).map(|_| bind_to_with_config(ip, port, config)))
609            .collect()
610    }
611}
612
613#[cfg(test)]
614#[allow(deprecated)]
615mod tests {
616    use {
617        super::*,
618        crate::sockets::unique_port_range_for_tests,
619        ip_echo_server::IpEchoServerResponse,
620        itertools::Itertools,
621        std::{net::Ipv4Addr, time::Duration},
622        tokio::runtime::Runtime,
623    };
624
625    fn runtime() -> Runtime {
626        tokio::runtime::Builder::new_current_thread()
627            .enable_all()
628            .build()
629            .expect("Can not create a runtime")
630    }
631    #[test]
632    fn test_response_length() {
633        let resp = IpEchoServerResponse {
634            address: IpAddr::from([u16::MAX; 8]), // IPv6 variant
635            shred_version: Some(u16::MAX),
636        };
637        let resp_size = bincode::serialized_size(&resp).unwrap();
638        assert_eq!(
639            IP_ECHO_SERVER_RESPONSE_LENGTH,
640            HEADER_LENGTH + resp_size as usize
641        );
642    }
643
644    // Asserts that an old client can parse the response from a new server.
645    #[test]
646    fn test_backward_compat() {
647        let address = IpAddr::from([
648            525u16, 524u16, 523u16, 522u16, 521u16, 520u16, 519u16, 518u16,
649        ]);
650        let response = IpEchoServerResponse {
651            address,
652            shred_version: Some(42),
653        };
654        let mut data = vec![0u8; IP_ECHO_SERVER_RESPONSE_LENGTH];
655        bincode::serialize_into(&mut data[HEADER_LENGTH..], &response).unwrap();
656        data.truncate(HEADER_LENGTH + 20);
657        assert_eq!(
658            bincode::deserialize::<IpAddr>(&data[HEADER_LENGTH..]).unwrap(),
659            address
660        );
661    }
662
663    // Asserts that a new client can parse the response from an old server.
664    #[test]
665    fn test_forward_compat() {
666        let address = IpAddr::from([
667            525u16, 524u16, 523u16, 522u16, 521u16, 520u16, 519u16, 518u16,
668        ]);
669        let mut data = [0u8; IP_ECHO_SERVER_RESPONSE_LENGTH];
670        bincode::serialize_into(&mut data[HEADER_LENGTH..], &address).unwrap();
671        let response: Result<IpEchoServerResponse, _> =
672            bincode::deserialize(&data[HEADER_LENGTH..]);
673        assert_eq!(
674            response.unwrap(),
675            IpEchoServerResponse {
676                address,
677                shred_version: None,
678            }
679        );
680    }
681
682    #[test]
683    fn test_parse_port_or_addr() {
684        let p1 = parse_port_or_addr(Some("9000"), SocketAddr::from(([1, 2, 3, 4], 1)));
685        assert_eq!(p1.port(), 9000);
686        let p2 = parse_port_or_addr(Some("127.0.0.1:7000"), SocketAddr::from(([1, 2, 3, 4], 1)));
687        assert_eq!(p2.port(), 7000);
688        let p2 = parse_port_or_addr(Some("hi there"), SocketAddr::from(([1, 2, 3, 4], 1)));
689        assert_eq!(p2.port(), 1);
690        let p3 = parse_port_or_addr(None, SocketAddr::from(([1, 2, 3, 4], 1)));
691        assert_eq!(p3.port(), 1);
692    }
693
694    #[test]
695    fn test_parse_port_range() {
696        assert_eq!(parse_port_range("garbage"), None);
697        assert_eq!(parse_port_range("1-"), None);
698        assert_eq!(parse_port_range("1-2"), Some((1, 2)));
699        assert_eq!(parse_port_range("1-2-3"), None);
700        assert_eq!(parse_port_range("2-1"), None);
701    }
702
703    #[test]
704    fn test_parse_host() {
705        parse_host("localhost:1234").unwrap_err();
706        parse_host("localhost").unwrap();
707        parse_host("127.0.0.0:1234").unwrap_err();
708        parse_host("127.0.0.0").unwrap();
709    }
710
711    #[test]
712    fn test_parse_host_port() {
713        parse_host_port("localhost:1234").unwrap();
714        parse_host_port("localhost").unwrap_err();
715        parse_host_port("127.0.0.0:1234").unwrap();
716        parse_host_port("127.0.0.0").unwrap_err();
717    }
718
719    #[test]
720    fn test_is_host_port() {
721        assert!(is_host_port("localhost:1234".to_string()).is_ok());
722        assert!(is_host_port("localhost".to_string()).is_err());
723    }
724
725    #[test]
726    fn test_bind() {
727        let (pr_s, pr_e) = sockets::localhost_port_range_for_tests();
728        let ip_addr = IpAddr::V4(Ipv4Addr::UNSPECIFIED);
729        let s = bind_in_range(ip_addr, (pr_s, pr_e)).unwrap();
730        assert_eq!(s.0, pr_s, "bind_in_range should use first available port");
731        let ip_addr = IpAddr::V4(Ipv4Addr::UNSPECIFIED);
732        let config = SocketConfig::default().reuseport(true);
733        let x = bind_to_with_config(ip_addr, pr_s + 1, config).unwrap();
734        let y = bind_to_with_config(ip_addr, pr_s + 1, config).unwrap();
735        assert_eq!(
736            x.local_addr().unwrap().port(),
737            y.local_addr().unwrap().port()
738        );
739        bind_to(ip_addr, pr_s, false).unwrap_err();
740        bind_in_range(ip_addr, (pr_s, pr_s + 2)).unwrap_err();
741
742        let (port, v) =
743            multi_bind_in_range_with_config(ip_addr, (pr_s + 5, pr_e), config, 10).unwrap();
744        for sock in &v {
745            assert_eq!(port, sock.local_addr().unwrap().port());
746        }
747    }
748
749    #[test]
750    fn test_bind_with_any_port() {
751        let ip_addr = IpAddr::V4(Ipv4Addr::UNSPECIFIED);
752        let config = SocketConfig::default();
753        let x = bind_with_any_port_with_config(ip_addr, config).unwrap();
754        let y = bind_with_any_port_with_config(ip_addr, config).unwrap();
755        assert_ne!(
756            x.local_addr().unwrap().port(),
757            y.local_addr().unwrap().port()
758        );
759    }
760
761    #[test]
762    fn test_bind_in_range_nil() {
763        let ip_addr = IpAddr::V4(Ipv4Addr::UNSPECIFIED);
764        let range = sockets::unique_port_range_for_tests(2);
765        bind_in_range(ip_addr, (range.end, range.end)).unwrap_err();
766        bind_in_range(ip_addr, (range.end, range.start)).unwrap_err();
767    }
768
769    #[test]
770    fn test_find_available_port_in_range() {
771        let ip_addr = IpAddr::V4(Ipv4Addr::LOCALHOST);
772        let range = sockets::unique_port_range_for_tests(4);
773        let (pr_s, pr_e) = (range.start, range.end);
774        assert_eq!(
775            find_available_port_in_range(ip_addr, (pr_s, pr_s + 1)).unwrap(),
776            pr_s
777        );
778        let port = find_available_port_in_range(ip_addr, (pr_s, pr_e)).unwrap();
779        assert!((pr_s..pr_e).contains(&port));
780
781        let _socket = bind_to(ip_addr, port, false).unwrap();
782        find_available_port_in_range(ip_addr, (port, port + 1)).unwrap_err();
783    }
784
785    #[test]
786    fn test_find_available_ports_in_range() {
787        let ip_addr = IpAddr::V4(Ipv4Addr::LOCALHOST);
788        let port_range = sockets::localhost_port_range_for_tests();
789        assert!(port_range.1 - port_range.0 > 16);
790        // reserve 1 port to make it non-trivial
791        let sock = bind_to_with_config(ip_addr, port_range.0 + 2, SocketConfig::default()).unwrap();
792        let ports: [u16; 15] = find_available_ports_in_range(ip_addr, port_range).unwrap();
793        let mut ports_vec = Vec::from(ports);
794        ports_vec.push(sock.local_addr().unwrap().port());
795        let res: Vec<_> = ports_vec.into_iter().unique().collect();
796        assert_eq!(res.len(), 16, "Should reserve 16 unique ports");
797    }
798
799    #[test]
800    fn test_bind_common_in_range() {
801        let ip_addr = IpAddr::V4(Ipv4Addr::LOCALHOST);
802        let range = sockets::unique_port_range_for_tests(5);
803        let config = SocketConfig::default();
804        let (port, _sockets) =
805            bind_common_in_range_with_config(ip_addr, (range.start, range.end), config).unwrap();
806        assert!(range.contains(&port));
807        bind_common_in_range_with_config(ip_addr, (port, port + 1), config).unwrap_err();
808    }
809
810    #[test]
811    fn test_get_public_ip_addr_none() {
812        solana_logger::setup();
813        let ip_addr = IpAddr::V4(Ipv4Addr::LOCALHOST);
814        let (pr_s, pr_e) = sockets::localhost_port_range_for_tests();
815        let config = SocketConfig::default();
816        let (_server_port, (server_udp_socket, server_tcp_listener)) =
817            bind_common_in_range_with_config(ip_addr, (pr_s, pr_e), config).unwrap();
818
819        let _runtime = ip_echo_server(
820            server_tcp_listener,
821            DEFAULT_IP_ECHO_SERVER_THREADS,
822            /*shred_version=*/ Some(42),
823        );
824
825        let server_ip_echo_addr = server_udp_socket.local_addr().unwrap();
826        assert_eq!(
827            get_public_ip_addr_with_binding(
828                &server_ip_echo_addr,
829                IpAddr::V4(Ipv4Addr::UNSPECIFIED)
830            )
831            .unwrap(),
832            parse_host("127.0.0.1").unwrap(),
833        );
834        assert_eq!(get_cluster_shred_version(&server_ip_echo_addr).unwrap(), 42);
835        assert!(verify_all_reachable_tcp(&server_ip_echo_addr, vec![],));
836        assert!(verify_all_reachable_udp(&server_ip_echo_addr, &[],));
837    }
838
839    #[test]
840    fn test_get_public_ip_addr_reachable() {
841        solana_logger::setup();
842        let ip_addr = IpAddr::V4(Ipv4Addr::LOCALHOST);
843        let port_range = sockets::localhost_port_range_for_tests();
844        let config = SocketConfig::default();
845        let (_server_port, (server_udp_socket, server_tcp_listener)) =
846            bind_common_in_range_with_config(ip_addr, port_range, config).unwrap();
847        let (_client_port, (client_udp_socket, client_tcp_listener)) =
848            bind_common_in_range_with_config(ip_addr, port_range, config).unwrap();
849
850        let _runtime = ip_echo_server(
851            server_tcp_listener,
852            DEFAULT_IP_ECHO_SERVER_THREADS,
853            /*shred_version=*/ Some(65535),
854        );
855
856        let ip_echo_server_addr = server_udp_socket.local_addr().unwrap();
857        assert_eq!(
858            get_public_ip_addr_with_binding(
859                &ip_echo_server_addr,
860                IpAddr::V4(Ipv4Addr::UNSPECIFIED)
861            )
862            .unwrap(),
863            parse_host("127.0.0.1").unwrap(),
864        );
865        assert_eq!(
866            get_cluster_shred_version(&ip_echo_server_addr).unwrap(),
867            65535
868        );
869        assert!(verify_all_reachable_tcp(
870            &ip_echo_server_addr,
871            vec![client_tcp_listener],
872        ));
873        assert!(verify_all_reachable_udp(
874            &ip_echo_server_addr,
875            &[&client_udp_socket],
876        ));
877    }
878
879    #[test]
880    fn test_verify_ports_tcp_unreachable() {
881        solana_logger::setup();
882        let ip_addr = IpAddr::V4(Ipv4Addr::LOCALHOST);
883        let port_range = sockets::localhost_port_range_for_tests();
884        let config = SocketConfig::default();
885        let (_server_port, (server_udp_socket, _server_tcp_listener)) =
886            bind_common_in_range_with_config(ip_addr, port_range, config).unwrap();
887
888        // make the socket unreachable by not running the ip echo server!
889        let server_ip_echo_addr = server_udp_socket.local_addr().unwrap();
890
891        let (_, (_client_udp_socket, client_tcp_listener)) =
892            bind_common_in_range_with_config(ip_addr, port_range, config).unwrap();
893
894        let rt = runtime();
895        assert!(!rt.block_on(ip_echo_client::verify_all_reachable_tcp(
896            server_ip_echo_addr,
897            vec![client_tcp_listener],
898            Duration::from_secs(2),
899        )));
900    }
901
902    #[test]
903    fn test_verify_ports_udp_unreachable() {
904        solana_logger::setup();
905        let ip_addr = IpAddr::V4(Ipv4Addr::LOCALHOST);
906        let port_range = unique_port_range_for_tests(2);
907        let config = SocketConfig::default();
908        let (_server_port, (server_udp_socket, _server_tcp_listener)) =
909            bind_common_in_range_with_config(ip_addr, (port_range.start, port_range.end), config)
910                .unwrap();
911
912        // make the socket unreachable by not running the ip echo server!
913        let server_ip_echo_addr = server_udp_socket.local_addr().unwrap();
914
915        let (_correct_client_port, (client_udp_socket, _client_tcp_listener)) =
916            bind_common_in_range_with_config(ip_addr, (port_range.start, port_range.end), config)
917                .unwrap();
918
919        let rt = runtime();
920        assert!(!rt.block_on(ip_echo_client::verify_all_reachable_udp(
921            server_ip_echo_addr,
922            &[&client_udp_socket],
923            Duration::from_secs(2),
924            3,
925        )));
926    }
927
928    #[test]
929    fn test_verify_many_ports_reachable() {
930        solana_logger::setup();
931        let ip_addr = IpAddr::V4(Ipv4Addr::LOCALHOST);
932        let config = SocketConfig::default();
933        let mut tcp_listeners = vec![];
934        let mut udp_sockets = vec![];
935
936        let port_range = unique_port_range_for_tests(1);
937        let (_server_port, (_, server_tcp_listener)) =
938            bind_common_in_range_with_config(ip_addr, (port_range.start, port_range.end), config)
939                .unwrap();
940        for _ in 0..MAX_PORT_VERIFY_THREADS * 2 {
941            let port_range = unique_port_range_for_tests(1);
942            let (_client_port, (client_udp_socket, client_tcp_listener)) =
943                bind_common_in_range_with_config(
944                    ip_addr,
945                    (port_range.start, port_range.end),
946                    config,
947                )
948                .unwrap();
949            tcp_listeners.push(client_tcp_listener);
950            udp_sockets.push(client_udp_socket);
951        }
952
953        let ip_echo_server_addr = server_tcp_listener.local_addr().unwrap();
954
955        let _runtime = ip_echo_server(
956            server_tcp_listener,
957            DEFAULT_IP_ECHO_SERVER_THREADS,
958            Some(65535),
959        );
960
961        assert_eq!(
962            get_public_ip_addr_with_binding(
963                &ip_echo_server_addr,
964                IpAddr::V4(Ipv4Addr::UNSPECIFIED)
965            )
966            .unwrap(),
967            parse_host("127.0.0.1").unwrap(),
968        );
969
970        let socket_refs = udp_sockets.iter().collect_vec();
971        assert!(verify_all_reachable_tcp(
972            &ip_echo_server_addr,
973            tcp_listeners,
974        ));
975        assert!(verify_all_reachable_udp(&ip_echo_server_addr, &socket_refs));
976    }
977
978    #[test]
979    fn test_bind_two_in_range_with_offset() {
980        solana_logger::setup();
981        let ip_addr = IpAddr::V4(Ipv4Addr::UNSPECIFIED);
982        let offset = 6;
983        let port_range = unique_port_range_for_tests(10);
984        if let Ok(((port1, _), (port2, _))) =
985            bind_two_in_range_with_offset(ip_addr, (port_range.start, port_range.end), offset)
986        {
987            assert!(port2 == port1 + offset);
988        }
989        let offset = 7;
990        if let Ok(((port1, _), (port2, _))) =
991            bind_two_in_range_with_offset(ip_addr, (port_range.start, port_range.end), offset)
992        {
993            assert!(port2 == port1 + offset);
994        }
995        assert!(bind_two_in_range_with_offset(
996            ip_addr,
997            (port_range.start, port_range.start + 5),
998            offset
999        )
1000        .is_err());
1001    }
1002
1003    #[test]
1004    fn test_multi_bind_in_range_with_config_reuseport_disabled() {
1005        let ip_addr: IpAddr = IpAddr::V4(Ipv4Addr::LOCALHOST);
1006        let config = SocketConfig::default(); //reuseport is false by default
1007
1008        let port_range = unique_port_range_for_tests(3);
1009        let result =
1010            multi_bind_in_range_with_config(ip_addr, (port_range.start, port_range.end), config, 2);
1011
1012        assert!(
1013            result.is_err(),
1014            "Expected an error when reuseport is not set to true"
1015        );
1016    }
1017
1018    #[test]
1019    fn test_verify_udp_multiple_ips_reachable() {
1020        solana_logger::setup();
1021        let config = SocketConfig::default();
1022        let ip_a = IpAddr::V4(Ipv4Addr::LOCALHOST);
1023        let ip_b = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2));
1024
1025        let port_range = sockets::localhost_port_range_for_tests();
1026
1027        let (_srv_udp_port, (srv_udp_sock, srv_tcp_listener)) =
1028            bind_common_in_range_with_config(ip_a, port_range, config).unwrap();
1029
1030        let ip_echo_server_addr = srv_udp_sock.local_addr().unwrap();
1031        let _runtime = ip_echo_server(
1032            srv_tcp_listener,
1033            DEFAULT_IP_ECHO_SERVER_THREADS,
1034            /*shred_version=*/ Some(42),
1035        );
1036
1037        let mut udp_sockets = Vec::new();
1038        let (_p1, (sock_a, _tl_a)) =
1039            bind_common_in_range_with_config(ip_a, port_range, config).unwrap();
1040        let (_p2, (sock_b, _tl_b)) =
1041            bind_common_in_range_with_config(ip_b, port_range, config).unwrap();
1042
1043        udp_sockets.push(sock_a);
1044        udp_sockets.push(sock_b);
1045
1046        let socket_refs: Vec<&UdpSocket> = udp_sockets.iter().collect();
1047
1048        assert!(
1049            verify_all_reachable_udp(&ip_echo_server_addr, &socket_refs),
1050            "all UDP ports on both 127.0.0.1 and 127.0.0.2 should be reachable"
1051        );
1052    }
1053}