v2 / vlib / net / socks / socks5.v
196 lines · 171 sloc · 4.21 KB · 126d6e3e5fff2e8620e9fd11ae127487303e9191
Raw
1module socks
2
3import net.ssl
4import net
5
6const socks_version5 = u8(5)
7
8const addr_type_ipv4 = u8(1)
9
10const addr_type_fqdn = u8(3)
11
12const addr_type_ipv6 = u8(4)
13
14const no_auth = u8(0)
15
16const auth_user_password = u8(2)
17
18// socks5_dial create new instance of &net.TcpConn
19pub fn socks5_dial(proxy_url string, host string, username string, password string) !&net.TcpConn {
20 mut con := net.dial_tcp(proxy_url)!
21 socks_conn_as_interface := handshake(mut con, host, username, password)!
22 socks_conn := socks_conn_as_interface as net.TcpConn
23 return &socks_conn
24}
25
26// socks5_ssl_dial create new instance of &ssl.SSLConn
27pub fn socks5_ssl_dial(proxy_url string, host string, username string, password string) !&ssl.SSLConn {
28 mut ssl_conn := ssl.new_ssl_conn(
29 verify: ''
30 cert: ''
31 cert_key: ''
32 validate: false
33 in_memory_verification: false
34 )!
35 mut con := socks5_dial(proxy_url, host, username, password)!
36 ssl_conn.connect(mut con, host.all_before_last(':')) or {
37 con.close() or {}
38 return err
39 }
40 ssl_conn.owns_socket = true
41 return ssl_conn
42}
43
44// SOCKS5Dialer implements the Dialer interface initiating connections through a SOCKS5 proxy.
45pub struct SOCKS5Dialer {
46pub:
47 dialer net.Dialer
48 proxy_address string
49 username string
50 password string
51}
52
53// new_socks5_dialer creates a dialer that will use a SOCKS5 proxy server to
54// initiate connections. An underlying dialer is required to initiate the
55// connection to the proxy server. Most users should use either
56// net.default_tcp_dialer or ssl.create_ssl_dialer.
57pub fn new_socks5_dialer(base net.Dialer, proxy_address string, username string, password string) net.Dialer {
58 return &SOCKS5Dialer{
59 dialer: base
60 proxy_address: proxy_address
61 username: username
62 password: password
63 }
64}
65
66// dial initiates a new connection through the SOCKS5 proxy.
67pub fn (sd SOCKS5Dialer) dial(address string) !net.Connection {
68 mut conn := sd.dialer.dial(sd.proxy_address)!
69 return handshake(mut conn, address, sd.username, sd.password)!
70}
71
72fn handshake(mut con net.Connection, host string, username string, password string) !net.Connection {
73 mut v := [socks_version5, 1]
74 if username.len > 0 {
75 v << auth_user_password
76 } else {
77 v << no_auth
78 }
79
80 con.write(v)!
81 mut bf := []u8{len: 2}
82 con.read(mut bf)!
83
84 if bf[0] != socks_version5 {
85 con.close()!
86 return error('unexpected protocol version ${bf[0]}')
87 }
88 if username.len == 0 {
89 if bf[1] != 0 {
90 con.close()!
91 return error(reply(bf[1]))
92 }
93 }
94 if username.len > 0 {
95 v.clear()
96 v << u8(1)
97 v << u8(username.len)
98 v << username.bytes()
99 v << u8(password.len)
100 v << password.bytes()
101
102 con.write(v)!
103 mut resp := []u8{len: 2}
104 con.read(mut resp)!
105
106 if resp[0] != 1 {
107 con.close()!
108 return error('server does not support user/password version 1')
109 } else if resp[1] != 0 {
110 con.close()!
111 return error('user/password login failed')
112 }
113 }
114 v.clear()
115 v = [socks_version5, 1, 0]
116
117 mut port := host.all_after_last(':').u64()
118 if port == 0 {
119 port = u64(80)
120 }
121 address := host.all_before_last(':')
122
123 if address.contains_only('.1234567890') { // ipv4
124 v << addr_type_ipv4
125 v << parse_ipv4(address)!
126 } else if address.contains_only(':1234567890abcdf') {
127 // v << addr_type_ipv6
128 // v << parse_ipv4(address)!
129 // TODO: support ipv6
130 } else { // domain
131 if address.len > 255 {
132 return error('${address} is too long')
133 } else {
134 v << addr_type_fqdn
135 v << u8(address.len)
136 v << address.bytes()
137 }
138 }
139 v << u8(port >> 8)
140 v << u8(port)
141
142 con.write(v)!
143
144 mut bff := []u8{len: v.len}
145
146 con.read(mut bff)!
147 if bff[1] != 0 {
148 con.close()!
149 return error(reply(bff[1]))
150 }
151 return con
152}
153
154fn reply(code u8) string {
155 match code {
156 0 {
157 return 'succeeded'
158 }
159 1 {
160 return 'general SOCKS server failure'
161 }
162 2 {
163 return 'connection not allowed by ruleset'
164 }
165 3 {
166 return 'network unreachable'
167 }
168 4 {
169 return 'host unreachable'
170 }
171 5 {
172 return 'connection refused'
173 }
174 6 {
175 return 'TTL expired'
176 }
177 7 {
178 return 'command not supported'
179 }
180 8 {
181 return 'address type not supported'
182 }
183 else {
184 return 'unknown code: ${code}'
185 }
186 }
187}
188
189fn parse_ipv4(addr string) ![]u8 {
190 mut ip := []u8{}
191 for part in addr.split('.') {
192 ip << part.u8()
193 }
194
195 return ip
196}
197