Skip to main content

fidl_fuchsia_net_matchers_ext/
lib.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Extensions for the fuchsia.net.matchers FIDL library.
6//!
7//! Note that this library as written is not meant for inclusion in the SDK. It
8//! is only meant to be used in conjunction with a netstack that is compiled
9//! against the same API level of the `fuchsia.net.matchers` FIDL library. This
10//! library opts in to compile-time and runtime breakage when the FIDL library
11//! is evolved in order to enforce that it is updated along with the FIDL
12//! library itself.
13
14use std::fmt::Debug;
15use std::num::NonZeroU64;
16use std::ops::RangeInclusive;
17
18use fidl::marker::SourceBreaking;
19use fidl_fuchsia_net_ext::{FromExt, IntoExt};
20use net_types::ip::Ip;
21use thiserror::Error;
22use {
23    fidl_fuchsia_net as fnet, fidl_fuchsia_net_interfaces as fnet_interfaces,
24    fidl_fuchsia_net_interfaces_ext as fnet_interfaces_ext,
25    fidl_fuchsia_net_matchers as fnet_matchers,
26};
27
28/// Extension type for [`fnet_matchers::Interface`].
29#[derive(Debug, Clone, PartialEq, Eq, Hash)]
30pub enum Interface {
31    Id(NonZeroU64),
32    Name(fnet_interfaces::Name),
33    PortClass(fnet_interfaces_ext::PortClass),
34}
35
36/// Errors when creating an [`Interface`].
37#[derive(Debug, Error, PartialEq, Eq)]
38pub enum InterfaceError {
39    #[error("interface matcher specified an invalid ID of 0")]
40    ZeroId,
41    #[error(transparent)]
42    UnknownPortClass(fnet_interfaces_ext::UnknownPortClassError),
43    #[error("interface union is of an unknown variant")]
44    UnknownUnionVariant,
45}
46
47impl From<Interface> for fnet_matchers::Interface {
48    fn from(matcher: Interface) -> Self {
49        match matcher {
50            Interface::Id(id) => Self::Id(id.get()),
51            Interface::Name(name) => Self::Name(name),
52            Interface::PortClass(port_class) => Self::PortClass(port_class.into()),
53        }
54    }
55}
56
57impl TryFrom<fnet_matchers::Interface> for Interface {
58    type Error = InterfaceError;
59
60    fn try_from(matcher: fnet_matchers::Interface) -> Result<Self, Self::Error> {
61        match matcher {
62            fnet_matchers::Interface::Id(id) => {
63                let id = NonZeroU64::new(id).ok_or(InterfaceError::ZeroId)?;
64                Ok(Self::Id(id))
65            }
66            fnet_matchers::Interface::Name(name) => Ok(Self::Name(name)),
67            fnet_matchers::Interface::PortClass(port_class) => {
68                port_class.try_into().map(Self::PortClass).map_err(InterfaceError::UnknownPortClass)
69            }
70            fnet_matchers::Interface::__SourceBreaking { .. } => {
71                Err(InterfaceError::UnknownUnionVariant)
72            }
73        }
74    }
75}
76
77/// Extension type for [`fnet_matchers::BoundInterface`].
78#[derive(Debug, Clone, PartialEq, Eq, Hash)]
79pub enum BoundInterface {
80    Unbound,
81    Bound(Interface),
82}
83
84/// Errors when creating an [`BoundInterface`].
85#[derive(Debug, Error, PartialEq)]
86pub enum BoundInterfaceError {
87    #[error(transparent)]
88    Interface(InterfaceError),
89    #[error("interface union is of an unknown variant")]
90    UnknownUnionVariant(u64),
91}
92
93impl From<BoundInterface> for fnet_matchers::BoundInterface {
94    fn from(matcher: BoundInterface) -> Self {
95        match matcher {
96            BoundInterface::Unbound => fnet_matchers::BoundInterface::Unbound(fnet_matchers::Empty),
97            BoundInterface::Bound(interface) => {
98                fnet_matchers::BoundInterface::Bound(interface.into())
99            }
100        }
101    }
102}
103
104impl TryFrom<fnet_matchers::BoundInterface> for BoundInterface {
105    type Error = BoundInterfaceError;
106
107    fn try_from(matcher: fnet_matchers::BoundInterface) -> Result<Self, Self::Error> {
108        match matcher {
109            fnet_matchers::BoundInterface::Unbound(fnet_matchers::Empty) => {
110                Ok(BoundInterface::Unbound)
111            }
112            fnet_matchers::BoundInterface::Bound(interface) => Ok(BoundInterface::Bound(
113                interface.try_into().map_err(|e| BoundInterfaceError::Interface(e))?,
114            )),
115            fnet_matchers::BoundInterface::__SourceBreaking { unknown_ordinal } => {
116                Err(BoundInterfaceError::UnknownUnionVariant(unknown_ordinal))
117            }
118        }
119    }
120}
121
122/// Extension type for the `Subnet` variant of [`fnet_matchers::Address`].
123///
124/// This type witnesses to the invariant that the prefix length of the subnet is
125/// no greater than the number of bits in the IP address, and that no host bits
126/// in the network address are set.
127#[derive(Clone, Copy, Eq, Hash, PartialEq)]
128pub struct Subnet(net_types::ip::SubnetEither);
129
130/// Errors when creating a [`Subnet`].
131#[derive(Debug, Error, PartialEq)]
132pub enum SubnetError {
133    #[error("prefix length of subnet is longer than number of bits in IP address")]
134    PrefixTooLong,
135    #[error("host bits are set in subnet network")]
136    HostBitsSet,
137}
138
139impl From<Subnet> for net_types::ip::SubnetEither {
140    fn from(subnet: Subnet) -> Self {
141        let Subnet(subnet) = subnet;
142        subnet
143    }
144}
145
146impl From<net_types::ip::SubnetEither> for Subnet {
147    fn from(subnet: net_types::ip::SubnetEither) -> Self {
148        Self(subnet)
149    }
150}
151
152impl From<Subnet> for fnet::Subnet {
153    fn from(subnet: Subnet) -> Self {
154        let Subnet(subnet) = subnet;
155        let (addr, prefix_len) = subnet.net_prefix();
156
157        Self { addr: fnet::IpAddress::from_ext(addr), prefix_len }
158    }
159}
160
161impl TryFrom<fnet::Subnet> for Subnet {
162    type Error = SubnetError;
163
164    fn try_from(subnet: fnet::Subnet) -> Result<Self, Self::Error> {
165        let fnet::Subnet { addr, prefix_len } = subnet;
166
167        match net_types::ip::SubnetEither::new(addr.into_ext(), prefix_len) {
168            Ok(inner) => Ok(Self(inner)),
169            Err(err) => Err(match err {
170                net_types::ip::SubnetError::PrefixTooLong => SubnetError::PrefixTooLong,
171                net_types::ip::SubnetError::HostBitsSet => SubnetError::HostBitsSet,
172            }),
173        }
174    }
175}
176
177impl Debug for Subnet {
178    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
179        let Self(subnet) = self;
180        match subnet {
181            net_types::ip::SubnetEither::V4(subnet) => subnet.fmt(f),
182            net_types::ip::SubnetEither::V6(subnet) => subnet.fmt(f),
183        }
184    }
185}
186
187/// Extension type for [`fnet_matchers::AddressRange`].
188///
189/// This type witnesses that `start <= end`. (Comparisons are performed on the
190/// numerical big-endian representation of the IP address.)
191#[derive(Debug, Clone, PartialEq, Eq)]
192pub enum AddressRange {
193    V4(RangeInclusive<fnet::Ipv4Address>),
194    V6(RangeInclusive<fnet::Ipv6Address>),
195}
196
197/// Errors when creating an [`AddressRange`].
198#[derive(Debug, Error, PartialEq)]
199pub enum AddressRangeError {
200    #[error("invalid address range (start must be <= end)")]
201    Invalid,
202    #[error("address range start and end addresses are not the same IP family")]
203    FamilyMismatch,
204}
205
206impl From<AddressRange> for fnet_matchers::AddressRange {
207    fn from(range: AddressRange) -> Self {
208        let (start, end) = match range {
209            AddressRange::V4(range) => ((*range.start()).into_ext(), (*range.end()).into_ext()),
210            AddressRange::V6(range) => ((*range.start()).into_ext(), (*range.end()).into_ext()),
211        };
212
213        Self { start, end }
214    }
215}
216
217impl TryFrom<fnet_matchers::AddressRange> for AddressRange {
218    type Error = AddressRangeError;
219
220    fn try_from(range: fnet_matchers::AddressRange) -> Result<Self, Self::Error> {
221        let fnet_matchers::AddressRange { start, end } = range;
222        match (start, end) {
223            (fnet::IpAddress::Ipv4(start), fnet::IpAddress::Ipv4(end)) => {
224                if u32::from_be_bytes(start.addr) > u32::from_be_bytes(end.addr) {
225                    Err(AddressRangeError::Invalid)
226                } else {
227                    Ok(Self::V4(start..=end))
228                }
229            }
230            (fnet::IpAddress::Ipv6(start), fnet::IpAddress::Ipv6(end)) => {
231                if u128::from_be_bytes(start.addr) > u128::from_be_bytes(end.addr) {
232                    Err(AddressRangeError::Invalid)
233                } else {
234                    Ok(Self::V6(start..=end))
235                }
236            }
237            _ => Err(AddressRangeError::FamilyMismatch),
238        }
239    }
240}
241
242impl AddressRange {
243    /// Create a new [`AddressRange`] for a single address.
244    pub fn new_single<I: Ip>(ip: I::Addr) -> Self {
245        I::map_ip_in(
246            ip,
247            |ip| {
248                let ip = ip.into_ext();
249                Self::V4(ip..=ip)
250            },
251            |ip| {
252                let ip = ip.into_ext();
253                Self::V6(ip..=ip)
254            },
255        )
256    }
257}
258
259/// Extension type for [`fnet_matchers::Address`].
260#[derive(Clone, PartialEq, Eq)]
261pub enum AddressMatcherType {
262    Subnet(Subnet),
263    Range(AddressRange),
264}
265
266/// Errors when creating an [`AddressMatcherType`].
267#[derive(Debug, Error, PartialEq)]
268pub enum AddressMatcherTypeError {
269    #[error("AddressMatcher is of an unknown variant")]
270    UnknownUnionVariant,
271    #[error("subnet conversion error: {0}")]
272    Subnet(SubnetError),
273    #[error("address range conversion error: {0}")]
274    AddressRange(AddressRangeError),
275}
276
277impl From<SubnetError> for AddressMatcherTypeError {
278    fn from(value: SubnetError) -> Self {
279        AddressMatcherTypeError::Subnet(value)
280    }
281}
282impl From<AddressRangeError> for AddressMatcherTypeError {
283    fn from(value: AddressRangeError) -> Self {
284        AddressMatcherTypeError::AddressRange(value)
285    }
286}
287
288impl From<AddressMatcherType> for fnet_matchers::AddressMatcherType {
289    fn from(matcher: AddressMatcherType) -> Self {
290        match matcher {
291            AddressMatcherType::Subnet(subnet) => Self::Subnet(subnet.into()),
292            AddressMatcherType::Range(range) => Self::Range(range.into()),
293        }
294    }
295}
296
297impl TryFrom<fnet_matchers::AddressMatcherType> for AddressMatcherType {
298    type Error = AddressMatcherTypeError;
299
300    fn try_from(matcher: fnet_matchers::AddressMatcherType) -> Result<Self, Self::Error> {
301        match matcher {
302            fnet_matchers::AddressMatcherType::Subnet(subnet) => {
303                Ok(Self::Subnet(subnet.try_into()?))
304            }
305            fnet_matchers::AddressMatcherType::Range(range) => Ok(Self::Range(range.try_into()?)),
306            fnet_matchers::AddressMatcherType::__SourceBreaking { .. } => {
307                Err(AddressMatcherTypeError::UnknownUnionVariant)
308            }
309        }
310    }
311}
312
313impl Debug for AddressMatcherType {
314    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
315        match self {
316            AddressMatcherType::Subnet(subnet) => subnet.fmt(f),
317            AddressMatcherType::Range(address_range) => address_range.fmt(f),
318        }
319    }
320}
321
322/// Extension type for [`fnet_matchers::Address`].
323#[derive(Debug, Clone, PartialEq, Eq)]
324pub struct Address {
325    pub matcher: AddressMatcherType,
326    pub invert: bool,
327}
328
329/// Errors when creating an [`Address`].
330#[derive(Debug, Error, PartialEq)]
331pub enum AddressError {
332    #[error("address matcher conversion failure: {0}")]
333    AddressMatcherType(AddressMatcherTypeError),
334}
335
336impl From<AddressMatcherTypeError> for AddressError {
337    fn from(value: AddressMatcherTypeError) -> Self {
338        Self::AddressMatcherType(value)
339    }
340}
341
342impl From<Address> for fnet_matchers::Address {
343    fn from(matcher: Address) -> Self {
344        let Address { matcher, invert } = matcher;
345        Self { matcher: matcher.into(), invert }
346    }
347}
348
349impl TryFrom<fnet_matchers::Address> for Address {
350    type Error = AddressError;
351
352    fn try_from(matcher: fnet_matchers::Address) -> Result<Self, Self::Error> {
353        let fnet_matchers::Address { matcher, invert } = matcher;
354        Ok(Self { matcher: matcher.try_into()?, invert })
355    }
356}
357
358/// Extension type for [`fnet_matchers::BoundAddress`].
359#[derive(Debug, Clone, PartialEq, Eq)]
360pub enum BoundAddress {
361    Unbound,
362    Bound(Address),
363}
364
365/// Errors when creating an [`BoundAddress`].
366#[derive(Debug, Error, PartialEq)]
367pub enum BoundAddressError {
368    #[error(transparent)]
369    Address(AddressError),
370    #[error("bound address union is of an unknown variant")]
371    UnknownUnionVariant(u64),
372}
373
374impl From<BoundAddress> for fnet_matchers::BoundAddress {
375    fn from(matcher: BoundAddress) -> Self {
376        match matcher {
377            BoundAddress::Unbound => fnet_matchers::BoundAddress::Unbound(fnet_matchers::Empty),
378            BoundAddress::Bound(address) => fnet_matchers::BoundAddress::Bound(address.into()),
379        }
380    }
381}
382
383impl TryFrom<fnet_matchers::BoundAddress> for BoundAddress {
384    type Error = BoundAddressError;
385
386    fn try_from(matcher: fnet_matchers::BoundAddress) -> Result<Self, Self::Error> {
387        match matcher {
388            fnet_matchers::BoundAddress::Unbound(fnet_matchers::Empty) => Ok(BoundAddress::Unbound),
389            fnet_matchers::BoundAddress::Bound(address) => {
390                Ok(BoundAddress::Bound(address.try_into().map_err(BoundAddressError::Address)?))
391            }
392            fnet_matchers::BoundAddress::__SourceBreaking { unknown_ordinal } => {
393                Err(BoundAddressError::UnknownUnionVariant(unknown_ordinal))
394            }
395        }
396    }
397}
398
399/// Extension type for [`fnet_matchers::Port`].
400///
401/// This type witnesses to the invariant that `start <= end`.
402#[derive(Debug, Clone, PartialEq, Eq, Hash)]
403pub struct Port {
404    range: RangeInclusive<u16>,
405    invert: bool,
406}
407
408/// Extension type for [`fnet_matchers::BoundPort`].
409#[derive(Debug, Clone, PartialEq, Eq, Hash)]
410pub enum BoundPort {
411    Unbound,
412    Bound(Port),
413}
414
415/// Errors when creating an [`BoundPort`].
416#[derive(Debug, Error, PartialEq, Eq)]
417pub enum BoundPortError {
418    #[error(transparent)]
419    Port(PortError),
420    #[error("bound port union is of an unknown variant")]
421    UnknownUnionVariant(u64),
422}
423
424impl From<BoundPort> for fnet_matchers::BoundPort {
425    fn from(matcher: BoundPort) -> Self {
426        match matcher {
427            BoundPort::Unbound => fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty),
428            BoundPort::Bound(port) => fnet_matchers::BoundPort::Bound(port.into()),
429        }
430    }
431}
432
433impl TryFrom<fnet_matchers::BoundPort> for BoundPort {
434    type Error = BoundPortError;
435
436    fn try_from(matcher: fnet_matchers::BoundPort) -> Result<Self, Self::Error> {
437        match matcher {
438            fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty) => Ok(BoundPort::Unbound),
439            fnet_matchers::BoundPort::Bound(port) => {
440                Ok(BoundPort::Bound(port.try_into().map_err(BoundPortError::Port)?))
441            }
442            fnet_matchers::BoundPort::__SourceBreaking { unknown_ordinal } => {
443                Err(BoundPortError::UnknownUnionVariant(unknown_ordinal))
444            }
445        }
446    }
447}
448
449/// Errors when creating a `Port`.
450#[derive(Debug, Error, PartialEq, Eq)]
451pub enum PortError {
452    #[error("invalid port range (start must be <= end)")]
453    InvalidPortRange,
454}
455
456impl Port {
457    pub fn new(start: u16, end: u16, invert: bool) -> Result<Self, PortError> {
458        if start > end {
459            return Err(PortError::InvalidPortRange);
460        }
461        Ok(Self { range: start..=end, invert })
462    }
463
464    /// Create a new [`Port`] for a single port.
465    pub fn new_single(port: u16, invert: bool) -> Self {
466        Self::new(port, port, invert).unwrap()
467    }
468
469    pub fn range(&self) -> &RangeInclusive<u16> {
470        &self.range
471    }
472
473    pub fn start(&self) -> u16 {
474        *self.range.start()
475    }
476
477    pub fn end(&self) -> u16 {
478        *self.range.end()
479    }
480
481    pub fn invert(&self) -> bool {
482        self.invert
483    }
484}
485
486impl From<Port> for fnet_matchers::Port {
487    fn from(matcher: Port) -> Self {
488        let Port { range, invert } = matcher;
489        Self { start: *range.start(), end: *range.end(), invert }
490    }
491}
492
493impl TryFrom<fnet_matchers::Port> for Port {
494    type Error = PortError;
495
496    fn try_from(matcher: fnet_matchers::Port) -> Result<Self, Self::Error> {
497        let fnet_matchers::Port { start, end, invert } = matcher;
498        if start > end {
499            return Err(PortError::InvalidPortRange);
500        }
501        Ok(Self { range: start..=end, invert })
502    }
503}
504
505/// Extension type for [`fnet_matchers::PacketTransportProtocol`].
506#[derive(Clone, PartialEq)]
507pub enum TransportProtocol {
508    Tcp { src_port: Option<Port>, dst_port: Option<Port> },
509    Udp { src_port: Option<Port>, dst_port: Option<Port> },
510    Icmp,
511    Icmpv6,
512}
513
514/// Errors when creating a [`TransportProtocol`].
515#[derive(Debug, Error, PartialEq)]
516pub enum TransportProtocolError {
517    #[error("invalid port: {0}")]
518    Port(PortError),
519    #[error("TransportProtocol is of an unknown variant")]
520    UnknownUnionVariant,
521}
522
523impl From<PortError> for TransportProtocolError {
524    fn from(value: PortError) -> Self {
525        TransportProtocolError::Port(value)
526    }
527}
528
529impl From<TransportProtocol> for fnet_matchers::PacketTransportProtocol {
530    fn from(matcher: TransportProtocol) -> Self {
531        match matcher {
532            TransportProtocol::Tcp { src_port, dst_port } => Self::Tcp(fnet_matchers::TcpPacket {
533                src_port: src_port.map(Into::into),
534                dst_port: dst_port.map(Into::into),
535                __source_breaking: SourceBreaking,
536            }),
537            TransportProtocol::Udp { src_port, dst_port } => Self::Udp(fnet_matchers::UdpPacket {
538                src_port: src_port.map(Into::into),
539                dst_port: dst_port.map(Into::into),
540                __source_breaking: SourceBreaking,
541            }),
542            TransportProtocol::Icmp => Self::Icmp(fnet_matchers::IcmpPacket::default()),
543            TransportProtocol::Icmpv6 => Self::Icmpv6(fnet_matchers::Icmpv6Packet::default()),
544        }
545    }
546}
547
548impl TryFrom<fnet_matchers::PacketTransportProtocol> for TransportProtocol {
549    type Error = TransportProtocolError;
550
551    fn try_from(matcher: fnet_matchers::PacketTransportProtocol) -> Result<Self, Self::Error> {
552        match matcher {
553            fnet_matchers::PacketTransportProtocol::Tcp(fnet_matchers::TcpPacket {
554                src_port,
555                dst_port,
556                __source_breaking,
557            }) => Ok(Self::Tcp {
558                src_port: src_port.map(TryInto::try_into).transpose()?,
559                dst_port: dst_port.map(TryInto::try_into).transpose()?,
560            }),
561            fnet_matchers::PacketTransportProtocol::Udp(fnet_matchers::UdpPacket {
562                src_port,
563                dst_port,
564                __source_breaking,
565            }) => Ok(Self::Udp {
566                src_port: src_port.map(TryInto::try_into).transpose()?,
567                dst_port: dst_port.map(TryInto::try_into).transpose()?,
568            }),
569            fnet_matchers::PacketTransportProtocol::Icmp(fnet_matchers::IcmpPacket {
570                __source_breaking,
571            }) => Ok(Self::Icmp),
572            fnet_matchers::PacketTransportProtocol::Icmpv6(fnet_matchers::Icmpv6Packet {
573                __source_breaking,
574            }) => Ok(Self::Icmpv6),
575            fnet_matchers::PacketTransportProtocol::__SourceBreaking { .. } => {
576                Err(TransportProtocolError::UnknownUnionVariant)
577            }
578        }
579    }
580}
581
582impl Debug for TransportProtocol {
583    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
584        // Omit empty fields.
585        match self {
586            TransportProtocol::Tcp { src_port, dst_port } => {
587                let mut debug_struct = f.debug_struct("Tcp");
588
589                // Omit empty fields.
590                if let Some(port) = &src_port {
591                    let _ = debug_struct.field("src_port", port);
592                }
593
594                if let Some(port) = &dst_port {
595                    let _ = debug_struct.field("dst_port", port);
596                }
597
598                debug_struct.finish()
599            }
600            TransportProtocol::Udp { src_port, dst_port } => {
601                let mut debug_struct = f.debug_struct("Udp");
602
603                // Omit empty fields.
604                if let Some(port) = &src_port {
605                    let _ = debug_struct.field("src_port", port);
606                }
607
608                if let Some(port) = &dst_port {
609                    let _ = debug_struct.field("dst_port", port);
610                }
611
612                debug_struct.finish()
613            }
614            TransportProtocol::Icmp => f.write_str("Icmp"),
615            TransportProtocol::Icmpv6 => f.write_str("Icmpv6"),
616        }
617    }
618}
619
620/// An extension type for [`fnet_matchers::Mark`]
621#[derive(Debug, Clone, PartialEq, Eq, Hash)]
622pub enum Mark {
623    Unmarked,
624    Marked { mask: u32, between: RangeInclusive<u32>, invert: bool },
625}
626
627#[derive(Debug, Clone, PartialEq, Error)]
628pub enum MarkError {
629    #[error("mark union is of an unknown variant")]
630    UnknownUnionVariant(u64),
631}
632
633impl TryFrom<fnet_matchers::Mark> for Mark {
634    type Error = MarkError;
635
636    fn try_from(matcher: fnet_matchers::Mark) -> Result<Self, Self::Error> {
637        match matcher {
638            fnet_matchers::Mark::Unmarked(fnet_matchers::Unmarked) => Ok(Mark::Unmarked),
639            fnet_matchers::Mark::Marked(fnet_matchers::Marked {
640                mask,
641                between: fnet_matchers::Between { start, end },
642                invert,
643            }) => Ok(Mark::Marked { mask, between: RangeInclusive::new(start, end), invert }),
644            fnet_matchers::Mark::__SourceBreaking { unknown_ordinal } => {
645                Err(MarkError::UnknownUnionVariant(unknown_ordinal))
646            }
647        }
648    }
649}
650
651impl From<Mark> for fnet_matchers::Mark {
652    fn from(matcher: Mark) -> Self {
653        match matcher {
654            Mark::Unmarked => fnet_matchers::Mark::Unmarked(fnet_matchers::Unmarked),
655            Mark::Marked { mask, between, invert } => {
656                let (start, end) = between.into_inner();
657                fnet_matchers::Mark::Marked(fnet_matchers::Marked {
658                    mask,
659                    between: fnet_matchers::Between { start, end },
660                    invert,
661                })
662            }
663        }
664    }
665}
666
667/// An extension type for [`fnet_matchers::MarkInDomain`].
668#[derive(Debug, Clone, PartialEq, Eq, Hash)]
669pub struct MarkInDomain {
670    pub domain: fnet::MarkDomain,
671    pub mark: Mark,
672}
673
674#[derive(Debug, Clone, PartialEq, Error)]
675pub enum MarkInDomainError {
676    #[error("mark conversion failed: {0}")]
677    Mark(MarkError),
678}
679
680impl TryFrom<fnet_matchers::MarkInDomain> for MarkInDomain {
681    type Error = MarkInDomainError;
682
683    fn try_from(matcher: fnet_matchers::MarkInDomain) -> Result<Self, Self::Error> {
684        let fnet_matchers::MarkInDomain { domain, mark } = matcher;
685        Ok(Self { domain, mark: mark.try_into().map_err(MarkInDomainError::Mark)? })
686    }
687}
688
689impl From<MarkInDomain> for fnet_matchers::MarkInDomain {
690    fn from(matcher: MarkInDomain) -> Self {
691        let MarkInDomain { domain, mark } = matcher;
692        Self { domain, mark: mark.into() }
693    }
694}
695
696/// An extension type for [`fnet_matchers::TcpSocket`]
697#[derive(Debug, Clone, PartialEq, Eq)]
698pub enum TcpSocket {
699    Empty,
700    SrcPort(BoundPort),
701    DstPort(BoundPort),
702    States(fnet_matchers::TcpState),
703}
704
705#[derive(Debug, PartialEq, Eq, Error)]
706pub enum TcpSocketError {
707    #[error("bound port matcher conversion failed: {0}")]
708    BoundPort(BoundPortError),
709    #[error("tcp union is of an unknown variant")]
710    UnknownUnionVariant(u64),
711}
712
713impl TryFrom<fnet_matchers::TcpSocket> for TcpSocket {
714    type Error = TcpSocketError;
715
716    fn try_from(matcher: fnet_matchers::TcpSocket) -> Result<Self, Self::Error> {
717        match matcher {
718            fnet_matchers::TcpSocket::Empty(fnet_matchers::Empty) => Ok(Self::Empty),
719            fnet_matchers::TcpSocket::SrcPort(port) => {
720                Ok(Self::SrcPort(port.try_into().map_err(|e| TcpSocketError::BoundPort(e))?))
721            }
722            fnet_matchers::TcpSocket::DstPort(port) => {
723                Ok(Self::DstPort(port.try_into().map_err(|e| TcpSocketError::BoundPort(e))?))
724            }
725            fnet_matchers::TcpSocket::States(states) => Ok(Self::States(states)),
726            fnet_matchers::TcpSocket::__SourceBreaking { unknown_ordinal } => {
727                Err(TcpSocketError::UnknownUnionVariant(unknown_ordinal))
728            }
729        }
730    }
731}
732
733impl From<TcpSocket> for fnet_matchers::TcpSocket {
734    fn from(matcher: TcpSocket) -> Self {
735        match matcher {
736            TcpSocket::Empty => Self::Empty(fnet_matchers::Empty),
737            TcpSocket::SrcPort(port) => Self::SrcPort(port.into()),
738            TcpSocket::DstPort(port) => Self::DstPort(port.into()),
739            TcpSocket::States(states) => Self::States(states),
740        }
741    }
742}
743
744/// An extension type for [`fnet_matchers::UdpSocket`]
745#[derive(Debug, Clone, PartialEq, Eq)]
746pub enum UdpSocket {
747    Empty,
748    SrcPort(BoundPort),
749    DstPort(BoundPort),
750    States(fnet_matchers::UdpState),
751}
752
753#[derive(Debug, PartialEq, Eq, Error)]
754pub enum UdpSocketError {
755    #[error("bound port matcher conversion failed: {0}")]
756    BoundPort(BoundPortError),
757    #[error("udp union is of an unknown variant")]
758    UnknownUnionVariant(u64),
759}
760
761impl TryFrom<fnet_matchers::UdpSocket> for UdpSocket {
762    type Error = UdpSocketError;
763
764    fn try_from(matcher: fnet_matchers::UdpSocket) -> Result<Self, Self::Error> {
765        match matcher {
766            fnet_matchers::UdpSocket::Empty(fnet_matchers::Empty) => Ok(Self::Empty),
767            fnet_matchers::UdpSocket::SrcPort(port) => {
768                Ok(Self::SrcPort(port.try_into().map_err(|e| UdpSocketError::BoundPort(e))?))
769            }
770            fnet_matchers::UdpSocket::DstPort(port) => {
771                Ok(Self::DstPort(port.try_into().map_err(|e| UdpSocketError::BoundPort(e))?))
772            }
773            fnet_matchers::UdpSocket::States(states) => Ok(Self::States(states)),
774            fnet_matchers::UdpSocket::__SourceBreaking { unknown_ordinal } => {
775                Err(UdpSocketError::UnknownUnionVariant(unknown_ordinal))
776            }
777        }
778    }
779}
780
781impl From<UdpSocket> for fnet_matchers::UdpSocket {
782    fn from(matcher: UdpSocket) -> Self {
783        match matcher {
784            UdpSocket::Empty => Self::Empty(fnet_matchers::Empty),
785            UdpSocket::SrcPort(port) => Self::SrcPort(port.into()),
786            UdpSocket::DstPort(port) => Self::DstPort(port.into()),
787            UdpSocket::States(states) => Self::States(states),
788        }
789    }
790}
791
792/// An extension type for [`fnet_matchers::SocketTransportProtocol`].
793#[derive(Debug, Clone, PartialEq, Eq)]
794pub enum SocketTransportProtocol {
795    Tcp(TcpSocket),
796    Udp(UdpSocket),
797}
798
799#[derive(Debug, PartialEq, Eq, Error)]
800pub enum SocketTransportProtocolError {
801    #[error("invalid tcp matcher: {0}")]
802    Tcp(TcpSocketError),
803    #[error("invalid udp matcher: {0}")]
804    Udp(UdpSocketError),
805    #[error("socket transport protocol union is of an unknown variant")]
806    UnknownUnionVariant(u64),
807}
808
809impl TryFrom<fnet_matchers::SocketTransportProtocol> for SocketTransportProtocol {
810    type Error = SocketTransportProtocolError;
811
812    fn try_from(matcher: fnet_matchers::SocketTransportProtocol) -> Result<Self, Self::Error> {
813        match matcher {
814            fnet_matchers::SocketTransportProtocol::Tcp(tcp) => {
815                Ok(Self::Tcp(tcp.try_into().map_err(|e| SocketTransportProtocolError::Tcp(e))?))
816            }
817            fnet_matchers::SocketTransportProtocol::Udp(udp) => {
818                Ok(Self::Udp(udp.try_into().map_err(|e| SocketTransportProtocolError::Udp(e))?))
819            }
820            fnet_matchers::SocketTransportProtocol::__SourceBreaking { unknown_ordinal } => {
821                Err(SocketTransportProtocolError::UnknownUnionVariant(unknown_ordinal))
822            }
823        }
824    }
825}
826
827impl From<SocketTransportProtocol> for fnet_matchers::SocketTransportProtocol {
828    fn from(matcher: SocketTransportProtocol) -> Self {
829        match matcher {
830            SocketTransportProtocol::Tcp(tcp) => Self::Tcp(tcp.into()),
831            SocketTransportProtocol::Udp(udp) => Self::Udp(udp.into()),
832        }
833    }
834}
835
836#[cfg(test)]
837mod tests {
838    use net_declare::{fidl_ip, fidl_ip_v4, fidl_ip_v6, fidl_subnet, net_subnet_v4, net_subnet_v6};
839    use test_case::test_case;
840
841    use super::*;
842
843    #[test_case(
844        fnet_matchers::Interface::Id(1),
845        Interface::Id(NonZeroU64::new(1).unwrap());
846        "Interface"
847    )]
848    #[test_case(
849        fnet_matchers::BoundInterface::Unbound(fnet_matchers::Empty),
850        BoundInterface::Unbound;
851        "BoundInterface Unbound"
852    )]
853    #[test_case(
854        fnet_matchers::BoundInterface::Bound(fnet_matchers::Interface::Id(1)),
855        BoundInterface::Bound(Interface::Id(NonZeroU64::new(1).unwrap()));
856        "BoundInterface Bound"
857    )]
858    #[test_case(
859        fnet_matchers::Mark::Unmarked(fnet_matchers::Unmarked),
860        Mark::Unmarked;
861        "Unmarked"
862    )]
863    #[test_case(
864        fnet_matchers::Mark::Marked(fnet_matchers::Marked {
865            mask: 0xFF,
866            between: fnet_matchers::Between { start: 10, end: 20 },
867            invert: true,
868        }),
869        Mark::Marked { mask: 0xFF, between: 10..=20, invert: true };
870        "Marked"
871    )]
872    #[test_case(
873        fnet_matchers::MarkInDomain {
874            domain: fnet::MarkDomain::Mark1,
875            mark: fnet_matchers::Mark::Unmarked(fnet_matchers::Unmarked),
876        },
877        MarkInDomain {
878            domain: fnet::MarkDomain::Mark1,
879            mark: Mark::Unmarked,
880        };
881        "MarkInDomain"
882    )]
883    #[test_case(
884        fnet_matchers::AddressMatcherType::Subnet(fidl_subnet!("192.0.2.0/24")),
885        AddressMatcherType::Subnet(Subnet(net_subnet_v4!("192.0.2.0/24").into()));
886        "AddressMatcherTypeV4"
887    )]
888    #[test_case(
889        fnet_matchers::AddressMatcherType::Subnet(fidl_subnet!("2001:db8::/64")),
890        AddressMatcherType::Subnet(Subnet(net_subnet_v6!("2001:db8::/64").into()));
891        "AddressMatcherTypeV6"
892    )]
893    #[test_case(
894        fnet_matchers::Address {
895            matcher: fnet_matchers::AddressMatcherType::Subnet(fidl_subnet!("2001:db8::/64")),
896            invert: true,
897        },
898        Address {
899            matcher: AddressMatcherType::Subnet(Subnet(net_subnet_v6!("2001:db8::/64").into())),
900            invert: true,
901        };
902        "AddressV6"
903    )]
904    #[test_case(
905        fnet_matchers::AddressRange {
906            start: fidl_ip!("192.0.2.0"),
907            end: fidl_ip!("192.0.2.1"),
908        },
909        AddressRange::V4(
910            fidl_ip_v4!("192.0.2.0")..=fidl_ip_v4!("192.0.2.1"),
911        );
912        "AddressRangeV4"
913    )]
914    #[test_case(
915        fnet_matchers::AddressRange {
916            start: fidl_ip!("2001:db8::0"),
917            end: fidl_ip!("2001:db8::8"),
918        },
919        AddressRange::V6(
920            fidl_ip_v6!("2001:db8::0")..=fidl_ip_v6!("2001:db8::8"),
921        );
922        "AddressRangeV6"
923    )]
924    #[test_case(
925        fnet_matchers::PacketTransportProtocol::Udp(fnet_matchers::UdpPacket {
926            src_port: Some(fnet_matchers::Port { start: 1024, end: u16::MAX, invert: false }),
927            dst_port: None,
928            ..Default::default()
929        }),
930        TransportProtocol::Udp {
931            src_port: Some(Port { range: 1024..=u16::MAX, invert: false }),
932            dst_port: None,
933        };
934        "TransportProtocol"
935    )]
936    #[test_case(
937        fnet_matchers::TcpSocket::Empty(fnet_matchers::Empty),
938        TcpSocket::Empty;
939        "TcpSocketEmpty"
940    )]
941    #[test_case(
942        fnet_matchers::TcpSocket::SrcPort(
943            fnet_matchers::BoundPort::Bound(fnet_matchers::Port { start: 1024, end: u16::MAX, invert: false })
944        ),
945        TcpSocket::SrcPort(BoundPort::Bound(Port { range: 1024..=u16::MAX, invert: false }));
946        "TcpSocketSrcPort"
947    )]
948    #[test_case(
949        fnet_matchers::TcpSocket::DstPort(
950            fnet_matchers::BoundPort::Bound(fnet_matchers::Port { start: 80, end: 80, invert: true })
951        ),
952        TcpSocket::DstPort(BoundPort::Bound(Port { range: 80..=80, invert: true }));
953        "TcpSocketDstPort"
954    )]
955    #[test_case(
956        fnet_matchers::TcpSocket::States(fnet_matchers::TcpState::ESTABLISHED),
957        TcpSocket::States(fnet_matchers::TcpState::ESTABLISHED);
958        "TcpSocketStates"
959    )]
960    #[test_case(
961        fnet_matchers::UdpSocket::Empty(fnet_matchers::Empty),
962        UdpSocket::Empty;
963        "UdpSocketEmpty"
964    )]
965    #[test_case(
966        fnet_matchers::UdpSocket::SrcPort(
967            fnet_matchers::BoundPort::Bound(fnet_matchers::Port { start: 1024, end: u16::MAX, invert: false })
968        ),
969        UdpSocket::SrcPort(BoundPort::Bound(Port { range: 1024..=u16::MAX, invert: false }));
970        "UdpSocketSrcPort"
971    )]
972    #[test_case(
973        fnet_matchers::UdpSocket::DstPort(
974            fnet_matchers::BoundPort::Bound(fnet_matchers::Port { start: 53, end: 53, invert: true })
975        ),
976        UdpSocket::DstPort(BoundPort::Bound(Port { range: 53..=53, invert: true }));
977        "UdpSocketDstPort"
978    )]
979    #[test_case(
980        fnet_matchers::UdpSocket::States(fnet_matchers::UdpState::BOUND),
981        UdpSocket::States(fnet_matchers::UdpState::BOUND);
982        "UdpSocketStates"
983    )]
984    #[test_case(
985        fnet_matchers::SocketTransportProtocol::Tcp(
986            fnet_matchers::TcpSocket::SrcPort(
987                fnet_matchers::BoundPort::Bound(fnet_matchers::Port { start: 123, end: 123, invert: false })
988            )
989        ),
990        SocketTransportProtocol::Tcp(TcpSocket::SrcPort(BoundPort::Bound(Port { range: 123..=123, invert: false })));
991        "SocketTransportProtocolTcp"
992    )]
993    #[test_case(
994        fnet_matchers::SocketTransportProtocol::Udp(
995            fnet_matchers::UdpSocket::SrcPort(
996                fnet_matchers::BoundPort::Bound(fnet_matchers::Port { start: 123, end: 123, invert: false })
997            )
998        ),
999        SocketTransportProtocol::Udp(UdpSocket::SrcPort(BoundPort::Bound(Port { range: 123..=123, invert: false })));
1000        "SocketTransportProtocolUdp"
1001    )]
1002    #[test_case(
1003        fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty),
1004        BoundPort::Unbound;
1005        "BoundPort Unbound"
1006    )]
1007    #[test_case(
1008        fnet_matchers::BoundAddress::Unbound(fnet_matchers::Empty),
1009        BoundAddress::Unbound;
1010        "BoundAddress Unbound"
1011    )]
1012    #[test_case(
1013        fnet_matchers::TcpSocket::SrcPort(fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty)),
1014        TcpSocket::SrcPort(BoundPort::Unbound);
1015        "TcpSocket SrcPort Unbound"
1016    )]
1017    #[test_case(
1018        fnet_matchers::TcpSocket::DstPort(fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty)),
1019        TcpSocket::DstPort(BoundPort::Unbound);
1020        "TcpSocket DstPort Unbound"
1021    )]
1022    #[test_case(
1023        fnet_matchers::UdpSocket::SrcPort(fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty)),
1024        UdpSocket::SrcPort(BoundPort::Unbound);
1025        "UdpSocket SrcPort Unbound"
1026    )]
1027    #[test_case(
1028        fnet_matchers::UdpSocket::DstPort(fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty)),
1029        UdpSocket::DstPort(BoundPort::Unbound);
1030        "UdpSocket DstPort Unbound"
1031    )]
1032    fn convert_from_fidl_and_back<F, E>(fidl_type: F, local_type: E)
1033    where
1034        E: TryFrom<F> + Clone + Debug + PartialEq,
1035        <E as TryFrom<F>>::Error: Debug + PartialEq,
1036        F: From<E> + Clone + Debug + PartialEq,
1037    {
1038        assert_eq!(fidl_type.clone().try_into(), Ok(local_type.clone()));
1039        assert_eq!(<_ as Into<F>>::into(local_type), fidl_type.clone());
1040    }
1041
1042    #[test_case(
1043        fnet_matchers::BoundInterface::__SourceBreaking { unknown_ordinal: 0 } =>
1044            Err(BoundInterfaceError::UnknownUnionVariant(0));
1045        "UnknownUnionVariant"
1046    )]
1047    #[test_case(
1048        fnet_matchers::BoundInterface::Bound(fnet_matchers::Interface::Id(0)) =>
1049            Err(BoundInterfaceError::Interface(InterfaceError::ZeroId));
1050        "InterfaceError"
1051    )]
1052    fn bound_interface_try_from_error(
1053        fidl: fnet_matchers::BoundInterface,
1054    ) -> Result<BoundInterface, BoundInterfaceError> {
1055        BoundInterface::try_from(fidl)
1056    }
1057
1058    #[test_case(
1059        fnet_matchers::Mark::__SourceBreaking { unknown_ordinal: 0 } =>
1060            Err(MarkError::UnknownUnionVariant(0));
1061        "UnknownUnionVariant"
1062    )]
1063    fn mark_try_from_error(fidl: fnet_matchers::Mark) -> Result<Mark, MarkError> {
1064        Mark::try_from(fidl)
1065    }
1066
1067    #[test_case(
1068        fnet_matchers::MarkInDomain {
1069            domain: fnet::MarkDomain::Mark1,
1070            mark: fnet_matchers::Mark::__SourceBreaking { unknown_ordinal: 0 },
1071        } => Err(MarkInDomainError::Mark(MarkError::UnknownUnionVariant(0)));
1072        "MarkInDomain Mark Error"
1073    )]
1074    fn mark_and_domain_try_from_error(
1075        fidl: fnet_matchers::MarkInDomain,
1076    ) -> Result<MarkInDomain, MarkInDomainError> {
1077        MarkInDomain::try_from(fidl)
1078    }
1079
1080    #[test]
1081    fn address_matcher_type_try_from_unknown_variant() {
1082        assert_eq!(
1083            AddressMatcherType::try_from(fnet_matchers::AddressMatcherType::__SourceBreaking {
1084                unknown_ordinal: 0
1085            }),
1086            Err(AddressMatcherTypeError::UnknownUnionVariant)
1087        );
1088    }
1089
1090    #[test]
1091    fn subnet_try_from_invalid() {
1092        assert_eq!(
1093            Subnet::try_from(fnet::Subnet { addr: fidl_ip!("192.0.2.1"), prefix_len: 33 }),
1094            Err(SubnetError::PrefixTooLong)
1095        );
1096        assert_eq!(Subnet::try_from(fidl_subnet!("192.0.2.1/24")), Err(SubnetError::HostBitsSet));
1097    }
1098
1099    #[test]
1100    fn address_range_try_from_invalid() {
1101        assert_eq!(
1102            AddressRange::try_from(fnet_matchers::AddressRange {
1103                start: fidl_ip!("192.0.2.1"),
1104                end: fidl_ip!("192.0.2.0"),
1105            }),
1106            Err(AddressRangeError::Invalid)
1107        );
1108        assert_eq!(
1109            AddressRange::try_from(fnet_matchers::AddressRange {
1110                start: fidl_ip!("2001:db8::1"),
1111                end: fidl_ip!("2001:db8::"),
1112            }),
1113            Err(AddressRangeError::Invalid)
1114        );
1115    }
1116
1117    #[test]
1118    fn address_range_try_from_family_mismatch() {
1119        assert_eq!(
1120            AddressRange::try_from(fnet_matchers::AddressRange {
1121                start: fidl_ip!("192.0.2.0"),
1122                end: fidl_ip!("2001:db8::"),
1123            }),
1124            Err(AddressRangeError::FamilyMismatch)
1125        );
1126    }
1127
1128    #[test]
1129    fn port_matcher_try_from_invalid() {
1130        assert_eq!(
1131            Port::try_from(fnet_matchers::Port { start: 1, end: 0, invert: false }),
1132            Err(PortError::InvalidPortRange)
1133        );
1134    }
1135
1136    #[test]
1137    fn transport_protocol_try_from_unknown_variant() {
1138        assert_eq!(
1139            TransportProtocol::try_from(fnet_matchers::PacketTransportProtocol::__SourceBreaking {
1140                unknown_ordinal: 0
1141            }),
1142            Err(TransportProtocolError::UnknownUnionVariant)
1143        );
1144    }
1145
1146    #[test_case(
1147        fnet_matchers::TcpSocket::__SourceBreaking { unknown_ordinal: 100 } =>
1148            Err(TcpSocketError::UnknownUnionVariant(100));
1149        "TcpSocket UnknownUnionVariant"
1150    )]
1151    #[test_case(
1152        fnet_matchers::TcpSocket::SrcPort(fnet_matchers::BoundPort::Bound(fnet_matchers::Port {
1153            start: 1,
1154            end: 0,
1155            invert: false,
1156        })) => Err(TcpSocketError::BoundPort(BoundPortError::Port(PortError::InvalidPortRange)));
1157        "TcpSocket SrcPort Error"
1158    )]
1159    #[test_case(
1160        fnet_matchers::TcpSocket::DstPort(fnet_matchers::BoundPort::Bound(fnet_matchers::Port {
1161            start: 1,
1162            end: 0,
1163            invert: false,
1164        })) => Err(TcpSocketError::BoundPort(BoundPortError::Port(PortError::InvalidPortRange)));
1165        "TcpSocket DstPort Error"
1166    )]
1167    fn tcp_socket_try_from_error(
1168        fidl: fnet_matchers::TcpSocket,
1169    ) -> Result<TcpSocket, TcpSocketError> {
1170        TcpSocket::try_from(fidl)
1171    }
1172
1173    #[test_case(
1174        fnet_matchers::UdpSocket::__SourceBreaking { unknown_ordinal: 100 } =>
1175            Err(UdpSocketError::UnknownUnionVariant(100));
1176        "UdpSocket UnknownUnionVariant"
1177    )]
1178    #[test_case(
1179        fnet_matchers::UdpSocket::SrcPort(fnet_matchers::BoundPort::Bound(fnet_matchers::Port {
1180            start: 1,
1181            end: 0,
1182            invert: false,
1183        })) => Err(UdpSocketError::BoundPort(BoundPortError::Port(PortError::InvalidPortRange)));
1184        "UdpSocket SrcPort Error"
1185    )]
1186    #[test_case(
1187        fnet_matchers::UdpSocket::DstPort(fnet_matchers::BoundPort::Bound(fnet_matchers::Port {
1188            start: 1,
1189            end: 0,
1190            invert: false,
1191        })) => Err(UdpSocketError::BoundPort(BoundPortError::Port(PortError::InvalidPortRange)));
1192        "UdpSocket DstPort Error"
1193    )]
1194    fn udp_socket_try_from_error(
1195        fidl: fnet_matchers::UdpSocket,
1196    ) -> Result<UdpSocket, UdpSocketError> {
1197        UdpSocket::try_from(fidl)
1198    }
1199
1200    #[test_case(
1201        fnet_matchers::SocketTransportProtocol::__SourceBreaking {
1202            unknown_ordinal: 100
1203        } => Err(SocketTransportProtocolError::UnknownUnionVariant(100));
1204        "SocketTransportProtocol UnknownUnionVariant"
1205    )]
1206    #[test_case(
1207        fnet_matchers::SocketTransportProtocol::Tcp(
1208            fnet_matchers::TcpSocket::__SourceBreaking { unknown_ordinal: 100 }
1209        ) => Err(SocketTransportProtocolError::Tcp(TcpSocketError::UnknownUnionVariant(100)));
1210        "SocketTransportProtocol Tcp Error"
1211    )]
1212    #[test_case(
1213        fnet_matchers::SocketTransportProtocol::Udp(
1214            fnet_matchers::UdpSocket::__SourceBreaking { unknown_ordinal: 100 }
1215        ) => Err(SocketTransportProtocolError::Udp(UdpSocketError::UnknownUnionVariant(100)));
1216        "SocketTransportProtocol Udp Error"
1217    )]
1218    fn socket_transport_protocol_try_from_error(
1219        fidl: fnet_matchers::SocketTransportProtocol,
1220    ) -> Result<SocketTransportProtocol, SocketTransportProtocolError> {
1221        SocketTransportProtocol::try_from(fidl)
1222    }
1223}