Path: blob/master/src/java.base/unix/classes/sun/net/sdp/SdpProvider.java
41137 views
/*1* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.net.sdp;2627import sun.net.NetHooks;28import java.net.InetAddress;29import java.net.Inet4Address;30import java.net.UnknownHostException;31import java.util.*;32import java.io.File;33import java.io.FileDescriptor;34import java.io.IOException;35import java.io.PrintStream;3637import sun.net.sdp.SdpSupport;38import sun.security.action.GetPropertyAction;3940/**41* A NetHooks provider that converts sockets from the TCP to SDP protocol prior42* to binding or connecting.43*/4445public class SdpProvider extends NetHooks.Provider {46// maximum port47private static final int MAX_PORT = 65535;4849// indicates if SDP is enabled and the rules for when the protocol is used50private final boolean enabled;51private final List<Rule> rules;5253// logging for debug purposes54private PrintStream log;5556public SdpProvider() {57Properties props = GetPropertyAction.privilegedGetProperties();58// if this property is not defined then there is nothing to do.59String file = props.getProperty("com.sun.sdp.conf");60if (file == null) {61this.enabled = false;62this.rules = null;63return;64}6566// load configuration file67List<Rule> list = null;68try {69list = loadRulesFromFile(file);70} catch (IOException e) {71fail("Error reading %s: %s", file, e.getMessage());72}7374// check if debugging is enabled75PrintStream out = null;76String logfile = props.getProperty("com.sun.sdp.debug");77if (logfile != null) {78out = System.out;79if (!logfile.isEmpty()) {80try {81out = new PrintStream(logfile);82} catch (IOException ignore) { }83}84}8586this.enabled = !list.isEmpty();87this.rules = list;88this.log = out;89}9091// supported actions92private static enum Action {93BIND,94CONNECT;95}9697// a rule for matching a bind or connect request98private static interface Rule {99boolean match(Action action, InetAddress address, int port);100}101102// rule to match port[-end]103private static class PortRangeRule implements Rule {104private final Action action;105private final int portStart;106private final int portEnd;107PortRangeRule(Action action, int portStart, int portEnd) {108this.action = action;109this.portStart = portStart;110this.portEnd = portEnd;111}112Action action() {113return action;114}115@Override116public boolean match(Action action, InetAddress address, int port) {117return (action == this.action &&118port >= this.portStart &&119port <= this.portEnd);120}121}122123// rule to match address[/prefix] port[-end]124private static class AddressPortRangeRule extends PortRangeRule {125private final byte[] addressAsBytes;126private final int prefixByteCount;127private final byte mask;128AddressPortRangeRule(Action action, InetAddress address,129int prefix, int port, int end)130{131super(action, port, end);132this.addressAsBytes = address.getAddress();133this.prefixByteCount = prefix >> 3;134this.mask = (byte)(0xff << (8 - (prefix % 8)));135}136@Override137public boolean match(Action action, InetAddress address, int port) {138if (action != action())139return false;140byte[] candidate = address.getAddress();141// same address type?142if (candidate.length != addressAsBytes.length)143return false;144// check bytes145for (int i=0; i<prefixByteCount; i++) {146if (candidate[i] != addressAsBytes[i])147return false;148}149// check remaining bits150if ((prefixByteCount < addressAsBytes.length) &&151((candidate[prefixByteCount] & mask) !=152(addressAsBytes[prefixByteCount] & mask)))153return false;154return super.match(action, address, port);155}156}157158// parses port:[-end]159private static int[] parsePortRange(String s) {160int pos = s.indexOf('-');161try {162int[] result = new int[2];163if (pos < 0) {164boolean all = s.equals("*");165result[0] = all ? 0 : Integer.parseInt(s);166result[1] = all ? MAX_PORT : result[0];167} else {168String low = s.substring(0, pos);169if (low.isEmpty()) low = "*";170String high = s.substring(pos+1);171if (high.isEmpty()) high = "*";172result[0] = low.equals("*") ? 0 : Integer.parseInt(low);173result[1] = high.equals("*") ? MAX_PORT : Integer.parseInt(high);174}175return result;176} catch (NumberFormatException e) {177return new int[0];178}179}180181private static void fail(String msg, Object... args) {182Formatter f = new Formatter();183f.format(msg, args);184throw new RuntimeException(f.out().toString());185}186187// loads rules from the given file188// Each non-blank/non-comment line must have the format:189// ("bind" | "connect") 1*LWSP-char (hostname | ipaddress["/" prefix])190// 1*LWSP-char ("*" | port) [ "-" ("*" | port) ]191private static List<Rule> loadRulesFromFile(String file)192throws IOException193{194Scanner scanner = new Scanner(new File(file));195try {196List<Rule> result = new ArrayList<>();197while (scanner.hasNextLine()) {198String line = scanner.nextLine().trim();199200// skip blank lines and comments201if (line.isEmpty() || line.charAt(0) == '#')202continue;203204// must have 3 fields205String[] s = line.split("\\s+");206if (s.length != 3) {207fail("Malformed line '%s'", line);208continue;209}210211// first field is the action ("bind" or "connect")212Action action = null;213for (Action a: Action.values()) {214if (s[0].equalsIgnoreCase(a.name())) {215action = a;216break;217}218}219if (action == null) {220fail("Action '%s' not recognized", s[0]);221continue;222}223224// * port[-end]225int[] ports = parsePortRange(s[2]);226if (ports.length == 0) {227fail("Malformed port range '%s'", s[2]);228continue;229}230231// match all addresses232if (s[1].equals("*")) {233result.add(new PortRangeRule(action, ports[0], ports[1]));234continue;235}236237// hostname | ipaddress[/prefix]238int pos = s[1].indexOf('/');239try {240if (pos < 0) {241// hostname or ipaddress (no prefix)242InetAddress[] addresses = InetAddress.getAllByName(s[1]);243for (InetAddress address: addresses) {244int prefix =245(address instanceof Inet4Address) ? 32 : 128;246result.add(new AddressPortRangeRule(action, address,247prefix, ports[0], ports[1]));248}249} else {250// ipaddress/prefix251InetAddress address = InetAddress252.getByName(s[1].substring(0, pos));253int prefix = -1;254try {255prefix = Integer.parseInt(s[1], pos + 1,256s[1].length(), 10);257if (address instanceof Inet4Address) {258// must be 1-31259if (prefix < 0 || prefix > 32) prefix = -1;260} else {261// must be 1-128262if (prefix < 0 || prefix > 128) prefix = -1;263}264} catch (NumberFormatException e) {265}266267if (prefix > 0) {268result.add(new AddressPortRangeRule(action,269address, prefix, ports[0], ports[1]));270} else {271fail("Malformed prefix '%s'", s[1]);272continue;273}274}275} catch (UnknownHostException uhe) {276fail("Unknown host or malformed IP address '%s'", s[1]);277continue;278}279}280return result;281} finally {282scanner.close();283}284}285286// converts unbound TCP socket to a SDP socket if it matches the rules287private void convertTcpToSdpIfMatch(FileDescriptor fdObj,288Action action,289InetAddress address,290int port)291throws IOException292{293boolean matched = false;294for (Rule rule: rules) {295if (rule.match(action, address, port)) {296SdpSupport.convertSocket(fdObj);297matched = true;298break;299}300}301if (log != null) {302String addr = (address instanceof Inet4Address) ?303address.getHostAddress() : "[" + address.getHostAddress() + "]";304if (matched) {305log.format("%s to %s:%d (socket converted to SDP protocol)\n", action, addr, port);306} else {307log.format("%s to %s:%d (no match)\n", action, addr, port);308}309}310}311312@Override313public void implBeforeTcpBind(FileDescriptor fdObj,314InetAddress address,315int port)316throws IOException317{318if (enabled)319convertTcpToSdpIfMatch(fdObj, Action.BIND, address, port);320}321322@Override323public void implBeforeTcpConnect(FileDescriptor fdObj,324InetAddress address,325int port)326throws IOException327{328if (enabled)329convertTcpToSdpIfMatch(fdObj, Action.CONNECT, address, port);330}331}332333334