【fix】 添加协议解析处理

This commit is contained in:
wujiawei 2025-03-21 20:57:44 +08:00
parent cf9438a7da
commit 3321e0dd7b
41 changed files with 2162 additions and 23 deletions

View File

@ -24,6 +24,7 @@
<module>wu-lazy-cloud-heartbeat-client</module>
<module>wu-lazy-cloud-heartbeat-common</module>
<module>wu-lazy-cloud-heartbeat-dns</module>
<module>wu-lazy-cloud-heartbeat-protocol-proxy</module>
<!-- 样例 -->
<module>wu-lazy-cloud-heartbeat-start</module>

View File

@ -23,6 +23,11 @@
<groupId>top.wu2020</groupId>
<artifactId>wu-lazy-cloud-heartbeat-common</artifactId>
</dependency>
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-lazy-cloud-heartbeat-protocol-proxy</artifactId>
<version>1.3.0-JDK17-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>

View File

@ -39,7 +39,7 @@ public interface SocketApplicationListener extends CommandLineRunner, Disposable
/**
* 运行
*
* @throws InterruptedException
* @throws InterruptedException 异常信息
*/
void doRunning() throws Exception;

View File

@ -1,4 +1,4 @@
package org.framework.lazy.cloud.network.heartbeat.dns;
package org.framework.lazy.cloud.network.heartbeat.protocol;
import org.springframework.context.annotation.ComponentScan;
import org.wu.framework.lazy.orm.core.stereotype.LazyScan;
@ -7,6 +7,6 @@ import org.wu.framework.lazy.orm.core.stereotype.LazyScan;
"org.framework.lazy.cloud.network.heartbeat.dns.standalone.infrastructure.entity",
"org.framework.lazy.cloud.network.heartbeat.dns.cluster.infrastructure.entity"
})
@ComponentScan(basePackages = "org.framework.lazy.cloud.network.heartbeat.dns")
@ComponentScan(basePackages = "org.framework.lazy.cloud.network.heartbeat.protocol")
public class EnableDnsAutoConfiguration {
}

View File

@ -1,4 +1,4 @@
package org.framework.lazy.cloud.network.heartbeat.dns.config;
package org.framework.lazy.cloud.network.heartbeat.protocol.config;
import org.springframework.beans.factory.config.BeanDefinition;

View File

@ -1,4 +1,4 @@
package org.framework.lazy.cloud.network.heartbeat.dns.config;
package org.framework.lazy.cloud.network.heartbeat.protocol.config;
import org.framework.lazy.cloud.network.heartbeat.common.adapter.ChannelFlowAdapter;
import org.framework.lazy.cloud.network.heartbeat.common.advanced.flow.HandleChannelFlowAdvanced;

View File

@ -1,4 +1,4 @@
package org.framework.lazy.cloud.network.heartbeat.dns.context;
package org.framework.lazy.cloud.network.heartbeat.protocol.context;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.common.context.SocketApplicationListener;

View File

@ -1,8 +1,8 @@
package org.framework.lazy.cloud.network.heartbeat.dns.init;
package org.framework.lazy.cloud.network.heartbeat.protocol.init;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.dns.context.NettyDnsSocketApplicationListener;
import org.framework.lazy.cloud.network.heartbeat.protocol.context.NettyDnsSocketApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

View File

@ -1,6 +1,6 @@
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.framework.lazy.cloud.network.heartbeat.dns.EnableDnsAutoConfiguration,\
org.framework.lazy.cloud.network.heartbeat.dns.config.DnsAutoConfiguration,\
org.framework.lazy.cloud.network.heartbeat.dns.init.InitDnsSocket,\
org.framework.lazy.cloud.network.heartbeat.dns.config.DnsFlowConfiguration
org.framework.lazy.cloud.network.heartbeat.protocol.EnableProtocolProxyAutoConfiguration,\
org.framework.lazy.cloud.network.heartbeat.protocol.config.ProtocolProxyAutoConfiguration,\
org.framework.lazy.cloud.network.heartbeat.protocol.init.InitProtocolProxySocket,\
org.framework.lazy.cloud.network.heartbeat.protocol.config.ProtocolProxyFlowConfiguration

View File

@ -1,4 +1,4 @@
org.framework.lazy.cloud.network.heartbeat.dns.EnableDnsAutoConfiguration
org.framework.lazy.cloud.network.heartbeat.dns.config.DnsAutoConfiguration
org.framework.lazy.cloud.network.heartbeat.dns.init.InitDnsSocket
org.framework.lazy.cloud.network.heartbeat.dns.config.DnsFlowConfiguration
org.framework.lazy.cloud.network.heartbeat.protocol.EnableProtocolProxyAutoConfiguration
org.framework.lazy.cloud.network.heartbeat.protocol.config.ProtocolProxyAutoConfiguration
org.framework.lazy.cloud.network.heartbeat.protocol.init.InitProtocolProxySocket
org.framework.lazy.cloud.network.heartbeat.protocol.config.ProtocolProxyFlowConfiguration

View File

@ -1,4 +1,4 @@
package org.framework.lazy.cloud.network.heartbeat.dns;
package org.framework.lazy.cloud.network.heartbeat.protocol;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;

View File

@ -1,4 +1,4 @@
package org.framework.lazy.cloud.network.heartbeat.dns;
package org.framework.lazy.cloud.network.heartbeat.protocol;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
@ -62,6 +62,7 @@ public class DnsServerHandler extends SimpleChannelInboundHandler<DatagramDnsQue
response.addRecord(DnsSection.ANSWER, new DefaultDnsRawRecord(
domain, DnsRecordType.A, 3600, Unpooled.wrappedBuffer(address.getAddress())));
}
// log.info("response:{}",response.toString());
ctx.writeAndFlush(response);
} catch (Exception e) {
e.printStackTrace();

View File

@ -0,0 +1,146 @@
package org.framework.lazy.cloud.network.heartbeat.protocol;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import java.net.URI;
public class HttpProxyServer {
private static final int PORT = 8080;
public static void main(String[] args) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new HttpClientCodec(),
new HttpObjectAggregator(1048576),
new HttpProxyServerHandler()
);
}
});
ChannelFuture f = b.bind(PORT).sync();
System.out.println("Proxy server started and listening on port " + PORT);
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
private static class HttpProxyServerHandler extends ChannelInboundHandlerAdapter {
private Channel outboundChannel;
@Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FullHttpRequest) {
FullHttpRequest request = (FullHttpRequest) msg;
URI uri = new URI(request.uri());
String host = uri.getHost();
int port = uri.getPort();
if (port == -1) {
port = 80;
}
Bootstrap b = new Bootstrap();
b.group(ctx.channel().eventLoop())
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new HttpClientCodec(),
new HttpObjectAggregator(1048576),
new ProxyBackendHandler(ctx.channel())
);
}
});
ChannelFuture f = b.connect(host, port);
outboundChannel = f.channel();
f.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
// 连接成功将客户端请求转发到目标服务器
outboundChannel.writeAndFlush(request.retain());
} else {
// 连接失败关闭客户端通道
ctx.channel().close();
}
}
});
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
if (outboundChannel != null) {
closeOnFlush(outboundChannel);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
closeOnFlush(ctx.channel());
}
static void closeOnFlush(Channel ch) {
if (ch.isActive()) {
ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
}
}
private static class ProxyBackendHandler extends ChannelInboundHandlerAdapter {
private final Channel inboundChannel;
ProxyBackendHandler(Channel inboundChannel) {
this.inboundChannel = inboundChannel;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// 将目标服务器的响应转发给客户端
inboundChannel.writeAndFlush(msg);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
closeOnFlush(inboundChannel);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
closeOnFlush(ctx.channel());
}
static void closeOnFlush(Channel ch) {
if (ch.isActive()) {
ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
}
}
}

