1use std::fmt;
4use std::net::SocketAddr;
5#[cfg(unix)]
6use std::path::Path;
7use std::sync::Arc;
8use std::time::Instant;
9
10use crate::addr::{PrefixKey, PrefixPolicy};
11use crate::id::{ConnectionId, ListenerId, WorkerId};
12use crate::proxy::ProxyFacts;
13use crate::tls::TlsInfo;
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
26pub struct Lane(pub u8);
27
28impl Lane {
29 pub const MOST_IMPORTANT: Lane = Lane(0);
31}
32
33#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
45pub struct GateTag(pub u64);
46
47#[non_exhaustive]
53#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
54pub enum Transport {
55 Tcp,
57 Udp,
59 Unix,
61 Pipe,
63}
64
65#[non_exhaustive]
71#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
72pub enum Origin {
73 Accepted,
75 Dialed,
77 Adopted,
79 Inherited,
81}
82
83#[derive(Clone, Copy, PartialEq, Eq, Hash)]
90pub struct UnixPath {
91 bytes: [u8; 108],
92 len: u8,
93}
94
95impl UnixPath {
96 pub const MAX: usize = 108;
98
99 #[must_use]
101 pub fn new(bytes: &[u8]) -> Option<UnixPath> {
102 if bytes.len() > Self::MAX {
103 return None;
104 }
105 let mut out = [0u8; 108];
106 out[..bytes.len()].copy_from_slice(bytes);
107 Some(UnixPath {
108 bytes: out,
109 len: u8::try_from(bytes.len()).ok()?,
110 })
111 }
112
113 #[must_use]
115 pub fn as_bytes(&self) -> &[u8] {
116 &self.bytes[..usize::from(self.len)]
117 }
118
119 #[cfg(unix)]
128 #[must_use]
129 pub fn as_path(&self) -> &Path {
130 use std::os::unix::ffi::OsStrExt;
131 Path::new(std::ffi::OsStr::from_bytes(self.as_bytes()))
132 }
133}
134
135impl std::fmt::Debug for UnixPath {
136 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137 write!(
138 f,
139 "UnixPath({:?})",
140 String::from_utf8_lossy(self.as_bytes())
141 )
142 }
143}
144
145#[derive(Clone, Copy, PartialEq, Eq, Hash)]
152pub struct PipeName {
153 units: [u16; 256],
154 len: u16,
155}
156
157impl PipeName {
158 pub const MAX: usize = 256;
160
161 #[must_use]
163 pub fn new(name: &str) -> Option<PipeName> {
164 let mut units = [0u16; 256];
165 let mut len = 0usize;
166 for u in name.encode_utf16() {
167 if len == Self::MAX {
168 return None;
169 }
170 units[len] = u;
171 len += 1;
172 }
173 Some(PipeName {
174 units,
175 len: u16::try_from(len).ok()?,
176 })
177 }
178
179 #[must_use]
181 pub fn as_wide(&self) -> &[u16] {
182 &self.units[..usize::from(self.len)]
183 }
184
185 #[must_use]
187 pub fn to_string_lossy(&self) -> String {
188 String::from_utf16_lossy(self.as_wide())
189 }
190}
191
192impl std::fmt::Debug for PipeName {
193 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
194 write!(f, "PipeName({:?})", self.to_string_lossy())
195 }
196}
197
198#[non_exhaustive]
205#[derive(Clone, Debug, PartialEq, Eq, Hash)]
206pub enum Endpoint {
207 Ip(SocketAddr),
209 Unix(Arc<UnixPath>),
211 Pipe(Arc<PipeName>),
213 Unnamed,
215}
216
217impl fmt::Display for Endpoint {
224 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225 match self {
226 Endpoint::Ip(a) => write!(f, "{a}"),
227 Endpoint::Unix(p) => write!(f, "{}", String::from_utf8_lossy(p.as_bytes())),
228 Endpoint::Pipe(p) => {
229 for c in char::decode_utf16(p.as_wide().iter().copied()) {
230 write!(f, "{}", c.unwrap_or(char::REPLACEMENT_CHARACTER))?;
231 }
232 Ok(())
233 }
234 Endpoint::Unnamed => f.write_str("unnamed"),
235 }
236 }
237}
238
239impl Endpoint {
240 #[must_use]
242 pub fn socket_addr(&self) -> Option<SocketAddr> {
243 match self {
244 Endpoint::Ip(a) => Some(*a),
245 _ => None,
246 }
247 }
248}
249
250#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
257pub struct PeerCredentials {
258 pub pid: Option<u32>,
260 pub uid: Option<u32>,
262 pub gid: Option<u32>,
264}
265
266#[non_exhaustive]
278#[derive(Clone, Debug)]
279pub struct ConnectionInfo {
280 pub id: ConnectionId,
282 pub listener: Option<ListenerId>,
284 pub worker: WorkerId,
286 pub transport: Transport,
288 pub origin: Origin,
290 pub local: Endpoint,
292 pub peer: Endpoint,
294 pub real_peer: Option<SocketAddr>,
296 pub proxy: Option<Arc<ProxyFacts>>,
298 pub peer_credentials: Option<PeerCredentials>,
300 pub tls: Option<Arc<TlsInfo>>,
302 pub lane: Lane,
304 pub gate: GateTag,
306 pub accepted_at: Instant,
308}
309
310impl ConnectionInfo {
311 #[inline]
314 #[must_use]
315 pub fn client_addr(&self) -> Option<SocketAddr> {
316 self.real_peer.or_else(|| self.peer.socket_addr())
317 }
318
319 #[must_use]
321 pub fn client_key(&self, policy: PrefixPolicy) -> Option<PrefixKey> {
322 self.client_addr().map(|a| PrefixKey::of(a.ip(), policy))
323 }
324
325 #[must_use]
327 pub fn socket_peer(&self) -> Option<SocketAddr> {
328 self.peer.socket_addr()
329 }
330
331 #[must_use]
336 pub fn for_testing(peer: SocketAddr) -> ConnectionInfo {
337 let local = if peer.is_ipv4() {
338 SocketAddr::from(([127, 0, 0, 1], 0))
339 } else {
340 SocketAddr::from(([0u16, 0, 0, 0, 0, 0, 0, 1], 0))
341 };
342 ConnectionInfo::new_tcp(
343 crate::id::issue_single(),
344 None,
345 WorkerId(0),
346 Origin::Accepted,
347 local,
348 crate::addr::normalize_socket(peer),
349 Instant::now(),
350 )
351 }
352
353 pub(crate) fn new_tcp(
354 id: ConnectionId,
355 listener: Option<ListenerId>,
356 worker: WorkerId,
357 origin: Origin,
358 local: SocketAddr,
359 peer: SocketAddr,
360 at: Instant,
361 ) -> ConnectionInfo {
362 ConnectionInfo {
363 id,
364 listener,
365 worker,
366 transport: Transport::Tcp,
367 origin,
368 local: Endpoint::Ip(local),
369 peer: Endpoint::Ip(peer),
370 real_peer: None,
371 proxy: None,
372 peer_credentials: None,
373 tls: None,
374 lane: Lane::MOST_IMPORTANT,
375 gate: GateTag(0),
376 accepted_at: at,
377 }
378 }
379}
380
381#[cfg(test)]
382mod tests {
383 use super::*;
384
385 #[test]
386 fn test_client_addr_prefers_real_peer() {
387 let mut info = ConnectionInfo::for_testing("10.0.0.1:1".parse().unwrap());
388 assert_eq!(info.client_addr().unwrap().ip().to_string(), "10.0.0.1");
389 info.real_peer = Some("203.0.113.5:9".parse().unwrap());
390 assert_eq!(info.client_addr().unwrap().ip().to_string(), "203.0.113.5");
391 assert_eq!(info.socket_peer().unwrap().ip().to_string(), "10.0.0.1");
392 }
393
394 #[test]
395 fn test_for_testing_normalizes_mapped_peer() {
396 let info = ConnectionInfo::for_testing("[::ffff:1.2.3.4]:80".parse().unwrap());
397 assert_eq!(info.socket_peer().unwrap().to_string(), "1.2.3.4:80");
398 }
399
400 #[test]
401 fn test_unix_path_bounds() {
402 assert!(UnixPath::new(&[b'a'; 108]).is_some());
403 assert!(UnixPath::new(&[b'a'; 109]).is_none());
404 }
405
406 #[test]
407 fn test_pipe_name_bounds() {
408 assert!(PipeName::new(&"a".repeat(256)).is_some());
409 assert!(PipeName::new(&"a".repeat(257)).is_none());
410 }
411
412 #[test]
413 fn test_connection_info_size_fits_idle_budget() {
414 assert!(
416 std::mem::size_of::<ConnectionInfo>() <= 256,
417 "{}",
418 std::mem::size_of::<ConnectionInfo>()
419 );
420 }
421}