natpmp/
asynchronous.rs

1use std::io;
2use std::net::Ipv4Addr;
3use std::time::Duration;
4
5use async_trait::async_trait;
6
7use crate::{
8    Error, GatewayResponse, MappingResponse, Protocol, Response, Result, NATPMP_MAX_ATTEMPS,
9};
10
11/// A wrapper trait for async udpsocket.
12#[async_trait]
13pub trait AsyncUdpSocket {
14    async fn connect(&self, addr: &str) -> io::Result<()>;
15
16    async fn send(&self, buf: &[u8]) -> io::Result<usize>;
17
18    async fn recv(&self, buf: &mut [u8]) -> io::Result<usize>;
19}
20
21/// NAT-PMP async client
22pub struct NatpmpAsync<S>
23where
24    S: AsyncUdpSocket,
25{
26    s: S,
27    gateway: Ipv4Addr,
28}
29
30/// Create a NAT-PMP object with async udpsocket and gateway
31pub fn new_natpmp_async_with<S>(s: S, gateway: Ipv4Addr) -> NatpmpAsync<S>
32where
33    S: AsyncUdpSocket,
34{
35    NatpmpAsync { s, gateway }
36}
37
38impl<S> NatpmpAsync<S>
39where
40    S: AsyncUdpSocket,
41{
42    /// NAT-PMP gateway address.
43    pub fn gateway(&self) -> &Ipv4Addr {
44        &self.gateway
45    }
46
47    /// Send public address request.
48    ///
49    /// # Errors
50    /// * [`Error::NATPMP_ERR_SENDERR`](enum.Error.html#variant.NATPMP_ERR_SENDERR)
51    ///
52    /// # Examples
53    /// ```
54    /// use natpmp::*;
55    ///
56    /// let mut n = new_tokio_natpmp().await?;
57    /// n.send_public_address_request().await?;
58    /// ```
59    pub async fn send_public_address_request(&mut self) -> Result<()> {
60        let request = [0_u8; 2];
61        let n = self
62            .s
63            .send(&request[..])
64            .await
65            .map_err(|_| Error::NATPMP_ERR_SENDERR)?;
66        if n != request.len() {
67            return Err(Error::NATPMP_ERR_SENDERR);
68        }
69        Ok(())
70    }
71
72    /// Send port mapping request.
73    ///
74    /// # Errors
75    /// * [`Error::NATPMP_ERR_SENDERR`](enum.Error.html#variant.NATPMP_ERR_SENDERR)
76    ///
77    /// # Examples
78    /// ```
79    /// use natpmp::*;
80    ///
81    /// let mut n = new_tokio_natpmp().await?;
82    /// n.send_port_mapping_request(Protocol::UDP, 4020, 4020, 30).await?;
83    /// ```
84    pub async fn send_port_mapping_request(
85        &self,
86        protocol: Protocol,
87        private_port: u16,
88        public_port: u16,
89        lifetime: u32,
90    ) -> Result<()> {
91        let mut request = [0_u8; 12];
92        request[1] = match protocol {
93            Protocol::UDP => 1,
94            _ => 2,
95        };
96        request[2] = 0; // reserved
97        request[3] = 0;
98        // private port
99        request[4] = (private_port >> 8 & 0xff) as u8;
100        request[5] = (private_port & 0xff) as u8;
101        // public port
102        request[6] = (public_port >> 8 & 0xff) as u8;
103        request[7] = (public_port & 0xff) as u8;
104        // lifetime
105        request[8] = ((lifetime >> 24) & 0xff) as u8;
106        request[9] = ((lifetime >> 16) & 0xff) as u8;
107        request[10] = ((lifetime >> 8) & 0xff) as u8;
108        request[11] = (lifetime & 0xff) as u8;
109
110        let n = self
111            .s
112            .send(&request[..])
113            .await
114            .map_err(|_| Error::NATPMP_ERR_SENDERR)?;
115        if n != request.len() {
116            return Err(Error::NATPMP_ERR_SENDERR);
117        }
118        Ok(())
119    }
120
121    /// Read NAT-PMP response if possible
122    ///
123    /// # Errors
124    /// * [`Error::NATPMP_TRYAGAIN`](enum.Error.html#variant.NATPMP_TRYAGAIN)
125    /// * [`Error::NATPMP_ERR_NOPENDINGREQ`](enum.Error.html#variant.NATPMP_ERR_NOPENDINGREQ)
126    /// * [`Error::NATPMP_ERR_NOGATEWAYSUPPORT`](enum.Error.html#variant.NATPMP_ERR_NOGATEWAYSUPPORT)
127    /// * [`Error::NATPMP_ERR_RECVFROM`](enum.Error.html#variant.NATPMP_ERR_RECVFROM)
128    /// * [`Error::NATPMP_ERR_WRONGPACKETSOURCE`](enum.Error.html#variant.NATPMP_ERR_WRONGPACKETSOURCE)
129    /// * [`Error::NATPMP_ERR_UNSUPPORTEDVERSION`](enum.Error.html#variant.NATPMP_ERR_UNSUPPORTEDVERSION)
130    /// * [`Error::NATPMP_ERR_UNSUPPORTEDOPCODE`](enum.Error.html#variant.NATPMP_ERR_UNSUPPORTEDOPCODE)
131    /// * [`Error::NATPMP_ERR_UNSUPPORTEDVERSION`](enum.Error.html#variant.NATPMP_ERR_UNSUPPORTEDVERSION)
132    /// * [`Error::NATPMP_ERR_NOTAUTHORIZED`](enum.Error.html#variant.NATPMP_ERR_NOTAUTHORIZED)
133    /// * [`Error::NATPMP_ERR_NETWORKFAILURE`](enum.Error.html#variant.NATPMP_ERR_NETWORKFAILURE)
134    /// * [`Error::NATPMP_ERR_OUTOFRESOURCES`](enum.Error.html#variant.NATPMP_ERR_OUTOFRESOURCES)
135    /// * [`Error::NATPMP_ERR_UNSUPPORTEDOPCODE`](enum.Error.html#variant.NATPMP_ERR_OUTOFRESOURCES)
136    /// * [`Error::NATPMP_ERR_UNDEFINEDERROR`](enum.Error.html#variant.NATPMP_ERR_UNDEFINEDERROR)
137    ///
138    /// # Examples
139    /// ```
140    /// use natpmp::*;
141    ///
142    /// let mut n = new_tokio_natpmp().await?;
143    /// n.send_public_address_request().await?;
144    /// let response = n.read_response_or_retry().await?;
145    ///
146    /// ```
147    pub async fn read_response_or_retry(&self) -> Result<Response> {
148        let mut buf = [0_u8; 16];
149        let mut retries = 0;
150        while retries < NATPMP_MAX_ATTEMPS {
151            match self.s.recv(&mut buf).await {
152                Err(_) => retries += 1,
153                Ok(_) => {
154                    // version
155                    if buf[0] != 0 {
156                        return Err(Error::NATPMP_ERR_UNSUPPORTEDVERSION);
157                    }
158                    // opcode
159                    if buf[1] < 128 || buf[1] > 130 {
160                        return Err(Error::NATPMP_ERR_UNSUPPORTEDOPCODE);
161                    }
162                    // result code
163                    let resultcode = u16::from_be_bytes([buf[2], buf[3]]);
164                    // result
165                    if resultcode != 0 {
166                        return Err(match resultcode {
167                            1 => Error::NATPMP_ERR_UNSUPPORTEDVERSION,
168                            2 => Error::NATPMP_ERR_NOTAUTHORIZED,
169                            3 => Error::NATPMP_ERR_NETWORKFAILURE,
170                            4 => Error::NATPMP_ERR_OUTOFRESOURCES,
171                            5 => Error::NATPMP_ERR_UNSUPPORTEDOPCODE,
172                            _ => Error::NATPMP_ERR_UNDEFINEDERROR,
173                        });
174                    }
175                    // epoch
176                    let epoch = u32::from_be_bytes([buf[4], buf[5], buf[6], buf[7]]);
177                    let rsp_type = buf[1] & 0x7f;
178                    return Ok(match rsp_type {
179                        0 => Response::Gateway(GatewayResponse {
180                            epoch,
181                            public_address: Ipv4Addr::from(u32::from_be_bytes([
182                                buf[8], buf[9], buf[10], buf[11],
183                            ])),
184                        }),
185                        _ => {
186                            let private_port = u16::from_be_bytes([buf[8], buf[9]]);
187                            let public_port = u16::from_be_bytes([buf[10], buf[11]]);
188                            let lifetime = u32::from_be_bytes([buf[12], buf[13], buf[14], buf[15]]);
189                            let lifetime = Duration::from_secs(lifetime.into());
190                            let m = MappingResponse {
191                                epoch,
192                                private_port,
193                                public_port,
194                                lifetime,
195                            };
196                            if rsp_type == 1 {
197                                Response::UDP(m)
198                            } else {
199                                Response::TCP(m)
200                            }
201                        }
202                    });
203                }
204            }
205        }
206
207        Err(Error::NATPMP_ERR_RECVFROM)
208    }
209}