学校网络是通过http代理的connect隧道
上网,直接设置系统网络http和https代理
的话,其他有些不使用http协议
的软件便不能联网。
为了解决这个问题最好并且一劳永逸的办法就是把代理设置到路由器上,通过透明代理转发一切tcp协议流量。
具体设备是asus-ac56s
刷 koolshare
固件。利用系统自带的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
到本机,本地运行nodejs
和ss客户端(带socks服务端)
。nodejs
运行服务端收到来自ubuntu
的socks
请求后转发到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('不支持的地址类型')
}
}