SocksClient.java
上传用户:demmber
上传日期:2007-12-22
资源大小:717k
文件大小:17k
源码类别:

Java编程

开发平台:

Java

  1. /*
  2.  * @(#)SocksClient.java 0.3-3 06/05/2001
  3.  *
  4.  *  This file is part of the HTTPClient package
  5.  *  Copyright (C) 1996-2001 Ronald Tschal鋜
  6.  *
  7.  *  This library is free software; you can redistribute it and/or
  8.  *  modify it under the terms of the GNU Lesser General Public
  9.  *  License as published by the Free Software Foundation; either
  10.  *  version 2 of the License, or (at your option) any later version.
  11.  *
  12.  *  This library is distributed in the hope that it will be useful,
  13.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  *  Lesser General Public License for more details.
  16.  *
  17.  *  You should have received a copy of the GNU Lesser General Public
  18.  *  License along with this library; if not, write to the Free
  19.  *  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  20.  *  MA 02111-1307, USA
  21.  *
  22.  *  For questions, suggestions, bug-reports, enhancement-requests etc.
  23.  *  I may be contacted at:
  24.  *
  25.  *  ronald@innovation.ch
  26.  *
  27.  *  The HTTPClient's home page is located at:
  28.  *
  29.  *  http://www.innovation.ch/java/HTTPClient/ 
  30.  *
  31.  */
  32. package HTTPClient;
  33. import java.io.IOException;
  34. import java.io.InputStream;
  35. import java.io.OutputStream;
  36. import java.io.ByteArrayOutputStream;
  37. import java.net.Socket;
  38. import java.net.InetAddress;
  39. import java.net.SocketException;
  40. import java.net.UnknownHostException;
  41. /**
  42.  * This class implements a SOCKS Client. Supports both versions 4 and 5.
  43.  * GSSAPI however is not yet implemented.
  44.  * <P>Usage is as follows: somewhere in the initialization code (and before
  45.  * the first socket creation call) create a SocksClient instance. Then replace
  46.  * each socket creation call
  47.  *
  48.  *     <code>sock = new Socket(host, port);</code>
  49.  *
  50.  * with
  51.  *
  52.  *     <code>sock = socks_client.getSocket(host, port);</code>
  53.  *
  54.  * (where <var>socks_client</var> is the above created SocksClient instance).
  55.  * That's all.
  56.  *
  57.  * @version 0.3-3  06/05/2001
  58.  * @author Ronald Tschal鋜
  59.  */
  60. class SocksClient
  61. {
  62.     /** the host the socks server sits on */
  63.     private String socks_host;
  64.     /** the port the socks server listens on */
  65.     private int    socks_port;
  66.     /** the version of socks that the server handles */
  67.     private int    socks_version;
  68.     /** socks commands */
  69.     private final static byte CONNECT = 1,
  70.       BIND    = 2,
  71.       UDP_ASS = 3;
  72.     /** socks version 5 authentication methods */
  73.     private final static byte NO_AUTH = 0,
  74.       GSSAPI  = 1,
  75.       USERPWD = 2,
  76.       NO_ACC  = (byte) 0xFF;
  77.     /** socks version 5 address types */
  78.     private final static byte IP_V4   = 1,
  79.       DMNAME  = 3,
  80.       IP_V6   = 4;
  81.     // Constructors
  82.     /**
  83.      * Creates a new SOCKS Client using the specified host and port for
  84.      * the server. Will try to establish the SOCKS version used when
  85.      * establishing the first connection.
  86.      *
  87.      * @param host  the host the SOCKS server is sitting on.
  88.      * @param port  the port the SOCKS server is listening on.
  89.      */
  90.     SocksClient(String host, int port)
  91.     {
  92. this.socks_host    = host;
  93. this.socks_port    = port;
  94. this.socks_version = -1; // as yet unknown
  95.     }
  96.     /**
  97.      * Creates a new SOCKS Client using the specified host and port for
  98.      * the server.
  99.      *
  100.      * @param host     the host the SOCKS server is sitting on.
  101.      * @param port     the port the SOCKS server is listening on.
  102.      * @param version  the version the SOCKS server is using.
  103.      * @exception SocksException if the version is invalid (Currently allowed
  104.      *                           are: 4 and 5).
  105.      */
  106.     SocksClient(String host, int port, int version)  throws SocksException
  107.     {
  108. this.socks_host    = host;
  109. this.socks_port    = port;
  110. if (version != 4  &&  version != 5)
  111.     throw new SocksException("SOCKS Version not supported: "+version);
  112. this.socks_version = version;
  113.     }
  114.     // Methods
  115.     /**
  116.      * Initiates a connection to the socks server, does the startup
  117.      * protocol and returns a socket ready for talking.
  118.      *
  119.      * @param host  the host you wish to connect to
  120.      * @param port  the port you wish to connect to
  121.      * @return a Socket with a connection via socks to the desired host/port
  122.      * @exception IOException if any socket operation fails
  123.      */
  124.     Socket getSocket(String host, int port)  throws IOException
  125.     {
  126. return getSocket(host, port, null, -1);
  127.     }
  128.     /**
  129.      * Initiates a connection to the socks server, does the startup
  130.      * protocol and returns a socket ready for talking.
  131.      *
  132.      * @param host      the host you wish to connect to
  133.      * @param port      the port you wish to connect to
  134.      * @param localAddr the local address to bind to
  135.      * @param localPort the local port to bind to
  136.      * @return a Socket with a connection via socks to the desired host/port
  137.      * @exception IOException if any socket operation fails
  138.      */
  139.     Socket getSocket(String host, int port, InetAddress localAddr,
  140.      int localPort)  throws IOException
  141.     {
  142. Socket sock = null;
  143. try
  144. {
  145.     Log.write(Log.SOCKS, "Socks: contacting server on " +
  146.  socks_host + ":" + socks_port);
  147.     // create socket and streams
  148.     sock = connect(socks_host, socks_port, localAddr, localPort);
  149.     InputStream  inp = sock.getInputStream();
  150.     OutputStream out = sock.getOutputStream();
  151.     // setup connection depending on socks version
  152.     switch (socks_version)
  153.     {
  154. case 4:
  155.     v4ProtExchg(inp, out, host, port);
  156.     break;
  157. case 5:
  158.     v5ProtExchg(inp, out, host, port);
  159.     break;
  160. case -1:
  161.     // Ok, let's try and figure it out
  162.     try
  163.     {
  164. v4ProtExchg(inp, out, host, port);
  165. socks_version = 4;
  166.     }
  167.     catch (SocksException se)
  168.     {
  169. Log.write(Log.SOCKS, "Socks: V4 request failed: " +
  170.      se.getMessage());
  171. sock.close();
  172. sock = connect(socks_host, socks_port, localAddr,
  173.        localPort);
  174. inp = sock.getInputStream();
  175. out = sock.getOutputStream();
  176. v5ProtExchg(inp, out, host, port);
  177. socks_version = 5;
  178.     }
  179.     break;
  180. default:
  181.     throw new Error("SocksClient internal error: unknown " +
  182.     "version "+socks_version);
  183.     }
  184.     Log.write(Log.SOCKS, "Socks: connection established.");
  185.     return sock;
  186. }
  187. catch (IOException ioe)
  188. {
  189.     if (sock != null)
  190.     {
  191. try { sock.close(); }
  192. catch (IOException ee) {}
  193.     }
  194.     throw ioe;
  195. }
  196.     }
  197.     /**
  198.      * Connect to the host/port, trying all addresses assciated with that
  199.      * host.
  200.      *
  201.      * @param host      the host you wish to connect to
  202.      * @param port      the port you wish to connect to
  203.      * @param localAddr the local address to bind to
  204.      * @param localPort the local port to bind to
  205.      * @return the Socket
  206.      * @exception IOException if the connection could not be established
  207.      */
  208.     private static final Socket connect(String host, int port,
  209. InetAddress localAddr, int localPort)
  210.     throws IOException
  211.     {
  212. InetAddress[] addr_list = InetAddress.getAllByName(host);
  213. for (int idx=0; idx<addr_list.length; idx++)
  214. {
  215.     try
  216.     {
  217. if (localAddr == null)
  218.     return new Socket(addr_list[idx], port);
  219. else
  220.     return new Socket(addr_list[idx], port, localAddr, localPort);
  221.     }
  222.     catch (SocketException se)
  223.     {
  224. if (idx < addr_list.length-1)
  225.     continue; // try next IP address
  226. else
  227.     throw se; // none of them worked
  228.     }
  229. }
  230. return null; // never reached - just here to shut up the compiler
  231.     }
  232.     private boolean v4A  = false; // SOCKS version 4A
  233.     private byte[]  user = null;
  234.     /**
  235.      * Does the protocol exchange for a version 4 SOCKS connection.
  236.      */
  237.     private void v4ProtExchg(InputStream inp, OutputStream out, String host,
  238.      int port)
  239. throws SocksException, IOException
  240.     {
  241. ByteArrayOutputStream buffer = new ByteArrayOutputStream(100);
  242. Log.write(Log.SOCKS, "Socks: Beginning V4 Protocol Exchange for host "
  243.      + host + ":" + port);
  244. // get ip addr and user name
  245. byte[] addr = { 0, 0, 0, 42 };
  246. if (!v4A)
  247. {
  248.     try
  249. { addr = InetAddress.getByName(host).getAddress(); }
  250.     // if we can't translate, let's try the server
  251.     catch (UnknownHostException uhe)
  252. { v4A = true; }
  253.     catch (SecurityException se)
  254. { v4A = true; }
  255.     if (v4A)
  256. Log.write(Log.SOCKS, "Socks: Switching to version 4A");
  257. }
  258. if (user == null) // I see no reason not to cache this
  259. {
  260.     String user_str;
  261.     try
  262. { user_str = System.getProperty("user.name", ""); }
  263.     catch (SecurityException se)
  264. { user_str = ""; /* try it anyway */ }
  265.     byte[] tmp = user_str.getBytes();
  266.     user = new byte[tmp.length+1];
  267.     System.arraycopy(tmp, 0, user, 0, tmp.length);
  268.     user[user_str.length()] = 0; // 0-terminated string
  269. }
  270. // send version 4 request
  271. Log.write(Log.SOCKS, "Socks: Sending connect request for user " +
  272.      new String(user, 0, user.length-1));
  273. buffer.reset();
  274. buffer.write(4); // version
  275. buffer.write(CONNECT); // command
  276. buffer.write((port >> 8) & 0xff); // port
  277. buffer.write(port & 0xff);
  278. buffer.write(addr); // address
  279. buffer.write(user); // user
  280. if (v4A)
  281. {
  282.     buffer.write(host.getBytes("8859_1")); // host name
  283.     buffer.write(0); // terminating 0
  284. }
  285. buffer.writeTo(out);
  286. // read response
  287. int version = inp.read();
  288. if (version == -1)
  289.     throw new SocksException("Connection refused by server");
  290. else if (version == 4) // not all socks4 servers are correct...
  291.     Log.write(Log.SOCKS, "Socks: Warning: received version 4 " +
  292.  "instead of 0");
  293. else if (version != 0)
  294.     throw new SocksException("Received invalid version: " + version +
  295.      "; expected: 0");
  296. int sts = inp.read();
  297. Log.write(Log.SOCKS, "Socks: Received response; version: " + version +
  298.      "; status: " + sts);
  299. switch (sts)
  300. {
  301.     case 90: // request granted
  302. break;
  303.     case 91: // request rejected
  304. throw new SocksException("Connection request rejected");
  305.     case 92: // request rejected: can't connect to identd
  306. throw new SocksException("Connection request rejected: " +
  307.  "can't connect to identd");
  308.     case 93: // request rejected: identd reports diff uid
  309. throw new SocksException("Connection request rejected: " +
  310.  "identd reports different user-id " +
  311.  "from "+
  312.  new String(user, 0, user.length-1));
  313.     default: // unknown status
  314. throw new SocksException("Connection request rejected: " +
  315.  "unknown error " + sts);
  316. }
  317. byte[] skip = new byte[2+4]; // skip port + address
  318. int rcvd = 0,
  319.     tot  = 0;
  320. while (tot < skip.length  &&
  321. (rcvd = inp.read(skip, 0, skip.length-tot)) != -1)
  322.     tot += rcvd;
  323.     }
  324.     /**
  325.      * Does the protocol exchange for a version 5 SOCKS connection.
  326.      * (rfc-1928)
  327.      */
  328.     private void v5ProtExchg(InputStream inp, OutputStream out, String host,
  329.      int port)
  330. throws SocksException, IOException
  331.     {
  332. int                   version;
  333. ByteArrayOutputStream buffer = new ByteArrayOutputStream(100);
  334. Log.write(Log.SOCKS, "Socks: Beginning V5 Protocol Exchange for host "
  335.      + host + ":" + port);
  336. // send version 5 verification methods
  337. Log.write(Log.SOCKS, "Socks: Sending authentication request; methods"
  338.      + " No-Authentication, Username/Password");
  339. buffer.reset();
  340. buffer.write(5); // version
  341. buffer.write(2); // number of verification methods
  342. buffer.write(NO_AUTH); // method: no authentication
  343. buffer.write(USERPWD); // method: username/password
  344. //buffer.write(GSSAPI); // method: gssapi
  345. buffer.writeTo(out);
  346. // receive servers repsonse
  347. version = inp.read();
  348. if (version == -1)
  349.     throw new SocksException("Connection refused by server");
  350. else if (version != 5)
  351.     throw new SocksException("Received invalid version: " + version +
  352.      "; expected: 5");
  353. int method = inp.read();
  354. Log.write(Log.SOCKS, "Socks: Received response; version: " + version +
  355.      "; method: " + method);
  356. // enter sub-negotiation for authentication
  357. switch(method)
  358. {
  359.     case NO_AUTH:
  360. break;
  361.     case GSSAPI:
  362. negotiate_gssapi(inp, out);
  363. break;
  364.     case USERPWD:
  365. negotiate_userpwd(inp, out);
  366. break;
  367.     case NO_ACC:
  368. throw new SocksException("Server unwilling to accept any " +
  369.  "standard authentication methods");
  370.     default:
  371. throw new SocksException("Cannot handle authentication method "
  372.  + method);
  373. }
  374. // send version 5 request
  375. Log.write(Log.SOCKS, "Socks: Sending connect request");
  376. buffer.reset();
  377. buffer.write(5); // version
  378. buffer.write(CONNECT); // command
  379. buffer.write(0); // reserved - must be 0
  380. buffer.write(DMNAME); // address type
  381. buffer.write(host.length() & 0xff); // address length
  382. buffer.write(host.getBytes("8859_1")); // address
  383. buffer.write((port >> 8) & 0xff); // port
  384. buffer.write(port & 0xff);
  385. buffer.writeTo(out);
  386. // read response
  387. version = inp.read();
  388. if (version != 5)
  389.     throw new SocksException("Received invalid version: " + version +
  390.      "; expected: 5");
  391. int sts = inp.read();
  392. Log.write(Log.SOCKS, "Socks: Received response; version: " + version +
  393.      "; status: " + sts);
  394. switch (sts)
  395. {
  396.     case 0: // succeeded
  397. break;
  398.     case 1:
  399. throw new SocksException("General SOCKS server failure");
  400.     case 2:
  401. throw new SocksException("Connection not allowed");
  402.     case 3:
  403. throw new SocksException("Network unreachable");
  404.     case 4:
  405. throw new SocksException("Host unreachable");
  406.     case 5:
  407. throw new SocksException("Connection refused");
  408.     case 6:
  409. throw new SocksException("TTL expired");
  410.     case 7:
  411. throw new SocksException("Command not supported");
  412.     case 8:
  413. throw new SocksException("Address type not supported");
  414.     default:
  415. throw new SocksException("Unknown reply received from server: "
  416.  + sts);
  417. }
  418. inp.read(); // Reserved
  419. int atype = inp.read(), // address type
  420.     alen; // address length
  421. switch(atype)
  422. {
  423.     case IP_V6:
  424. alen = 16;
  425. break;
  426.     case IP_V4:
  427. alen = 4;
  428. break;
  429.     case DMNAME:
  430. alen = inp.read();
  431. break;
  432.     default:
  433. throw new SocksException("Invalid address type received from" +
  434.  " server: "+atype);
  435. }
  436. byte[] skip = new byte[alen+2]; // skip address + port
  437. int rcvd = 0,
  438.     tot  = 0;
  439. while (tot < skip.length  &&
  440. (rcvd = inp.read(skip, 0, skip.length-tot)) != -1)
  441.     tot += rcvd;
  442.     }
  443.     /**
  444.      * Negotiates authentication using the gssapi protocol
  445.      * (draft-ietf-aft-gssapi-02).
  446.      *
  447.      * NOTE: this is not implemented currently. Will have to wait till
  448.      *       Java provides the necessary access to the system routines.
  449.      */
  450.     private void negotiate_gssapi(InputStream inp, OutputStream out)
  451. throws SocksException, IOException
  452.     {
  453. throw new
  454.     SocksException("GSSAPI authentication protocol not implemented");
  455.     }
  456.     /**
  457.      * Negotiates authentication using the username/password protocol
  458.      * (rfc-1929). The username and password should previously have been
  459.      * stored using the scheme "SOCKS5" and realm "USER/PASS"; e.g.
  460.      * AuthorizationInfo.addAuthorization(socks_host, socks_port, "SOCKS5",
  461.      *   "USER/PASS", null,
  462.      *   { new NVPair(username, password) });
  463.      *
  464.      */
  465.     private void negotiate_userpwd(InputStream inp, OutputStream out)
  466. throws SocksException, IOException
  467.     {
  468. byte[] buffer;
  469. Log.write(Log.SOCKS, "Socks: Entering authorization subnegotiation" +
  470.      "; method: Username/Password");
  471. // get username/password
  472. AuthorizationInfo auth_info;
  473. try
  474. {
  475.     auth_info =
  476. AuthorizationInfo.getAuthorization(socks_host, socks_port,
  477.    "SOCKS5", "USER/PASS",
  478.    null, null, true);
  479. }
  480. catch (AuthSchemeNotImplException atnie)
  481.     { auth_info = null; }
  482. if (auth_info == null)
  483.     throw new SocksException("No Authorization info for SOCKS found " +
  484.      "(server requested username/password).");
  485. NVPair[] unpw = auth_info.getParams();
  486. if (unpw == null  ||  unpw.length == 0)
  487.     throw new SocksException("No Username/Password found in " +
  488.      "authorization info for SOCKS.");
  489. String user_str = unpw[0].getName();
  490. String pass_str = unpw[0].getValue();
  491. // send them to server
  492. Log.write(Log.SOCKS, "Socks: Sending authorization request for user "+
  493.      user_str);
  494. byte[] utmp = user_str.getBytes();
  495. byte[] ptmp = pass_str.getBytes();
  496. buffer = new byte[1+1+utmp.length+1+ptmp.length];
  497. buffer[0] = 1; // version 1 (subnegotiation)
  498. buffer[1] = (byte) utmp.length; // Username length
  499. System.arraycopy(utmp, 0, buffer, 2, utmp.length); // Username
  500. buffer[2+buffer[1]] = (byte) ptmp.length; // Password length
  501. System.arraycopy(ptmp, 0, buffer, 2+buffer[1]+1, ptmp.length); // Password
  502. out.write(buffer);
  503. // get reply
  504. int version = inp.read();
  505. if (version != 1)
  506.     throw new SocksException("Wrong version received in username/" +
  507.      "password subnegotiation response: " +
  508.      version + "; expected: 1");
  509. int sts = inp.read();
  510. if (sts != 0)
  511.     throw new SocksException("Username/Password authentication " +
  512.      "failed; status: "+sts);
  513. Log.write(Log.SOCKS, "Socks: Received response; version: " + version +
  514.      "; status: " + sts);
  515.     }
  516.     /**
  517.      * produces a string.
  518.      * @return a string containing the host and port of the socks server
  519.      */
  520.     public String toString()
  521.     {
  522. return getClass().getName() + "[" + socks_host + ":" + socks_port + "]";
  523.     }
  524. }