标题: 加密SOCKS5信道中防DNS泄露 创建: 2023-02-26 19:08 更新: 链接: https://scz.617.cn/network/202302261908.txt 本文从纯粹的技术原理角度简介加密SOCKS5信道中防DNS泄露的问题,不涉及其它。 初代SOCKS5协议本身不包含加密机制,但现实世界中有许多加密SOCKS5实现。现代浏 览器及相应Proxy插件普遍支持远程DNS解析,DNS泄露得到相当程度的阻止。但是, 这样不足以彻底阻止DNS泄露。比较理想的状态是本机所有DNS解析请求都走加密 SOCKS5信道,对此至少有两种现成的开源实现,Tor-DNS与DNS2SOCKS,自行放狗。 缺省情况下,Tor-DNS侦听53/UDP,收到DNS请求后做某种处理再发送到Tor SOCKS5 Proxy侦听的9050/TCP,由后者设法进行加密后的远程DNS解析,以此对付DNS泄露。 Tor-DNS用Go语言编写,第一次看Go代码,语法都不了解,全凭其他语言经验瞎猜。 起初不想猜,让ChatGPT给我翻译成Python,未能如愿,后来硬着头皮看Go代码,大 致看明白了。注意answer函数实现,里面有这么一段 -------------------------------------------------------------------------- buf[0] = 5 // VER: 5 if q.opcode == 0 // QUERY { var dn []byte if q.typ == 12 // PTR { buf[1] = 0xF1 // CMD: RESOLVE_PTR dn = []byte( ptr2ip( q.name ) ) } else // Any other record type { buf[1] = 0xF0 // CMD: RESOLVE dn = []byte( q.name ) } num := len(dn) buf[2] = 0 // RSV buf[3] = 3 // ATYP: DOMAINNAME buf[4] = byte( num ) for i, v := range dn { buf[5+i] = v } buf[5+num] = 0 // DST.PORT buf[6+num] = 0 // DST.PORT -------------------------------------------------------------------------- 没看过Tor的源码及文档,仅凭上述代码判断这是一种私有"Tor SOCKS5 DNS"协议, 域名解析请求报文格式如下 +------------+ | DST.ADDR | +----+------+-------+------+-----+------+----------+ |VER | CMD | RSV | ATYP | LEN | FQDN | DST.PORT | +----+------+-------+------+-----+------+----------+ | 5 | 0xF0 | 0 | 3 | X | ANY | 0 | +----+------+-------+------+-----+------+----------+ 参看 《RFC 1928意译版(非直译版)》 https://scz.617.cn/network/200503311423.txt CMD为非标0xF0,Tor SOCKS5 Proxy对此类请求报文做了扩展处理,此时DST.ADDR格 式是"len+str",其余字段如上图所示。下面是对www.youtube.com进行A记录查询时 的请求报文 -------------------------------------------------------------------------- 05 // +0x0 VER f0 // +0x1 CMD 00 // +0x2 RSV 03 // +0x3 ATYP 3表示DST.ADDR字段对应FQDN(全称域名) 0f // +0x4 LEN 77 77 77 2e 79 6f 75 74 // +0x5 FQDN www.youtube.com 75 62 65 2e 63 6f 6d 00 00 // +0x14 DST.PORT -------------------------------------------------------------------------- 相应的响应报文 +----+-----+-------+------+----------+----------+ |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | +----+-----+-------+------+----------+----------+ | 5 | 0 | 0 | 1 | Variable | 0 | +----+-----+-------+------+----------+----------+ -------------------------------------------------------------------------- 05 // +0x0 VER 00 // +0x1 REP 0表示成功,其余非0值对应不同的失败情形 00 // +0x2 RSV 01 // +0x3 ATYP 1表示BND.ADDR字段对应IPv4地址 ac d9 0e 6e // +0x4 BND.ADDR 网络字节序 172.217.14.110 00 00 // +0x8 BND.PORT -------------------------------------------------------------------------- Tor-DNS收到上述响应报文后再组织一个符合DNS协议的A记录查询响应报文,发送给 DNS Client。 目前stunnel不支持UDP转发,但Tor-DNS可与stunnel配合,只有一种合理解释, stunnel也支持私有"Tor SOCKS5 DNS"协议,我找到了相应代码。 -------------------------------------------------------------------------- /* * stunnel-5.68\src\protocol.c */ NOEXPORT void socks5_server ( CLI *c ) { if ( socks.req.ver != 0x05 ) { ... } // CONNECT else if ( socks.req.cmd == 0x01 ) { ... } /* * Line 488 * RESOLVE (a TOR extension) */ else if ( socks.req.cmd == 0xf0 ) { s_ssl_read( c, &host_len, sizeof host_len ); host_name = str_alloc( (size_t)host_len+1 ); s_ssl_read( c, host_name, host_len ); host_name[host_len] ='\0'; s_ssl_read( c, &port_number, 2 ); port_name = str_printf( "%u", ntohs( port_number ) ); if ( hostport2addr( &addr, host_name, port_name, 0 ) ) { -------------------------------------------------------------------------- stunnel只检查了socks.req.cmd是否为0xf0,未检查socks.req.atyp是否为3,并且 不支持socks.req.cmd为0xf1;换句话说,stunnel部分支持私有"Tor SOCKS5 DNS"协 议,但对于防DNS泄露,stunnel足矣。 Go可以跨平台交叉编译,Tor-DNS可以编译成PE。 DNS2SOCKS不同于Tor-DNS,前者是一种更通用的防DNS泄露方案,不必与Tor SOCKS5 Proxy或stunnel相配合,可与任意加密SOCKS5 Proxy相配合,DNS2SOCKS要求指定一 个支持TCP DNS的DNS Server。其技术原理是,侦听53/UDP,收到UDP DNS请求报文后 将之转换成TCP DNS请求报文,通过SOCKS5发送TCP DNS请求;通过SOCKS5收TCP DNS 响应,将之转换成UDP DNS响应返回给DNS Client。所有的SOCKS5 Proxy都支持TCP转 发,但不见得支持UDP转发,后者实现起来比较复杂。DNS2SOCKS可与Tor、stunnel配 合,但未使用私有"Tor SOCKS5 DNS"协议。DNS2SOCKS不利之处在于TCP DNS效率不如 UDP DNS,容易超时。若与Tor、stunnel配合,建议用Tor-DNS,后者效率高得多。 Windows版可用Visual C++ 2015 Express Edition编译,作者提供了预编译的PE;同 一份源码可用GCC编译Linux版。