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}