1use std::{
11 io::{IoSlice, IoSliceMut},
12 os::unix::{
13 io::{FromRawFd, IntoRawFd, RawFd},
14 net::UnixStream as StdUnixStream,
15 },
16};
17
18use mio::net::{TcpListener, UdpSocket};
19use nix::{cmsg_space, sys::socket};
20use prost::{DecodeError, Message};
21
22use crate::{
23 listener_key::{ListenerKey, ListenerKeyError},
24 proto::command::ListenersCount,
25};
26
27pub const MAX_FDS_OUT: usize = 200;
28pub const MAX_BYTES_OUT: usize = 4096;
29
30#[derive(thiserror::Error, Debug)]
31pub enum ScmSocketError {
32 #[error("could not set the blocking status of the unix stream to {blocking}: {error}")]
33 SetBlocking {
34 blocking: bool,
35 error: std::io::Error,
36 },
37 #[error("could not send message for SCM socket: {0}")]
38 Send(String),
39 #[error("could not receive message for SCM socket: {0}")]
40 Receive(String),
41 #[error("invalid char set: {0}")]
42 InvalidCharSet(String),
43 #[error("Could not deserialize utf8 string into listeners: {0}")]
44 ListenerParse(String),
45 #[error("Wrong listener key {address}: {error}")]
46 WrongSocketAddress {
47 address: String,
48 error: ListenerKeyError,
49 },
50 #[error("error decoding the protobuf format of the listeners: {0}")]
51 DecodeError(DecodeError),
52 #[error(
53 "listeners count manifest is inconsistent with the SCM payload: \
54 http={http}, tls={tls}, tcp={tcp} (sum={total}), fds_received={fds_received}, max_fds={max_fds}"
55 )]
56 ListenersCountInconsistent {
57 http: usize,
58 tls: usize,
59 tcp: usize,
60 total: usize,
61 fds_received: usize,
62 max_fds: usize,
63 },
64}
65
66#[derive(Clone, Debug, Serialize, Deserialize)]
68pub struct ScmSocket {
69 pub fd: RawFd,
70 pub blocking: bool,
71}
72
73impl ScmSocket {
74 pub fn new(fd: RawFd) -> Result<Self, ScmSocketError> {
76 unsafe {
81 let stream = StdUnixStream::from_raw_fd(fd);
82 stream
83 .set_nonblocking(false)
84 .map_err(|error| ScmSocketError::SetBlocking {
85 blocking: false,
86 error,
87 })?;
88 let _dropped_fd = stream.into_raw_fd();
89 }
90
91 Ok(ScmSocket { fd, blocking: true })
92 }
93
94 pub fn raw_fd(&self) -> i32 {
96 self.fd
97 }
98
99 pub fn set_blocking(&mut self, blocking: bool) -> Result<(), ScmSocketError> {
101 if self.blocking == blocking {
102 return Ok(());
103 }
104 debug_assert_ne!(
106 self.blocking, blocking,
107 "set_blocking only reaches the syscall when the state actually changes"
108 );
109 let blocking_before = self.blocking;
110 unsafe {
115 let stream = StdUnixStream::from_raw_fd(self.fd);
116 stream
117 .set_nonblocking(!blocking)
118 .map_err(|error| ScmSocketError::SetBlocking { blocking, error })?;
119 let _dropped_fd = stream.into_raw_fd();
120 }
121 self.blocking = blocking;
122 debug_assert_eq!(
124 self.blocking, blocking,
125 "blocking flag must reflect the requested state after a successful set"
126 );
127 debug_assert_ne!(
128 self.blocking, blocking_before,
129 "blocking flag must have toggled across a real state change"
130 );
131 Ok(())
132 }
133
134 pub fn send_listeners(&self, listeners: &Listeners) -> Result<(), ScmSocketError> {
136 let listeners_count = ListenersCount {
137 http: listeners.http.iter().map(|t| t.0.to_string()).collect(),
138 tls: listeners.tls.iter().map(|t| t.0.to_string()).collect(),
139 tcp: listeners.tcp.iter().map(|t| t.0.to_string()).collect(),
140 udp: listeners.udp.iter().map(|t| t.0.to_string()).collect(),
141 };
142
143 debug_assert_eq!(
149 listeners_count.http.len(),
150 listeners.http.len(),
151 "http manifest count must match the http listener table"
152 );
153 debug_assert_eq!(
154 listeners_count.tls.len(),
155 listeners.tls.len(),
156 "tls manifest count must match the tls listener table"
157 );
158 debug_assert_eq!(
159 listeners_count.tcp.len(),
160 listeners.tcp.len(),
161 "tcp manifest count must match the tcp listener table"
162 );
163
164 let message = listeners_count.encode_length_delimited_to_vec();
165
166 let mut file_descriptors: Vec<RawFd> = Vec::new();
167
168 file_descriptors.extend(listeners.http.iter().map(|t| t.1));
169 file_descriptors.extend(listeners.tls.iter().map(|t| t.1));
170 file_descriptors.extend(listeners.tcp.iter().map(|t| t.1));
171 file_descriptors.extend(listeners.udp.iter().map(|t| t.1));
172
173 let address_total =
177 listeners.http.len() + listeners.tls.len() + listeners.tcp.len() + listeners.udp.len();
178 debug_assert_eq!(
179 file_descriptors.len(),
180 address_total,
181 "the FD count sent must equal the total listener-address count (one FD per address)"
182 );
183
184 self.send_msg_and_fds(&message, &file_descriptors)
185 }
186
187 pub fn receive_listeners(&self) -> Result<Listeners, ScmSocketError> {
189 let mut buf = vec![0; MAX_BYTES_OUT];
190
191 let mut received_fds: [RawFd; MAX_FDS_OUT] = [0; MAX_FDS_OUT];
192
193 let (size, file_descriptor_length) =
194 self.receive_msg_and_fds(&mut buf, &mut received_fds)?;
195
196 debug!("{} received :{:?}", self.fd, (size, file_descriptor_length));
197
198 let listeners_count = ListenersCount::decode_length_delimited(&buf[..size])
199 .map_err(ScmSocketError::DecodeError)?;
200
201 let http_len = listeners_count.http.len();
210 let tls_len = listeners_count.tls.len();
211 let tcp_len = listeners_count.tcp.len();
212 let udp_len = listeners_count.udp.len();
213 let total = http_len
214 .checked_add(tls_len)
215 .and_then(|s| s.checked_add(tcp_len))
216 .and_then(|s| s.checked_add(udp_len))
217 .ok_or(ScmSocketError::ListenersCountInconsistent {
218 http: http_len,
219 tls: tls_len,
220 tcp: tcp_len.saturating_add(udp_len),
221 total: usize::MAX,
222 fds_received: file_descriptor_length,
223 max_fds: MAX_FDS_OUT,
224 })?;
225 if total > MAX_FDS_OUT || total > file_descriptor_length {
226 return Err(ScmSocketError::ListenersCountInconsistent {
227 http: http_len,
228 tls: tls_len,
229 tcp: tcp_len.saturating_add(udp_len),
230 total,
231 fds_received: file_descriptor_length,
232 max_fds: MAX_FDS_OUT,
233 });
234 }
235
236 debug_assert_eq!(
242 total,
243 http_len + tls_len + tcp_len + udp_len,
244 "folded total must equal the sum of per-protocol counts"
245 );
246 debug_assert!(
247 total <= MAX_FDS_OUT,
248 "total FD slots must fit the fixed-size received_fds array before indexing"
249 );
250 debug_assert!(
251 total <= file_descriptor_length,
252 "manifest total must not exceed the FDs actually received"
253 );
254 debug_assert!(
255 total <= received_fds.len(),
256 "every (address, fd) zip below must stay within the received_fds array"
257 );
258
259 let mut http_addresses = parse_addresses(&listeners_count.http)?;
260 let mut tls_addresses = parse_addresses(&listeners_count.tls)?;
261 let mut tcp_addresses = parse_addresses(&listeners_count.tcp)?;
262 let mut udp_addresses = parse_addresses(&listeners_count.udp)?;
263
264 debug_assert_eq!(
267 http_addresses.len(),
268 http_len,
269 "parsed http address count must match the manifest count"
270 );
271 debug_assert_eq!(
272 tls_addresses.len(),
273 tls_len,
274 "parsed tls address count must match the manifest count"
275 );
276 debug_assert_eq!(
277 tcp_addresses.len(),
278 tcp_len,
279 "parsed tcp address count must match the manifest count"
280 );
281
282 let mut index = 0;
283 let len = http_len;
284 debug_assert!(
287 index + len <= total,
288 "http FD slice must lie within the reconciled total"
289 );
290 let mut http = Vec::new();
291 http.extend(
292 http_addresses
293 .drain(..)
294 .zip(received_fds[index..index + len].iter().cloned()),
295 );
296 debug_assert_eq!(
298 http.len(),
299 http_len,
300 "every http address must be paired with exactly one FD"
301 );
302
303 index += len;
304 let len = tls_len;
305 debug_assert!(
306 index + len <= total,
307 "tls FD slice must lie within the reconciled total"
308 );
309 let mut tls = Vec::new();
310 tls.extend(
311 tls_addresses
312 .drain(..)
313 .zip(received_fds[index..index + len].iter().cloned()),
314 );
315 debug_assert_eq!(
316 tls.len(),
317 tls_len,
318 "every tls address must be paired with exactly one FD"
319 );
320
321 index += len;
322 let len = tcp_len;
323 debug_assert!(
324 index + len <= total,
325 "tcp FD slice must lie within the reconciled total"
326 );
327 let mut tcp = Vec::new();
328 tcp.extend(
329 tcp_addresses
330 .drain(..)
331 .zip(received_fds[index..index + len].iter().cloned()),
332 );
333 debug_assert_eq!(
334 tcp.len(),
335 tcp_len,
336 "every tcp address must be paired with exactly one FD"
337 );
338
339 index += len;
340 let len = udp_len;
341 let mut udp = Vec::new();
342 udp.extend(
343 udp_addresses
344 .drain(..)
345 .zip(received_fds[index..index + len].iter().cloned()),
346 );
347 debug_assert_eq!(
348 udp.len(),
349 udp_len,
350 "every udp address must be paired with exactly one FD"
351 );
352
353 debug_assert_eq!(
356 index + len,
357 total,
358 "the (address, fd) zips must consume exactly the reconciled total of FD slots"
359 );
360 debug_assert_eq!(
361 http.len() + tls.len() + tcp.len() + udp.len(),
362 total,
363 "reconstructed listener count must equal the reconciled FD total"
364 );
365
366 Ok(Listeners {
367 http,
368 tls,
369 tcp,
370 udp,
371 })
372 }
373
374 fn send_msg_and_fds(&self, message: &[u8], fds: &[RawFd]) -> Result<(), ScmSocketError> {
377 let iov = [IoSlice::new(message)];
378 let flags = if self.blocking {
379 socket::MsgFlags::empty()
380 } else {
381 socket::MsgFlags::MSG_DONTWAIT
382 };
383
384 if fds.is_empty() {
385 debug!("{} send empty", self.fd);
386 socket::sendmsg::<()>(self.fd, &iov, &[], flags, None)
387 .map_err(|error| ScmSocketError::Send(error.to_string()))?;
388 return Ok(());
389 };
390
391 let control_message = [socket::ControlMessage::ScmRights(fds)];
392 debug!("{} send with data", self.fd);
393 socket::sendmsg::<()>(self.fd, &iov, &control_message, flags, None)
394 .map_err(|error| ScmSocketError::Send(error.to_string()))?;
395 Ok(())
396 }
397
398 fn receive_msg_and_fds(
400 &self,
401 message: &mut [u8],
402 fds: &mut [RawFd],
403 ) -> Result<(usize, usize), ScmSocketError> {
404 let message_capacity = message.len();
407 let mut cmsg = cmsg_space!([RawFd; MAX_FDS_OUT]);
408 let mut iov = [IoSliceMut::new(message)];
409
410 let flags = if self.blocking {
411 socket::MsgFlags::empty()
412 } else {
413 socket::MsgFlags::MSG_DONTWAIT
414 };
415
416 #[cfg(any(
427 target_os = "linux",
428 target_os = "android",
429 target_os = "freebsd",
430 target_os = "dragonfly",
431 target_os = "netbsd",
432 target_os = "openbsd"
433 ))]
434 let flags = flags | socket::MsgFlags::MSG_CMSG_CLOEXEC;
435
436 let msg = socket::recvmsg::<()>(self.fd, &mut iov[..], Some(&mut cmsg), flags)
437 .map_err(|error| ScmSocketError::Receive(error.to_string()))?;
438
439 let fds_capacity = fds.len();
442 debug_assert!(
443 fds_capacity <= MAX_FDS_OUT,
444 "destination FD slice must not exceed the MAX_FDS_OUT cmsg space"
445 );
446 let mut fd_count = 0;
447 let received_fds = msg
448 .cmsgs()
449 .map_err(|error| ScmSocketError::Receive(error.to_string()))?
450 .filter_map(|cmsg| {
451 if let socket::ControlMessageOwned::ScmRights(s) = cmsg {
452 Some(s)
453 } else {
454 None
455 }
456 })
457 .flatten();
458 for (fd, place) in received_fds.zip(fds.iter_mut()) {
459 fd_count += 1;
460 *place = fd;
461 #[cfg(not(any(
462 target_os = "linux",
463 target_os = "android",
464 target_os = "freebsd",
465 target_os = "dragonfly",
466 target_os = "netbsd",
467 target_os = "openbsd"
468 )))]
469 unsafe {
473 libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC);
474 }
475 debug_assert!(
478 fd_count <= fds_capacity,
479 "received FD count must never exceed the destination array capacity"
480 );
481 }
482 debug_assert!(
485 fd_count <= fds_capacity,
486 "final received FD count must fit the destination array"
487 );
488 debug_assert!(
489 msg.bytes <= message_capacity,
490 "received byte count must not exceed the message buffer it was read into"
491 );
492 Ok((msg.bytes, fd_count))
493 }
494}
495
496#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq)]
501pub struct Listeners {
502 pub http: Vec<(ListenerKey, RawFd)>,
503 pub tls: Vec<(ListenerKey, RawFd)>,
504 pub tcp: Vec<(ListenerKey, RawFd)>,
505 #[serde(default)]
506 pub udp: Vec<(ListenerKey, RawFd)>,
507}
508
509impl Listeners {
510 pub fn get_http(&mut self, addr: &ListenerKey) -> Option<RawFd> {
511 let before = self.http.len();
512 let pos = self.http.iter().position(|(front, _)| front == addr);
513 let result = pos.map(|pos| self.http.remove(pos).1);
514 debug_assert_eq!(
517 self.http.len(),
518 before - result.is_some() as usize,
519 "http listener table shrinks by exactly one iff an address matched"
520 );
521 debug_assert!(
522 result.is_none() || !self.http.iter().any(|(front, _)| front == addr),
523 "the matched http address must no longer be present after removal"
524 );
525 result
526 }
527
528 pub fn get_https(&mut self, addr: &ListenerKey) -> Option<RawFd> {
529 let before = self.tls.len();
530 let pos = self.tls.iter().position(|(front, _)| front == addr);
531 let result = pos.map(|pos| self.tls.remove(pos).1);
532 debug_assert_eq!(
533 self.tls.len(),
534 before - result.is_some() as usize,
535 "tls listener table shrinks by exactly one iff an address matched"
536 );
537 debug_assert!(
538 result.is_none() || !self.tls.iter().any(|(front, _)| front == addr),
539 "the matched tls address must no longer be present after removal"
540 );
541 result
542 }
543
544 pub fn get_tcp(&mut self, addr: &ListenerKey) -> Option<RawFd> {
545 let before = self.tcp.len();
546 let pos = self.tcp.iter().position(|(front, _)| front == addr);
547 let result = pos.map(|pos| self.tcp.remove(pos).1);
548 debug_assert_eq!(
549 self.tcp.len(),
550 before - result.is_some() as usize,
551 "tcp listener table shrinks by exactly one iff an address matched"
552 );
553 debug_assert!(
554 result.is_none() || !self.tcp.iter().any(|(front, _)| front == addr),
555 "the matched tcp address must no longer be present after removal"
556 );
557 result
558 }
559
560 pub fn get_udp(&mut self, addr: &ListenerKey) -> Option<RawFd> {
561 let before = self.udp.len();
562 let pos = self.udp.iter().position(|(front, _)| front == addr);
563 let result = pos.map(|pos| self.udp.remove(pos).1);
564 debug_assert_eq!(
565 self.udp.len(),
566 before - result.is_some() as usize,
567 "udp listener table shrinks by exactly one iff an address matched"
568 );
569 debug_assert!(
570 result.is_none() || !self.udp.iter().any(|(front, _)| front == addr),
571 "the matched udp address must no longer be present after removal"
572 );
573 result
574 }
575
576 pub fn close(&self) {
578 for (_, fd) in &self.http {
579 unsafe {
583 let _ = TcpListener::from_raw_fd(*fd);
584 }
585 }
586
587 for (_, fd) in &self.tls {
588 unsafe {
592 let _ = TcpListener::from_raw_fd(*fd);
593 }
594 }
595
596 for (_, fd) in &self.tcp {
597 unsafe {
601 let _ = TcpListener::from_raw_fd(*fd);
602 }
603 }
604
605 for (_, fd) in &self.udp {
606 unsafe {
612 let _ = UdpSocket::from_raw_fd(*fd);
613 }
614 }
615 }
616}
617
618fn parse_addresses(addresses: &[String]) -> Result<Vec<ListenerKey>, ScmSocketError> {
623 let mut parsed_addresses = Vec::new();
624 for address in addresses {
625 parsed_addresses.push(address.parse::<ListenerKey>().map_err(|error| {
626 ScmSocketError::WrongSocketAddress {
627 address: address.to_owned(),
628 error,
629 }
630 })?);
631 }
632 Ok(parsed_addresses)
633}
634
635#[cfg(test)]
636mod tests {
637
638 use std::{net::SocketAddr, os::unix::prelude::AsRawFd, str::FromStr};
639
640 use mio::net::UnixStream as MioUnixStream;
641
642 use super::*;
643
644 #[test]
645 fn create_block_unblock_an_scm_socket() {
646 let (nonblocking_stream, _) =
647 MioUnixStream::pair().expect("Could not create a pair of unix streams");
648 let raw_file_descriptor = nonblocking_stream.into_raw_fd();
649
650 let scm_socket = ScmSocket::new(raw_file_descriptor);
651 assert!(scm_socket.is_ok());
652
653 let mut scm_socket = scm_socket.unwrap();
654
655 assert!(scm_socket.set_blocking(true).is_ok());
656 assert!(scm_socket.set_blocking(false).is_ok());
657 }
658
659 fn socket_addr_from_str(str: &str) -> SocketAddr {
660 SocketAddr::from_str(str)
661 .unwrap_or_else(|_| panic!("failed to create socket address from string slice {str}"))
662 }
663
664 #[test]
674 fn received_listener_descriptors_are_close_on_exec() {
675 let (stream_1, stream_2) =
676 MioUnixStream::pair().expect("Could not create a pair of mio unix streams");
677 let sending_scm_socket =
678 ScmSocket::new(stream_1.into_raw_fd()).expect("Could not create scm socket");
679 let receiving_scm_socket =
680 ScmSocket::new(stream_2.into_raw_fd()).expect("Could not create scm socket");
681
682 let tcp_listener =
683 std::net::TcpListener::bind("127.0.0.1:0").expect("Could not bind a TCP listener");
684 let udp_socket =
685 std::net::UdpSocket::bind("127.0.0.1:0").expect("Could not bind a UDP socket");
686 let listeners = Listeners {
687 http: vec![],
688 tls: vec![],
689 tcp: vec![(
690 tcp_listener.local_addr().expect("TCP local address").into(),
691 tcp_listener.as_raw_fd(),
692 )],
693 udp: vec![(
694 udp_socket.local_addr().expect("UDP local address").into(),
695 udp_socket.as_raw_fd(),
696 )],
697 };
698 sending_scm_socket
699 .send_listeners(&listeners)
700 .expect("Could not send listeners");
701
702 let received = receiving_scm_socket
703 .receive_listeners()
704 .expect("Could not receive listeners");
705 let received_fds: Vec<RawFd> = received
706 .tcp
707 .iter()
708 .chain(received.udp.iter())
709 .map(|(_, fd)| *fd)
710 .collect();
711 assert_eq!(received_fds.len(), 2, "both descriptors must arrive");
712 for fd in received_fds {
713 let flags = unsafe { libc::fcntl(fd, libc::F_GETFD) };
716 assert!(flags >= 0, "F_GETFD failed on received fd {fd}");
717 assert_ne!(
718 flags & libc::FD_CLOEXEC,
719 0,
720 "received fd {fd} must be close-on-exec so no exec'd child inherits it"
721 );
722 }
723 received.close();
724 }
725
726 #[test]
727 fn send_and_receive_empty_listeners() {
728 let (stream_1, stream_2) =
729 MioUnixStream::pair().expect("Could not create a pair of mio unix streams");
730
731 let sending_scm_socket =
732 ScmSocket::new(stream_1.into_raw_fd()).expect("Could not create scm socket");
733
734 let receiving_scm_socket =
735 ScmSocket::new(stream_2.as_raw_fd()).expect("Could not create scm socket");
736
737 let listeners = Listeners::default();
738
739 sending_scm_socket
740 .send_listeners(&listeners)
741 .expect("Could not send listeners");
742
743 let received_listeners = receiving_scm_socket
744 .receive_listeners()
745 .expect("Could not receive listeners");
746
747 assert_eq!(listeners, received_listeners);
748 }
749
750 #[test]
755 fn send_and_receive_listeners_keyed_by_interface() {
756 let (stream_1, stream_2) =
757 MioUnixStream::pair().expect("Could not create a pair of mio unix streams");
758 let sending_scm_socket =
759 ScmSocket::new(stream_1.into_raw_fd()).expect("Could not create scm socket");
760 let receiving_scm_socket =
761 ScmSocket::new(stream_2.into_raw_fd()).expect("Could not create scm socket");
762
763 let (bare, wg0) =
764 MioUnixStream::pair().expect("Could not create a pair of mio unix streams");
765 let (lo, udp) = MioUnixStream::pair().expect("Could not create a pair of mio unix streams");
766
767 let address = socket_addr_from_str("0.0.0.0:8443");
768 let bare_key = ListenerKey::from(address);
769 let wg0_key = ListenerKey::new(address, Some("wg0"));
770 let lo_key = ListenerKey::new(address, Some("lo"));
771 let listeners = Listeners {
772 http: vec![
773 (bare_key.clone(), bare.as_raw_fd()),
774 (wg0_key.clone(), wg0.as_raw_fd()),
775 (lo_key.clone(), lo.as_raw_fd()),
776 ],
777 udp: vec![(
778 ListenerKey::new(socket_addr_from_str("[::]:5353"), Some("wg0")),
779 udp.as_raw_fd(),
780 )],
781 ..Default::default()
782 };
783
784 sending_scm_socket
785 .send_listeners(&listeners)
786 .expect("Could not send listeners");
787 let mut received = receiving_scm_socket
788 .receive_listeners()
789 .expect("Could not receive listeners");
790
791 let keys: Vec<&ListenerKey> = received.http.iter().map(|(key, _)| key).collect();
792 assert_eq!(keys, vec![&bare_key, &wg0_key, &lo_key]);
793 assert_eq!(received.udp[0].0, listeners.udp[0].0);
794
795 let wg0_fd = received.get_http(&wg0_key).expect("the wg0 socket");
796 assert_eq!(received.http[1].0, lo_key, "only the wg0 entry is taken");
797 assert!(received.get_http(&wg0_key).is_none());
798 assert!(
799 received
800 .get_http(&ListenerKey::new(address, Some("eth0")))
801 .is_none(),
802 "an unknown interface on a known address matches nothing"
803 );
804 let bare_fd = received.get_http(&bare_key).expect("the bare socket");
805 assert_ne!(wg0_fd, bare_fd);
806 }
807
808 #[test]
809 fn send_and_receive_socket_addresses() {
810 let (stream_1, stream_2) =
811 MioUnixStream::pair().expect("Could not create a pair of mio unix streams");
812
813 println!("unix stream pair: {stream_1:?} and {stream_2:?}");
814 let sending_scm_socket =
815 ScmSocket::new(stream_1.into_raw_fd()).expect("Could not create scm socket");
816
817 println!("sending socket: {sending_scm_socket:?}");
818
819 let receiving_scm_socket =
820 ScmSocket::new(stream_2.into_raw_fd()).expect("Could not create scm socket");
821
822 println!("receiving socket: {receiving_scm_socket:?}");
823
824 let (http_socket1, http_socket2) =
826 MioUnixStream::pair().expect("Could not create a pair of mio unix streams");
827 let (tcp_socket1, tcp_socket2) =
828 MioUnixStream::pair().expect("Could not create a pair of mio unix streams");
829 let (tls_socket1, tls_socket2) =
830 MioUnixStream::pair().expect("Could not create a pair of mio unix streams");
831 let (udp_socket1, udp_socket2) =
832 MioUnixStream::pair().expect("Could not create a pair of mio unix streams");
833
834 let listeners = Listeners {
835 http: vec![
836 (
837 socket_addr_from_str("127.0.1.1:8080").into(),
838 http_socket1.as_raw_fd(),
839 ),
840 (
841 socket_addr_from_str("127.0.1.2:8080").into(),
842 http_socket2.as_raw_fd(),
843 ),
844 ],
845 tcp: vec![
846 (
847 socket_addr_from_str("127.0.2.1:8080").into(),
848 tcp_socket1.as_raw_fd(),
849 ),
850 (
851 socket_addr_from_str("127.0.2.2:8080").into(),
852 tcp_socket2.as_raw_fd(),
853 ),
854 ],
855 tls: vec![
856 (
857 socket_addr_from_str("127.0.3.1:8443").into(),
858 tls_socket1.as_raw_fd(),
859 ),
860 (
861 socket_addr_from_str("127.0.3.2:8443").into(),
862 tls_socket2.as_raw_fd(),
863 ),
864 ],
865 udp: vec![
866 (
867 socket_addr_from_str("127.0.4.1:5353").into(),
868 udp_socket1.as_raw_fd(),
869 ),
870 (
871 socket_addr_from_str("127.0.4.2:5353").into(),
872 udp_socket2.as_raw_fd(),
873 ),
874 ],
875 };
876
877 println!("self.fd: {}", sending_scm_socket.fd);
878 println!("listeners to send: {listeners:#?}");
879
880 sending_scm_socket
881 .send_listeners(&listeners)
882 .expect("Could not send listeners");
883
884 let received_listeners = receiving_scm_socket
885 .receive_listeners()
886 .expect("Could not receive listeners");
887
888 assert_eq!(listeners.http[0].0, received_listeners.http[0].0);
889 assert_eq!(listeners.udp.len(), received_listeners.udp.len());
890 assert_eq!(listeners.udp[0].0, received_listeners.udp[0].0);
891 assert_eq!(listeners.udp[1].0, received_listeners.udp[1].0);
892 }
893
894 #[test]
903 fn rejects_listeners_count_with_more_entries_than_fds() {
904 let (stream_1, stream_2) =
905 MioUnixStream::pair().expect("Could not create a pair of mio unix streams");
906 let sender = ScmSocket::new(stream_1.into_raw_fd()).expect("Could not create scm socket");
907 let receiver = ScmSocket::new(stream_2.into_raw_fd()).expect("Could not create scm socket");
908
909 let bogus = ListenersCount {
911 http: vec![
912 "127.0.0.1:80".to_string(),
913 "127.0.0.2:80".to_string(),
914 "127.0.0.3:80".to_string(),
915 ],
916 tls: vec![],
917 tcp: vec![],
918 udp: vec![],
919 };
920 let payload = bogus.encode_length_delimited_to_vec();
921 sender
922 .send_msg_and_fds(&payload, &[])
923 .expect("manual send_msg_and_fds with zero fds must succeed at the syscall layer");
924
925 match receiver.receive_listeners() {
926 Err(ScmSocketError::ListenersCountInconsistent {
927 http,
928 tls,
929 tcp,
930 total,
931 fds_received,
932 max_fds,
933 }) => {
934 assert_eq!(http, 3);
935 assert_eq!(tls, 0);
936 assert_eq!(tcp, 0);
937 assert_eq!(total, 3);
938 assert_eq!(fds_received, 0);
939 assert_eq!(max_fds, MAX_FDS_OUT);
940 }
941 other => panic!(
942 "expected ListenersCountInconsistent, got {other:?}\n\
943 NOTE: a panic / OOM here means the SCM bounds check was reverted",
944 ),
945 }
946 }
947}