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 #[cfg(feature = "local")]
355 pub(crate) fn new_unix(
356 id: ConnectionId,
357 listener: Option<ListenerId>,
358 origin: Origin,
359 local: Endpoint,
360 peer: Endpoint,
361 credentials: Option<PeerCredentials>,
362 at: Instant,
363 ) -> ConnectionInfo {
364 ConnectionInfo {
365 id,
366 listener,
367 worker: WorkerId(0),
368 transport: Transport::Unix,
369 origin,
370 local,
371 peer,
372 real_peer: None,
373 proxy: None,
374 peer_credentials: credentials,
375 tls: None,
376 lane: Lane::MOST_IMPORTANT,
377 gate: GateTag(0),
378 accepted_at: at,
379 }
380 }
381
382 pub(crate) fn new_tcp(
383 id: ConnectionId,
384 listener: Option<ListenerId>,
385 worker: WorkerId,
386 origin: Origin,
387 local: SocketAddr,
388 peer: SocketAddr,
389 at: Instant,
390 ) -> ConnectionInfo {
391 ConnectionInfo {
392 id,
393 listener,
394 worker,
395 transport: Transport::Tcp,
396 origin,
397 local: Endpoint::Ip(local),
398 peer: Endpoint::Ip(peer),
399 real_peer: None,
400 proxy: None,
401 peer_credentials: None,
402 tls: None,
403 lane: Lane::MOST_IMPORTANT,
404 gate: GateTag(0),
405 accepted_at: at,
406 }
407 }
408}
409
410#[cfg(test)]
411mod tests {
412 use super::*;
413
414 #[test]
415 fn test_client_addr_prefers_real_peer() {
416 let mut info = ConnectionInfo::for_testing("10.0.0.1:1".parse().unwrap());
417 assert_eq!(info.client_addr().unwrap().ip().to_string(), "10.0.0.1");
418 info.real_peer = Some("203.0.113.5:9".parse().unwrap());
419 assert_eq!(info.client_addr().unwrap().ip().to_string(), "203.0.113.5");
420 assert_eq!(info.socket_peer().unwrap().ip().to_string(), "10.0.0.1");
421 }
422
423 #[test]
424 fn test_for_testing_normalizes_mapped_peer() {
425 let info = ConnectionInfo::for_testing("[::ffff:1.2.3.4]:80".parse().unwrap());
426 assert_eq!(info.socket_peer().unwrap().to_string(), "1.2.3.4:80");
427 }
428
429 #[test]
430 fn test_unix_path_bounds() {
431 assert!(UnixPath::new(&[b'a'; 108]).is_some());
432 assert!(UnixPath::new(&[b'a'; 109]).is_none());
433 }
434
435 #[test]
436 fn test_pipe_name_bounds() {
437 assert!(PipeName::new(&"a".repeat(256)).is_some());
438 assert!(PipeName::new(&"a".repeat(257)).is_none());
439 }
440
441 #[test]
442 fn test_connection_info_size_fits_idle_budget() {
443 assert!(
445 std::mem::size_of::<ConnectionInfo>() <= 256,
446 "{}",
447 std::mem::size_of::<ConnectionInfo>()
448 );
449 }
450}