// Path: src/main/java/cc/agentx/protocol/Socks5.java
// public class Socks5 {
//     public static final int VERSION = 5;
//     public static final int ATYP_IPV4 = 1;
//     public static final int ATYP_DOMAIN = 3;
//     public static final int ATYP_IPV6 = 4;
//     // preset replies
//     public static byte[] echo = {5, 0};
//     public static byte[] reject = {5, (byte) 0xff};
//     public static byte[] succeed = {5, 0, 0, 1, 0, 0, 0, 0, 0, 0};
//     public static byte[] error_1 = {5, 1, 0, 1, 0, 0, 0, 0, 0, 0}; // general socks server failure
//     public static byte[] error_2 = {5, 2, 0, 1, 0, 0, 0, 0, 0, 0}; // connection not allowed by rule set
//     public static byte[] error_3 = {5, 3, 0, 1, 0, 0, 0, 0, 0, 0}; // network unreachable
//     public static byte[] error_4 = {5, 4, 0, 1, 0, 0, 0, 0, 0, 0}; // host unreachable
//     public static byte[] error_5 = {5, 5, 0, 1, 0, 0, 0, 0, 0, 0}; // connection refused
//     public static byte[] error_6 = {5, 6, 0, 1, 0, 0, 0, 0, 0, 0}; // ttl expired
//     public static byte[] error_7 = {5, 7, 0, 1, 0, 0, 0, 0, 0, 0}; // command not supported
//     public static byte[] error_8 = {5, 8, 0, 1, 0, 0, 0, 0, 0, 0}; // address type not supported
//     private Socks5() {
//     }
// 
// }


