记 socks5 协议

本文共有5901个字,关键词:socks5

学校网络是通过http代理的connect隧道上网,直接设置系统网络http和https代理的话,其他有些不使用http协议的软件便不能联网。
为了解决这个问题最好并且一劳永逸的办法就是把代理设置到路由器上,通过透明代理转发一切tcp协议流量。
具体设备是asus-ac56skoolshare固件。利用系统自带的entware_setup.sh插上u盘格式化成ext4后可以自动安装。
安装过程中路由器也需要设置代理联网,否则会卡在下载文件阶段。具体设置代码如下。

export http_proxy=http://158.217.200.50:8080
export https_proxy=http://158.217.200.50:8080

需要同时设置http和https代理后即可正常安装,使用opkg安装nodejs后不能启动,貌似是运行库的问题,这个之后再解决。

首先需要做的,就是把http的connect隧道转换成socks协议,才能在iptables设置全局代理。这里只打算转发tcp连接,由于http不支持udp转发,这里放弃udp。今后可能通过shadowsocks来转发udp连接。

首先先用node实现socks5服务器,具体协议通过另一台ubuntu设置socks到本机,本地运行nodejsss客户端(带socks服务端)nodejs运行服务端收到来自ubuntusocks请求后转发到ss客户端,中途通过console.log来展示协议内容。

通过ubuntu浏览器访问8.8.8.8来抓包,具体内容如下(<<<表示收到来自ubuntu信息,>>>表示发出信息)

1<<<[5,1,0]
2>>>[5,0]
3<<<[5,1,0,3,7,56,46,56,46,56,46,56,0,80]
4>>>[5,0,0,1,0,0,0,0,0,0]
5<<<{{隧道建立成功,隧道内容}}

第一条客户端发送[5,1,0]分别表示

0[VER]5            =>socks版本,这里是v5版本
1[NMETHODS]1       =>[METHODS]长度
2[METHODS]0        =>客户端支持的认证方式列表,每个方法占1字节。当前的定义是: 
                      0x00 不需要认证
                      0x01 GSSAPI
                      0x02 用户名、密码认证
                      0x03 - 0x7F由IANA分配(保留)
                      0x80 - 0xFE为私人方法保留
                      0xFF 无可接受的方法

第二条服务端回复 [5,0] 分别表示

0[VER]5            =>socks版本,这里是v5版本
1[NMETHODS]0       =>METHOD是服务端选中的方法。
                      如果返回0xFF表示没有一个认证方法被选中,客户端需要关闭连接。

第三条客户端申请连接远程服务器[5,1,0,3,7,56,46,56,46,56,46,56,0,80]
这里是请求连接8.8.8.8:80(不知道为啥这里8.8.8.8是当成域名的)

    0[VER]5       =>socks版本,这里是v5版本
    1[CMD]1       =>SOCK的命令码 
                     0x01表示CONNECT请求
                     0x02表示BIND请求
                     0x03表示UDP转发
    2[RSV]0       =>固定码
    3[ATYP]3      =>[DST.ADDR]类型
                     0x01 IPv4地址,DST.ADDR部分4字节长度
                     0x03域名,DST。ADDR第一个字节为域名长度,剩余内容为域名,没有\0结尾。
                     0x04 IPv6地址,16个字节长度。
    4-11[DST.ADDR]7,56,46,56,46,56,46,56  => 目的地址
    12-13[DST.ADDR]0,80  => 网络字节序表示的目的端口

第四条服务器连接上远程服务器后回复[5,0,0,1,0,0,0,0,0,0]

    0[VER]5       =>socks版本,这里是v5版本
    1[REP]0       =>REP应答字段
                     0x00表示成功
                     0x01普通SOCKS服务器连接失败
                     0x02现有规则不允许连接
                     0x03网络不可达
                     0x04主机不可达
                     0x05连接被拒
                     0x06 TTL超时
                     0x07不支持的命令
                     0x08不支持的地址类型
                     0x09 - 0xFF未定义
    2[RSV]0       =>固定值
    3[ATYP]1      =>ATYP BND.ADDR类型
                     0x01 IPv4地址,BND.ADDR部分4字节长度
                     0x03域名,BND.ADDR第一个字节为域名长度,剩余内容为域名,没有\0结尾。
                     0x04 IPv6地址,16个字节长度。
    4-7[BND.ADDR]0,0,0,0   =>服务器绑定的地址(固定即可)
    8-9[BND.PORT]0,0       =>网络字节序表示的服务器绑定的端口(固定即可)

