changeset 1040:93d1fbe001b8

4890703: Support SDP (sol) Reviewed-by: michaelm
author alanb
date Mon, 06 Apr 2009 08:59:33 +0100
parents e50a00095a53
children d89688532509
files make/java/net/FILES_c.gmk make/java/net/Makefile make/java/net/mapfile-vers make/sun/net/FILES_java.gmk src/share/classes/java/net/AbstractPlainSocketImpl.java src/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java src/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java src/share/classes/sun/nio/ch/SocketChannelImpl.java src/solaris/classes/sun/net/NetHooks.java src/solaris/classes/sun/net/spi/SdpProvider.java src/solaris/classes/sun/nio/ch/UnixAsynchronousSocketChannelImpl.java src/solaris/lib/sdp/sdp.conf.template src/solaris/native/sun/net/spi/SdpProvider.c src/solaris/native/sun/nio/ch/FileChannelImpl.c src/windows/classes/sun/net/NetHooks.java test/sun/net/sdp/ProbeIB.java test/sun/net/sdp/Sanity.java test/sun/net/sdp/sanity.sh
diffstat 19 files changed, 981 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/make/java/net/FILES_c.gmk	Fri Apr 03 22:10:36 2009 +0100
+++ b/make/java/net/FILES_c.gmk	Mon Apr 06 08:59:33 2009 +0100
@@ -39,6 +39,10 @@
 	ResolverConfigurationImpl.c \
 	DefaultProxySelector.c
 
+ifeq ($(PLATFORM), solaris)
+    FILES_c += SdpProvider.c
+endif
+
 ifeq ($(PLATFORM), linux)
     FILES_c += linux_close.c
 endif
--- a/make/java/net/Makefile	Fri Apr 03 22:10:36 2009 +0100
+++ b/make/java/net/Makefile	Mon Apr 06 08:59:33 2009 +0100
@@ -108,11 +108,24 @@
 #
 LOCALE_SET_DEFINITION = jre
 
-properties: $(LIBDIR) $(LIBDIR)/net.properties
+MISC_FILES = $(LIBDIR) $(LIBDIR)/net.properties
 
 $(LIBDIR)/net.properties: $(SHARE_SRC)/lib/net.properties
 	@$(RM) $@
 	$(CP) $< $@
 
-build: properties
+# 
+# SDP configuration template
+#
+ifeq ($(PLATFORM), solaris)
+SDP_PATH = sdp/sdp.conf.template
+SDP_CONF = $(LIBDIR)/$(SDP_PATH)
+$(SDP_CONF): $(PLATFORM_SRC)/lib/$(SDP_PATH)
+	@$(RM) $*
+	$(install-file)
 
+MISC_FILES += $(SDP_CONF)
+endif
+
+build: $(MISC_FILES)
+
--- a/make/java/net/mapfile-vers	Fri Apr 03 22:10:36 2009 +0100
+++ b/make/java/net/mapfile-vers	Mon Apr 06 08:59:33 2009 +0100
@@ -90,6 +90,7 @@
 		Java_sun_net_dns_ResolverConfigurationImpl_fallbackDomain0;
 		Java_sun_net_spi_DefaultProxySelector_init;
 		Java_sun_net_spi_DefaultProxySelector_getSystemProxy;
+		Java_sun_net_spi_SdpProvider_convert;
 		NET_AllocSockaddr;
 		NET_SockaddrToInetAddress;
                 NET_SockaddrEqualsInetAddress;
--- a/make/sun/net/FILES_java.gmk	Fri Apr 03 22:10:36 2009 +0100
+++ b/make/sun/net/FILES_java.gmk	Mon Apr 06 08:59:33 2009 +0100
@@ -39,6 +39,7 @@
 	sun/net/TransferProtocolClient.java \
 	sun/net/ConnectionResetException.java \
 	sun/net/NetProperties.java \
+	sun/net/NetHooks.java \
 	sun/net/util/IPAddressUtil.java \
 	sun/net/dns/ResolverConfiguration.java \
 	sun/net/dns/ResolverConfigurationImpl.java \
@@ -123,3 +124,7 @@
 ifeq ($(PLATFORM), windows)
     FILES_java += sun/net/www/protocol/http/NTLMAuthSequence.java 
 endif
