socks5
作用:将开发的服务运行在海外服务器,可以访问谷歌。
浏览器支持
chrome
首先开启socks5代理
可以正常访问
safari
首先开启socks5代理
可以正常访问
调试
输入: curl --socks5 192.168.1.101:1080 https://www.baidu.com
对应端口为50207
wireshark抓包
查看socks5协议
一次收发
二次收发
至此协议升级完成,后面开始传输数据。
查看https协议
tlsv加密正常。
查看http协议
输入命令行:curl --socks5 192.168.1.101:1080 http://www.baidu.com
压测 todo
没找到合适的工具暂没有压测。
代码
协议比较简单。
首先客户端发送一串数据进行权限校验。
服务端允许。
public void exec() throws IOException {
ByteBuffer writeBuffer = ByteBuffer.allocate(4096 * 5);
CompositeByteBuf cumulation = channelWrapped.cumulation();
String uuid = channelWrapped.uuid();
byte VER;
int len = cumulation.remaining();
//auth协议最少有3位
if (len < 3) {
LOGGER.warn("数据包不完整 {}", uuid);
return;
}
//切换读取模式
cumulation.mark();
VER = cumulation.get();
if (0x05 > VER) {
LOGGER.warn("版本号错误或版本过低,只能支持5 {}", uuid);
closeChildChannel();
return;
}
byte NMETHODS = cumulation.get();
//读取数据不够,接着重读
if (cumulation.remaining() < NMETHODS) {
cumulation.reset();
LOGGER.warn("数据包不完整 {}", uuid);
return;
}
//说明读取正常,后面的method不校验了,直接clean。
cumulation.clearAll();
//2~255
writeBuffer.put((byte) 5);
writeBuffer.put((byte) 0);
writeBuffer.flip();
channelWrapped.channel().write(writeBuffer);
writeBuffer.clear();
//更换附件
ConnectionHandler connectionHandler = new ConnectionHandler(channelWrapped);
channelWrapped.key().attach(connectionHandler);
LOGGER.info("鉴权成功 {}", uuid);
}
客户端在发送ip地址和端口。
服务端回应。
public void exec() throws IOException {
ByteBuffer writeBuffer = ByteBuffer.allocate(4096 * 5);
CompositeByteBuf cumulation = channelWrapped.cumulation();
String uuid = channelWrapped.uuid();
int len = cumulation.remaining();
//协议最少5位
if (len < 5) {
LOGGER.warn("数据包不完整 {}", uuid);
return;
}
cumulation.mark();
byte VER = cumulation.get();
if (0x05 > VER) {
closeChildChannel();
LOGGER.warn("版本号错误或版本过低,只能支持5 {}", uuid);
return;
}
byte CMD = cumulation.get();
if (0x01 != CMD) {
closeChildChannel();
LOGGER.warn("协议格式不对 {}", uuid);
return;
}
byte RSV = cumulation.get();
byte ATYP = cumulation.get();
String host = null;
Integer port = 0;
SocketChannel channel = channelWrapped.channel();
if (0x01 == ATYP) {//IPV4
if (cumulation.remaining() + 1 < 6) {
cumulation.reset();
LOGGER.warn("数据包不完整 {}", uuid);
return;
}
host = Utils.byteToIntV2(cumulation.get()) + "." + Utils.byteToIntV2(cumulation.get()) + "." + Utils.byteToIntV2(cumulation.get()) + "." + Utils.byteToIntV2(cumulation.get());
port = (Utils.byteToIntV2(cumulation.get()) << 8) + Utils.byteToIntV2(cumulation.get());
LOGGER.info("IPV4 host:{} port:{} remoteAddress:{} {}", host, port, channel.getRemoteAddress(), uuid);
} else if (0x03 == ATYP) {//域名
byte hostnameSize = cumulation.get();
if (cumulation.remaining() < hostnameSize) {
cumulation.reset();
LOGGER.warn("数据包不完整 {}", uuid);
return;
}
byte[] b = new byte[hostnameSize];
for (int i = 0; i < hostnameSize; i++) {
b[i] = cumulation.get();
}
host = new String(b, "utf-8");
//按照大端
port = Utils.byteToIntV2(cumulation.get()) * 256 + Utils.byteToIntV2(cumulation.get());
LOGGER.info("IPV4 host:{} port:{} remoteAddress:{} {}", host, port, channel.getRemoteAddress(), uuid);
} else if (0x04 == ATYP) {//IPV6
LOGGER.warn("不支持IPV6访问 {}", uuid);
closeChildChannel();
return;
} else {
LOGGER.warn("不知道的访问方式 {}", uuid);
closeChildChannel();
return;
}
//说明正常读取结束,切换为写模式。
cumulation.clear();
writeBuffer.put((byte) 5);
writeBuffer.put((byte) 0);
writeBuffer.put((byte) 0);
//这里写死,后面紧接着6位hose和port
writeBuffer.put((byte) 1);
//put host
writeBuffer.put(new byte[]{0, 0, 0, 0});
//put port
writeBuffer.put(new byte[]{0, 0});
writeBuffer.flip();
channel.write(writeBuffer);
writeBuffer.clear();
//建立异步连接
Resource resource = new RemoteConnect(host, port, uuid, channel,this).connect();
if (Objects.nonNull(resource)) {
//更换附件
DeliverHandler deliverHandler = new DeliverHandler(channelWrapped, resource);
channelWrapped.key().attach(deliverHandler);
} else {
closeChildChannel();
}
}
客户端发送代理的协议数据。
服务端访问代理的ip和端口并将代理协议数据转发给客户端。
public class DeliverHandler extends AbstractHandler {
Resource resource;
public DeliverHandler(ChannelWrapped channelWrapped, Resource resource) {
super(channelWrapped);
this.resource = resource;
}
public void exec() throws IOException {
CompositeByteBuf cumulation = channelWrapped.cumulation();
String uuid = channelWrapped.uuid();
//判断当前channel是否已经关闭了
if (!channelWrapped.channel().isOpen()) {
LOGGER.warn("channel 已经关闭 {}", uuid);
return;
}
//获取服务端数据
cumulation.write(resource.remoteChannel());
//清除读取的数据
cumulation.clear();
LOGGER.info("child -> remote end {}", uuid);
}
public void after() {
String uuid = channelWrapped.uuid();
if (Objects.isNull(resource)) {
// 走到这里说明连接远端地址失败,因为他会关闭流,所以跳过即可。
LOGGER.warn("exception child close {}", uuid);
return;
}
try {
resource.closeRemote();
} catch (IOException e) {
LOGGER.error("child close " + uuid, e);
}
}
}