edu.tsinghua.lumaqq.qq.net.Socks5Proxy.java Source code

Java tutorial

Introduction

Here is the source code for edu.tsinghua.lumaqq.qq.net.Socks5Proxy.java

Source

/*
* LumaQQ - Java QQ Client
*
* Copyright (C) 2004 luma <stubma@163.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package edu.tsinghua.lumaqq.qq.net;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import edu.tsinghua.lumaqq.qq.Util;
import edu.tsinghua.lumaqq.qq.packets.PacketParseException;

/**
 * <pre>
 * Socks5 ???
 * </pre>
 * 
 * @see RFC 1928
 * @see RFC 1929
 * @author luma
 */
public class Socks5Proxy extends AbstractProxy {
    /** Log */
    private static final Log log = LogFactory.getLog(Socks5Proxy.class);
    /** Socks? */
    public static final byte VER_5 = 0x5;
    /** ? - ?? */
    public static final byte AUTH_NONE = 0x0;
    /** ? - ??/? */
    public static final byte AUTH_USERNAME_PASSWORD = 0x2;
    /**  -  */
    public static final byte REQ_CONNECT = 0x1;
    /**  - UDP */
    public static final byte REQ_UDP_ASSOCIATE = 0x3;
    /** ? - ipv4 */
    public static final byte ATYP_IPV4 = 0x1;
    /** ? - ?? */
    public static final byte ATYP_DOMAIN_NAME = 0x3;
    /** ? - IPV6 */
    public static final byte ATYP_IPV6 = 0x4;
    /** ?? - ? */
    public static final byte REPLY_SUCCESS = 0x0;

    /** ? -  */
    public static final int STATUS_NONE = 0;
    /** ? - ? */
    public static final int STATUS_INIT = 1;
    /** ? - ? */
    public static final int STATUS_AUTH = 2;
    /** ? - CONNECT */
    public static final int STATUS_CONNECT = 3;
    /** ? - UDP */
    public static final int STATUS_UDP_ASSOCIATE = 4;
    /** ? - Ready */
    public static final int STATUS_READY = 5;

    /** ???ip??? */
    protected byte[] remoteAddress;
    /** ? */
    protected int remotePort;
    /** ?IP?? */
    protected boolean isIp;
    /**???
     * socks5 ??
     * ????????
     * ???05 02 00 02
     * ??05 02
     * ?
     */
    protected boolean NeedVerify;

    /**
     * 
     * 
     * @param handler
     * @throws IOException
     */
    public Socks5Proxy(IProxyHandler handler, String u, String p, DatagramChannel channel) throws IOException {
        super(handler, channel);
        status = STATUS_NONE;
        NeedVerify = false;
        if (u != null) {
            NeedVerify = true;
            username = u;
        }
        if (p != null)
            password = p;
    }

    /**
     * ??
     * 
     * @param handler
     * @param u ??
     * @param p ?
     * @throws IOException
     */
    public Socks5Proxy(IProxyHandler handler, String u, String p) throws IOException {
        super(handler);
        status = STATUS_NONE;
        NeedVerify = false;
        if (u != null) {
            NeedVerify = true;
            username = u;
        }
        if (p != null)
            password = p;
    }

    /**
     * ????
     * +----+----------+----------+
     * |VER | NMETHODS | METHODS  |
     * +----+----------+----------+
     * | 1  |    1     | 1 to 255 |
     * +----+----------+----------+
     * 
     * @see edu.tsinghua.lumaqq.qq.net.IProxy#init()
     */
    public void init() {
        log.trace("Socks5 init");
        // ?
        buffer.clear();
        buffer.put(VER_5).put((byte) 0x1).put(NeedVerify ? AUTH_USERNAME_PASSWORD : AUTH_NONE);
        // ??
        buffer.flip();
        send();
        status = STATUS_INIT;
    }

    /**
     * ???
     * Socks5 ??
     * +----+------+----------+------+----------+
     * |VER | ULEN |  UNAME   | PLEN |  PASSWD  |
     * +----+------+----------+------+----------+
     * | 1  |  1   | 1 to 255 |  1   | 1 to 255 |
     * +----+------+----------+------+----------+
     */
    protected void auth() {
        log.trace("Socks5 auth");
        // ??????null
        buffer.clear();
        buffer.put(VER_5).put((byte) (username.length() & 0xFF)).put(username.getBytes())
                .put((byte) (password.length() & 0xFF)).put(password.getBytes());
        // ??
        buffer.flip();
        send();
        status = STATUS_AUTH;
    }

    /**
     * Socks?
     * +----+-----+-------+------+----------+----------+
     * |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
     * +----+-----+-------+------+----------+----------+
     * | 1  |  1  | X'00' |  1   | Variable |    2     |
     * +----+-----+-------+------+----------+----------+
     */
    protected void connect() {
        log.trace("Socks5 connect");
        // 
        buffer.clear();
        buffer.put(VER_5).put(REQ_CONNECT).put((byte) 0x0).put(isIp ? ATYP_IPV4 : ATYP_DOMAIN_NAME)
                .put(remoteAddress).putChar((char) remotePort);
        // ??
        buffer.flip();
        send();
        status = STATUS_CONNECT;
    }

