gatekeeper/
raw_message.rs

1//! RFC1928 SOCKS Protocol Version 5 Raw Message Types
2//! For each type structures correspond to SOCKS5 packet layout.
3//!
4use 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
12/// Version of socks
13pub use model::ProtocolVersion;
14
15/// Section 6. Replies > Reply field value
16#[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/// Client Authentication Methods
106#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
107pub enum AuthMethods {
108    /// No Authentication
109    NoAuth,
110    /// GSSAPI
111    GssApi,
112    /// Authenticate with a username / password
113    UserPass,
114    /// IANA assigned method
115    IANAMethod(u8),
116    /// Reserved for private method
117    Private(u8),
118    /// No acceptable method
119    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    /// source value
195    value: u8,
196    /// target type
197    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/// ATYP
217#[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    /// Parse Byte to Command
227    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/// SOCK5 CMD Type
273#[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    /// Parse Byte to Command
305    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/// aux for impl TryFrom to model::Address
377#[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    /// fragment number
524    pub frag: u8,
525    pub atyp: AddrType,
526    pub dst_addr: Addr,
527    pub dst_port: u16,
528}