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        socket.set_reuse_port(true)?;
75        if v4 {
76            let addr: SocketAddr = format!("0.0.0.0:{}", bind_port).parse().unwrap();
77            socket.bind(&addr.into())?;
78        } else {
79            socket.set_only_v6(true)?;
80            let addr: SocketAddr = format!("[::]:{}", bind_port).parse().unwrap();
81            socket.bind(&addr.into())?;
82        }
83    }
84    if let Some(ttl) = ttl {
85        socket.set_ttl(ttl as _)?;
86    }
87    socket.set_nonblocking(true)?;
88    socket.set_nodelay(true)?;
89    let res = socket.connect(&addr.into());
90    match res {
91        Ok(()) => {}
92        Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {}
93        #[cfg(unix)]
94        Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {}
95        Err(e) => Err(e)?,
96    }
97    tokio::net::TcpStream::from_std(socket.into())
98}
99
100pub(crate) fn create_tcp_listener(addr: SocketAddr) -> io::Result<std::net::TcpListener> {
101    let socket = if addr.is_ipv6() {
102        let socket = socket2::Socket::new(socket2::Domain::IPV6, socket2::Type::STREAM, None)?;
103        socket.set_only_v6(false)?;
104        socket
105    } else {
106        socket2::Socket::new(socket2::Domain::IPV4, socket2::Type::STREAM, None)?
107    };
108    socket.set_reuse_address(true)?;
109    #[cfg(unix)]
110    if let Err(e) = socket.set_reuse_port(true) {
111        log::warn!("set_reuse_port {:?}", e)
112    }
113    socket.bind(&addr.into())?;
114    socket.listen(128)?;
115    socket.set_nonblocking(true)?;
116    socket.set_nodelay(true)?;
117    Ok(socket.into())
118}
119
120pub(crate) fn bind_udp_ops(
121    addr: SocketAddr,
122    only_v6: bool,
123    default_interface: Option<&LocalInterface>,
124) -> io::Result<socket2::Socket> {
125    let socket = if addr.is_ipv4() {
126        let socket = socket2::Socket::new(
127            socket2::Domain::IPV4,
128            socket2::Type::DGRAM,
129            Some(Protocol::UDP),
130        )?;
131        if let Some(default_interface) = default_interface {
132            socket.set_ip_unicast_if(default_interface)?;
133        }
134        socket
135    } else {
136        let socket = socket2::Socket::new(
137            socket2::Domain::IPV6,
138            socket2::Type::DGRAM,
139            Some(Protocol::UDP),
140        )?;
141        socket.set_only_v6(only_v6)?;
142        socket
143    };
144    #[cfg(windows)]
145    if let Err(e) = ignore_conn_reset(&socket) {
146        log::warn!("ignore_conn_reset {e:?}")
147    }
148    socket.set_nonblocking(true)?;
149    socket.bind(&addr.into())?;
150    Ok(socket)
151}
152
153pub fn bind_udp(
154    addr: SocketAddr,
155    default_interface: Option<&LocalInterface>,
156) -> io::Result<socket2::Socket> {
157    bind_udp_ops(addr, true, default_interface)
158}