portmapper/pcp/protocol/
request.rs1use std::net::{Ipv4Addr, Ipv6Addr};
4
5use super::{
6 opcode_data::{MapData, MapProtocol, OpcodeData},
7 Version,
8};
9
10#[derive(Debug, PartialEq, Eq)]
15pub struct Request {
16 pub(super) version: Version,
18 pub(super) lifetime_seconds: u32,
20 pub(super) client_addr: Ipv6Addr,
24 pub(super) opcode_data: OpcodeData,
26}
27
28impl Request {
29 pub const MIN_SIZE: usize = 1 + 1 + 2 + 4 + 16; pub fn encode(&self) -> Vec<u8> {
39 let Request {
40 version,
41 lifetime_seconds,
42 client_addr,
43 opcode_data,
44 } = self;
45 let mut buf = Vec::with_capacity(Self::MIN_SIZE + opcode_data.encoded_size());
46 buf.push((*version).into());
48 buf.push(opcode_data.opcode().into());
50 buf.push(0);
52 buf.push(0);
54 buf.extend_from_slice(&lifetime_seconds.to_be_bytes());
56 buf.extend_from_slice(&client_addr.octets());
58 opcode_data.encode_into(&mut buf);
60
61 buf
62 }
63
64 pub fn announce(client_addr: Ipv6Addr) -> Request {
66 Request {
67 version: Version::Pcp,
68 lifetime_seconds: 0,
70 client_addr,
71 opcode_data: OpcodeData::Announce,
73 }
74 }
75
76 pub fn mapping(
78 nonce: [u8; 12],
79 local_port: u16,
80 local_ip: Ipv4Addr,
81 preferred_external_port: Option<u16>,
82 preferred_external_address: Option<Ipv4Addr>,
83 lifetime_seconds: u32,
84 ) -> Request {
85 Request {
86 version: Version::Pcp,
87 lifetime_seconds,
88 client_addr: local_ip.to_ipv6_mapped(),
89 opcode_data: OpcodeData::MapData(MapData {
90 nonce,
91 protocol: MapProtocol::Udp,
92 local_port,
93 external_port: preferred_external_port.unwrap_or_default(),
96 external_address: preferred_external_address
97 .unwrap_or(Ipv4Addr::UNSPECIFIED)
98 .to_ipv6_mapped(),
99 }),
100 }
101 }
102
103 #[cfg(test)]
104 fn random<R: rand::Rng>(opcode: super::Opcode, rng: &mut R) -> Self {
105 let opcode_data = OpcodeData::random(opcode, rng);
106 let addr_octets: [u8; 16] = rng.gen();
107 Request {
108 version: Version::Pcp,
109 lifetime_seconds: rng.gen(),
110 client_addr: Ipv6Addr::from(addr_octets),
111 opcode_data,
112 }
113 }
114
115 #[cfg(test)]
116 #[track_caller]
117 fn decode(buf: &[u8]) -> Self {
118 let version: Version = buf[0].try_into().unwrap();
119 let opcode: super::Opcode = buf[1].try_into().unwrap();
120 let lifetime_bytes: [u8; 4] = buf[4..8].try_into().unwrap();
123 let lifetime_seconds = u32::from_be_bytes(lifetime_bytes);
124
125 let local_ip_bytes: [u8; 16] = buf[8..24].try_into().unwrap();
126 let client_addr: Ipv6Addr = local_ip_bytes.into();
127
128 let opcode_data = OpcodeData::decode(opcode, &buf[24..]).unwrap();
129 Self {
130 version,
131 lifetime_seconds,
132 client_addr,
133 opcode_data,
134 }
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use rand::SeedableRng;
141
142 use super::*;
143
144 #[test]
145 fn test_encode_decode_addr_request() {
146 let mut gen = rand_chacha::ChaCha8Rng::seed_from_u64(42);
147
148 let request = Request::random(super::super::Opcode::Announce, &mut gen);
149 let encoded = request.encode();
150 assert_eq!(request, Request::decode(&encoded));
151 }
152
153 #[test]
154 fn test_encode_decode_map_request() {
155 let mut gen = rand_chacha::ChaCha8Rng::seed_from_u64(42);
156
157 let request = Request::random(super::super::Opcode::Map, &mut gen);
158 let encoded = request.encode();
159 assert_eq!(request, Request::decode(&encoded));
160 }
161}