Initial load
diff --git a/test/java/net/Socks/SocksServer.java b/test/java/net/Socks/SocksServer.java
new file mode 100644
index 0000000..8f6fad5
--- /dev/null
+++ b/test/java/net/Socks/SocksServer.java
@@ -0,0 +1,523 @@
+/*
+ * Copyright 2002 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+import java.net.*;
+import java.io.*;
+
+public class SocksServer extends Thread {
+    // Some useful SOCKS constant
+
+    static final int PROTO_VERS4                = 4;
+    static final int PROTO_VERS         = 5;
+    static final int DEFAULT_PORT               = 1080;
+
+    static final int NO_AUTH            = 0;
+    static final int GSSAPI             = 1;
+    static final int USER_PASSW         = 2;
+    static final int NO_METHODS         = -1;
+
+    static final int CONNECT            = 1;
+    static final int BIND                       = 2;
+    static final int UDP_ASSOC          = 3;
+
+    static final int IPV4                       = 1;
+    static final int DOMAIN_NAME                = 3;
+    static final int IPV6                       = 4;
+
+    static final int REQUEST_OK         = 0;
+    static final int GENERAL_FAILURE    = 1;
+    static final int NOT_ALLOWED                = 2;
+    static final int NET_UNREACHABLE    = 3;
+    static final int HOST_UNREACHABLE   = 4;
+    static final int CONN_REFUSED               = 5;
+    static final int TTL_EXPIRED                = 6;
+    static final int CMD_NOT_SUPPORTED  = 7;
+    static final int ADDR_TYPE_NOT_SUP  = 8;
+
+    private int port;
+    private ServerSocket server;
+    private boolean useV4 = false;
+    private java.util.Hashtable users = new java.util.Hashtable();
+    private boolean done = false;
+    // Inner class to handle protocol with client
+    // This is the bulk of the work (protocol handler)
+    class ClientHandler extends Thread {
+        private InputStream in;
+        private OutputStream out;
+        private Socket client;
+        private Socket dest;
+
+        // Simple tunneling class, moving bits from one stream to another
+
+        class Tunnel extends Thread {
+            private InputStream tin;
+            private OutputStream tout;
+
+            Tunnel(InputStream in, OutputStream out) {
+                tin = in;
+                tout = out;
+            }
+
+            public void run() {
+                int b;
+                while (true) {
+                    try {
+                        b = tin.read();
+                        if (b == -1) {
+                            tin.close();
+                            tout.close();
+                            return;
+                        }
+                        tout.write(b);
+                    } catch (IOException e) {
+                        // actually exit from the thread
+                        return;
+                    }
+                }
+            }
+        }
+
+        ClientHandler(Socket s) throws IOException {
+            client = s;
+            in = client.getInputStream();
+            out = client.getOutputStream();
+        }
+
+        private void readBuf(InputStream is, byte[] buf) throws IOException {
+            int l = buf.length;
+            int count = 0;
+            int i;
+            do {
+                i = is.read(buf, count, l - count);
+                if (i == -1)
+                    throw new IOException("unexpected EOF");
+                count += i;
+            } while (count < l);
+        }
+
+
+        private boolean userPassAuth() throws IOException {
+            int ver = in.read();
+            int ulen = in.read();
+            if (ulen <= 0)
+                throw new SocketException("SOCKS protocol error");
+            byte[] buf = new byte[ulen];
+            readBuf(in, buf);
+            String uname = new String(buf);
+            String password = null;
+            ulen = in.read();
+            if (ulen < 0)
+                throw new SocketException("SOCKS protocol error");
+            if (ulen > 0) {
+                buf = new byte[ulen];
+                readBuf(in, buf);
+                password = new String(buf);
+            }
+            // Check username/password validity here
+            System.err.println("User: '" + uname);
+            System.err.println("PSWD: '" + password);
+            if (users.containsKey(uname)) {
+                String p1 = (String) users.get(uname);
+                System.err.println("p1 = " + p1);
+                if (p1.equals(password)) {
+                    out.write(PROTO_VERS);
+                    out.write(REQUEST_OK);
+                    out.flush();
+                    return true;
+                }
+            }
+            out.write(PROTO_VERS);
+            out.write(NOT_ALLOWED);
+            out.flush();
+            return false;
+        }
+
+        private void purge() throws IOException {
+            boolean done = false;
+            int i = 0;
+            client.setSoTimeout(1000);
+            while(!done && i != -1) {
+                try {
+                    i = in.read();
+                } catch(IOException e) {
+                    done = true;
+                }
+            }
+        }
+
+
+        // Handle the SOCKS version 4 protocl
+
+        private void getRequestV4() throws IOException {
+            int ver = in.read();
+            int cmd = in.read();
+            if (ver == -1 || cmd == -1) {
+                // EOF
+                in.close();
+                out.close();
+                return;
+            }
+
+            if (ver != 0 && ver != 4) {
+                out.write(PROTO_VERS4);
+                out.write(91); // Bad Request
+                out.write(0);
+                out.write(0);
+                out.write(0);
+                out.write(0);
+                out.write(0);
+                out.write(0);
+                out.write(0);
+                out.flush();
+                purge();
+                out.close();
+                in.close();
+                return;
+            }
+
+            if (cmd == CONNECT) {
+                int port = ((in.read() & 0xff) << 8);
+                port += (in.read() & 0xff);
+                byte[] buf = new byte[4];
+                readBuf(in, buf);
+                InetAddress addr = InetAddress.getByAddress(buf);
+                // We don't use the username...
+                int c;
+                do {
+                    c = (in.read() & 0xff);
+                } while (c!=0);
+                boolean ok = true;
+                try {
+                    dest = new Socket(addr, port);
+                } catch (IOException e) {
+                    ok = false;
+                }
+                if (!ok) {
+                    out.write(PROTO_VERS4);
+                    out.write(91);
+                    out.write(0);
+                    out.write(0);
+                    out.write(buf);
+                    out.flush();
+                    purge();
+                    out.close();
+                    in.close();
+                    return;
+                }
+                out.write(PROTO_VERS4);
+                out.write(90); // Success
+                out.write((port >> 8) & 0xff);
+                out.write(port & 0xff);
+                out.write(buf);
+                out.flush();
+                InputStream in2 = dest.getInputStream();
+                OutputStream out2 = dest.getOutputStream();
+
+                Tunnel tunnel = new Tunnel(in2, out);
+                tunnel.start();
+
+                int b = 0;
+                do {
+                    try {
+                        b = in.read();
+                        if (b == -1) {
+                            in.close();
+                            out2.close();
+                            return;
+                        }
+                        out2.write(b);
+                    } catch (IOException ex) {
+                    }
+                } while (!client.isClosed());
+            }
+        }
+
+
+        // Negociate the authentication scheme with the client
+        private void negociate() throws IOException {
+            int ver = in.read();
+            int n = in.read();
+            byte[] buf = null;
+            if (n > 0) {
+                buf = new byte[n];
+                readBuf(in, buf);
+            }
+            int scheme = NO_AUTH;
+            for (int i = 0; i < n; i++)
+                if (buf[i] == USER_PASSW)
+                    scheme = USER_PASSW;
+            out.write(PROTO_VERS);
+            out.write(scheme);
+            out.flush();
+            if (scheme == USER_PASSW)
+                userPassAuth();
+        }
+
+        // Send error message then close the streams
+        private void sendError(int code) {
+            try {
+                out.write(PROTO_VERS);
+                out.write(code);
+                out.write(0);
+                out.write(IPV4);
+                for (int i=0; i<6; i++)
+                    out.write(0);
+                out.flush();
+                out.close();
+            } catch (IOException ex) {
+            }
+        }
+
+        // Actually connect the proxy to the destination then initiate tunneling
+
+        private void doConnect(InetSocketAddress addr) throws IOException {
+            dest = new Socket();
+            try {
+                dest.connect(addr, 10000);
+            } catch (SocketTimeoutException ex) {
+                sendError(HOST_UNREACHABLE);
+                return;
+            } catch (ConnectException cex) {
+                sendError(CONN_REFUSED);
+                return;
+            }
+            // Success
+            InetAddress iadd = addr.getAddress();
+            if (iadd instanceof Inet4Address) {
+                out.write(PROTO_VERS);
+                out.write(REQUEST_OK);
+                out.write(0);
+                out.write(IPV4);
+                out.write(iadd.getAddress());
+            } else if (iadd instanceof Inet6Address) {
+                out.write(PROTO_VERS);
+                out.write(REQUEST_OK);
+                out.write(0);
+                out.write(IPV6);
+                out.write(iadd.getAddress());
+            } else {
+                sendError(GENERAL_FAILURE);
+                return;
+            }
+            out.write((addr.getPort() >> 8) & 0xff);
+            out.write((addr.getPort() >> 0) & 0xff);
+            out.flush();
+
+            InputStream in2 = dest.getInputStream();
+            OutputStream out2 = dest.getOutputStream();
+
+            Tunnel tunnel = new Tunnel(in2, out);
+            tunnel.start();
+
+            int b = 0;
+            do {
+                // Note that the socket might be closed from another thread (the tunnel)
+                try {
+                    b = in.read();
+                    if (b == -1) {
+                        in.close();
+                        out2.close();
+                        return;
+                    }
+                    out2.write(b);
+                } catch(IOException ioe) {
+                }
+            } while (!client.isClosed());
+        }
+
+        private void doBind(InetSocketAddress addr) throws IOException {
+            ServerSocket svr = new ServerSocket();
+            svr.bind(null);
+            InetSocketAddress bad = (InetSocketAddress) svr.getLocalSocketAddress();
+            out.write(PROTO_VERS);
+            out.write(REQUEST_OK);
+            out.write(0);
+            out.write(IPV4);
+            out.write(bad.getAddress().getAddress());
+            out.write((bad.getPort() >> 8) & 0xff);
+            out.write((bad.getPort() & 0xff));
+            out.flush();
+            dest = svr.accept();
+            bad = (InetSocketAddress) dest.getRemoteSocketAddress();
+            out.write(PROTO_VERS);
+            out.write(REQUEST_OK);
+            out.write(0);
+            out.write(IPV4);
+            out.write(bad.getAddress().getAddress());
+            out.write((bad.getPort() >> 8) & 0xff);
+            out.write((bad.getPort() & 0xff));
+            out.flush();
+            InputStream in2 = dest.getInputStream();
+            OutputStream out2 = dest.getOutputStream();
+
+            Tunnel tunnel = new Tunnel(in2, out);
+            tunnel.start();
+
+            int b = 0;
+            do {
+                // Note that the socket might be close from another thread (the tunnel)
+                try {
+                    b = in.read();
+                    if (b == -1) {
+                        in.close();
+                        out2.close();
+                        return;
+                    }
+                    out2.write(b);
+                } catch(IOException ioe) {
+                }
+            } while (!client.isClosed());
+
+        }
+
+        // Handle the SOCKS v5 requests
+
+        private void getRequest() throws IOException {
+            int ver = in.read();
+            int cmd = in.read();
+            if (ver == -1 || cmd == -1) {
+                in.close();
+                out.close();
+                return;
+            }
+            int rsv = in.read();
+            int atyp = in.read();
+            String addr = null;
+            int port = 0;
+
+            switch(atyp) {
+            case IPV4:
+                {
+                byte[] buf = new byte[4];
+                readBuf(in, buf);
+                int i = 0;
+                StringBuffer sb = new StringBuffer();
+                for (i = 0; i < 4; i++) {
+                    sb.append(buf[i]&0xff);
+                    if (i < 3)
+                        sb.append('.');
+                }
+                addr = sb.toString();
+                }
+                break;
+            case DOMAIN_NAME:
+                {
+                int i = in.read();
+                byte[] buf = new byte[i];
+                readBuf(in, buf);
+                addr = new String(buf);
+                }
+                break;
+            case IPV6:
+                {
+                byte[] buf = new byte[16];
+                readBuf(in, buf);
+                int i = 0;
+                StringBuffer sb = new StringBuffer();
+                for (i = 0; i<16; i++) {
+                    sb.append(Integer.toHexString(buf[i]&0xff));
+                    if (i < 15)
+                        sb.append(':');
+                }
+                addr = sb.toString();
+                }
+                break;
+            }
+
+            port = ((in.read()&0xff) << 8);
+            port += (in.read()&0xff);
+
+            InetSocketAddress socAddr = new InetSocketAddress(addr, port);
+            switch(cmd) {
+            case CONNECT:
+                doConnect(socAddr);
+                break;
+            case BIND:
+                doBind(socAddr);
+                break;
+            case UDP_ASSOC:
+                // doUDP(socAddr);
+                break;
+            }
+        }
+
+        public void run() {
+            String line = null;
+            try {
+                if (useV4) {
+                    getRequestV4();
+                } else {
+                    negociate();
+                    getRequest();
+                }
+            } catch (IOException ex) {
+                try {
+                    sendError(GENERAL_FAILURE);
+                } catch (Exception e) {
+                }
+            } finally {
+                try {
+                    client.close();
+                } catch (IOException e2) {
+                }
+            }
+        }
+
+    }
+
+    public SocksServer(int port, boolean v4) throws IOException {
+        this(port);
+        this.useV4 = v4;
+    }
+
+    public SocksServer(int port) throws IOException {
+        this.port = port;
+        server = new ServerSocket();
+        server.bind(new InetSocketAddress(port));
+    }
+
+    public SocksServer() throws IOException {
+        this (DEFAULT_PORT);
+    }
+
+    public void addUser(String user, String passwd) {
+        users.put(user, passwd);
+    }
+
+    public synchronized void terminate() {
+        done = true;
+    }
+
+    public void run() {
+        ClientHandler cl = null;
+        while (!done) {
+            try {
+                Socket s = server.accept();
+                cl = new ClientHandler(s);
+                cl.start();
+            } catch (IOException ex) {
+                if (cl != null)
+                    cl.interrupt();
+            }
+        }
+    }
+}
diff --git a/test/java/net/Socks/SocksV4Test.java b/test/java/net/Socks/SocksV4Test.java
new file mode 100644
index 0000000..091d5d4
--- /dev/null
+++ b/test/java/net/Socks/SocksV4Test.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2002 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * @test
+ * @bug 4727547
+ * @summary SocksSocketImpl throws NullPointerException
+ * @build SocksServer
+ */
+
+import java.net.*;
+import java.io.*;
+
+public class SocksV4Test {
+    public static void main(String[] args) throws IOException {
+        // Create a SOCKS V4 proxy on port 8888
+        SocksServer srvr = new SocksServer(8888, true);
+        srvr.start();
+        System.setProperty("socksProxyHost", "localhost");
+        System.setProperty("socksProxyPort", "8888");
+        // Let's create an unresolved address
+        InetSocketAddress ad = new InetSocketAddress("doesnt.exist.name", 1234);
+        Socket s = new Socket();
+        try {
+            s.connect(ad,10000);
+        } catch (UnknownHostException ex) {
+            // OK, that's what we expected
+        } catch (NullPointerException npe) {
+            // Not OK, this used to be the bug
+            throw new RuntimeException("Got a NUllPointerException");
+        } finally {
+            srvr.terminate();
+            srvr.interrupt();
+        }
+    }
+}