Skip to content

Instantly share code, notes, and snippets.

@renatoalencar
Last active December 11, 2021 12:17
Show Gist options
  • Save renatoalencar/e6cb120554f1ef80b19f4e9ac71ffdb6 to your computer and use it in GitHub Desktop.
Save renatoalencar/e6cb120554f1ef80b19f4e9ac71ffdb6 to your computer and use it in GitHub Desktop.
Socks4 and socks5
type version = Socks4
type command = Connect | Bind
type port = int
type request = { version: version
; command: command
; destport: port
; destip: string
; userid: string option }
let of_bytes bytes =
assert (Bytes.length bytes >= 9);
let version =
match Bytes.get bytes 0 with
| '\004' -> Socks4
| _ -> assert false
in
let command =
match Bytes.get bytes 1 with
| '\001' -> Connect
| '\002' -> Bind
| _ -> assert false
in
let destport =
let h = Char.code (Bytes.get bytes 2) in
let l = Char.code (Bytes.get bytes 3) in
(h lsl 8) lor l
in
let destip =
let parts = [ Bytes.get bytes 4
; Bytes.get bytes 5
; Bytes.get bytes 6
; Bytes.get bytes 7 ] in
parts
|> List.map Char.code
|> List.map string_of_int
|> String.concat "."
in
let userid =
let last_byte_index = ref 8 in
for i = 8 to (Bytes.length bytes) - 1 do
if Bytes.get bytes i = '\000' then
last_byte_index := i
done;
if !last_byte_index = 8 then
None
else
Some (Bytes.to_string (Bytes.sub bytes 8 !last_byte_index))
in
{ version
; command
; destport
; destip
; userid }
let () =
let packet = Bytes.of_string "\004\001\000\x50\xc0\xa8\000\001\000" in
let req = of_bytes packet in
Printf.printf "IP: %s, port: %d, user id: %s\n" req.destip req.destport (Option.value ~default:"No user id" req.userid)
# Almost full working proof of concept in python
import socket
import codecs
def to_hex(b):
return codecs.encode(b, 'hex')
class Socks4:
def make_request_packet_ip(self, destip, destport, command):
version = b'\x04'
port = bytes([0xff & (destport >> 8), 0xff & destport])
ip = socket.inet_aton(destip)
return version + command + port + ip + b'\0'
def make_request_packet_domain(self, domain, destport, command):
req = self.make_request_packet_ip('0.0.0.1', destport, command)
return req + bytes(domain, 'utf-8') + b'\0'
def connect(self, s, dest):
ip, port = dest
try:
request = self.make_request_packet_ip(ip, port, b'\1')
except OSError:
request = self.make_request_packet_domain(ip, port, b'\1')
print('Sending ', to_hex(request))
s.send(request)
response = s.recv(8)
print('Received ', to_hex(response))
assert response[0] == 0
if response[1] == '\90':
print('Request granted')
elif response[1] == '\91':
print('Request rejected or failed')
elif response[1] == '\92':
printf('Socks cannot connect to identd')
elif response[1] == '\93':
printf('User id doesnt match')
connection = socket.socket()
connection.connect(('127.0.0.1', 9050))
socks = Socks4()
domain = 'torchdeedp3i2jigzjdmfpn5ttjhthh5wbmda2rr3jvqjg5p77c54dqd.onion'
socks.connect(connection, (domain, 80))
connection.send(f'''GET /search?query=socks4 HTTP/1.1\r
Host: {domain}\r
User-Agent: curl/7.79.1\r
Accept: */*\r
\r
'''.encode('utf-8'))
response = connection.recv(4096)
print(response.decode('utf-8'))
type version = Socks4 | Socks4a
type command = Connect | Bind
type port = int
type ip_address = int * int * int * int
type addr = IPAddress of ip_address | DomainName of string
type request = { version: version
; command: command
; destport: port
; destip: addr
; userid: string option }
let read_null_terminated_string bytes start =
let last_byte_index = ref start in
for i = start to (Bytes.length bytes) - 1 do
if Bytes.get bytes i = '\000' then
last_byte_index := i
done;
if !last_byte_index = start then
(String.empty, start + 1)
else
(Bytes.to_string (Bytes.sub bytes 8 !last_byte_index), !last_byte_index + 1)
let of_bytes bytes =
assert (Bytes.length bytes >= 9);
let command =
match Bytes.get bytes 1 with
| '\001' -> Connect
| '\002' -> Bind
| _ -> assert false
in
let destport =
let h = Char.code (Bytes.get bytes 2) in
let l = Char.code (Bytes.get bytes 3) in
(h lsl 8) lor l
in
let (userid, last_null_byte) =
match read_null_terminated_string bytes 8 with
| ("", x) -> (None, x)
| (s, x) -> (Some s, x)
in
let destip =
let parts = [ Bytes.get bytes 4
; Bytes.get bytes 5
; Bytes.get bytes 6
; Bytes.get bytes 7 ]
|> List.map int_of_char
in
match parts with
| 0 :: 0 :: 0 :: x :: [] when x > 0 ->
let (domain_name, _) =
read_null_terminated_string bytes last_null_byte
in
DomainName domain_name
| p1 :: p2 :: p3 :: p4 :: [] ->
IPAddress (p1, p2, p3, p4)
| _ -> assert false
in
let version =
match (destip, Bytes.get bytes 0) with
| (IPAddress _, '\004') -> Socks4
| (DomainName _, '\004') -> Socks4a
| _ -> assert false
in
{ version
; command
; destport
; destip
; userid }
let () =
let packet = Bytes.of_string "\004\001\000\x50\xc0\xa8\000\001\000" in
let _req = of_bytes packet in
()
@renatoalencar
Copy link
Author

For matching the response code

type result_code =
  | RequestGranted
  | RequestRejected
  | RequestRejected_Identd
  | UserIdNotMatching

let result_code_of_char = function
  | '\090' -> RequestGranted
  | '\091' -> RequestRejected
  | '\092' -> RequestRejected_Identd
  | '\093' -> UserIdNotMatching
  | _ -> assert false

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment