home *** CD-ROM | disk | FTP | other *** search
/ Java 1.2 How-To / JavaHowTo.iso / 3rdParty / jbuilder / unsupported / JDK1.2beta3 / SOURCE / SRC.ZIP / java / net / PlainSocketImpl.java < prev    next >
Encoding:
Java Source  |  1998-03-20  |  12.6 KB  |  466 lines

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