socketstat/
lib.rs

1//! Get socket information and statistics.
2//!
3//! **[Crates.io](https://crates.io/crates/socketstat) │ [Repo](https://github.com/alecmocatta/socketstat)**
4//!
5//! Currently works on macOS only, PRs for other platforms welcome!
6//!
7//! # Example
8//!
9//! ```
10//! #[cfg(unix)]
11//! use std::os::unix::io::AsRawFd;
12//! #[cfg(windows)]
13//! use std::os::windows::io::AsRawSocket;
14//! use socketstat::socketstat;
15//!
16//! let sock = std::net::TcpStream::connect("google.com:80").unwrap();
17//!
18//! #[cfg(unix)]
19//! let fd = sock.as_raw_fd();
20//! #[cfg(windows)]
21//! let fd = sock.as_raw_socket();
22//!
23//! println!("{:#?}", socketstat(fd));
24//!
25//! // prints:
26//! //   Ok(
27//! //       SocketStat {
28//! //           unreceived: 0,
29//! //           unsent: 0,
30//! //           connection_info: tcp_connection_info {
31//! //               tcpi_state: "ESTABLISHED",
32//! //               tcpi_snd_wscale: 8,
33//! //               tcpi_rcv_wscale: 6,
34//! //               tcpi_options: 7,
35//! //               tcpi_flags: 0,
36//! //               tcpi_rto: 0,
37//! //               tcpi_maxseg: 1368,
38//! //               tcpi_snd_ssthresh: 1073725440,
39//! //               tcpi_snd_cwnd: 4380,
40//! //               tcpi_snd_wnd: 60192,
41//! //               tcpi_snd_sbbytes: 0,
42//! //               tcpi_rcv_wnd: 131328,
43//! //               tcpi_rttcur: 79,
44//! //               tcpi_srtt: 79,
45//! //               tcpi_rttvar: 39,
46//! //               tcpi_tfo: 0,
47//! //               tcpi_txpackets: 0,
48//! //               tcpi_txbytes: 0,
49//! //               tcpi_txretransmitbytes: 0,
50//! //               tcpi_rxpackets: 0,
51//! //               tcpi_rxbytes: 0,
52//! //               tcpi_rxoutoforderbytes: 0,
53//! //               tcpi_txretransmitpackets: 0,
54//! //           },
55//! //           socket_info: tcp_sockinfo {
56//! //               tcpsi_ini: in_sockinfo {
57//! //                   insi_fport: 80,
58//! //                   insi_lport: 52621,
59//! //                   insi_gencnt: 100950561,
60//! //                   insi_flags: 8390720,
61//! //                   insi_flow: 0,
62//! //                   insi_vflag: "IPV4",
63//! //                   insi_ip_ttl: 64,
64//! //                   rfu_1: 0,
65//! //               },
66//! //               tcpsi_state: "ESTABLISHED",
67//! //               tcpsi_timer: [
68//! //                   0,
69//! //                   0,
70//! //                   7200079,
71//! //                   0,
72//! //               ],
73//! //               tcpsi_mss: 1368,
74//! //               tcpsi_flags: 1140851680,
75//! //               rfu_1: 0,
76//! //               tcpsi_tp: 9662996336038732135,
77//! //           },
78//! //       },
79//! //   )
80//! ```
81//!
82//! # Note
83//!
84//! On macOS this calls:
85//! * `getsockopt(fd, IPPROTO_TCP, TCP_CONNECTION_INFO, ...)`
86//! * `proc_pidfdinfo(getpid(), fd, PROC_PIDFDSOCKETINFO, ...)`
87//! * `ioctl(fd, FIONREAD, ...)`
88//! * `getsockopt(fd, SOL_SOCKET, SO_NWRITE, ...)`
89//!
90//! Other sources to explore:
91//! * `sysctl([CTL_NET, PF_INET, IPPROTO_TCP, TCPCTL_PCBLIST], ...`
92//! * <https://stackoverflow.com/questions/31263289/on-linux-mac-windows-is-it-possible-to-access-the-tcp-timestamp-and-or-rtt-in-u>
93
94#![doc(html_root_url = "https://docs.rs/socketstat/0.1.0")]
95#![warn(
96	missing_copy_implementations,
97	missing_debug_implementations,
98	missing_docs,
99	trivial_casts,
100	trivial_numeric_casts,
101	unused_import_braces,
102	unused_qualifications,
103	unused_results,
104	clippy::pedantic
105)] // from https://github.com/rust-unofficial/patterns/blob/master/anti_patterns/deny-warnings.md
106#![allow()]
107
108use std::fmt;
109
110#[cfg(unix)]
111type Fd = std::os::unix::io::RawFd;
112#[cfg(windows)]
113type Fd = std::os::windows::io::RawSocket;
114
115#[cfg(any(target_os = "macos", target_os = "ios"))]
116mod mac;
117#[cfg(any(target_os = "macos", target_os = "ios"))]
118use mac as sys;
119
120#[cfg(not(any(target_os = "macos", target_os = "ios")))]
121mod stub;
122#[cfg(not(any(target_os = "macos", target_os = "ios")))]
123use stub as sys;
124
125/// Information and stats that can be printed via the Debug implementation.
126#[derive(Clone)]
127pub struct SocketStat {
128	sys: sys::SocketStat,
129}
130impl fmt::Debug for SocketStat {
131	fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
132		self.sys.fmt(fmt)
133	}
134}
135
136/// An error occurred
137#[derive(Clone)]
138pub struct Error {
139	sys: sys::Error,
140}
141impl fmt::Debug for Error {
142	fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
143		self.sys.fmt(fmt)
144	}
145}
146
147/// Get socket information and statistics for a given File Descriptor or Socket.
148pub fn socketstat(fd: Fd) -> Result<SocketStat, Error> {
149	sys::socketstat(fd)
150		.map(|sys| SocketStat { sys })
151		.map_err(|sys| Error { sys })
152}
153
154#[cfg(test)]
155mod tests {
156	#[cfg(unix)]
157	use std::os::unix::io::AsRawFd;
158	#[cfg(windows)]
159	use std::os::windows::io::AsRawSocket;
160
161	use super::socketstat;
162
163	#[test]
164	fn succeeds() {
165		let sock = std::net::TcpStream::connect("google.com:80").unwrap();
166
167		#[cfg(unix)]
168		let fd = sock.as_raw_fd();
169		#[cfg(windows)]
170		let fd = sock.as_raw_socket();
171
172		println!("{:#?}", socketstat(fd));
173	}
174}