综上,socks5协议

附上http代理的connect隧道转socks的nodejs程序。
连接断开和重连方面可能还有问题,具体表现在,程序有提示 接口关闭继续写入的保存,虽然已经做了处理还是会有发生。
还有网页上传文件时不时上传失败。
不过测网速效果还不错。用系统自带http代理能跑98Mb,用nodejs代理也能跑个92Mb。

const net = require('net');
const proxy_addr = “158.217.200.50”
const proxy_port = 8080
const timeout = 5000

const server = net.createServer((client) => {
    const remote = new net.Socket();
    client.on('error', (err) => {
        client.end();
        console.error('来自 本地客户端 error', err)
    })
    remote.on('error', (err) => {
        remote.end();
        console.error('来自 远程服务器 error', err)
    })
    client.setTimeout(5000, client.end);
    remote.setTimeout(5000, remote.end);
    remote.on('end', client.end);
    client.on('end', remote.end);

    client.once('data', (data) => { //客户端第一次握手
        if (data[0] != 5) {
            return client.end('仅支持socks5');
        }
        client.write(new Buffer([5, 0])) //服务器应答握手
        client.once('data', (data) => { //客户端发来请求连接地址
            const cmd = data[1] //1connect 2bind 3udp
            if (cmd !== 1) {
                return client.end(new Buffer([5, 0, 0, 2, 0, 0, 0, 0, 0, 0])) //不支持tcp以外连接
            }
            const addr_type = data[3] //1ipv4 3domain 4ipv6
            const addr = data.slice(4, data.length - 2)
            const host = addr2host(addr_type, addr)
            const port = data[data.length - 1] + data[data.length - 2] * 256
            if (host instanceof Error) {
                return client.end(new Buffer([5, 0, 0, 3, 0, 0, 0, 0, 0, 0])) //不支持ipv6地址
            }
            console.log(`访问 => ${host}:${port}`)
            remote.connect(proxy_port, proxy_addr, () => {
                remote.write(`CONNECT ${host}:${port}\n\n`)
                remote.once('data', (data) => {
                    if (data[9] == 50) { //建立成功
                        client.write(new Buffer([5, 0, 0, 1, 0, 0, 0, 0, 0, 0])) //连接远程服务器成功
                        remote.pipe(client)
                        client.pipe(remote)
                    } else {
                        client.end(new Buffer([5, 0, 0, 5, 0, 0, 0, 0, 0, 0])) //远程端不接受
                    }
                })
            });
        })
    })
})

server.listen(8017, () => {
    console.log('Server listening: ' + JSON.stringify(server.address()));
    server.on('close', function () {
        console.log('服务结束退出');
    });
    server.on('error', function (err) {
        console.log('服务出错: ', JSON.stringify(err));
    });
});

const addr2host = (addr_type, addr) => {
    if (addr_type == 1) { //ipv4地址
        return addr.join('.')
    } else if (addr_type == 3) { //domain
        return addr.slice(1).toString()
    } else if (addr_type == 4) {//ipv6
        return new Error('不支持的ipv6地址类型')
    } else {
        return new Error('不支持的地址类型')
    }
}

「一键投喂 软糖/蛋糕/布丁/牛奶/冰阔乐!」

pch18

(๑>ڡ<)☆谢谢老板~

使用微信扫描二维码完成支付

版权声明:如无特别说明,本文为作者原创,转载请在首行注明来源:http://pch18.cn/archives/71.html
添加新评论
暂无评论