SOCKS5(RFC 1928)是廣泛部署的通用代理協議,支援無驗證、使用者名稱/密碼、GSSAPI 等多種認證機制,並提供 TCP 連線、BIND、UDP 轉發三種命令。本文依照 RFC 流程,逐步拆解協議封包格式、狀態機轉換,並提供可直接編譯的 C++ 實作範例,涵蓋連線建立、方法協商、認證處理、請求轉發、回應解析全流程。
VER=0x05, NMETHODS, METHODS...VER=0x05, METHOD(0x00無驗證、0x02帳密、0xFF不支援)VER=0x01, ULEN, UNAME, PLEN, PASS)VER=0x01, STATUS(0x00成功)CMD=0x01 CONNECT、目標位址類型、位址、埠)REP=0x00成功、綁定位址/埠)+----+----------+----------+
|VER | NMETHODS | METHODS |
+----+----------+----------+
| 1 | 1 | 1-255 |
+----+----------+----------+
METHODS 常見值:0x00 無驗證、0x02 帳密、0x80-0xFE 私有擴充。
+----+--------+
|VER | METHOD |
+----+--------+
| 1 | 1 |
+----+--------+
Client→Server: +----+------+----------+------+----------+
|VER | ULEN | UNAME | PLEN | PASS |
+----+------+----------+------+----------+
| 1 | 1 | 1-255 | 1 | 1-255 |
+----+------+----------+------+----------+
Server→Client: +----+--------+
|VER | STATUS |
+----+--------+
| 1 | 1 |
+----+--------+
注意:此處 VER=0x01,與主協議 0x05 不同。
+----+-----+-------+------+----------+----------+
|VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | 0x00 | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
CMD:0x01 CONNECT、0x02 BIND、0x03 UDP ASSOCIATEATYP:0x01 IPv4(4 位元組)、0x03 Domain(首位元組長度+名稱)、0x04 IPv6(16 位元組)+----+-----+-------+------+----------+----------+
|VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | 0x00 | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
REP 常見碼:0x00 成功、0x01 一般失敗、0x02 規則不允許、0x03 網路不可達、0x04 主機不可達、0x05 連線被拒、0x07 指令不支援、0x08 位址類型不支援。
bool CSocks5Proxy::Connect(LPCTSTR szServer, int nPort, LPCTSTR szUser, LPCTSTR szPass) {
// 1. 建立 TCP 連線至代理伺服器
if (!m_socket.Connect(szServer, nPort)) return false;
// 2. 方法協商
BYTE req[257] = {5}; // VER=5
req[1] = (szUser && *szUser) ? 2 : 1; // NMETHODS
req[2] = 0x00; // METHOD: NO AUTH
if (req[1] == 2) req[3] = 0x02; // METHOD: USER/PASS
if (!SendPacket(req, 3 + (req[1]==2))) return false;
BYTE rsp[2];
if (!RecvPacket(rsp, 2) || rsp[0]!=5 || rsp[1]==0xFF) return false;
// 3. 帳密認證
if (rsp[1] == 0x02) {
if (!szUser || !*szUser) return false;
int ulen = strlen(szUser), plen = strlen(szPass);
BYTE auth[513] = {1, (BYTE)ulen};
memcpy(auth+2, szUser, ulen);
auth[2+ulen] = (BYTE)plen;
memcpy(auth+3+ulen, szPass, plen);
if (!SendPacket(auth, 3+ulen+plen)) return false;
if (!RecvPacket(rsp, 2) || rsp[0]!=1 || rsp[1]!=0) return false;
}
// 4. 發送 CONNECT 請求(這裡僅示範 IPv4)
BYTE cmd[10] = {5, 1, 0, 1}; // VER=5, CMD=CONNECT, RSV=0, ATYP=IPv4
DWORD dwIP = inet_addr(m_szTargetIP); // 目標 IP
WORD wPort = htons(m_nTargetPort); // 目標埠
memcpy(cmd+4, &dwIP, 4);
memcpy(cmd+8, &wPort, 2);
if (!SendPacket(cmd, 10)) return false;
// 5. 解析回應
BYTE resp[256];
int n = RecvPacket(resp, 10); // 至少 10 位元組
if (n < 10 || resp[0]!=5 || resp[1]!=0) return false;
// 6. 連線成功,進入透明轉發迴圈...
return true;
}
WSAAsyncSelect/IOCP 實作高併發代理用戶端。ATYP=0x03/0x04 請求,解析 BND.ADDR 回應。SO_RCVTIMEO/SO_SNDTIMEO,失敗按 REP 碼決定重試策略。SOCKS5 協議結構清晰、擴充性強,掌握其封包格式與狀態機即可實作健壯的代理用戶端。本文提供的同步阻塞範例可作為教學與輕量級應用基礎;生產環境建議採用非同步架構並完善異常處理、日誌記錄與單元測試。