diff -r a1fa6863fc50 -r 07047237e4d4 jdk/src/solaris/classes/sun/net/spi/SdpProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/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 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 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 loadRulesFromFile(String file) + throws IOException + { + Scanner scanner = new Scanner(new File(file)); + try { + List result = new ArrayList(); + 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; +}