mirror of
https://github.com/vlang/v.git
synced 2025-09-13 06:22:26 +03:00
162 lines
3.1 KiB
V
162 lines
3.1 KiB
V
module socks
|
|
|
|
import net.ssl
|
|
import net
|
|
|
|
const socks_version5 = u8(5)
|
|
|
|
const addr_type_ipv4 = u8(1)
|
|
|
|
const addr_type_fqdn = u8(3)
|
|
|
|
const addr_type_ipv6 = u8(4)
|
|
|
|
const no_auth = u8(0)
|
|
|
|
const auth_user_password = u8(2)
|
|
|
|
// socks5_dial create new instance of &net.TcpConn
|
|
pub fn socks5_dial(proxy_url string, host string, username string, password string) !&net.TcpConn {
|
|
mut con := net.dial_tcp(proxy_url)!
|
|
return handshake(mut con, host, username, password)!
|
|
}
|
|
|
|
// socks5_ssl_dial create new instance of &ssl.SSLConn
|
|
pub fn socks5_ssl_dial(proxy_url string, host string, username string, password string) !&ssl.SSLConn {
|
|
mut ssl_conn := ssl.new_ssl_conn(
|
|
verify: ''
|
|
cert: ''
|
|
cert_key: ''
|
|
validate: false
|
|
in_memory_verification: false
|
|
)!
|
|
mut con := socks5_dial(proxy_url, host, username, password)!
|
|
ssl_conn.connect(mut con, host.all_before_last(':')) or { panic(err) }
|
|
return ssl_conn
|
|
}
|
|
|
|
fn handshake(mut con net.TcpConn, host string, username string, password string) !&net.TcpConn {
|
|
mut v := [socks.socks_version5, 1]
|
|
if username.len > 0 {
|
|
v << socks.auth_user_password
|
|
} else {
|
|
v << socks.no_auth
|
|
}
|
|
|
|
con.write(v)!
|
|
mut bf := []u8{len: 2}
|
|
con.read(mut bf)!
|
|
|
|
if bf[0] != socks.socks_version5 {
|
|
con.close()!
|
|
return error('unexpected protocol version ${bf[0]}')
|
|
}
|
|
if username.len == 0 {
|
|
if bf[1] != 0 {
|
|
con.close()!
|
|
return error(reply(bf[1]))
|
|
}
|
|
}
|
|
if username.len > 0 {
|
|
v.clear()
|
|
v << u8(1)
|
|
v << u8(username.len)
|
|
v << username.bytes()
|
|
v << u8(password.len)
|
|
v << password.bytes()
|
|
|
|
con.write(v)!
|
|
mut resp := []u8{len: 2}
|
|
con.read(mut resp)!
|
|
|
|
if resp[0] != 1 {
|
|
con.close()!
|
|
return error('server does not support user/password version 1')
|
|
} else if resp[1] != 0 {
|
|
con.close()!
|
|
return error('user/password login failed')
|
|
}
|
|
}
|
|
v.clear()
|
|
v = [socks.socks_version5, 1, 0]
|
|
|
|
mut port := host.all_after_last(':').u64()
|
|
if port == 0 {
|
|
port = u64(80)
|
|
}
|
|
address := host.all_before_last(':')
|
|
|
|
if address.contains_only('.1234567890') { // ipv4
|
|
v << socks.addr_type_ipv4
|
|
v << parse_ipv4(address)!
|
|
} else if address.contains_only(':1234567890abcdf') {
|
|
// v << addr_type_ipv6
|
|
// v << parse_ipv4(address)!
|
|
// TODO: support ipv6
|
|
} else { // domain
|
|
if address.len > 255 {
|
|
return error('${address} is too long')
|
|
} else {
|
|
v << socks.addr_type_fqdn
|
|
v << u8(address.len)
|
|
v << address.bytes()
|
|
}
|
|
}
|
|
v << u8(port >> 8)
|
|
v << u8(port)
|
|
|
|
con.write(v)!
|
|
|
|
mut bff := []u8{len: v.len}
|
|
|
|
con.read(mut bff)!
|
|
if bff[1] != 0 {
|
|
con.close()!
|
|
return error(reply(bff[1]))
|
|
}
|
|
return con
|
|
}
|
|
|
|
fn reply(code u8) string {
|
|
match code {
|
|
0 {
|
|
return 'succeeded'
|
|
}
|
|
1 {
|
|
return 'general SOCKS server failure'
|
|
}
|
|
2 {
|
|
return 'connection not allowed by ruleset'
|
|
}
|
|
3 {
|
|
return 'network unreachable'
|
|
}
|
|
4 {
|
|
return 'host unreachable'
|
|
}
|
|
5 {
|
|
return 'connection refused'
|
|
}
|
|
6 {
|
|
return 'TTL expired'
|
|
}
|
|
7 {
|
|
return 'command not supported'
|
|
}
|
|
8 {
|
|
return 'address type not supported'
|
|
}
|
|
else {
|
|
return 'unknown code: ${code}'
|
|
}
|
|
}
|
|
}
|
|
|
|
fn parse_ipv4(addr string) ![]u8 {
|
|
mut ip := []u8{}
|
|
for part in addr.split('.') {
|
|
ip << part.u8()
|
|
}
|
|
|
|
return ip
|
|
}
|