Skip to main content

net_io/
handoff.rs

1//! Listener hand-off between processes, and the owned listener handle that
2//! inheritance, hand-off, and `Server::release_listeners` produce.
3//!
4//! `send_listeners` and `recv_listeners` are Unix (feature `local`);
5//! `share_listeners` and `receive_shared` are their Windows counterparts. A
6//! `ListenerHandle` and the error type exist in every build.
7//!
8//! Manifest, version 1 (big-endian integers):
9//!
10//! ```text
11//!  "NIOH" | version 1 | count | total length (u16, all bytes including the CRC)
12//!  per listener: kind (1 TCP, 2 Unix) | name length (at most 64) | name
13//!                | address length (u16) | address (TCP: family 4 or 6, IP, port;
14//!                  Unix: the path bytes)
15//!  CRC32C of everything before it (u32)
16//! ```
17//!
18//! On Windows the manifest is followed by a 16-byte nonce, one 628-byte
19//! `WSAPROTOCOL_INFOW` per listener, and a CRC32C of those.
20
21// The manifest is read and written only by the hand-off functions.
22#![cfg_attr(not(feature = "local"), allow(dead_code))]
23
24use std::fmt;
25use std::io;
26#[cfg(unix)]
27use std::sync::Arc;
28
29#[cfg(unix)]
30use crate::info::UnixPath;
31use crate::info::{Endpoint, Transport};
32use crate::sys::{self, KindError, SockKind};
33
34/// Why a listener hand-off failed. Nothing is adopted on any error.
35///
36/// ```
37/// assert_eq!(net_io::HandoffError::BadChecksum.to_string(), "the hand-off manifest checksum does not match");
38/// ```
39#[non_exhaustive]
40#[derive(Debug)]
41pub enum HandoffError {
42    /// The sender is not the expected process or user.
43    PeerMismatch,
44    /// The control message was truncated.
45    Truncated,
46    /// The descriptor count does not match the manifest.
47    CountMismatch,
48    /// The manifest does not start with the expected magic.
49    BadMagic,
50    /// The manifest version is unknown.
51    BadVersion,
52    /// The manifest checksum does not match.
53    BadChecksum,
54    /// More listeners than one hand-off carries.
55    TooMany {
56        /// The maximum.
57        max: u8,
58    },
59    /// A received socket failed verification.
60    VerifyFailed {
61        /// Its position in the manifest.
62        index: u8,
63        /// Which check failed.
64        why: &'static str,
65    },
66    /// The hand-off did not finish in time.
67    Timeout,
68    /// An OS call failed.
69    Os(io::Error),
70}
71
72impl fmt::Display for HandoffError {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        match self {
75            HandoffError::PeerMismatch => {
76                f.write_str("the hand-off sender is not the expected process or user")
77            }
78            HandoffError::Truncated => f.write_str("the hand-off message was truncated"),
79            HandoffError::CountMismatch => {
80                f.write_str("the descriptor count does not match the manifest")
81            }
82            HandoffError::BadMagic => f.write_str("not a hand-off manifest"),
83            HandoffError::BadVersion => f.write_str("unknown hand-off manifest version"),
84            HandoffError::BadChecksum => {
85                f.write_str("the hand-off manifest checksum does not match")
86            }
87            HandoffError::TooMany { max } => write!(f, "more than {max} listeners in one hand-off"),
88            HandoffError::VerifyFailed { index, why } => {
89                write!(f, "received listener {index} failed: {why}")
90            }
91            HandoffError::Timeout => f.write_str("the hand-off did not finish in time"),
92            HandoffError::Os(e) => write!(f, "{e}"),
93        }
94    }
95}
96
97impl std::error::Error for HandoffError {
98    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
99        match self {
100            HandoffError::Os(e) => Some(e),
101            _ => None,
102        }
103    }
104}
105
106/// An owned listening socket with what is known about it: its kind, an
107/// optional name (up to 64 bytes), and its local endpoint. Dropping it closes
108/// the socket. Made by [`crate::systemd`], hand-off, and
109/// `Server::release_listeners`; used by `ListenerSpec::adopt`.
110pub struct ListenerHandle {
111    kind: Transport,
112    name: Option<String>,
113    local: Endpoint,
114    sock: sys::OwnedSock,
115}
116
117impl ListenerHandle {
118    /// Verifies `sock` (a listening stream socket of a family net-io serves) and
119    /// records its endpoint.
120    #[cfg_attr(not(unix), allow(dead_code))]
121    pub(crate) fn verified(
122        sock: sys::OwnedSock,
123        name: Option<String>,
124    ) -> Result<ListenerHandle, KindError> {
125        let kind = sys::listener_kind(sys::AsSock::sock(&sock))?;
126        let (kind, local) = endpoint_of(kind)?;
127        Ok(ListenerHandle {
128            kind,
129            name,
130            local,
131            sock,
132        })
133    }
134
135    pub(crate) fn new(
136        kind: Transport,
137        name: Option<String>,
138        local: Endpoint,
139        sock: sys::OwnedSock,
140    ) -> ListenerHandle {
141        ListenerHandle {
142            kind,
143            name,
144            local,
145            sock,
146        }
147    }
148
149    pub(crate) fn into_sock(self) -> sys::OwnedSock {
150        self.sock
151    }
152
153    /// TCP or Unix.
154    #[must_use]
155    pub fn kind(&self) -> Transport {
156        self.kind
157    }
158
159    /// The name it was given (systemd's `LISTEN_FDNAMES`, a listener's name).
160    #[must_use]
161    pub fn name(&self) -> Option<&str> {
162        self.name.as_deref()
163    }
164
165    /// The address it is bound to.
166    #[must_use]
167    pub fn local(&self) -> &Endpoint {
168        &self.local
169    }
170}
171
172/// The transport and endpoint of a verified socket.
173fn endpoint_of(kind: SockKind) -> Result<(Transport, Endpoint), KindError> {
174    match kind {
175        SockKind::Tcp(a) => Ok((Transport::Tcp, Endpoint::Ip(a))),
176        #[cfg(unix)]
177        SockKind::Unix(path) => {
178            if !cfg!(feature = "local") {
179                return Err(KindError::WrongKind);
180            }
181            let local = UnixPath::new(&path)
182                .filter(|_| !path.is_empty())
183                .map_or(Endpoint::Unnamed, |p| Endpoint::Unix(Arc::new(p)));
184            Ok((Transport::Unix, local))
185        }
186    }
187}
188
189impl fmt::Debug for ListenerHandle {
190    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191        f.debug_struct("ListenerHandle")
192            .field("kind", &self.kind)
193            .field("name", &self.name)
194            .field("local", &self.local)
195            .finish()
196    }
197}
198
199#[cfg(unix)]
200impl std::os::fd::AsFd for ListenerHandle {
201    fn as_fd(&self) -> std::os::fd::BorrowedFd<'_> {
202        self.sock.as_fd()
203    }
204}
205
206#[cfg(windows)]
207impl std::os::windows::io::AsSocket for ListenerHandle {
208    fn as_socket(&self) -> std::os::windows::io::BorrowedSocket<'_> {
209        self.sock.as_socket()
210    }
211}
212
213/// Whom `recv_listeners` accepts listeners from. `uid: None` means this
214/// process's own effective user id; `pid: None` accepts any process of that user.
215///
216/// ```
217/// let only_parent = net_io::HandoffPeer { uid: None, pid: Some(1234) };
218/// # let _ = only_parent;
219/// ```
220#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
221pub struct HandoffPeer {
222    /// The sender's user id (`None`: ours).
223    pub uid: Option<u32>,
224    /// The sender's process id (`None`: any).
225    pub pid: Option<u32>,
226}
227
228const MAGIC: [u8; 4] = *b"NIOH";
229const VERSION: u8 = 1;
230const HEADER: usize = 8;
231/// The most listeners one hand-off carries (one `SCM_RIGHTS` message).
232const MAX_LISTENERS: usize = 64;
233/// The longest name carried; longer names are cut at a character boundary.
234const MAX_NAME: usize = 64;
235
236/// One listener as the manifest describes it.
237#[derive(Clone, Debug, PartialEq, Eq)]
238struct Record {
239    kind: SockKind,
240    name: Option<String>,
241}
242
243/// Encodes the manifest for `records`.
244fn encode(records: &[Record]) -> Vec<u8> {
245    let mut out = Vec::with_capacity(HEADER + records.len() * 96 + 4);
246    out.extend_from_slice(&MAGIC);
247    out.push(VERSION);
248    out.push(records.len() as u8);
249    out.extend_from_slice(&[0, 0]); // total length, filled in below
250    for r in records {
251        let mut name = r.name.as_deref().unwrap_or("");
252        if name.len() > MAX_NAME {
253            let mut cut = MAX_NAME;
254            while !name.is_char_boundary(cut) {
255                cut -= 1;
256            }
257            name = &name[..cut];
258        }
259        let mut addr = Vec::with_capacity(32);
260        let kind = match &r.kind {
261            SockKind::Tcp(a) => {
262                match a.ip() {
263                    std::net::IpAddr::V4(ip) => {
264                        addr.push(4);
265                        addr.extend_from_slice(&ip.octets());
266                    }
267                    std::net::IpAddr::V6(ip) => {
268                        addr.push(6);
269                        addr.extend_from_slice(&ip.octets());
270                    }
271                }
272                addr.extend_from_slice(&a.port().to_be_bytes());
273                1u8
274            }
275            #[cfg(unix)]
276            SockKind::Unix(path) => {
277                addr.extend_from_slice(path);
278                2u8
279            }
280        };
281        out.push(kind);
282        out.push(name.len() as u8);
283        out.extend_from_slice(name.as_bytes());
284        out.extend_from_slice(&(addr.len() as u16).to_be_bytes());
285        out.extend_from_slice(&addr);
286    }
287    let total = (out.len() + 4) as u16;
288    out[6..8].copy_from_slice(&total.to_be_bytes());
289    let mut crc = crate::crc32c::Crc32c::new();
290    crc.update(&out);
291    out.extend_from_slice(&crc.finish().to_be_bytes());
292    out
293}
294
295/// Checks the header: magic, version, count, total length.
296fn parse_header(h: &[u8; HEADER]) -> Result<(usize, usize), HandoffError> {
297    if h[..4] != MAGIC {
298        return Err(HandoffError::BadMagic);
299    }
300    if h[4] != VERSION {
301        return Err(HandoffError::BadVersion);
302    }
303    let count = usize::from(h[5]);
304    if count > MAX_LISTENERS {
305        return Err(HandoffError::TooMany {
306            max: MAX_LISTENERS as u8,
307        });
308    }
309    let total = usize::from(u16::from_be_bytes([h[6], h[7]]));
310    if total < HEADER + 4 {
311        return Err(HandoffError::Truncated);
312    }
313    Ok((count, total))
314}
315
316/// Parses a whole manifest (header included).
317fn decode(bytes: &[u8]) -> Result<Vec<Record>, HandoffError> {
318    let header: &[u8; HEADER] = bytes
319        .get(..HEADER)
320        .and_then(|h| h.try_into().ok())
321        .ok_or(HandoffError::Truncated)?;
322    let (count, total) = parse_header(header)?;
323    if bytes.len() != total {
324        return Err(HandoffError::Truncated);
325    }
326    let (body, sum) = bytes.split_at(total - 4);
327    let mut crc = crate::crc32c::Crc32c::new();
328    crc.update(body);
329    if crc.finish().to_be_bytes() != sum {
330        return Err(HandoffError::BadChecksum);
331    }
332    let mut at = HEADER;
333    let mut take = |n: usize| -> Result<&[u8], HandoffError> {
334        let s = body.get(at..at + n).ok_or(HandoffError::Truncated)?;
335        at += n;
336        Ok(s)
337    };
338    let mut records = Vec::with_capacity(count);
339    for i in 0..count {
340        let bad = |why| HandoffError::VerifyFailed {
341            index: i as u8,
342            why,
343        };
344        let kind = take(1)?[0];
345        let name_len = usize::from(take(1)?[0]);
346        if name_len > MAX_NAME {
347            return Err(bad("name longer than 64 bytes"));
348        }
349        let name = std::str::from_utf8(take(name_len)?)
350            .map_err(|_| bad("name is not UTF-8"))?
351            .to_owned();
352        let addr_len = take(2)?;
353        let addr_len = usize::from(u16::from_be_bytes([addr_len[0], addr_len[1]]));
354        let addr = take(addr_len)?;
355        let kind = match (kind, addr) {
356            (1, [4, a, b, c, d, p0, p1]) => SockKind::Tcp(std::net::SocketAddr::from((
357                [*a, *b, *c, *d],
358                u16::from_be_bytes([*p0, *p1]),
359            ))),
360            (1, [6, rest @ ..]) if rest.len() == 18 => {
361                let mut ip = [0u8; 16];
362                ip.copy_from_slice(&rest[..16]);
363                SockKind::Tcp(std::net::SocketAddr::from((
364                    ip,
365                    u16::from_be_bytes([rest[16], rest[17]]),
366                )))
367            }
368            #[cfg(unix)]
369            (2, path) => SockKind::Unix(path.to_vec()),
370            _ => return Err(bad("unknown kind or malformed address")),
371        };
372        records.push(Record {
373            kind,
374            name: (!name.is_empty()).then_some(name),
375        });
376    }
377    if at != body.len() {
378        return Err(HandoffError::Truncated);
379    }
380    Ok(records)
381}
382
383/// The fuzz target's entry: decoding never panics, and an accepted manifest
384/// encodes back to the same bytes.
385#[cfg(fuzzing)]
386pub(crate) fn fuzz_manifest(data: &[u8]) {
387    if let Ok(records) = decode(data) {
388        assert_eq!(encode(&records), data);
389    }
390}
391
392/// Sends `listeners` over `channel` in one message: the manifest and every
393/// socket (`SCM_RIGHTS`). The listeners stay open here; the receiver gets its
394/// own copies. At most 64.
395///
396/// # Errors
397///
398/// `TooMany`, `Timeout`, `Os`.
399#[cfg(all(unix, feature = "local"))]
400pub fn send_listeners(
401    channel: &mut crate::local::UnixConnection,
402    listeners: &[&ListenerHandle],
403) -> Result<(), HandoffError> {
404    use std::os::fd::AsFd;
405    if listeners.len() > MAX_LISTENERS {
406        return Err(HandoffError::TooMany {
407            max: MAX_LISTENERS as u8,
408        });
409    }
410    let mut records = Vec::with_capacity(listeners.len());
411    for (i, l) in listeners.iter().enumerate() {
412        let kind = sys::listener_kind(l.as_fd()).map_err(|e| HandoffError::VerifyFailed {
413            index: i as u8,
414            why: why(&e),
415        })?;
416        records.push(Record {
417            kind,
418            name: l.name.clone(),
419        });
420    }
421    let manifest = encode(&records);
422    let fds: Vec<_> = listeners.iter().map(|l| l.as_fd()).collect();
423    channel
424        .send_descriptors(&manifest, &fds)
425        .map_err(handoff_error)
426}
427
428/// Receives listeners from `channel`. The sender's credentials must match
429/// `expect` (by default: the same user); the message must be complete, intact
430/// (magic, version, CRC32C), carry exactly as many sockets as it lists, and
431/// every socket must be a listener bound to the address the manifest names.
432/// All or nothing: on any error every received socket is closed.
433///
434/// # Errors
435///
436/// `PeerMismatch`, `Timeout` (nothing arrived by `deadline`), `Truncated` (the
437/// sender closed mid-message or the control data was cut), `BadMagic`,
438/// `BadVersion`, `BadChecksum`, `CountMismatch`, `TooMany`, `VerifyFailed`, `Os`.
439#[cfg(all(unix, feature = "local"))]
440pub fn recv_listeners(
441    channel: &mut crate::local::UnixConnection,
442    expect: &HandoffPeer,
443    deadline: std::time::Instant,
444) -> Result<Vec<ListenerHandle>, HandoffError> {
445    use crate::local::Descriptors;
446    let creds = channel
447        .peer_credentials()
448        .map_err(|_| HandoffError::PeerMismatch)?;
449    let uid = expect.uid.unwrap_or_else(sys::effective_uid);
450    if creds.uid != Some(uid) || expect.pid.is_some_and(|p| creds.pid != Some(p)) {
451        return Err(HandoffError::PeerMismatch);
452    }
453    channel.set_phase(crate::session::Phase::Receiving);
454    channel.set_exchange_deadline(Some(deadline));
455    let result = receive(channel);
456    channel.set_exchange_deadline(None);
457    let (records, mut fds): (Vec<Record>, Descriptors) = result?;
458    if fds.len() != records.len() {
459        return Err(HandoffError::CountMismatch);
460    }
461    let mut handles = Vec::with_capacity(records.len());
462    for (i, (record, fd)) in records.into_iter().zip(fds.take()).enumerate() {
463        let index = i as u8;
464        let got = sys::listener_kind(std::os::fd::AsFd::as_fd(&fd)).map_err(|e| {
465            HandoffError::VerifyFailed {
466                index,
467                why: why(&e),
468            }
469        })?;
470        if got != record.kind {
471            return Err(HandoffError::VerifyFailed {
472                index,
473                why: "bound to another address than the manifest says",
474            });
475        }
476        let (kind, local) = endpoint_of(got).map_err(|e| HandoffError::VerifyFailed {
477            index,
478            why: why(&e),
479        })?;
480        handles.push(ListenerHandle::new(kind, record.name, local, fd));
481    }
482    Ok(handles)
483}
484
485/// Reads the manifest and its descriptors.
486#[cfg(all(unix, feature = "local"))]
487fn receive(
488    channel: &mut crate::local::UnixConnection,
489) -> Result<(Vec<Record>, crate::local::Descriptors), HandoffError> {
490    use crate::local::Descriptors;
491    let mut fds = Descriptors::new();
492    let mut header = [0u8; HEADER];
493    let mut got = 0;
494    while got < HEADER {
495        // The descriptors travel with the first byte; a later read must bring none.
496        let mut extra = Descriptors::new();
497        let into = if got == 0 { &mut fds } else { &mut extra };
498        let n = channel
499            .recv_descriptors(&mut header[got..], into)
500            .map_err(handoff_error)?;
501        if n == 0 {
502            return Err(HandoffError::Truncated);
503        }
504        if !extra.is_empty() {
505            return Err(HandoffError::CountMismatch);
506        }
507        got += n;
508    }
509    let (_, total) = parse_header(&header)?;
510    let mut manifest = vec![0u8; total];
511    manifest[..HEADER].copy_from_slice(&header);
512    let mut at = HEADER;
513    while at < total {
514        let mut extra = Descriptors::new();
515        let n = channel
516            .recv_descriptors(&mut manifest[at..], &mut extra)
517            .map_err(handoff_error)?;
518        if n == 0 {
519            return Err(HandoffError::Truncated);
520        }
521        if !extra.is_empty() {
522            return Err(HandoffError::CountMismatch);
523        }
524        at += n;
525    }
526    let records = decode(&manifest)?;
527    Ok((records, fds))
528}
529
530#[cfg(feature = "local")]
531fn handoff_error(e: crate::error::NetError) -> HandoffError {
532    use crate::error::NetError;
533    match e {
534        NetError::TimedOut { .. } => HandoffError::Timeout,
535        NetError::Handoff(h) => h,
536        NetError::Closed => HandoffError::Truncated,
537        other => HandoffError::Os(io::Error::from(other)),
538    }
539}
540
541fn why(e: &KindError) -> &'static str {
542    match e {
543        KindError::NotASocket => "not a socket",
544        KindError::NotListening => "not listening",
545        KindError::WrongKind => "not a TCP or Unix stream listener",
546        KindError::Os(_) => "could not be inspected",
547    }
548}
549
550/// Listeners described for one process, ready to go over a pipe once.
551///
552/// ```no_run
553/// use net_io::{PipeConnection, share_listeners};
554/// # fn f(handles: Vec<net_io::ListenerHandle>, pid: u32, mut pipe: PipeConnection)
555/// #     -> Result<(), net_io::HandoffError> {
556/// let shared = share_listeners(&handles.iter().collect::<Vec<_>>(), pid)?;
557/// shared.write_to(&mut pipe)?;
558/// # Ok(())
559/// # }
560/// ```
561#[cfg(all(windows, feature = "local"))]
562#[cfg_attr(docsrs, doc(cfg(windows)))]
563pub struct SharedListeners {
564    frame: Vec<u8>,
565}
566
567#[cfg(all(windows, feature = "local"))]
568impl std::fmt::Debug for SharedListeners {
569    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
570        f.debug_struct("SharedListeners").finish_non_exhaustive()
571    }
572}
573
574#[cfg(all(windows, feature = "local"))]
575const NONCE: usize = 16;
576
577/// Nonces this process has accepted. Windows lets a description be used twice and
578/// returns the same socket; this makes it once.
579#[cfg(all(windows, feature = "local"))]
580static ACCEPTED: std::sync::Mutex<Vec<[u8; NONCE]>> = std::sync::Mutex::new(Vec::new());
581
582/// Describes `listeners` for the process `target_pid`. Each description works in that
583/// process alone. Send them over a pipe with an owner-only security descriptor, which
584/// is what [`PipeListener::create`](crate::PipeListener::create) makes.
585///
586/// # Errors
587///
588/// `TooMany`, `VerifyFailed`, `Os`.
589#[cfg(all(windows, feature = "local"))]
590#[cfg_attr(docsrs, doc(cfg(windows)))]
591pub fn share_listeners(
592    listeners: &[&ListenerHandle],
593    target_pid: u32,
594) -> Result<SharedListeners, HandoffError> {
595    use std::os::windows::io::AsSocket;
596    if listeners.len() > MAX_LISTENERS {
597        return Err(HandoffError::TooMany {
598            max: MAX_LISTENERS as u8,
599        });
600    }
601    let mut records = Vec::with_capacity(listeners.len());
602    let mut tail = vec![0u8; NONCE];
603    sys::random_bytes(&mut tail).map_err(HandoffError::Os)?;
604    for (i, l) in listeners.iter().enumerate() {
605        let kind = sys::listener_kind(l.as_socket()).map_err(|e| HandoffError::VerifyFailed {
606            index: i as u8,
607            why: why(&e),
608        })?;
609        records.push(Record {
610            kind,
611            name: l.name.clone(),
612        });
613        let blob = sys::share_socket(l.as_socket(), target_pid).map_err(HandoffError::Os)?;
614        tail.extend_from_slice(&blob);
615    }
616    let mut crc = crate::crc32c::Crc32c::new();
617    crc.update(&tail);
618    let check = crc.finish().to_be_bytes();
619    let mut frame = encode(&records);
620    frame.extend_from_slice(&tail);
621    frame.extend_from_slice(&check);
622    Ok(SharedListeners { frame })
623}
624
625#[cfg(all(windows, feature = "local"))]
626impl SharedListeners {
627    /// Sends the listeners; consumed, so they go once.
628    ///
629    /// # Errors
630    ///
631    /// `Timeout`, `Truncated`, `Os`.
632    pub fn write_to(self, pipe: &mut crate::pipe::PipeConnection) -> Result<(), HandoffError> {
633        pipe.send_all(&self.frame).map_err(handoff_error)
634    }
635}
636
637/// Receives listeners sent with [`SharedListeners::write_to`]. The process at the
638/// far end must be `expect.pid` when that is set; `expect.uid` has no meaning here,
639/// since the pipe's security descriptor decides who gets to the pipe. The message
640/// must be intact and never seen before, and every socket a listener bound to the
641/// address the manifest names. All or nothing: on any error every received socket
642/// is closed.
643///
644/// # Errors
645///
646/// `PeerMismatch`, `Timeout`, `Truncated`, `BadMagic`, `BadVersion`, `BadChecksum`,
647/// `TooMany`, `VerifyFailed` (also for a message accepted before), `Os`.
648#[cfg(all(windows, feature = "local"))]
649#[cfg_attr(docsrs, doc(cfg(windows)))]
650pub fn receive_shared(
651    pipe: &mut crate::pipe::PipeConnection,
652    expect: &HandoffPeer,
653    deadline: std::time::Instant,
654) -> Result<Vec<ListenerHandle>, HandoffError> {
655    let peer = pipe.peer_pid().map_err(|_| HandoffError::PeerMismatch)?;
656    if expect.pid.is_some_and(|p| p != peer) {
657        return Err(HandoffError::PeerMismatch);
658    }
659    pipe.set_phase(crate::session::Phase::Receiving);
660    pipe.set_exchange_deadline(Some(deadline));
661    let result = read_shared(pipe);
662    pipe.set_exchange_deadline(None);
663    let (records, tail) = result?;
664    let (nonce, blobs) = tail.split_at(NONCE);
665    let nonce: [u8; NONCE] = nonce.try_into().map_err(|_| HandoffError::Truncated)?;
666    adopt_shared(records, nonce, blobs)
667}
668
669#[cfg(all(windows, feature = "local"))]
670fn read_shared(
671    pipe: &mut crate::pipe::PipeConnection,
672) -> Result<(Vec<Record>, Vec<u8>), HandoffError> {
673    let mut header = [0u8; HEADER];
674    pipe.recv_exact(&mut header).map_err(handoff_error)?;
675    let (count, total) = parse_header(&header)?;
676    let mut manifest = vec![0u8; total];
677    manifest[..HEADER].copy_from_slice(&header);
678    pipe.recv_exact(&mut manifest[HEADER..])
679        .map_err(handoff_error)?;
680    let records = decode(&manifest)?;
681    let mut tail = vec![0u8; NONCE + count * sys::SHARED_SOCKET + 4];
682    pipe.recv_exact(&mut tail).map_err(handoff_error)?;
683    let check_at = tail.len() - 4;
684    let mut crc = crate::crc32c::Crc32c::new();
685    crc.update(&tail[..check_at]);
686    if crc.finish().to_be_bytes() != tail[check_at..] {
687        return Err(HandoffError::BadChecksum);
688    }
689    tail.truncate(check_at);
690    Ok((records, tail))
691}
692
693#[cfg(all(windows, feature = "local"))]
694fn adopt_shared(
695    records: Vec<Record>,
696    nonce: [u8; NONCE],
697    blobs: &[u8],
698) -> Result<Vec<ListenerHandle>, HandoffError> {
699    {
700        let mut seen = ACCEPTED
701            .lock()
702            .unwrap_or_else(std::sync::PoisonError::into_inner);
703        if seen.contains(&nonce) {
704            return Err(HandoffError::VerifyFailed {
705                index: 0,
706                why: "this hand-off was already accepted",
707            });
708        }
709        seen.push(nonce);
710    }
711    let mut handles = Vec::with_capacity(records.len());
712    for (i, (record, blob)) in records
713        .into_iter()
714        .zip(blobs.chunks_exact(sys::SHARED_SOCKET))
715        .enumerate()
716    {
717        let index = i as u8;
718        let blob: &[u8; sys::SHARED_SOCKET] =
719            blob.try_into().map_err(|_| HandoffError::Truncated)?;
720        let sock = sys::socket_from_shared(blob).map_err(HandoffError::Os)?;
721        let got = sys::listener_kind(sys::AsSock::sock(&sock)).map_err(|e| {
722            HandoffError::VerifyFailed {
723                index,
724                why: why(&e),
725            }
726        })?;
727        if got != record.kind {
728            return Err(HandoffError::VerifyFailed {
729                index,
730                why: "bound to another address than the manifest says",
731            });
732        }
733        let (kind, local) = endpoint_of(got).map_err(|e| HandoffError::VerifyFailed {
734            index,
735            why: why(&e),
736        })?;
737        handles.push(ListenerHandle::new(kind, record.name, local, sock));
738    }
739    Ok(handles)
740}
741
742#[cfg(test)]
743mod tests {
744    use super::*;
745
746    fn records() -> Vec<Record> {
747        #[cfg_attr(not(unix), allow(unused_mut))]
748        let mut v = vec![
749            Record {
750                kind: SockKind::Tcp("127.0.0.1:8080".parse().unwrap()),
751                name: Some("web".into()),
752            },
753            Record {
754                kind: SockKind::Tcp("[2001:db8::1]:443".parse().unwrap()),
755                name: None,
756            },
757        ];
758        #[cfg(unix)]
759        v.push(Record {
760            kind: SockKind::Unix(b"/run/app/api.sock".to_vec()),
761            name: Some("api".into()),
762        });
763        v
764    }
765
766    #[test]
767    fn test_manifest_round_trips() {
768        let r = records();
769        assert_eq!(decode(&encode(&r)).unwrap(), r);
770        assert_eq!(decode(&encode(&[])).unwrap(), Vec::new());
771    }
772
773    #[test]
774    fn test_manifest_rejects_damage() {
775        let good = encode(&records());
776        let mut m = good.clone();
777        m[0] = b'X';
778        assert!(matches!(decode(&m), Err(HandoffError::BadMagic)));
779        let mut m = good.clone();
780        m[4] = 2;
781        assert!(matches!(decode(&m), Err(HandoffError::BadVersion)));
782        let mut m = good.clone();
783        let mid = m.len() / 2;
784        m[mid] ^= 1;
785        assert!(matches!(decode(&m), Err(HandoffError::BadChecksum)));
786        assert!(matches!(
787            decode(&good[..good.len() - 1]),
788            Err(HandoffError::Truncated)
789        ));
790        let mut m = good.clone();
791        m[5] = 65;
792        assert!(matches!(decode(&m), Err(HandoffError::TooMany { max: 64 })));
793        assert!(matches!(decode(&good[..3]), Err(HandoffError::Truncated)));
794    }
795
796    #[test]
797    fn test_long_names_are_cut_at_a_character_boundary() {
798        let r = [Record {
799            kind: SockKind::Tcp("127.0.0.1:1".parse().unwrap()),
800            name: Some("é".repeat(40)),
801        }];
802        let back = decode(&encode(&r)).unwrap();
803        let name = back[0].name.clone().unwrap();
804        assert_eq!(name.len(), 64);
805        assert!(name.chars().all(|c| c == 'é'));
806    }
807
808    /// A connected channel pair in a fresh directory: (sender, receiver).
809    #[cfg(all(unix, feature = "local"))]
810    fn channel(
811        tag: &str,
812    ) -> (
813        crate::local::UnixConnection,
814        crate::local::UnixConnection,
815        std::path::PathBuf,
816    ) {
817        let dir = std::env::temp_dir().join(format!("nio-ho-{}-{tag}", std::process::id()));
818        let _ = std::fs::remove_dir_all(&dir);
819        std::fs::create_dir(&dir).unwrap();
820        let l = crate::local::UnixListener::bind(dir.join("c.sock")).unwrap();
821        let tx = crate::local::UnixConnection::connect(dir.join("c.sock")).unwrap();
822        let (rx, _) = l.accept().unwrap();
823        (tx, rx, dir)
824    }
825
826    #[cfg(all(unix, feature = "local"))]
827    #[test]
828    fn test_count_mismatch_truncation_timeout_and_peer() {
829        use std::os::fd::AsFd;
830        use std::time::{Duration, Instant};
831        let soon = || Instant::now() + Duration::from_secs(5);
832        let l = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
833        let r = Record {
834            kind: SockKind::Tcp(l.local_addr().unwrap()),
835            name: None,
836        };
837        // Edge case 83: the manifest lists two, one socket came.
838        let (mut tx, mut rx, d1) = channel("count");
839        tx.send_descriptors(&encode(&[r.clone(), r.clone()]), &[l.as_fd()])
840            .unwrap();
841        assert!(matches!(
842            recv_listeners(&mut rx, &HandoffPeer::default(), soon()),
843            Err(HandoffError::CountMismatch)
844        ));
845        // Edge case 103: the sender dies mid-message: nothing adopted.
846        let (mut tx, mut rx, d2) = channel("dies");
847        let m = encode(std::slice::from_ref(&r));
848        tx.send_descriptors(&m[..m.len() / 2], &[l.as_fd()])
849            .unwrap();
850        drop(tx);
851        assert!(matches!(
852            recv_listeners(&mut rx, &HandoffPeer::default(), soon()),
853            Err(HandoffError::Truncated)
854        ));
855        // Edge case 102: nothing arrives in time.
856        let (_tx, mut rx, d3) = channel("timeout");
857        let started = Instant::now();
858        assert!(matches!(
859            recv_listeners(
860                &mut rx,
861                &HandoffPeer::default(),
862                Instant::now() + Duration::from_millis(200)
863            ),
864            Err(HandoffError::Timeout)
865        ));
866        assert!(started.elapsed() < Duration::from_secs(3));
867        // The wrong sender.
868        let (_tx, mut rx, d4) = channel("peer");
869        let other = HandoffPeer {
870            uid: Some(sys::effective_uid().wrapping_add(1)),
871            pid: None,
872        };
873        assert!(matches!(
874            recv_listeners(&mut rx, &other, soon()),
875            Err(HandoffError::PeerMismatch)
876        ));
877        let wrong_pid = HandoffPeer {
878            uid: None,
879            pid: Some(1),
880        };
881        assert!(matches!(
882            recv_listeners(&mut rx, &wrong_pid, soon()),
883            Err(HandoffError::PeerMismatch)
884        ));
885        for d in [d1, d2, d3, d4] {
886            let _ = std::fs::remove_dir_all(d);
887        }
888    }
889
890    /// A socket bound elsewhere than the manifest says is refused.
891    #[cfg(all(unix, feature = "local"))]
892    #[test]
893    fn test_address_must_match_the_manifest() {
894        use std::os::fd::AsFd;
895        use std::time::{Duration, Instant};
896        let a = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
897        let b = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
898        let (mut tx, mut rx, dir) = channel("addr");
899        let lie = Record {
900            kind: SockKind::Tcp(b.local_addr().unwrap()),
901            name: None,
902        };
903        tx.send_descriptors(&encode(&[lie]), &[a.as_fd()]).unwrap();
904        assert!(matches!(
905            recv_listeners(
906                &mut rx,
907                &HandoffPeer::default(),
908                Instant::now() + Duration::from_secs(5)
909            ),
910            Err(HandoffError::VerifyFailed { index: 0, .. })
911        ));
912        let _ = std::fs::remove_dir_all(dir);
913    }
914
915    #[test]
916    fn test_handle_verifies_its_socket() {
917        let l = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
918        let addr = l.local_addr().unwrap();
919        let h = ListenerHandle::verified(sys::OwnedSock::from(l), Some("x".into())).unwrap();
920        assert_eq!(h.kind(), Transport::Tcp);
921        assert_eq!(h.local().socket_addr(), Some(addr));
922        assert_eq!(h.name(), Some("x"));
923        let u = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
924        assert!(matches!(
925            ListenerHandle::verified(sys::OwnedSock::from(u), None),
926            Err(KindError::WrongKind)
927        ));
928    }
929
930    /// Edge case 104: the same message twice is refused before any `WSASocketW`.
931    #[cfg(all(windows, feature = "local"))]
932    #[test]
933    fn test_a_hand_off_works_once() {
934        use std::net::TcpListener as Std;
935        use std::time::{Duration, Instant};
936        let std = Std::bind("127.0.0.1:0").unwrap();
937        let addr = std.local_addr().unwrap();
938        let handle =
939            ListenerHandle::verified(sys::OwnedSock::from(std), Some("a".to_owned())).unwrap();
940        let name = format!("net-io-handoff-{}", std::process::id());
941        let listener = crate::pipe::PipeListener::create(&name).unwrap();
942        let mut tx = crate::pipe::PipeConnection::connect(&name, Duration::from_secs(1)).unwrap();
943        let (mut rx, _) = listener.accept().unwrap();
944        let shared = share_listeners(&[&handle], std::process::id()).unwrap();
945        let frame = shared.frame.clone();
946        tx.send_all(&frame).unwrap();
947        tx.send_all(&frame).unwrap();
948        let until = Instant::now() + Duration::from_secs(5);
949        let peer = HandoffPeer {
950            uid: None,
951            pid: Some(std::process::id()),
952        };
953        let got = receive_shared(&mut rx, &peer, until).unwrap();
954        assert_eq!(got[0].name(), Some("a"));
955        assert_eq!(got[0].local().socket_addr(), Some(addr));
956        assert!(matches!(
957            receive_shared(&mut rx, &peer, until),
958            Err(HandoffError::VerifyFailed { .. })
959        ));
960    }
961
962    #[cfg(all(windows, feature = "local"))]
963    #[test]
964    fn test_wrong_peer_and_bad_checksum() {
965        use std::net::TcpListener as Std;
966        use std::time::{Duration, Instant};
967        let std = Std::bind("127.0.0.1:0").unwrap();
968        let handle = ListenerHandle::verified(sys::OwnedSock::from(std), None).unwrap();
969        let name = format!("net-io-handoff-b-{}", std::process::id());
970        let listener = crate::pipe::PipeListener::create(&name).unwrap();
971        let mut tx = crate::pipe::PipeConnection::connect(&name, Duration::from_secs(1)).unwrap();
972        let (mut rx, _) = listener.accept().unwrap();
973        let until = Instant::now() + Duration::from_secs(5);
974        let wrong = HandoffPeer {
975            uid: None,
976            pid: Some(1),
977        };
978        assert!(matches!(
979            receive_shared(&mut rx, &wrong, until),
980            Err(HandoffError::PeerMismatch)
981        ));
982        let mut frame = share_listeners(&[&handle], std::process::id())
983            .unwrap()
984            .frame;
985        let last = frame.len() - 1;
986        frame[last] ^= 1;
987        tx.send_all(&frame).unwrap();
988        assert!(matches!(
989            receive_shared(&mut rx, &HandoffPeer::default(), until),
990            Err(HandoffError::BadChecksum)
991        ));
992    }
993
994    #[cfg(all(windows, feature = "local"))]
995    #[test]
996    fn test_nothing_arrives_by_the_deadline() {
997        use std::time::{Duration, Instant};
998        let name = format!("net-io-handoff-c-{}", std::process::id());
999        let listener = crate::pipe::PipeListener::create(&name).unwrap();
1000        let _tx = crate::pipe::PipeConnection::connect(&name, Duration::from_secs(1)).unwrap();
1001        let (mut rx, _) = listener.accept().unwrap();
1002        let until = Instant::now() + Duration::from_millis(50);
1003        assert!(matches!(
1004            receive_shared(&mut rx, &HandoffPeer::default(), until),
1005            Err(HandoffError::Timeout)
1006        ));
1007    }
1008}