Understanding SOCKS5 in C: Greeting and CONNECT
SOCKS5 is an application-layer proxy protocol defined by RFC 1928. Unlike a basic socket server, it negotiates an authentication method, receives a command describing a destination, opens that connection on the client’s behalf, and then relays bytes in both directions.
This study builds on the socket lifecycle example and the TCP stream-framing article.
The Conversation
- The client sends a greeting containing the SOCKS version and authentication methods it supports.
- The server chooses one method or rejects the client.
- The client sends a command such as CONNECT with a destination address and port.
- The server attempts the connection and returns a status reply.
- After success, the server relays the TCP stream until either side closes.
Client Greeting
A client that supports only “no authentication” sends three bytes:
05 01 00
│ │ └─ method 0x00: no authentication
│ └──── one method follows
└─────── SOCKS version 5
If the server accepts that method, it replies 05 00. A reply of 05 ff means no offered method is acceptable.
Read and Select a Method
#include <stddef.h>
#include <stdint.h>
#include <sys/socket.h>
int recv_exact(int fd, void *buffer, size_t length);
int send_all(int fd, const void *buffer, size_t length);
int negotiate_no_auth(int client_fd)
{
uint8_t header[2];
if (recv_exact(client_fd, header, sizeof(header)) != 1)
return -1;
if (header[0] != 0x05 || header[1] == 0)
return -1;
uint8_t methods[255];
if (recv_exact(client_fd, methods, header[1]) != 1)
return -1;
uint8_t selected = 0xff;
for (uint8_t i = 0; i < header[1]; ++i) {
if (methods[i] == 0x00) {
selected = 0x00;
break;
}
}
const uint8_t reply[2] = {0x05, selected};
if (send_all(client_fd, reply, sizeof(reply)) < 0)
return -1;
return selected == 0x00 ? 0 : -1;
}
The recv_exact() and send_all() helpers are the same stream-safe loops used in the TCP framing article.
The CONNECT Request
+-----+-----+-------+------+----------+----------+
| VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
+-----+-----+-------+------+----------+----------+
| 1 | 1 | X'00' | 1 | Variable | 2 |
+-----+-----+-------+------+----------+----------+
VERmust be0x05.CMDis0x01for CONNECT.RSVis reserved and must be zero.ATYPis0x01for IPv4,0x03for a domain name, or0x04for IPv6.DST.PORTis an unsigned 16-bit port in network byte order.
Parse an IPv4 CONNECT Destination
#include <arpa/inet.h>
#include <stdint.h>
#include <string.h>
struct socks5_ipv4_target {
struct in_addr address;
uint16_t port;
};
int read_ipv4_connect(int fd, struct socks5_ipv4_target *target)
{
uint8_t request[10];
if (recv_exact(fd, request, sizeof(request)) != 1)
return -1;
if (request[0] != 0x05 || request[1] != 0x01 ||
request[2] != 0x00 || request[3] != 0x01)
return -1;
memcpy(&target->address.s_addr, &request[4], 4);
uint16_t network_port;
memcpy(&network_port, &request[8], 2);
target->port = ntohs(network_port);
return target->port == 0 ? -1 : 0;
}
This deliberately accepts only IPv4 CONNECT requests. Domain names and IPv6 have different lengths and must be parsed separately. Never cast arbitrary bytes to larger integer pointers: copying with memcpy() avoids alignment and aliasing problems.
Server Reply
The reply has the same address shape as the request. The second byte reports the result:
0x00— succeeded0x01— general server failure0x02— connection not allowed0x03— network unreachable0x04— host unreachable0x05— connection refused0x07— command not supported0x08— address type not supported
On success, the address and port in the reply identify the local endpoint used for the outbound connection—not the destination copied from the request.
What a Complete Proxy Still Needs
- Resolve and connect to allowed destinations without creating DNS or internal-network bypasses.
- Reject unsupported commands and address types with the correct status.
- Relay traffic in both directions while handling partial writes, half-closes, and backpressure.
- Apply authentication or strict source-address controls.
- Set connection and idle timeouts, descriptor limits, and maximum concurrent sessions.
- Bind only to an intentional interface. Never expose an unauthenticated teaching proxy to the internet.
dispelled