Reference: https://www.ietf.org/rfc/rfc1928.txt
只是部分章节的翻译
3. TCP客户端的连接流程
当某一基于TCP的客户端想要经由防火墙(取决于具体实现)与远端对象建立连接时,它必须首先开启与SOCKS服务器上对应端口的TCP连接。通常情况下SOCKS服务器监听的TCP端口号是1080. 若连接请求成功,客户端将进入授权模式协商阶段,随后使用所选的模式进行授权,继而开始连接。SOCKS服务器对请求进行评估,继而建立指定的连接或拒绝连接。
除非另有说明,下文数据报中提及的十进制数字均为对应字段的字节长度。当某个字节必须是一个具体数值时,以X’hh’来表示这一字段的具体值。当使用’Variable'(可变长)时,表示这一字段拥有可变长度的内容,具体长度通过一个关联的长度字段表示,或根据某钟具体的数据类型字段决定。
客户端连接至服务器并发送一个标识符/方法选择信息:
+----+----------+----------+
|VER | NMETHODS | METHODS |
+----+----------+----------+
| 1 | 1 | 1 to 255 |
+----+----------+----------+
其中,字段VER设置为X’05’以表明此协议的版本。NMETHODS字段包含了METHODS字段中所填鉴权方法(method)的个数,以字节为长度单位——METHODS中的一个方法标识符占一个字节。
服务器从METHODS给定的方法中选择一种,回传一个方法选择消息:
+----+--------+
|VER | METHOD |
+----+--------+
| 1 | 1 |
+----+--------+
如果选择的方法值为X’FF’,则表明服务器不接受客户端能提供的任一种鉴权方法,客户端必须在收到此消息后断开连接。
METHOD中可以定义的方法为:
- X’00’ 无需鉴权方法
- X’01’ GSSAPI
- X’02’ 用户名/密码
- X’03’ 至 X’7F’ IANA指定
- X’80’ 至 X’FE’ 私有方法保留
- X’FF’ 没有可接受的方法
随后,服务器与客户端进入协议特定的子协商(sub-negotiation)阶段。
协议特定的子协商过程在描述于不同备忘录中。
本协议新方法的开发人员必须向IANA申请一个新的METHOD号……(省略两段合规相关的话)
4. 请求(Requests)
一旦完成了协议特定的子协商过程,客户端便发送请求细节。如果方法的协商包括了完整性检查或保密相关的封装,那么这些请求必须以指定方式进行相同的封装。
SOCKS的请求,其格式如下:
+----+-----+-------+------+----------+----------+
|VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | X'00' | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
其中:
- VER: 协议版本号:X’05’
- CMD
- CONNECT X’01’
- BIND X’02’
- UDP ASSOCIATE X’03’
- RSV 保留字段
- ATYP 地址类型
- IP V4 地址: X’01’
- 域名: X’03’
- IP V6 地址: X’04’
- DST.ADDR 期望的目标地址
- DST.PORT 期望的目标端口号,以网络字节序表示
5. 地址(Addressing)
一个地址字段(DST.ADDR, BND.ADDR)的具体类型由ATYP字段的值来表示:
- X’01’ 为4个字节表示的IPV4地址
- X’03’ 表示一个域名,地址字段中的第一个字节表示后续字符的总长度(字节数),地址字段不包含NUL结束符
- X’04’ 为16字节表示的IPV6地址
6. 回复(Replies)
一旦SOCKS客户端与服务器建立连接,SOCKS请求即被发送,随即进行鉴权方式的协商。服务器端对请求进行识别,并以下属形式回复:
+----+-----+-------+------+----------+----------+
|VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | X'00' | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
其中:
- VER:协议版本号: X’05’
- REP:回复字段:
- X’00’ 成功
- X’01’ 一般SOCKS服务器错误
- X’02’ 规则集(ruleset)不允许该连接
- X’03’ 网络不可用
- X’04’ 主机不可达
- X’05’ 连接被拒绝
- X’06’ TTL超时
- X’07’ 不支持该命令
- X’08’ 不支持该地址类型
- X’09’ 至 X’FF’ 尚未指定
- RSV:保留字段
- ATYP:后续地址的类型
- IPV4地址: X’01’
- 域名:X’03’
- IPV6地址: X’04’
- BND.ADDR: 服务器绑定地址
- BND.PORT:服务器绑定端口号,网络字节序表示
CONNECT
对CONNECT(连接)请求的回复中,BND.PORT包含服务器指派的用于连接远程主机的端口号,BND.ADDR为对应的IP地址。指派的BND.ADDR通常与客户端请求的目标地址不同,因为目标主机可能拥有多组地址(multi-homed)。预期情况下SOCKS服务器使用DST.ADDR与DST.PORT,连同客户端的地址、端口号,一起确定一个CONNECT请求。
BIND
BIND(绑定)请求通常用在那些客户端需要接受(accept)服务器连接的协议中。FTP便是一个令人熟知的例子,它主要使用客户端->服务器连接来发送命令、报告状态,但同时也会用服务器->客户端连接来传输数据(e.g. LS, GET, PUT)。
预期情况下,客户端一侧的应用协议仅在使用过CONNECT请求完成一个主连接的情况下,再使用BIND请求请求一个二次连接。预期情况下SOCKS服务器使用DST.ADDR与DST.PORT来确定一个BIND请求。
在一次BIND操作过程中,SOCKS服务器将给客户端回复两条消息。第一条将会在服务器建立连接并绑定一个新的socket口时发送。BND.PORT字段包含了一个端口号,该端口号是SOCKS服务器指派的用于接收传入连接的。BND.ADDR包含了对应的IP地址。通常情况下,客户端根据这些信息来通知(经由主连接)应用程序服务器(目标主机)这一地址的存在。第二次应答只发生与预期的传入连接成功或失败发生时。在二次应答中,BND.PORT及BND.ADDR字段包含了目标主机(connecting host)的地址和端口号。
UDP ASSOCIATE
UDP ASSOCIATE请求用作UDP中继过程中建立关联以处理UDP数据报文。DST.ADDR和DST.PORT字段包含了客户端期望关联的,用于发送UDP数据报的目标地址及端口号。服务器或能使用这一信息来限制连接请求。如果客户端在UDP ASSOCIATE阶段尚不能拥有这些信息,则它必须使用全零的端口号及地址。
当UDP ASSOCIATE请求到达的TCP连接终止时,UDP关联终止。
在对UDP ASSOCIATE请求的应答中,BND.PORT和BND.ADDR字段表明了客户端必须发送UDP中继请求的端口号/地址。
应答处理
当应答失败时(REP值不等于X’00’),SOCKS服务器必须在发送应答消息后立即关闭TCP连接。这一操作必须在检测到异常状态后不超过10秒时执行。
如果应答码标识为成功(REP==X’00’),并且请求既不是BIND也不是CONNECT,则客户端可以开始传输数据。如果协商的传输方式包含对于数据完整性,鉴权或保密性的封装要求,其传输的数据也应按此要求封装。类似操作也许是实施于反向的传输过程中。
7. UDP客户端的操作
一个UDP客户端必须将自己的数据报发送至指定的UDP中继服务器,其端口号是在UDP ASSOCIATE应答中由BND.PORT字段指定的端口号。其数据报中鉴权、一致性及加密性封装必须与协商时表明的相同。每个UDP数据报包含一个UDP请求头:
+----+------+------+----------+----------+----------+
|RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA |
+----+------+------+----------+----------+----------+
| 2 | 1 | 1 | Variable | 2 | Variable |
+----+------+------+----------+----------+----------+
该请求头中包含的字段为:
- RSV:保留字段 X’0000′
- FRAG:当前分段号
- ATYP:下述地址类型:
- IPV4地址: X’01’
- 域名:X’03’
- IPV6地址:X’04’
- DST.ADDR:期望的目标地址
- DST.PORT:期望的目标端口号
- DATA:用户数据
当一个UDP中继服务器决定中继一个UDP数据报时,它静默地执行上述操作,不通知任何客户端。类似的,它会丢弃它无法中继的数据报。当一个UDP中继服务器收到远程主机传来的应答数据报时,它必须使用上述报头格式封装数据报,若有必要同时将依据之前协商的鉴权方法再封装。
UDP中继服务器必须从SOCKS服务器中获取客户端期望发送数据报的IP地址,客户端用该地址加上UDP ASSOCIATE应答中提及的BND.PORT来发送数据报。它必须丢弃任何在关联之外的,从源IP地址传输过来的数据报。
FRAG字段表明了这个数据报是不是数据报段中的一部分。如果分段实施,则最高阶的位(bit)表明是否分段结束,值X’00’表明这是个独立的数据报。在1与127之间的数值表明这个数据报在分段中的顺序。每个接收者必须实现一个与之关联的REASSEMBLY QUEUE(重组队列)与REASSEMBLY TIMER(重组计时器)。重组队列必须在计时器超时时,亦或是传入数据报FRAG字段值小于当前最大字段值时,丢弃那些尚未组合成功的数据报,并且重新初始化。重组计时器必须设置不超过5秒的超时值。一般建议,只要条件允许,不启用分段。
分段的实施时可选的;一个不允许分段操作的实现,它必须丢弃任何FRAG值不为X’00’的任何数据报。
一个SOCKS-可感知的UDP程序接口必须报告一个可用的UDP数据报缓存长度,且其必须小于操作系统允许的空间:
- 若ATYP==X’01’ < 10 + method_dependent 字节
- 若ATYP==X’03’ : < 262 + method_dependent 字节
- 若ATYP==X’04’ : < 20 + method_dependent 字节