Java tutorial
/* * 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; } }