1use bytes::Bytes;
4use serde::{Deserialize, Serialize};
5
6use crate::{ProtocolError, Result};
7
8#[derive(Debug, Clone)]
10pub enum ControlMessage {
11 TlsData(Bytes),
13 PushRequest,
15 PushReply(PushReply),
17 Auth(AuthMessage),
19 Info(String),
21 Exit,
23}
24
25#[derive(Debug, Clone)]
27pub struct ControlPacket {
28 pub packet_id: u32,
30 pub message: ControlMessage,
32}
33
34impl ControlPacket {
35 pub fn new(packet_id: u32, message: ControlMessage) -> Self {
37 Self { packet_id, message }
38 }
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct PushReply {
44 pub routes: Vec<PushRoute>,
46 pub ifconfig: Option<(String, String)>,
48 pub ifconfig_ipv6: Option<String>,
50 pub dns: Vec<String>,
52 pub dns_search: Vec<String>,
54 pub redirect_gateway: bool,
56 pub topology: Topology,
58 pub ping: u32,
60 pub ping_restart: u32,
62 pub options: Vec<String>,
64}
65
66impl Default for PushReply {
67 fn default() -> Self {
68 Self {
69 routes: vec![],
70 ifconfig: None,
71 ifconfig_ipv6: None,
72 dns: vec![],
73 dns_search: vec![],
74 redirect_gateway: false,
75 topology: Topology::Subnet,
76 ping: 10,
77 ping_restart: 60,
78 options: vec![],
79 }
80 }
81}
82
83impl PushReply {
84 pub fn encode(&self) -> String {
86 let mut parts = vec!["PUSH_REPLY".to_string()];
87
88 parts.push(format!("topology {}", self.topology.as_str()));
90
91 if let Some((ip, mask)) = &self.ifconfig {
93 parts.push(format!("ifconfig {} {}", ip, mask));
94 }
95
96 if let Some(ipv6) = &self.ifconfig_ipv6 {
98 parts.push(format!("ifconfig-ipv6 {}", ipv6));
99 }
100
101 for route in &self.routes {
103 parts.push(route.encode());
104 }
105
106 if self.redirect_gateway {
108 parts.push("redirect-gateway def1".to_string());
109 }
110
111 for dns in &self.dns {
113 parts.push(format!("dhcp-option DNS {}", dns));
114 }
115
116 for domain in &self.dns_search {
118 parts.push(format!("dhcp-option DOMAIN {}", domain));
119 }
120
121 parts.push(format!("ping {}", self.ping));
123 parts.push(format!("ping-restart {}", self.ping_restart));
124
125 for opt in &self.options {
127 parts.push(opt.clone());
128 }
129
130 parts.join(",")
131 }
132
133 pub fn parse(s: &str) -> Result<Self> {
135 let mut reply = Self::default();
136
137 let s = s.strip_prefix("PUSH_REPLY,").unwrap_or(s);
139
140 for part in s.split(',') {
141 let part = part.trim();
142 if part.is_empty() {
143 continue;
144 }
145
146 let mut tokens = part.split_whitespace();
147 match tokens.next() {
148 Some("topology") => {
149 if let Some(topo) = tokens.next() {
150 reply.topology = Topology::parse(topo);
151 }
152 }
153 Some("ifconfig") => {
154 let ip = tokens.next().unwrap_or("").to_string();
155 let mask = tokens.next().unwrap_or("").to_string();
156 reply.ifconfig = Some((ip, mask));
157 }
158 Some("ifconfig-ipv6") => {
159 if let Some(ipv6) = tokens.next() {
160 reply.ifconfig_ipv6 = Some(ipv6.to_string());
161 }
162 }
163 Some("route") => {
164 if let Ok(route) = PushRoute::parse(part) {
165 reply.routes.push(route);
166 }
167 }
168 Some("redirect-gateway") => {
169 reply.redirect_gateway = true;
170 }
171 Some("dhcp-option") => {
172 match tokens.next() {
173 Some("DNS") => {
174 if let Some(dns) = tokens.next() {
175 reply.dns.push(dns.to_string());
176 }
177 }
178 Some("DOMAIN") => {
179 if let Some(domain) = tokens.next() {
180 reply.dns_search.push(domain.to_string());
181 }
182 }
183 _ => {}
184 }
185 }
186 Some("ping") => {
187 if let Some(Ok(p)) = tokens.next().map(|s| s.parse()) {
188 reply.ping = p;
189 }
190 }
191 Some("ping-restart") => {
192 if let Some(Ok(p)) = tokens.next().map(|s| s.parse()) {
193 reply.ping_restart = p;
194 }
195 }
196 _ => {
197 reply.options.push(part.to_string());
198 }
199 }
200 }
201
202 Ok(reply)
203 }
204}
205
206#[derive(Debug, Clone, Serialize, Deserialize)]
208pub struct PushRoute {
209 pub network: String,
211 pub netmask: String,
213 pub gateway: Option<String>,
215 pub metric: Option<u32>,
217}
218
219impl PushRoute {
220 pub fn new(network: &str, netmask: &str) -> Self {
222 Self {
223 network: network.to_string(),
224 netmask: netmask.to_string(),
225 gateway: None,
226 metric: None,
227 }
228 }
229
230 pub fn encode(&self) -> String {
232 let mut s = format!("route {} {}", self.network, self.netmask);
233 if let Some(gw) = &self.gateway {
234 s.push_str(&format!(" {}", gw));
235 } else {
236 s.push_str(" vpn_gateway");
237 }
238 if let Some(metric) = self.metric {
239 s.push_str(&format!(" {}", metric));
240 }
241 s
242 }
243
244 pub fn parse(s: &str) -> Result<Self> {
246 let mut tokens = s.split_whitespace();
247 tokens.next(); let network = tokens
250 .next()
251 .ok_or_else(|| ProtocolError::InvalidPacket("missing network in route".into()))?
252 .to_string();
253
254 let netmask = tokens
255 .next()
256 .ok_or_else(|| ProtocolError::InvalidPacket("missing netmask in route".into()))?
257 .to_string();
258
259 let gateway = tokens.next().and_then(|g| {
260 if g == "vpn_gateway" {
261 None
262 } else {
263 Some(g.to_string())
264 }
265 });
266
267 let metric = tokens.next().and_then(|m| m.parse().ok());
268
269 Ok(Self {
270 network,
271 netmask,
272 gateway,
273 metric,
274 })
275 }
276}
277
278#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
280pub enum Topology {
281 Net30,
283 P2P,
285 #[default]
287 Subnet,
288}
289
290impl Topology {
291 pub fn parse(s: &str) -> Self {
293 match s.to_lowercase().as_str() {
294 "net30" => Topology::Net30,
295 "p2p" => Topology::P2P,
296 "subnet" => Topology::Subnet,
297 _ => Topology::Subnet,
298 }
299 }
300
301 pub fn as_str(&self) -> &'static str {
303 match self {
304 Topology::Net30 => "net30",
305 Topology::P2P => "p2p",
306 Topology::Subnet => "subnet",
307 }
308 }
309}
310
311#[derive(Debug, Clone)]
313pub struct AuthMessage {
314 pub username: String,
316 pub password: String,
318}
319
320impl AuthMessage {
321 pub fn parse(data: &[u8]) -> Result<Self> {
323 let s = std::str::from_utf8(data)
325 .map_err(|_| ProtocolError::InvalidPacket("invalid UTF-8 in auth".into()))?;
326
327 let parts: Vec<&str> = s.split('\0').collect();
328 if parts.len() < 2 {
329 return Err(ProtocolError::InvalidPacket("missing auth fields".into()));
330 }
331
332 Ok(Self {
333 username: parts[0].to_string(),
334 password: parts[1].to_string(),
335 })
336 }
337
338 pub fn encode(&self) -> Vec<u8> {
340 let mut data = Vec::new();
341 data.extend_from_slice(self.username.as_bytes());
342 data.push(0);
343 data.extend_from_slice(self.password.as_bytes());
344 data.push(0);
345 data
346 }
347}
348
349#[derive(Debug, Clone)]
351pub struct KeyMethodV2 {
352 pub pre_master: [u8; 48],
354 pub random: [u8; 32],
356 pub options: String,
358 pub username: Option<String>,
360 pub password: Option<String>,
362 pub peer_info: Option<String>,
364}
365
366impl KeyMethodV2 {
367 pub fn encode(&self) -> Vec<u8> {
369 let mut buf = Vec::new();
370
371 buf.extend_from_slice(&[0u8; 4]);
373
374 buf.push(2);
376
377 buf.extend_from_slice(&self.pre_master);
379
380 buf.extend_from_slice(&self.random);
382
383 let opts_bytes = self.options.as_bytes();
385 buf.extend_from_slice(&(opts_bytes.len() as u16).to_be_bytes());
386 buf.extend_from_slice(opts_bytes);
387
388 if let Some(username) = &self.username {
390 let username_bytes = username.as_bytes();
391 buf.extend_from_slice(&(username_bytes.len() as u16).to_be_bytes());
392 buf.extend_from_slice(username_bytes);
393 } else {
394 buf.extend_from_slice(&0u16.to_be_bytes());
395 }
396
397 if let Some(password) = &self.password {
399 let password_bytes = password.as_bytes();
400 buf.extend_from_slice(&(password_bytes.len() as u16).to_be_bytes());
401 buf.extend_from_slice(password_bytes);
402 } else {
403 buf.extend_from_slice(&0u16.to_be_bytes());
404 }
405
406 if let Some(peer_info) = &self.peer_info {
408 let peer_info_bytes = peer_info.as_bytes();
409 buf.extend_from_slice(&(peer_info_bytes.len() as u16).to_be_bytes());
410 buf.extend_from_slice(peer_info_bytes);
411 }
412
413 buf
414 }
415}
416
417#[cfg(test)]
418mod tests {
419 use super::*;
420
421 #[test]
422 fn test_push_reply_roundtrip() {
423 let mut reply = PushReply::default();
424 reply.ifconfig = Some(("10.8.0.2".to_string(), "255.255.255.0".to_string()));
425 reply.dns.push("1.1.1.1".to_string());
426 reply.routes.push(PushRoute::new("192.168.1.0", "255.255.255.0"));
427 reply.redirect_gateway = true;
428
429 let encoded = reply.encode();
430 let parsed = PushReply::parse(&encoded).unwrap();
431
432 assert_eq!(parsed.ifconfig, reply.ifconfig);
433 assert_eq!(parsed.dns, reply.dns);
434 assert!(parsed.redirect_gateway);
435 }
436
437 #[test]
438 fn test_auth_message() {
439 let auth = AuthMessage {
440 username: "user".to_string(),
441 password: "pass".to_string(),
442 };
443
444 let encoded = auth.encode();
445 let parsed = AuthMessage::parse(&encoded).unwrap();
446
447 assert_eq!(parsed.username, "user");
448 assert_eq!(parsed.password, "pass");
449 }
450}