东莞政府网站建设seo的含义是什么意思
首先思路是客户端发送socks5请求数据——>服务端解密并解析socks5数据是否为真,解析出Host和port,并用net.socket访问目标网站,目标网站返回数据,服务端再用ws发送返回数据给客户端
//解析socks5数据和返回socks5格式响应
//在读下面代码时希望你了解socks5的格式
+----+-----+-------+------+----------+----------+----------+
|0x05|0x01 | 0x00 | 0x03 | 0x09 | baidu.com| 0x00 0x50 |
+----+-----+-------+------+----------+----------+----------+
下面共7个字节+域名字节长度
偏移量 长度 描述
0 1 版本号 (0x05)
1 1 命令 (0x01: CONNECT)
2 1 保留字段 (0x00)
3 1 地址类型 (0x03: 域名)
4 1 域名长度 (1 字节)
5 可变 域名 (根据长度)
5 + 域名长度 2 端口号 (2 字节)
SOCKS5 请求示例
假设客户端请求连接到 baidu.com:80,SOCKS5 请求的格式如下:
字段 值 说明
版本号 0x05 SOCKS5 版本号。
命令 0x01 CONNECT 命令。
保留字段 0x00 保留字段,必须为 0x00。
地址类型 0x03 DOMAINNAME 地址类型。
域名长度 0x09 域名长度为 9 字节。
域名 baidu.com 目标域名。
端口号 0x0050 目标端口号(80)
。
SOCKS5 响应示例
+----+-----+-------+------+----------+----------+----------+
|0x05| rep | 0x00 | 0x01 | 0x00 0x00 0x00 0x00 | 0x00 0x00 |
+----+-----+-------+------+----------+----------+----------+
假设请求成功,SOCKS5 响应的格式如下:
字段 值 说明
版本号 0x05 SOCKS5 版本号。
响应码 0x00 SUCCEEDED,请求成功。
保留字段 0x00 保留字段,必须为 0x00。
地址类型 0x01 IP_V4_ADDR 地址类型。
绑定地址 0x00000000 绑定地址(通常为 0.0.0.0)。
绑定端口 0x0000 绑定端口(通常为 0)。
//socks.js代码
'use strict';const util=require('util');
//socks5的版本号永远是0x05,即5;
const VERSION=5;
//SOCKS5 支持多种认证方法,客户端和服务端在握手阶段协商使用哪种认证方法。
const AUTH={NO_AUTH: 0, //不需要认证。GSSAPI: 1, //使用 GSSAPI(通用安全服务应用程序接口)进行认证。USERNAME_PASSWD: 2, //使用用户名和密码进行认证。IANA_ASSIGNED: 3, // 0x03 to 0x7f IANA 分配的认证方法RESERVED: 0x80, // 0x80 to 0xfe 保留的认证方法(0x80 到 0xFE)NO_ACCEPTABLE_METHODS: 0xff //没有可接受的认证方法
};
//SOCKS5 客户端可以发送以下命令,指示代理服务器执行的操作
const CMD={CONNECT:1, //建立 TCP 连接。通常用于 HTTP、HTTPS 等协议BIND:2, //绑定端口。通常用于 FTP 等需要反向连接的协议。UDP_ASSOCIATE:3 //建立 UDP 关联。用于 UDP 协议(如 DNS、QUIC)
};
//SOCKS5 支持多种目标地址类型
const ATYP={IP_V4_ADDR:1, //ip4DOMAINNAME:3, //域名IP_V6_ADDR:4 //ip6
};//SOCKS5 服务器在处理客户端请求后,会返回一个响应码,表示请求的处理结果。
const REP={SUCCEEDED:0, //请求成功。SOCKS_SERV_FAILURE:1, //SOCKS 服务器故障。CONN_NOT_ALLOWED:2, //连接不被允许(例如,目标地址被禁止)NETWORK_UNREACHABLE: 3, //网络不可达HOST_UNREACHABLE: 4, //主机不可达CONN_REFUSED: 5, //连接被拒绝TTL_EXPIRED: 6, //TTL(生存时间)已过期CMD_NOT_SURPPORTED: 7, //命令不支持ATYP_NOT_SUPPORTED: 8 //地址类型不支持
};//解析客户端发送过来的网址请求,是socks5格式的请求
//最后最到host和port,并检查host是域名|ip_V4|ip_V6
function parseRequest(data)
{let host,port;if(data.length<8){return {ok:false};}let buf=Buffer.from(data);let ver=buf.readUInt8(0);let cmd=buf.readUInt8(1);if(ver !=VERSION || cmd != CMD.CONNECT){return{ok:false};//return {ok:false,msg:generateReply(REP.CMD_NOT_SURPPORTED)};}let atyp=buf.readUInt8(3);switch(atyp){case ATYP.DOMAINNAME:let alen=buf.readUInt8(4);host=buf.toString('utf8',5,5+alen);port=buf.readUInt16BE(5+alen);break;case ATYP.IP_V4_ADDR:if(data.length<10){return {ok:false,msg:generateReply(REP.CONN_NOT_ALLOWED)};} host=util.format('%d.%d.%d.%d',buf[4],buf[5],buf[6],buf[7]);port=buf.readUInt16BE(8);break;case ATYP.IP_V6_ADDR:if(data.length<22){return {ok:false,msg:generateReply(REP.CONN_NOT_ALLOWED)}} host=util.format('%s:%s:%s:%s:%s:%s:%s:%s', buf.toString('hex',4,6), buf.toString('hex',6,8), buf.toString('hex',8,10), buf.toString('hex',10,12), buf.toString('hex',12,14), buf.toString('hex',14,16), buf.toString('hex',16,18), buf.toString('hex',18,20));port=buf.readUInt16BE(20);break;default:return {ok:false,msg:generateReply(REP.ATYP_NOT_SUPPORTED)}; }return {ok:true,host:host,port:port};
}//返回一个socks5响应,10个字节,可看上面的响应格式
function generateReply(rep=REP.SUCCEEDED)
{var rep=Buffer.alloc(10);rep.writeUInt8(VERSION);rep.writeUInt8(rep,1);rep.writeUInt8(ATYP.IP_V4_ADDR,3);return rep;
}function parseNegotiation(data)
{if(data.length<3){return {ok:false};}let arr=Buffer.from(data);let ver=arr.readUInt8(0);let n=arr.readUInt8(1);let method=arr.readUInt8(2);if(ver != VERSION || n <1 || method!=AUTH.NO_AUTH){return {ok:false,msg:Buffer.from([VERSION,AUTH.NO_ACCEPTABLE_METHODS])};}return {ok:true,msg:Buffer.from([VERSION,AUTH.NO_AUTH])};
}module.exports={parseNegotiation:parseNegotiation,generateReply:generateReply,parseRequest:parseRequest
};
//服务端代码
'use strict';
const net=require('net');
const dns=require('dns');
const util=require("util");
const WebSocket=require('ws');
const Cipher=require('./cipher');
const socks=require("./socks");const TIMEOUT=60000;
const TIMEWAIT=10000;class Server{constructor(options){this.key=options.key;this.host=options.host;this.port=options.port;//创建ws服务端this.server=new WebSocket.Server({host:options.host,port:options.port});this.server.on('error',err=>{console.log(err.toString());});this.server.on('close',()=>{console.log('server close');this.server.clients.forEach(function each(client){if(client && client.readyState===WebSocket.OPEN){client.destroy();}});}); }//运行服务端run(){const key=this.key;const port=this.port;this.server.on('connection',(ws,req)=>{//连接到服务端的ip和端口,就是用户的IP和端口const peer=req.socket.remoteAddress+":"+req.socket.remotePort;console.log('%s -- :%d $ connected',peer,port);//创建加密let cipher=new Cipher(key);//接收到客户端发送过来的请求ws.on('message',msg=>{console.log('%s -> [%d] :%d',peer,msg.length,this.port);let raw;try{//解密请求信息raw=cipher.decrypt(msg);console.log(raw);}catch(e){console.log(e.toString());setTimeout(()=>ws.close(),Math.random()+TIMEOUT);return;}//解析SOCKS5请求let res=socks.parseRequest(raw);if(!res.ok){console.log('Invalid socks5 request');ws.close();return;}//解析出客户端请求中的host和端口,就是你要访问的网站的域名和端口const {host,port}=res;//与目标服务器建立连接,就是你请求域名的目标服务器const targetSocket=new net.Socket();//和目标服务器建立连接,也就是你要的访问网站建立连接targetSocket.connect(port,host,()=>{console.log('Connected to target server:',host,port);//返回SOCKS5成功响应,这里要先给客户端返回一个socks5格式数据表示已经和目标网站建立连接const response=socks.generateReply();ws.send(response);//这里收到了客户端返回的数据//也就是这段httpRequest='GET / HTTP/1.1\r\nHost: baidu.com\r\nConnection: close\r\n\r\n';//让targatSocket把这个请求头发给百度的服务器ws.on('message',(data)=>{console.log('这里是转发数据:',data.toString());//这里发给你请求的网站的服务器,如现在请求的百度targetSocket.write(data);});//这里是百度服务器返回的数据targetSocket.on('data',data=>{console.log('这接收到targetSocket过来的数据:',data);//然后转发给我们的客户端ws.send(data);});});targetSocket.on('error',err=>{console.log('Target server connection error:',err);ws.close();});targetSocket.on('close',()=>{console.log('Target server connection close:',close);ws.close();})})ws.on('close',()=>{console.log('%s -- disconnected.',peer);});ws.on('error',err=>{console.error('WebSocket error: ',err);});})}}const options={key:"metadata",host:'localhost',port:8000
};let s=new Server(options);
s.run();
//接着是客户端
const WebSocket=require('ws');
const Cipher=require("./cipher");//SOCKS5协议常量
const VERSION=0x05; //socks5版本号
const CMD={CONNECT:0x01, //CONNECT命令
};
const ATYP={DOMAINNAME:0x03, //域名
};//目录服务器信息
const TARGET_HOST='www.baidu.com'; //目标主机
const TARGET_PORT=80; //目标端口//创建SOCKS5请求
//这里是硬创建的数据写入内存中,现实中应该用上面的协议常量
function createSocks5Request(host, port) {const hostLength = Buffer.byteLength(host);const buffer = Buffer.alloc(7 + hostLength);// 写入版本号buffer.writeUInt8(0x05, 0); // 版本号必须是 0x05// 写入命令 (CONNECT)buffer.writeUInt8(0x01, 1); // 命令必须是 0x01 (CONNECT)// 写入保留字段buffer.writeUInt8(0x00, 2); // 保留字段必须是 0x00// 写入地址类型 (域名)buffer.writeUInt8(0x03, 3); // 地址类型必须是 0x03 (域名)// 写入域名长度buffer.writeUInt8(hostLength, 4); // 域名长度// 写入域名buffer.write(host, 5); // 域名// 写入端口号buffer.writeUInt16BE(port, 5 + hostLength); // 端口号return buffer;
}//连接到服务器
const ws=new WebSocket('ws://localhost:8000');
//加密认证
const cipher=new Cipher("metadata");
ws.on('open',()=>{console.log('Connected to WebSocket server');//发送SOCKS5请求const request=createSocks5Request(TARGET_HOST,TARGET_PORT);ws.send(cipher.encrypt(request));
});ws.on('message',data=>{//console.log('接收来自服务端数据:',data.toString());//如果SOCKS5响应,发送HTTP请求//SOCKS5响应为10字节,因为在socks.js中的generateReply()生成的就是10个字节if(data.length===10){const httpRequest='GET / HTTP/1.1\r\nHost: baidu.com\r\nConnection: close\r\n\r\n';ws.send(httpRequest);}else{//打印目标服务器返回的数据,这里返回的就是网站数据即html格式的数据 console.log('Respnonse from target server: ',data.toString());}
});ws.on('close',()=>{console.log('Connection closed');
});
ws.on('error',(err)=>{console.error('WebSocket error: ',err);
});
实现客户端与目标服务器之间的双向数据转发。
这里没有贴出加密的代码,这个不难你们可以自己现实一个加密即可,也可以不加密,只是测试用
- 数据转发的工作流程
客户端发送数据:
客户端通过 WebSocket 发送数据(如 HTTP 请求)。
ws.on(‘message’, …) 捕获数据,并通过 targetSocket.write(data) 将数据转发给目标服务器。
目标服务器返回数据:
目标服务器处理请求后,返回数据(如 HTTP 响应)。
targetSocket.on(‘data’, …) 捕获数据,并通过 ws.send(data) 将数据转发给客户端。
双向通信:
通过这两个事件监听器,客户端与目标服务器之间的通信可以实现双向转发。
- 示例场景
假设客户端通过 SOCKS5 代理访问 baidu.com:
客户端发送 SOCKS5 请求:
客户端发送 SOCKS5 请求到代理服务器,请求连接到 baidu.com:80。
代理服务器建立连接:
代理服务器解析 SOCKS5 请求,并与 baidu.com:80 建立 TCP 连接。
代理服务器返回 SOCKS5 响应:
代理服务器生成 SOCKS5 响应(response),并通过 ws.send(response) 发送给客户端。
客户端发送 HTTP 请求:
客户端发送 HTTP 请求(如 GET / HTTP/1.1)到代理服务器。
代理服务器通过 ws.on(‘message’, …) 捕获请求,并通过 targetSocket.write(data) 将请求转发给 baidu.com。
目标服务器返回 HTTP 响应:
baidu.com 返回 HTTP 响应。
代理服务器通过 targetSocket.on(‘data’, …) 捕获响应,并通过 ws.send(data) 将响应转发给客户端。
- 总结
const response = socks.generateReply();:生成 SOCKS5 响应,表示连接成功。
ws.send(response);:将 SOCKS5 响应发送给客户端。
数据转发部分:
ws.on(‘message’, …):将客户端的数据转发给目标服务器。
targetSocket.on(‘data’, …):将目标服务器的数据转发给客户端。