服务端收到UDP数据包转发至客户端调通
This commit is contained in:
+1
-1
@@ -47,7 +47,7 @@ public class TcpProxyChannelHandler extends SimpleChannelInboundHandler<ProxyMes
|
||||
realServerChannel.close();
|
||||
}
|
||||
|
||||
ProxyUtil.removeProxyChanel(ctx.channel());
|
||||
ProxyUtil.removeTcpProxyChanel(ctx.channel());
|
||||
super.channelInactive(ctx);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ public class UdpProxyChannelHandler extends SimpleChannelInboundHandler<ProxyMes
|
||||
realServerChannel.close();
|
||||
}
|
||||
|
||||
ProxyUtil.removeProxyChanel(ctx.channel());
|
||||
ProxyUtil.removeTcpProxyChanel(ctx.channel());
|
||||
super.channelInactive(ctx);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ public class ProxyMessageDisconnectHandler implements ProxyMessageHandler {
|
||||
Channel realServerChannel = ctx.channel().attr(Constants.NEXT_CHANNEL).get();
|
||||
if (null != realServerChannel) {
|
||||
ctx.channel().attr(Constants.NEXT_CHANNEL).remove();
|
||||
ProxyUtil.returnProxyChanel(ctx.channel());
|
||||
ProxyUtil.returnTcpProxyChanel(ctx.channel());
|
||||
realServerChannel.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
ctx.close();
|
||||
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package org.dromara.neutrinoproxy.client.handler;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.neutrinoproxy.client.config.ProxyConfig;
|
||||
import org.dromara.neutrinoproxy.client.core.ProxyChannelBorrowListener;
|
||||
import org.dromara.neutrinoproxy.client.util.ProxyUtil;
|
||||
import org.dromara.neutrinoproxy.core.Constants;
|
||||
import org.dromara.neutrinoproxy.core.ProxyDataTypeEnum;
|
||||
import org.dromara.neutrinoproxy.core.ProxyMessage;
|
||||
import org.dromara.neutrinoproxy.core.ProxyMessageHandler;
|
||||
import org.dromara.neutrinoproxy.core.dispatcher.Match;
|
||||
import org.noear.solon.annotation.Component;
|
||||
import org.noear.solon.annotation.Inject;
|
||||
|
||||
/**
|
||||
* @author: aoshiguchen
|
||||
* @date: 2023/9/19
|
||||
*/
|
||||
@Slf4j
|
||||
@Match(type = Constants.ProxyDataTypeName.UDP_CONNECT)
|
||||
@Component
|
||||
public class UdpProxyMessageConnectHandler implements ProxyMessageHandler {
|
||||
@Inject
|
||||
private ProxyConfig proxyConfig;
|
||||
@Inject("udpProxyTunnelBootstrap")
|
||||
private Bootstrap udpProxyTunnelBootstrap;
|
||||
|
||||
@Override
|
||||
public void handle(ChannelHandlerContext ctx, ProxyMessage proxyMessage) {
|
||||
final Channel cmdChannel = ctx.channel();
|
||||
final ProxyMessage.UdpBaseInfo udpBaseInfo = JSONObject.parseObject(proxyMessage.getInfo(), ProxyMessage.UdpBaseInfo.class);
|
||||
log.info("[UDP connect]info:{}", proxyMessage.getInfo());
|
||||
|
||||
// 获取连接
|
||||
ProxyUtil.borrowTcpProxyChanel(udpProxyTunnelBootstrap, new ProxyChannelBorrowListener() {
|
||||
|
||||
@Override
|
||||
public void success(Channel channel) {
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildUdpConnectMessage(new ProxyMessage.UdpBaseInfo()
|
||||
.setVisitorId(udpBaseInfo.getVisitorId())
|
||||
.setServerPort(udpBaseInfo.getServerPort())
|
||||
.setTargetIp(udpBaseInfo.getTargetIp())
|
||||
.setTargetPort(udpBaseInfo.getTargetPort())
|
||||
).setData(proxyConfig.getTunnel().getLicenseKey().getBytes()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(Throwable cause) {
|
||||
cmdChannel.writeAndFlush(ProxyMessage.buildDisconnectMessage(udpBaseInfo.toJsonString()));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return ProxyDataTypeEnum.UDP_CONNECT.getDesc();
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package org.dromara.neutrinoproxy.client.handler;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.neutrinoproxy.core.Constants;
|
||||
import org.dromara.neutrinoproxy.core.ProxyDataTypeEnum;
|
||||
import org.dromara.neutrinoproxy.core.ProxyMessage;
|
||||
import org.dromara.neutrinoproxy.core.ProxyMessageHandler;
|
||||
import org.dromara.neutrinoproxy.core.dispatcher.Match;
|
||||
import org.noear.solon.annotation.Component;
|
||||
|
||||
/**
|
||||
* @author: aoshiguchen
|
||||
* @date: 2023/9/20
|
||||
*/
|
||||
@Slf4j
|
||||
@Match(type = Constants.ProxyDataTypeName.UDP_TRANSFER)
|
||||
@Component
|
||||
public class UdpProxyMessageTransferHandler implements ProxyMessageHandler {
|
||||
|
||||
|
||||
@Override
|
||||
public void handle(ChannelHandlerContext ctx, ProxyMessage proxyMessage) {
|
||||
final ProxyMessage.UdpBaseInfo udpBaseInfo = JSONObject.parseObject(proxyMessage.getInfo(), ProxyMessage.UdpBaseInfo.class);
|
||||
log.info("[UDP transfer]info:{} data:{}", proxyMessage.getInfo(), new String(proxyMessage.getData()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return ProxyDataTypeEnum.UDP_TRANSFER.getDesc();
|
||||
}
|
||||
}
|
||||
+42
-7
@@ -64,7 +64,8 @@ public class ProxyUtil {
|
||||
|
||||
private static Map<String, Channel> realServerChannels = new ConcurrentHashMap<String, Channel>();
|
||||
|
||||
private static ConcurrentLinkedQueue<Channel> proxyChannelPool = new ConcurrentLinkedQueue<Channel>();
|
||||
private static ConcurrentLinkedQueue<Channel> tcpProxyChannelPool = new ConcurrentLinkedQueue<Channel>();
|
||||
private static ConcurrentLinkedQueue<Channel> udpProxyChannelPool = new ConcurrentLinkedQueue<>();
|
||||
|
||||
private static volatile Channel cmdChannel;
|
||||
|
||||
@@ -72,7 +73,7 @@ public class ProxyUtil {
|
||||
private static final String CLIENT_ID_FILE = ".NEUTRINO_PROXY_CLIENT_ID";
|
||||
|
||||
public static void borrowTcpProxyChanel(Bootstrap tcpProxyTunnelBootstrap, final ProxyChannelBorrowListener borrowListener) {
|
||||
Channel channel = proxyChannelPool.poll();
|
||||
Channel channel = tcpProxyChannelPool.poll();
|
||||
if (null != channel) {
|
||||
borrowListener.success(channel);
|
||||
return;
|
||||
@@ -87,18 +88,52 @@ public class ProxyUtil {
|
||||
});
|
||||
}
|
||||
|
||||
public static void returnProxyChanel(Channel proxyChanel) {
|
||||
if (proxyChannelPool.size() > MAX_POOL_SIZE) {
|
||||
public static void returnTcpProxyChanel(Channel proxyChanel) {
|
||||
if (tcpProxyChannelPool.size() > MAX_POOL_SIZE) {
|
||||
proxyChanel.close();
|
||||
} else {
|
||||
proxyChanel.config().setOption(ChannelOption.AUTO_READ, true);
|
||||
proxyChanel.attr(Constants.NEXT_CHANNEL).remove();
|
||||
proxyChannelPool.offer(proxyChanel);
|
||||
tcpProxyChannelPool.offer(proxyChanel);
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeProxyChanel(Channel proxyChanel) {
|
||||
proxyChannelPool.remove(proxyChanel);
|
||||
|
||||
|
||||
public static void removeTcpProxyChanel(Channel proxyChanel) {
|
||||
tcpProxyChannelPool.remove(proxyChanel);
|
||||
}
|
||||
|
||||
public static void borrowUdpProxyChanel(Bootstrap tcpProxyTunnelBootstrap, final ProxyChannelBorrowListener borrowListener) {
|
||||
Channel channel = udpProxyChannelPool.poll();
|
||||
if (null != channel) {
|
||||
borrowListener.success(channel);
|
||||
return;
|
||||
}
|
||||
|
||||
tcpProxyTunnelBootstrap.connect().addListener((ChannelFutureListener) future -> {
|
||||
if (future.isSuccess()) {
|
||||
borrowListener.success(future.channel());
|
||||
} else {
|
||||
borrowListener.error(future.cause());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void returnUdpProxyChanel(Channel proxyChanel) {
|
||||
if (udpProxyChannelPool.size() > MAX_POOL_SIZE) {
|
||||
proxyChanel.close();
|
||||
} else {
|
||||
proxyChanel.config().setOption(ChannelOption.AUTO_READ, true);
|
||||
proxyChanel.attr(Constants.NEXT_CHANNEL).remove();
|
||||
udpProxyChannelPool.offer(proxyChanel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void removeUdpProxyChanel(Channel proxyChanel) {
|
||||
udpProxyChannelPool.remove(proxyChanel);
|
||||
}
|
||||
|
||||
public static void setCmdChannel(Channel cmdChannel) {
|
||||
|
||||
@@ -38,6 +38,9 @@ public interface Constants {
|
||||
|
||||
AttributeKey<Integer> LICENSE_ID = AttributeKey.newInstance("license_id");
|
||||
|
||||
AttributeKey<String> TARGET_IP = AttributeKey.newInstance("targetIp");
|
||||
AttributeKey<Integer> TARGET_PORT = AttributeKey.newInstance("targetPort");
|
||||
|
||||
int HEADER_SIZE = 4;
|
||||
int TYPE_SIZE = 1;
|
||||
int SERIAL_NUMBER_SIZE = 8;
|
||||
@@ -49,6 +52,9 @@ public interface Constants {
|
||||
String CONNECT = "CONNECT";
|
||||
String DISCONNECT = "DISCONNECT";
|
||||
String TRANSFER = "TRANSFER";
|
||||
String UDP_CONNECT = "UDP_CONNECT";
|
||||
String UDP_DISCONNECT = "UDP_DISCONNECT";
|
||||
String UDP_TRANSFER = "UDP_TRANSFER";
|
||||
String ERROR = "ERROR";
|
||||
String PORT_MAPPING_SYNC = "PORT_MAPPING_SYNC";
|
||||
}
|
||||
|
||||
+4
-1
@@ -44,7 +44,10 @@ public enum ProxyDataTypeEnum {
|
||||
DISCONNECT(0x04, Constants.ProxyDataTypeName.DISCONNECT,"DISCONNECT"),
|
||||
TRANSFER(0x05, Constants.ProxyDataTypeName.TRANSFER,"TRANSFER"),
|
||||
ERROR(0x06, Constants.ProxyDataTypeName.ERROR,"ERROR"),
|
||||
PORT_MAPPING_SYNC(0x07, Constants.ProxyDataTypeName.PORT_MAPPING_SYNC, "PORT_MAPPING_SYNC");
|
||||
PORT_MAPPING_SYNC(0x07, Constants.ProxyDataTypeName.PORT_MAPPING_SYNC, "PORT_MAPPING_SYNC"),
|
||||
UDP_CONNECT(0x08, Constants.ProxyDataTypeName.UDP_CONNECT,"UDP_CONNECT"),
|
||||
UDP_DISCONNECT(0x09, Constants.ProxyDataTypeName.UDP_DISCONNECT,"UDP_DISCONNECT"),
|
||||
UDP_TRANSFER(0x10, Constants.ProxyDataTypeName.UDP_TRANSFER,"UDP_TRANSFER");
|
||||
private static Map<Integer,ProxyDataTypeEnum> cache = Stream.of(values()).collect(Collectors.toMap(ProxyDataTypeEnum::getType, Function.identity()));
|
||||
|
||||
private int type;
|
||||
|
||||
@@ -66,10 +66,18 @@ public class ProxyMessage {
|
||||
* 通用异常信息
|
||||
*/
|
||||
public static final byte TYPE_ERROR = 0x06;
|
||||
/**
|
||||
* UDP代理隧道连接
|
||||
*/
|
||||
public static final byte TYPE_UDP_CONNECT = 0x08;
|
||||
/**
|
||||
* UDP代理隧道断开连接
|
||||
*/
|
||||
public static final byte TYPE_UDP_DISCONNECT = 0x09;
|
||||
/**
|
||||
* UDP数据传输
|
||||
*/
|
||||
private static final byte TYPE_UDP_TRANSFER = 0x08;
|
||||
public static final byte TYPE_UDP_TRANSFER = 0x10;
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
@@ -134,15 +142,19 @@ public class ProxyMessage {
|
||||
.setData(data);
|
||||
}
|
||||
|
||||
public static ProxyMessage buildUdpTransferMessage(String visitorIp, int visitorPort, String targetIp, int targetPort, byte[] data) {
|
||||
|
||||
public static ProxyMessage buildUdpConnectMessage(UdpBaseInfo info) {
|
||||
return create().setType(TYPE_UDP_CONNECT)
|
||||
.setInfo(info.toJsonString());
|
||||
}
|
||||
|
||||
public static ProxyMessage buildUdpDisconnectMessage() {
|
||||
return create().setType(TYPE_UDP_DISCONNECT);
|
||||
}
|
||||
|
||||
public static ProxyMessage buildUdpTransferMessage(UdpBaseInfo info) {
|
||||
return create().setType(TYPE_UDP_TRANSFER)
|
||||
.setInfo(JSONObject.toJSONString(new UdpBaseInfo()
|
||||
.setVisitorIp(visitorIp)
|
||||
.setVisitorPort(visitorPort)
|
||||
.setTargetIp(targetIp)
|
||||
.setTargetPort(targetPort)
|
||||
))
|
||||
.setData(data);
|
||||
.setInfo(info.toJsonString());
|
||||
}
|
||||
|
||||
public static ProxyMessage buildErrMessage(ExceptionEnum exceptionEnum, String info) {
|
||||
@@ -161,9 +173,14 @@ public class ProxyMessage {
|
||||
@Accessors(chain = true)
|
||||
@Data
|
||||
public static class UdpBaseInfo {
|
||||
private String visitorId;
|
||||
private String visitorIp;
|
||||
private int visitorPort;
|
||||
private int serverPort;
|
||||
private String targetIp;
|
||||
private int targetPort;
|
||||
public String toJsonString() {
|
||||
return JSONObject.toJSONString(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
@@ -74,6 +74,14 @@ public interface PortMappingMapper extends BaseMapper<PortMappingDO> {
|
||||
);
|
||||
}
|
||||
|
||||
default PortMappingDO findByLicenseIdAndServerPort(Integer licenseId, Integer serverPort) {
|
||||
return this.selectOne(new LambdaQueryWrapper<PortMappingDO>()
|
||||
.eq(PortMappingDO::getLicenseId, licenseId)
|
||||
.eq(PortMappingDO::getServerPort, serverPort)
|
||||
.last("limit 1")
|
||||
);
|
||||
}
|
||||
|
||||
default void updateOnlineStatus(Integer licenseId,Integer serverPort, Integer isOnline, Date updateTime) {
|
||||
this.update(null, new LambdaUpdateWrapper<PortMappingDO>()
|
||||
.eq(PortMappingDO::getLicenseId, licenseId)
|
||||
|
||||
+2
-1
@@ -4,6 +4,7 @@ import cn.hutool.core.util.StrUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.neutrinoproxy.core.Constants;
|
||||
import org.dromara.neutrinoproxy.core.ProxyMessage;
|
||||
import org.dromara.neutrinoproxy.server.constant.NetworkProtocolEnum;
|
||||
import org.dromara.neutrinoproxy.server.proxy.domain.VisitorChannelAttachInfo;
|
||||
import org.dromara.neutrinoproxy.server.service.FlowReportService;
|
||||
import org.dromara.neutrinoproxy.server.util.ProxyUtil;
|
||||
@@ -76,7 +77,7 @@ public class TcpVisitorChannelHandler extends SimpleChannelInboundHandler<ByteBu
|
||||
visitorChannel.config().setOption(ChannelOption.AUTO_READ, false);
|
||||
|
||||
String visitorId = ProxyUtil.newVisitorId();
|
||||
ProxyUtil.addVisitorChannelToCmdChannel(cmdChannel, visitorId, visitorChannel, sa.getPort());
|
||||
ProxyUtil.addVisitorChannelToCmdChannel(NetworkProtocolEnum.TCP, cmdChannel, visitorId, visitorChannel, sa.getPort());
|
||||
cmdChannel.writeAndFlush(ProxyMessage.buildConnectMessage(visitorId).setData(lanInfo.getBytes()));
|
||||
|
||||
super.channelActive(ctx);
|
||||
|
||||
+61
-10
@@ -6,8 +6,14 @@ import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.channel.socket.DatagramPacket;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.neutrinoproxy.core.Constants;
|
||||
import org.dromara.neutrinoproxy.core.ProxyMessage;
|
||||
import org.dromara.neutrinoproxy.server.constant.NetworkProtocolEnum;
|
||||
import org.dromara.neutrinoproxy.server.proxy.domain.VisitorChannelAttachInfo;
|
||||
import org.dromara.neutrinoproxy.server.service.FlowReportService;
|
||||
import org.dromara.neutrinoproxy.server.util.ProxyUtil;
|
||||
import org.noear.solon.Solon;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@@ -16,12 +22,46 @@ import java.nio.charset.StandardCharsets;
|
||||
* @author: aoshiguchen
|
||||
* @date: 2023/9/16
|
||||
*/
|
||||
@Slf4j
|
||||
public class UdpVisitorChannelHandler extends SimpleChannelInboundHandler<DatagramPacket> {
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket datagramPacket) throws Exception {
|
||||
System.out.println("channelId:" + ctx.channel().id().asLongText());
|
||||
System.out.println("服务端接收到消息 \nsender:" + datagramPacket.sender().toString() + "内容\n" + datagramPacket.content().toString(StandardCharsets.UTF_8));
|
||||
|
||||
// 通知代理客户端
|
||||
Channel visitorChannel = ctx.channel();
|
||||
Channel proxyChannel = visitorChannel.attr(Constants.NEXT_CHANNEL).get();
|
||||
|
||||
if (null == proxyChannel) {
|
||||
// 该端口还没有代理客户端
|
||||
ctx.channel().close();
|
||||
return;
|
||||
}
|
||||
String targetIp = proxyChannel.attr(Constants.TARGET_IP).get();
|
||||
int targetPort = proxyChannel.attr(Constants.TARGET_PORT).get();
|
||||
|
||||
// 转发代理数据
|
||||
byte[] bytes = new byte[datagramPacket.content().readableBytes()];
|
||||
datagramPacket.content().readBytes(bytes);
|
||||
String visitorId = ProxyUtil.getVisitorIdByChannel(visitorChannel);
|
||||
proxyChannel.writeAndFlush(ProxyMessage.buildUdpTransferMessage(new ProxyMessage.UdpBaseInfo()
|
||||
.setVisitorId(visitorId)
|
||||
.setVisitorIp(datagramPacket.sender().getAddress().getHostAddress())
|
||||
.setVisitorPort(datagramPacket.sender().getPort())
|
||||
.setTargetIp(targetIp)
|
||||
.setTargetPort(targetPort)
|
||||
).setData(bytes));
|
||||
|
||||
// 增加流量计数
|
||||
VisitorChannelAttachInfo visitorChannelAttachInfo = ProxyUtil.getAttachInfo(visitorChannel);
|
||||
Solon.context().getBean(FlowReportService.class).addWriteByte(visitorChannelAttachInfo.getLicenseId(), bytes.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
System.out.println("active channelId:" + ctx.channel().id().asLongText());
|
||||
Channel visitorChannel = ctx.channel();
|
||||
InetSocketAddress sa = (InetSocketAddress) visitorChannel.localAddress();
|
||||
Channel cmdChannel = ProxyUtil.getCmdChannelByServerPort(sa.getPort());
|
||||
@@ -43,16 +83,27 @@ public class UdpVisitorChannelHandler extends SimpleChannelInboundHandler<Datagr
|
||||
String targetIp = targetInfo[0];
|
||||
int targetPort = Integer.parseInt(targetInfo[1]);
|
||||
|
||||
// 转发代理数据
|
||||
byte[] bytes = new byte[datagramPacket.content().readableBytes()];
|
||||
datagramPacket.content().readBytes(bytes);
|
||||
// 用户连接到代理服务器时,设置用户连接不可读,等待代理后端服务器连接成功后再改变为可读状态
|
||||
visitorChannel.config().setOption(ChannelOption.AUTO_READ, false);
|
||||
|
||||
cmdChannel.writeAndFlush(ProxyMessage.buildUdpTransferMessage(
|
||||
sa.getAddress().getHostAddress(),
|
||||
sa.getPort(),
|
||||
targetIp,
|
||||
targetPort,
|
||||
bytes
|
||||
));
|
||||
// UDP此处叫visitor似有不妥,与TCP不同
|
||||
String visitorId = ProxyUtil.newVisitorId();
|
||||
// 此处需要和tcp分开
|
||||
ProxyUtil.addVisitorChannelToCmdChannel(NetworkProtocolEnum.UDP, cmdChannel, visitorId, visitorChannel, sa.getPort());
|
||||
cmdChannel.writeAndFlush(ProxyMessage.buildUdpConnectMessage(new ProxyMessage.UdpBaseInfo()
|
||||
.setVisitorId(visitorId)
|
||||
.setServerPort(sa.getPort())
|
||||
.setTargetIp(targetIp)
|
||||
.setTargetPort(targetPort)
|
||||
));
|
||||
|
||||
super.channelActive(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 当出现异常就关闭连接
|
||||
ctx.close();
|
||||
log.error("[UDP Visitor Channel]VisitorChannel error", cause);
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -23,6 +23,7 @@ package org.dromara.neutrinoproxy.server.proxy.domain;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.dromara.neutrinoproxy.server.constant.NetworkProtocolEnum;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -32,6 +33,7 @@ import lombok.experimental.Accessors;
|
||||
@Accessors(chain = true)
|
||||
@Data
|
||||
public class VisitorChannelAttachInfo {
|
||||
private NetworkProtocolEnum protocol;
|
||||
private String visitorId;
|
||||
private String lanInfo;
|
||||
private Integer serverPort;
|
||||
|
||||
+2
-1
@@ -10,6 +10,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dromara.neutrinoproxy.core.Constants;
|
||||
import org.dromara.neutrinoproxy.core.ProxyMessage;
|
||||
import org.dromara.neutrinoproxy.server.constant.NetworkProtocolEnum;
|
||||
import org.dromara.neutrinoproxy.server.proxy.domain.ProxyAttachment;
|
||||
import org.dromara.neutrinoproxy.server.proxy.domain.VisitorChannelAttachInfo;
|
||||
import org.dromara.neutrinoproxy.server.service.FlowReportService;
|
||||
@@ -98,7 +99,7 @@ public class HttpVisitorChannelHandler extends SimpleChannelInboundHandler<ByteB
|
||||
}
|
||||
|
||||
visitorId = ProxyUtil.newVisitorId();
|
||||
ProxyUtil.addVisitorChannelToCmdChannel(cmdChannel, visitorId, ctx.channel(), serverPort);
|
||||
ProxyUtil.addVisitorChannelToCmdChannel(NetworkProtocolEnum.HTTP, cmdChannel, visitorId, ctx.channel(), serverPort);
|
||||
ProxyUtil.addProxyConnectAttachment(visitorId, proxyAttachment);
|
||||
cmdChannel.writeAndFlush(ProxyMessage.buildConnectMessage(visitorId).setData(lanInfo.getBytes()));
|
||||
}
|
||||
|
||||
+6
-6
@@ -34,14 +34,14 @@ public class ProxyMessageConnectHandler implements ProxyMessageHandler {
|
||||
public void handle(ChannelHandlerContext ctx, ProxyMessage proxyMessage) {
|
||||
String info = proxyMessage.getInfo();
|
||||
if (StrUtil.isEmpty(info)) {
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildErrMessage(ExceptionEnum.CONNECT_FAILED, "info不能为空!"));
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildErrMessage(ExceptionEnum.CONNECT_FAILED, "info cannot be empty!"));
|
||||
ctx.channel().close();
|
||||
return;
|
||||
}
|
||||
|
||||
String[] tokens = info.split("@");
|
||||
if (tokens.length != 2) {
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildErrMessage(ExceptionEnum.CONNECT_FAILED, "info格式有误!"));
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildErrMessage(ExceptionEnum.CONNECT_FAILED, "info format error!"));
|
||||
ctx.channel().close();
|
||||
return;
|
||||
}
|
||||
@@ -50,18 +50,18 @@ public class ProxyMessageConnectHandler implements ProxyMessageHandler {
|
||||
|
||||
LicenseDO licenseDO = licenseService.findByKey(licenseKey);
|
||||
if (null == licenseDO) {
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildErrMessage(ExceptionEnum.CONNECT_FAILED, "license不存在!"));
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildErrMessage(ExceptionEnum.CONNECT_FAILED, "the license notfound!"));
|
||||
ctx.channel().close();
|
||||
return;
|
||||
}
|
||||
if (EnableStatusEnum.DISABLE.getStatus().equals(licenseDO.getEnable())) {
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildErrMessage(ExceptionEnum.CONNECT_FAILED, "当前license已被禁用!"));
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildErrMessage(ExceptionEnum.CONNECT_FAILED, "the license invalid!"));
|
||||
ctx.channel().close();
|
||||
return;
|
||||
}
|
||||
UserDO userDO = userService.findById(licenseDO.getUserId());
|
||||
if (null == userDO || EnableStatusEnum.DISABLE.getStatus().equals(userDO.getEnable())) {
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildErrMessage(ExceptionEnum.CONNECT_FAILED, "当前license无效!"));
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildErrMessage(ExceptionEnum.CONNECT_FAILED, "the license invalid!"));
|
||||
ctx.channel().close();
|
||||
return;
|
||||
}
|
||||
@@ -69,7 +69,7 @@ public class ProxyMessageConnectHandler implements ProxyMessageHandler {
|
||||
Channel cmdChannel = ProxyUtil.getCmdChannelByLicenseId(licenseDO.getId());
|
||||
|
||||
if (null == cmdChannel) {
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildErrMessage(ExceptionEnum.CONNECT_FAILED, "服务端异常,指令通道不存在!"));
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildErrMessage(ExceptionEnum.CONNECT_FAILED, "server error,cmd channel notfound!"));
|
||||
ctx.channel().close();
|
||||
return;
|
||||
}
|
||||
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
package org.dromara.neutrinoproxy.server.proxy.handler;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.neutrinoproxy.core.*;
|
||||
import org.dromara.neutrinoproxy.core.dispatcher.Match;
|
||||
import org.dromara.neutrinoproxy.server.constant.EnableStatusEnum;
|
||||
import org.dromara.neutrinoproxy.server.dal.PortMappingMapper;
|
||||
import org.dromara.neutrinoproxy.server.dal.entity.LicenseDO;
|
||||
import org.dromara.neutrinoproxy.server.dal.entity.PortMappingDO;
|
||||
import org.dromara.neutrinoproxy.server.dal.entity.UserDO;
|
||||
import org.dromara.neutrinoproxy.server.service.LicenseService;
|
||||
import org.dromara.neutrinoproxy.server.service.UserService;
|
||||
import org.dromara.neutrinoproxy.server.util.ProxyUtil;
|
||||
import org.noear.solon.annotation.Component;
|
||||
import org.noear.solon.annotation.Inject;
|
||||
|
||||
/**
|
||||
* @author: aoshiguchen
|
||||
* @date: 2023/9/19
|
||||
*/
|
||||
@Slf4j
|
||||
@Match(type = Constants.ProxyDataTypeName.UDP_CONNECT)
|
||||
@Component
|
||||
public class UdpProxyMessageConnectHandler implements ProxyMessageHandler {
|
||||
@Inject
|
||||
private LicenseService licenseService;
|
||||
@Inject
|
||||
private UserService userService;
|
||||
@Inject
|
||||
private PortMappingMapper portMappingMapper;
|
||||
|
||||
@Override
|
||||
public void handle(ChannelHandlerContext ctx, ProxyMessage proxyMessage) {
|
||||
final Channel proxyChannel = ctx.channel();
|
||||
final ProxyMessage.UdpBaseInfo udpBaseInfo = JSONObject.parseObject(proxyMessage.getInfo(), ProxyMessage.UdpBaseInfo.class);
|
||||
final String licenseKey = new String(proxyMessage.getData());
|
||||
log.info("[UDP connect]info:{} licenseKey:{}", proxyMessage.getInfo(), licenseKey);
|
||||
|
||||
LicenseDO licenseDO = licenseService.findByKey(licenseKey);
|
||||
if (null == licenseDO) {
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildErrMessage(ExceptionEnum.CONNECT_FAILED, "the license notfound!"));
|
||||
ctx.channel().close();
|
||||
return;
|
||||
}
|
||||
if (EnableStatusEnum.DISABLE.getStatus().equals(licenseDO.getEnable())) {
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildErrMessage(ExceptionEnum.CONNECT_FAILED, "the license disabled!"));
|
||||
ctx.channel().close();
|
||||
return;
|
||||
}
|
||||
UserDO userDO = userService.findById(licenseDO.getUserId());
|
||||
if (null == userDO || EnableStatusEnum.DISABLE.getStatus().equals(userDO.getEnable())) {
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildErrMessage(ExceptionEnum.CONNECT_FAILED, "the license invalid!"));
|
||||
ctx.channel().close();
|
||||
return;
|
||||
}
|
||||
|
||||
Channel cmdChannel = ProxyUtil.getCmdChannelByLicenseId(licenseDO.getId());
|
||||
|
||||
if (null == cmdChannel) {
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildErrMessage(ExceptionEnum.CONNECT_FAILED, "server error,cmd channel notfound!"));
|
||||
ctx.channel().close();
|
||||
return;
|
||||
}
|
||||
|
||||
Channel visitorChannel = ProxyUtil.getVisitorChannel(cmdChannel, udpBaseInfo.getVisitorId());
|
||||
if (null == visitorChannel) {
|
||||
return;
|
||||
}
|
||||
PortMappingDO portMappingDO = portMappingMapper.findByLicenseIdAndServerPort(licenseDO.getId(), udpBaseInfo.getServerPort());
|
||||
if (null == portMappingDO || !EnableStatusEnum.ENABLE.getStatus().equals(portMappingDO.getEnable())) {
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildErrMessage(ExceptionEnum.CONNECT_FAILED, "server error, port mapping notfound!"));
|
||||
ctx.channel().close();
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.channel().attr(Constants.VISITOR_ID).set(udpBaseInfo.getVisitorId());
|
||||
ctx.channel().attr(Constants.LICENSE_ID).set(licenseDO.getId());
|
||||
ctx.channel().attr(Constants.NEXT_CHANNEL).set(visitorChannel);
|
||||
ctx.channel().attr(Constants.TARGET_IP).set(portMappingDO.getClientIp());
|
||||
ctx.channel().attr(Constants.TARGET_PORT).set(portMappingDO.getClientPort());
|
||||
visitorChannel.attr(Constants.NEXT_CHANNEL).set(ctx.channel());
|
||||
// 代理客户端与后端服务器连接成功,修改用户连接为可读状态
|
||||
visitorChannel.config().setOption(ChannelOption.AUTO_READ, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return ProxyDataTypeEnum.UDP_CONNECT.getDesc();
|
||||
}
|
||||
}
|
||||
+12
-8
@@ -5,6 +5,7 @@ import com.google.common.collect.Sets;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dromara.neutrinoproxy.core.ChannelAttribute;
|
||||
import org.dromara.neutrinoproxy.core.Constants;
|
||||
import org.dromara.neutrinoproxy.server.constant.NetworkProtocolEnum;
|
||||
import org.dromara.neutrinoproxy.server.proxy.domain.CmdChannelAttachInfo;
|
||||
import org.dromara.neutrinoproxy.server.proxy.domain.ProxyAttachment;
|
||||
import org.dromara.neutrinoproxy.server.proxy.domain.ProxyMapping;
|
||||
@@ -201,18 +202,21 @@ public class ProxyUtil {
|
||||
* @param visitorId
|
||||
* @param visitorChannel
|
||||
*/
|
||||
public static void addVisitorChannelToCmdChannel(Channel cmdChannel, String visitorId, Channel visitorChannel, Integer serverPort) {
|
||||
public static void addVisitorChannelToCmdChannel(NetworkProtocolEnum protocol, Channel cmdChannel, String visitorId, Channel visitorChannel, Integer serverPort) {
|
||||
InetSocketAddress sa = (InetSocketAddress) visitorChannel.localAddress();
|
||||
String lanInfo = getClientLanInfoByServerPort(sa.getPort());
|
||||
CmdChannelAttachInfo cmdChannelAttachInfo = getAttachInfo(cmdChannel);
|
||||
|
||||
setAttachInfo(visitorChannel, new VisitorChannelAttachInfo()
|
||||
.setVisitorId(visitorId)
|
||||
.setLanInfo(lanInfo)
|
||||
.setServerPort(serverPort)
|
||||
.setLicenseId(cmdChannelAttachInfo.getLicenseId())
|
||||
.setIp(((InetSocketAddress)visitorChannel.remoteAddress()).getAddress().getHostAddress())
|
||||
);
|
||||
VisitorChannelAttachInfo attachInfo = new VisitorChannelAttachInfo()
|
||||
.setProtocol(protocol)
|
||||
.setVisitorId(visitorId)
|
||||
.setLanInfo(lanInfo)
|
||||
.setServerPort(serverPort)
|
||||
.setLicenseId(cmdChannelAttachInfo.getLicenseId());
|
||||
if (NetworkProtocolEnum.UDP != protocol) {
|
||||
attachInfo.setIp(((InetSocketAddress)visitorChannel.remoteAddress()).getAddress().getHostAddress());
|
||||
}
|
||||
setAttachInfo(visitorChannel, attachInfo);
|
||||
userChannelMapLock.writeLock().lock();
|
||||
try {
|
||||
cmdChannelAttachInfo.getVisitorChannelMap().put(visitorId, visitorChannel);
|
||||
|
||||
Reference in New Issue
Block a user