View File

@ -0,0 +1,312 @@
package org.framework.lazy.cloud.network.heartbeat.protocol;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import java.net.InetSocketAddress;
import java.net.URI;
public class MultiProtocolProxyServer {
private static final int HTTP_PORT = 8080;
private static final int TCP_PORT = 9090;
private static final int UDP_PORT = 10000;
public static void main(String[] args) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// 启动 HTTP 代理服务器
startHttpProxy(bossGroup, workerGroup);
// 启动 TCP 代理服务器
startTcpProxy(bossGroup, workerGroup);
// 启动 UDP 代理服务器
startUdpProxy(workerGroup);
System.out.println("Proxy servers started.");
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
private static void startHttpProxy(EventLoopGroup bossGroup, EventLoopGroup workerGroup) throws InterruptedException {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new HttpClientCodec(),
new HttpObjectAggregator(1048576),
new HttpProxyServerHandler()
);
}
});
b.bind(HTTP_PORT).sync();
System.out.println("HTTP proxy server started on port " + HTTP_PORT);
}
private static void startTcpProxy(EventLoopGroup bossGroup, EventLoopGroup workerGroup) throws InterruptedException {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new TcpProxyServerHandler());
}
});
b.bind(TCP_PORT).sync();
System.out.println("TCP proxy server started on port " + TCP_PORT);
}
private static void startUdpProxy(EventLoopGroup workerGroup) throws InterruptedException {
Bootstrap b = new Bootstrap();
b.group(workerGroup)
.channel(NioDatagramChannel.class)
.handler(new UdpProxyServerHandler());
b.bind(UDP_PORT).sync();
System.out.println("UDP proxy server started on port " + UDP_PORT);
}
private static class HttpProxyServerHandler extends ChannelInboundHandlerAdapter {
private Channel outboundChannel;
@Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FullHttpRequest) {
FullHttpRequest request = (FullHttpRequest) msg;
URI uri = new URI(request.uri());
String host = uri.getHost();
int port = uri.getPort();
if (port == -1) {
port = 80;
}
Bootstrap b = new Bootstrap();
b.group(ctx.channel().eventLoop())
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new HttpClientCodec(),
new HttpObjectAggregator(1048576),
new HttpProxyBackendHandler(ctx.channel())
);
}
});
ChannelFuture f = b.connect(host, port);
outboundChannel = f.channel();
f.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
outboundChannel.writeAndFlush(request.retain());
} else {
ctx.channel().close();
}
}
});
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
if (outboundChannel != null) {
closeOnFlush(outboundChannel);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
closeOnFlush(ctx.channel());
}
static void closeOnFlush(Channel ch) {
if (ch.isActive()) {
ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
}
}
private static class HttpProxyBackendHandler extends ChannelInboundHandlerAdapter {
private final Channel inboundChannel;
HttpProxyBackendHandler(Channel inboundChannel) {
this.inboundChannel = inboundChannel;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
inboundChannel.writeAndFlush(msg);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
closeOnFlush(inboundChannel);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
closeOnFlush(ctx.channel());
}
static void closeOnFlush(Channel ch) {
if (ch.isActive()) {
ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
}
}
private static class TcpProxyServerHandler extends ChannelInboundHandlerAdapter {
private Channel outboundChannel;
@Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
if (outboundChannel == null) {
// 假设目标服务器地址和端口
String targetHost = "example.com";
int targetPort = 80;
Bootstrap b = new Bootstrap();
b.group(ctx.channel().eventLoop())
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new TcpProxyBackendHandler(ctx.channel()));
}
});
ChannelFuture f = b.connect(targetHost, targetPort);
outboundChannel = f.channel();
f.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
outboundChannel.writeAndFlush(msg);
} else {
ctx.channel().close();
}
}
});
} else {
outboundChannel.writeAndFlush(msg);
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
if (outboundChannel != null) {
closeOnFlush(outboundChannel);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
closeOnFlush(ctx.channel());
}
static void closeOnFlush(Channel ch) {
if (ch.isActive()) {
ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
}
}
private static class TcpProxyBackendHandler extends ChannelInboundHandlerAdapter {
private final Channel inboundChannel;
TcpProxyBackendHandler(Channel inboundChannel) {
this.inboundChannel = inboundChannel;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
inboundChannel.writeAndFlush(msg);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
closeOnFlush(inboundChannel);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
closeOnFlush(ctx.channel());
}
static void closeOnFlush(Channel ch) {
if (ch.isActive()) {
ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
}
}
private static class UdpProxyServerHandler extends SimpleChannelInboundHandler<DatagramPacket> {
private InetSocketAddress targetAddress = new InetSocketAddress("example.com", 80);
@Override
protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
Bootstrap b = new Bootstrap();
b.group(ctx.channel().eventLoop())
.channel(NioDatagramChannel.class)
.handler(new UdpProxyBackendHandler(ctx.channel(), packet.sender()));
Channel channel = b.bind(0).sync().channel();
channel.writeAndFlush(new DatagramPacket(packet.content().retain(), targetAddress));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
private static class UdpProxyBackendHandler extends SimpleChannelInboundHandler<DatagramPacket> {
private final Channel inboundChannel;
private final InetSocketAddress clientAddress;
UdpProxyBackendHandler(Channel inboundChannel, InetSocketAddress clientAddress) {
this.inboundChannel = inboundChannel;
this.clientAddress = clientAddress;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
inboundChannel.writeAndFlush(new DatagramPacket(packet.content().retain(), clientAddress));
ctx.channel().close();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
}

View File

@ -1,13 +1,11 @@
package org.framework.lazy.cloud.network.heartbeat.dns.demo;
package org.framework.lazy.cloud.network.heartbeat.protocol.demo;
import com.github.xiaoymin.knife4j.core.util.StrUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.handler.codec.dns.*;
import io.netty.util.AttributeKey;
@ -16,6 +14,9 @@ import lombok.extern.slf4j.Slf4j;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
@ -75,10 +76,25 @@ public final class DnsServer {
DatagramDnsResponse dnsResponse = new DatagramDnsResponse(msg.recipient(), msg.sender(), id);
dnsResponse.addRecord(DnsSection.QUESTION, question);
InetAddress inetAddress = null;
try {
inetAddress = Inet4Address.getByName("www.baidu.com");
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
byte[] ipBytes = inetAddress.getAddress();
// just print the IP after query
// DefaultDnsRawRecord queryAnswer = new DefaultDnsRawRecord(
// question.name(),
// DnsRecordType.A, 600, Unpooled.wrappedBuffer(new byte[]{(byte) 192, (byte) 168, 1, 1}));
DefaultDnsRawRecord queryAnswer = new DefaultDnsRawRecord(
question.name(),
DnsRecordType.A, 600, Unpooled.wrappedBuffer(new byte[]{(byte) 192, (byte) 168, 1, 1}));
DnsRecordType.A,
600,
Unpooled.wrappedBuffer(ipBytes)
);
dnsResponse.addRecord(DnsSection.ANSWER, queryAnswer);
return dnsResponse;
}

View File

@ -1,4 +1,4 @@
package org.framework.lazy.cloud.network.heartbeat.dns.demo;
package org.framework.lazy.cloud.network.heartbeat.protocol.demo;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBufUtil;

View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>top.wu2020</groupId>
<artifactId>wu-lazy-cloud-network</artifactId>
<version>1.3.0-JDK17-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>wu-lazy-cloud-heartbeat-protocol-proxy</artifactId>
<description>云上心跳服务协议代理</description>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-framework-web</artifactId>
</dependency>
<!-- 通用心跳包 -->
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-lazy-cloud-heartbeat-common</artifactId>
</dependency>
<!-- 数据库 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-database-lazy-plus-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.50</version>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations-jakarta</artifactId>
<version>2.2.21</version>
</dependency>
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-framework-lazy-orm-spring-starter</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,11 @@
package org.framework.lazy.cloud.network.heartbeat.protocol;
import org.springframework.context.annotation.ComponentScan;
import org.wu.framework.lazy.orm.core.stereotype.LazyScan;
@LazyScan(scanBasePackages = {
"org.framework.lazy.cloud.network.heartbeat.dns.standalone.infrastructure.entity"
})
@ComponentScan(basePackages = "org.framework.lazy.cloud.network.heartbeat.protocol")
public class EnableProtocolProxyAutoConfiguration {
}

View File

@ -0,0 +1,11 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.config;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Role;
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProtocolProxyAutoConfiguration {
}

View File

@ -0,0 +1,15 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.config;
import org.framework.lazy.cloud.network.heartbeat.common.adapter.ChannelFlowAdapter;
import org.framework.lazy.cloud.network.heartbeat.common.advanced.flow.HandleChannelFlowAdvanced;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Role;
/**
* @see ChannelFlowAdapter
* @see HandleChannelFlowAdvanced
*/
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProtocolProxyFlowConfiguration {
}

View File

@ -0,0 +1,85 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.context;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.common.context.SocketApplicationListener;
import org.framework.lazy.cloud.network.heartbeat.protocol.filter.NettyHttpProxyFilter;
import org.framework.lazy.cloud.network.heartbeat.protocol.properties.ProtocolProxyProperties;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class NettyHttpProxySocketApplicationListener implements SocketApplicationListener {
private final EventLoopGroup bossGroup = new NioEventLoopGroup();
private final EventLoopGroup workerGroup = new NioEventLoopGroup();
private ChannelFuture channelFuture;
private final NettyHttpProxyFilter nettyHttpProxyFilter;// 通道业务处理
private final ProtocolProxyProperties protocolProxyProperties;
public NettyHttpProxySocketApplicationListener(NettyHttpProxyFilter nettyHttpProxyFilter, ProtocolProxyProperties protocolProxyProperties) {
this.nettyHttpProxyFilter = nettyHttpProxyFilter;
this.protocolProxyProperties = protocolProxyProperties;
}
/**
* 运行
*
* @throws InterruptedException
*/
@Override
public void doRunning() throws Exception {
try {
ProtocolProxyProperties.HttpProtocolProxy httpProtocolProxy = protocolProxyProperties.getHttpProtocolProxy();
Integer httpProtocolProxyPort = httpProtocolProxy.getPort();
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
// 给服务端channel设置属性
// 设置读缓冲区为2M
.childOption(ChannelOption.SO_RCVBUF, 2048 * 1024)
// 设置写缓冲区为1M
.childOption(ChannelOption.SO_SNDBUF, 1024 * 1024)
.childOption(ChannelOption.SO_KEEPALIVE, true)
// .childOption(ChannelOption.TCP_NODELAY, false)
.childOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000 * 60)//连接超时时间设置为 60
// .childOption(ChannelOption.RCVBUF_ALLOCATOR, new NettyRecvByteBufAllocator(1024 * 1024))//用于Channel分配接受Buffer的分配器 默认AdaptiveRecvByteBufAllocator.DEFAULT
.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(1024 * 1024, 1024 * 1024 * 2))
.childHandler(nettyHttpProxyFilter);
channelFuture = b.bind(httpProtocolProxyPort).sync();
channelFuture.addListener((ChannelFutureListener) channelFuture -> {
// 服务器已启动
log.info("http 协议代理 服务器启动成功");
});
channelFuture.channel().closeFuture().sync();
} catch (Exception e) {
log.error("启动http 协议代理 失败", e);
} finally {
destroy();
// 服务器已关闭
log.warn("http 协议代理 服务关闭");
}
}
@PreDestroy
@Override
public void destroy() throws Exception {
if (channelFuture != null) {
channelFuture.channel().close().syncUninterruptibly();
}
if (!bossGroup.isShutdown()) {
bossGroup.shutdownGracefully();
}
if (!workerGroup.isShutdown()) {
workerGroup.shutdownGracefully();
}
}
}

View File

@ -0,0 +1,86 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.context;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.common.context.SocketApplicationListener;
import org.framework.lazy.cloud.network.heartbeat.protocol.filter.NettyHttpProxyFilter;
import org.framework.lazy.cloud.network.heartbeat.protocol.filter.NettyTcpProxyFilter;
import org.framework.lazy.cloud.network.heartbeat.protocol.properties.ProtocolProxyProperties;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class NettyTcpProxySocketApplicationListener implements SocketApplicationListener {
private final EventLoopGroup bossGroup = new NioEventLoopGroup();
private final EventLoopGroup workerGroup = new NioEventLoopGroup();
private final NettyTcpProxyFilter nettyTcpProxyFilter;// 通道业务处理
private ChannelFuture channelFuture;
private final ProtocolProxyProperties protocolProxyProperties;
public NettyTcpProxySocketApplicationListener(NettyTcpProxyFilter nettyTcpProxyFilter, ProtocolProxyProperties protocolProxyProperties) {
this.nettyTcpProxyFilter = nettyTcpProxyFilter;
this.protocolProxyProperties = protocolProxyProperties;
}
/**
* 运行
*
* @throws InterruptedException
*/
@Override
public void doRunning() throws Exception {
try {
ProtocolProxyProperties.TcpProtocolProxy tcpHttpProtocolProxy = protocolProxyProperties.getTcpHttpProtocolProxy();
Integer httpProtocolProxyPort = tcpHttpProtocolProxy.getPort();
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
// 给服务端channel设置属性
// 设置读缓冲区为2M
.childOption(ChannelOption.SO_RCVBUF, 2048 * 1024)
// 设置写缓冲区为1M
.childOption(ChannelOption.SO_SNDBUF, 1024 * 1024)
.childOption(ChannelOption.SO_KEEPALIVE, true)
// .childOption(ChannelOption.TCP_NODELAY, false)
.childOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000 * 60)//连接超时时间设置为 60
// .childOption(ChannelOption.RCVBUF_ALLOCATOR, new NettyRecvByteBufAllocator(1024 * 1024))//用于Channel分配接受Buffer的分配器 默认AdaptiveRecvByteBufAllocator.DEFAULT
.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(1024 * 1024, 1024 * 1024 * 2))
.childHandler(nettyTcpProxyFilter);
channelFuture = b.bind(httpProtocolProxyPort).sync();
channelFuture.addListener((ChannelFutureListener) channelFuture -> {
// 服务器已启动
log.info("TCP 协议代理 服务器启动成功");
});
channelFuture.channel().closeFuture().sync();
} catch (Exception e) {
log.error("启动TCP 协议代理 失败", e);
} finally {
destroy();
// 服务器已关闭
log.warn("TCP 协议代理 服务关闭");
}
}
@PreDestroy
@Override
public void destroy() throws Exception {
if (channelFuture != null) {
channelFuture.channel().close().syncUninterruptibly();
}
if (!bossGroup.isShutdown()) {
bossGroup.shutdownGracefully();
}
if (!workerGroup.isShutdown()) {
workerGroup.shutdownGracefully();
}
}
}

