1use std::convert::{TryFrom, TryInto};
5use std::fmt;
6pub use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
7
8use crate::model;
9
10pub const RESERVED: u8 = 0x00;
11
12pub use model::ProtocolVersion;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
17pub enum ResponseCode {
18 Success = 0x00,
19 Failure = 0x01,
20 RuleFailure = 0x02,
21 NetworkUnreachable = 0x03,
22 HostUnreachable = 0x04,
23 ConnectionRefused = 0x05,
24 TtlExpired = 0x06,
25 CommandNotSupported = 0x07,
26 AddrTypeNotSupported = 0x08,
27}
28
29impl From<model::ConnectResult> for ResponseCode {
30 fn from(res: model::ConnectResult) -> Self {
31 use model::ConnectError::*;
32 match res {
33 Ok(()) => ResponseCode::Success,
34 Err(ServerFailure) => ResponseCode::Failure,
35 Err(ConnectionNotAllowed) => ResponseCode::RuleFailure,
36 Err(NetworkUnreachable) => ResponseCode::NetworkUnreachable,
37 Err(HostUnreachable) => ResponseCode::HostUnreachable,
38 Err(ConnectionRefused) => ResponseCode::ConnectionRefused,
39 Err(TtlExpired) => ResponseCode::TtlExpired,
40 Err(CommandNotSupported) => ResponseCode::CommandNotSupported,
41 Err(AddrTypeNotSupported) => ResponseCode::AddrTypeNotSupported,
42 }
43 }
44}
45
46impl From<ResponseCode> for model::ConnectResult {
47 fn from(res: ResponseCode) -> Self {
48 use model::ConnectError as CErr;
49 use ResponseCode::*;
50 match res {
51 Success => Ok(()),
52 Failure => Err(CErr::ServerFailure),
53 RuleFailure => Err(CErr::ConnectionNotAllowed),
54 NetworkUnreachable => Err(CErr::NetworkUnreachable),
55 HostUnreachable => Err(CErr::HostUnreachable),
56 ConnectionRefused => Err(CErr::ConnectionRefused),
57 TtlExpired => Err(CErr::TtlExpired),
58 CommandNotSupported => Err(CErr::CommandNotSupported),
59 AddrTypeNotSupported => Err(CErr::AddrTypeNotSupported),
60 }
61 }
62}
63
64impl ResponseCode {
65 pub fn code(&self) -> u8 {
66 *self as u8
67 }
68
69 pub fn from_u8(code: u8) -> Result<Self, TryFromU8Error> {
70 match code {
71 0 => Ok(ResponseCode::Success),
72 1 => Ok(ResponseCode::Failure),
73 2 => Ok(ResponseCode::RuleFailure),
74 3 => Ok(ResponseCode::NetworkUnreachable),
75 4 => Ok(ResponseCode::HostUnreachable),
76 5 => Ok(ResponseCode::ConnectionRefused),
77 6 => Ok(ResponseCode::TtlExpired),
78 7 => Ok(ResponseCode::CommandNotSupported),
79 8 => Ok(ResponseCode::AddrTypeNotSupported),
80 c => Err(TryFromU8Error {
81 value: c,
82 to: "ResponseCode".to_owned(),
83 }),
84 }
85 }
86}
87
88impl fmt::Display for ResponseCode {
89 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
90 use ResponseCode::*;
91 match self {
92 Success => write!(f, "succeeded"),
93 Failure => write!(f, "general SOCKS server failure"),
94 RuleFailure => write!(f, "connection now allowed by ruleset"),
95 NetworkUnreachable => write!(f, "Network unreachable"),
96 HostUnreachable => write!(f, "Host unreachable"),
97 ConnectionRefused => write!(f, "Connection refused"),
98 TtlExpired => write!(f, "TTL expired"),
99 CommandNotSupported => write!(f, "Command not supported"),
100 AddrTypeNotSupported => write!(f, "Address type not supported"),
101 }
102 }
103}
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
107pub enum AuthMethods {
108 NoAuth,
110 GssApi,
112 UserPass,
114 IANAMethod(u8),
116 Private(u8),
118 NoMethods,
120}
121
122impl AuthMethods {
123 pub fn code(&self) -> u8 {
124 use AuthMethods::*;
125 match self {
126 NoAuth => 0x00,
127 GssApi => 0x01,
128 UserPass => 0x02,
129 IANAMethod(c) => *c,
130 Private(c) => *c,
131 NoMethods => 0xff,
132 }
133 }
134}
135
136impl From<AuthMethods> for model::Method {
137 fn from(methods: AuthMethods) -> Self {
138 use model::Method::*;
139 match methods {
140 AuthMethods::NoAuth => NoAuth,
141 AuthMethods::GssApi => GssApi,
142 AuthMethods::UserPass => UserPass,
143 AuthMethods::IANAMethod(c) => IANAMethod(c),
144 AuthMethods::Private(c) => Private(c),
145 AuthMethods::NoMethods => NoMethods,
146 }
147 }
148}
149
150impl From<model::Method> for AuthMethods {
151 fn from(method: model::Method) -> Self {
152 use AuthMethods::*;
153 match method {
154 model::Method::NoAuth => NoAuth,
155 model::Method::GssApi => GssApi,
156 model::Method::UserPass => UserPass,
157 model::Method::IANAMethod(c) => IANAMethod(c),
158 model::Method::Private(c) => Private(c),
159 model::Method::NoMethods => NoMethods,
160 }
161 }
162}
163
164impl From<u8> for AuthMethods {
165 fn from(code: u8) -> Self {
166 use AuthMethods::*;
167 match code {
168 0x00 => NoAuth,
169 0x01 => GssApi,
170 0x02 => UserPass,
171 0x03..=0x7F => IANAMethod(code),
172 0x80..=0xFE => Private(code),
173 0xFF => NoMethods,
174 }
175 }
176}
177
178impl fmt::Display for AuthMethods {
179 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
180 use AuthMethods::*;
181 match self {
182 NoAuth => write!(f, "No Authentication Required"),
183 GssApi => write!(f, "GSSAPI"),
184 UserPass => write!(f, "Username/Password"),
185 IANAMethod(c) => write!(f, "IANA Assigned: {:#X}", c),
186 Private(c) => write!(f, "Private Methods: {:#X}", c),
187 NoMethods => write!(f, "No Acceptable Methods"),
188 }
189 }
190}
191
192#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
193pub struct TryFromU8Error {
194 value: u8,
196 to: String,
198}
199
200impl fmt::Display for TryFromU8Error {
201 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
202 write!(f, "try from u8({:#X}) error to {}", self.value, self.to)
203 }
204}
205
206impl std::error::Error for TryFromU8Error {
207 fn description(&self) -> &str {
208 "TryFromU8Error"
209 }
210
211 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
212 None
213 }
214}
215
216#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
218pub enum AddrType {
219 V4 = 0x01,
220 Domain = 0x03,
221 V6 = 0x04,
222}
223
224impl TryFrom<u8> for AddrType {
225 type Error = TryFromU8Error;
226 fn try_from(n: u8) -> Result<AddrType, Self::Error> {
228 match n {
229 1 => Ok(AddrType::V4),
230 3 => Ok(AddrType::Domain),
231 4 => Ok(AddrType::V6),
232 _ => Err(TryFromU8Error {
233 value: n,
234 to: "protocol::AddrType".to_owned(),
235 }),
236 }
237 }
238}
239
240impl fmt::Display for AddrType {
241 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
242 use AddrType::*;
243 match self {
244 V4 => write!(f, "Version4 IP Address"),
245 Domain => write!(f, "Fully Qualified Domain Name"),
246 V6 => write!(f, "Version6 IP Address"),
247 }
248 }
249}
250
251#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
252pub enum Addr {
253 IpAddr(IpAddr),
254 Domain(Vec<u8>),
255}
256
257impl From<IpAddr> for Addr {
258 fn from(addr: IpAddr) -> Self {
259 Addr::IpAddr(addr)
260 }
261}
262
263impl From<model::Address> for Addr {
264 fn from(addr: model::Address) -> Self {
265 match addr {
266 model::Address::IpAddr(addr, _) => Addr::IpAddr(addr),
267 model::Address::Domain(domain, _) => Addr::Domain(domain.as_bytes().to_vec()),
268 }
269 }
270}
271
272#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
274pub enum SockCommand {
275 Connect = 0x01,
276 Bind = 0x02,
277 UdpAssociate = 0x3,
278}
279
280impl From<SockCommand> for model::Command {
281 fn from(cmd: SockCommand) -> Self {
282 use SockCommand::*;
283 match cmd {
284 Connect => model::Command::Connect,
285 Bind => model::Command::Bind,
286 UdpAssociate => model::Command::UdpAssociate,
287 }
288 }
289}
290
291impl From<model::Command> for SockCommand {
292 fn from(cmd: model::Command) -> Self {
293 use SockCommand::*;
294 match cmd {
295 model::Command::Connect => Connect,
296 model::Command::Bind => Bind,
297 model::Command::UdpAssociate => UdpAssociate,
298 }
299 }
300}
301
302impl TryFrom<u8> for SockCommand {
303 type Error = TryFromU8Error;
304 fn try_from(n: u8) -> Result<SockCommand, Self::Error> {
306 match n {
307 1 => Ok(SockCommand::Connect),
308 2 => Ok(SockCommand::Bind),
309 3 => Ok(SockCommand::UdpAssociate),
310 _ => Err(TryFromU8Error {
311 value: n,
312 to: "protocol::SockCommand".to_owned(),
313 }),
314 }
315 }
316}
317
318#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
319pub struct MethodCandidates {
320 pub ver: ProtocolVersion,
321 pub methods: Vec<AuthMethods>,
322}
323
324impl From<MethodCandidates> for model::MethodCandidates {
325 fn from(candidates: MethodCandidates) -> Self {
326 model::MethodCandidates {
327 version: candidates.ver,
328 method: candidates.methods.into_iter().map(Into::into).collect(),
329 }
330 }
331}
332
333impl From<model::MethodCandidates> for MethodCandidates {
334 fn from(candidates: model::MethodCandidates) -> Self {
335 MethodCandidates {
336 ver: candidates.version,
337 methods: candidates.method.into_iter().map(Into::into).collect(),
338 }
339 }
340}
341
342#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
343pub struct MethodSelection {
344 pub ver: ProtocolVersion,
345 pub method: AuthMethods,
346}
347
348impl From<model::MethodSelection> for MethodSelection {
349 fn from(select: model::MethodSelection) -> Self {
350 MethodSelection {
351 ver: select.version,
352 method: select.method.into(),
353 }
354 }
355}
356
357impl From<MethodSelection> for model::MethodSelection {
358 fn from(select: MethodSelection) -> Self {
359 model::MethodSelection {
360 version: select.ver,
361 method: select.method.into(),
362 }
363 }
364}
365
366#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
367pub struct ConnectRequest {
368 pub ver: ProtocolVersion,
369 pub cmd: SockCommand,
370 pub rsv: u8,
371 pub atyp: AddrType,
372 pub dst_addr: Addr,
373 pub dst_port: u16,
374}
375
376#[derive(Debug, Clone, PartialEq, Eq)]
378pub struct AddrTriple {
379 atyp: AddrType,
380 addr: Addr,
381 port: u16,
382}
383
384impl AddrTriple {
385 pub fn new(atyp: AddrType, addr: Addr, port: u16) -> Self {
386 Self { atyp, addr, port }
387 }
388}
389
390impl TryFrom<AddrTriple> for model::Address {
391 type Error = TryFromAddress;
392
393 fn try_from(addr: AddrTriple) -> Result<Self, Self::Error> {
394 use AddrType::*;
395 let AddrTriple { atyp, addr, port } = addr;
396 match (atyp, addr) {
397 (V4, Addr::IpAddr(addr @ IpAddr::V4(_))) => Ok(model::Address::IpAddr(addr, port)),
398 (V6, Addr::IpAddr(addr @ IpAddr::V6(_))) => Ok(model::Address::IpAddr(addr, port)),
399 (Domain, Addr::Domain(domain)) => Ok(model::Address::Domain(
400 String::from_utf8_lossy(&domain).to_string(),
401 port,
402 )),
403 (atyp, addr) => Err(TryFromAddress { atyp, addr, port }),
404 }
405 }
406}
407
408#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
409pub struct TryFromAddress {
410 atyp: AddrType,
411 addr: Addr,
412 port: u16,
413}
414
415impl fmt::Display for TryFromAddress {
416 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
417 write!(
418 f,
419 "try_from address({}, {:?}, {})",
420 self.atyp, self.addr, self.port
421 )
422 }
423}
424
425impl std::error::Error for TryFromAddress {
426 fn description(&self) -> &str {
427 "TryFromAddress"
428 }
429
430 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
431 None
432 }
433}
434
435impl From<TryFromAddress> for model::Error {
436 fn from(err: TryFromAddress) -> Self {
437 model::Error::message_fmt(format_args!("{}", err))
438 }
439}
440
441impl TryFrom<ConnectRequest> for model::ConnectRequest {
442 type Error = TryFromAddress;
443 fn try_from(req: ConnectRequest) -> Result<Self, Self::Error> {
444 let dst = AddrTriple::new(req.atyp, req.dst_addr, req.dst_port).try_into()?;
445 Ok(model::ConnectRequest {
446 version: req.ver,
447 command: req.cmd.into(),
448 connect_to: dst,
449 })
450 }
451}
452
453impl From<model::ConnectRequest> for ConnectRequest {
454 fn from(req: model::ConnectRequest) -> Self {
455 use model::Address as A;
456 let (atyp, dst_addr, dst_port) = match req.connect_to {
457 A::IpAddr(addr @ IpAddr::V4(_), port) => (AddrType::V4, Addr::IpAddr(addr), port),
458 A::IpAddr(addr @ IpAddr::V6(_), port) => (AddrType::V6, Addr::IpAddr(addr), port),
459 A::Domain(addr, port) => (
460 AddrType::Domain,
461 Addr::Domain(addr.as_bytes().to_vec()),
462 port,
463 ),
464 };
465 ConnectRequest {
466 ver: req.version,
467 cmd: req.command.into(),
468 rsv: 0,
469 atyp,
470 dst_addr,
471 dst_port,
472 }
473 }
474}
475
476#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
477pub struct ConnectReply {
478 pub ver: ProtocolVersion,
479 pub rep: ResponseCode,
480 pub rsv: u8,
481 pub atyp: AddrType,
482 pub bnd_addr: Addr,
483 pub bnd_port: u16,
484}
485
486impl TryFrom<ConnectReply> for model::ConnectReply {
487 type Error = TryFromAddress;
488 fn try_from(rep: ConnectReply) -> Result<Self, Self::Error> {
489 Ok(model::ConnectReply {
490 version: rep.ver,
491 connect_result: rep.rep.into(),
492 server_addr: AddrTriple::new(rep.atyp, rep.bnd_addr, rep.bnd_port).try_into()?,
493 })
494 }
495}
496
497impl From<model::ConnectReply> for ConnectReply {
498 fn from(rep: model::ConnectReply) -> Self {
499 use model::Address as A;
500 let (atyp, addr, port) = match rep.server_addr {
501 A::IpAddr(addr @ IpAddr::V4(_), port) => (AddrType::V4, Addr::IpAddr(addr), port),
502 A::IpAddr(addr @ IpAddr::V6(_), port) => (AddrType::V6, Addr::IpAddr(addr), port),
503 A::Domain(addr, port) => (
504 AddrType::Domain,
505 Addr::Domain(addr.as_bytes().to_vec()),
506 port,
507 ),
508 };
509 ConnectReply {
510 ver: rep.version,
511 rep: rep.connect_result.into(),
512 rsv: 0u8,
513 atyp,
514 bnd_addr: addr,
515 bnd_port: port,
516 }
517 }
518}
519
520#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
521pub struct UdpHeader {
522 pub rsv: u16,
523 pub frag: u8,
525 pub atyp: AddrType,
526 pub dst_addr: Addr,
527 pub dst_port: u16,
528}