1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
use rama_core::error::BoxError;
use rama_net::client::{ConnectionError, ConnectionErrorKind};
use rama_utils::macros::enums::enum_builder;
enum_builder! {
/// Protocol version as defined by [RFC 1928].
///
/// [RFC 1928]: https://datatracker.ietf.org/doc/html/rfc1928
@U8
pub enum ProtocolVersion {
Socks5 => 0x05,
}
}
enum_builder! {
/// Subnegotiation version as defined by [RFC 1929].
///
/// [RFC 1929]: https://datatracker.ietf.org/doc/html/rfc1929#section-2
@U8
pub enum UsernamePasswordSubnegotiationVersion {
One => 0x01,
}
}
enum_builder! {
/// Socks5 Method as defined by [IANA SOCKS Methods]
///
/// [IANA SOCKS Methods]: https://www.iana.org/assignments/socks-methods/socks-methods.xhtml
@U8
pub enum SocksMethod {
/// No authentication required.
///
/// Reference: [RFC 1928](https://datatracker.ietf.org/doc/html/rfc1928)
NoAuthenticationRequired => 0x00,
/// Generic Security Services Application Program Interface.
///
/// The method code is assigned in [RFC 1928]; the subnegotiation
/// protocol is defined in [RFC 1961].
///
/// **Not implemented**: rama does not implement GSSAPI authentication.
/// Servers will reply with [`NoAcceptableMethods`] when a client
/// proposes only this method.
///
/// [RFC 1928]: https://datatracker.ietf.org/doc/html/rfc1928
/// [RFC 1961]: https://datatracker.ietf.org/doc/html/rfc1961
GSSAPI => 0x01,
/// Username/Password Authentication for SOCKS V5
///
/// Reference: [RFC 1929](https://datatracker.ietf.org/doc/html/rfc1929)
UsernamePassword => 0x02,
/// Challenge-Handshake Authentication Protocol
///
/// Reference: Marc VanHeyingen <mailto:marcvh@aventail.com>.
ChallengeHandshakeAuthenticationProtocol => 0x03,
/// Challenge-Response Authentication Method
///
/// Reference: Marc VanHeyingen <mailto:marcvh@aventail.com>.
ChallengeResponseAuthenticationMethod => 0x05,
/// Secure Sockets Layer
///
/// Reference: Marc VanHeyingen <mailto:marcvh@aventail.com>.
SecureSocksLayer => 0x06,
/// NDS Authentication
///
/// Reference: Vijay Talati <mailto:VTalati@novell.com>.
NDSAuthentication => 0x07,
/// Multi-Authentication Framework
///
/// Reference: Vijay Talati <mailto:VTalati@novell.com>.
MultiAuthenticationFramework => 0x08,
/// JSON Parameter Block
///
/// Reference: Brandon Wiley <mailto:brandon@operatorfoundation.org>.
JSONParameterBlock => 0x09,
/// No acceptable methods.
///
/// If the selected METHOD (by the server) is X'FF', none of the methods listed by the
/// client are acceptable, and the client MUST close the connection.
///
/// Reference: [RFC 1928](https://datatracker.ietf.org/doc/html/rfc1928)
NoAcceptableMethods => 0xFF,
}
}
enum_builder! {
/// Request Command.
///
/// Reference: <https://datatracker.ietf.org/doc/html/rfc1928#section-4>
@U8
pub enum Command {
/// Request the server to establish a connection on behalf of the client
/// with the destination address.
///
/// Reference: [RFC 1928](https://datatracker.ietf.org/doc/html/rfc1928)
Connect => 0x01,
/// Used in protocols which require the client to accept connections from the server.
///
/// FTP is a well-known example, which uses the primary client-to-server connection for commands and
/// status reports, but may use a server-to-client connection for
/// transferring data on demand (e.g. LS, GET, PUT).
///
/// Reference: [RFC 1928](https://datatracker.ietf.org/doc/html/rfc1928)
Bind => 0x02,
/// Used to establish an association within
/// the UDP relay process to handle UDP datagrams.
///
/// Reference: [RFC 1928](https://datatracker.ietf.org/doc/html/rfc1928)
UdpAssociate => 0x03,
}
}
enum_builder! {
/// Type of the address following it.
///
/// Only used during encoding and decoding,
/// but no use for the in-memory representation.
///
/// Reference: <https://datatracker.ietf.org/doc/html/rfc1928>
@U8
pub enum AddressType {
/// The address is a version-4 IP address, with a length of 4 octets.
IpV4 => 0x01,
/// The address is a length-prefixed (max 255 byte) domain name.
///
/// The address field contains a fully-qualified domain name (FQDN). The first
/// octet of the address field contains the number of octets of name that
/// follow, there is no terminating NUL octet.
DomainName => 0x03,
/// The address is a version-6 IP address, with a length of 16 octets.
IpV6 => 0x04,
}
}
enum_builder! {
/// Indicates success or failure as the reply to a client request.
///
/// Reference: <https://datatracker.ietf.org/doc/html/rfc1928#section-6>
@U8
pub enum ReplyKind {
Succeeded => 0x00,
GeneralServerFailure => 0x01,
ConnectionNotAllowed => 0x02,
NetworkUnreachable => 0x03,
HostUnreachable => 0x04,
ConnectionRefused => 0x05,
TtlExpired => 0x06,
CommandNotSupported => 0x07,
AddressTypeNotSupported => 0x08,
}
}
impl From<&BoxError> for ReplyKind {
fn from(err: &BoxError) -> Self {
let mut source = Some(err.as_ref() as &(dyn std::error::Error + 'static));
for _ in 0..64 {
let Some(err) = source else {
break;
};
if let Some(err) = err.downcast_ref::<ConnectionError>()
&& err.kind() == ConnectionErrorKind::Timeout
{
return Self::TtlExpired;
}
if let Some(err) = err.downcast_ref::<std::io::Error>() {
return match err.kind() {
std::io::ErrorKind::PermissionDenied => Self::ConnectionNotAllowed,
std::io::ErrorKind::HostUnreachable => Self::HostUnreachable,
std::io::ErrorKind::NetworkUnreachable => Self::NetworkUnreachable,
std::io::ErrorKind::TimedOut | std::io::ErrorKind::UnexpectedEof => {
Self::TtlExpired
}
_ => Self::ConnectionRefused,
};
}
source = err.source();
}
Self::ConnectionRefused
}
}