home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1996 May / PCW596.iso / wtest / clico / sunsoft / java / solaris.lzh / SOLARIS.TAZ / SOLARIS / hotjava / classsrc / net / Socket.java < prev    next >
Encoding:
Java Source  |  1995-05-15  |  13.8 KB  |  481 lines

  1. /*
  2.  * @(#)Socket.java    1.16 95/05/15 Jonathan Payne, Chuck McManis
  3.  *
  4.  * Copyright (c) 1994,1995 Sun Microsystems, Inc. All Rights Reserved.
  5.  *
  6.  * Permission to use, copy, modify, and distribute this software
  7.  * and its documentation for NON-COMMERCIAL purposes and without
  8.  * fee is hereby granted provided that this copyright notice
  9.  * appears in all copies. Please refer to the file "copyright.html"
  10.  * for further important copyright and licensing information.
  11.  *
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
  13.  * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  14.  * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  15.  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
  16.  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  17.  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  18.  */
  19.  
  20. package net;
  21.  
  22. import java.io.*;
  23. import java.util.Linker;
  24.  
  25. /**
  26.  * This is the basic socket class. It is currently 'SOCKSified'
  27.  * so if you have SOCKS_HOST and SOCKS_PORT set, and the address
  28.  * cannot be connected to locally, then we try going through sockd.
  29.  *
  30.  * @author    Jonathan Payne
  31.  * @author    Chuck McManis
  32.  * @version    1.16, 15 May 1995
  33.  */
  34. public final class Socket {
  35.     private int        s;        /* UNIX socket FD */
  36.  
  37.     /** address this socket is bound to */
  38.     public InetAddress    address;
  39.  
  40.     /** Port this socket is bound to in network byte order. */
  41.     public int         port = -1;
  42.  
  43.     /** file for doing input on this socket */
  44.     public FileInputStream        inputStream;
  45.  
  46.     /** file for doing output to other end */
  47.     public FileOutputStream        outputStream;
  48.  
  49.     /** set to true if the connection has gone through socks. */
  50.     private boolean isSOCKSSocket = false;
  51.  
  52.     /**
  53.      * SOCKS Variables. These variables are set when the user is
  54.      * using SOCKS to go through the firewall. 
  55.      *
  56.      * The first SOCKSUser should be the current username.
  57.      */
  58.     private static String SOCKSUser;
  59.  
  60.     /** This is the host running sockd, SOCKS_HOST environment var */
  61.     private static InetAddress SOCKSHost;
  62.  
  63.     /** Port number for sockd, by default it is 1080 */
  64.     private static int SOCKSPort = 1080;
  65.  
  66.     /** 
  67.      * This is the SOCKS name server 
  68.      */
  69.     private static InetAddress SOCKSNameServer;
  70.  
  71.     /** This is the SOCKS domain name, SOCKS_DN environment variable */
  72.     private static String SOCKSDomainName;
  73.  
  74.  
  75.     /** A pair of booleans to control SOCKS */
  76.     private static boolean SOCKSInit;
  77.  
  78.     /** Link network.so into the runtime. */
  79.     static {
  80.     Linker.loadLibrary("net");
  81.     }
  82.  
  83.     /** Specify how many connections can attempt to connect to this
  84.         socket at once before getting an error. */
  85.     public native void listen(int count);
  86.  
  87.     /** Accept a connection to this socket from some place else,
  88.     and fill in the specified socket with the new socket
  89.     information.  This is a helper function for the accept()
  90.     method that returns a new Socket object. */
  91.     private native void accept(Socket dst);
  92.  
  93.     /** This does nothing except provide an entry point for other
  94.         related packages to cause the above static initializer to
  95.     run. */
  96.     public static void initialize() {}
  97.  
  98.     /** Create an internet socket.  If isStream is true, creates
  99.     a stream socket, otherwise a DGRAM style socket is created. */
  100.     private native void        create(boolean isStream);
  101.  
  102.     /** dup 'this' socket, this is for the fake accept that SOCKS does. */
  103.     private native int dup();
  104.  
  105.     /** Bind this socket to a local address. */
  106.     public native void    bindAnonymously(InetAddress addr);
  107.  
  108.     private native void    connectUnchecked(InetAddress dest, int port);
  109.  
  110.     /** Bind this socket to a known port at the specified address. */
  111.     public native void    bindToPort(InetAddress addr, int port);
  112.  
  113.     /** Connect this socket to the specified destination and
  114.     port.  Throws an exception if this fails. */
  115.     public void connect(InetAddress dest, int port) {
  116.     String    appURL = Firewall.verifyAccess(dest.hostName, port);
  117.  
  118.     if (appURL != null) {
  119.         String msg = "Applet at " + appURL +
  120.         " attempted illegal socket connection to " +
  121.         dest.hostName + ":" + port;
  122.  
  123.         Firewall.securityError(msg);
  124.         return;
  125.     }
  126.  
  127.     connectUnchecked(dest, port);
  128.     }
  129.  
  130.     /** Accept a connection on this socket, returning a new one.
  131.         The old socket is still around for listening for new
  132.     connections. */
  133.     public Socket accept() {
  134.     Socket    newSocket = new Socket();
  135.  
  136.     newSocket.address = new InetAddress();
  137.     accept(newSocket);
  138.     newSocket.makeStreams();
  139.  
  140.     return newSocket;
  141.     }
  142.  
  143.     public Socket SOCKSAccept() {
  144.     int    i;
  145.     if (! usingSOCKS()) {
  146.         throw new SOCKSException("Not using socks on this socket.");
  147.     }
  148.     byte dst[] = new byte[8];
  149.  
  150.     i = inputStream.read(dst);    // Get the remote connect request.
  151.     if (i != 8) {
  152.         throw new SOCKSException("remote server timed out.");
  153.     }
  154.  
  155.     if (dst[0] != 4) {
  156.         throw new SOCKSException("Wrong version returned from sockd.");
  157.     }
  158.     if (dst[1] != 90) {
  159.         exceptionThrower((int) dst[1]);
  160.     }
  161.  
  162.     Socket newSocket = new Socket();
  163.     newSocket.address = new InetAddress();
  164.     newSocket.address.address = ((dst[4] << 24) & 0xff000000) +
  165.                                 ((dst[5] << 16) & 0xff0000) +
  166.                                 ((dst[6] << 8) & 0xff00) +
  167.                                 ((dst[7] << 0) & 0xff);
  168.      newSocket.port = ((dst[2] << 8) & 0xff00) + (dst[3] & 0xff);
  169.     newSocket.s = dup();
  170.     newSocket.makeStreams();
  171.     return (newSocket);
  172.     }
  173.  
  174.     private void exceptionThrower(int msgnum) {
  175.     String msg;
  176.  
  177.     switch (msgnum) {
  178.         case 90: msg = new String("Call Succeeded.");
  179.         break;
  180.         case 91: msg = new String("Call Failed.");
  181.         break;
  182.         case 92: msg = new String("Failed to connect to identd on client.");
  183.         break;
  184.         case 93: msg = new String("Identd reported different user id.");
  185.         break;
  186.         default: msg = new String("SOCKS Error "+msgnum);
  187.         break;
  188.     }
  189.     throw new SOCKSException(msg);
  190.     }
  191.  
  192.     /**
  193.      * This method returns the state of the isSOCKSSocket variable.
  194.      * It has to be a method to prevent the variable from being overwritten
  195.      * my malicious programs.
  196.      */
  197.     public boolean usingSOCKS() {
  198.     return (isSOCKSSocket);
  199.     }
  200.  
  201.     /**
  202.      * Create a bound socket on the other side of a SOCKS firewall.
  203.      */
  204.     public void SOCKSBind(InetAddress addr, Socket remHost) {
  205.     byte dst[];
  206.     boolean connected;
  207.     int    ntries;
  208.  
  209.     if (! remHost.usingSOCKS()) {
  210.         throw new SOCKSException("SOCKS bind called for inside host."); 
  211.     }
  212.     doSOCKSInit();    // Initialize SOCKS variables if not already.
  213.     for (ntries = 0; ntries < 3; ntries++) {
  214.         try {
  215.         create(true);
  216.         makeStreams();
  217.         connectUnchecked(Socket.SOCKSHost, Socket.SOCKSPort);
  218.         break;
  219.         } catch (ProtocolException e) {
  220.         // Sometimes connect on Solaris will get a
  221.         // protocol exception. The only thing that seems
  222.         // to help in that case is to close down the
  223.         // socket and start again from scratch. 
  224.         close();
  225.         continue;
  226.         } catch (Exception e) {
  227.         // Let someone else handle any other exception
  228.         // after clsing the socket down. 
  229.         close();
  230.         throw e;
  231.         }
  232.     }
  233.     if (ntries == 3) {
  234.         throw new SOCKSException("Unable to connect to sockd.");
  235.     }
  236.  
  237.     dst = new byte[8 + SOCKSUser.length() + 1]; /* connect packet */
  238.     dst[0] = 4;    // SOCKS version
  239.     dst[1] = 2;    // SOCKS bind command
  240.     dst[2] = (byte) ((remHost.port >>> 8) & 0xff);
  241.     dst[3] = (byte) (remHost.port & 0xff);
  242.     dst[4] = (byte) ((remHost.address.address >>> 24)  & 0xff);
  243.     dst[5] = (byte) ((remHost.address.address >>> 16)  & 0xff);
  244.     dst[6] = (byte) ((remHost.address.address >>> 8)  & 0xff);
  245.     dst[7] = (byte) ((remHost.address.address >>> 0)  & 0xff);
  246.     SOCKSUser.getBytes(0, SOCKSUser.length(), dst, 8);
  247.     dst[dst.length - 1] = 0;
  248.     outputStream.write(dst);
  249.     int i = inputStream.read(dst, 0, 8);
  250.     if (i != 8) {
  251.         throw new SOCKSException("connection timed out.");
  252.     }
  253.     if (dst[1] != 90) {
  254.         exceptionThrower((int) dst[1]);
  255.     }
  256.     /* if result as INADDR_ANY */
  257.     if ((dst[4] | dst[5] | dst[6] | dst[7]) == 0) {
  258.         addr.address = SOCKSHost.address;
  259.     }
  260.      port = ((dst[2] << 8) & 0xff00) + (dst[3] & 0xff);
  261.     isSOCKSSocket = true;
  262.     }
  263.  
  264.     private void doSOCKSConnect(String host, int port) {
  265.     byte dst[];
  266.     InetAddress addr;
  267.     boolean debug;
  268.  
  269.     debug = System.getenv("SOCKS_DEBUG") != null;
  270.     if (debug)
  271.         System.out.println("Attempting to resolve host '"+host+"'");
  272.     if (SOCKSNameServer == null) {
  273.         addr = InetAddress.getByName(host);
  274.     } else {
  275.         addr = InetAddress.getByName(host, SOCKSNameServer);
  276.        }
  277.     if (debug)
  278.         System.out.println("address is '"+addr+"'");
  279.  
  280.     /* XXX this works on UNIX but may fail on Win/Mac */
  281.     if (SOCKSUser == null) {
  282.         SOCKSUser = System.getenv("USER"); 
  283.         if (SOCKSUser == null) {
  284.             SOCKSUser = new String("NOBODY");
  285.         }
  286.     }
  287.  
  288.     dst = new byte[8 + SOCKSUser.length() + 1]; /* connect packet */
  289.     dst[0] = 4;    // SOCKS version
  290.     dst[1] = 1;    // SOCKS connect command.
  291.     dst[2] = (byte) ((port >>> 8) & 0xff);
  292.     dst[3] = (byte) (port & 0xff);
  293.     dst[4] = (byte) ((addr.address >>> 24)  & 0xff);
  294.     dst[5] = (byte) ((addr.address >>> 16)  & 0xff);
  295.     dst[6] = (byte) ((addr.address >>> 8)  & 0xff);
  296.     dst[7] = (byte) ((addr.address >>> 0)  & 0xff);
  297.     SOCKSUser.getBytes(0, SOCKSUser.length(), dst, 8);
  298.     dst[dst.length - 1] = 0;
  299.     outputStream.write(dst);
  300.     int i = inputStream.read(dst, 0, 8);
  301.     if (i != 8) {
  302.         throw new SOCKSException("connection timed out.");
  303.     }
  304.     if (dst[1] != 90) {
  305.         exceptionThrower((int) dst[1]);
  306.     }
  307.     isSOCKSSocket = true;
  308.     }
  309.  
  310.     /** Close the connection.  Other end will get EOF on reads,
  311.         and SIGPIPE on writes. */
  312.     public synchronized void close() {
  313.     /*
  314.      * Note that we only have to close one of the streams here
  315.      * because they both share the same filesdescriptor.
  316.      * closing one has the effect of closing filedescriptor of
  317.      * the other.
  318.      */
  319.     inputStream.close();
  320.     s = -1;
  321.     }
  322.  
  323.     public String toString() {
  324.     return "Socket[fd=" + s + ", address=" + address + ", port=" + port + "]";
  325.     }
  326.  
  327.     /** Make the input and output streams for this socket. */
  328.     private void makeStreams() {
  329.     inputStream = new SocketInputStream(this, s);
  330.     outputStream = new SocketOutputStream(this, s);
  331.     }
  332.  
  333.     /** Create a socket object without a corresponding underlying
  334.     system socket object associated with it.  This is used for
  335.     the accept() call, which in UNIX, anyway, allocates a new
  336.     system-level socket upon return (which we then use to fill
  337.     in this socket with). */
  338.     private Socket() {
  339.     }
  340.  
  341.  
  342.     private static void doSOCKSInit() {
  343.     /*
  344.      * Initialize SOCKS variables if necessary.
  345.      */
  346.     if (! SOCKSInit) {
  347.         String t;
  348.  
  349.         SOCKSInit = true;
  350.         t = System.getenv("SOCKS_HOST");
  351.         if (t == null) {
  352.         return;
  353.         }
  354.         SOCKSHost = InetAddress.getByName(t);
  355.         t = System.getenv("SOCKS_PORT");
  356.         if (t != null) {
  357.         SOCKSPort = Integer.parseInt(t);
  358.         }
  359.         t = System.getenv("SOCKS_NS");
  360.         if (t != null)
  361.            SOCKSNameServer = InetAddress.getByName(t);
  362.         SOCKSDomainName = System.getenv("SOCKS_DN");
  363.     }
  364.     }
  365.  
  366.     /** Create a socket which is a TCP socket if isStream is true,
  367.     or is a UDP socket otherwise. */
  368.     public Socket(boolean isStream) {
  369.     create(isStream);
  370.     makeStreams();
  371.     doSOCKSInit();
  372.     }
  373.  
  374.     /** Create a TCP socket and connect it to the specified port on
  375.     the specified host. */
  376.     public Socket(String host, int port) {
  377.     int ntries = 3;   // number of tries before faulure.
  378.     boolean connected = false;
  379.     String    tmps;
  380.     Exception    pendingException = null;
  381.     String        appURL;
  382.  
  383.     /*
  384.      * Initialize SOCKS variables if necessary.
  385.      */
  386.     doSOCKSInit();
  387.  
  388.     if ((appURL = Firewall.verifyAccess(host, port)) != null) {
  389.         String msg = "Applet at " + appURL +
  390.         " attempted illegal socket connection to " +
  391.         host + ":" + port;
  392.  
  393.         Firewall.securityError(msg);
  394.     } else {
  395.         while (!connected && ntries-- > 0) {
  396.         try {
  397.             create(true);
  398.             makeStreams();
  399.             connectUnchecked(InetAddress.getByName(host), port);
  400.             connected = true;
  401.         } catch (ProtocolException e) {
  402.             // Sometimes connect on Solaris will get a
  403.             // protocol exception. The only thing that seems
  404.              // to help in that case is to close down the
  405.             // socket and start again from scratch. 
  406.             close();
  407.             pendingException = e; // save it for later
  408.             if (ntries == 0) {
  409.                 break;
  410.             }
  411.         } catch (UnknownHostException e) {
  412.             close();
  413.             pendingException = e; 
  414.             break; // Try socks if that is configured.
  415.         } catch (Exception e) {
  416.             // Let someone else handle any other exception
  417.             // after clsing the socket down. 
  418.             close();
  419.             throw e;
  420.         }
  421.         } // end while
  422.  
  423.         /* Were we successful? If so, return from here. */
  424.         if (connected)
  425.         return;
  426.  
  427.         // If not using SOCKS really quit.
  428.         if (SOCKSHost == null) {
  429.         throw pendingException;
  430.         }
  431.         /*
  432.          * Could not connect directly, and while statement above
  433.          * did a 'break' so we must be using SOCKS
  434.          * 
  435.          * XXX    Weaknesses    XXX
  436.          * - We aren't a "versatile" client, and don't support many
  437.          *   SOCKS server.
  438.          * - We probably should encapsulate the firewall behaviour to
  439.          *   support others such as NAT, etc.
  440.          *
  441.          * Step 1.  Attempt to establish a connection to the SOCKS server
  442.          */
  443.         while (! connected && ntries-- > 0) {
  444.         try {
  445.             create(true);
  446.             makeStreams();
  447.             connectUnchecked(Socket.SOCKSHost, Socket.SOCKSPort);
  448.             connected = true;
  449.         } catch (ProtocolException e) {
  450.             // Sometimes connect on Solaris will get a
  451.             // protocol exception. The only thing that seems
  452.             // to help in that case is to close down the
  453.             // socket and start again from scratch. 
  454.             close();
  455.             if (ntries == 0)
  456.                 throw e;    // give up
  457.         } catch (Exception e) {
  458.             // Let someone else handle any other exception
  459.             // after clsing the socket down. 
  460.             close();
  461.             throw e;
  462.         }
  463.         }
  464.  
  465.         /*
  466.          * Now we're connected to the SOCKS server, 
  467.          * Step 2. Tell it who we _really_ want.
  468.          * (throws an exception if it fails)
  469.          */
  470.         doSOCKSConnect(host, port);
  471.     }
  472.     }
  473.  
  474.     protected void finalize() {
  475.     try {
  476.         close();
  477.     } catch (Exception e) {
  478.     }
  479.     }
  480. }
  481.