home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 November / Chip_1998-11_cd.bin / tema / Cafe / main.bin / PlainSocketImpl.java < prev    next >
Text File  |  1997-05-20  |  13KB  |  446 lines

  1. /*
  2.  * @(#)PlainSocketImpl.java    1.25 97/01/25
  3.  * 
  4.  * Copyright (c) 1995, 1996 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  * CopyrightVersion 1.1_beta
  20.  * 
  21.  */
  22.  
  23. package java.net;
  24.  
  25. import java.io.IOException;
  26. import java.io.InputStream;
  27. import java.io.OutputStream;
  28. import java.io.InterruptedIOException;
  29. import java.io.FileDescriptor;
  30. import java.io.ByteArrayOutputStream;
  31.  
  32. /**
  33.  * Default Socket Implementation. This implementation does
  34.  * not implement any security checks.  It does support SOCKS version 4.
  35.  * Note this class should <b>NOT</b> be public.
  36.  *
  37.  * @author  Steven B. Byrne
  38.  * @version 1.25, 01/25/97
  39.  */
  40. class PlainSocketImpl extends SocketImpl
  41. {
  42.     /* instance variable for SO_TIMEOUT */
  43.     int timeout;   // timeout in millisec
  44.  
  45.     /* SOCKS related constants */
  46.  
  47.     private static final int SOCKS_PROTO_VERS        = 4;
  48.     private static final int SOCKS_REPLY_VERS        = 4;
  49.  
  50.     private static final int COMMAND_CONNECT        = 1;
  51.     private static final int COMMAND_BIND        = 2;
  52.  
  53.     private static final int REQUEST_GRANTED        = 90;
  54.     private static final int REQUEST_REJECTED        = 91;
  55.     private static final int REQUEST_REJECTED_NO_IDENTD  = 92;
  56.     private static final int REQUEST_REJECTED_DIFF_IDENTS = 93;
  57.  
  58.     public static final String socksServerProp        = "socksProxyHost";
  59.     public static final String socksPortProp        = "socksProxyPort";
  60.  
  61.     public static final String socksDefaultPortStr    = "1080";
  62.  
  63.     /**
  64.      * Load net library into runtime.
  65.      */
  66.     static {
  67.     System.loadLibrary("net");
  68.     initProto();
  69.     }
  70.  
  71.     /**
  72.      * Creates a socket with a boolean that specifies whether this
  73.      * is a stream socket (true) or an unconnected UDP socket (false).
  74.      */
  75.     protected synchronized void create(boolean stream) throws IOException {
  76.     fd = new FileDescriptor();
  77.     socketCreate(stream);
  78.     }
  79.  
  80.     /** 
  81.      * Creates a socket and connects it to the specified port on
  82.      * the specified host.
  83.      * @param host the specified host
  84.      * @param port the specified port 
  85.      */
  86.     protected void connect(String host, int port)
  87.         throws UnknownHostException, IOException
  88.     {
  89.     IOException pending = null;
  90.     try {
  91.         InetAddress address = InetAddress.getByName(host);
  92.  
  93.         try {
  94.         connectToAddress(address, port);
  95.         return;
  96.         } catch (IOException e) {
  97.         pending = e;
  98.         }
  99.     } catch (UnknownHostException e) {
  100.         pending = e;
  101.     }
  102.  
  103.     // everything failed
  104.     close();
  105.     throw pending;
  106.     }
  107.  
  108.     /** 
  109.      * Creates a socket and connects it to the specified address on
  110.      * the specified port.
  111.      * @param address the address
  112.      * @param port the specified port
  113.      */
  114.     protected void connect(InetAddress address, int port) throws IOException {
  115.     this.port = port;
  116.     this.address = address;
  117.  
  118.     try {
  119.         connectToAddress(address, port);
  120.         return;
  121.     } catch (IOException e) {
  122.         // everything failed
  123.         close();
  124.         throw e;
  125.     }
  126.     }
  127.  
  128.     private void connectToAddress(InetAddress address, int port) throws IOException {
  129.     if (usingSocks()) {
  130.         doSOCKSConnect(address, port);
  131.     } else {
  132.         doConnect(address, port);
  133.     }
  134.     }
  135.  
  136.     public void setOption(int opt, Object val) throws SocketException {
  137.     boolean on = true;
  138.     switch (opt) {
  139.         /* check type safety b4 going native.  These should never
  140.          * fail, since only java.Socket* has access to 
  141.          * PlainSocketImpl.setOption().
  142.          */
  143.         case SO_LINGER:
  144.         if (val == null || (!(val instanceof Integer) && !(val instanceof Boolean)))
  145.             throw new SocketException("Bad parameter for option");
  146.         if (val instanceof Boolean) { 
  147.             /* true only if disabling - enabling should be Integer */
  148.             on = false;
  149.         }
  150.         break;
  151.         case SO_TIMEOUT:
  152.         if (val == null || (!(val instanceof Integer)))
  153.             throw new SocketException("Bad parameter for SO_TIMEOUT");
  154.         int tmp = ((Integer) val).intValue();
  155.         if (tmp < 0)
  156.             throw new IllegalArgumentException("timeout < 0");
  157.         timeout = tmp;
  158.         return;
  159.         case SO_BINDADDR:
  160.         throw new SocketException("Cannot re-bind socket");
  161.         case TCP_NODELAY:
  162.         if (val == null || !(val instanceof Boolean))
  163.             throw new SocketException("bad parameter for TCP_NODELAY");
  164.         on = ((Boolean)val).booleanValue();
  165.         break;
  166.         default:
  167.         throw new SocketException("unrecognized TCP option: " + opt);
  168.     }
  169.     socketSetOption(opt, on, val);
  170.     }
  171.  
  172.     public Object getOption(int opt) throws SocketException {
  173.     if (opt == SO_TIMEOUT) {
  174.         return new Integer(timeout);
  175.     }
  176.     int ret = socketGetOption(opt);
  177.     /*
  178.      * The native socketGetOption() knows about 3 options.
  179.      * The 32 bit value it returns will be interpreted according
  180.      * to what we're asking.  A return of -1 means it understands
  181.      * the option but its turned off.  It will raise a SocketException
  182.      * if "opt" isn't one it understands.
  183.      */
  184.  
  185.     switch (opt) {
  186.     case TCP_NODELAY:
  187.         return (ret == -1) ? new Boolean(false): new Boolean(true);
  188.     case SO_LINGER:
  189.         return (ret == -1) ? new Boolean(false): (Object)(new Integer(ret));
  190.     case SO_BINDADDR:
  191.         InetAddress in = new InetAddress();
  192.         in.address = ret;
  193.         return in;
  194.     }
  195.     // should never get here
  196.     return null;
  197.     }
  198.  
  199.     /**
  200.      * Connect to the SOCKS server using the SOCKS connection protocol.
  201.      */
  202.     private void doSOCKSConnect(InetAddress address, int port) throws IOException {
  203.     connectToSocksServer();
  204.  
  205.     sendSOCKSCommandPacket(COMMAND_CONNECT, address, port);
  206.  
  207.     int protoStatus = getSOCKSReply();
  208.  
  209.     switch (protoStatus) {
  210.       case REQUEST_GRANTED:
  211.         // connection set up, return control to the socket client
  212.         return;
  213.  
  214.       case REQUEST_REJECTED:
  215.       case REQUEST_REJECTED_NO_IDENTD:
  216.         throw new SocketException("SOCKS server cannot conect to identd");
  217.  
  218.       case REQUEST_REJECTED_DIFF_IDENTS:
  219.         throw new SocketException("User name does not match identd name");
  220.     }
  221.     }
  222.     
  223.  
  224.     /**
  225.      * Read the response from the socks server.  Return the result code.
  226.      */
  227.     private int getSOCKSReply() throws IOException {
  228.     InputStream in = getInputStream();
  229.  
  230.     // REMIND: this could deal with reading < 8 bytes and buffering
  231.     // them up.
  232.  
  233.     byte response[] = new byte[8];
  234.  
  235.     int code;
  236.     if ((code = in.read(response)) != response.length) {
  237.         throw new SocketException("Malformed reply from SOCKS server");
  238.     }
  239.  
  240.     if (response[0] != 0) { // should be version 0
  241.         throw new SocketException("Malformed reply from SOCKS server");
  242.     }
  243.  
  244.     return response[1];    // the response code
  245.     }
  246.  
  247.     /**
  248.      * Just set up a connection to the SOCKS server and return.  The caller
  249.      * needs to handle the SOCKS initiation protocol with the server after
  250.      * the connection is established.
  251.      */
  252.     private void connectToSocksServer() throws IOException {
  253.  
  254.     String socksServerString = System.getProperty(socksServerProp);
  255.     if (socksServerString == null) {
  256.         // REMIND: this is too trusting of its (internal) callers --
  257.         // needs to robustly assert that SOCKS are in fact being used,
  258.         // and signal an error (in some manner) if SOCKS are not being
  259.         // used.
  260.         return;
  261.     }
  262.  
  263.     InetAddress socksServer = InetAddress.getByName(socksServerString);
  264.  
  265.     String socksPortString = System.getProperty(socksPortProp,
  266.                             socksDefaultPortStr);
  267.  
  268.     int socksServerPort;
  269.     try {
  270.         socksServerPort = Integer.parseInt(socksPortString);
  271.     } catch (Exception e) {
  272.         throw new SocketException("Bad port number format");
  273.     }
  274.     
  275.     doConnect(socksServer, socksServerPort);
  276.     }
  277.  
  278.  
  279.     /**
  280.      * The workhorse of the connection operation.  Tries several times to
  281.      * establish a connection to the given <host, port>.  If unsuccessful,
  282.      * throws an IOException indicating what went wrong.
  283.      */
  284.  
  285.     private void doConnect(InetAddress address, int port) throws IOException {
  286.     IOException pending = null;
  287.  
  288.     for (int i = 0 ; i < 3 ; i++) {
  289.         try {
  290.         socketConnect(address, port);
  291.         return;
  292.         } catch (ProtocolException e) {
  293.         // Try again in case of a protocol exception
  294.         close();
  295.         fd = new FileDescriptor();
  296.         socketCreate(true);
  297.         pending = e;
  298.         } catch (IOException e) {
  299.         // Let someone else deal with this exception
  300.         close();
  301.         throw e;
  302.         }
  303.     }
  304.  
  305.     // failed to connect -- tell our client the bad news
  306.     close();
  307.     throw pending;
  308.     }
  309.  
  310.  
  311.     /**
  312.      * Just creates and sends out to the connected socket a SOCKS command
  313.      * packet.
  314.      */
  315.     private void sendSOCKSCommandPacket(int command, InetAddress address,
  316.                     int port) throws IOException {
  317.     
  318.         byte commandPacket[] = makeCommandPacket(command, address, port);
  319.     OutputStream out = getOutputStream();
  320.  
  321.     out.write(commandPacket);
  322.     }
  323.  
  324.     /**
  325.      * Create and return a SOCKS V4 command packet.
  326.      */
  327.     private byte[] makeCommandPacket(int command, InetAddress address,
  328.                     int port) { 
  329.  
  330.     // base packet size = 8, + 1 null byte 
  331.     ByteArrayOutputStream byteStream = new ByteArrayOutputStream(8 + 1);
  332.  
  333.     byteStream.write(SOCKS_PROTO_VERS);
  334.     byteStream.write(command);
  335.  
  336.  
  337.     byteStream.write((port >> 8) & 0xff);
  338.     byteStream.write((port >> 0) & 0xff);
  339.  
  340.     byte addressBytes[] = address.getAddress();
  341.     byteStream.write(addressBytes, 0, addressBytes.length);
  342.  
  343.     String userName = System.getProperty("user.name");
  344.     byte userNameBytes[] = new byte[userName.length()];
  345.     userName.getBytes(0, userName.length(), userNameBytes, 0);
  346.  
  347.     byteStream.write(userNameBytes, 0, userNameBytes.length);
  348.     byteStream.write(0);    // null termination for user name
  349.  
  350.     return byteStream.toByteArray();
  351.     }
  352.  
  353.     /**
  354.      * Returns true if implementation should use the SOCKS protocol
  355.      * (i.e. the user has set the required properties to enable SOCKS to
  356.      * be used).
  357.      */
  358.     private boolean usingSocks() {
  359.     return (System.getProperty(socksServerProp) != null);
  360.     }
  361.     
  362.  
  363.     /**
  364.      * Binds the socket to the specified address of the specified local port.
  365.      * @param address the address
  366.      * @param port the port
  367.      */
  368.     protected synchronized void bind(InetAddress address, int lport) 
  369.     throws IOException
  370.     {
  371.     socketBind(address, lport);
  372.     }
  373.  
  374.     /**
  375.      * Listens, for a specified amount of time, for connections.
  376.      * @param count the amount of time to listen for connections
  377.      */
  378.     protected synchronized void listen(int count) throws IOException {
  379.     socketListen(count);
  380.     }
  381.  
  382.     /**
  383.      * Accepts connections.
  384.      * @param s the connection
  385.      */
  386.     protected synchronized void accept(SocketImpl s) throws IOException {
  387.     socketAccept(s);
  388.     }
  389.  
  390.     /**
  391.      * Gets an InputStream for this socket.
  392.      */
  393.     protected synchronized InputStream getInputStream() throws IOException {
  394.     return new SocketInputStream(this);
  395.     }
  396.  
  397.     /**
  398.      * Gets an OutputStream for this socket.
  399.      */
  400.     protected synchronized OutputStream getOutputStream() throws IOException {
  401.     return new SocketOutputStream(this);
  402.     }
  403.  
  404.     /**
  405.      * Returns the number of bytes that can be read without blocking.
  406.      */
  407.     protected synchronized int available() throws IOException {
  408.     return socketAvailable();
  409.     }
  410.  
  411.     /**
  412.      * Closes the socket.
  413.      */
  414.     protected void close() throws IOException {
  415.     if (fd != null) {
  416.         socketClose();
  417.         fd = null;
  418.     }
  419.     }
  420.  
  421.     /**
  422.      * Cleans up if the user forgets to close it.
  423.      */
  424.     protected void finalize() throws IOException {
  425.     close();
  426.     }
  427.  
  428.     private native void socketCreate(boolean isServer) throws IOException;
  429.     private native void socketConnect(InetAddress address, int port)
  430.     throws IOException;
  431.     private native void socketBind(InetAddress address, int port)
  432.     throws IOException;
  433.     private native void socketListen(int count)
  434.     throws IOException;
  435.     private native void socketAccept(SocketImpl s)
  436.     throws IOException;
  437.     private native int socketAvailable()
  438.     throws IOException;
  439.     private native void socketClose()
  440.     throws IOException;
  441.     private static native void initProto();
  442.     private native void socketSetOption(int cmd, boolean on, Object value) 
  443.     throws SocketException;
  444.     private native int socketGetOption(int opt) throws SocketException;
  445. }
  446.