Skip to main content

dope_sys/io/socket/msg/
source.rs

1use std::{net, os::fd::AsFd as _};
2
3use crate::{
4    Errno,
5    io::socket::{self, Domain, Kind, addr, gso},
6};
7
8/// A concrete source IP and scope with the endpoint's requested port.
9#[derive(Clone, Copy, Debug)]
10pub struct Address(net::SocketAddr);
11
12impl Address {
13    pub const fn get(self) -> net::SocketAddr {
14        self.0
15    }
16}
17
18/// The exact cold source-selection failure.
19#[derive(Clone, Copy, Debug, Eq, PartialEq)]
20pub enum Error {
21    Family {
22        bind: net::SocketAddr,
23        peer: net::SocketAddr,
24    },
25    Open(socket::Create),
26    Bind(Errno),
27    Connect(Errno),
28    Local(addr::Local<Errno>),
29    Address(addr::Error),
30    Unspecified(net::SocketAddr),
31}
32
33/// Selects the native ancillary encoding for a fixed datagram source.
34pub trait Source: socket::Prepare + addr::Source + gso::Source + Sized {
35    type Control: Copy + AsRef<[u8]>;
36    /// Owns the selected source and exactly the optional segmentation capability supplied.
37    type Combined;
38
39    fn control(source: Address) -> Self::Control;
40    fn compose(source: Self::Control, gso: Option<<Self as gso::Source>::Mode>) -> Self::Combined;
41    fn release(control: Self::Combined) -> (Self::Control, Option<<Self as gso::Source>::Mode>);
42    fn bytes(control: &mut Self::Combined) -> &[u8];
43
44    /// Preserves an explicit source; resolves wildcard binds once on a separate socket.
45    fn source(
46        bind: net::SocketAddr,
47        peer: net::SocketAddr,
48    ) -> Result<(Address, Self::Control), Error> {
49        if bind.is_ipv4() != peer.is_ipv4() {
50            return Err(Error::Family { bind, peer });
51        }
52        let local = if bind.ip().to_canonical().is_unspecified() {
53            let file = Self::open(Domain::for_addr(&peer), Kind::Dgram).map_err(Error::Open)?;
54            let mut local = bind;
55            local.set_port(0);
56            addr::Internet::from_std::<Self>(local)
57                .as_addr()
58                .try_bind::<Self, Errno>(file.as_fd())
59                .map_err(Error::Bind)?;
60            addr::Internet::from_std::<Self>(peer)
61                .as_addr()
62                .try_connect::<Self, Errno>(file.as_fd())
63                .map_err(Error::Connect)?;
64            let mut storage = addr::Storage::empty();
65            addr::Addr::local::<Self, Errno>(file.as_fd(), &mut storage).map_err(Error::Local)?;
66            let mut local = storage.to_std().map_err(Error::Address)?;
67            if local.ip().to_canonical().is_unspecified() {
68                return Err(Error::Unspecified(local));
69            }
70            local.set_port(bind.port());
71            Address(local)
72        } else {
73            Address(bind)
74        };
75        Ok((local, Self::control(local)))
76    }
77}