netstack3_base/socket/
address.rs

1// Copyright 2022 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//! A collection of types that represent the various parts of socket addresses.
6
7use core::fmt::{self, Debug, Display, Formatter};
8use core::marker::PhantomData;
9use core::num::NonZeroU16;
10use core::ops::Deref;
11
12use derivative::Derivative;
13use net_types::ip::{GenericOverIp, Ip, IpAddress, Ipv4, Ipv4Addr, Ipv6Addr, Ipv6SourceAddr};
14use net_types::{
15    MulticastAddr, NonMappedAddr, ScopeableAddress, SpecifiedAddr, UnicastAddr, Witness, ZonedAddr,
16};
17
18use crate::socket::base::{
19    AddrVec, DualStackIpExt, EitherStack, SocketIpAddrExt as _, SocketIpExt, SocketMapAddrSpec,
20};
21
22/// A [`ZonedAddr`] whose address is `Zoned` iff a zone is required.
23///
24/// Any address whose scope can have a zone, will be the `Zoned` variant. The
25/// one exception is the loopback address, which is represented as `Unzoned`.
26/// This is because the loopback address is allowed to have, but does not
27/// require having, a zone.
28///
29/// # Type Parameters
30///
31/// A: The base [`IpAddress`] type.
32/// W: The [`Witness`] types of the `A`.
33/// Z: The zone of `A`.
34#[derive(Copy, Clone, Eq, Hash, PartialEq)]
35pub struct StrictlyZonedAddr<A, W, Z> {
36    addr: ZonedAddr<W, Z>,
37    marker: PhantomData<A>,
38}
39
40impl<A: Debug, W: Debug, Z: Debug> Debug for StrictlyZonedAddr<A, W, Z> {
41    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
42        let StrictlyZonedAddr { addr, marker: PhantomData } = self;
43        write!(f, "{:?}", addr)
44    }
45}
46
47impl<A: IpAddress, W: Witness<A> + ScopeableAddress + Copy, Z> StrictlyZonedAddr<A, W, Z> {
48    /// Convert self into the inner [`ZonedAddr`]
49    pub fn into_inner(self) -> ZonedAddr<W, Z> {
50        let StrictlyZonedAddr { addr, marker: PhantomData } = self;
51        addr
52    }
53
54    /// Convert self into the inner [`ZonedAddr`] while discarding the witness.
55    pub fn into_inner_without_witness(self) -> ZonedAddr<A, Z> {
56        self.into_inner().map_addr(|addr| addr.into_addr())
57    }
58
59    /// Creates from a specified IP address and an optional zone.
60    ///
61    /// If `addr` requires a zone, then `get_zone` will be called to provide
62    /// the zone.
63    ///
64    /// # Panics
65    /// This method panics if the `addr` wants a zone and `get_zone` will panic
66    /// when called.
67    pub fn new_with_zone(addr: W, get_zone: impl FnOnce() -> Z) -> Self {
68        if let Some(addr_and_zone) = addr.try_into_null_zoned() {
69            StrictlyZonedAddr {
70                addr: ZonedAddr::Zoned(addr_and_zone.map_zone(move |()| get_zone())),
71                marker: PhantomData,
72            }
73        } else {
74            StrictlyZonedAddr { addr: ZonedAddr::Unzoned(addr), marker: PhantomData }
75        }
76    }
77
78    #[cfg(feature = "testutils")]
79    /// Creates the unzoned variant, or panics if the addr's scope needs a zone.
80    pub fn new_unzoned_or_panic(addr: W) -> Self {
81        Self::new_with_zone(addr, || panic!("addr unexpectedly required a zone."))
82    }
83}
84
85impl<A: IpAddress, W: Witness<A>, Z> Deref for StrictlyZonedAddr<A, W, Z> {
86    type Target = ZonedAddr<W, Z>;
87    fn deref(&self) -> &Self::Target {
88        let StrictlyZonedAddr { addr, marker: PhantomData } = self;
89        addr
90    }
91}
92
93/// An IP address that witnesses all required properties of a socket address.
94///
95/// Requires `SpecifiedAddr` because most contexts do not permit unspecified
96/// addresses; those that do can hold a `Option<SocketIpAddr>`.
97///
98/// Requires `NonMappedAddr` because mapped addresses (i.e. ipv4-mapped-ipv6
99/// addresses) are converted from their original IP version to their target IP
100/// version when entering the stack.
101#[derive(Copy, Clone, Eq, GenericOverIp, Hash, PartialEq)]
102#[generic_over_ip(A, IpAddress)]
103pub struct SocketIpAddr<A: IpAddress>(NonMappedAddr<SpecifiedAddr<A>>);
104
105impl<A: IpAddress> Display for SocketIpAddr<A> {
106    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
107        let Self(addr) = self;
108        write!(f, "{}", addr)
109    }
110}
111
112impl<A: IpAddress> Debug for SocketIpAddr<A> {
113    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
114        let Self(addr) = self;
115        write!(f, "{:?}", addr)
116    }
117}
118
119impl<A: IpAddress> SocketIpAddr<A> {
120    /// Constructs a [`SocketIpAddr`] if the address is compliant, else `None`.
121    pub fn new(addr: A) -> Option<SocketIpAddr<A>> {
122        Some(SocketIpAddr(NonMappedAddr::new(SpecifiedAddr::new(addr)?)?))
123    }
124
125    /// Constructs a [`SocketIpAddr`] from the inner witness.
126    pub fn new_from_witness(addr: NonMappedAddr<SpecifiedAddr<A>>) -> Self {
127        Self(addr)
128    }
129
130    /// Constructs a [`SocketIpAddr`] without verify the address's properties.
131    ///
132    ///
133    /// # Safety
134    ///
135    /// Callers must ensure that the addr is both a [`SpecifiedAddr`] and
136    /// a [`NonMappedAddr`].
137    pub const unsafe fn new_unchecked(addr: A) -> SocketIpAddr<A> {
138        // SAFETY: delegated to caller.
139        let non_mapped =
140            unsafe { NonMappedAddr::new_unchecked(SpecifiedAddr::new_unchecked(addr)) };
141        SocketIpAddr(non_mapped)
142    }
143
144    /// Like [`SocketIpAddr::new_unchecked`], but the address is specified.
145    ///
146    /// # Safety
147    ///
148    /// Callers must ensure that the addr is a [`NonMappedAddr`].
149    pub const unsafe fn new_from_specified_unchecked(addr: SpecifiedAddr<A>) -> SocketIpAddr<A> {
150        // SAFETY: delegated to caller.
151        let non_mapped = unsafe { NonMappedAddr::new_unchecked(addr) };
152        SocketIpAddr(non_mapped)
153    }
154
155    /// Returns the inner address, dropping all witnesses.
156    pub fn addr(self) -> A {
157        let SocketIpAddr(addr) = self;
158        **addr
159    }
160
161    /// Returns the inner address, including all witness types.
162    pub fn into_inner(self) -> NonMappedAddr<SpecifiedAddr<A>> {
163        let SocketIpAddr(addr) = self;
164        addr
165    }
166
167    /// Constructs a [`SocketIpAddr`] from the given multicast address.
168    pub fn new_from_multicast(addr: MulticastAddr<A>) -> SocketIpAddr<A> {
169        let addr: MulticastAddr<NonMappedAddr<_>> = addr.non_mapped().transpose();
170        let addr: NonMappedAddr<SpecifiedAddr<_>> = addr.into_specified().transpose();
171        SocketIpAddr(addr)
172    }
173}
174
175impl SocketIpAddr<Ipv4Addr> {
176    /// Constructs a [`SocketIpAddr`] from a given specified IPv4 address.
177    pub fn new_ipv4_specified(addr: SpecifiedAddr<Ipv4Addr>) -> Self {
178        addr.try_into().unwrap_or_else(|AddrIsMappedError {}| {
179            unreachable!("IPv4 addresses must be non-mapped")
180        })
181    }
182}
183
184impl SocketIpAddr<Ipv6Addr> {
185    /// Constructs a [`SocketIpAddr`] from the given [`Ipv6DeviceAddr`].
186    pub fn new_from_ipv6_non_mapped_unicast(addr: NonMappedAddr<UnicastAddr<Ipv6Addr>>) -> Self {
187        let addr: UnicastAddr<NonMappedAddr<_>> = addr.transpose();
188        let addr: NonMappedAddr<SpecifiedAddr<_>> = addr.into_specified().transpose();
189        SocketIpAddr(addr)
190    }
191
192    /// Optionally constructs a [`SocketIpAddr`] from the given
193    /// [`Ipv6SourceAddr`], returning `None` if the given addr is `Unspecified`.
194    pub fn new_from_ipv6_source(addr: Ipv6SourceAddr) -> Option<Self> {
195        match addr {
196            Ipv6SourceAddr::Unspecified => None,
197            Ipv6SourceAddr::Unicast(addr) => {
198                Some(SocketIpAddr::new_from_ipv6_non_mapped_unicast(addr))
199            }
200        }
201    }
202}
203
204impl<A: IpAddress> From<SocketIpAddr<A>> for SpecifiedAddr<A> {
205    fn from(addr: SocketIpAddr<A>) -> Self {
206        let SocketIpAddr(addr) = addr;
207        *addr
208    }
209}
210
211impl<A: IpAddress> AsRef<SpecifiedAddr<A>> for SocketIpAddr<A> {
212    fn as_ref(&self) -> &SpecifiedAddr<A> {
213        let SocketIpAddr(addr) = self;
214        addr.as_ref()
215    }
216}
217
218/// The addr could not be converted to a `NonMappedAddr`.
219///
220/// Perhaps the address was an ipv4-mapped-ipv6 addresses.
221#[derive(Debug)]
222pub struct AddrIsMappedError {}
223
224impl<A: IpAddress> TryFrom<SpecifiedAddr<A>> for SocketIpAddr<A> {
225    type Error = AddrIsMappedError;
226    fn try_from(addr: SpecifiedAddr<A>) -> Result<Self, Self::Error> {
227        NonMappedAddr::new(addr).map(SocketIpAddr).ok_or(AddrIsMappedError {})
228    }
229}
230
231/// Allows [`SocketIpAddr`] to be used inside of a [`ZonedAddr`].
232impl<A: IpAddress> ScopeableAddress for SocketIpAddr<A> {
233    type Scope = A::Scope;
234    fn scope(&self) -> Self::Scope {
235        let SocketIpAddr(addr) = self;
236        addr.scope()
237    }
238}
239
240/// The IP address and identifier (port) of a listening socket.
241#[derive(Copy, Clone, Debug, Eq, GenericOverIp, Hash, PartialEq)]
242#[generic_over_ip(A, IpAddress)]
243pub struct ListenerIpAddr<A: IpAddress, LI> {
244    /// The specific address being listened on, or `None` for all addresses.
245    pub addr: Option<SocketIpAddr<A>>,
246    /// The local identifier (i.e. port for TCP/UDP).
247    pub identifier: LI,
248}
249
250impl<A: IpAddress, LI: Into<NonZeroU16>> Into<(Option<SpecifiedAddr<A>>, NonZeroU16)>
251    for ListenerIpAddr<A, LI>
252{
253    fn into(self) -> (Option<SpecifiedAddr<A>>, NonZeroU16) {
254        let Self { addr, identifier } = self;
255        (addr.map(Into::into), identifier.into())
256    }
257}
258
259/// The address of a listening socket.
260#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, GenericOverIp)]
261#[generic_over_ip(A, GenericOverIp)]
262pub struct ListenerAddr<A, D> {
263    /// The IP address of the listening socket.
264    pub ip: A,
265    /// The bound device of the listening socket.
266    pub device: Option<D>,
267}
268
269impl<A, D> AsRef<Option<D>> for ListenerAddr<A, D> {
270    fn as_ref(&self) -> &Option<D> {
271        &self.device
272    }
273}
274
275impl<TA, OA, D> AsRef<Option<D>> for EitherStack<ListenerAddr<TA, D>, ListenerAddr<OA, D>> {
276    fn as_ref(&self) -> &Option<D> {
277        match self {
278            EitherStack::ThisStack(l) => &l.device,
279            EitherStack::OtherStack(l) => &l.device,
280        }
281    }
282}
283
284/// The IP addresses and identifiers (ports) of a connected socket.
285#[derive(Copy, Clone, Debug, Eq, GenericOverIp, Hash, PartialEq)]
286#[generic_over_ip(A, IpAddress)]
287pub struct ConnIpAddrInner<A, LI, RI> {
288    /// The local address, port tuple.
289    pub local: (A, LI),
290    /// The remote address, port tuple.
291    pub remote: (A, RI),
292}
293
294/// The IP addresses and identifiers (ports) of a connected socket.
295pub type ConnIpAddr<A, LI, RI> = ConnIpAddrInner<SocketIpAddr<A>, LI, RI>;
296/// The IP addresses (mapped if dual-stack) and identifiers (ports) of a connected socket.
297pub type ConnInfoAddr<A, RI> = ConnIpAddrInner<SpecifiedAddr<A>, NonZeroU16, RI>;
298
299impl<A: IpAddress, LI: Into<NonZeroU16>, RI> From<ConnIpAddr<A, LI, RI>> for ConnInfoAddr<A, RI> {
300    fn from(
301        ConnIpAddr { local: (local_ip, local_identifier), remote: (remote_ip, remote_identifier) }: ConnIpAddr<A, LI, RI>,
302    ) -> Self {
303        Self {
304            local: (local_ip.into(), local_identifier.into()),
305            remote: (remote_ip.into(), remote_identifier),
306        }
307    }
308}
309
310/// The address of a connected socket.
311#[derive(Copy, Clone, Debug, Eq, GenericOverIp, Hash, PartialEq)]
312#[generic_over_ip()]
313pub struct ConnAddr<A, D> {
314    /// The IP address for the connected socket.
315    pub ip: A,
316    /// The bound device for the connected socket.
317    pub device: Option<D>,
318}
319
320/// The IP address and identifier (port) of a dual-stack listening socket.
321#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
322pub enum DualStackListenerIpAddr<A: IpAddress, LI: Into<NonZeroU16>>
323where
324    A::Version: DualStackIpExt,
325{
326    /// The socket is bound to this stack.
327    ThisStack(ListenerIpAddr<A, LI>),
328    /// The socket is bound to the other stack.
329    OtherStack(ListenerIpAddr<<<A::Version as DualStackIpExt>::OtherVersion as Ip>::Addr, LI>),
330    /// The socket is dual-stack enabled and bound to the IPv6 any address.
331    BothStacks(LI),
332}
333
334impl<A: IpAddress, NewIp: DualStackIpExt, LI: Into<NonZeroU16>> GenericOverIp<NewIp>
335    for DualStackListenerIpAddr<A, LI>
336where
337    A::Version: DualStackIpExt,
338{
339    type Type = DualStackListenerIpAddr<NewIp::Addr, LI>;
340}
341
342impl<LI: Into<NonZeroU16>> Into<(Option<SpecifiedAddr<Ipv6Addr>>, NonZeroU16)>
343    for DualStackListenerIpAddr<Ipv6Addr, LI>
344{
345    fn into(self) -> (Option<SpecifiedAddr<Ipv6Addr>>, NonZeroU16) {
346        match self {
347            Self::ThisStack(listener_ip_addr) => listener_ip_addr.into(),
348            Self::OtherStack(ListenerIpAddr { addr, identifier }) => (
349                Some(addr.map_or(Ipv4::UNSPECIFIED_ADDRESS, SocketIpAddr::addr).to_ipv6_mapped()),
350                identifier.into(),
351            ),
352            Self::BothStacks(identifier) => (None, identifier.into()),
353        }
354    }
355}
356
357/// The IP address and identifiers (ports) of a dual-stack connected socket.
358#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
359#[allow(missing_docs)]
360pub enum DualStackConnIpAddr<A: IpAddress, LI, RI>
361where
362    A::Version: DualStackIpExt,
363{
364    ThisStack(ConnIpAddr<A, LI, RI>),
365    OtherStack(ConnIpAddr<<<A::Version as DualStackIpExt>::OtherVersion as Ip>::Addr, LI, RI>),
366}
367
368impl<A: IpAddress, NewIp: DualStackIpExt, LI, RI> GenericOverIp<NewIp>
369    for DualStackConnIpAddr<A, LI, RI>
370where
371    A::Version: DualStackIpExt,
372{
373    type Type = DualStackConnIpAddr<NewIp::Addr, LI, RI>;
374}
375
376impl<LI: Into<NonZeroU16>, RI> From<DualStackConnIpAddr<Ipv6Addr, LI, RI>>
377    for ConnInfoAddr<Ipv6Addr, RI>
378{
379    fn from(addr: DualStackConnIpAddr<Ipv6Addr, LI, RI>) -> Self {
380        match addr {
381            DualStackConnIpAddr::ThisStack(conn_ip_addr) => conn_ip_addr.into(),
382            DualStackConnIpAddr::OtherStack(ConnIpAddr {
383                local: (local_ip, local_identifier),
384                remote: (remote_ip, remote_identifier),
385            }) => ConnInfoAddr {
386                local: (local_ip.addr().to_ipv6_mapped(), local_identifier.into()),
387                remote: (remote_ip.addr().to_ipv6_mapped(), remote_identifier),
388            },
389        }
390    }
391}
392
393impl<I: Ip, A: SocketMapAddrSpec> From<ListenerIpAddr<I::Addr, A::LocalIdentifier>>
394    for IpAddrVec<I, A>
395{
396    fn from(listener: ListenerIpAddr<I::Addr, A::LocalIdentifier>) -> Self {
397        IpAddrVec::Listener(listener)
398    }
399}
400
401impl<I: Ip, A: SocketMapAddrSpec> From<ConnIpAddr<I::Addr, A::LocalIdentifier, A::RemoteIdentifier>>
402    for IpAddrVec<I, A>
403{
404    fn from(conn: ConnIpAddr<I::Addr, A::LocalIdentifier, A::RemoteIdentifier>) -> Self {
405        IpAddrVec::Connected(conn)
406    }
407}
408
409impl<I: Ip, D, A: SocketMapAddrSpec>
410    From<ListenerAddr<ListenerIpAddr<I::Addr, A::LocalIdentifier>, D>> for AddrVec<I, D, A>
411{
412    fn from(listener: ListenerAddr<ListenerIpAddr<I::Addr, A::LocalIdentifier>, D>) -> Self {
413        AddrVec::Listen(listener)
414    }
415}
416
417impl<I: Ip, D, A: SocketMapAddrSpec>
418    From<ConnAddr<ConnIpAddr<I::Addr, A::LocalIdentifier, A::RemoteIdentifier>, D>>
419    for AddrVec<I, D, A>
420{
421    fn from(
422        conn: ConnAddr<ConnIpAddr<I::Addr, A::LocalIdentifier, A::RemoteIdentifier>, D>,
423    ) -> Self {
424        AddrVec::Conn(conn)
425    }
426}
427
428/// An address vector containing the portions of a socket address that are
429/// visible in an IP packet.
430#[derive(Derivative)]
431#[derivative(
432    Debug(bound = ""),
433    Clone(bound = ""),
434    Eq(bound = ""),
435    PartialEq(bound = ""),
436    Hash(bound = "")
437)]
438#[allow(missing_docs)]
439pub enum IpAddrVec<I: Ip, A: SocketMapAddrSpec> {
440    Listener(ListenerIpAddr<I::Addr, A::LocalIdentifier>),
441    Connected(ConnIpAddr<I::Addr, A::LocalIdentifier, A::RemoteIdentifier>),
442}
443
444impl<I: Ip, A: SocketMapAddrSpec> IpAddrVec<I, A> {
445    fn with_device<D>(self, device: Option<D>) -> AddrVec<I, D, A> {
446        match self {
447            IpAddrVec::Listener(ip) => AddrVec::Listen(ListenerAddr { ip, device }),
448            IpAddrVec::Connected(ip) => AddrVec::Conn(ConnAddr { ip, device }),
449        }
450    }
451}
452
453impl<I: Ip, A: SocketMapAddrSpec> IpAddrVec<I, A> {
454    /// Returns the next smallest address vector that would receive all the same
455    /// packets as this one.
456    ///
457    /// Address vectors are ordered by their shadowing relationship, such that
458    /// a "smaller" vector shadows a "larger" one. This function returns the
459    /// smallest of the set of shadows of `self`.
460    fn widen(self) -> Option<Self> {
461        match self {
462            IpAddrVec::Listener(ListenerIpAddr { addr: None, identifier }) => {
463                let _: A::LocalIdentifier = identifier;
464                None
465            }
466            IpAddrVec::Connected(ConnIpAddr { local: (local_ip, local_identifier), remote }) => {
467                let _: (SocketIpAddr<I::Addr>, A::RemoteIdentifier) = remote;
468                Some(ListenerIpAddr { addr: Some(local_ip), identifier: local_identifier })
469            }
470            IpAddrVec::Listener(ListenerIpAddr { addr: Some(addr), identifier }) => {
471                let _: SocketIpAddr<I::Addr> = addr;
472                Some(ListenerIpAddr { addr: None, identifier })
473            }
474        }
475        .map(IpAddrVec::Listener)
476    }
477}
478
479pub(crate) enum AddrVecIterInner<I: Ip, D, A: SocketMapAddrSpec> {
480    WithDevice { device: D, emitted_device: bool, addr: IpAddrVec<I, A> },
481    NoDevice { addr: IpAddrVec<I, A> },
482    Done,
483}
484
485impl<I: Ip, D: Clone, A: SocketMapAddrSpec> Iterator for AddrVecIterInner<I, D, A> {
486    type Item = AddrVec<I, D, A>;
487
488    fn next(&mut self) -> Option<Self::Item> {
489        match self {
490            Self::Done => None,
491            Self::WithDevice { device, emitted_device, addr } => {
492                if !*emitted_device {
493                    *emitted_device = true;
494                    Some(addr.clone().with_device(Some(device.clone())))
495                } else {
496                    let r = addr.clone().with_device(None);
497                    if let Some(next) = addr.clone().widen() {
498                        *addr = next;
499                        *emitted_device = false;
500                    } else {
501                        *self = Self::Done;
502                    }
503                    Some(r)
504                }
505            }
506            Self::NoDevice { addr } => {
507                let r = addr.clone().with_device(None);
508                if let Some(next) = addr.clone().widen() {
509                    *addr = next;
510                } else {
511                    *self = Self::Done
512                }
513                Some(r)
514            }
515        }
516    }
517}
518
519/// An iterator over socket addresses.
520///
521/// The generated address vectors are ordered according to the following
522/// rules (ordered by precedence):
523///   - a connected address is preferred over a listening address,
524///   - a listening address for a specific IP address is preferred over one
525///     for all addresses,
526///   - an address with a specific device is preferred over one for all
527///     devices.
528///
529/// The first yielded address is always the one provided via
530/// [`AddrVecIter::with_device`] or [`AddrVecIter::without_device`].
531pub struct AddrVecIter<I: Ip, D, A: SocketMapAddrSpec>(AddrVecIterInner<I, D, A>);
532
533impl<I: Ip, D, A: SocketMapAddrSpec> AddrVecIter<I, D, A> {
534    /// Constructs an [`AddrVecIter`] with `addr` and a specified `device`.
535    pub fn with_device(addr: IpAddrVec<I, A>, device: D) -> Self {
536        Self(AddrVecIterInner::WithDevice { device, emitted_device: false, addr })
537    }
538
539    /// Constructs an [`AddrVecIter`] with `addr` without a specified device.
540    pub fn without_device(addr: IpAddrVec<I, A>) -> Self {
541        Self(AddrVecIterInner::NoDevice { addr })
542    }
543}
544
545impl<I: Ip, D: Clone, A: SocketMapAddrSpec> Iterator for AddrVecIter<I, D, A> {
546    type Item = AddrVec<I, D, A>;
547
548    fn next(&mut self) -> Option<Self::Item> {
549        let Self(it) = self;
550        it.next()
551    }
552}
553
554#[derive(GenericOverIp)]
555#[generic_over_ip(I, Ip)]
556enum TryUnmapResult<I: DualStackIpExt, D> {
557    /// The address does not have an un-mapped representation.
558    ///
559    /// This spits back the input address unmodified.
560    CannotBeUnmapped(ZonedAddr<SocketIpAddr<I::Addr>, D>),
561    /// The address in the other stack that corresponds to the input.
562    ///
563    /// Since [`SocketIpAddr`] is guaranteed to hold a specified address,
564    /// this must hold an `Option<SocketIpAddr>`. Since `::FFFF:0.0.0.0` is
565    /// a legal IPv4-mapped IPv6 address, this allows us to represent it as the
566    /// unspecified IPv4 address.
567    Mapped(Option<ZonedAddr<SocketIpAddr<<I::OtherVersion as Ip>::Addr>, D>>),
568}
569
570/// Try to convert a specified address into the address that maps to it from
571/// the other stack.
572///
573/// This is an IP-generic function that tries to invert the
574/// IPv4-to-IPv4-mapped-IPv6 conversion that is performed by
575/// [`Ipv4Addr::to_ipv6_mapped`].
576///
577/// The only inputs that will produce [`TryUnmapResult::Mapped`] are
578/// IPv4-mapped IPv6 addresses. All other inputs will produce
579/// [`TryUnmapResult::CannotBeUnmapped`].
580fn try_unmap<A: IpAddress, D>(addr: ZonedAddr<SpecifiedAddr<A>, D>) -> TryUnmapResult<A::Version, D>
581where
582    A::Version: DualStackIpExt,
583{
584    <A::Version as Ip>::map_ip(
585        addr,
586        |v4| {
587            let addr = SocketIpAddr::new_ipv4_specified(v4.addr());
588            TryUnmapResult::CannotBeUnmapped(ZonedAddr::Unzoned(addr))
589        },
590        |v6| match v6.addr().to_ipv4_mapped() {
591            Some(v4) => {
592                let addr = SpecifiedAddr::new(v4).map(SocketIpAddr::new_ipv4_specified);
593                TryUnmapResult::Mapped(addr.map(ZonedAddr::Unzoned))
594            }
595            None => {
596                let (addr, zone) = v6.into_addr_zone();
597                let addr: SocketIpAddr<_> =
598                    addr.try_into().unwrap_or_else(|AddrIsMappedError {}| {
599                        unreachable!(
600                            "addr cannot be mapped because `to_ipv4_mapped` returned `None`"
601                        )
602                    });
603                TryUnmapResult::CannotBeUnmapped(ZonedAddr::new(addr, zone).unwrap_or_else(|| {
604                    unreachable!("addr should still be scopeable after wrapping in `SocketIpAddr`")
605                }))
606            }
607        },
608    )
609}
610
611/// Provides a specified IP address to use in-place of an unspecified
612/// remote.
613///
614/// Concretely, this method is called during `connect()` and `send_to()`
615/// socket operations to transform an unspecified remote IP address to the
616/// loopback address. This ensures conformance with Linux and BSD.
617fn specify_unspecified_remote<I: SocketIpExt, A: From<SocketIpAddr<I::Addr>>, Z>(
618    addr: Option<ZonedAddr<A, Z>>,
619) -> ZonedAddr<A, Z> {
620    addr.unwrap_or_else(|| ZonedAddr::Unzoned(I::LOOPBACK_ADDRESS_AS_SOCKET_IP_ADDR.into()))
621}
622
623/// A remote IP address that's either in the current stack or the other stack.
624#[allow(missing_docs)]
625pub enum DualStackRemoteIp<I: DualStackIpExt, D> {
626    ThisStack(ZonedAddr<SocketIpAddr<I::Addr>, D>),
627    OtherStack(ZonedAddr<SocketIpAddr<<I::OtherVersion as Ip>::Addr>, D>),
628}
629
630impl<I: SocketIpExt + DualStackIpExt<OtherVersion: SocketIpExt>, D> DualStackRemoteIp<I, D> {
631    /// Returns the [`DualStackRemoteIp`] for the given `remote_ip``.
632    ///
633    /// An IPv4-mapped-IPv6 address will be unmapped to the inner IPv4 address,
634    /// and an unspecified address will be populated with
635    /// [`specify_unspecified_remote`].
636    pub fn new(remote_ip: Option<ZonedAddr<SpecifiedAddr<I::Addr>, D>>) -> Self {
637        let remote_ip = specify_unspecified_remote::<I, _, _>(remote_ip);
638        match try_unmap(remote_ip) {
639            TryUnmapResult::CannotBeUnmapped(remote_ip) => Self::ThisStack(remote_ip),
640            TryUnmapResult::Mapped(remote_ip) => {
641                // NB: Even though we ensured the address was specified above by
642                // calling `specify_unspecified_remote`, it's possible that
643                // unmapping the address made it unspecified (e.g. `::FFFF:0.0.0.0`
644                // is a specified IPv6 addr but an unspecified IPv4 addr). Call
645                // `specify_unspecified_remote` again to ensure the unmapped address
646                // is specified.
647                let remote_ip = specify_unspecified_remote::<I::OtherVersion, _, _>(remote_ip);
648                Self::OtherStack(remote_ip)
649            }
650        }
651    }
652}
653
654/// A local IP address that's either in the current stack or the other stack.
655#[allow(missing_docs)]
656pub enum DualStackLocalIp<I: DualStackIpExt, D> {
657    ThisStack(ZonedAddr<SocketIpAddr<I::Addr>, D>),
658    OtherStack(Option<ZonedAddr<SocketIpAddr<<I::OtherVersion as Ip>::Addr>, D>>),
659}
660
661impl<I: SocketIpExt + DualStackIpExt<OtherVersion: SocketIpExt>, D> DualStackLocalIp<I, D> {
662    /// Returns the [`DualStackLocalIp`] for the given `local_ip``.
663    ///
664    /// If `local_ip` is the unspecified address for the other stack, returns
665    /// `Self::OtherStack(None)`.
666    pub fn new(local_ip: ZonedAddr<SpecifiedAddr<I::Addr>, D>) -> Self {
667        match try_unmap(local_ip) {
668            TryUnmapResult::CannotBeUnmapped(local_ip) => Self::ThisStack(local_ip),
669            TryUnmapResult::Mapped(local_ip) => Self::OtherStack(local_ip),
670        }
671    }
672}