兑现之前的承诺。其实网上这类的文章比较多,但是还是想自己分析一下,加深印象。这里用的是nodejs的版本分析,究其原因,因为最近正在复习nodejs,所以趁热打铁吧。

项目地址:https://github.com/shadowsocks/shadowsocks-nodejs

其实shadowsocks的原理比较简单,最初的思想无非是把socks5协议拆分,并且加密了中间数据。

好吧,我们这里分析客户端,首先是本地创建了一个监听:

exports.createServer = createServer;

这个createServer函数定义是:

createServer = function(serverAddr, serverPort, port, key, method, timeout, local_address);

这其中创建了一个监听,作为socsk5的服务器,也就是我们本机流量走向的第一个地方:

server = net.createServer(function(connection);

其余的udpRelay部分不是分析重点,继续分析:

encryptor = new Encryptor(key, method);

这里创建了encryptor是用来进行后来的数据加密,科科shadowsocks的重要特性就是支持多种加密协议,其实我们也可以改写并添加自己的协议,不过貌似大部分人不会酱紫做,做了也不会公开吧0.0

connection.on("data", function(data) {});

这里监听数据到来事件,里面分成多个stage(stage 1,5,10等),初始化为stage 0并不包括在这5个stage中,因为此时协议还没握手0.0

	if (stage === 0) {
          tempBuf = new Buffer(2);
          tempBuf.write("\u0005\u0000", 0);
          connection.write(tempBuf);
          stage = 1;
          utils.debug("stage = 1");
          return;
        }

这里的stage 0发送的奇怪的unicode是什么呢?其实是socks5协议的头,socks5的协议过程如下:

1.向服务器的1080端口建立tcp连接。
2.向服务器发送 05 01 00 (此为16进制码,以下同)
3.如果接到 05 00 则是可以代理
4.发送 05 01 00 01 + 目的地址(4字节) + 目的端口(2字节),目的地址和端口都是16进制码(不是字符串!!)。 例202.103.190.27 -7201 则发送的信息为:05 01 00 01 CA 67 BE 1B 1C 21 (CA=202 67=103 BE=190 1B=27 1C21=7201)
5.接受服务器返回的自身地址和端口,连接完成
6.以后操作和直接与目的方进行TCP连接相同。

其实就是过程3,这里我们扮演的是本地socks5服务器的角色,谨记。那么后面的过程呢? 其实都是相同的,建立了一个没有密码验证的socks5服务器,其作用就是来接受本地数据。

接下来,按照socks5协议,响应了本地连接请求。之后:

remote = net.connect(aPort, aServer, function() {});

这里就是建立连接到远程的shadowsocks服务器的连接。然后,就进入stage 5了,就愉快的使用

data = encryptor.decrypt(data);

或者相反的方法互传数据了0.0其实到这里就差不多了,其余的部分:

remote.on("data", function(data) {});

你懂得,就是远程服务器返回数据时触发,然后数据回写。

其余的部分就是超时和事件处理,这里不再赘述。

exports.main = function() {});

其实就是说,导出一个main函数,然后

if (require.main === module) {

exports.main();

}

看bin文件夹下的sslocal:

#!/usr/bin/env node

var path = require('path');
var fs = require('fs');
var lib = path.join(path.dirname(fs.realpathSync(__filename)), '../lib');

require(lib + '/shadowsocks/local').main()

这个才是最后的入口,是为了兼容shadowsocks的可执行文件路径。这里赞下,整个lib下所有文件都是每个文件有一个单独的立即执行匿名函数,酱紫保证自己的代码作用域不会污染全局作用域0.0,其中call(this)导入了exports这个对象引用。

以上就是简单的分析内容,其实我们可以添加自己的加密方案,错误处其实程序已经做的不错了,Nodejs的单线程异步模型,错误处理不好的话,很容易就满盘皆输0.0

转载请著名: SpringHack