/* * JLibs: Common Utilities for Java * Copyright (C) 2009 Santhosh Kumar T <santhosh.tekuri@gmail.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. */ package jlibs.nio.listeners; import jlibs.core.io.IOUtil; import jlibs.nio.*; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketException; import java.net.UnknownHostException; import java.nio.ByteBuffer; import static java.nio.channels.SelectionKey.OP_WRITE; import static jlibs.nio.listeners.Socks4Tunnel.Step.*; /** * @author Santhosh Kumar Tekuri */ public class Socks4Tunnel extends Task{ private static final byte VERSION = 4; private static final byte CONNECT = 1; private ByteBuffer buffer; public Socks4Tunnel(String user, InetSocketAddress endpoint) throws IOException{ super(OP_WRITE); if(endpoint.isUnresolved()) throw new UnknownHostException(endpoint.getHostString()); buffer = Reactor.current().allocator.allocate(); buffer.put(VERSION); // SOCKS version number buffer.put(CONNECT); // CONNECT command code buffer.put((byte)((endpoint.getPort()>>8)&0xff)); buffer.put((byte)(endpoint.getPort()&0xff)); buffer.put(endpoint.getAddress().getAddress()); if(user==null) user = System.getProperty("user.name"); buffer.put(user.getBytes(IOUtil.ISO_8859_1)); buffer.put((byte)0); buffer.flip(); } enum Step{ WRITE_REQUEST, READ_RESPONSE } private Step step = WRITE_REQUEST; @Override protected boolean process(int readyOp) throws IOException{ while(true){ switch(step){ case WRITE_REQUEST: if(!send(buffer)) return false; buffer.position(0); buffer.limit(8); step = READ_RESPONSE; case READ_RESPONSE: if(!read(buffer)) return false; buffer.flip(); if(buffer.remaining()!=8) throw new SocketException("Reply from SOCKS server has bad length: "+buffer.remaining()); byte firstByte = buffer.get(); if(firstByte!=0 && firstByte != VERSION) throw new SocketException("Reply from SOCKS server has bad version"); switch(buffer.get()){ case 0x5a: return true; case 0x5b: throw new SocketException("SOCKS request rejected or failed"); case 0x5c: throw new SocketException("SOCKS server couldn't reach destination"); case 0x5d: throw new SocketException("SOCKS authentication failed"); default: throw new SocketException("Reply from SOCKS server contains bad status"); } } } } @Override protected void cleanup(Throwable thr){ Reactor.current().allocator.free(buffer); buffer = null; } }