opends/src/server/org/opends/server/messages/ProtocolMessages.java
@@ -6531,7 +6531,7 @@ "Cannot decode the provided address mask because an prefix mask"+ "was specified with an wild card \"*\" match character"); registerMessage(MSGID_ADDRESSMASK_FORMAT_DECODE_ERROR, "Cannot decode the provided address mask because the it has an" + "Cannot decode the provided address mask because the it has an " + "invalid format"); opends/src/server/org/opends/server/types/AddressMask.java
@@ -30,19 +30,14 @@ import static org.opends.server.messages.ProtocolMessages.*; import static org.opends.server.messages.MessageHandler.*; import java.util.BitSet; //This import statements causes problems in checkstyles so //it has been turned off until issue #??? is fixed. //import sun.net.util.IPAddressUtil; //import java.net.Inet6Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; /** * This class defines an address mask, which can be used to perform * efficient comparisons against IP addresses to determine whether a * particular IP address is in a given range. * Currently IPV6 is not supported. Issue #670 will track IPV6 * support. */ public final class AddressMask @@ -52,7 +47,7 @@ * Types of rules we have. * * IPv4 - ipv4 rule * IPv6 - ipv6 rule (begin with '['). Not supported see issue #670 * IPv6 - ipv6 rule (begin with '[' or contains an ':'). * HOST - hostname match (foo.sun.com) * HOSTPATTERN - host pattern match (begin with '.') * ALLWILDCARD - *.*.*.* (first HOST is applied then ipv4) @@ -61,7 +56,7 @@ enum RuleType { IPv4, IPv6, HOSTPATTERN, ALLWILDCARD, HOST; IPv4, IPv6, HOSTPATTERN, ALLWILDCARD, HOST } // Type of rule determined @@ -75,8 +70,8 @@ private int IPV4MAXPREFIX = 32; // IPv6 values for number of bytes and max CIDR prefix //private int IN6ADDRSZ = 16; //private int IPV6MAXPREFIX = 128; private int IN6ADDRSZ = 16; private int IPV6MAXPREFIX = 128; //Holds binary representations of rule and mask respectively. private byte[] ruleMask, prefixMask; @@ -104,14 +99,14 @@ determineRuleType(rule); switch (ruleType) { case IPv6: processIPv6(rule); break; case IPv4: processIpv4(rule); break; /* case IPv6: processIPv6(rule); break; */ case HOST: processHost(rule); break; @@ -148,13 +143,10 @@ { ruleType=RuleType.HOSTPATTERN; } else if(ruleString.startsWith("[")) else if(ruleString.startsWith("[") || (ruleString.indexOf(':') != -1)) { int msgID=MSGID_ADDRESSMASK_FORMAT_DECODE_ERROR; String message = getMessage(msgID); throw new ConfigException(msgID, message); //IPV6 is not supported see issue #670 //ruleType=RuleType.IPv6; ruleType=RuleType.IPv6; } else { @@ -164,17 +156,14 @@ // hostname (can't begin with digit) or ipv4 address. //Default to IPv4 ruletype. ruleType=RuleType.HOST; for(int i=0;i<s.length;i++) { if(s[i].equals("*")) { for (String value : s) { if (value.equals("*")) { wildCount++; continue; } //Looks like an ipv4 address if(Character.isDigit(s[i].charAt(0))) { ruleType=RuleType.IPv4; if (Character.isDigit(value.charAt(0))) { ruleType = RuleType.IPv4; break; } } @@ -196,10 +185,10 @@ private void processIpv4(String rule) throws ConfigException { String[] s = rule.split("/", -1); ruleMask=new byte[IN4ADDRSZ]; prefixMask=new byte[IN4ADDRSZ]; this.ruleMask=new byte[IN4ADDRSZ]; this.prefixMask=new byte[IN4ADDRSZ]; prefixMask(processPrefix(s,IPV4MAXPREFIX)); processIpv4Subnet((s.length == 0) ? rule : s[0]); processIPv4Subnet((s.length == 0) ? rule : s[0]); } /** @@ -266,13 +255,13 @@ */ private void prefixMask(int prefix) { int i=0; int i; for( i=0;prefix > 8 ; i++) { prefixMask[i] = (byte) 0xff; this.prefixMask[i] = (byte) 0xff; prefix -= 8; } prefixMask[i] = (byte) ((0xff) << (8 - prefix)); this.prefixMask[i] = (byte) ((0xff) << (8 - prefix)); } /** @@ -282,7 +271,7 @@ * @throws ConfigException If the subnet string is not a valid * IPv4 subnet string. */ private void processIpv4Subnet(String subnet) private void processIPv4Subnet(String subnet) throws ConfigException { String[] s = subnet.split("\\.", -1); try { @@ -397,12 +386,11 @@ * <CODE>false</CODE> if it does not. */ public static boolean maskListContains(byte[] remoteAddr, String remoteName, AddressMask[] masks) String remoteName, AddressMask[] masks) { for(int i=0; i < masks.length; i++) { if(masks[i].match(remoteAddr, remoteName)) for (AddressMask mask : masks) { if(mask.match(remoteAddr, remoteName)) return true; } return false; @@ -431,6 +419,7 @@ boolean ret=false; switch(ruleType) { case IPv6: case IPv4: //this Address mask is an IPv4 rule ret=matchAddress(remoteAddr); @@ -441,15 +430,6 @@ ret=matchHostName(remoteName); break; /* case IPv6: //IPv6 rule only valid of addr is an Inet6Address if(addr instanceof Inet6Address) { Inet6Address addr6 = (Inet6Address) addr; ret=match(addr6.getAddress()); } break; */ case HOSTPATTERN: //HOSTPATTERN rule ret=matchPattern(remoteName); @@ -473,12 +453,8 @@ */ private boolean matchPattern(String remoteHostName) { int len=remoteHostName.length() - hostPattern.length(); if(len > 0) { return remoteHostName.regionMatches(true,len, hostPattern,0,hostPattern.length()); } return false; return len > 0 && remoteHostName.regionMatches(true, len, hostPattern, 0, hostPattern.length()); } /** @@ -532,48 +508,44 @@ return true; } /* Turned off until IPV6 issue #670 is fixed. private void processIPv6(String rule) throws ConfigException { String[] s = rule.split("/", -1); //ipv6 rule must end with ']'' if(!s[0].endsWith("]")) { int msgID=MSGID_ADDRESSMASK_FORMAT_DECODE_ERROR; String message = getMessage(msgID); throw new ConfigException(msgID,message); } String subnet = s[0].substring(1, s[0].length() -1); byte[] tmpMask=IPAddressUtil.textToNumericFormatV6(subnet); //don't have a vaild ipv6 address bail if (tmpMask == null) { int msgID=MSGID_ADDRESSMASK_FORMAT_DECODE_ERROR; String message = getMessage(msgID); throw new ConfigException(msgID, message);remoteMask } //we were returned an ipv4-mapped address ::ffff:<ipv4 addr> //make sure we don't have a prefix if((tmpMask.length == IN4ADDRSZ) && (s.length == 2)) { int msgID = MSGID_ADDRESSMASK_FORMAT_DECODE_ERROR; String message = getMessage(msgID); throw new ConfigException(msgID, message); //build a ipv4 structure using a 32-bit prefix } else if (tmpMask.length == IN4ADDRSZ) { setRuleType(Rtype.IPv4); setRuleMask(tmpMask); setPrefixMask(new byte[IN4ADDRSZ]); prefixMask(32); } else { //plain Ipv6 address setRuleMask(tmpMask); setPrefixMask(new byte[IN6ADDRSZ]); prefixMask(processPrefix(s,IPV6MAXPREFIX)); setRuleType(Rtype.IPv6); } } /** * The rule string is an IPv6 rule. Build both the prefix * mask array and rule mask from the string. * * @param rule The rule string containing the IPv6 rule. * @throws ConfigException If the rule string is not a valid * IPv6 rule. */ private void processIPv6(String rule) throws ConfigException { String[] s = rule.split("/", -1); InetAddress addr; try { addr = InetAddress.getByName(s[0]); } catch (UnknownHostException ex) { int msgID=MSGID_ADDRESSMASK_FORMAT_DECODE_ERROR; String message = getMessage(msgID); throw new ConfigException(msgID, message); } if(addr instanceof Inet6Address) { this.ruleType=RuleType.IPv6; Inet6Address addr6 = (Inet6Address) addr; this.ruleMask=addr6.getAddress(); this.prefixMask=new byte[IN6ADDRSZ]; prefixMask(processPrefix(s,IPV6MAXPREFIX)); } else { //The address might be an IPv4-compat address. //Throw an error if the rule has a prefix. if(s.length == 2) { int msgID = MSGID_ADDRESSMASK_FORMAT_DECODE_ERROR; String message = getMessage(msgID); throw new ConfigException(msgID, message); } this.ruleMask=addr.getAddress(); this.ruleType=RuleType.IPv4; this.prefixMask=new byte[IN4ADDRSZ]; prefixMask(processPrefix(s,IPV4MAXPREFIX)); } } } opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestAddressMask.java
@@ -26,12 +26,14 @@ */ package org.opends.server.types; import org.opends.server.types.AddressMask; import org.opends.server.config.ConfigException; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import static org.testng.Assert.*; import java.net.InetAddress; import java.net.UnknownHostException; public class TestAddressMask extends TypesTestCase { /* These are all valid rules -- should all pass. */ @@ -70,7 +72,6 @@ {"129.**.56.67"}, {"foo bar.com"}, {"12foo.example.com"}, {"[2001:fecd:ba23:cd1f:dcb1:1010:9234:4088]/124"}, {"123.45."}, {".central.sun day.com"}, {"129.34.45.45/4/3/"} @@ -106,8 +107,8 @@ "Invalid mask <" + mask + "> did not throw an exception."); } @DataProvider(name = "matchRules") public Object[][] ruleMatchData() { @DataProvider(name = "matchRules") public Object[][] ruleMatchData() { return new Object[][] { { //Rules @@ -121,7 +122,7 @@ "128.153.147.32/21",//7 "128.153.146.32/26",//8 "90.89.78.67/26"}, //9 //Addresses //Addresses new String[] { "128.153.147.45", //rule 7 "128.153.146.60", //rule 8 @@ -130,7 +131,7 @@ "148.45.45.47", //host "148.45.45.48", //host "90.89.78.65"}, //rule 5 //Hostnames //Hostnames new String[] { "some.host.name", //addr "some.host.name", //addr @@ -159,9 +160,9 @@ "128.153.147.32/21",//7 "128.153.146.32/26",//8 "90.89.78.67/26"}, //9 //Addresses //Addresses new String[] { "128.153.140.45", "128.153.140.45", "128.153.143.255", "148.45.45.46", "126.56.78.22", @@ -169,7 +170,7 @@ "148.45.45.48", "90.89.78.128", "148.45.45.49"}, //Hostnames //Hostnames new String[] { "some.host.name", "some.host.name", @@ -191,12 +192,12 @@ //Rules new String[] { "*.*.*", "*.*.*.*"}, //Addresses "*.*.*.*"}, //Addresses new String[] { "129.34.45.12", "129.34.45.13"}, //Hostnames "129.34.45.13"}, //Hostnames new String[] { "some.host.name" , "some.host.name"} @@ -206,7 +207,7 @@ @Test(dataProvider = "matchRules") public void testMatch(String[] rules, String[] addrs, String[]hostNames) { boolean ret=true; boolean ret; ret=match(rules,addrs,hostNames); assertTrue(ret); } @@ -214,7 +215,7 @@ @Test(dataProvider = "matchWCRules") public void testWildCardMatch(String[] rules, String[] addrs, String[]hostNames) { boolean ret=true; boolean ret; ret=match(rules,addrs,hostNames); assertTrue(ret); } @@ -222,7 +223,7 @@ @Test(dataProvider = "noMatchRules") public void testNoMatch(String[] rules, String[] addrs, String[] hostNames) { boolean ret=false; boolean ret; ret=match(rules,addrs,hostNames); assertFalse(ret); } @@ -234,47 +235,130 @@ assertEquals(rule, m.toString()); } catch (ConfigException ce) { throw new RuntimeException( "Invalid mask <" + rule + "Invalid mask <" + rule + "> all data should be valid for this test"); } } private byte[] getAddress(String remote) { byte[] addr=new byte[AddressMask.IN4ADDRSZ]; String[] s = remote.split("\\.", -1); try { for(int i=0;i<AddressMask.IN4ADDRSZ;i++) { long val = Integer.parseInt(s[i]); addr[i] = (byte) (val & 0xff); } } catch (NumberFormatException nfex) { return addr; } return addr; private boolean match(String[] rules, String[] addrs, String[]hostNames) { boolean ret=true; int i=0; AddressMask[] m = new AddressMask[rules.length]; try { for (i = 0; i < rules.length; i++) { m[i] = AddressMask.decode(rules[i]); } } catch (ConfigException ce) { throw new RuntimeException( "Invalid mask <" + rules[i] + "> all data must be valid for this test"); } for(int j = 0; j < addrs.length; j++) { try { InetAddress addr = InetAddress.getByName(addrs[j]); if(!AddressMask.maskListContains(addr.getAddress(), hostNames[j], m)) { ret=false; break; } } catch (UnknownHostException ex) { ret=false; } } return ret; } /* IPV6 data and tests. */ //Invalid IPv6 expressions. @DataProvider(name = "invalid6Rules") public Object[][] inValid6Data() { return new Object[][] { {"2001:feca:ba23:cd1f:dcb1:1010:9234:4088///124"}, {"2001:feca:ba23:cd1f:dcb1:1010:9234:4088?124"}, {"2001:fecz:ba23:cd1f:dcb1:1010:9234:4088/124"}, {"2001:fecd:ba23:cd1ff:dcb1:1010:9234:4088/46"}, {"0:0:0:0:0:ffff:101..45.75.219"}, {"0:0:0:0:0:0:101.45.75.700"}, {"1080::8:800:200C:417A/500"}, {"1080::8:800:*:417A/66"}, {"2001:fecd:ba23:cd1ff:dcb1:1010:202.45.66.20"}, }; } //Valid IPv6 expressions. @DataProvider(name = "valid6Rules") public Object[][] valid6Data() { return new Object[][] { {"2001:fecd:ba23:cd1f:dcb1:1010:9234:4088/124"}, {"2001:fecd:ba23:cd1f:dcb1:1010:9234:4088"}, {"[2001:fecd:ba23:cd1f:dcb1:1010:9234:4088]/45"}, {"::/128"}, {"::1/128"}, {"::"}, {"0:0:0:0:0:ffff:101.45.75.219"}, {"1080::8:800:200C:417A"}, {"0:0:0:0:0:0:101.45.75.219"}, {"::101.45.75.219"} }; } @DataProvider(name = "match6Rules") public Object[][] ruleMatch6Data() { return new Object[][] { { //IPV6 Rules new String[] { "[12ab:0:0:cd30::]/60", "::ffff:72.56.78.9", "::", "42ab:0:0:dd30::"}, //IPv6 Addresses new String[] { "12ab:0:0:cd3f:0000:0000:23DC:DC30", "72.56.78.9", "::", "42ab:0000:0000:dd30:0000:0000:0000:0000"}, //ignored Hostnames new String[] { "ignored.host.name", "ignored.host.name", "ignored.host.name", "ignored.host.name" } } }; } private boolean match(String[] rules, String[] addrs, String[]hostNames) { boolean ret=true; int i=0; @Test(dataProvider = "valid6Rules") public void testValid6Decode(String mask) throws Exception { AddressMask.decode(mask); } AddressMask[] m = new AddressMask[rules.length]; try { for (i = 0; i < rules.length; i++) { m[i] = AddressMask.decode(rules[i]); } } catch (ConfigException ce) { throw new RuntimeException( "Invalid mask <" + rules[i] + "> all data must be valid for this test"); } for(int j =0; j < addrs.length; j++) { if(!AddressMask.maskListContains(getAddress(addrs[j]), hostNames[j],m)) { ret=false; break; } } return ret; } @Test(expectedExceptions=ConfigException.class, dataProvider="invalid6Rules") public void testInvalid6Decode(String mask) throws Exception { try { AddressMask.decode(mask); } catch (ConfigException e) { throw e; } catch (Exception e) { System.out.println( "Invalid mask <" + mask + "> threw wrong exception type."); throw e; } throw new RuntimeException( "Invalid mask <" + mask + "> did not throw an exception."); } @Test(dataProvider = "match6Rules") public void testMatch6(String[] rules, String[] addrs, String[]hostNames) { boolean ret; ret=match(rules,addrs,hostNames); assertTrue(ret); } }