View File

@ -0,0 +1,89 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.context;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.common.adapter.ChannelTypeAdapter;
import org.framework.lazy.cloud.network.heartbeat.common.advanced.HandleChannelTypeAdvanced;
import org.framework.lazy.cloud.network.heartbeat.common.context.SocketApplicationListener;
import org.framework.lazy.cloud.network.heartbeat.protocol.handler.NettyUdpProxyHandler;
import org.framework.lazy.cloud.network.heartbeat.protocol.properties.ProtocolProxyProperties;
import org.springframework.stereotype.Component;
import java.util.List;
@Slf4j
@Component
public class NettyUdpProxySocketApplicationListener implements SocketApplicationListener {
private final EventLoopGroup bossGroup = new NioEventLoopGroup();
private ChannelFuture channelFuture;
private final ProtocolProxyProperties protocolProxyProperties;
private final List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList;
public NettyUdpProxySocketApplicationListener(ProtocolProxyProperties protocolProxyProperties,
List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList) {
this.protocolProxyProperties = protocolProxyProperties;
this.handleChannelTypeAdvancedList = handleChannelTypeAdvancedList;
}
/**
* 运行
*
* @throws InterruptedException
*/
@Override
public void doRunning() throws Exception {
try {
ChannelTypeAdapter channelTypeAdapter = new ChannelTypeAdapter(handleChannelTypeAdvancedList);
NettyUdpProxyHandler nettyUdpProxyHandler = new NettyUdpProxyHandler(channelTypeAdapter);// 通道业务处理
ProtocolProxyProperties.UdpProtocolProxy udpHttpProtocolProxy = protocolProxyProperties.getUdpHttpProtocolProxy();
Integer udpProtocolProxyPort = udpHttpProtocolProxy.getPort();
Bootstrap b = new Bootstrap();
b.group(bossGroup)
.channel(NioDatagramChannel.class)
// 给服务端channel设置属性
// 设置读缓冲区为2M
.option(ChannelOption.SO_RCVBUF, 2048 * 1024)
// 设置写缓冲区为1M
.option(ChannelOption.SO_SNDBUF, 1024 * 1024)
.option(ChannelOption.SO_KEEPALIVE, true)
// .childOption(ChannelOption.TCP_NODELAY, false)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000 * 60)//连接超时时间设置为 60
// .childOption(ChannelOption.RCVBUF_ALLOCATOR, new NettyRecvByteBufAllocator(1024 * 1024))//用于Channel分配接受Buffer的分配器 默认AdaptiveRecvByteBufAllocator.DEFAULT
.option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(1024 * 1024, 1024 * 1024 * 2))
.handler(nettyUdpProxyHandler);
channelFuture = b.bind(udpProtocolProxyPort).sync();
channelFuture.addListener((ChannelFutureListener) channelFuture -> {
// 服务器已启动
log.info("UDP 协议代理 服务器启动成功");
});
channelFuture.channel().closeFuture().sync();
} catch (Exception e) {
log.error("启动UDP 协议代理 失败", e);
} finally {
destroy();
// 服务器已关闭
log.warn("UDP 协议代理 服务关闭");
}
}
@PreDestroy
@Override
public void destroy() throws Exception {
if (channelFuture != null) {
channelFuture.channel().close().syncUninterruptibly();
}
if (!bossGroup.isShutdown()) {
bossGroup.shutdownGracefully();
}
}
}

