[学习记录]源码分析-lightsocks
这次是第一次研究别人的源码,之前因为看不懂,太长不看,不理解相对正规的写法等等,所以并没有很认真地读完过。
这一次首先读的源码较简单,其次也是稍微静下心来好好梳理了一下代码逻辑,并且把自己学到的东西记录下来。
gitub源地址 传送门
----------------------------------------------------------
1.目录结构
可以看出来大概目录树的情况,lightsocks目录下有core和utils两个子目录,utils下放置的是config.py和net.py,大概推测是配置信息和网络信息
core中包括cipher.py, password.py, securesocket.py,推测为加密解密模块、密码模块和安全socket模块。在lightsocks目录下有local.py和server.py, 应该就是负责服务器和客户端的模块,而在lightsocks目录外还存在lslocal.py, lsserver.py,推测这两个文件才是最终的服务端和客户端。
以上推测以未正式阅读源码为前提。
-----------------------------------------------------
然后开始正式阅读源码,编程思想主要有两种,自顶向下或自底向上,
自顶向下即先思考整个系统的构成,然后一块一块拆分成子模块,再一点点地实现这些子模块,优点是整个系统功能会非常清晰,对于庞大的系统而言不易产生混乱。
自底向上即先考虑系统需要哪些基本功能,先将基本功能实现再堆砌起来,优点是代码复用性高。
这次采用自底向上的方法,先了解每个子模块的功能,再一点一点向上整合,最后拼出整体功能。
以下分析不直接粘贴源码,直接介绍感悟,源码传送门已经放在上面了。
----------------------------------------------------------------------------------------
cipher.py
1.创建了一个 cipher类,创建时需要提供加密密码和解密密码,拥有加密解密两种常用的方法,分别将利用对应密码来对输入内容进行加密解密。还具备一个类方法,输入加密密码,生成解密密码并且返回一个cipher实例
2.可以将cipher理解成这样一个角色:持有自己独一无二的密码,主要任务是负责对数据进行加密解密。
3.加密解密算法可以自定义,作者提供的是简单的位移变换
--------------------------------------------------------------------------------------------
password.py
1.这是一个方法库,定义了一个自定义的错误类型以及众多和密码有关的方法
2.通过如下格式定义了一个错误
class InvalidPasswordError(Exception): """不合法的密码"""
3.定义了一个安全性函数,用来检查密码是否合法。该函数被频繁地调用,并且会raise之前定义的密码错误
4.定义了将字节型的密码和字符串型密码相互变换的两个函数。
---------------------------------------------------------------------------------------
securesocket.py
1.通过如下方式引入了之前编写的类
from .cipher import Cipher
2.创建了一个安全套接字类
该类传入一个asyncio的事件循环和密码类进行创建
设置了用于单向通信的decodeRead和encodeWrite,
设置了用于持续双向通信的encodeCopy和decodeCopy,其中encodeCopy为获取信息后调用encodeWrite进行加密发送,decodeCopy为获取加密信息后解密再发送。至此可以推测解密发送是发送给被请求的主机,加密发送为发送给客户端。
--------------------------------------------------------------------------------
config.py
1.通过如下方式调用另一个目录下的文件
from lightsocks.core.password import (InvalidPasswordError, dumpsPassword, loadsPassword)
2.使用了一个namedtuple的结构,简要描述下就是一个包含名称的元组,可以直接通过属性名访问属性,类似一个没有方法仅有属性的对象
3.定义了将URL和Config对象相互转换的方法
4.定义了Config和json字符串互相转换的方法
5.定义了Config和json文件的转换方法
6.语法知识
*#告诉函数,入参是一个tuple **#告诉函数,入参是一个dict
---------------------------------------------------------------------------------------------------
net.py
使用namedtupe定义了一个地址对象
为了构造一个namedtuple需要两个参数,分别是tuple的名字和其中域的名字。
例如
Address = namedtuple('Address', 'ip port')
tuple名字为Address,内部子域有ip和port
-----------------------------------------------------------------
local.py
1.创建了lslocal类,该类为securesocket的子类,为了创建它徐娅传入异步事件循环,字节型格式密码,代理监听地址与远程访问地址
定义了监听方法,用来与代理服务器进行通信,在循环中执行handleConn方法
定义了dialRemote方法,用来与代理服务器通信,是直接连接。
定义了handleConn方法,首先通过一个协程调用dialRemote获取连接,然后维持本地客户端与代理服务器的通信。
--------------------------------------------------------------------
server.py
1.定义了LsServer类,为了创建它需要传递一个事件循环、字节流密码以及监听地址
定义了监听方法,等待客户端的请求,
定义了请求处理方法handleConn:首先依照socks5协议进行协商,其中的通信均由decodeRead和encodeWrite完成,为了规避防火墙的检查。然后服务器接收用户传递的数据提取目的ip,目的端口。最后维护代理服务器与远端服务器的通信。
---------------------------------------------------------------------
lslocal.py
1.使用了argparse进行命令行参数解析,并进行初步设置,如果无要求则导入默认设置
2.最后本地运行客户端级服务器
-------------------------------------------------------------------------
lslocal.py
1.使用了argparse进行命令行参数解析,并进行初步设置,如果无要求则导入默认设置
2.最后运行代理服务器
--------------------------------------------------------------------------
小结
该程序总体代码量不算太长,各个模块较独立,易于修改。在面向对象方面在底层的一些事务处理全是由实例完成,
仅部分服务端与客户端都有相同需求的函数定义在utils库中。
最后再梳理一下shadowsocks的原理,本地在上网时通过本地代理服务器进行访问,本地代理服务器将会与远端的代理服务器进行加密通讯,最后交给远端代理服务器访问我们真正想要访问的网站。