sock5代理node实现(2)

上一篇我们已经完成了socks5客服端和服务端的第一次握手认证,相互确认了socks协议的版本号,认证的方式,现在认证方式定为用户名和密码认证,接下来我们马上就来认证。

RFC的socks5文档中并没有写用户名和密码认证的认证细节,而是写在另一篇文档中。

客户端在接收到上一篇最后发出的数据后,会发出下面这个格式的数据

+—-+——+———-+——+———-+
|VER | ULEN | UNAME | PLEN | PASSWD |
+—-+——+———-+——+———-+
| 1 | 1 | 1 to 255 | 1 | 1 to 255 |
+—-+——+———-+——+———-+

第一个参数VER表示当前的账号密码认证的协议版本号(注意这个不是socks的版本号),暂时只有0x01

第二个参数带ULEN为用户名的长度,从这里可以看到用户名最长为255位。

第三组参数ULEN就是用户名,长度就是ULEN

第四个参数PLEN为密码长度,同样最长为255位。

第五组参数PLEN是密码,长度为PLEN

而我们的服务端需要在获取到账号密码后返回以下格式的数据

+—-+——–+
|VER | STATUS |
+—-+——–+
| 1 | 1 |
+—-+——–+

第一个参数VER表示当前的账号密码认证的协议版本号(注意这个不是socks的版本号),暂时只有0x01

第二个参数STATUS表示是否认证成功,其中只有STATUS0x00表示成功,其他都是表示错误。

下面是对应的Node代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 这个后续会说明
const receiveRequest = require('./receive-request.js');

module.exports = function (data) {
const socket = this;
let ver = data[0];

// 用户名长度
let ulen = parseInt(data[1], 10);
// 用户名
let username = data.slice(2, 2 + ulen).toString('utf8');
// 密码长度
let plen = parseInt(data[1 + ulen + 1], 10);
let passwordStartPostion = 1 + ulen + 2;
// 密码
let password = data.slice(passwordStartPostion, passwordStartPostion + plen).toString('utf8');
// status不是0x00就表示密码错误
let status = 0x01;
// 这里我们账号密码暂时写死为abc 1234
if (username === 'abc' && password === '1234') {
status = 0x00;
}
let res = new Buffer([ver, status]);
// 将结果返回给客户端
socket.write(res);
// 监听下一次请求
socket.once('data', receiveRequest.bind(socket));
};

sock5代理node实现(1)
sock5代理node实现(3)