View File

@ -0,0 +1,52 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.filter;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import org.framework.lazy.cloud.network.heartbeat.common.adapter.ChannelTypeAdapter;
import org.framework.lazy.cloud.network.heartbeat.common.advanced.HandleChannelTypeAdvanced;
import org.framework.lazy.cloud.network.heartbeat.common.filter.DebugChannelInitializer;
import org.framework.lazy.cloud.network.heartbeat.protocol.handler.NettyHttpProxyHandler;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* description
*
* @author 吴佳伟
* @date 2023/09/13 10:26
*/
@Component
public class NettyHttpProxyFilter extends DebugChannelInitializer<SocketChannel> {
private final List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList;
public NettyHttpProxyFilter(List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList) {
this.handleChannelTypeAdvancedList = handleChannelTypeAdvancedList;
}
@Override
protected void initChannel0(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
// ("\n")为结尾分割的 解码器
// 解码编码
// pipeline.addLast(new NettyProxyMsgDecoder(Integer.MAX_VALUE, 0, 4, -4, 0));
// pipeline.addLast(new NettyProxyMsgEncoder());
//// ph.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
// // 解码和编码应和客户端一致
// //入参说明: 读超时时间写超时时间所有类型的超时时间时间格式
//
// pipeline.addLast(new IdleStateHandler(5, 0, 0, TimeUnit.SECONDS));
// pipeline.addLast("decoder", new StringDecoder());
// pipeline.addLast("encoder", new StringEncoder());
// 类型处理器适配器
ChannelTypeAdapter channelTypeAdapter = new ChannelTypeAdapter(handleChannelTypeAdvancedList);
pipeline.addLast("doHandler", new NettyHttpProxyHandler(channelTypeAdapter));// 服务端业务逻辑
pipeline.addLast(new HttpClientCodec());
pipeline.addLast(new HttpObjectAggregator(1048576));
}
}

View File

@ -0,0 +1,40 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.filter;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import org.framework.lazy.cloud.network.heartbeat.common.adapter.ChannelTypeAdapter;
import org.framework.lazy.cloud.network.heartbeat.common.advanced.HandleChannelTypeAdvanced;
import org.framework.lazy.cloud.network.heartbeat.common.filter.DebugChannelInitializer;
import org.framework.lazy.cloud.network.heartbeat.protocol.handler.NettyHttpProxyHandler;
import org.framework.lazy.cloud.network.heartbeat.protocol.handler.NettyTcpProxyHandler;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* description
*
* @author 吴佳伟
* @date 2023/09/13 10:26
*/
@Component
public class NettyTcpProxyFilter extends DebugChannelInitializer<SocketChannel> {
private final List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList;
public NettyTcpProxyFilter(List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList) {
this.handleChannelTypeAdvancedList = handleChannelTypeAdvancedList;
}
@Override
protected void initChannel0(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
// 类型处理器适配器
ChannelTypeAdapter channelTypeAdapter = new ChannelTypeAdapter(handleChannelTypeAdvancedList);
pipeline.addLast("doHandler", new NettyTcpProxyHandler(channelTypeAdapter));// 服务端业务逻辑
}
}

View File

@ -0,0 +1,39 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.handler;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class NettyHttpProxyBackendHandler extends ChannelInboundHandlerAdapter {
private final Channel inboundChannel;
NettyHttpProxyBackendHandler(Channel inboundChannel) {
this.inboundChannel = inboundChannel;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
inboundChannel.writeAndFlush(msg);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
closeOnFlush(inboundChannel);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
closeOnFlush(ctx.channel());
}
static void closeOnFlush(Channel ch) {
if (ch.isActive()) {
ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
}
}

View File

@ -0,0 +1,92 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.handler;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.common.adapter.ChannelTypeAdapter;
import java.net.URI;
/**
* description 服务端数据处理器
*
* @author 吴佳伟
* @date 2023/09/13 10:27
*/
@Slf4j
public class NettyHttpProxyHandler extends ChannelInboundHandlerAdapter {
private final ChannelTypeAdapter channelTypeAdapter;
/**
* 传出数据延迟次数* 心跳时间作为关闭时间
*/
private int transfer_count = 1;
public NettyHttpProxyHandler(ChannelTypeAdapter channelTypeAdapter) {
this.channelTypeAdapter = channelTypeAdapter;
}
private Channel outboundChannel;
@Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FullHttpRequest) {
FullHttpRequest request = (FullHttpRequest) msg;
URI uri = new URI(request.uri());
String host = uri.getHost();
int port = uri.getPort();
if (port == -1) {
port = 80;
}
Bootstrap b = new Bootstrap();
b.group(ctx.channel().eventLoop())
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new HttpClientCodec(),
new HttpObjectAggregator(1048576),
new NettyHttpProxyBackendHandler(ctx.channel())
);
}
});
ChannelFuture f = b.connect(host, port);
outboundChannel = f.channel();
f.addListener((ChannelFutureListener) future -> {
if (future.isSuccess()) {
outboundChannel.writeAndFlush(request.retain());
} else {
ctx.channel().close();
}
});
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
if (outboundChannel != null) {
closeOnFlush(outboundChannel);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
closeOnFlush(ctx.channel());
}
static void closeOnFlush(Channel ch) {
if (ch.isActive()) {
ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
}
}

View File

