// Path: src/main/java/cc/agentx/protocol/request/XRequest.java
// public class XRequest {
//     private Type atyp;
//     private String host;
//     private int port;
//     private int subsequentDataLength;
//     private Channel channel = Channel.TCP;
// 
//     public XRequest(Type atyp, String host, int port, int subsequentDataLength) {
//         this.atyp = atyp;
//         this.host = host;
//         this.port = port;
//         this.subsequentDataLength = subsequentDataLength;
//     }
// 
//     public XRequest(byte[] bytes) {
//         String[] target = new String(bytes).split(":");
//         if (target[0].equals(Type.IPV4.name()))
//             this.atyp = Type.IPV4;
//         if (target[0].equals(Type.DOMAIN.name()))
//             this.atyp = Type.DOMAIN;
//         if (target[0].equals(Type.IPV6.name()))
//             this.atyp = Type.IPV6;
//         this.host = target[1];
//         this.port = Integer.parseInt(target[2]);
//         this.subsequentDataLength = Integer.parseInt(target[3]);
//     }
// 
//     public Channel getChannel() {
//         return channel;
//     }
// 
//     public XRequest setChannel(Channel channel) {
//         this.channel = channel;
//         return this;
//     }
// 
//     public byte[] getBytes() {
//         return (atyp.name() + ":" + host + ":" + port + ":" + subsequentDataLength).getBytes();
//     }
// 
//     public String getHost() {
//         return host;
//     }
// 
//     public int getPort() {
//         return port;
//     }
// 
//     public int getSubsequentDataLength() {
//         return subsequentDataLength;
//     }
// 
//     public Type getAtyp() {
//         return atyp;
//     }
// 
//     @Override
//     public String toString() {
//         return "XRequest{" +
//                 "atyp=" + atyp +
//                 ", host='" + host + '\'' +
//                 ", port=" + port +
//                 ", subsequentDataLength=" + subsequentDataLength +
//                 ", channel=" + channel +
//                 '}';
//     }
// 
//     public enum Channel {
//         TCP, UDP
//     }
// 
//     public enum Type {
//         IPV4, DOMAIN, IPV6, UNKNOWN
//     }
// }
// 
// Path: src/main/java/cc/agentx/protocol/request/XRequestResolver.java
// public abstract class XRequestResolver {
// 
//     public abstract byte[] wrap(XRequest.Channel channel, byte[] bytes);
// 
//     public abstract XRequest parse(final byte[] bytes);
// 
//     public abstract boolean exposeRequest();
// 
//     public byte[] wrap(byte[] bytes) {
//         return wrap(XRequest.Channel.TCP, bytes);
//     }
// 
//     public byte[] unwrap(final byte[] bytes) {
//         return parse(bytes).getBytes();
//     }
// }
// 
// Path: src/main/java/cc/agentx/wrapper/Wrapper.java
// public abstract class Wrapper implements Parcelable {
// 
// }