    /**
     * ??UDP?
     * +----+-----+-------+------+----------+----------+
     * |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
     * +----+-----+-------+------+----------+----------+
     * | 1  |  1  | X'00' |  1   | Variable |    2     |
     * +----+-----+-------+------+----------+----------+
     */
    protected void associate() {
        log.trace("Socks5 associate");
        // 
        buffer.clear();
        buffer.put(VER_5).put(REQ_UDP_ASSOCIATE).put((byte) 0x0).put(ATYP_IPV4).putInt(0)
                .putChar((char) clientPort);
        // ??
        buffer.flip();
        send();
        status = STATUS_UDP_ASSOCIATE;
    }

    /* (non-Javadoc)
     * @see edu.tsinghua.lumaqq.qq.IHandler#processRead(java.nio.channels.SelectionKey)
     */
    public void processRead(SelectionKey sk) throws IOException, PacketParseException {
        // ?
        receive();
        // ?
        if (!buffer.hasRemaining())
            return;
        // ???5
        if (buffer.get(0) != VER_5)
            return;
        // ??????
        switch (status) {
        case STATUS_INIT:
            /* ?????????? */
            if (buffer.get(1) == AUTH_NONE) {
                log.debug("??????");
                if (udp)
                    associate();
                else
                    connect();
            } else if (buffer.get(1) == AUTH_USERNAME_PASSWORD) {
                log.debug("?????");
                auth();
            } else {
                log.debug("???");
                handler.proxyAuthFail();
            }
            break;
        case STATUS_AUTH:
            /* ?? */
            if (buffer.get(1) == REPLY_SUCCESS) {
                log.debug("?");
                if (udp)
                    associate();
                else
                    connect();
            } else {
                log.debug("?");
                handler.proxyAuthFail();
            }
            break;
        case STATUS_CONNECT:
            /* ???ready */
            if (buffer.get(1) == REPLY_SUCCESS) {
                log.debug("CONNECT?");
                status = STATUS_READY;
                handler.proxyReady(null);
            } else {
                /*  */
                log.debug("?????");
                handler.proxyError("?????");
            }
            break;
        case STATUS_UDP_ASSOCIATE:
            /*
             * ???ready?bind address
             * bind address??Socks5?IP??
             * ??QQ?0x0031?bind 
             * address?????TCP? 
             */
            if (buffer.get(1) == REPLY_SUCCESS) {
                // ?
                byte addressType = buffer.get(3);
                // bind address
                byte[] a = new byte[buffer.limit() - 6];
                buffer.position(4);
                buffer.get(a);
                // bind port
                int p = buffer.getChar();
                // ????
                String addressStr = null;
                if (addressType == ATYP_IPV4)
                    addressStr = Util.getIpStringFromBytes(a);
                else if (addressType == ATYP_DOMAIN_NAME)
                    addressStr = new String(a);
                else {
                    log.debug("??IPV6");
                    return;
                }
                // ??
                InetSocketAddress address = new InetSocketAddress(addressStr, p);
                // log
                log.debug("UDP?BND.ADDR: " + addressStr + " BND.PORT: " + p);
                // ?
                status = STATUS_READY;
                // UDP Socks5???
                sk.cancel();
                // 
                handler.proxyReady(address);
            } else {
                /*  */
                log.debug("?????");
                handler.proxyError("?????");
            }
            break;
        default:
            break;
        }
    }

    /* (non-Javadoc)
     * @see edu.tsinghua.lumaqq.qq.IHandler#processWrite()
     */
    public void processWrite() throws IOException {
        log.trace("Socks5Proxy processWrite");
        if (connected && status == STATUS_NONE)
            init();
    }

    /* (non-Javadoc)
     * @see edu.tsinghua.lumaqq.qq.IProxy#setServerAddress(java.net.InetSocketAddress)
     */
    @Override
    public void setRemoteAddress(InetSocketAddress serverAddress) {
        super.setRemoteAddress(serverAddress);
        // ???
        if (serverAddress.isUnresolved()) {
            /* ??? */
            isIp = false;
            byte[] b = serverAddress.getHostName().getBytes();
            remoteAddress = new byte[b.length + 1];
            remoteAddress[0] = (byte) (b.length & 0xFF);
            System.arraycopy(b, 0, remoteAddress, 1, b.length);
        } else {
            /* ???IPSocks5??????ipip */
            isIp = true;
            remoteAddress = serverAddress.getAddress().getAddress();
        }
        remotePort = serverAddress.getPort();
    }

    /**
     * @param clientPort The clientPort to set.
     */
    @Override
    public void setClientPort(int clientPort) {
        this.clientPort = clientPort;
    }
}