slug: shadowsocks-kai-yuan-tao-lun-he-yuan-ma-jie-xi datepublished: 2015-08-24T04:00:00 dateupdated: 2019-02-25T00:34:13 tags: Tech Ideas draft: true –-

题图:https://cipherpoint.com/tag/encryption/


在互联网上观察了几日有关 Shadowsocks 开源部分2.8.2版本以后的更新和讨论, 几乎没有看到什么. V2EX的SS版块可能有一些讨论本人没有1000日的注册账户所以无法围观, 但是V2EX作为一个备案的网站, 也肯定冒着极大的风险不会顶风作案继续开发SS的.

在前两天的博客里提到SS-RSS的作者接手, 然而闭源这段时间也是没法信任和再利用的.

Shadowsocks源代码观察

目前观察到还有些价值的分析post如下:

shadowsocks源码解析| Summer Space, Summer Mind

Shadowsocks 源码解释- Hello YC

Shadowsocks源码阅读笔记【1】——语言相关| 张学程

以及C#编写一篇:

shadowsocks-csharp 源码分析

这些文章都有些时候了而且并没有展开. 其实SS的源代码, 引用很多人的观点, 相当高效而且很Pythonic值得学习, 特别是现在SS开源部分可能以及转入地下开发的情况下, 有不少人也在G+上回复表示对源代码有兴趣. 昨日找到了这个GitHub Repo, 是关于SS源码分析的. 刚刚开始并且兴许会有发展. 建议作者开了一个Gitter交流的频道: Gitter

欢迎加入讨论!

项目的README原文:

shadowsocks源码分析

此份代码仅用作学术交流用途,其他行为带来的后果本人概不负责

This copy of code is for study of python only

项目结构:

asyncdns.py 用于处理dns请求

common.py

daemon.py,提供daemon(守护进程)运行机制

encrypt.py,处理Shadowsocks协议的加密解密

eventloop.py,事件循环,使用select、poll、epoll、kequeue实现IO复用,作者将三种底层实现包装成一个类Eventloop

local.py讲,在本地(客户端)运行的程序

lru_cache.py,作者实现的一个基于LRU的Key-Value缓存

server.py,在远程服务端运行的程序

tcprelay.py,实现tcp的转达,用在远程端中使远程和dest连接

udprelay.py,实现udp的转达,用于local端处理local和 客户器端的SOCKS5协议通信,用于local端和远程端Shadowsocks协议的通信;用于远程端与local端Shadowsocks协议的通信,用于远程端和dest端(destination)的通信

11、utils.py 工具函数

代码质量相当的高,感觉都能达到重用的级别。而且由于作者设计的思想是,一个配置文件,同一段程序,在本地和远程通用,所以其中的代码,常常能够达到一个函数,在本地和服务器有不同的功能这样的效果。


核心:

eventloop.py,udprelay.py,tcprelay.py,asyndns.py

eventloop使用select、epoll、kqueue等IO复用实现异步处理。优先级为epoll>kqueue>select。Eventloop将三种复用机制的add,remove,poll,addhandler,remvehandler接口统一起来,程序员只需要使用这些函数即可,不需要处理底层细节。

后三个文件分别实现用来处理udp的请求,tcp的请求,dns的查询请求,并且将三种请求的处理包装成handler。对于tcp,udp的handler,它们bind到特定的端口,并且将socket交给eventloop,并且将自己的处理函数加到eventloop的handlers;对于dns的handler,它接受来自udp handler和tcp handler的dns查询请求,并且向远程dns服务器发出udp请求;

当eventloop监测到socket的数据,程序就将所有监测到的socket和事件交给所有handler去处理,每个handler通过socket和事件判断自己是否要处理该事件,并进行相对的处理:

当local收到udprelay handler绑定的端口的事件:

说明客户端发来请求,local对SOCKS5协议的内容进行处理之后经过加密转发给远程;

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

trim->

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

->encrypt

+-------+--------------+ | IV | PAYLOAD | +-------+--------------+ | Fixed | Variable | +-------+--------------+

当local新建的socket收到连接请求时:

说明远程向local发送结果,此时对信息进行解密,并且对shadowsocks协议进行适当加工,发回给客户端

+-------+--------------+ | IV | PAYLOAD | +-------+--------------+ | Fixed | Variable | +-------+--------------+

->decrypt

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

->add

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

当远程端收到udp handler绑定的端口的事件:

说明local端发来请求,远程端对信息进行解密并根据dest服务器/端口的协议类型对其发出tcp连接或者udp连接;

+-------+--------------+ | IV | PAYLOAD | +-------+--------------+ | Fixed | Variable | +-------+--------------+

->decrypt

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

->trim

+----------+ | DATA | +----------+ | Variable | +----------+

->getaddrinfo->tcp/udp

->send to dest server via tcp/udp

当远程新建的socket收到连接请求时:

说明dest服务器向远程端发出响应,远程端对其进行加密,并且转发给local端

+----------+ | DATA | +----------+ | Variable | +----------+

->add

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

->encrypt

+-------+--------------+ | IV | PAYLOAD | +-------+--------------+ | Fixed | Variable | +-------+--------------+

->send to local

在handler函数里面的基本逻辑就是:

if sock == self._server_socket: self._handle_server() elif sock and (fd in self._sockets): self._handle_client(sock)

协议解析和构建用的struct.pack()和struct.unpack()


asyndns.py实现的是一个DNS服务器.

封装得相当的好

读取/etc/hosts和/etc/resolv.conf文件,如果没有设置,就设置dns服务器为8.8.8.8和8.8.4.4

收到tcp handler和udp handler的dns请求之后,建立socket并且向远程服务器发送请求,并把(hostname:callback)加入hostnameto_cb

收到响应之后触发callback hostnameto_cbhostname

全程用二进制构建dns报文

# 请求 # 1 1 1 1 1 1 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # | ID | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # |QR| Opcode |AA|TC|RD|RA| Z | RCODE | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # | QDCOUNT | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # | ANCOUNT | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # | NSCOUNT | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # | ARCOUNT | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

响应:

1 1 1 1 1 1 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | / / / NAME / | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | TYPE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | CLASS | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | TTL | | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | RDLENGTH | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| / RDATA / / / +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+


lru_cache.py一个缓存

self._store = self._time_to_keys = collections.defaultdict(list) self._keys_to_last_time = self._last_visits = collections.deque()

先找访问时间_last_visits中超出timeout的所有键

然后去找_time_to_keys,找出所有可能过期的键

因为最早访问时间访问过的键之后可能又访问了,所以要_keys_to_last_time

找出那些没被访问过的,然后删除


学到的其他东西:
  1. future

  2. json.loads(f.read().decode(‘utf8’),objecthook=decode_dict)

  3. python内置的logging也可作大规模使用

  4. 把我理解层面阔伸到协议层面,学到怎么构建一个协议(协议的设计还要学习)

  5. 网络编程和信息安全息息相关

  6. 这个网络编程的学习路线挺不错的:爬虫->XX软件。不知道下一步怎么加深

一些问题:

  1. 如何做到线程安全?

  2. 大量对变量是否存在的检查是为了什么?

  3. FSM的思想怎么应用到网络编程?

  4. 防火墙到底是怎么工作的?

  5. linux的内核异步IO怎么调用(操作系统)