rust_p2p_core/socket/
mod.rs

1#[cfg(windows)]
2use crate::socket::windows::ignore_conn_reset;
3use socket2::Protocol;
4use std::io;
5use std::net::SocketAddr;
6
7#[cfg(unix)]
8mod unix;
9#[cfg(windows)]
10mod windows;
11
12pub(crate) trait SocketTrait {
13    fn set_ip_unicast_if(&self, _interface: &LocalInterface) -> io::Result<()> {
14        Ok(())
15    }
16}
17
18#[derive(Clone, Debug)]
19pub struct LocalInterface {
20    #[cfg(not(any(target_os = "linux", target_os = "android")))]
21    pub index: u32,
22    #[cfg(any(target_os = "linux", target_os = "android"))]
23    pub name: String,
24}
25
26impl LocalInterface {
27    #[cfg(not(any(target_os = "linux", target_os = "android")))]
28    pub fn new(index: u32) -> Self {
29        Self { index }
30    }
31    #[cfg(any(target_os = "linux", target_os = "android"))]
32    pub fn new(name: String) -> Self {
33        Self { name }
34    }
35}
36
37pub(crate) async fn connect_tcp(
38    addr: SocketAddr,
39    bind_port: u16,
40    default_interface: Option<&LocalInterface>,
41    ttl: Option<u8>,
42) -> io::Result<tokio::net::TcpStream> {
43    let socket = create_tcp0(addr, bind_port, default_interface, ttl)?;
44    socket.writable().await?;
45    Ok(socket)
46}
47
48pub(crate) fn create_tcp0(
49    addr: SocketAddr,
50    bind_port: u16,
51    default_interface: Option<&LocalInterface>,
52    ttl: Option<u8>,
53) -> io::Result<tokio::net::TcpStream> {
54    let v4 = addr.is_ipv4();
55    let socket = if v4 {
56        socket2::Socket::new(
57            socket2::Domain::IPV4,
58            socket2::Type::STREAM,
59            Some(Protocol::TCP),
60        )?
61    } else {
62        socket2::Socket::new(
63            socket2::Domain::IPV6,
64            socket2::Type::STREAM,
65            Some(Protocol::TCP),
66        )?
67    };
68    if v4 && default_interface.is_some() {
69        socket.set_ip_unicast_if(default_interface.unwrap())?;
70    }
71    if bind_port != 0 {
72        _ = socket.set_reuse_address(true);
73        #[cfg(unix)]
74        {
75            _ = socket.set_reuse_port(true);
76        }
77        if v4 {
78            let addr: SocketAddr = format!("0.0.0.0:{bind_port}").parse().unwrap();
79            socket.bind(&addr.into())?;
80        } else {
81            socket.set_only_v6(true)?;
82            let addr: SocketAddr = format!("[::]:{bind_port}").parse().unwrap();
83            socket.bind(&addr.into())?;
84        }
85    }
86    if let Some(ttl) = ttl {
87        _ = socket.set_ttl(ttl as _);
88    }
89    socket.set_nonblocking(true)?;
90    socket.set_nodelay(true)?;
91    let res = socket.connect(&addr.into());
92    match res {
93        Ok(()) => {}
94        Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {}
95        #[cfg(unix)]
96        Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {}
97        Err(e) => Err(e)?,
98    }
99    tokio::net::TcpStream::from_std(socket.into())
100}
101
102pub(crate) fn create_tcp_listener(addr: SocketAddr) -> io::Result<std::net::TcpListener> {
103    let socket = if addr.is_ipv6() {
104        let socket = socket2::Socket::new(socket2::Domain::IPV6, socket2::Type::STREAM, None)?;
105        socket.set_only_v6(false)?;
106        socket
107    } else {
108        socket2::Socket::new(socket2::Domain::IPV4, socket2::Type::STREAM, None)?
109    };
110    socket.set_reuse_address(true)?;
111    #[cfg(unix)]
112    if let Err(e) = socket.set_reuse_port(true) {
113        log::warn!("set_reuse_port {e:?}")
114    }
115    socket.bind(&addr.into())?;
116    socket.listen(128)?;
117    socket.set_nonblocking(true)?;
118    socket.set_nodelay(true)?;
119    Ok(socket.into())
120}
121
122pub(crate) fn bind_udp_ops(
123    addr: SocketAddr,
124    only_v6: bool,
125    default_interface: Option<&LocalInterface>,
126) -> io::Result<socket2::Socket> {
127    let socket = if addr.is_ipv4() {
128        let socket = socket2::Socket::new(
129            socket2::Domain::IPV4,
130            socket2::Type::DGRAM,
131            Some(Protocol::UDP),
132        )?;
133        if let Some(default_interface) = default_interface {
134            socket.set_ip_unicast_if(default_interface)?;
135        }
136        socket
137    } else {
138        let socket = socket2::Socket::new(
139            socket2::Domain::IPV6,
140            socket2::Type::DGRAM,
141            Some(Protocol::UDP),
142        )?;
143        socket.set_only_v6(only_v6)?;
144        socket
145    };
146    #[cfg(windows)]
147    if let Err(e) = ignore_conn_reset(&socket) {
148        log::warn!("ignore_conn_reset {e:?}")
149    }
150    socket.set_nonblocking(true)?;
151    socket.bind(&addr.into())?;
152    Ok(socket)
153}
154
155pub fn bind_udp(
156    addr: SocketAddr,
157    default_interface: Option<&LocalInterface>,
158) -> io::Result<socket2::Socket> {
159    bind_udp_ops(addr, true, default_interface)
160}