1#![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#[non_exhaustive]
40#[derive(Debug)]
41pub enum HandoffError {
42 PeerMismatch,
44 Truncated,
46 CountMismatch,
48 BadMagic,
50 BadVersion,
52 BadChecksum,
54 TooMany {
56 max: u8,
58 },
59 VerifyFailed {
61 index: u8,
63 why: &'static str,
65 },
66 Timeout,
68 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
106pub struct ListenerHandle {
111 kind: Transport,
112 name: Option<String>,
113 local: Endpoint,
114 sock: sys::OwnedSock,
115}
116
117impl ListenerHandle {
118 #[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 #[must_use]
155 pub fn kind(&self) -> Transport {
156 self.kind
157 }
158
159 #[must_use]
161 pub fn name(&self) -> Option<&str> {
162 self.name.as_deref()
163 }
164
165 #[must_use]
167 pub fn local(&self) -> &Endpoint {
168 &self.local
169 }
170}
171
172fn 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#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
221pub struct HandoffPeer {
222 pub uid: Option<u32>,
224 pub pid: Option<u32>,
226}
227
228const MAGIC: [u8; 4] = *b"NIOH";
229const VERSION: u8 = 1;
230const HEADER: usize = 8;
231const MAX_LISTENERS: usize = 64;
233const MAX_NAME: usize = 64;
235
236#[derive(Clone, Debug, PartialEq, Eq)]
238struct Record {
239 kind: SockKind,
240 name: Option<String>,
241}
242
243fn 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]); 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
295fn 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
316fn 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#[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#[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#[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#[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 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#[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#[cfg(all(windows, feature = "local"))]
580static ACCEPTED: std::sync::Mutex<Vec<[u8; NONCE]>> = std::sync::Mutex::new(Vec::new());
581
582#[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 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#[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 #[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 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 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 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 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 #[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 #[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}