import cc.agentx.protocol.Socks5;
import java.util.Arrays;
/*
 * 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.protocol.request;



public class ShadowsocksRequestResolver extends XRequestResolver {

    public ShadowsocksRequestResolver() {
    }

    /**
     * <pre>
     * TCP Request:
     * +-------------------+----------------------------+
     * |  SOCKS5 FIELD (3) |      SHADOWSOCKS REQ       |
     * +-----+-----+-------+------+----------+----------+
     * | VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
     * +-----+-----+-------+------+----------+----------+
     * |  1  |  1  | X'00' |  1   | Variable |    2     |
     * +-----+-----+-------+------+----------+----------+
     *
     * UDP Request:
     *       +-------------------------------------------------------------+
     *       |                        AGENTX HEADER                        |
     * +-----+---------------------+---------------------------------------+
     * |      SOCKS5 FIELD (3)     |            SHADOWSOCKS REQ            |
     * +-----+---------------------+------+----------+----------+----------+
     * | RSV |  FRAG (FAKE-ATYP)   | ATYP | DST.ADDR | DST.PORT |   DATA   |
     * +----+----------------------+------+----------+----------+----------+
     * |  2  |        X'00'        |  1   | Variable |    2     | Variable |
     * +-----+---------------------+------+----------+----------+----------+
     * </pre>
     * Notice: In AgentX implementation, we send udp traffic though a secure tcp tunnel,
     * to distinct these two types of tcp payload, we add an extra 0 byte in front of any
     * udp-target requests. Thus, if we find a ATYP equals to 0, after truncate the first
     * byte, we can get the accurate udp-target request.
     */
    @Override
    public byte[] wrap(XRequest.Channel channel, final byte[] bytes) {
        if (channel == XRequest.Channel.UDP)
            return Arrays.copyOfRange(bytes, 2, bytes.length);
        else
            return Arrays.copyOfRange(bytes, 3, bytes.length);
    }

    @Override
    public XRequest parse(byte[] bytes) {
        boolean udp = false;
        XRequest.Type atyp;
        String host;
        int port, subsequentDataLength;

        // mark udp payload
        if (bytes[0] == 0) {
            udp = true;
            bytes = Arrays.copyOfRange(bytes, 1, bytes.length);
        }

        switch (bytes[0]) {
// Path: src/main/java/cc/agentx/protocol/Socks5.java
// public class Socks5 {
//     public static final int VERSION = 5;
//     public static final int ATYP_IPV4 = 1;
//     public static final int ATYP_DOMAIN = 3;
//     public static final int ATYP_IPV6 = 4;
//     // preset replies
//     public static byte[] echo = {5, 0};
//     public static byte[] reject = {5, (byte) 0xff};
//     public static byte[] succeed = {5, 0, 0, 1, 0, 0, 0, 0, 0, 0};
//     public static byte[] error_1 = {5, 1, 0, 1, 0, 0, 0, 0, 0, 0}; // general socks server failure
//     public static byte[] error_2 = {5, 2, 0, 1, 0, 0, 0, 0, 0, 0}; // connection not allowed by rule set
//     public static byte[] error_3 = {5, 3, 0, 1, 0, 0, 0, 0, 0, 0}; // network unreachable
//     public static byte[] error_4 = {5, 4, 0, 1, 0, 0, 0, 0, 0, 0}; // host unreachable
//     public static byte[] error_5 = {5, 5, 0, 1, 0, 0, 0, 0, 0, 0}; // connection refused
//     public static byte[] error_6 = {5, 6, 0, 1, 0, 0, 0, 0, 0, 0}; // ttl expired
//     public static byte[] error_7 = {5, 7, 0, 1, 0, 0, 0, 0, 0, 0}; // command not supported
//     public static byte[] error_8 = {5, 8, 0, 1, 0, 0, 0, 0, 0, 0}; // address type not supported
//     private Socks5() {
//     }
// 
// }


// Path: src/main/java/cc/agentx/protocol/request/ShadowsocksRequestResolver.java
import cc.agentx.protocol.Socks5;
import java.util.Arrays;

/*
 * 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.protocol.request;



public class ShadowsocksRequestResolver extends XRequestResolver {

    public ShadowsocksRequestResolver() {
    }

    /**
     * <pre>
     * TCP Request:
     * +-------------------+----------------------------+
     * |  SOCKS5 FIELD (3) |      SHADOWSOCKS REQ       |
     * +-----+-----+-------+------+----------+----------+
     * | VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
     * +-----+-----+-------+------+----------+----------+
     * |  1  |  1  | X'00' |  1   | Variable |    2     |
     * +-----+-----+-------+------+----------+----------+
     *
     * UDP Request:
     *       +-------------------------------------------------------------+
     *       |                        AGENTX HEADER                        |
     * +-----+---------------------+---------------------------------------+
     * |      SOCKS5 FIELD (3)     |            SHADOWSOCKS REQ            |
     * +-----+---------------------+------+----------+----------+----------+
     * | RSV |  FRAG (FAKE-ATYP)   | ATYP | DST.ADDR | DST.PORT |   DATA   |
     * +----+----------------------+------+----------+----------+----------+
     * |  2  |        X'00'        |  1   | Variable |    2     | Variable |
     * +-----+---------------------+------+----------+----------+----------+
     * </pre>
     * Notice: In AgentX implementation, we send udp traffic though a secure tcp tunnel,
     * to distinct these two types of tcp payload, we add an extra 0 byte in front of any
     * udp-target requests. Thus, if we find a ATYP equals to 0, after truncate the first
     * byte, we can get the accurate udp-target request.
     */
    @Override
    public byte[] wrap(XRequest.Channel channel, final byte[] bytes) {
        if (channel == XRequest.Channel.UDP)
            return Arrays.copyOfRange(bytes, 2, bytes.length);
        else
            return Arrays.copyOfRange(bytes, 3, bytes.length);
    }

    @Override
    public XRequest parse(byte[] bytes) {
        boolean udp = false;
        XRequest.Type atyp;
        String host;
        int port, subsequentDataLength;

        // mark udp payload
        if (bytes[0] == 0) {
            udp = true;
            bytes = Arrays.copyOfRange(bytes, 1, bytes.length);
        }

        switch (bytes[0]) {
            case Socks5.ATYP_IPV4: