package com.lightsocks.socks5.handler;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import com.lightsocks.socks5.Client;
import com.lightsocks.socks5.bean.DstServer;
public class CLeftHandler extends ChannelHandlerAdapter implements
ForwardAdapter {
private ChannelHandlerContext ctx;
private ForwardAdapter forwardWriter;
private DstServer dst;
private volatile boolean close = false;
public CLeftHandler(DstServer dst) {
this.dst = dst;
}
@Override
public void handlerAdded(final ChannelHandlerContext ctx) { // (1)
this.ctx = ctx;
try {
ServerConnector proxy = new ServerConnector(
Client.AppConfig.getServerIp(),
Client.AppConfig.getServerPort(), Client.getWorkerGroup2(),
this);
proxy.run();
} catch (Exception e) {
e.printStackTrace();
ctx.close();
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception { // (2)
ByteBuf buf = ((ByteBuf) msg);
forwardWriter.forward(buf);
}
private void sendDstAddress() throws Exception {
int addLen = dst.getAddr().length;
int portLen = dst.getPort().length;
int headLength = 1 + addLen + portLen;
if (headLength > 16) {
int padding = 16 - headLength % 16;
headLength += padding;
}
ByteBuf buf = ctx.alloc().buffer(headLength);
byte[] head = new byte[headLength];
head[0] = dst.getAtyp();
for (int i = 0; i < addLen; i++) {
head[1 + i] = dst.getAddr()[i];
}
for (int i = 0; i < portLen; i++) {
head[addLen + 1 + i] = dst.getPort()[i];
}
buf.writeBytes(head);
forwardWriter.forward(buf);
}
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
// ctx.fireChannelInactive();
close = true;
if (forwardWriter != null) {
forwardWriter.closeNotify();
}
}
public void closeNotify() {
if (!close) {
close = true;
this.ctx.close();
}
}
public void forward(ByteBuf buf) throws Exception {
ctx.writeAndFlush(buf);
}
public void attach(ForwardAdapter target) {
this.forwardWriter = target;
try {
sendDstAddress();
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void forwardReadyNotify() {
ByteBuf replies = ctx.alloc().buffer(10);
byte[] bytes = new byte[] { 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x08, 0x43 };
replies.writeBytes(bytes);
ctx.writeAndFlush(replies);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
private final static class ServerConnector {
private final String host;
private final int port;
// private EventLoopGroup group;
private ForwardAdapter forwardWrite;
private EventLoopGroup workerGroup;
public ServerConnector(String host, int port, EventLoopGroup group,
ForwardAdapter forwardWrite) {
this.host = host;
this.port = port;
this.workerGroup = group;
this.forwardWrite = forwardWrite;
}
public void run() throws Exception {
// Configure the client.
// EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch)
throws Exception {
ch.pipeline().addLast(
new LengthFieldBasedFrameDecoder(
Integer.MAX_VALUE, 4, 4),
// new LoggingHandler(LogLevel.INFO),
new CRightHandler(forwardWrite));
}
});
// Start the client.
ChannelFuture f = b.connect(host, port).sync();
// Wait until the connection is closed.
// f.channel().closeFuture().sync();
} finally {
// Shut down the event loop to terminate all threads.
// group.shutdownGracefully();
}
}
}
}