1#![warn(
8 missing_docs,
9 unreachable_patterns,
10 clippy::useless_conversion,
11 clippy::redundant_clone,
12 clippy::precedence
13)]
14
15use fidl_fuchsia_net as fnet;
16use fidl_fuchsia_net_ext::{IntoExt, Marks};
17use fidl_fuchsia_net_matchers as fnet_matchers;
18use fidl_fuchsia_net_matchers_ext as fnet_matchers_ext;
19use fidl_fuchsia_net_sockets as fnet_sockets;
20use fidl_fuchsia_net_tcp as fnet_tcp;
21use fidl_fuchsia_net_udp as fnet_udp;
22use futures::{Stream, TryStreamExt as _};
23use net_types::ip::{self, GenericOverIp, Ip, IpInvariant, Ipv4, Ipv6};
24use thiserror::Error;
25
26#[derive(Debug, Clone, PartialEq, Eq)]
28pub enum IpSocketMatcher {
29 Family(ip::IpVersion),
31 SrcAddr(fnet_matchers_ext::BoundAddress),
33 DstAddr(fnet_matchers_ext::BoundAddress),
35 Proto(fnet_matchers_ext::SocketTransportProtocol),
37 BoundInterface(fnet_matchers_ext::BoundInterface),
40 Cookie(fnet_matchers::SocketCookie),
42 Mark(fnet_matchers_ext::MarkInDomain),
44}
45
46#[derive(Debug, PartialEq, Error)]
49pub enum IpSocketMatcherError {
50 #[error("got unexpected union variant: {0}")]
52 UnknownUnionVariant(u64),
53 #[error("address matcher conversion failure: {0}")]
55 Address(fnet_matchers_ext::BoundAddressError),
56 #[error("protocol matcher conversion failure: {0}")]
59 TransportProtocol(fnet_matchers_ext::SocketTransportProtocolError),
60 #[error("bound interface matcher conversion failure: {0}")]
62 BoundInterface(fnet_matchers_ext::BoundInterfaceError),
63 #[error("mark matcher conversion failure: {0}")]
65 Mark(fnet_matchers_ext::MarkInDomainError),
66}
67
68impl TryFrom<fnet_sockets::IpSocketMatcher> for IpSocketMatcher {
69 type Error = IpSocketMatcherError;
70
71 fn try_from(matcher: fnet_sockets::IpSocketMatcher) -> Result<Self, Self::Error> {
72 match matcher {
73 fnet_sockets::IpSocketMatcher::Family(ip_version) => {
74 Ok(Self::Family(ip_version.into_ext()))
75 }
76 fnet_sockets::IpSocketMatcher::SrcAddr(addr) => {
77 Ok(Self::SrcAddr(addr.try_into().map_err(|e| IpSocketMatcherError::Address(e))?))
78 }
79 fnet_sockets::IpSocketMatcher::DstAddr(addr) => {
80 Ok(Self::DstAddr(addr.try_into().map_err(|e| IpSocketMatcherError::Address(e))?))
81 }
82 fnet_sockets::IpSocketMatcher::Proto(proto) => Ok(Self::Proto(
83 proto.try_into().map_err(|e| IpSocketMatcherError::TransportProtocol(e))?,
84 )),
85 fnet_sockets::IpSocketMatcher::BoundInterface(bound_interface) => {
86 Ok(Self::BoundInterface(
87 bound_interface
88 .try_into()
89 .map_err(|e| IpSocketMatcherError::BoundInterface(e))?,
90 ))
91 }
92 fnet_sockets::IpSocketMatcher::Cookie(cookie) => Ok(Self::Cookie(cookie)),
93 fnet_sockets::IpSocketMatcher::Mark(mark) => {
94 Ok(Self::Mark(mark.try_into().map_err(|e| IpSocketMatcherError::Mark(e))?))
95 }
96 fnet_sockets::IpSocketMatcher::__SourceBreaking { unknown_ordinal } => {
97 Err(IpSocketMatcherError::UnknownUnionVariant(unknown_ordinal))
98 }
99 }
100 }
101}
102
103impl From<IpSocketMatcher> for fnet_sockets::IpSocketMatcher {
104 fn from(value: IpSocketMatcher) -> Self {
105 match value {
106 IpSocketMatcher::Family(ip_version) => {
107 fnet_sockets::IpSocketMatcher::Family(ip_version.into_ext())
108 }
109 IpSocketMatcher::SrcAddr(address) => {
110 fnet_sockets::IpSocketMatcher::SrcAddr(address.into())
111 }
112 IpSocketMatcher::DstAddr(address) => {
113 fnet_sockets::IpSocketMatcher::DstAddr(address.into())
114 }
115 IpSocketMatcher::Proto(socket_transport_protocol) => {
116 fnet_sockets::IpSocketMatcher::Proto(socket_transport_protocol.into())
117 }
118 IpSocketMatcher::BoundInterface(mark) => {
119 fnet_sockets::IpSocketMatcher::BoundInterface(mark.into())
120 }
121 IpSocketMatcher::Cookie(socket_cookie) => {
122 fnet_sockets::IpSocketMatcher::Cookie(socket_cookie)
123 }
124 IpSocketMatcher::Mark(mark) => fnet_sockets::IpSocketMatcher::Mark(mark.into()),
125 }
126 }
127}
128
129#[derive(Debug, PartialEq, Eq, Clone)]
131pub enum IpSocketState {
132 V4(IpSocketStateSpecific<Ipv4>),
134 V6(IpSocketStateSpecific<Ipv6>),
136}
137
138#[derive(Debug, Error, PartialEq)]
140pub enum IpSocketStateError {
141 #[error("missing field: {0}")]
143 MissingField(&'static str),
144 #[error("version mismatch")]
146 VersionMismatch,
147 #[error("transport state error: {0}")]
149 Transport(IpSocketTransportStateError),
150}
151
152impl TryFrom<fnet_sockets::IpSocketState> for IpSocketState {
153 type Error = IpSocketStateError;
154
155 fn try_from(value: fnet_sockets::IpSocketState) -> Result<Self, Self::Error> {
156 fn convert_address<I: Ip>(addr: fnet::IpAddress) -> Result<I::Addr, IpSocketStateError> {
157 I::map_ip::<_, Option<I::Addr>>(
158 IpInvariant(addr.into_ext()),
159 |IpInvariant(addr)| match addr {
160 net_types::ip::IpAddr::V4(addr) => Some(addr),
161 _ => None,
162 },
163 |IpInvariant(addr)| match addr {
164 net_types::ip::IpAddr::V6(addr) => Some(addr),
165 _ => None,
166 },
167 )
168 .ok_or(IpSocketStateError::VersionMismatch)
169 }
170
171 fn to_ip_socket_specific<I: Ip>(
172 src_addr: Option<fnet::IpAddress>,
173 dst_addr: Option<fnet::IpAddress>,
174 cookie: u64,
175 marks: fnet::Marks,
176 transport: fnet_sockets::IpSocketTransportState,
177 ) -> Result<IpSocketStateSpecific<I>, IpSocketStateError> {
178 let src_addr: Option<I::Addr> = src_addr.map(convert_address::<I>).transpose()?;
179 let dst_addr: Option<I::Addr> = dst_addr.map(convert_address::<I>).transpose()?;
180
181 Ok(IpSocketStateSpecific {
182 src_addr,
183 dst_addr,
184 cookie,
185 marks: marks.into(),
186 transport: transport.try_into().map_err(IpSocketStateError::Transport)?,
187 })
188 }
189
190 let fnet_sockets::IpSocketState {
191 family,
192 src_addr,
193 dst_addr,
194 cookie,
195 marks,
196 transport,
197 __source_breaking,
198 } = value;
199
200 let family = family.ok_or(IpSocketStateError::MissingField("family"))?;
201 let cookie = cookie.ok_or(IpSocketStateError::MissingField("cookie"))?;
202 let marks = marks.ok_or(IpSocketStateError::MissingField("marks"))?;
203 let transport = transport.ok_or(IpSocketStateError::MissingField("transport"))?;
204
205 match family {
206 fnet::IpVersion::V4 => Ok(IpSocketState::V4(to_ip_socket_specific(
207 src_addr, dst_addr, cookie, marks, transport,
208 )?)),
209 fnet::IpVersion::V6 => Ok(IpSocketState::V6(to_ip_socket_specific(
210 src_addr, dst_addr, cookie, marks, transport,
211 )?)),
212 }
213 }
214}
215
216impl From<IpSocketState> for fnet_sockets::IpSocketState {
217 fn from(state: IpSocketState) -> Self {
218 match state {
219 IpSocketState::V4(state) => state.into(),
220 IpSocketState::V6(state) => state.into(),
221 }
222 }
223}
224
225#[derive(Debug, PartialEq, Eq, Clone, GenericOverIp)]
228#[generic_over_ip(I, Ip)]
229pub struct IpSocketStateSpecific<I: Ip> {
230 pub src_addr: Option<I::Addr>,
232 pub dst_addr: Option<I::Addr>,
234 pub cookie: u64,
236 pub marks: Marks,
238 pub transport: IpSocketTransportState,
240}
241
242impl<I: Ip> From<IpSocketStateSpecific<I>> for fnet_sockets::IpSocketState {
243 fn from(value: IpSocketStateSpecific<I>) -> Self {
244 let IpSocketStateSpecific { src_addr, dst_addr, cookie, marks, transport } = value;
245
246 fnet_sockets::IpSocketState {
247 family: Some(I::VERSION.into_ext()),
248 src_addr: src_addr.map(|a| net_types::ip::IpAddr::from(a).into_ext()),
249 dst_addr: dst_addr.map(|a| net_types::ip::IpAddr::from(a).into_ext()),
250 cookie: Some(cookie),
251 marks: Some(marks.into()),
252 transport: Some(transport.into()),
253 __source_breaking: fidl::marker::SourceBreaking,
254 }
255 }
256}
257
258#[derive(Debug, PartialEq, Eq, Clone)]
260pub enum IpSocketTransportState {
261 Tcp(IpSocketTcpState),
263 Udp(IpSocketUdpState),
265}
266
267#[derive(Debug, PartialEq, Error)]
269pub enum IpSocketTransportStateError {
270 #[error("tcp validation error: {0}")]
272 Tcp(IpSocketTcpStateError),
273 #[error("udp validation error: {0}")]
275 Udp(IpSocketUdpStateError),
276 #[error("got unexpected union variant: {0}")]
278 UnknownUnionVariant(u64),
279}
280
281impl TryFrom<fnet_sockets::IpSocketTransportState> for IpSocketTransportState {
282 type Error = IpSocketTransportStateError;
283
284 fn try_from(value: fnet_sockets::IpSocketTransportState) -> Result<Self, Self::Error> {
285 match value {
286 fnet_sockets::IpSocketTransportState::Tcp(tcp) => Ok(IpSocketTransportState::Tcp(
287 tcp.try_into().map_err(IpSocketTransportStateError::Tcp)?,
288 )),
289 fnet_sockets::IpSocketTransportState::Udp(udp) => Ok(IpSocketTransportState::Udp(
290 udp.try_into().map_err(IpSocketTransportStateError::Udp)?,
291 )),
292 fnet_sockets::IpSocketTransportState::__SourceBreaking { unknown_ordinal } => {
293 Err(IpSocketTransportStateError::UnknownUnionVariant(unknown_ordinal))
294 }
295 }
296 }
297}
298
299impl From<IpSocketTransportState> for fnet_sockets::IpSocketTransportState {
300 fn from(state: IpSocketTransportState) -> Self {
301 match state {
302 IpSocketTransportState::Tcp(tcp) => {
303 fnet_sockets::IpSocketTransportState::Tcp(tcp.into())
304 }
305 IpSocketTransportState::Udp(udp) => {
306 fnet_sockets::IpSocketTransportState::Udp(udp.into())
307 }
308 }
309 }
310}
311
312#[derive(Debug, PartialEq, Eq, Clone)]
314pub struct IpSocketTcpState {
315 pub src_port: Option<u16>,
317 pub dst_port: Option<u16>,
319 pub state: fnet_tcp::State,
321 pub tcp_info: Option<TcpInfo>,
323}
324
325#[derive(Debug, PartialEq, Error)]
327pub enum IpSocketTcpStateError {
328 #[error("missing field: {0}")]
330 MissingField(&'static str),
331 #[error("tcp info error: {0}")]
333 TcpInfo(TcpInfoError),
334}
335
336impl TryFrom<fnet_sockets::IpSocketTcpState> for IpSocketTcpState {
337 type Error = IpSocketTcpStateError;
338
339 fn try_from(value: fnet_sockets::IpSocketTcpState) -> Result<Self, Self::Error> {
340 let fnet_sockets::IpSocketTcpState {
341 src_port,
342 dst_port,
343 state,
344 tcp_info,
345 __source_breaking,
346 } = value;
347
348 let state = state.ok_or(IpSocketTcpStateError::MissingField("state"))?;
349
350 Ok(IpSocketTcpState {
351 src_port,
352 dst_port,
353 state,
354 tcp_info: tcp_info
355 .map(|t| t.try_into())
356 .transpose()
357 .map_err(|e| IpSocketTcpStateError::TcpInfo(e))?,
358 })
359 }
360}
361
362impl From<IpSocketTcpState> for fnet_sockets::IpSocketTcpState {
363 fn from(state: IpSocketTcpState) -> Self {
364 let IpSocketTcpState { src_port, dst_port, state, tcp_info } = state;
365 fnet_sockets::IpSocketTcpState {
366 src_port,
367 dst_port,
368 state: Some(state),
369 tcp_info: tcp_info.map(Into::into),
370 __source_breaking: fidl::marker::SourceBreaking,
371 }
372 }
373}
374
375#[derive(Debug, PartialEq, Eq, Clone)]
377pub struct TcpInfo {
378 pub state: fnet_tcp::State,
380 pub ca_state: fnet_tcp::CongestionControlState,
382 pub rto_usec: Option<u32>,
384 pub tcpi_last_data_sent_msec: Option<u32>,
386 pub tcpi_last_ack_recv_msec: Option<u32>,
388 pub rtt_usec: Option<u32>,
390 pub rtt_var_usec: Option<u32>,
392 pub snd_ssthresh: u32,
394 pub snd_cwnd: u32,
396 pub tcpi_total_retrans: u32,
398 pub tcpi_segs_out: u64,
400 pub tcpi_segs_in: u64,
402 pub reorder_seen: bool,
404 pub tcpi_snd_mss: Option<u32>,
406 pub tcpi_rcv_mss: Option<u32>,
408}
409
410#[derive(Debug, PartialEq, Error)]
412pub enum TcpInfoError {
413 #[error("missing field: {0}")]
415 MissingField(&'static str),
416}
417
418impl TryFrom<fnet_tcp::Info> for TcpInfo {
419 type Error = TcpInfoError;
420
421 fn try_from(value: fnet_tcp::Info) -> Result<Self, Self::Error> {
422 let fnet_tcp::Info {
423 state,
424 ca_state,
425 rto_usec,
426 tcpi_last_data_sent_msec,
427 tcpi_last_ack_recv_msec,
428 rtt_usec,
429 rtt_var_usec,
430 snd_ssthresh,
431 snd_cwnd,
432 tcpi_total_retrans,
433 tcpi_segs_out,
434 tcpi_segs_in,
435 reorder_seen,
436 tcpi_snd_mss,
437 tcpi_rcv_mss,
438 __source_breaking,
439 } = value;
440
441 Ok(TcpInfo {
442 state: state.ok_or(TcpInfoError::MissingField("state"))?,
443 ca_state: ca_state.ok_or(TcpInfoError::MissingField("ca_state"))?,
444 rto_usec,
445 tcpi_last_data_sent_msec,
446 tcpi_last_ack_recv_msec,
447 rtt_usec,
448 rtt_var_usec,
449 snd_ssthresh: snd_ssthresh.ok_or(TcpInfoError::MissingField("snd_ssthresh"))?,
450 snd_cwnd: snd_cwnd.ok_or(TcpInfoError::MissingField("snd_cwnd"))?,
451 tcpi_total_retrans: tcpi_total_retrans
452 .ok_or(TcpInfoError::MissingField("tcpi_total_retrans"))?,
453 tcpi_segs_out: tcpi_segs_out.ok_or(TcpInfoError::MissingField("tcpi_segs_out"))?,
454 tcpi_segs_in: tcpi_segs_in.ok_or(TcpInfoError::MissingField("tcpi_segs_in"))?,
455 reorder_seen: reorder_seen.ok_or(TcpInfoError::MissingField("reorder_seen"))?,
456 tcpi_snd_mss,
457 tcpi_rcv_mss,
458 })
459 }
460}
461
462impl From<TcpInfo> for fnet_tcp::Info {
463 fn from(info: TcpInfo) -> Self {
464 let TcpInfo {
465 state,
466 ca_state,
467 rto_usec,
468 tcpi_last_data_sent_msec,
469 tcpi_last_ack_recv_msec,
470 rtt_usec,
471 rtt_var_usec,
472 snd_ssthresh,
473 snd_cwnd,
474 tcpi_total_retrans,
475 tcpi_segs_out,
476 tcpi_segs_in,
477 reorder_seen,
478 tcpi_snd_mss,
479 tcpi_rcv_mss,
480 } = info;
481 fnet_tcp::Info {
482 state: Some(state),
483 ca_state: Some(ca_state),
484 rto_usec: rto_usec,
485 tcpi_last_data_sent_msec,
486 tcpi_last_ack_recv_msec,
487 rtt_usec: rtt_usec,
488 rtt_var_usec: rtt_var_usec,
489 snd_ssthresh: Some(snd_ssthresh),
490 snd_cwnd: Some(snd_cwnd),
491 tcpi_total_retrans: Some(tcpi_total_retrans),
492 tcpi_segs_out: Some(tcpi_segs_out),
493 tcpi_segs_in: Some(tcpi_segs_in),
494 reorder_seen: Some(reorder_seen),
495 tcpi_snd_mss,
496 tcpi_rcv_mss,
497 __source_breaking: fidl::marker::SourceBreaking,
498 }
499 }
500}
501
502#[derive(Debug, PartialEq, Eq, Clone)]
504pub struct IpSocketUdpState {
505 pub src_port: Option<u16>,
507 pub dst_port: Option<u16>,
509 pub state: fnet_udp::State,
511}
512
513#[derive(Debug, PartialEq, Error)]
515pub enum IpSocketUdpStateError {
516 #[error("missing field: {0}")]
518 MissingField(&'static str),
519}
520
521impl TryFrom<fnet_sockets::IpSocketUdpState> for IpSocketUdpState {
522 type Error = IpSocketUdpStateError;
523
524 fn try_from(value: fnet_sockets::IpSocketUdpState) -> Result<Self, Self::Error> {
525 let fnet_sockets::IpSocketUdpState { src_port, dst_port, state, __source_breaking } = value;
526
527 let state = state.ok_or(IpSocketUdpStateError::MissingField("state"))?;
528
529 Ok(IpSocketUdpState { src_port, dst_port, state })
530 }
531}
532
533impl From<IpSocketUdpState> for fnet_sockets::IpSocketUdpState {
534 fn from(state: IpSocketUdpState) -> Self {
535 let IpSocketUdpState { src_port, dst_port, state } = state;
536 fnet_sockets::IpSocketUdpState {
537 src_port,
538 dst_port,
539 state: Some(state),
540 __source_breaking: fidl::marker::SourceBreaking,
541 }
542 }
543}
544
545#[derive(Debug, Error)]
547pub enum IterateIpError {
548 #[error("invalid matcher at position {0}")]
550 InvalidMatcher(usize),
551 #[error("unknown ordinal on Diagnostics.IterateIp call: {0}")]
553 UnknownOrdinal(u64),
554 #[error("fidl error during Diagnostics.IterateIp call: {0}")]
557 Fidl(fidl::Error),
558}
559
560impl From<fidl::Error> for IterateIpError {
561 fn from(e: fidl::Error) -> Self {
562 IterateIpError::Fidl(e)
563 }
564}
565
566#[derive(Debug, Error)]
568pub enum IpIteratorError {
569 #[error("received empty batch of sockets")]
571 EmptyBatch,
572 #[error("fidl error during Diagnostics.IterateIp call: {0}")]
575 Fidl(fidl::Error),
576 #[error("error converting socket state: {0}")]
578 Conversion(IpSocketStateError),
579}
580
581impl From<fidl::Error> for IpIteratorError {
582 fn from(e: fidl::Error) -> Self {
583 IpIteratorError::Fidl(e)
584 }
585}
586
587pub async fn iterate_ip<M, I>(
597 diagnostics: &fnet_sockets::DiagnosticsProxy,
598 extensions: fnet_sockets::Extensions,
599 matchers: M,
600) -> Result<impl Stream<Item = Result<IpSocketState, IpIteratorError>> + use<M, I>, IterateIpError>
601where
602 M: IntoIterator<Item = I>,
603 I: Into<fnet_sockets::IpSocketMatcher>,
604{
605 let (proxy, server_end) = fidl::endpoints::create_proxy::<fnet_sockets::IpIteratorMarker>();
606 match diagnostics
607 .iterate_ip(
608 server_end,
609 extensions,
610 &matchers.into_iter().map(Into::into).collect::<Vec<_>>()[..],
611 )
612 .await?
613 {
614 fnet_sockets::IterateIpResult::Ok(_empty) => Ok(()),
615 fnet_sockets::IterateIpResult::InvalidMatcher(fnet_sockets::InvalidMatcher { index }) => {
616 Err(IterateIpError::InvalidMatcher(index as usize))
617 }
618 fnet_sockets::IterateIpResult::__SourceBreaking { unknown_ordinal } => {
619 Err(IterateIpError::UnknownOrdinal(unknown_ordinal))
620 }
621 }?;
622
623 Ok(futures::stream::try_unfold((proxy, true), |(proxy, has_more)| async move {
624 if !has_more {
625 return Ok(None);
626 }
627
628 let (batch, has_more) = proxy.next().await?;
629 if batch.is_empty() && has_more {
630 Err(IpIteratorError::EmptyBatch)
631 } else {
632 Ok(Some((
633 futures::stream::iter(
634 batch
635 .into_iter()
636 .map(|s| s.try_into().map_err(|e| IpIteratorError::Conversion(e))),
637 ),
638 (proxy, has_more),
639 )))
640 }
641 })
642 .try_flatten())
643}
644
645#[derive(Debug, Error)]
647pub enum DisconnectIpError {
648 #[error("invalid matcher at position {0}")]
650 InvalidMatcher(usize),
651 #[error("matchers were unconstrained")]
653 UnconstrainedMatchers,
654 #[error("unknown ordinal on Control.DisconnectIp call: {0}")]
656 UnknownOrdinal(u64),
657 #[error("fidl error during Control.DisconnectIp call: {0}")]
660 Fidl(fidl::Error),
661}
662
663pub async fn disconnect_ip<M, I>(
665 control: &fnet_sockets::ControlProxy,
666 matchers: M,
667) -> Result<usize, DisconnectIpError>
668where
669 M: IntoIterator<Item = I>,
670 I: Into<fnet_sockets::IpSocketMatcher>,
671{
672 match control
673 .disconnect_ip(&fnet_sockets::ControlDisconnectIpRequest {
674 matchers: Some(matchers.into_iter().map(Into::into).collect()),
675 __source_breaking: fidl::marker::SourceBreaking,
676 })
677 .await
678 {
679 Ok(r) => match r {
680 fnet_sockets::DisconnectIpResult::Ok(fnet_sockets::DisconnectIpResponse {
681 disconnected,
682 }) => {
683 Ok(disconnected.try_into().unwrap())
685 }
686 fnet_sockets::DisconnectIpResult::InvalidMatcher(fnet_sockets::InvalidMatcher {
687 index,
688 }) => {
689 Err(DisconnectIpError::InvalidMatcher(index.try_into().unwrap()))
691 }
692 fnet_sockets::DisconnectIpResult::UnconstrainedMatchers(fnet_sockets::Empty) => {
693 Err(DisconnectIpError::UnconstrainedMatchers)
694 }
695 fnet_sockets::DisconnectIpResult::__SourceBreaking { unknown_ordinal } => {
696 Err(DisconnectIpError::UnknownOrdinal(unknown_ordinal))
697 }
698 },
699 Err(e) => Err(DisconnectIpError::Fidl(e)),
700 }
701}
702
703#[derive(Debug, Error)]
705pub enum DestructionWatcherError {
706 #[error("fidl error during DestructionWatcher.Watch call: {0}")]
709 Fidl(fidl::Error),
710 #[error("error converting socket state: {0}")]
712 Conversion(IpSocketStateError),
713 #[error("received empty batch of sockets")]
715 EmptyBatch,
716}
717
718impl From<fidl::Error> for DestructionWatcherError {
719 fn from(e: fidl::Error) -> Self {
720 DestructionWatcherError::Fidl(e)
721 }
722}
723
724pub async fn watch_destruction(
726 diagnostics: &fnet_sockets::DiagnosticsProxy,
727) -> Result<impl Stream<Item = Result<IpSocketState, DestructionWatcherError>> + use<>, fidl::Error>
728{
729 let (proxy, server_end) =
730 fidl::endpoints::create_proxy::<fnet_sockets::DestructionWatcherMarker>();
731 diagnostics.get_destruction_watcher(server_end).await?;
732
733 Ok(futures::stream::try_unfold(proxy, |proxy| async {
734 let batch = proxy.watch().await?;
735 if batch.is_empty() {
736 Err(DestructionWatcherError::EmptyBatch)
737 } else {
738 let batch = batch
739 .into_iter()
740 .map(|s| s.try_into().map_err(DestructionWatcherError::Conversion))
741 .collect::<Result<Vec<_>, _>>()?;
742 Ok::<_, DestructionWatcherError>(Some((
743 futures::stream::iter(batch.into_iter().map(Ok)),
744 proxy,
745 )))
746 }
747 })
748 .try_flatten())
749}
750
751#[cfg(test)]
752mod tests {
753 use super::*;
754
755 use std::num::NonZeroU64;
756
757 use assert_matches::assert_matches;
758 use fidl_fuchsia_net as fnet;
759 use fidl_fuchsia_net_tcp as fnet_tcp;
760 use futures::{FutureExt as _, StreamExt as _, future, pin_mut};
761 use net_declare::{fidl_ip, fidl_subnet, net_ip_v4, net_ip_v6};
762 use test_case::test_case;
763
764 #[test_case(
765 fnet_sockets::IpSocketMatcher::Family(fnet::IpVersion::V4),
766 IpSocketMatcher::Family(ip::IpVersion::V4);
767 "FamilyIpv4"
768 )]
769 #[test_case(
770 fnet_sockets::IpSocketMatcher::Family(fnet::IpVersion::V6),
771 IpSocketMatcher::Family(ip::IpVersion::V6);
772 "FamilyIpv6"
773 )]
774 #[test_case(
775 fnet_sockets::IpSocketMatcher::SrcAddr(fnet_matchers::BoundAddress::Bound(
776 fnet_matchers::Address {
777 matcher: fnet_matchers::AddressMatcherType::Subnet(fidl_subnet!("192.0.2.0/24")),
778 invert: true,
779 }
780 )),
781 IpSocketMatcher::SrcAddr(fnet_matchers_ext::BoundAddress::Bound(
782 fnet_matchers_ext::Address {
783 matcher: fnet_matchers_ext::AddressMatcherType::Subnet(
784 fnet_matchers_ext::Subnet::try_from(fidl_subnet!("192.0.2.0/24")).unwrap()
785 ),
786 invert: true,
787 }
788 ));
789 "SrcAddr"
790 )]
791 #[test_case(
792 fnet_sockets::IpSocketMatcher::DstAddr(fnet_matchers::BoundAddress::Bound(
793 fnet_matchers::Address {
794 matcher: fnet_matchers::AddressMatcherType::Subnet(fidl_subnet!("2001:db8::/32")),
795 invert: false,
796 }
797 )),
798 IpSocketMatcher::DstAddr(fnet_matchers_ext::BoundAddress::Bound(
799 fnet_matchers_ext::Address {
800 matcher: fnet_matchers_ext::AddressMatcherType::Subnet(
801 fnet_matchers_ext::Subnet::try_from(fidl_subnet!("2001:db8::/32")).unwrap()
802 ),
803 invert: false,
804 }
805 ));
806 "DstAddr"
807 )]
808 #[test_case(
809 fnet_sockets::IpSocketMatcher::Proto(fnet_matchers::SocketTransportProtocol::Tcp(
810 fnet_matchers::TcpSocket::Empty(fnet_matchers::Empty)
811 )),
812 IpSocketMatcher::Proto(fnet_matchers_ext::SocketTransportProtocol::Tcp(
813 fnet_matchers_ext::TcpSocket::Empty
814 ));
815 "ProtoTcp"
816 )]
817 #[test_case(
818 fnet_sockets::IpSocketMatcher::Proto(fnet_matchers::SocketTransportProtocol::Udp(
819 fnet_matchers::UdpSocket::Empty(fnet_matchers::Empty)
820 )),
821 IpSocketMatcher::Proto(fnet_matchers_ext::SocketTransportProtocol::Udp(
822 fnet_matchers_ext::UdpSocket::Empty
823 ));
824 "ProtoUdp"
825 )]
826 #[test_case(
827 fnet_sockets::IpSocketMatcher::BoundInterface(fnet_matchers::BoundInterface::Unbound(
828 fnet_matchers::Empty
829 )),
830 IpSocketMatcher::BoundInterface(fnet_matchers_ext::BoundInterface::Unbound);
831 "BoundInterfaceUnbound"
832 )]
833 #[test_case(
834 fnet_sockets::IpSocketMatcher::BoundInterface(fnet_matchers::BoundInterface::Bound(
835 fnet_matchers::Interface::Id(1)
836 )),
837 IpSocketMatcher::BoundInterface(fnet_matchers_ext::BoundInterface::Bound(
838 fnet_matchers_ext::Interface::Id(NonZeroU64::new(1).unwrap())
839 ));
840 "BoundInterfaceBound"
841 )]
842 #[test_case(
843 fnet_sockets::IpSocketMatcher::Cookie(fnet_matchers::SocketCookie {
844 cookie: 12345,
845 invert: false,
846 }),
847 IpSocketMatcher::Cookie(fnet_matchers::SocketCookie {
848 cookie: 12345,
849 invert: false,
850 });
851 "Cookie"
852 )]
853 #[test_case(
854 fnet_sockets::IpSocketMatcher::Mark(fnet_matchers::MarkInDomain {
855 domain: fnet::MarkDomain::Mark1,
856 mark: fnet_matchers::Mark::Unmarked(fnet_matchers::Unmarked),
857 }),
858 IpSocketMatcher::Mark(fnet_matchers_ext::MarkInDomain {
859 domain: fnet::MarkDomain::Mark1,
860 mark: fnet_matchers_ext::Mark::Unmarked,
861 });
862 "Mark"
863 )]
864 #[test_case(
865 fnet_sockets::IpSocketMatcher::SrcAddr(fnet_matchers::BoundAddress::Unbound(fnet_matchers::Empty)),
866 IpSocketMatcher::SrcAddr(fnet_matchers_ext::BoundAddress::Unbound);
867 "SrcAddrUnbound"
868 )]
869 #[test_case(
870 fnet_sockets::IpSocketMatcher::DstAddr(fnet_matchers::BoundAddress::Unbound(fnet_matchers::Empty)),
871 IpSocketMatcher::DstAddr(fnet_matchers_ext::BoundAddress::Unbound);
872 "DstAddrUnbound"
873 )]
874 #[test_case(
875 fnet_sockets::IpSocketMatcher::Proto(fnet_matchers::SocketTransportProtocol::Tcp(
876 fnet_matchers::TcpSocket::SrcPort(fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty))
877 )),
878 IpSocketMatcher::Proto(fnet_matchers_ext::SocketTransportProtocol::Tcp(
879 fnet_matchers_ext::TcpSocket::SrcPort(fnet_matchers_ext::BoundPort::Unbound)
880 ));
881 "ProtoTcpSrcPortUnbound"
882 )]
883 #[test_case(
884 fnet_sockets::IpSocketMatcher::Proto(fnet_matchers::SocketTransportProtocol::Tcp(
885 fnet_matchers::TcpSocket::DstPort(fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty))
886 )),
887 IpSocketMatcher::Proto(fnet_matchers_ext::SocketTransportProtocol::Tcp(
888 fnet_matchers_ext::TcpSocket::DstPort(fnet_matchers_ext::BoundPort::Unbound)
889 ));
890 "ProtoTcpDstPortUnbound"
891 )]
892 #[test_case(
893 fnet_sockets::IpSocketMatcher::Proto(fnet_matchers::SocketTransportProtocol::Udp(
894 fnet_matchers::UdpSocket::SrcPort(fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty))
895 )),
896 IpSocketMatcher::Proto(fnet_matchers_ext::SocketTransportProtocol::Udp(
897 fnet_matchers_ext::UdpSocket::SrcPort(fnet_matchers_ext::BoundPort::Unbound)
898 ));
899 "ProtoUdpSrcPortUnbound"
900 )]
901 #[test_case(
902 fnet_sockets::IpSocketMatcher::Proto(fnet_matchers::SocketTransportProtocol::Udp(
903 fnet_matchers::UdpSocket::DstPort(fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty))
904 )),
905 IpSocketMatcher::Proto(fnet_matchers_ext::SocketTransportProtocol::Udp(
906 fnet_matchers_ext::UdpSocket::DstPort(fnet_matchers_ext::BoundPort::Unbound)
907 ));
908 "ProtoUdpDstPortUnbound"
909 )]
910 #[test_case(
911 fnet_tcp::Info {
912 state: Some(fnet_tcp::State::Established),
913 ca_state: Some(fnet_tcp::CongestionControlState::Open),
914 rto_usec: Some(1),
915 tcpi_last_data_sent_msec: Some(2),
916 tcpi_last_ack_recv_msec: Some(3),
917 rtt_usec: Some(4),
918 rtt_var_usec: Some(5),
919 snd_ssthresh: Some(6),
920 snd_cwnd: Some(7),
921 tcpi_total_retrans: Some(8),
922 tcpi_segs_out: Some(9),
923 tcpi_segs_in: Some(10),
924 reorder_seen: Some(true),
925 tcpi_snd_mss: Some(11),
926 tcpi_rcv_mss: Some(12),
927 __source_breaking: fidl::marker::SourceBreaking,
928 },
929 TcpInfo {
930 state: fnet_tcp::State::Established,
931 ca_state: fnet_tcp::CongestionControlState::Open,
932 rto_usec: Some(1),
933 tcpi_last_data_sent_msec: Some(2),
934 tcpi_last_ack_recv_msec: Some(3),
935 rtt_usec: Some(4),
936 rtt_var_usec: Some(5),
937 snd_ssthresh: 6,
938 snd_cwnd: 7,
939 tcpi_total_retrans: 8,
940 tcpi_segs_out: 9,
941 tcpi_segs_in: 10,
942 reorder_seen: true,
943 tcpi_snd_mss: Some(11),
944 tcpi_rcv_mss: Some(12),
945 };
946 "TcpInfo"
947 )]
948 #[test_case(
949 fnet_sockets::IpSocketState {
950 family: Some(fnet::IpVersion::V4),
951 src_addr: Some(fidl_ip!("192.168.1.1")),
952 dst_addr: Some(fidl_ip!("192.168.1.2")),
953 cookie: Some(1234),
954 marks: Some(fnet::Marks {
955 mark_1: Some(1111),
956 mark_2: None,
957 __source_breaking: fidl::marker::SourceBreaking,
958 }),
959 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
960 fnet_sockets::IpSocketTcpState {
961 src_port: Some(1111),
962 dst_port: Some(2222),
963 state: Some(fnet_tcp::State::Established),
964 tcp_info: None,
965 __source_breaking: fidl::marker::SourceBreaking,
966 },
967 )),
968 __source_breaking: fidl::marker::SourceBreaking,
969 },
970 IpSocketState::V4(IpSocketStateSpecific {
971 src_addr: Some(net_ip_v4!("192.168.1.1")),
972 dst_addr: Some(net_ip_v4!("192.168.1.2")),
973 cookie: 1234,
974 marks: fnet::Marks {
975 mark_1: Some(1111),
976 mark_2: None,
977 __source_breaking: fidl::marker::SourceBreaking,
978 }.into(),
979 transport: IpSocketTransportState::Tcp(IpSocketTcpState {
980 src_port: Some(1111),
981 dst_port: Some(2222),
982 state: fnet_tcp::State::Established,
983 tcp_info: None,
984 }),
985 });
986 "IpSocketStateV4"
987 )]
988 #[test_case(
989 fnet_sockets::IpSocketState {
990 family: Some(fnet::IpVersion::V6),
991 src_addr: Some(fidl_ip!("2001:db8::1")),
992 dst_addr: Some(fidl_ip!("2001:db8::2")),
993 cookie: Some(1234),
994 marks: Some(fnet::Marks {
995 mark_1: Some(1111),
996 mark_2: None,
997 __source_breaking: fidl::marker::SourceBreaking,
998 }),
999 transport: Some(fnet_sockets::IpSocketTransportState::Udp(
1000 fnet_sockets::IpSocketUdpState {
1001 src_port: Some(3333),
1002 dst_port: Some(4444),
1003 state: Some(fnet_udp::State::Connected),
1004 __source_breaking: fidl::marker::SourceBreaking,
1005 },
1006 )),
1007 __source_breaking: fidl::marker::SourceBreaking,
1008 },
1009 IpSocketState::V6(IpSocketStateSpecific {
1010 src_addr: Some(net_ip_v6!("2001:db8::1")),
1011 dst_addr: Some(net_ip_v6!("2001:db8::2")),
1012 cookie: 1234,
1013 marks: fnet::Marks {
1014 mark_1: Some(1111),
1015 mark_2: None,
1016 __source_breaking: fidl::marker::SourceBreaking,
1017 }.into(),
1018 transport: IpSocketTransportState::Udp(IpSocketUdpState {
1019 src_port: Some(3333),
1020 dst_port: Some(4444),
1021 state: fnet_udp::State::Connected,
1022 }),
1023 });
1024 "IpSocketStateV6"
1025 )]
1026 fn convert_from_fidl_and_back<F, E>(fidl_type: F, local_type: E)
1027 where
1028 E: TryFrom<F> + Clone + std::fmt::Debug + PartialEq,
1029 <E as TryFrom<F>>::Error: std::fmt::Debug + PartialEq,
1030 F: From<E> + Clone + std::fmt::Debug + PartialEq,
1031 {
1032 assert_eq!(fidl_type.clone().try_into(), Ok(local_type.clone()));
1033 assert_eq!(<_ as Into<F>>::into(local_type), fidl_type);
1034 }
1035
1036 #[test_case(
1037 fnet_sockets::IpSocketMatcher::__SourceBreaking { unknown_ordinal: 100 } =>
1038 Err(IpSocketMatcherError::UnknownUnionVariant(100));
1039 "UnknownUnionVariant"
1040 )]
1041 #[test_case(
1042 fnet_sockets::IpSocketMatcher::SrcAddr(fnet_matchers::BoundAddress::Bound(
1043 fnet_matchers::Address {
1044 matcher: fnet_matchers::AddressMatcherType::__SourceBreaking { unknown_ordinal: 100 },
1045 invert: false,
1046 }
1047 )) => Err(IpSocketMatcherError::Address(fnet_matchers_ext::BoundAddressError::Address(
1048 fnet_matchers_ext::AddressError::AddressMatcherType(
1049 fnet_matchers_ext::AddressMatcherTypeError::UnknownUnionVariant
1050 )
1051 )));
1052 "AddressError"
1053 )]
1054 #[test_case(
1055 fnet_sockets::IpSocketMatcher::Proto(
1056 fnet_matchers::SocketTransportProtocol::__SourceBreaking { unknown_ordinal: 100 }
1057 ) => Err(IpSocketMatcherError::TransportProtocol(
1058 fnet_matchers_ext::SocketTransportProtocolError::UnknownUnionVariant(100)
1059 ));
1060 "TransportProtocolError"
1061 )]
1062 #[test_case(
1063 fnet_sockets::IpSocketMatcher::BoundInterface(
1064 fnet_matchers::BoundInterface::__SourceBreaking { unknown_ordinal: 100 }
1065 ) => Err(IpSocketMatcherError::BoundInterface(
1066 fnet_matchers_ext::BoundInterfaceError::UnknownUnionVariant(100)
1067 ));
1068 "BoundInterfaceError"
1069 )]
1070 #[test_case(
1071 fnet_sockets::IpSocketMatcher::Mark(fnet_matchers::MarkInDomain {
1072 domain: fnet::MarkDomain::Mark1,
1073 mark: fnet_matchers::Mark::__SourceBreaking { unknown_ordinal: 100 },
1074 }) => Err(IpSocketMatcherError::Mark(
1075 fnet_matchers_ext::MarkInDomainError::Mark(
1076 fnet_matchers_ext::MarkError::UnknownUnionVariant(100)
1077 )
1078 ));
1079 "MarkError"
1080 )]
1081 fn ip_socket_matcher_try_from_error(
1082 fidl: fnet_sockets::IpSocketMatcher,
1083 ) -> Result<IpSocketMatcher, IpSocketMatcherError> {
1084 IpSocketMatcher::try_from(fidl)
1085 }
1086
1087 #[test_case(
1088 fnet_sockets::IpSocketState {
1089 family: None,
1090 src_addr: Some(fidl_ip!("192.168.1.1")),
1091 dst_addr: Some(fidl_ip!("192.168.1.2")),
1092 cookie: Some(1234),
1093 marks: Some(fnet::Marks {
1094 mark_1: Some(1111),
1095 mark_2: None,
1096 __source_breaking: fidl::marker::SourceBreaking,
1097 }),
1098 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1099 fnet_sockets::IpSocketTcpState {
1100 src_port: Some(1111),
1101 dst_port: Some(2222),
1102 state: Some(fnet_tcp::State::Established),
1103 tcp_info: None,
1104 __source_breaking: fidl::marker::SourceBreaking,
1105 },
1106 )),
1107 __source_breaking: fidl::marker::SourceBreaking,
1108 } => Err(IpSocketStateError::MissingField("family"));
1109 "MissingFamily"
1110 )]
1111 #[test_case(
1112 fnet_sockets::IpSocketState {
1113 family: Some(fnet::IpVersion::V4),
1114 src_addr: Some(fidl_ip!("192.168.1.1")),
1115 dst_addr: Some(fidl_ip!("192.168.1.2")),
1116 cookie: None,
1117 marks: Some(fnet::Marks {
1118 mark_1: Some(1111),
1119 mark_2: None,
1120 __source_breaking: fidl::marker::SourceBreaking,
1121 }),
1122 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1123 fnet_sockets::IpSocketTcpState {
1124 src_port: Some(1111),
1125 dst_port: Some(2222),
1126 state: Some(fnet_tcp::State::Established),
1127 tcp_info: None,
1128 __source_breaking: fidl::marker::SourceBreaking,
1129 },
1130 )),
1131 __source_breaking: fidl::marker::SourceBreaking,
1132 } => Err(IpSocketStateError::MissingField("cookie"));
1133 "MissingCookie"
1134 )]
1135 #[test_case(
1136 fnet_sockets::IpSocketState {
1137 family: Some(fnet::IpVersion::V4),
1138 src_addr: Some(fidl_ip!("192.168.1.1")),
1139 dst_addr: Some(fidl_ip!("192.168.1.2")),
1140 cookie: Some(1234),
1141 marks: None,
1142 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1143 fnet_sockets::IpSocketTcpState {
1144 src_port: Some(1111),
1145 dst_port: Some(2222),
1146 state: Some(fnet_tcp::State::Established),
1147 tcp_info: None,
1148 __source_breaking: fidl::marker::SourceBreaking,
1149 },
1150 )),
1151 __source_breaking: fidl::marker::SourceBreaking,
1152 } => Err(IpSocketStateError::MissingField("marks"));
1153 "MissingMarks"
1154 )]
1155 #[test_case(
1156 fnet_sockets::IpSocketState {
1157 family: Some(fnet::IpVersion::V4),
1158 src_addr: Some(fidl_ip!("192.168.1.1")),
1159 dst_addr: Some(fidl_ip!("192.168.1.2")),
1160 cookie: Some(1234),
1161 marks: Some(fnet::Marks {
1162 mark_1: Some(1111),
1163 mark_2: None,
1164 __source_breaking: fidl::marker::SourceBreaking,
1165 }),
1166 transport: None,
1167 __source_breaking: fidl::marker::SourceBreaking,
1168 } => Err(IpSocketStateError::MissingField("transport"));
1169 "MissingTransport"
1170 )]
1171 #[test_case(
1172 fnet_sockets::IpSocketState {
1173 family: Some(fnet::IpVersion::V4),
1174 src_addr: Some(fidl_ip!("192.168.1.1")),
1175 dst_addr: Some(fidl_ip!("2001:db8::2")),
1176 cookie: Some(1234),
1177 marks: Some(fnet::Marks {
1178 mark_1: Some(1111),
1179 mark_2: None,
1180 __source_breaking: fidl::marker::SourceBreaking,
1181 }),
1182 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1183 fnet_sockets::IpSocketTcpState {
1184 src_port: Some(1111),
1185 dst_port: Some(2222),
1186 state: Some(fnet_tcp::State::Established),
1187 tcp_info: None,
1188 __source_breaking: fidl::marker::SourceBreaking,
1189 },
1190 )),
1191 __source_breaking: fidl::marker::SourceBreaking,
1192 } => Err(IpSocketStateError::VersionMismatch);
1193 "VersionMismatchV4"
1194 )]
1195 #[test_case(
1196 fnet_sockets::IpSocketState {
1197 family: Some(fnet::IpVersion::V6),
1198 src_addr: Some(fidl_ip!("192.168.1.1")),
1199 dst_addr: Some(fidl_ip!("2001:db8::2")),
1200 cookie: Some(1234),
1201 marks: Some(fnet::Marks {
1202 mark_1: Some(1111),
1203 mark_2: None,
1204 __source_breaking: fidl::marker::SourceBreaking,
1205 }),
1206 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1207 fnet_sockets::IpSocketTcpState {
1208 src_port: Some(1111),
1209 dst_port: Some(2222),
1210 state: Some(fnet_tcp::State::Established),
1211 tcp_info: None,
1212 __source_breaking: fidl::marker::SourceBreaking,
1213 },
1214 )),
1215 __source_breaking: fidl::marker::SourceBreaking,
1216 } => Err(IpSocketStateError::VersionMismatch);
1217 "VersionMismatchV6"
1218 )]
1219 #[test_case(
1220 fnet_sockets::IpSocketState {
1221 family: Some(fnet::IpVersion::V4),
1222 src_addr: Some(fidl_ip!("192.168.1.1")),
1223 dst_addr: Some(fidl_ip!("192.168.1.2")),
1224 cookie: Some(1234),
1225 marks: Some(fnet::Marks {
1226 mark_1: Some(1111),
1227 mark_2: None,
1228 __source_breaking: fidl::marker::SourceBreaking,
1229 }),
1230 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1231 fnet_sockets::IpSocketTcpState {
1232 src_port: Some(1111),
1233 dst_port: Some(2222),
1234 state: None,
1235 tcp_info: None,
1236 __source_breaking: fidl::marker::SourceBreaking,
1237 },
1238 )),
1239 __source_breaking: fidl::marker::SourceBreaking,
1240 } => Err(IpSocketStateError::Transport(IpSocketTransportStateError::Tcp(
1241 IpSocketTcpStateError::MissingField("state"),
1242 )));
1243 "MissingTcpState"
1244 )]
1245 #[test_case(
1246 fnet_sockets::IpSocketState {
1247 family: Some(fnet::IpVersion::V6),
1248 src_addr: Some(fidl_ip!("2001:db8::1")),
1249 dst_addr: Some(fidl_ip!("2001:db8::2")),
1250 cookie: Some(1234),
1251 marks: Some(fnet::Marks {
1252 mark_1: Some(1111),
1253 mark_2: None,
1254 __source_breaking: fidl::marker::SourceBreaking,
1255 }),
1256 transport: Some(fnet_sockets::IpSocketTransportState::Udp(
1257 fnet_sockets::IpSocketUdpState {
1258 src_port: Some(3333),
1259 dst_port: Some(4444),
1260 state: None,
1261 __source_breaking: fidl::marker::SourceBreaking,
1262 },
1263 )),
1264 __source_breaking: fidl::marker::SourceBreaking,
1265 } => Err(IpSocketStateError::Transport(IpSocketTransportStateError::Udp(
1266 IpSocketUdpStateError::MissingField("state"),
1267 )));
1268 "MissingUdpState"
1269 )]
1270 fn ip_socket_state_try_from_error(
1271 fidl: fnet_sockets::IpSocketState,
1272 ) -> Result<IpSocketState, IpSocketStateError> {
1273 IpSocketState::try_from(fidl)
1274 }
1275
1276 #[fuchsia_async::run_singlethreaded(test)]
1277 async fn iterate_ip_diagnostics_iterate_ip_error() {
1278 async fn serve_matcher_error(req: fnet_sockets::DiagnosticsRequest) {
1279 match req {
1280 fnet_sockets::DiagnosticsRequest::IterateIp {
1281 s: _,
1282 extensions: _,
1283 matchers: _,
1284 responder,
1285 } => responder
1286 .send(&fnet_sockets::IterateIpResult::InvalidMatcher(
1287 fnet_sockets::InvalidMatcher { index: 0 },
1288 ))
1289 .unwrap(),
1290 fnet_sockets::DiagnosticsRequest::GetDestructionWatcher { .. } => unreachable!(),
1291 };
1292 }
1293
1294 let (diagnostics, diagnostics_server_end) =
1295 fidl::endpoints::create_proxy::<fnet_sockets::DiagnosticsMarker>();
1296
1297 let (mut diagnostics_request_stream, _control_handle) =
1298 diagnostics_server_end.into_stream_and_control_handle();
1299 let server_fut = diagnostics_request_stream
1300 .next()
1301 .then(|req| {
1302 serve_matcher_error(req.expect("Request stream ended unexpectedly").unwrap())
1303 })
1304 .fuse();
1305 let client_fut = iterate_ip::<[IpSocketMatcher; 0], _>(
1306 &diagnostics,
1307 fnet_sockets::Extensions::empty(),
1308 [],
1309 );
1310
1311 pin_mut!(server_fut);
1312 pin_mut!(client_fut);
1313
1314 let ((), resp) = future::join(server_fut, client_fut).await;
1315
1316 assert_matches!(
1317 resp.map(|_| ()),
1319 Err(IterateIpError::InvalidMatcher(0))
1320 );
1321 }
1322
1323 #[fuchsia_async::run_singlethreaded(test)]
1324 async fn iterate_ip_next_error() {
1325 async fn serve_matcher(req: fnet_sockets::DiagnosticsRequest) {
1326 match req {
1327 fnet_sockets::DiagnosticsRequest::IterateIp {
1328 s,
1329 extensions: _,
1330 matchers: _,
1331 responder,
1332 } => {
1333 s.close_with_epitaph(zx_status::Status::PEER_CLOSED).unwrap();
1334 responder.send(&fnet_sockets::IterateIpResult::Ok(fnet_sockets::Empty)).unwrap()
1335 }
1336 fnet_sockets::DiagnosticsRequest::GetDestructionWatcher { .. } => unreachable!(),
1337 }
1338 }
1339
1340 let (diagnostics, diagnostics_server_end) =
1341 fidl::endpoints::create_proxy::<fnet_sockets::DiagnosticsMarker>();
1342
1343 let (mut diagnostics_request_stream, _control_handle) =
1344 diagnostics_server_end.into_stream_and_control_handle();
1345 let server_fut = diagnostics_request_stream
1346 .next()
1347 .then(|req| serve_matcher(req.expect("Request stream ended unexpectedly").unwrap()))
1348 .fuse();
1349
1350 let client_fut = iterate_ip::<[IpSocketMatcher; 0], _>(
1351 &diagnostics,
1352 fnet_sockets::Extensions::empty(),
1353 [],
1354 );
1355
1356 let ((), resp) = future::join(server_fut, client_fut).await;
1357 let stream = resp.unwrap();
1358 pin_mut!(stream);
1359
1360 assert_matches!(
1361 stream.try_next().await,
1362 Err(IpIteratorError::Fidl(fidl::Error::ClientChannelClosed { .. }))
1363 );
1364 }
1365
1366 #[fuchsia_async::run_singlethreaded(test)]
1367 async fn iterate_ip_empty_batch() {
1368 async fn serve_matcher(req: fnet_sockets::DiagnosticsRequest) {
1369 match req {
1370 fnet_sockets::DiagnosticsRequest::IterateIp {
1371 s,
1372 extensions: _,
1373 matchers: _,
1374 responder,
1375 } => {
1376 responder
1377 .send(&fnet_sockets::IterateIpResult::Ok(fnet_sockets::Empty))
1378 .unwrap();
1379
1380 let (mut stream, _control) = s.into_stream_and_control_handle();
1381 match stream.next().await.unwrap().unwrap() {
1382 fidl_fuchsia_net_sockets::IpIteratorRequest::Next { responder } => {
1383 responder.send(&[], true).unwrap();
1385 }
1386 fidl_fuchsia_net_sockets::IpIteratorRequest::_UnknownMethod { .. } => {
1387 unreachable!()
1388 }
1389 }
1390 }
1391 fnet_sockets::DiagnosticsRequest::GetDestructionWatcher { .. } => unreachable!(),
1392 }
1393 }
1394
1395 let (diagnostics, diagnostics_server_end) =
1396 fidl::endpoints::create_proxy::<fnet_sockets::DiagnosticsMarker>();
1397
1398 let (mut diagnostics_request_stream, _control_handle) =
1399 diagnostics_server_end.into_stream_and_control_handle();
1400 let server_fut = diagnostics_request_stream
1401 .next()
1402 .then(|req| serve_matcher(req.expect("Request stream ended unexpectedly").unwrap()))
1403 .fuse();
1404
1405 let client_fut = async {
1406 let stream = iterate_ip::<[IpSocketMatcher; 0], _>(
1407 &diagnostics,
1408 fnet_sockets::Extensions::empty(),
1409 [],
1410 )
1411 .await
1412 .unwrap();
1413 pin_mut!(stream);
1414 stream.try_next().await
1415 };
1416
1417 let ((), resp) = future::join(server_fut, client_fut).await;
1418 assert_matches!(resp, Err(IpIteratorError::EmptyBatch));
1419 }
1420
1421 #[fuchsia_async::run_singlethreaded(test)]
1422 async fn iterate_ip_success() {
1423 let socket_1 = fnet_sockets::IpSocketState {
1424 family: Some(fnet::IpVersion::V4),
1425 src_addr: Some(fidl_ip!("192.168.1.1")),
1426 dst_addr: Some(fidl_ip!("192.168.1.2")),
1427 cookie: Some(1234),
1428 marks: Some(fnet::Marks {
1429 mark_1: Some(1111),
1430 mark_2: None,
1431 __source_breaking: fidl::marker::SourceBreaking,
1432 }),
1433 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1434 fnet_sockets::IpSocketTcpState {
1435 src_port: Some(1111),
1436 dst_port: Some(2222),
1437 state: Some(fnet_tcp::State::Established),
1438 tcp_info: None,
1439 __source_breaking: fidl::marker::SourceBreaking,
1440 },
1441 )),
1442 __source_breaking: fidl::marker::SourceBreaking,
1443 };
1444
1445 let socket_2 = fnet_sockets::IpSocketState {
1446 family: Some(fnet::IpVersion::V4),
1447 src_addr: Some(fidl_ip!("192.168.8.1")),
1448 dst_addr: Some(fidl_ip!("192.168.8.2")),
1449 cookie: Some(9876),
1450 marks: Some(fnet::Marks {
1451 mark_1: None,
1452 mark_2: Some(2222),
1453 __source_breaking: fidl::marker::SourceBreaking,
1454 }),
1455 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1456 fnet_sockets::IpSocketTcpState {
1457 src_port: Some(3333),
1458 dst_port: Some(4444),
1459 state: Some(fnet_tcp::State::TimeWait),
1460 tcp_info: None,
1461 __source_breaking: fidl::marker::SourceBreaking,
1462 },
1463 )),
1464 __source_breaking: fidl::marker::SourceBreaking,
1465 };
1466
1467 let socket_3 = fnet_sockets::IpSocketState {
1468 family: Some(fnet::IpVersion::V6),
1469 src_addr: Some(fidl_ip!("2001:db8::1")),
1470 dst_addr: Some(fidl_ip!("2001:db8::2")),
1471 cookie: Some(5678),
1472 marks: Some(fnet::Marks {
1473 mark_1: None,
1474 mark_2: None,
1475 __source_breaking: fidl::marker::SourceBreaking,
1476 }),
1477 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1478 fnet_sockets::IpSocketTcpState {
1479 src_port: Some(5555),
1480 dst_port: Some(6666),
1481 state: Some(fnet_tcp::State::TimeWait),
1482 tcp_info: None,
1483 __source_breaking: fidl::marker::SourceBreaking,
1484 },
1485 )),
1486 __source_breaking: fidl::marker::SourceBreaking,
1487 };
1488
1489 let (diagnostics, diagnostics_server_end) =
1490 fidl::endpoints::create_proxy::<fnet_sockets::DiagnosticsMarker>();
1491
1492 let serve_matcher = async |req: fnet_sockets::DiagnosticsRequest| {
1493 let responses = &[vec![socket_1.clone()], vec![socket_2.clone(), socket_3.clone()]];
1494
1495 match req {
1496 fnet_sockets::DiagnosticsRequest::IterateIp {
1497 s,
1498 extensions: _,
1499 matchers: _,
1500 responder,
1501 } => {
1502 responder
1503 .send(&fnet_sockets::IterateIpResult::Ok(fnet_sockets::Empty))
1504 .unwrap();
1505
1506 let (mut stream, _control) = s.into_stream_and_control_handle();
1507 for (i, resp) in responses.iter().enumerate() {
1508 match stream.next().await.unwrap().unwrap() {
1509 fidl_fuchsia_net_sockets::IpIteratorRequest::Next { responder } => {
1510 let has_more = i < responses.len() - 1;
1511 responder.send(&resp, has_more).unwrap();
1512 }
1513 fidl_fuchsia_net_sockets::IpIteratorRequest::_UnknownMethod {
1514 ..
1515 } => {
1516 unreachable!()
1517 }
1518 }
1519 }
1520 }
1521 fnet_sockets::DiagnosticsRequest::GetDestructionWatcher { .. } => unreachable!(),
1522 };
1523 };
1524
1525 let (mut diagnostics_request_stream, _control_handle) =
1526 diagnostics_server_end.into_stream_and_control_handle();
1527
1528 let server_fut = diagnostics_request_stream
1529 .next()
1530 .then(|req| serve_matcher(req.expect("Request stream ended unexpectedly").unwrap()))
1531 .fuse();
1532
1533 let client_fut = async {
1534 iterate_ip::<[IpSocketMatcher; 0], _>(
1535 &diagnostics,
1536 fnet_sockets::Extensions::empty(),
1537 [],
1538 )
1539 .await
1540 .unwrap()
1541 .try_collect::<Vec<_>>()
1542 .await
1543 .unwrap()
1544 };
1545
1546 let ((), sockets) = future::join(server_fut, client_fut).await;
1547 assert_eq!(
1548 sockets,
1549 vec![
1550 socket_1.clone().try_into().unwrap(),
1551 socket_2.clone().try_into().unwrap(),
1552 socket_3.clone().try_into().unwrap()
1553 ]
1554 );
1555 }
1556
1557 #[fuchsia_async::run_singlethreaded(test)]
1558 async fn watch_destruction_success() {
1559 let socket_1 = fnet_sockets::IpSocketState {
1560 family: Some(fnet::IpVersion::V4),
1561 src_addr: Some(fidl_ip!("192.168.1.1")),
1562 dst_addr: Some(fidl_ip!("192.168.1.2")),
1563 cookie: Some(1234),
1564 marks: Some(fnet::Marks {
1565 mark_1: Some(1111),
1566 mark_2: None,
1567 __source_breaking: fidl::marker::SourceBreaking,
1568 }),
1569 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1570 fnet_sockets::IpSocketTcpState {
1571 src_port: Some(1111),
1572 dst_port: Some(2222),
1573 state: Some(fnet_tcp::State::Established),
1574 tcp_info: None,
1575 __source_breaking: fidl::marker::SourceBreaking,
1576 },
1577 )),
1578 __source_breaking: fidl::marker::SourceBreaking,
1579 };
1580
1581 let socket_2 = fnet_sockets::IpSocketState {
1582 family: Some(fnet::IpVersion::V4),
1583 src_addr: Some(fidl_ip!("192.168.8.1")),
1584 dst_addr: Some(fidl_ip!("192.168.8.2")),
1585 cookie: Some(9876),
1586 marks: Some(fnet::Marks {
1587 mark_1: None,
1588 mark_2: Some(2222),
1589 __source_breaking: fidl::marker::SourceBreaking,
1590 }),
1591 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1592 fnet_sockets::IpSocketTcpState {
1593 src_port: Some(3333),
1594 dst_port: Some(4444),
1595 state: Some(fnet_tcp::State::TimeWait),
1596 tcp_info: None,
1597 __source_breaking: fidl::marker::SourceBreaking,
1598 },
1599 )),
1600 __source_breaking: fidl::marker::SourceBreaking,
1601 };
1602
1603 let (diagnostics, diagnostics_server_end) =
1604 fidl::endpoints::create_proxy::<fnet_sockets::DiagnosticsMarker>();
1605 let serve_watcher = async |req: fnet_sockets::DiagnosticsRequest| match req {
1606 fnet_sockets::DiagnosticsRequest::GetDestructionWatcher { watcher, responder } => {
1607 responder.send().unwrap();
1608 let mut stream = watcher.into_stream();
1609 let batches = [
1610 Some(vec![socket_1.clone()]),
1611 Some(vec![socket_2.clone(), socket_1.clone()]),
1612 None,
1613 ];
1614 for batch in batches {
1615 let req = stream.next().await.unwrap().unwrap();
1616 let responder = match req {
1617 fnet_sockets::DestructionWatcherRequest::Watch { responder } => responder,
1618 fnet_sockets::DestructionWatcherRequest::_UnknownMethod { .. } => {
1619 unreachable!()
1620 }
1621 };
1622 if let Some(batch) = batch {
1623 responder.send(&batch).unwrap();
1624 } else {
1625 drop(responder);
1626 }
1627 }
1628 }
1629 fnet_sockets::DiagnosticsRequest::IterateIp { .. } => unreachable!(),
1630 };
1631
1632 let (mut diagnostics_request_stream, _control_handle) =
1633 diagnostics_server_end.into_stream_and_control_handle();
1634 let server_fut = diagnostics_request_stream
1635 .next()
1636 .then(|req| serve_watcher(req.expect("Request stream ended unexpectedly").unwrap()))
1637 .fuse();
1638
1639 let expected_socket_1: IpSocketState = socket_1.clone().try_into().unwrap();
1640 let expected_socket_2: IpSocketState = socket_2.clone().try_into().unwrap();
1641
1642 let client_fut = async {
1643 let stream = watch_destruction(&diagnostics).await.unwrap();
1644 pin_mut!(stream);
1645
1646 assert_matches!(
1647 stream.next().await,
1648 Some(Ok(sock)) => assert_eq!(sock, expected_socket_1)
1649 );
1650 assert_matches!(
1651 stream.next().await,
1652 Some(Ok(sock)) => assert_eq!(sock, expected_socket_2)
1653 );
1654 assert_matches!(
1655 stream.next().await,
1656 Some(Ok(sock)) => assert_eq!(sock, expected_socket_1)
1657 );
1658 assert_matches!(stream.next().await, Some(Err(DestructionWatcherError::Fidl(_))));
1659 };
1660
1661 let _: ((), ()) = future::join(server_fut, client_fut).await;
1662 }
1663
1664 #[test_case(
1665 None,
1666 DestructionWatcherError::EmptyBatch;
1667 "empty_batch"
1668 )]
1669 #[test_case(
1670 Some(fnet_sockets::IpSocketState {
1671 family: None,
1672 src_addr: Some(fidl_ip!("192.168.1.1")),
1673 dst_addr: Some(fidl_ip!("192.168.1.2")),
1674 cookie: Some(1234),
1675 marks: Some(fnet::Marks {
1676 mark_1: Some(1111),
1677 mark_2: None,
1678 __source_breaking: fidl::marker::SourceBreaking,
1679 }),
1680 transport: Some(fnet_sockets::IpSocketTransportState::Tcp(
1681 fnet_sockets::IpSocketTcpState {
1682 src_port: Some(1111),
1683 dst_port: Some(2222),
1684 state: Some(fnet_tcp::State::Established),
1685 tcp_info: None,
1686 __source_breaking: fidl::marker::SourceBreaking,
1687 },
1688 )),
1689 __source_breaking: fidl::marker::SourceBreaking,
1690 }),
1691 DestructionWatcherError::Conversion(IpSocketStateError::MissingField("family"));
1692 "conversion_error"
1693 )]
1694 #[fuchsia_async::run_singlethreaded(test)]
1695 async fn watch_destruction_error(
1696 socket: Option<fnet_sockets::IpSocketState>,
1697 expected_error: DestructionWatcherError,
1698 ) {
1699 let (diagnostics, diagnostics_server_end) =
1700 fidl::endpoints::create_proxy::<fnet_sockets::DiagnosticsMarker>();
1701 let serve_watcher = async |req: fnet_sockets::DiagnosticsRequest| match req {
1702 fnet_sockets::DiagnosticsRequest::GetDestructionWatcher { watcher, responder } => {
1703 responder.send().unwrap();
1704 let mut stream = watcher.into_stream();
1705 let req = stream.next().await.unwrap().unwrap();
1706 let responder = match req {
1707 fnet_sockets::DestructionWatcherRequest::Watch { responder } => responder,
1708 fnet_sockets::DestructionWatcherRequest::_UnknownMethod { .. } => {
1709 unreachable!()
1710 }
1711 };
1712 let batch = match socket {
1713 None => vec![],
1714 Some(s) => vec![s],
1715 };
1716 responder.send(&batch).unwrap();
1717 }
1718 fnet_sockets::DiagnosticsRequest::IterateIp { .. } => unreachable!(),
1719 };
1720
1721 let (mut diagnostics_request_stream, _control_handle) =
1722 diagnostics_server_end.into_stream_and_control_handle();
1723 let server_fut = diagnostics_request_stream
1724 .next()
1725 .then(|req| serve_watcher(req.expect("Request stream ended unexpectedly").unwrap()))
1726 .fuse();
1727
1728 let client_fut = async {
1729 let stream = watch_destruction(&diagnostics).await.unwrap();
1730 pin_mut!(stream);
1731
1732 let result = stream.next().await.expect("got a result");
1733 assert_matches!(stream.next().await, None);
1734 result
1735 };
1736
1737 let ((), result) = future::join(server_fut, client_fut).await;
1738 match expected_error {
1739 DestructionWatcherError::EmptyBatch => {
1740 assert_matches!(result, Err(DestructionWatcherError::EmptyBatch));
1741 }
1742 DestructionWatcherError::Conversion(b) => {
1743 assert_matches!(result, Err(DestructionWatcherError::Conversion(a)) if a == b);
1744 }
1745 DestructionWatcherError::Fidl(_) => unreachable!(),
1746 }
1747 }
1748}