jdk/src/solaris/classes/sun/net/spi/SdpProvider.java
changeset 6533 9c96150b74d0
parent 6326 047748ce0a45
parent 6532 46e43203603e
child 6534 ad71f5af4022
equal deleted inserted replaced
6326:047748ce0a45 6533:9c96150b74d0
     1 /*
       
     2  * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package sun.net.spi;
       
    27 
       
    28 import sun.net.NetHooks;
       
    29 import java.net.InetAddress;
       
    30 import java.net.Inet4Address;
       
    31 import java.net.UnknownHostException;
       
    32 import java.util.*;
       
    33 import java.io.File;
       
    34 import java.io.FileDescriptor;
       
    35 import java.io.IOException;
       
    36 import java.io.PrintStream;
       
    37 
       
    38 import sun.misc.SharedSecrets;
       
    39 import sun.misc.JavaIOFileDescriptorAccess;
       
    40 
       
    41 /**
       
    42  * A NetHooks provider that converts sockets from the TCP to SDP protocol prior
       
    43  * to binding or connecting.
       
    44  */
       
    45 
       
    46 public class SdpProvider extends NetHooks.Provider {
       
    47     private static final JavaIOFileDescriptorAccess fdAccess =
       
    48         SharedSecrets.getJavaIOFileDescriptorAccess();
       
    49 
       
    50     // maximum port
       
    51     private static final int MAX_PORT = 65535;
       
    52 
       
    53     // indicates if SDP is enabled and the rules for when the protocol is used
       
    54     private final boolean enabled;
       
    55     private final List<Rule> rules;
       
    56 
       
    57     // logging for debug purposes
       
    58     private PrintStream log;
       
    59 
       
    60     public SdpProvider() {
       
    61         // if this property is not defined then there is nothing to do.
       
    62         String file = System.getProperty("com.sun.sdp.conf");
       
    63         if (file == null) {
       
    64             this.enabled = false;
       
    65             this.rules = null;
       
    66             return;
       
    67         }
       
    68 
       
    69         // load configuration file
       
    70         List<Rule> list = null;
       
    71         if (file != null) {
       
    72             try {
       
    73                 list = loadRulesFromFile(file);
       
    74             } catch (IOException e) {
       
    75                 fail("Error reading %s: %s", file, e.getMessage());
       
    76             }
       
    77         }
       
    78 
       
    79         // check if debugging is enabled
       
    80         PrintStream out = null;
       
    81         String logfile = System.getProperty("com.sun.sdp.debug");
       
    82         if (logfile != null) {
       
    83             out = System.out;
       
    84             if (logfile.length() > 0) {
       
    85                 try {
       
    86                     out = new PrintStream(logfile);
       
    87                 } catch (IOException ignore) { }
       
    88             }
       
    89         }
       
    90 
       
    91         this.enabled = !list.isEmpty();
       
    92         this.rules = list;
       
    93         this.log = out;
       
    94     }
       
    95 
       
    96     // supported actions
       
    97     private static enum Action {
       
    98         BIND,
       
    99         CONNECT;
       
   100     }
       
   101 
       
   102     // a rule for matching a bind or connect request
       
   103     private static interface Rule {
       
   104         boolean match(Action action, InetAddress address, int port);
       
   105     }
       
   106 
       
   107     // rule to match port[-end]
       
   108     private static class PortRangeRule implements Rule {
       
   109         private final Action action;
       
   110         private final int portStart;
       
   111         private final int portEnd;
       
   112         PortRangeRule(Action action, int portStart, int portEnd) {
       
   113             this.action = action;
       
   114             this.portStart = portStart;
       
   115             this.portEnd = portEnd;
       
   116         }
       
   117         Action action() {
       
   118             return action;
       
   119         }
       
   120         @Override
       
   121         public boolean match(Action action, InetAddress address, int port) {
       
   122             return (action == this.action &&
       
   123                     port >= this.portStart &&
       
   124                     port <= this.portEnd);
       
   125         }
       
   126     }
       
   127 
       
   128     // rule to match address[/prefix] port[-end]
       
   129     private static class AddressPortRangeRule extends PortRangeRule {
       
   130         private final byte[] addressAsBytes;
       
   131         private final int prefixByteCount;
       
   132         private final byte mask;
       
   133         AddressPortRangeRule(Action action, InetAddress address,
       
   134                              int prefix, int port, int end)
       
   135         {
       
   136             super(action, port, end);
       
   137             this.addressAsBytes = address.getAddress();
       
   138             this.prefixByteCount = prefix >> 3;
       
   139             this.mask = (byte)(0xff << (8 - (prefix % 8)));
       
   140         }
       
   141         @Override
       
   142         public boolean match(Action action, InetAddress address, int port) {
       
   143             if (action != action())
       
   144                 return false;
       
   145             byte[] candidate = address.getAddress();
       
   146             // same address type?
       
   147             if (candidate.length != addressAsBytes.length)
       
   148                 return false;
       
   149             // check bytes
       
   150             for (int i=0; i<prefixByteCount; i++) {
       
   151                 if (candidate[i] != addressAsBytes[i])
       
   152                     return false;
       
   153             }
       
   154             // check remaining bits
       
   155             if ((prefixByteCount < addressAsBytes.length) &&
       
   156                 ((candidate[prefixByteCount] & mask) !=
       
   157                  (addressAsBytes[prefixByteCount] & mask)))
       
   158                     return false;
       
   159             return super.match(action, address, port);
       
   160         }
       
   161     }
       
   162 
       
   163     // parses port:[-end]
       
   164     private static int[] parsePortRange(String s) {
       
   165         int pos = s.indexOf('-');
       
   166         try {
       
   167             int[] result = new int[2];
       
   168             if (pos < 0) {
       
   169                 boolean all = s.equals("*");
       
   170                 result[0] = all ? 0 : Integer.parseInt(s);
       
   171                 result[1] = all ? MAX_PORT : result[0];
       
   172             } else {
       
   173                 String low = s.substring(0, pos);
       
   174                 if (low.length() == 0) low = "*";
       
   175                 String high = s.substring(pos+1);
       
   176                 if (high.length() == 0) high = "*";
       
   177                 result[0] = low.equals("*") ? 0 : Integer.parseInt(low);
       
   178                 result[1] = high.equals("*") ? MAX_PORT : Integer.parseInt(high);
       
   179             }
       
   180             return result;
       
   181         } catch (NumberFormatException e) {
       
   182             return new int[0];
       
   183         }
       
   184     }
       
   185 
       
   186     private static void fail(String msg, Object... args) {
       
   187         Formatter f = new Formatter();
       
   188         f.format(msg, args);
       
   189         throw new RuntimeException(f.out().toString());
       
   190     }
       
   191 
       
   192     // loads rules from the given file
       
   193     // Each non-blank/non-comment line must have the format:
       
   194     // ("bind" | "connect") 1*LWSP-char (hostname | ipaddress["/" prefix])
       
   195     //     1*LWSP-char ("*" | port) [ "-" ("*" | port) ]
       
   196     private static List<Rule> loadRulesFromFile(String file)
       
   197         throws IOException
       
   198     {
       
   199         Scanner scanner = new Scanner(new File(file));
       
   200         try {
       
   201             List<Rule> result = new ArrayList<Rule>();
       
   202             while (scanner.hasNextLine()) {
       
   203                 String line = scanner.nextLine().trim();
       
   204 
       
   205                 // skip blank lines and comments
       
   206                 if (line.length() == 0 || line.charAt(0) == '#')
       
   207                     continue;
       
   208 
       
   209                 // must have 3 fields
       
   210                 String[] s = line.split("\\s+");
       
   211                 if (s.length != 3) {
       
   212                     fail("Malformed line '%s'", line);
       
   213                     continue;
       
   214                 }
       
   215 
       
   216                 // first field is the action ("bind" or "connect")
       
   217                 Action action = null;
       
   218                 for (Action a: Action.values()) {
       
   219                     if (s[0].equalsIgnoreCase(a.name())) {
       
   220                         action = a;
       
   221                         break;
       
   222                     }
       
   223                 }
       
   224                 if (action == null) {
       
   225                     fail("Action '%s' not recognized", s[0]);
       
   226                     continue;
       
   227                 }
       
   228 
       
   229                 // * port[-end]
       
   230                 int[] ports = parsePortRange(s[2]);
       
   231                 if (ports.length == 0) {
       
   232                     fail("Malformed port range '%s'", s[2]);
       
   233                     continue;
       
   234                 }
       
   235 
       
   236                 // match all addresses
       
   237                 if (s[1].equals("*")) {
       
   238                     result.add(new PortRangeRule(action, ports[0], ports[1]));
       
   239                     continue;
       
   240                 }
       
   241 
       
   242                 // hostname | ipaddress[/prefix]
       
   243                 int pos = s[1].indexOf('/');
       
   244                 try {
       
   245                     if (pos < 0) {
       
   246                         // hostname or ipaddress (no prefix)
       
   247                         InetAddress[] addresses = InetAddress.getAllByName(s[1]);
       
   248                         for (InetAddress address: addresses) {
       
   249                             int prefix =
       
   250                                 (address instanceof Inet4Address) ? 32 : 128;
       
   251                             result.add(new AddressPortRangeRule(action, address,
       
   252                                 prefix, ports[0], ports[1]));
       
   253                         }
       
   254                     } else {
       
   255                         // ipaddress/prefix
       
   256                         InetAddress address = InetAddress
       
   257                             .getByName(s[1].substring(0, pos));
       
   258                         int prefix = -1;
       
   259                         try {
       
   260                             prefix = Integer.parseInt(s[1].substring(pos+1));
       
   261                             if (address instanceof Inet4Address) {
       
   262                                 // must be 1-31
       
   263                                 if (prefix < 0 || prefix > 32) prefix = -1;
       
   264                             } else {
       
   265                                 // must be 1-128
       
   266                                 if (prefix < 0 || prefix > 128) prefix = -1;
       
   267                             }
       
   268                         } catch (NumberFormatException e) {
       
   269                         }
       
   270 
       
   271                         if (prefix > 0) {
       
   272                             result.add(new AddressPortRangeRule(action,
       
   273                                         address, prefix, ports[0], ports[1]));
       
   274                         } else {
       
   275                             fail("Malformed prefix '%s'", s[1]);
       
   276                             continue;
       
   277                         }
       
   278                     }
       
   279                 } catch (UnknownHostException uhe) {
       
   280                     fail("Unknown host or malformed IP address '%s'", s[1]);
       
   281                     continue;
       
   282                 }
       
   283             }
       
   284             return result;
       
   285         } finally {
       
   286             scanner.close();
       
   287         }
       
   288     }
       
   289 
       
   290     // converts unbound TCP socket to a SDP socket if it matches the rules
       
   291     private void convertTcpToSdpIfMatch(FileDescriptor fdObj,
       
   292                                                Action action,
       
   293                                                InetAddress address,
       
   294                                                int port)
       
   295         throws IOException
       
   296     {
       
   297         boolean matched = false;
       
   298         for (Rule rule: rules) {
       
   299             if (rule.match(action, address, port)) {
       
   300                 int fd = fdAccess.get(fdObj);
       
   301                 convert(fd);
       
   302                 matched = true;
       
   303                 break;
       
   304             }
       
   305         }
       
   306         if (log != null) {
       
   307             String addr = (address instanceof Inet4Address) ?
       
   308                 address.getHostAddress() : "[" + address.getHostAddress() + "]";
       
   309             if (matched) {
       
   310                 log.format("%s to %s:%d (socket converted to SDP protocol)\n", action, addr, port);
       
   311             } else {
       
   312                 log.format("%s to %s:%d (no match)\n", action, addr, port);
       
   313             }
       
   314         }
       
   315     }
       
   316 
       
   317     @Override
       
   318     public void implBeforeTcpBind(FileDescriptor fdObj,
       
   319                               InetAddress address,
       
   320                               int port)
       
   321         throws IOException
       
   322     {
       
   323         if (enabled)
       
   324             convertTcpToSdpIfMatch(fdObj, Action.BIND, address, port);
       
   325     }
       
   326 
       
   327     @Override
       
   328     public void implBeforeTcpConnect(FileDescriptor fdObj,
       
   329                                 InetAddress address,
       
   330                                 int port)
       
   331         throws IOException
       
   332     {
       
   333         if (enabled)
       
   334             convertTcpToSdpIfMatch(fdObj, Action.CONNECT, address, port);
       
   335     }
       
   336 
       
   337     // -- native methods --
       
   338     private static native void convert(int fd) throws IOException;
       
   339 }