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}