import java.net.InetSocketAddress;
import java.util.Arrays;
import cc.agentx.protocol.request.XRequest;
import cc.agentx.protocol.request.XRequestResolver;
import cc.agentx.wrapper.Wrapper;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
/*
 * Copyright 2017 ZhangJiupeng
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cc.agentx.client.net.nio;




public final class Tcp2UdpHandler extends ChannelInboundHandlerAdapter {
    private static final InternalLogger log;

    static {
        log = InternalLoggerFactory.getInstance(XRelayHandler.class);
    }

    private final XRequestResolver requestResolver;
    private final InetSocketAddress udpSource;
    private final Wrapper wrapper;

    public Tcp2UdpHandler(InetSocketAddress udpSource, XRequestResolver requestResolver, Wrapper wrapper) {
        this.udpSource = udpSource;
        this.requestResolver = requestResolver;
        this.wrapper = wrapper;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        Channel udpChannel = XChannelMapper.getUdpChannel(udpSource);
        if (udpChannel == null) {
            log.warn("Bad Connection! (udp channel closed)");
            XChannelMapper.closeChannelGracefullyByTcpChannel(ctx.channel());
        } else if (udpChannel.isActive()) {
            ByteBuf byteBuf = (ByteBuf) msg;
            try {
                if (!byteBuf.hasArray()) {
                    byte[] bytes = new byte[byteBuf.readableBytes()];
                    byteBuf.getBytes(0, bytes);
                    bytes = wrapper.unwrap(bytes);
                    XRequest request = requestResolver.parse(bytes);
                    String host = request.getHost();
// Path: src/main/java/cc/agentx/protocol/request/XRequest.java
// public class XRequest {
//     private Type atyp;
//     private String host;
//     private int port;
//     private int subsequentDataLength;
//     private Channel channel = Channel.TCP;
// 
//     public XRequest(Type atyp, String host, int port, int subsequentDataLength) {
//         this.atyp = atyp;
//         this.host = host;
//         this.port = port;
//         this.subsequentDataLength = subsequentDataLength;
//     }
// 
//     public XRequest(byte[] bytes) {
//         String[] target = new String(bytes).split(":");
//         if (target[0].equals(Type.IPV4.name()))
//             this.atyp = Type.IPV4;
//         if (target[0].equals(Type.DOMAIN.name()))
//             this.atyp = Type.DOMAIN;
//         if (target[0].equals(Type.IPV6.name()))
//             this.atyp = Type.IPV6;
//         this.host = target[1];
//         this.port = Integer.parseInt(target[2]);
//         this.subsequentDataLength = Integer.parseInt(target[3]);
//     }
// 
//     public Channel getChannel() {
//         return channel;
//     }
// 
//     public XRequest setChannel(Channel channel) {
//         this.channel = channel;
//         return this;
//     }
// 
//     public byte[] getBytes() {
//         return (atyp.name() + ":" + host + ":" + port + ":" + subsequentDataLength).getBytes();
//     }
// 
//     public String getHost() {
//         return host;
//     }
// 
//     public int getPort() {
//         return port;
//     }
// 
//     public int getSubsequentDataLength() {
//         return subsequentDataLength;
//     }
// 
//     public Type getAtyp() {
//         return atyp;
//     }
// 
//     @Override
//     public String toString() {
//         return "XRequest{" +
//                 "atyp=" + atyp +
//                 ", host='" + host + '\'' +
//                 ", port=" + port +
//                 ", subsequentDataLength=" + subsequentDataLength +
//                 ", channel=" + channel +
//                 '}';
//     }
// 
//     public enum Channel {
//         TCP, UDP
//     }
// 
//     public enum Type {
//         IPV4, DOMAIN, IPV6, UNKNOWN
//     }
// }
// 
// Path: src/main/java/cc/agentx/protocol/request/XRequestResolver.java
// public abstract class XRequestResolver {
// 
//     public abstract byte[] wrap(XRequest.Channel channel, byte[] bytes);
// 
//     public abstract XRequest parse(final byte[] bytes);
// 
//     public abstract boolean exposeRequest();
// 
//     public byte[] wrap(byte[] bytes) {
//         return wrap(XRequest.Channel.TCP, bytes);
//     }
// 
//     public byte[] unwrap(final byte[] bytes) {
//         return parse(bytes).getBytes();
//     }
// }
// 
// Path: src/main/java/cc/agentx/wrapper/Wrapper.java
// public abstract class Wrapper implements Parcelable {
// 
// }


// Path: src/main/java/cc/agentx/client/net/nio/Tcp2UdpHandler.java
import java.net.InetSocketAddress;
import java.util.Arrays;
import cc.agentx.protocol.request.XRequest;
import cc.agentx.protocol.request.XRequestResolver;
import cc.agentx.wrapper.Wrapper;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

/*
 * Copyright 2017 ZhangJiupeng
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cc.agentx.client.net.nio;




public final class Tcp2UdpHandler extends ChannelInboundHandlerAdapter {
    private static final InternalLogger log;

    static {
        log = InternalLoggerFactory.getInstance(XRelayHandler.class);
    }

    private final XRequestResolver requestResolver;
    private final InetSocketAddress udpSource;
    private final Wrapper wrapper;

    public Tcp2UdpHandler(InetSocketAddress udpSource, XRequestResolver requestResolver, Wrapper wrapper) {
        this.udpSource = udpSource;
        this.requestResolver = requestResolver;
        this.wrapper = wrapper;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        Channel udpChannel = XChannelMapper.getUdpChannel(udpSource);
        if (udpChannel == null) {
            log.warn("Bad Connection! (udp channel closed)");
            XChannelMapper.closeChannelGracefullyByTcpChannel(ctx.channel());
        } else if (udpChannel.isActive()) {
            ByteBuf byteBuf = (ByteBuf) msg;
            try {
                if (!byteBuf.hasArray()) {
                    byte[] bytes = new byte[byteBuf.readableBytes()];
                    byteBuf.getBytes(0, bytes);
                    bytes = wrapper.unwrap(bytes);
                    XRequest request = requestResolver.parse(bytes);
                    String host = request.getHost();
                    int port = request.getPort();