Skip to content

☆ RFC 1928意译版(非直译版)

http://www.ietf.org/rfc/rfc1928.txt https://scz.617.cn/network/200503311423.txt

SOCKS协议位于传输层(TCP/UDP等)与应用层之间,因而显然地位于网络层(IP)之上。 诸如IP层报文转发、ICMP协议等等都因太低层而与SOCKS协议无关。

SOCKS 4不支持认证、UDP协议以及远程解析FQDN。SOCKS 5支持。

SOCKS Server缺省侦听在1080/TCP口。这是SOCKS Client连接到SOCKS Server之后发 送的第一个报文:

+----+----------+----------+ |VER | NMETHODS | METHODS | +----+----------+----------+ | 1 | 1 | 1 to 255 | +----+----------+----------+

对于SOCKS 5,VER字段为0x05,版本4对应0x04。NMETHODS字段指定METHODS域的字节 数。不知NMETHODS可以为0否,看上图所示,可取值[1,255]。METHODS字段有多少字 节(假设不重复),就意味着SOCKS Client支持多少种认证机制。

SOCKS Server从METHODS字段中选中一个字节(一种认证机制),并向SOCKS Client发 送响应报文:

+----+--------+ |VER | METHOD | +----+--------+ | 1 | 1 | +----+--------+

目前可用METHOD值有:

0x00 NO AUTHENTICATION REQUIRED(无需认证) 0x01 GSSAPI 0x02 USERNAME/PASSWORD(用户名/口令认证机制) 0x03-0x7F IANA ASSIGNED 0x80-0xFE RESERVED FOR PRIVATE METHODS(私有认证机制) 0xFF NO ACCEPTABLE METHODS(完全不兼容)

如果SOCKS Server响应以0xFF,表示SOCKS Server与SOCKS Client完全不兼容, SOCKS Client必须关闭TCP连接。认证机制协商完成后,SOCKS Client与 SOCKS Server进行认证机制相关的子协商,参看其它文档。为保持最广泛兼容性, SOCKS Client、SOCKS Server必须支持0x01,同时应该支持0x02。

认证机制相关的子协商完成后,SOCKS Client提交转发请求:

+----+-----+-------+------+----------+----------+ |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | +----+-----+-------+------+----------+----------+ | 1 | 1 | X'00' | 1 | Variable | 2 | +----+-----+-------+------+----------+----------+

VER 对于版本5这里是0x05

CMD 可取如下值:

        0x01    CONNECT
        0x02    BIND
        0x03    UDP ASSOCIATE

RSV 保留字段,必须为0x00

ATYP 用于指明DST.ADDR域的类型,可取如下值:

        0x01    IPv4地址
        0x03    FQDN(全称域名)
        0x04    IPv6地址

DST.ADDR CMD相关的地址信息,不要为DST所迷惑

        如果是IPv4地址,这里是big-endian序的4字节数据

        如果是FQDN,比如"www.nsfocus.net",这里将是:

        0F 77 77 77 2E 6E 73 66 6F 63 75 73 2E 6E 65 74

        注意,没有结尾的NUL字符,非ASCIZ串,第一字节是长度域

        如果是IPv6地址,这里是16字节数据。

DST.PORT CMD相关的端口信息,big-endian序的2字节数据

SOCKS Server评估来自SOCKS Client的转发请求并发送响应报文:

+----+-----+-------+------+----------+----------+ |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | +----+-----+-------+------+----------+----------+ | 1 | 1 | X'00' | 1 | Variable | 2 | +----+-----+-------+------+----------+----------+

VER 对于版本5这里是0x05

REP 可取如下值:

        0x00        成功
        0x01        一般性失败
        0x02        规则不允许转发
        0x03        网络不可达
        0x04        主机不可达
        0x05        连接拒绝
        0x06        TTL超时
        0x07        不支持请求包中的CMD
        0x08        不支持请求包中的ATYP
        0x09-0xFF   unassigned

RSV 保留字段,必须为0x00

ATYP 用于指明BND.ADDR域的类型

