Skip to main content

rust_p2p_core/socket/
mod.rs

1//! Low-level socket creation and management.
2//!
3//! This module provides functions for creating and configuring UDP and TCP sockets
4//! with support for interface binding, port reuse, and other socket options.
5//!
6//! # Examples
7//!
8//! ```rust,no_run
9//! use rust_p2p_core::socket::{create_udp_socket, LocalInterface};
10//! use std::net::SocketAddr;
11//!
12//! # fn main() -> std::io::Result<()> {
13//! let addr: SocketAddr = "0.0.0.0:0".parse().unwrap();
14//! let socket = create_udp_socket(addr, None)?;
15//! # Ok(())
16//! # }
17//! ```
18
19#[cfg(windows)]
20use crate::socket::windows::ignore_conn_reset;
21use socket2::Protocol;
22use std::io;
23use std::net::SocketAddr;
24
25#[cfg(unix)]
26mod unix;
27#[cfg(windows)]
28mod windows;
29
30pub(crate) trait SocketTrait {
31    fn set_ip_unicast_if(&self, _interface: &LocalInterface) -> io::Result<()> {
32        Ok(())
33    }
34}
35
36/// Network interface identifier for binding sockets.
37///
38/// On Linux/Android, this uses the interface name (e.g., "eth0").
39/// On other platforms, this uses the interface index.
40///
41/// # Examples
42///
43/// ```rust
44/// use rust_p2p_core::socket::LocalInterface;
45///
46/// // On Linux/Android
47/// #[cfg(any(target_os = "linux", target_os = "android"))]
48/// let iface = LocalInterface::new("eth0".to_string());
49///
50/// // On other platforms
51/// #[cfg(not(any(target_os = "linux", target_os = "android")))]
52/// let iface = LocalInterface::new(2); // interface index
53/// ```
54#[derive(Clone, Debug)]
55pub struct LocalInterface {
56    #[cfg(not(any(target_os = "linux", target_os = "android")))]
57    pub index: u32,
58    #[cfg(any(target_os = "linux", target_os = "android"))]
59    pub name: String,
60}
61
62impl LocalInterface {
63    #[cfg(not(any(target_os = "linux", target_os = "android")))]
64    pub fn new(index: u32) -> Self {
65        Self { index }
66    }
67    #[cfg(any(target_os = "linux", target_os = "android"))]
68    pub fn new(name: String) -> Self {
69        Self { name }
70    }
71}
72
73pub(crate) async fn connect_tcp(
74    addr: SocketAddr,
75    bind_port: u16,
76    default_interface: Option<&LocalInterface>,
77    ttl: Option<u8>,
78) -> io::Result<tokio::net::TcpStream> {
79    let socket = create_tcp0(addr, bind_port, default_interface, ttl)?;
80    socket.writable().await?;
81    Ok(socket)
82}
83
84pub(crate) fn create_tcp0(
85    addr: SocketAddr,
86    bind_port: u16,
87    default_interface: Option<&LocalInterface>,
88    ttl: Option<u8>,
89) -> io::Result<tokio::net::TcpStream> {
90    let v4 = addr.is_ipv4();
91    let socket = if v4 {
92        socket2::Socket::new(
93            socket2::Domain::IPV4,
94            socket2::Type::STREAM,
95            Some(Protocol::TCP),
96        )?
97    } else {
98        socket2::Socket::new(
99            socket2::Domain::IPV6,
100            socket2::Type::STREAM,
101            Some(Protocol::TCP),
102        )?
103    };
104    if let (true, Some(interface)) = (v4, default_interface) {
105        socket.set_ip_unicast_if(interface)?;
106    }
107    if bind_port != 0 {
108        _ = socket.set_reuse_address(true);
109        #[cfg(unix)]
110        {
111            _ = socket.set_reuse_port(true);
112        }
113        if v4 {
114            let addr: SocketAddr = format!("0.0.0.0:{bind_port}").parse().unwrap();
115            socket.bind(&addr.into())?;
116        } else {
117            socket.set_only_v6(true)?;
118            let addr: SocketAddr = format!("[::]:{bind_port}").parse().unwrap();
119            socket.bind(&addr.into())?;
120        }
121    }
122    if let Some(ttl) = ttl {
123        _ = socket.set_ttl(ttl as _);
124    }
125    socket.set_nonblocking(true)?;
126    socket.set_nodelay(true)?;
127    let res = socket.connect(&addr.into());
128    match res {
129        Ok(()) => {}
130        Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {}
131        #[cfg(unix)]
132        Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {}
133        Err(e) => Err(e)?,
134    }
135    tokio::net::TcpStream::from_std(socket.into())
136}
137
138pub(crate) fn create_tcp_listener(addr: SocketAddr) -> io::Result<std::net::TcpListener> {
139    let socket = if addr.is_ipv6() {
140        let socket = socket2::Socket::new(socket2::Domain::IPV6, socket2::Type::STREAM, None)?;
141        socket.set_only_v6(false)?;
142        socket
143    } else {
144        socket2::Socket::new(socket2::Domain::IPV4, socket2::Type::STREAM, None)?
145    };
146    socket.set_reuse_address(true)?;
147    #[cfg(unix)]
148    if let Err(e) = socket.set_reuse_port(true) {
149        log::warn!("set_reuse_port {e:?}")
150    }
151    socket.bind(&addr.into())?;
152    socket.listen(128)?;
153    socket.set_nonblocking(true)?;
154    socket.set_nodelay(true)?;
155    Ok(socket.into())
156}
157
158pub(crate) fn bind_udp_ops(
159    addr: SocketAddr,
160    only_v6: bool,
161    default_interface: Option<&LocalInterface>,
162) -> io::Result<socket2::Socket> {
163    let socket = if addr.is_ipv4() {
164        let socket = socket2::Socket::new(
165            socket2::Domain::IPV4,
166            socket2::Type::DGRAM,
167            Some(Protocol::UDP),
168        )?;
169        if let Some(default_interface) = default_interface {
170            socket.set_ip_unicast_if(default_interface)?;
171        }
172        socket
173    } else {
174        let socket = socket2::Socket::new(
175            socket2::Domain::IPV6,
176            socket2::Type::DGRAM,
177            Some(Protocol::UDP),
178        )?;
179        socket.set_only_v6(only_v6)?;
180        socket
181    };
182    #[cfg(windows)]
183    if let Err(e) = ignore_conn_reset(&socket) {
184        log::warn!("ignore_conn_reset {e:?}")
185    }
186    socket.set_nonblocking(true)?;
187    socket.bind(&addr.into())?;
188    Ok(socket)
189}
190
191pub fn bind_udp(
192    addr: SocketAddr,
193    default_interface: Option<&LocalInterface>,
194) -> io::Result<socket2::Socket> {
195    bind_udp_ops(addr, true, default_interface)
196}