1use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4};
11use std::time::Duration;
12
13use async_trait::async_trait;
14use tokio::net::UdpSocket;
15
16use crate::error::MethodError;
17use crate::method::natpmp::NATPMP_PORT;
18use crate::method::{MethodOutcome, TraversalKind, TraversalMethod};
19use crate::peer::PeerTarget;
20
21pub const PCP_VERSION: u8 = 2;
23pub const OP_MAP: u8 = 1;
25pub const RESPONSE_BIT: u8 = 0x80;
27pub const RESULT_SUCCESS: u8 = 0;
29pub const PROTO_UDP: u8 = 17;
31pub const PROTO_TCP: u8 = 6;
33
34pub type MapNonce = [u8; 12];
36
37pub fn encode_map_request(
46 nonce: &MapNonce,
47 tcp: bool,
48 internal_port: u16,
49 suggested_external_port: u16,
50 lifetime_secs: u32,
51 client_ip: IpAddr,
52) -> Vec<u8> {
53 let mut buf = Vec::with_capacity(60);
54 buf.push(PCP_VERSION);
55 buf.push(OP_MAP); buf.extend_from_slice(&[0, 0]); buf.extend_from_slice(&lifetime_secs.to_be_bytes());
58 buf.extend_from_slice(&ip_to_16(client_ip));
59 buf.extend_from_slice(nonce);
61 buf.push(if tcp { PROTO_TCP } else { PROTO_UDP });
62 buf.extend_from_slice(&[0, 0, 0]); buf.extend_from_slice(&internal_port.to_be_bytes());
64 buf.extend_from_slice(&suggested_external_port.to_be_bytes());
65 buf.extend_from_slice(&ip_to_16(IpAddr::V4(Ipv4Addr::UNSPECIFIED))); debug_assert_eq!(buf.len(), 60);
67 buf
68}
69
70#[derive(Debug, Clone, PartialEq, Eq)]
72pub struct MapResponse {
73 pub lifetime_secs: u32,
75 pub nonce: MapNonce,
77 pub external_port: u16,
79 pub external_ip: IpAddr,
81}
82
83pub fn parse_map_response(msg: &[u8], expected_nonce: &MapNonce) -> Result<MapResponse, PcpError> {
90 if msg.len() < 60 {
91 return Err(PcpError::Truncated);
92 }
93 if msg[0] != PCP_VERSION {
94 return Err(PcpError::BadVersion(msg[0]));
95 }
96 if msg[1] != (OP_MAP | RESPONSE_BIT) {
97 return Err(PcpError::UnexpectedOpcode(msg[1]));
98 }
99 let result = msg[3];
100 if result != RESULT_SUCCESS {
101 return Err(PcpError::ResultCode(result));
102 }
103 let lifetime = u32::from_be_bytes([msg[4], msg[5], msg[6], msg[7]]);
104 let nonce: MapNonce = msg[24..36].try_into().map_err(|_| PcpError::Truncated)?;
106 if &nonce != expected_nonce {
107 return Err(PcpError::NonceMismatch);
108 }
109 let external_port = u16::from_be_bytes([msg[42], msg[43]]);
110 let ext_ip_bytes: [u8; 16] = msg[44..60].try_into().map_err(|_| PcpError::Truncated)?;
111 Ok(MapResponse {
112 lifetime_secs: lifetime,
113 nonce,
114 external_port,
115 external_ip: ip_from_16(ext_ip_bytes),
116 })
117}
118
119#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
121pub enum PcpError {
122 #[error("PCP response truncated")]
124 Truncated,
125 #[error("unexpected PCP version: {0}")]
127 BadVersion(u8),
128 #[error("unexpected PCP opcode: {0}")]
130 UnexpectedOpcode(u8),
131 #[error("PCP result code {0}")]
133 ResultCode(u8),
134 #[error("PCP MAP nonce mismatch")]
136 NonceMismatch,
137 #[error("PCP io: {0}")]
139 Io(String),
140 #[error("PCP request timed out")]
142 Timeout,
143}
144
145#[derive(Debug, Clone)]
148pub struct PcpMethod {
149 pub gateway: SocketAddrV4,
151 pub local_port: u16,
153 pub client_ip: IpAddr,
155 pub lifetime_secs: u32,
157 pub timeout: Duration,
159}
160
161impl PcpMethod {
162 pub fn new(gateway: Ipv4Addr, local_port: u16, client_ip: IpAddr) -> Self {
164 PcpMethod {
165 gateway: SocketAddrV4::new(gateway, NATPMP_PORT),
166 local_port,
167 client_ip,
168 lifetime_secs: 7200,
169 timeout: Duration::from_secs(1),
170 }
171 }
172
173 async fn transact(&self, payload: &[u8]) -> Result<Vec<u8>, PcpError> {
180 let socket = UdpSocket::bind((Ipv4Addr::UNSPECIFIED, 0))
181 .await
182 .map_err(|e| PcpError::Io(e.to_string()))?;
183 socket
184 .send_to(payload, self.gateway)
185 .await
186 .map_err(|e| PcpError::Io(e.to_string()))?;
187 let mut buf = [0u8; 128];
188 let deadline = tokio::time::Instant::now() + self.timeout;
189 loop {
190 let remaining = deadline.saturating_duration_since(tokio::time::Instant::now());
191 if remaining.is_zero() {
192 return Err(PcpError::Timeout);
193 }
194 let (n, from) = match tokio::time::timeout(remaining, socket.recv_from(&mut buf)).await
195 {
196 Ok(Ok(x)) => x,
197 Ok(Err(e)) => return Err(PcpError::Io(e.to_string())),
198 Err(_) => return Err(PcpError::Timeout),
199 };
200 if from != SocketAddr::V4(self.gateway) {
201 continue;
203 }
204 return Ok(buf[..n].to_vec());
205 }
206 }
207}
208
209#[async_trait]
210impl TraversalMethod for PcpMethod {
211 fn kind(&self) -> TraversalKind {
212 TraversalKind::Pcp
213 }
214
215 async fn attempt(&self, peer: &PeerTarget) -> Result<MethodOutcome, MethodError> {
216 let dial_addrs = peer.direct_addrs();
219 if dial_addrs.is_empty() {
220 return Err(MethodError::failed(
221 TraversalKind::Pcp,
222 "peer has no address to dial after mapping",
223 ));
224 }
225 let nonce = new_nonce();
226 let req = encode_map_request(
227 &nonce,
228 false,
229 self.local_port,
230 self.local_port,
231 self.lifetime_secs,
232 self.client_ip,
233 );
234 let resp = self.transact(&req).await.map_err(|e| to_method_error(&e))?;
235 parse_map_response(&resp, &nonce).map_err(|e| to_method_error(&e))?;
236 Ok(MethodOutcome::candidates(
237 TraversalKind::Pcp,
238 dial_addrs.to_vec(),
239 ))
240 }
241}
242
243fn to_method_error(e: &PcpError) -> MethodError {
245 match e {
246 PcpError::Timeout => MethodError::timeout(TraversalKind::Pcp),
247 other => MethodError::failed(TraversalKind::Pcp, other.to_string()),
248 }
249}
250
251fn ip_to_16(ip: IpAddr) -> [u8; 16] {
253 match ip {
254 IpAddr::V4(v4) => v4.to_ipv6_mapped().octets(),
255 IpAddr::V6(v6) => v6.octets(),
256 }
257}
258
259fn ip_from_16(bytes: [u8; 16]) -> IpAddr {
261 let v6 = Ipv6Addr::from(bytes);
262 match v6.to_ipv4_mapped() {
263 Some(v4) => IpAddr::V4(v4),
264 None => IpAddr::V6(v6),
265 }
266}
267
268pub fn new_nonce() -> MapNonce {
279 use ring::rand::{SecureRandom, SystemRandom};
280
281 let mut n = [0u8; 12];
282 SystemRandom::new()
283 .fill(&mut n)
284 .expect("OS CSPRNG must be available to generate a PCP MAP nonce");
285 n
286}
287
288pub fn ipv4_gateway(addr: SocketAddr) -> Option<Ipv4Addr> {
290 match addr {
291 SocketAddr::V4(v4) => Some(*v4.ip()),
292 SocketAddr::V6(_) => None,
293 }
294}