@ -0,0 +1,38 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.handler;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class NettyTcpProxyBackendHandler extends ChannelInboundHandlerAdapter {
private final Channel inboundChannel;
public NettyTcpProxyBackendHandler(Channel inboundChannel) {
this.inboundChannel = inboundChannel;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
inboundChannel.writeAndFlush(msg);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
closeOnFlush(inboundChannel);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
closeOnFlush(ctx.channel());
}
static void closeOnFlush(Channel ch) {
if (ch.isActive()) {
ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
}
}

View File

@ -0,0 +1,84 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.handler;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.common.adapter.ChannelTypeAdapter;
import java.net.URI;
/**
* description 服务端数据处理器
*
* @author 吴佳伟
* @date 2023/09/13 10:27
*/
@Slf4j
public class NettyTcpProxyHandler extends ChannelInboundHandlerAdapter {
private final ChannelTypeAdapter channelTypeAdapter;
public NettyTcpProxyHandler(ChannelTypeAdapter channelTypeAdapter) {
this.channelTypeAdapter = channelTypeAdapter;
}
private Channel outboundChannel;
@Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
if (outboundChannel == null) {
// 假设目标服务器地址和端口
String targetHost = "example.com";
int targetPort = 80;
Bootstrap b = new Bootstrap();
b.group(ctx.channel().eventLoop())
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettyTcpProxyBackendHandler(ctx.channel()));
}
});
ChannelFuture f = b.connect(targetHost, targetPort);
outboundChannel = f.channel();
f.addListener((ChannelFutureListener) future -> {
if (future.isSuccess()) {
outboundChannel.writeAndFlush(msg);
} else {
ctx.channel().close();
}
});
} else {
outboundChannel.writeAndFlush(msg);
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
if (outboundChannel != null) {
closeOnFlush(outboundChannel);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
closeOnFlush(ctx.channel());
}
static void closeOnFlush(Channel ch) {
if (ch.isActive()) {
ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
}
}

View File

@ -0,0 +1,31 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.handler;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import java.net.InetSocketAddress;
public class NettyUdpProxyBackendHandler extends SimpleChannelInboundHandler<DatagramPacket> {
private final Channel inboundChannel;
private final InetSocketAddress clientAddress;
public NettyUdpProxyBackendHandler(Channel inboundChannel, InetSocketAddress clientAddress) {
this.inboundChannel = inboundChannel;
this.clientAddress = clientAddress;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
inboundChannel.writeAndFlush(new DatagramPacket(packet.content().retain(), clientAddress));
ctx.channel().close();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}

View File

@ -0,0 +1,46 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.handler;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.common.adapter.ChannelTypeAdapter;
import java.net.InetSocketAddress;
/**
* description 服务端数据处理器
*
* @author 吴佳伟
* @date 2023/09/13 10:27
*/
@Slf4j
public class NettyUdpProxyHandler extends SimpleChannelInboundHandler<DatagramPacket> {
private final ChannelTypeAdapter channelTypeAdapter;
private InetSocketAddress targetAddress = new InetSocketAddress("example.com", 80);
public NettyUdpProxyHandler(ChannelTypeAdapter channelTypeAdapter) {
this.channelTypeAdapter = channelTypeAdapter;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
Bootstrap b = new Bootstrap();
b.group(ctx.channel().eventLoop())
.channel(NioDatagramChannel.class)
.handler(new NettyUdpProxyBackendHandler(ctx.channel(), packet.sender()));
Channel channel = b.bind(0).sync().channel();
channel.writeAndFlush(new DatagramPacket(packet.content().retain(), targetAddress));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}

View File

@ -0,0 +1,26 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.init;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.protocol.context.NettyHttpProxySocketApplicationListener;
import org.framework.lazy.cloud.network.heartbeat.protocol.context.NettyTcpProxySocketApplicationListener;
import org.framework.lazy.cloud.network.heartbeat.protocol.context.NettyUdpProxySocketApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* description 初始化服务端
*
* @author 吴佳伟
* @date 2023/09/12 18:22
*/
@Slf4j
@Configuration
@Import({NettyHttpProxySocketApplicationListener.class,
NettyTcpProxySocketApplicationListener.class,
NettyUdpProxySocketApplicationListener.class})
public class InitProtocolProxySocket {
}

View File

@ -0,0 +1,46 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* 服务端模式配置
*/
@Configuration
@ConfigurationProperties(prefix = ProtocolProxyProperties.prefix)
@Data
public class ProtocolProxyProperties {
public static final String prefix = "spring.lazy.netty.protocol.proxy";
/**
* http协议代理
*/
private HttpProtocolProxy httpProtocolProxy;
/**
* tcp协议代理
*/
private TcpProtocolProxy tcpHttpProtocolProxy;
/**
* udp协议代理
*/
private UdpProtocolProxy udpHttpProtocolProxy;
@Data
public static class HttpProtocolProxy {
private Integer port=8001;
}
@Data
public static class TcpProtocolProxy {
private Integer port=9001;
}
@Data
public static class UdpProtocolProxy {
private Integer port=10001;
}
}

View File

@ -0,0 +1,6 @@
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.framework.lazy.cloud.network.heartbeat.protocol.EnableProtocolProxyAutoConfiguration,\
org.framework.lazy.cloud.network.heartbeat.protocol.config.ProtocolProxyAutoConfiguration,\
org.framework.lazy.cloud.network.heartbeat.protocol.init.InitProtocolProxySocket,\
org.framework.lazy.cloud.network.heartbeat.protocol.config.ProtocolProxyFlowConfiguration

View File

@ -0,0 +1,4 @@
org.framework.lazy.cloud.network.heartbeat.protocol.EnableProtocolProxyAutoConfiguration
org.framework.lazy.cloud.network.heartbeat.protocol.config.ProtocolProxyAutoConfiguration
org.framework.lazy.cloud.network.heartbeat.protocol.init.InitProtocolProxySocket
org.framework.lazy.cloud.network.heartbeat.protocol.config.ProtocolProxyFlowConfiguration

View File

@ -0,0 +1,52 @@
package org.framework.lazy.cloud.network.heartbeat.protocol;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.handler.codec.dns.DatagramDnsQueryDecoder;
import io.netty.handler.codec.dns.DatagramDnsResponseEncoder;
public class DnsServer {
private final int port;
public DnsServer(int port) {
this.port = port;
}
public void run() throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioDatagramChannel.class)
.option(ChannelOption.SO_BROADCAST, true)
.handler(new ChannelInitializer<DatagramChannel>() {
@Override
protected void initChannel(DatagramChannel ch) throws Exception {
ch.pipeline().addLast(
new DatagramDnsQueryDecoder(),
new DatagramDnsResponseEncoder(),
new DnsServerHandler()
);
}
});
// 绑定端口
ChannelFuture f = b.bind(port).sync();
System.out.println("DNS server started and listening on port " + port);
f.channel().closeFuture().await();
} finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new DnsServer(53).run();
}
}

View File

@ -0,0 +1,84 @@
package org.framework.lazy.cloud.network.heartbeat.protocol;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.dns.*;
import lombok.extern.slf4j.Slf4j;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
@Slf4j
public class DnsServerHandler extends SimpleChannelInboundHandler<DatagramDnsQuery> {
private Map<String, String> domainIpMapping = new HashMap<>();
{
domainIpMapping.put("test.wu-framework.cn.", "124.222.152.160");
domainIpMapping.put("www.google.com.hk.", "120.253.253.97");
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, DatagramDnsQuery query) throws Exception {
// 创建DNS响应
DatagramDnsResponse response = new DatagramDnsResponse(query.recipient(), query.sender(), query.id());
DefaultDnsQuestion dnsQuestion = query.recordAt(DnsSection.QUESTION);
response.addRecord(DnsSection.QUESTION, dnsQuestion);
DnsRecordType dnsRecordType = dnsQuestion.type();
log.info("dns:{}", dnsQuestion.name());
// log.info("query:{}", query);
String domain = dnsQuestion.name();
if (dnsRecordType == DnsRecordType.A) {
String ip = domainIpMapping.get(domain);
if (ip != null) {
log.info("ip:{}", ip);
try {
InetAddress address = Inet4Address.getByName(ip);
DefaultDnsRawRecord answer = new DefaultDnsRawRecord(
domain, DnsRecordType.A, 3600,
Unpooled.wrappedBuffer(address.getAddress())
);
response.addRecord(DnsSection.ANSWER, answer);
} catch (UnknownHostException e) {
e.printStackTrace();
}
// 发送响应
ctx.writeAndFlush(response);
} else {
// 使用系统默认 DNS 解析
try {
java.net.InetAddress[] addresses = java.net.InetAddress.getAllByName(domain);
for (java.net.InetAddress address : addresses) {
log.info("使用本地dns解析域名{} ip:{}", domain, address.getHostAddress());
response.addRecord(DnsSection.ANSWER, new DefaultDnsRawRecord(
domain, DnsRecordType.A, 3600, Unpooled.wrappedBuffer(address.getAddress())));
}
// log.info("response:{}",response.toString());
ctx.writeAndFlush(response);
} catch (Exception e) {
e.printStackTrace();
}
}
} else {
log.info("type:{}", dnsRecordType.name());
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}

View File

@ -0,0 +1,146 @@
package org.framework.lazy.cloud.network.heartbeat.protocol;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import java.net.URI;
public class HttpProxyServer {
private static final int PORT = 8080;
public static void main(String[] args) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new HttpClientCodec(),
new HttpObjectAggregator(1048576),
new HttpProxyServerHandler()
);
}
});
ChannelFuture f = b.bind(PORT).sync();
System.out.println("Proxy server started and listening on port " + PORT);
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
private static class HttpProxyServerHandler extends ChannelInboundHandlerAdapter {
private Channel outboundChannel;
@Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FullHttpRequest) {
FullHttpRequest request = (FullHttpRequest) msg;
URI uri = new URI(request.uri());
String host = uri.getHost();
int port = uri.getPort();
if (port == -1) {
port = 80;
}
Bootstrap b = new Bootstrap();
b.group(ctx.channel().eventLoop())
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new HttpClientCodec(),
new HttpObjectAggregator(1048576),
new ProxyBackendHandler(ctx.channel())
);
}
});
ChannelFuture f = b.connect(host, port);
outboundChannel = f.channel();
f.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
// 连接成功将客户端请求转发到目标服务器
outboundChannel.writeAndFlush(request.retain());
} else {
// 连接失败关闭客户端通道
ctx.channel().close();
}
}
});
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
if (outboundChannel != null) {
closeOnFlush(outboundChannel);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
closeOnFlush(ctx.channel());
}
static void closeOnFlush(Channel ch) {
if (ch.isActive()) {
ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
}
}
private static class ProxyBackendHandler extends ChannelInboundHandlerAdapter {
private final Channel inboundChannel;
ProxyBackendHandler(Channel inboundChannel) {
this.inboundChannel = inboundChannel;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// 将目标服务器的响应转发给客户端
inboundChannel.writeAndFlush(msg);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
closeOnFlush(inboundChannel);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
closeOnFlush(ctx.channel());
}
static void closeOnFlush(Channel ch) {
if (ch.isActive()) {
ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
}
}
}