+
+ifeq ($(PLATFORM), solaris)
+    FILES_java += sun/net/spi/SdpProvider.java
+endif
--- a/src/share/classes/java/net/AbstractPlainSocketImpl.java	Fri Apr 03 22:10:36 2009 +0100
+++ b/src/share/classes/java/net/AbstractPlainSocketImpl.java	Mon Apr 06 08:59:33 2009 +0100
@@ -33,6 +33,7 @@
 import java.io.ByteArrayOutputStream;
 
 import sun.net.ConnectionResetException;
+import sun.net.NetHooks;
 
 /**
  * Default Socket Implementation. This implementation does
@@ -304,6 +305,11 @@
      */
 
     synchronized void doConnect(InetAddress address, int port, int timeout) throws IOException {
+        synchronized (fdLock) {
+            if (!closePending && (socket == null || !socket.isBound())) {
+                NetHooks.beforeTcpConnect(fd, address, port);
+            }
+        }
         try {
             FileDescriptor fd = acquireFD();
             try {
@@ -339,6 +345,11 @@
     protected synchronized void bind(InetAddress address, int lport)
         throws IOException
     {
+       synchronized (fdLock) {
+            if (!closePending && (socket == null || !socket.isBound())) {
+                NetHooks.beforeTcpBind(fd, address, lport);
+            }
+        }
         socketBind(address, lport);
         if (socket != null)
             socket.setBound();
--- a/src/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java	Fri Apr 03 22:10:36 2009 +0100
+++ b/src/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java	Mon Apr 06 08:59:33 2009 +0100
@@ -37,6 +37,7 @@
 import java.util.Collections;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
+import sun.net.NetHooks;
 
 /**
  * Base implementation of AsynchronousServerSocketChannel.
@@ -131,6 +132,7 @@
             synchronized (stateLock) {
                 if (localAddress != null)
                     throw new AlreadyBoundException();
+                NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
                 Net.bind(fd, isa.getAddress(), isa.getPort());
                 Net.listen(fd, backlog < 1 ? 50 : backlog);
                 localAddress = Net.localAddress(fd);
--- a/src/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java	Fri Apr 03 22:10:36 2009 +0100
+++ b/src/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java	Mon Apr 06 08:59:33 2009 +0100
@@ -38,6 +38,7 @@
 import java.util.Collections;
 import java.util.concurrent.*;
 import java.util.concurrent.locks.*;
+import sun.net.NetHooks;
 
 /**
  * Base implementation of AsynchronousSocketChannel
@@ -387,6 +388,7 @@
                     throw new AlreadyBoundException();
                 InetSocketAddress isa = (local == null) ?
                     new InetSocketAddress(0) : Net.checkAddress(local);
+                NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
                 Net.bind(fd, isa.getAddress(), isa.getPort());
                 localAddress = Net.localAddress(fd);
             }
--- a/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java	Fri Apr 03 22:10:36 2009 +0100
+++ b/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java	Mon Apr 06 08:59:33 2009 +0100
@@ -31,6 +31,7 @@
 import java.nio.channels.*;
 import java.nio.channels.spi.*;
 import java.util.*;
+import sun.net.NetHooks;
 
 
 /**
@@ -191,6 +192,7 @@
             SecurityManager sm = System.getSecurityManager();
             if (sm != null)
                 sm.checkListen(isa.getPort());
+            NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
             Net.bind(fd, isa.getAddress(), isa.getPort());
             Net.listen(fd, backlog < 1 ? 50 : backlog);
             synchronized (stateLock) {
--- a/src/share/classes/sun/nio/ch/SocketChannelImpl.java	Fri Apr 03 22:10:36 2009 +0100
+++ b/src/share/classes/sun/nio/ch/SocketChannelImpl.java	Mon Apr 06 08:59:33 2009 +0100
@@ -32,6 +32,7 @@
 import java.nio.channels.*;
 import java.nio.channels.spi.*;
 import java.util.*;
+import sun.net.NetHooks;
 
 
 /**
@@ -526,6 +527,7 @@
                         throw new AlreadyBoundException();
                     InetSocketAddress isa = (local == null) ?
                         new InetSocketAddress(0) : Net.checkAddress(local);
+                    NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
                     Net.bind(fd, isa.getAddress(), isa.getPort());
                     localAddress = Net.localAddress(fd);
                 }
@@ -577,6 +579,12 @@
                                 if (!isOpen()) {
                                     return false;
                                 }
+                                // notify hook only if unbound
+                                if (localAddress == null) {
+                                    NetHooks.beforeTcpConnect(fd,
+                                                           isa.getAddress(),
+                                                           isa.getPort());
+                                }
                                 readerThread = NativeThread.current();
                             }
                             for (;;) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/classes/sun/net/NetHooks.java	Mon Apr 06 08:59:33 2009 +0100
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2009 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.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package sun.net;
+
+import java.net.InetAddress;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import sun.security.action.GetPropertyAction;
+
+/**
+ * Defines static methods to be invoked prior to binding or connecting TCP sockets.
+ */
+
+public final class NetHooks {
+
+    /**
+     * A provider with hooks to allow sockets be converted prior to binding or
+     * connecting a TCP socket.
+     *
+     * <p> Concrete implementations of this class should define a zero-argument
+     * constructor and implement the abstract methods specified below.
+     */
+    public static abstract class Provider {
+        /**
+         * Initializes a new instance of this class.
+         */
+        protected Provider() {}
+
+        /**
+         * Invoked prior to binding a TCP socket.
+         */
+        public abstract void implBeforeTcpBind(FileDescriptor fdObj,
+                                               InetAddress address,
+                                               int port)
+            throws IOException;
+
+        /**
+         * Invoked prior to connecting an unbound TCP socket.
+         */
+        public abstract void implBeforeTcpConnect(FileDescriptor fdObj,
+                                                 InetAddress address,
+                                                 int port)
+            throws IOException;
+    }
+
+    /**
+     * For now, we load the SDP provider on Solaris. In the future this may
+     * be changed to use the ServiceLoader facility to allow the deployment of
+     * other providers.
+     */
+    private static Provider loadProvider(final String cn) {
+        return AccessController
+            .doPrivileged(new PrivilegedAction<Provider>() {
+                @Override public Provider run() {
+                    Class<Provider> c;
+                    try {
+                        c = (Class<Provider>)Class.forName(cn, true, null);
+                    } catch (ClassNotFoundException x) {
+                        throw new AssertionError(x);
+                    }
+                    try {
+                        return c.newInstance();
+                    } catch (IllegalAccessException x) {
+                        throw new AssertionError(x);
+                    } catch (InstantiationException x) {
+                        throw new AssertionError(x);
+                    }
+            }});
+    }
+    private static final Provider provider = AccessController
+        .doPrivileged(new GetPropertyAction("os.name")).equals("SunOS") ?
+            loadProvider("sun.net.spi.SdpProvider") : null;
+
+    /**
+     * Invoke prior to binding a TCP socket.
+     */
+    public static void beforeTcpBind(FileDescriptor fdObj,
+                                     InetAddress address,
+                                     int port)
+        throws IOException
+    {
+        if (provider != null)
+            provider.implBeforeTcpBind(fdObj, address, port);
+    }
+
+    /**
+     * Invoke prior to connecting an unbound TCP socket.
+     */
+    public static void beforeTcpConnect(FileDescriptor fdObj,
+                                        InetAddress address,
+                                        int port)
+        throws IOException
+    {
+        if (provider != null)
+            provider.implBeforeTcpConnect(fdObj, address, port);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/classes/sun/net/spi/SdpProvider.java	Mon Apr 06 08:59:33 2009 +0100
@@ -0,0 +1,339 @@
+/*
+ * Copyright 2009 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.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package sun.net.spi;
+
+import sun.net.NetHooks;
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.UnknownHostException;
+import java.util.*;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import sun.misc.SharedSecrets;
+import sun.misc.JavaIOFileDescriptorAccess;
+
+/**
+ * A NetHooks provider that converts sockets from the TCP to SDP protocol prior
+ * to binding or connecting.
+ */
+
+public class SdpProvider extends NetHooks.Provider {
+    private static final JavaIOFileDescriptorAccess fdAccess =
+        SharedSecrets.getJavaIOFileDescriptorAccess();
+
+    // maximum port
+    private static final int MAX_PORT = 65535;
+
+    // indicates if SDP is enabled and the rules for when the protocol is used
+    private final boolean enabled;
+    private final List<Rule> rules;
+
+    // logging for debug purposes
+    private PrintStream log;
+
+    public SdpProvider() {
+        // if this property is not defined then there is nothing to do.
+        String file = System.getProperty("com.sun.sdp.conf");
+        if (file == null) {
+            this.enabled = false;
+            this.rules = null;
+            return;
+        }
+
+        // load configuration file
+        List<Rule> list = null;
+        if (file != null) {
+            try {
+                list = loadRulesFromFile(file);
+            } catch (IOException e) {
+                fail("Error reading %s: %s", file, e.getMessage());
+            }
+        }
+
+        // check if debugging is enabled
+        PrintStream out = null;
+        String logfile = System.getProperty("com.sun.sdp.debug");
+        if (logfile != null) {
+            out = System.out;
+            if (logfile.length() > 0) {
+                try {
+                    out = new PrintStream(logfile);
+                } catch (IOException ignore) { }
+            }
+        }
+
+        this.enabled = !list.isEmpty();
+        this.rules = list;
+        this.log = out;
+    }
+
+    // supported actions
+    private static enum Action {
+        BIND,
+        CONNECT;
+    }
+
+    // a rule for matching a bind or connect request
+    private static interface Rule {
+        boolean match(Action action, InetAddress address, int port);
+    }
+
+    // rule to match port[-end]
+    private static class PortRangeRule implements Rule {
+        private final Action action;
+        private final int portStart;
+        private final int portEnd;
+        PortRangeRule(Action action, int portStart, int portEnd) {
+            this.action = action;
+            this.portStart = portStart;
+            this.portEnd = portEnd;
+        }
+        Action action() {
+            return action;
+        }
+        @Override
+        public boolean match(Action action, InetAddress address, int port) {
+            return (action == this.action &&
+                    port >= this.portStart &&
+                    port <= this.portEnd);
+        }
+    }
+
+    // rule to match address[/prefix] port[-end]
+    private static class AddressPortRangeRule extends PortRangeRule {
+        private final byte[] addressAsBytes;
+        private final int prefixByteCount;
+        private final byte mask;
+        AddressPortRangeRule(Action action, InetAddress address,
+                             int prefix, int port, int end)
+        {
+            super(action, port, end);
+            this.addressAsBytes = address.getAddress();
+            this.prefixByteCount = prefix >> 3;
+            this.mask = (byte)(0xff << (8 - (prefix % 8)));
+        }
+        @Override
+        public boolean match(Action action, InetAddress address, int port) {
+            if (action != action())
+                return false;
+            byte[] candidate = address.getAddress();
+            // same address type?
+            if (candidate.length != addressAsBytes.length)
+                return false;
+            // check bytes
+            for (int i=0; i<prefixByteCount; i++) {
+                if (candidate[i] != addressAsBytes[i])
+                    return false;
+            }
+            // check remaining bits
+            if ((prefixByteCount < addressAsBytes.length) &&
+                ((candidate[prefixByteCount] & mask) !=
+                 (addressAsBytes[prefixByteCount] & mask)))
+                    return false;
+            return super.match(action, address, port);
+        }
+    }
+
+    // parses port:[-end]
+    private static int[] parsePortRange(String s) {
+        int pos = s.indexOf('-');
+        try {
+            int[] result = new int[2];
+            if (pos < 0) {
+                boolean all = s.equals("*");
+                result[0] = all ? 0 : Integer.parseInt(s);
+                result[1] = all ? MAX_PORT : result[0];
+            } else {
+                String low = s.substring(0, pos);
+                if (low.length() == 0) low = "*";
+                String high = s.substring(pos+1);
+                if (high.length() == 0) high = "*";
+                result[0] = low.equals("*") ? 0 : Integer.parseInt(low);
+                result[1] = high.equals("*") ? MAX_PORT : Integer.parseInt(high);
+            }
+            return result;
+        } catch (NumberFormatException e) {
+            return new int[0];
+        }
+    }
+
+    private static void fail(String msg, Object... args) {
+        Formatter f = new Formatter();
+        f.format(msg, args);
+        throw new RuntimeException(f.out().toString());
+    }
+
+    // loads rules from the given file
+    // Each non-blank/non-comment line must have the format:
+    // ("bind" | "connect") 1*LWSP-char (hostname | ipaddress["/" prefix])
+    //     1*LWSP-char ("*" | port) [ "-" ("*" | port) ]
+    private static List<Rule> loadRulesFromFile(String file)
+        throws IOException
+    {
+        Scanner scanner = new Scanner(new File(file));
+        try {
+            List<Rule> result = new ArrayList<Rule>();
+            while (scanner.hasNextLine()) {
+                String line = scanner.nextLine().trim();
+
+                // skip blank lines and comments
+                if (line.length() == 0 || line.charAt(0) == '#')
+                    continue;
+
+                // must have 3 fields
+                String[] s = line.split("\\s+");
+                if (s.length != 3) {
+                    fail("Malformed line '%s'", line);
+                    continue;
+                }
+
+                // first field is the action ("bind" or "connect")
+                Action action = null;
+                for (Action a: Action.values()) {
+                    if (s[0].equalsIgnoreCase(a.name())) {
+                        action = a;
+                        break;
+                    }
+                }
+                if (action == null) {
+                    fail("Action '%s' not recognized", s[0]);
+                    continue;
+                }
+
+                // * port[-end]
+                int[] ports = parsePortRange(s[2]);
+                if (ports.length == 0) {
+                    fail("Malformed port range '%s'", s[2]);
+                    continue;
+                }
+
+                // match all addresses
+                if (s[1].equals("*")) {
+                    result.add(new PortRangeRule(action, ports[0], ports[1]));
+                    continue;
+                }
+
+                // hostname | ipaddress[/prefix]
+                int pos = s[1].indexOf('/');
+                try {
+                    if (pos < 0) {
+                        // hostname or ipaddress (no prefix)
+                        InetAddress[] addresses = InetAddress.getAllByName(s[1]);
+                        for (InetAddress address: addresses) {
+                            int prefix =
+                                (address instanceof Inet4Address) ? 32 : 128;
+                            result.add(new AddressPortRangeRule(action, address,
+                                prefix, ports[0], ports[1]));
+                        }
+                    } else {
+                        // ipaddress/prefix
+                        InetAddress address = InetAddress
+                            .getByName(s[1].substring(0, pos));
+                        int prefix = -1;
+                        try {
+                            prefix = Integer.parseInt(s[1].substring(pos+1));
+                            if (address instanceof Inet4Address) {
+                                // must be 1-31
+                                if (prefix < 0 || prefix > 32) prefix = -1;
+                            } else {
+                                // must be 1-128
+                                if (prefix < 0 || prefix > 128) prefix = -1;
+                            }
+                        } catch (NumberFormatException e) {
+                        }
+
+                        if (prefix > 0) {
+                            result.add(new AddressPortRangeRule(action,
+                                        address, prefix, ports[0], ports[1]));
+                        } else {
+                            fail("Malformed prefix '%s'", s[1]);
+                            continue;
+                        }
+                    }
+                } catch (UnknownHostException uhe) {
+                    fail("Unknown host or malformed IP address '%s'", s[1]);
+                    continue;
+                }
+            }
+            return result;
+        } finally {
+            scanner.close();
+        }
+    }
+
+    // converts unbound TCP socket to a SDP socket if it matches the rules
+    private void convertTcpToSdpIfMatch(FileDescriptor fdObj,
+                                               Action action,
+                                               InetAddress address,
+                                               int port)
+        throws IOException
+    {
+        boolean matched = false;
+        for (Rule rule: rules) {
+            if (rule.match(action, address, port)) {
+                int fd = fdAccess.get(fdObj);
+                convert(fd);
+                matched = true;
+                break;
+            }
+        }
+        if (log != null) {
+            String addr = (address instanceof Inet4Address) ?
+                address.getHostAddress() : "[" + address.getHostAddress() + "]";
+            if (matched) {
+                log.format("%s to %s:%d (socket converted to SDP protocol)\n", action, addr, port);
+            } else {
+                log.format("%s to %s:%d (no match)\n", action, addr, port);
+            }
+        }
+    }
+
+    @Override
+    public void implBeforeTcpBind(FileDescriptor fdObj,
+                              InetAddress address,
+                              int port)
+        throws IOException
+    {
+        if (enabled)
+            convertTcpToSdpIfMatch(fdObj, Action.BIND, address, port);
+    }
+
+    @Override
+    public void implBeforeTcpConnect(FileDescriptor fdObj,
+                                InetAddress address,
+                                int port)
+        throws IOException
+    {
+        if (enabled)
+            convertTcpToSdpIfMatch(fdObj, Action.CONNECT, address, port);
+    }
+
+    // -- native methods --
+    private static native void convert(int fd) throws IOException;
+}
--- a/src/solaris/classes/sun/nio/ch/UnixAsynchronousSocketChannelImpl.java	Fri Apr 03 22:10:36 2009 +0100
+++ b/src/solaris/classes/sun/nio/ch/UnixAsynchronousSocketChannelImpl.java	Mon Apr 06 08:59:33 2009 +0100
@@ -32,6 +32,7 @@
 import java.io.IOException;
 import java.io.FileDescriptor;
 import java.security.AccessController;
+import sun.net.NetHooks;
 import sun.security.action.GetPropertyAction;
 
 /**
@@ -305,6 +306,7 @@
             sm.checkConnect(isa.getAddress().getHostAddress(), isa.getPort());
 
         // check and set state
+        boolean notifyBeforeTcpConnect;
         synchronized (stateLock) {
             if (state == ST_CONNECTED)
                 throw new AlreadyConnectedException();
@@ -312,12 +314,16 @@
                 throw new ConnectionPendingException();
             state = ST_PENDING;
             pendingRemote = remote;
+            notifyBeforeTcpConnect = (localAddress == null);
         }
 
         AbstractFuture<Void,A> result = null;
         Throwable e = null;
         try {
             begin();
+            // notify hook if unbound
+            if (notifyBeforeTcpConnect)
+                NetHooks.beforeTcpConnect(fd, isa.getAddress(), isa.getPort());
             int n = Net.connect(fd, isa.getAddress(), isa.getPort());
             if (n == IOStatus.UNAVAILABLE) {
                 // connection could not be established immediately
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/lib/sdp/sdp.conf.template	Mon Apr 06 08:59:33 2009 +0100
@@ -0,0 +1,30 @@
+#
+# Configuration file to enable InfiniBand Sockets Direct Protocol.
+#
+# Each line that does not start with a comment (#) is a rule to indicate when
+# the SDP transport protocol should be used. The format of a rule is as follows:
+#   ("bind"|"connect") 1*LWSP-char (hostname|ipaddress["/"prefix]) 1*LWSP-char ("*"|port)["-"("*"|port)]
+#
+# A "bind" rule indicates that the SDP protocol transport should be used when
+# a TCP socket binds to an address/port that matches the rule. A "connect" rule
+# indicates that the SDP protocol transport should be used when an unbound
+# TCP socket attempts to connect to an address/port that matches the rule.
+# Addresses may be specified as hostnames or literal Internet Protocol (IP)
+# addresses. When a literal IP address is used then a prefix length may be used
+# to indicate the number of bits for matching (useful when a block of addresses
+# or subnet is allocated to the InfiniBand fabric). 
+
+# Use SDP for all sockets that bind to specific local addresses
+#bind    192.168.1.1    *
+#bind    fe80::21b:24ff:fe3d:7896    *
+
+# Use SDP for all sockets that bind to the wildcard address in a port range
+#bind    0.0.0.0    5000-5999
+#bind    ::0        5000-5999
+
+# Use SDP when connecting to all application services on 192.168.1.*
+#connect 192.168.1.0/24       1024-*
+
+# Use SDP when connecting to the http server or MySQL database on hpccluster.
+#connect hpccluster.foo.com   80
+#connect hpccluster.foo.com   3306
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/native/sun/net/spi/SdpProvider.c	Mon Apr 06 08:59:33 2009 +0100
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2009 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.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#if defined(__solaris__) && !defined(PROTO_SDP)
+#define PROTO_SDP       257
+#endif
+
+#include "jni.h"
+#include "jni_util.h"
+#include "net_util.h"
+
+#define RESTARTABLE(_cmd, _result) do { \
+  do { \
+    _result = _cmd; \
+  } while((_result == -1) && (errno == EINTR)); \
+} while(0)
+
+JNIEXPORT void JNICALL
+Java_sun_net_spi_SdpProvider_convert(JNIEnv *env, jclass cls, jint fd)
+{
+#ifdef PROTO_SDP
+    int domain = ipv6_available() ? AF_INET6 : AF_INET;
+    int s = socket(domain, SOCK_STREAM, PROTO_SDP);
+    if (s < 0) {
+        JNU_ThrowIOExceptionWithLastError(env, "socket");
+    } else {
+        int arg, len, res;
+        struct linger linger;
+
+        /* copy socket options that are relevant to SDP */
+        len = sizeof(arg);
+        if (getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&arg, &len) == 0)
+            setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&arg, len);
+        len = sizeof(arg);
+        if (getsockopt(fd, SOL_SOCKET, SO_OOBINLINE, (char*)&arg, &len) == 0)
+            setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char*)&arg, len);
+        len = sizeof(linger);
+        if (getsockopt(fd, SOL_SOCKET, SO_LINGER, (void*)&linger, &len) == 0)
+            setsockopt(s, SOL_SOCKET, SO_LINGER, (char*)&linger, len);
+
+        RESTARTABLE(dup2(s, fd), res);
+        if (res < 0)
+            JNU_ThrowIOExceptionWithLastError(env, "dup2");
+        RESTARTABLE(close(s), res);
+    }
+#else
+    JNU_ThrowInternalError(env, "should not reach here");
+#endif
+}
--- a/src/solaris/native/sun/nio/ch/FileChannelImpl.c	Fri Apr 03 22:10:36 2009 +0100
+++ b/src/solaris/native/sun/nio/ch/FileChannelImpl.c	Mon Apr 06 08:59:33 2009 +0100
@@ -231,6 +231,8 @@
         if (result < 0) {
             if (errno == EAGAIN)
                 return IOS_UNAVAILABLE;
+            if (errno == EOPNOTSUPP)
+                return IOS_UNSUPPORTED_CASE;
             if ((errno == EINVAL) && ((ssize_t)count >= 0))
                 return IOS_UNSUPPORTED_CASE;
             if (errno == EINTR)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/windows/classes/sun/net/NetHooks.java	Mon Apr 06 08:59:33 2009 +0100
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2009 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.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+package sun.net;
+
+import java.net.InetAddress;
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+/**
+ * Defines static methods to ensure that any installed net hooks are invoked
+ * prior to binding or connecting TCP sockets.
+ */
+
+public final class NetHooks {
+
+    /**
+     * Invoke prior to binding a TCP socket.
+     */
+    public static void beforeTcpBind(FileDescriptor fdObj,
+                                     InetAddress address,
+                                     int port)
+        throws IOException
+    {
+        // nothing to do
+    }
+
+    /**
+     * Invoke prior to connecting an unbound TCP socket.
+     */
+    public static void beforeTcpConnect(FileDescriptor fdObj,
+                                        InetAddress address,
+                                        int port)
+        throws IOException
+    {
+        // nothing to do
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/net/sdp/ProbeIB.java	Mon Apr 06 08:59:33 2009 +0100
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2009 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.io.File;
+import java.io.IOException;
+import java.net.NetworkInterface;
+import java.net.InetAddress;
+import java.util.Scanner;
+import java.util.Enumeration;
+
+/**
+ * Probes for InfiniBand devices plumbed with IP addresses.
+ */
+
+public class ProbeIB {
+    public static void main(String[] args) throws IOException {
+        Scanner s = new Scanner(new File("/etc/path_to_inst"));
+        try {
+            while (s.hasNextLine()) {
+                String line = s.nextLine();
+                if (line.startsWith("#"))
+                    continue;
+                String[] fields = line.split("\\s+");
+                if (!fields[2].equals("\"ibd\""))
+                    continue;
+                String name = fields[2].substring(1, fields[2].length()-1) + fields[1];
+                NetworkInterface ni = NetworkInterface.getByName(name);
+                if (ni != null) {
+                    Enumeration<InetAddress> addrs = ni.getInetAddresses();
+                    while (addrs.hasMoreElements()) {
+                        System.out.println(addrs.nextElement().getHostAddress());
+                    }
+                }
+            }
+        } finally {
+            s.close();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/net/sdp/Sanity.java	Mon Apr 06 08:59:33 2009 +0100
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2009 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.*;
+import java.nio.channels.*;
+import java.util.Enumeration;
+
+/**
+ * Sanity check Socket/ServerSocket and each of the stream-oriented channels
+ * on each IP address plumbed to the network adapters.
+ */
+
+public class Sanity {
+    public static void main(String[] args) throws Exception {
+        Enumeration<NetworkInterface> nifs = NetworkInterface.getNetworkInterfaces();
+        while (nifs.hasMoreElements()) {
+            NetworkInterface ni = nifs.nextElement();
+            Enumeration<InetAddress> addrs = ni.getInetAddresses();
+            while (addrs.hasMoreElements()) {
+                InetAddress addr = addrs.nextElement();
+                test(addr);
+            }
+        }
+    }
+
+    static void test(InetAddress addr) throws Exception {
+        System.out.println(addr.getHostAddress());
+
+        // ServerSocketChannel.bind
+        ServerSocketChannel ssc = ServerSocketChannel.open();
+        try {
+            ssc.bind(new InetSocketAddress(addr, 0));
+            int port = ((InetSocketAddress)(ssc.getLocalAddress())).getPort();
+
+            // SocketChannel.connect (implicit bind)
+            SocketChannel client = SocketChannel.open();
+            try {
+                client.connect(new InetSocketAddress(addr, port));
+                SocketChannel peer = ssc.accept();
+                try {
+                    testConnection(Channels.newOutputStream(client),
+                                   Channels.newInputStream(peer));
+                } finally {
+                    peer.close();
+                }
+            } finally {
+                client.close();
+            }
+
+            // SocketChannel.connect (explicit bind)
+            client = SocketChannel.open();
+            try {
+                client.bind(new InetSocketAddress(addr, 0))
+                  .connect(new InetSocketAddress(addr, port));
+                ssc.accept().close();
+            } finally {
+                client.close();
+            }
+        } finally {
+            ssc.close();
+        }
+
+        // AsynchronousServerSocketChannel.bind
+        AsynchronousServerSocketChannel server =
+            AsynchronousServerSocketChannel.open();
+        try {
+            server.bind(new InetSocketAddress(addr, 0));
+            int port = ((InetSocketAddress)(server.getLocalAddress())).getPort();
+
+            // AsynchronousSocketChannel.connect (implicit bind)
+            AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
+            try {
+                client.connect(new InetSocketAddress(addr, port)).get();
+                AsynchronousSocketChannel peer = server.accept().get();
+                try {
+                    testConnection(Channels.newOutputStream(client),
+                                   Channels.newInputStream(peer));
+                } finally {
+                    peer.close();
+                }
+            } finally {
+                client.close();
+            }
+
+            // AsynchronousSocketChannel.connect (explicit bind)
+            client = AsynchronousSocketChannel.open();
+            try {
+                client.bind(new InetSocketAddress(addr, 0))
+                  .connect(new InetSocketAddress(addr, port)).get();
+                server.accept().get().close();
+            } finally {
+                client.close();
+            }
+        } finally {
+            server.close();
+        }
+
+        // ServerSocket.bind
+        ServerSocket ss = new ServerSocket();
+        try {
+            ss.bind(new InetSocketAddress(addr, 0));
+            int port = ss.getLocalPort();
+
+            // Socket.connect (implicit bind)
+            Socket s = new Socket();
+            try {
+                s.connect(new InetSocketAddress(addr, port));
+                Socket peer = ss.accept();
+                try {
+                    testConnection(s.getOutputStream(), peer.getInputStream());
+                } finally {
+                    peer.close();
+                }
+            } finally {
+                s.close();
+            }
+
+            // Socket.connect (explicit bind)
+            s = new Socket();
+            try {
+                s.bind(new InetSocketAddress(addr, 0));
+                s.connect(new InetSocketAddress(addr, port));
+                ss.accept().close();
+            } finally {
+                s.close();
+            }
+        } finally {
+            ss.close();
+        }
+    }
+
+    static void testConnection(OutputStream out, InputStream in)
+        throws IOException
+    {
+        byte[] msg = "hello".getBytes();
+        out.write(msg);
+
+        byte[] ba = new byte[100];
+        int nread = 0;
+        while (nread < msg.length) {
+            int n = in.read(ba);
+            if (n < 0)
+                throw new IOException("EOF not expected!");
+            nread += n;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/net/sdp/sanity.sh	Mon Apr 06 08:59:33 2009 +0100
@@ -0,0 +1,72 @@
+#
+# Copyright 2009 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 4890703
+# @summary Unit test for Solaris SDP support
+# @build ProbeIB Sanity
+# @run shell sanity.sh
+
+# Check we are on Solaris and that SDP is enabled
+OS=`uname -s`
+if [ "$OS" != "SunOS" ]; then
+    echo "This is a Solaris-only test"
+    exit 0
+fi
+SDPADM=/usr/sbin/sdpadm
+if [ ! -f ${SDPADM} ]; then
+    echo "SDP not available"
+    exit 0
+fi
+${SDPADM} status|grep Enabled
+if [ $? != 0 ]; then 
+    echo "SDP not enabled"
+    exit 0
+fi
+
+if [ -z "$TESTJAVA" ]; then
+    JAVA=java
+    TESTCLASSES=.
+    TESTSRC=.
+else
+    JAVA="${TESTJAVA}/bin/java"
+fi
+
+CLASSPATH=${TESTCLASSES}:${TESTSRC}
+export CLASSPATH
+
+# Probe for IP addresses plumbed to IB interfaces
+$JAVA -Djava.net.preferIPv4Stack=true ProbeIB > ib_addrs
+
+# Create sdp.conf
+SDPCONF=sdp.conf
+rm ${SDPCONF}
+touch ${SDPCONF}
+cat ib_addrs | while read ADDR
+do
+   echo "bind ${ADDR} *" > ${SDPCONF}
+   echo "connect ${ADDR} *" >> ${SDPCONF}
+done
+
+# Sanity check
+$JAVA -Djava.net.preferIPv4Stack=true -Dcom.sun.sdp.conf=${SDPCONF} -Dcom.sun.sdp.debug Sanity