Protocols
One set of credentials works across every protocol. There are no separate accounts and no per-protocol pricing, and targeting behaves identically on all of them.
| Protocol | Port | Notes |
|---|---|---|
| HTTP | 8000 |
|
| HTTPS | 8000 |
via CONNECT |
| SOCKS5 | 1080 |
including CONNECT to any port |
| SOCKS5 UDP | 1080 |
via UDP ASSOCIATE |
| HTTP/3 (QUIC) | 1080 |
over SOCKS5 UDP ASSOCIATE |
| STUN | 1080 |
over SOCKS5 UDP ASSOCIATE |
HTTP and HTTPS
Section titled “HTTP and HTTPS”SOCKS5
Section titled “SOCKS5”Both SOCKS5 modes work. They differ in who resolves the hostname.
https://api.ipify.orgWe resolve the hostname at the exit. Two things follow, and both usually matter:
- Your local resolver never sees what you are visiting.
- You get the DNS answer a user in that location would get. Many large sites return different addresses per region, so resolving locally can send you to the wrong edge and undo your country targeting.
https://api.ipify.orgYour machine resolves the hostname and sends us an IP address. Use it when you are connecting to an address rather than a name, or when your client does not offer the remote-resolution mode.
In a proxy URL these are the socks5h:// and socks5:// schemes:
Most libraries follow the same naming. In Python requests, socks5h:// sends
the hostname and socks5:// does not, which is the same distinction under a
different spelling.
SOCKS5 also gives you CONNECT to arbitrary ports, not just 80 and 443. Useful
for anything that is not a web server.
The UDP protocols
Section titled “The UDP protocols”SOCKS5 UDP, HTTP/3 and STUN all run over SOCKS5 UDP ASSOCIATE (RFC 1928
command 0x03). curl cannot drive any of them, so these need real code.
They run on the same host and port as SOCKS5, with the same credentials.
Two rules that catch everyone
Section titled “Two rules that catch everyone”Keep the control connection open. RFC 1928 scopes the association to the TCP control socket. Close it and the relay is torn down. This is the single most common SOCKS5 UDP mistake.
The relay address is not the proxy address. The associate reply names a worker that will carry your datagrams, and you send UDP straight to it. Your egress firewall has to allow outbound UDP to arbitrary high ports.
The helper
Section titled “The helper”import socket, struct
def socks5_udp_associate(proxy_host, proxy_port, user, password, timeout=30): """Returns (ctrl_socket, (relay_ip, relay_port)).
KEEP ctrl OPEN for the life of the association. Closing it tears the relay down. """ def rd(s, n): b = b"" while len(b) < n: p = s.recv(n - len(b)) if not p: raise IOError("proxy closed the control connection") b += p return b
ctrl = socket.create_connection((proxy_host, proxy_port), timeout=timeout) ctrl.settimeout(timeout) ctrl.sendall(b"\x05\x01\x02") # offer user/password if rd(ctrl, 2)[1] != 0x02: raise IOError("no user/password auth offered") u, p = user.encode(), password.encode() ctrl.sendall(bytes([0x01, len(u)]) + u + bytes([len(p)]) + p) if rd(ctrl, 2)[1] != 0x00: raise IOError("credentials rejected")
ctrl.sendall(b"\x05\x03\x00\x01\x00\x00\x00\x00\x00\x00") # UDP ASSOCIATE r = rd(ctrl, 4) if r[1] != 0x00: raise IOError(f"UDP ASSOCIATE refused, rep=0x{r[1]:02x}") atyp = r[3] if atyp == 0x01: relay_ip = socket.inet_ntoa(rd(ctrl, 4)) elif atyp == 0x04: relay_ip = socket.inet_ntop(socket.AF_INET6, rd(ctrl, 16)) else: relay_ip = rd(ctrl, rd(ctrl, 1)[0]).decode() relay_port = struct.unpack("!H", rd(ctrl, 2))[0] if relay_ip in ("0.0.0.0", "::"): relay_ip = proxy_host return ctrl, (relay_ip, relay_port)
def udp_wrap(host, port, payload): """SOCKS5 UDP request header + payload. The header names the FINAL target.""" try: addr = b"\x01" + socket.inet_aton(host) except OSError: h = host.encode(); addr = b"\x03" + bytes([len(h)]) + h return b"\x00\x00\x00" + addr + struct.pack("!H", port) + payload
def udp_unwrap(datagram): """Strip the SOCKS5 UDP header from a reply.""" atyp = datagram[3] off = 4 + (4 if atyp == 1 else 16 if atyp == 4 else 1 + datagram[4]) + 2 return datagram[off:]Sending a datagram
Section titled “Sending a datagram”import socket
ctrl, relay = socks5_udp_associate( "us-east.gw.rayobyte.com", 1080, "USERNAME", "PASSWORD-country-US")try: target = socket.gethostbyname("example.com") u = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) u.settimeout(5) u.sendto(udp_wrap(target, 20443, b"probe"), relay) print(udp_unwrap(u.recvfrom(65535)[0]).decode().strip())finally: ctrl.close()A standard RFC 5389 binding request, wrapped the same way. Point it at any STUN
server on UDP 19302 and read XOR-MAPPED-ADDRESS (0x0020) out of the reply.
Remember STUN attributes are padded to four bytes when you walk them.
HTTP/3
Section titled “HTTP/3”Install aioquic, then drive a QuicConnection yourself, wrapping every
outbound QUIC packet with udp_wrap and unwrapping every inbound one.
Reply codes
Section titled “Reply codes”See Errors & retries for the SOCKS5 reply codes and what to do about each.
Was this page helpful?
Thanks — that helps us fix it.