View File

@ -0,0 +1,312 @@
package org.framework.lazy.cloud.network.heartbeat.protocol;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import java.net.InetSocketAddress;
import java.net.URI;
public class MultiProtocolProxyServer {
private static final int HTTP_PORT = 8080;
private static final int TCP_PORT = 9090;
private static final int UDP_PORT = 10000;
public static void main(String[] args) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// 启动 HTTP 代理服务器
startHttpProxy(bossGroup, workerGroup);
// 启动 TCP 代理服务器
startTcpProxy(bossGroup, workerGroup);
// 启动 UDP 代理服务器
startUdpProxy(workerGroup);
System.out.println("Proxy servers started.");
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
private static void startHttpProxy(EventLoopGroup bossGroup, EventLoopGroup workerGroup) throws InterruptedException {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new HttpClientCodec(),
new HttpObjectAggregator(1048576),
new HttpProxyServerHandler()
);
}
});
b.bind(HTTP_PORT).sync();
System.out.println("HTTP proxy server started on port " + HTTP_PORT);
}
private static void startTcpProxy(EventLoopGroup bossGroup, EventLoopGroup workerGroup) throws InterruptedException {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new TcpProxyServerHandler());
}
});
b.bind(TCP_PORT).sync();
System.out.println("TCP proxy server started on port " + TCP_PORT);
}
private static void startUdpProxy(EventLoopGroup workerGroup) throws InterruptedException {
Bootstrap b = new Bootstrap();
b.group(workerGroup)
.channel(NioDatagramChannel.class)
.handler(new UdpProxyServerHandler());
b.bind(UDP_PORT).sync();
System.out.println("UDP proxy server started on port " + UDP_PORT);
}
private static class HttpProxyServerHandler extends ChannelInboundHandlerAdapter {
private Channel outboundChannel;
@Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FullHttpRequest) {
FullHttpRequest request = (FullHttpRequest) msg;
URI uri = new URI(request.uri());
String host = uri.getHost();
int port = uri.getPort();
if (port == -1) {
port = 80;
}
Bootstrap b = new Bootstrap();
b.group(ctx.channel().eventLoop())
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new HttpClientCodec(),
new HttpObjectAggregator(1048576),
new HttpProxyBackendHandler(ctx.channel())
);
}
});
ChannelFuture f = b.connect(host, port);
outboundChannel = f.channel();
f.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
outboundChannel.writeAndFlush(request.retain());
} else {
ctx.channel().close();
}
}
});
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
if (outboundChannel != null) {
closeOnFlush(outboundChannel);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
closeOnFlush(ctx.channel());
}
static void closeOnFlush(Channel ch) {
if (ch.isActive()) {
ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
}
}
private static class HttpProxyBackendHandler extends ChannelInboundHandlerAdapter {
private final Channel inboundChannel;
HttpProxyBackendHandler(Channel inboundChannel) {
this.inboundChannel = inboundChannel;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
inboundChannel.writeAndFlush(msg);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
closeOnFlush(inboundChannel);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
closeOnFlush(ctx.channel());
}
static void closeOnFlush(Channel ch) {
if (ch.isActive()) {
ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
}
}
private static class TcpProxyServerHandler extends ChannelInboundHandlerAdapter {
private Channel outboundChannel;
@Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
if (outboundChannel == null) {
// 假设目标服务器地址和端口
String targetHost = "example.com";
int targetPort = 80;
Bootstrap b = new Bootstrap();
b.group(ctx.channel().eventLoop())
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new TcpProxyBackendHandler(ctx.channel()));
}
});
ChannelFuture f = b.connect(targetHost, targetPort);
outboundChannel = f.channel();
f.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
outboundChannel.writeAndFlush(msg);
} else {
ctx.channel().close();
}
}
});
} else {
outboundChannel.writeAndFlush(msg);
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
if (outboundChannel != null) {
closeOnFlush(outboundChannel);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
closeOnFlush(ctx.channel());
}
static void closeOnFlush(Channel ch) {
if (ch.isActive()) {
ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
}
}
private static class TcpProxyBackendHandler extends ChannelInboundHandlerAdapter {
private final Channel inboundChannel;
TcpProxyBackendHandler(Channel inboundChannel) {
this.inboundChannel = inboundChannel;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
inboundChannel.writeAndFlush(msg);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
closeOnFlush(inboundChannel);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
closeOnFlush(ctx.channel());
}
static void closeOnFlush(Channel ch) {
if (ch.isActive()) {
ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
}
}
private static class UdpProxyServerHandler extends SimpleChannelInboundHandler<DatagramPacket> {
private InetSocketAddress targetAddress = new InetSocketAddress("example.com", 80);
@Override
protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
Bootstrap b = new Bootstrap();
b.group(ctx.channel().eventLoop())
.channel(NioDatagramChannel.class)
.handler(new UdpProxyBackendHandler(ctx.channel(), packet.sender()));
Channel channel = b.bind(0).sync().channel();
channel.writeAndFlush(new DatagramPacket(packet.content().retain(), targetAddress));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
private static class UdpProxyBackendHandler extends SimpleChannelInboundHandler<DatagramPacket> {
private final Channel inboundChannel;
private final InetSocketAddress clientAddress;
UdpProxyBackendHandler(Channel inboundChannel, InetSocketAddress clientAddress) {
this.inboundChannel = inboundChannel;
this.clientAddress = clientAddress;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
inboundChannel.writeAndFlush(new DatagramPacket(packet.content().retain(), clientAddress));
ctx.channel().close();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
}

View File

@ -0,0 +1,124 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.demo;
import com.github.xiaoymin.knife4j.core.util.StrUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.handler.codec.dns.*;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public final class DnsServer {
private static final List<String> BLACK_LIST_DOMAIN = new ArrayList<>();
static {
String s;
try (InputStream is = DnsServer.class.getClassLoader().getResourceAsStream("black_list.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
while (StrUtil.isNotBlank(s = br.readLine())) {
BLACK_LIST_DOMAIN.add(s);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
public static void main(String[] args) throws Exception {
ProxyUdp proxyUdp = new ProxyUdp();
proxyUdp.init();
final int[] num = {0};
final NioEventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group).channel(NioDatagramChannel.class)
.handler(new ChannelInitializer<NioDatagramChannel>() {
@Override
protected void initChannel(NioDatagramChannel nioDatagramChannel) {
nioDatagramChannel.pipeline().addLast(new DatagramDnsQueryDecoder());
nioDatagramChannel.pipeline().addLast(new SimpleChannelInboundHandler<DatagramDnsQuery>() {
@Override
protected void channelRead0(ChannelHandlerContext ctx, DatagramDnsQuery msg) {
try {
DefaultDnsQuestion dnsQuestion = msg.recordAt(DnsSection.QUESTION);
String name = dnsQuestion.name();
log.info(name + ++num[0]);
Channel channel = ctx.channel();
int id = msg.id();
channel.attr(AttributeKey.<DatagramDnsQuery>valueOf(String.valueOf(id))).set(msg);
if (BLACK_LIST_DOMAIN.contains(name)) {
DnsQuestion question = msg.recordAt(DnsSection.QUESTION);
DatagramDnsResponse dnsResponse = getDatagramDnsResponse(msg, id, question);
channel.writeAndFlush(dnsResponse);
return;
}
proxyUdp.send(name, msg.id(), channel);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
private DatagramDnsResponse getDatagramDnsResponse(DatagramDnsQuery msg, int id, DnsQuestion question) {
DatagramDnsResponse dnsResponse = new DatagramDnsResponse(msg.recipient(), msg.sender(), id);
dnsResponse.addRecord(DnsSection.QUESTION, question);
InetAddress inetAddress = null;
try {
inetAddress = Inet4Address.getByName("www.baidu.com");
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
byte[] ipBytes = inetAddress.getAddress();
// just print the IP after query
// DefaultDnsRawRecord queryAnswer = new DefaultDnsRawRecord(
// question.name(),
// DnsRecordType.A, 600, Unpooled.wrappedBuffer(new byte[]{(byte) 192, (byte) 168, 1, 1}));
DefaultDnsRawRecord queryAnswer = new DefaultDnsRawRecord(
question.name(),
DnsRecordType.A,
600,
Unpooled.wrappedBuffer(ipBytes)
);
dnsResponse.addRecord(DnsSection.ANSWER, queryAnswer);
return dnsResponse;
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) {
log.error(e.getMessage(), e);
}
});
nioDatagramChannel.pipeline().addLast(new DatagramDnsResponseEncoder());
}
}).option(ChannelOption.SO_BROADCAST, true);
int port = 53;
ChannelFuture future = bootstrap.bind(port).addListener(future1 -> {
log.info("server listening port:{}", port);
});
future.channel().closeFuture().addListener(future1 -> {
if (future.isSuccess()) {
log.info(future.channel().toString());
}
});
}
}

View File

@ -0,0 +1,80 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.demo;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.handler.codec.dns.*;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
import java.net.InetSocketAddress;
@Slf4j
class ProxyUdp {
private Channel localChannel;
private Channel proxyChannel;
public void init() throws InterruptedException {
EventLoopGroup proxyGroup = new NioEventLoopGroup();
Bootstrap b = new Bootstrap();
b.group(proxyGroup)
.channel(NioDatagramChannel.class)
.handler(new ChannelInitializer<DatagramChannel>() {
@Override
protected void initChannel(DatagramChannel ch) {
ChannelPipeline p = ch.pipeline();
p.addLast(new DatagramDnsQueryEncoder())
.addLast(new DatagramDnsResponseDecoder())
.addLast(new SimpleChannelInboundHandler<DatagramDnsResponse>() {
@Override
public void channelActive(ChannelHandlerContext ctx) {
log.info(ctx.channel().toString());
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, DatagramDnsResponse msg) {
DatagramDnsQuery dnsQuery = localChannel.attr(AttributeKey.<DatagramDnsQuery>valueOf(String.valueOf(msg.id()))).get();
DnsQuestion question = msg.recordAt(DnsSection.QUESTION);
DatagramDnsResponse dnsResponse = new DatagramDnsResponse(dnsQuery.recipient(), dnsQuery.sender(), msg.id());
dnsResponse.addRecord(DnsSection.QUESTION, question);
for (int i = 0, count = msg.count(DnsSection.ANSWER); i < count; i++) {
DnsRecord record = msg.recordAt(DnsSection.ANSWER, i);
if (record.type() == DnsRecordType.A) {
// just print the IP after query
DnsRawRecord raw = (DnsRawRecord) record;
DefaultDnsRawRecord queryAnswer = new DefaultDnsRawRecord(
question.name(),
DnsRecordType.A, 600, Unpooled.wrappedBuffer(ByteBufUtil.getBytes(raw.content())));
dnsResponse.addRecord(DnsSection.ANSWER, queryAnswer);
}
}
localChannel.writeAndFlush(dnsResponse);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) {
log.error(e.getMessage(), e);
}
});
}
});
proxyChannel = b.bind(0).sync().addListener(future1 -> {
log.info("绑定成功");
}).channel();
}
public void send(String domain, int id, Channel localChannel) {
this.localChannel = localChannel;
DnsQuery query = new DatagramDnsQuery(null, new InetSocketAddress("114.114.114.114", 53), id).setRecord(
DnsSection.QUESTION,
new DefaultDnsQuestion(domain, DnsRecordType.A));
this.proxyChannel.writeAndFlush(query);
}
}