BND.ADDR CMD相关的地址信息,不要为BND所迷惑

BND.PORT CMD相关的端口信息,big-endian序的2字节数据

1) CONNECT命令

假设CMD为CONNECT,SOCKS Client、SOCKS Server之间通信的相关四元组是:

SOCKSCLIENT.ADDR,SOCKSCLIENT.PORT,SOCKSSERVER.ADDR,SOCKSSERVER.PORT

一般SOCKSSERVER.PORT是1080/TCP。

CONNECT请求包中的DST.ADDR/DST.PORT指明转发目的地。SOCKS Server可以靠 DST.ADDR、DST.PORT、SOCKSCLIENT.ADDR、SOCKSCLIENT.PORT进行评估,以决定建立 到转发目的地的TCP连接还是拒绝转发。

假设规则允许转发并且成功建立到转发目的地的TCP连接,相关四元组是:

BND.ADDR,BND.PORT,DST.ADDR,DST.PORT

此时SOCKS Server向SOCKS Client发送的CONNECT响应包中将指明BND.ADDR/BND.PORT。 注意,BND.ADDR可能不同于SOCKSSERVER.ADDR,SOCKS Server所在主机可能是多目( multi-homed)主机。

假设拒绝转发或未能成功建立到转发目的地的TCP连接,CONNECT响应包中REP字段将 指明具体原因。

响应包中REP非零时表示失败,SOCKS Server必须在发送响应包后不久(不超过10s)关 闭与SOCKS Client之间的TCP连接。

响应包中REP为零时表示成功。之后SOCKS Client直接在当前TCP连接上发送待转发数 据。

2) BIND命令

假设CMD为BIND。这多用于FTP协议,FTP协议在某些情况下要求FTP Server主动建立 到FTP Client的连接,即FTP数据流。

FTP Client - SOCKS Client - SOCKS Server - FTP Server

a. FTP Client试图建立FTP控制流。SOCKS Client向SOCKS Server发送CONNECT请求, 后者响应请求,最终FTP控制流建立。

CONNECT请求包中指明FTPSERVER.ADDR/FTPSERVER.PORT。

b. FTP Client试图建立FTP数据流。SOCKS Client建立新的到SOCKS Server的TCP连 接,并在新的TCP连接上发送BIND请求。

BIND请求包中仍然指明FTPSERVER.ADDR/FTPSERVER.PORT。SOCKS Server应该据此 进行评估。

SOCKS Server收到BIND请求,创建新套接字,侦听在AddrA/PortA上,并向SOCKS Client发送第一个BIND响应包,包中BND.ADDR/BND.PORT即AddrA/PortA。

c. SOCKS Client收到第一个BIND响应包。FTP Client通过FTP控制流向FTP Server发 送PORT命令,通知FTP Server应该主动建立到AddrA/PortA的TCP连接。

d. FTP Server收到PORT命令,主动建立到AddrA/PortA的TCP连接,假设TCP连接相关 四元组是:

AddrB,PortB,AddrA,PortA

e. SOCKS Server收到来自FTP Server的TCP连接请求,向SOCKS Client发送第二个 BIND响应包,包中BND.ADDR/BND.PORT即AddrB/PortB。然后SOCKS Server开始转 发FTP数据流。

下面是一些讨论记录:

scz

为什么需要发送第二个BIND响应包,指明AddrB/PortB的意义何在。

knightmare@apue

指明AddrB/PortB的意义在于,FTP Client出于安全考虑,会检查FTP数据流的源IP、 源端口,比如FTP数据流的源端只允许是FTPSERVER.ADDR/20。

scz

knightmare的答案是正确的,但我的疑惑可能源于我对SOCKS协议的错误理解,以至 提出一个产生歧义的问题。事实上应该查看David Koblas的原始文档以理解BIND请求 的全过程。前面关于FTP数据流的描述部分已做了修正,因此看不出提问的缘由了。

3) UDP ASSOCIATE命令

