1use std::io;
5use std::net::{SocketAddr, TcpListener as StdListener};
6use std::os::fd::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
7
8use socket2::{Domain, Protocol, Socket, Type};
9
10pub fn tcp_listener(addr: SocketAddr, reuse_port: bool, backlog: i32) -> io::Result<StdListener> {
18 let domain = if addr.is_ipv4() {
19 Domain::IPV4
20 } else {
21 Domain::IPV6
22 };
23 let sock = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))?;
24 sock.set_nonblocking(true)?;
25 sock.set_reuse_address(true)?;
26 if reuse_port {
27 set_sock_opt_bool(sock.as_raw_fd(), libc::SOL_SOCKET, libc::SO_REUSEPORT, true)?;
29 }
30 if addr.ip().is_ipv6() {
32 set_sock_opt_bool(
33 sock.as_raw_fd(),
34 libc::IPPROTO_IPV6,
35 libc::IPV6_V6ONLY,
36 true,
37 )?;
38 }
39 sock.bind(&addr.into())?;
40 sock.listen(backlog)?;
41 Ok(unsafe { StdListener::from_raw_fd(sock.into_raw_fd()) })
43}
44
45pub fn set_nodelay(fd: RawFd) -> io::Result<()> {
50 set_sock_opt_bool(fd, libc::IPPROTO_TCP, libc::TCP_NODELAY, true)
51}
52
53pub fn set_keepalive(fd: RawFd, idle_secs: u32) -> io::Result<()> {
58 set_sock_opt_bool(fd, libc::SOL_SOCKET, libc::SO_KEEPALIVE, true)?;
59 unsafe {
61 let v: libc::c_int = idle_secs as libc::c_int;
62 libc::setsockopt(
63 fd,
64 libc::IPPROTO_TCP,
65 libc::TCP_KEEPIDLE,
66 std::ptr::addr_of!(v).cast(),
67 std::mem::size_of::<libc::c_int>() as libc::socklen_t,
68 );
69 let i: libc::c_int = 3;
70 libc::setsockopt(
71 fd,
72 libc::IPPROTO_TCP,
73 libc::TCP_KEEPINTVL,
74 std::ptr::addr_of!(i).cast(),
75 std::mem::size_of::<libc::c_int>() as libc::socklen_t,
76 );
77 let c: libc::c_int = 3;
78 libc::setsockopt(
79 fd,
80 libc::IPPROTO_TCP,
81 libc::TCP_KEEPCNT,
82 std::ptr::addr_of!(c).cast(),
83 std::mem::size_of::<libc::c_int>() as libc::socklen_t,
84 );
85 }
86 Ok(())
87}
88
89fn set_sock_opt_bool(fd: RawFd, level: libc::c_int, name: libc::c_int, on: bool) -> io::Result<()> {
90 let v: libc::c_int = i32::from(on);
91 let rc = unsafe {
93 libc::setsockopt(
94 fd,
95 level,
96 name,
97 std::ptr::addr_of!(v).cast(),
98 std::mem::size_of::<libc::c_int>() as libc::socklen_t,
99 )
100 };
101 if rc == 0 {
102 Ok(())
103 } else {
104 Err(io::Error::last_os_error())
105 }
106}
107
108#[derive(Debug)]
110pub struct StreamFd(pub RawFd);
111
112impl StreamFd {
113 #[must_use]
115 pub fn fd(&self) -> RawFd {
116 self.0
117 }
118}
119
120impl Drop for StreamFd {
121 fn drop(&mut self) {
122 unsafe { libc::close(self.0) };
124 }
125}
126
127pub fn shutdown_write(fd: RawFd) {
129 unsafe {
131 let _ = libc::shutdown(fd, libc::SHUT_WR);
132 }
133}
134
135#[cfg(test)]
136mod net_tests {
137 use super::*;
138
139 #[test]
140 fn tcp_listener_reuseport_binds() {
141 let addr: SocketAddr = "127.0.0.1:0".parse().expect("addr");
142 let l = tcp_listener(addr, true, 64).expect("bind");
143 assert!(l.local_addr().is_ok());
144 }
145
146 #[test]
147 fn set_nodelay_and_keepalive_on_socket() {
148 let a = std::net::TcpListener::bind("127.0.0.1:0").expect("bind");
149 let addr = a.local_addr().expect("addr");
150 let client = std::net::TcpStream::connect(addr).expect("connect");
151 let fd = client.as_raw_fd();
152 set_nodelay(fd).expect("nodelay");
153 set_keepalive(fd, 30).expect("keepalive");
154 }
155
156 #[test]
157 fn set_nodelay_rejects_bad_fd() {
158 assert!(set_nodelay(-1).is_err());
159 }
160
161 #[test]
162 fn shutdown_write_sends_fin() {
163 use std::io::Read as _;
164 let a = std::net::TcpListener::bind("127.0.0.1:0").expect("bind");
165 let addr = a.local_addr().expect("addr");
166 let client = std::net::TcpStream::connect(addr).expect("connect");
167 let mut server = a.incoming().next().unwrap().expect("accept");
168 shutdown_write(client.as_raw_fd());
169 let mut buf = [0u8; 1];
171 assert_eq!(server.read(&mut buf).expect("read"), 0);
172 }
173}