假设CMD为UDP ASSOCIATE。此时DST.ADDR与DST.PORT指明发送UDP报文时的源IP、源 端口,而不是UDP转发目的地,SOCKS Server可以据此进行评估以决定是否进行UDP转 发。如果SOCKS Client发送UDP ASSOCIATE命令时无法提供DST.ADDR与DST.PORT,则 必须将这两个域置零。

下面是一些讨论记录:

scz

什么情况下SOCKS Client发送UDP ASSOCIATE命令,又无法提供DST.ADDR与DST.PORT, 或者说出于什么考虑才需要刻意将这两个域置零。有现实例子存在吗。

[email protected]

考虑这种情况:

Application Client - SOCKS Client - NAT - SOCKS Server - Application Server

SOCKS Client在UDP ASSOCIATE命令中指明DST.ADDR/DST.PORT,SOCKS Server靠这些 信息决定是否转发某个UDP报文。上图中SOCKS Client与SOCKS Server之间有NAT,前 者无法预知UDP报文经过NAT后源IP、源端口会变成什么样,但肯定会变,因此前者无 法提前在UDP ASSOCIATE命令中指明DST.ADDR/DST.PORT,如果强行指定非零值,后者 会检测到待转发UDP报文的源IP、源端口与DST.ADDR/DST.PORT不匹配而拒绝转发。针 对这种情况,RFC 1928建议SOCKS Client将DST.ADDR/DST.PORT置零,SOCKS Server 此时不再检查待转发UDP报文的源IP、源端口。

在一条TCP连接上SOCKS Client向SOCKS Server发送了UDP ASSOCIATE命令,后续UDP 转发要求此TCP连接继续维持,此TCP连接关闭时相应的UDP转发也将中止。换句话说, UDP转发必然伴随着一个TCP连接,这将消耗额外的资源。

SOCKS Server向SOCKS Client发送UDP ASSOCIATE响应包,BND.ADDR/BND.PORT指明 SOCKS Client应向哪里发送待转发UDP报文。

对于UDP转发,SOCKS Client发送出去的UDP数据区如下:

+----+------+------+----------+----------+----------+ |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | +----+------+------+----------+----------+----------+ | 2 | 1 | 1 | Variable | 2 | Variable | +----+------+------+----------+----------+----------+

RSV 保留字段,必须为0x0000

FRAG Current fragment number

        0x00        这是一个非碎片的SOCKS UDP报文
        0x01-0x7F   SOCKS碎片序号
        0x80-0xFF   最高位置1表示碎片序列结束,即这是最后一个SOCKS碎片

ATYP 用于指明DST.ADDR域的类型,可取如下值:

        0x01    IPv4地址
        0x03    FQDN(全称域名)
        0x04    IPv6地址

DST.ADDR 转发目标地址

DST.PORT 转发目标端口

DATA 原始UDP数据区

SOCKS Server静静地为SOCKS Client进行UDP转发,并不通知后者转发完成还是被拒 绝。

FRAG用于支持SOCKS碎片。SOCKS碎片接收方一般实现有重组队列与重组定时器。假设 重组定时器超时或者低序SOCKS碎片后于高序SOCKS碎片到达重组队列,此时必须重置 重组队列。重组定时器不得小于5秒。应该尽可能地避免出现SOCKS碎片。

是否支持SOCKS碎片是可选的,如果一个SOCKS实现不支持SOCKS碎片,则必须丢弃所 有接收到的SOCKS碎片,即那些FRAG字段非零的SOCKS UDP报文。

由于SOCKS实现在支持UDP转发时会在原始UDP数据区前增加一个SOCKS协议相关的头, 因此为UDP数据区分配空间时要为这个头留足空间:

ATYP 头占用字节 原因 0x01 10 IPv4地址占4字节,4+6=10 0x03 262 长度域是一个字节,因此最大0xFF,1+255+6=262 0x04 20 这里我怀疑是笔误,IPv6地址占16字节,16+6=22

我怀疑RFC 1928这里有笔误,写信询问[email protected][email protected]去了。