001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.types; 028 import org.opends.messages.Message; 029 030 import org.opends.server.config.ConfigException; 031 import static org.opends.messages.ProtocolMessages.*; 032 import java.util.BitSet; 033 import java.net.Inet6Address; 034 import java.net.InetAddress; 035 import java.net.UnknownHostException; 036 037 /** 038 * This class defines an address mask, which can be used to perform 039 * efficient comparisons against IP addresses to determine whether a 040 * particular IP address is in a given range. 041 */ 042 @org.opends.server.types.PublicAPI( 043 stability=org.opends.server.types.StabilityLevel.VOLATILE, 044 mayInstantiate=true, 045 mayExtend=false, 046 mayInvoke=true) 047 public final class AddressMask 048 { 049 /** 050 * Types of rules we have. 051 * 052 * IPv4 - ipv4 rule 053 * IPv6 - ipv6 rule (begin with '[' or contains an ':'). 054 * HOST - hostname match (foo.sun.com) 055 * HOSTPATTERN - host pattern match (begin with '.') 056 * ALLWILDCARD - *.*.*.* (first HOST is applied then ipv4) 057 * 058 */ 059 060 enum RuleType 061 { 062 IPv4, IPv6, HOSTPATTERN, ALLWILDCARD, HOST 063 } 064 065 // Type of rule determined 066 private RuleType ruleType; 067 068 // IPv4 values for number of bytes and max CIDR prefix 069 /** 070 * IPv4 address size. 071 */ 072 private static final int IN4ADDRSZ = 4; 073 private static final int IPV4MAXPREFIX = 32; 074 075 // IPv6 values for number of bytes and max CIDR prefix 076 private static final int IN6ADDRSZ = 16; 077 private static final int IPV6MAXPREFIX = 128; 078 079 //Holds binary representations of rule and mask respectively. 080 private byte[] ruleMask, prefixMask; 081 082 //Bit array that holds wildcard info for above binary arrays. 083 private final BitSet wildCard = new BitSet(); 084 085 //Array that holds each component of a hostname. 086 private String[] hostName; 087 088 //Holds a hostname pattern (ie, rule that begins with '.');' 089 private String hostPattern; 090 091 //Holds string passed into the constructor. 092 private String ruleString; 093 094 /** 095 * Address mask constructor. 096 * @param rule The rule string to process. 097 * @throws ConfigException If the rule string is not valid. 098 */ 099 private AddressMask( String rule) 100 throws ConfigException 101 { 102 determineRuleType(rule); 103 switch (ruleType) 104 { 105 case IPv6: 106 processIPv6(rule); 107 break; 108 109 case IPv4: 110 processIpv4(rule); 111 break; 112 113 case HOST: 114 processHost(rule); 115 break; 116 117 case HOSTPATTERN: 118 processHostPattern(rule); 119 break; 120 121 case ALLWILDCARD: 122 processAllWilds(rule); 123 } 124 ruleString=rule; 125 } 126 127 /** 128 * Try to determine what type of rule string this is. See 129 * RuleType above for valid types. 130 * @param ruleString The rule string to be examined. 131 * @throws ConfigException If the rule type cannot be 132 * determined from the rule string. 133 */ 134 private void determineRuleType(String ruleString) 135 throws ConfigException 136 { 137 138 //Rule ending with '.' is invalid' 139 if(ruleString.endsWith(".")) 140 { 141 Message message = 142 ERR_ADDRESSMASK_FORMAT_DECODE_ERROR.get(); 143 throw new ConfigException(message); 144 } 145 else if(ruleString.startsWith(".")) 146 { 147 ruleType=RuleType.HOSTPATTERN; 148 } 149 else if(ruleString.startsWith("[") || 150 (ruleString.indexOf(':') != -1)) 151 { 152 ruleType=RuleType.IPv6; 153 } 154 else 155 { 156 int wildCount=0; 157 String[] s = ruleString.split("\\.", -1); 158 //Try to figure out how many wildcards and if the rule is 159 // hostname (can't begin with digit) or ipv4 address. 160 //Default to IPv4 ruletype. 161 ruleType=RuleType.HOST; 162 for (String value : s) { 163 if (value.equals("*")) { 164 wildCount++; 165 continue; 166 } 167 //Looks like an ipv4 address 168 if (Character.isDigit(value.charAt(0))) { 169 ruleType = RuleType.IPv4; 170 break; 171 } 172 } 173 //All wildcards (*.*.*.*) 174 if(wildCount == s.length) 175 { 176 ruleType=RuleType.ALLWILDCARD; 177 } 178 } 179 } 180 181 /** 182 * The rule string is an IPv4 rule. Build both the prefix 183 * mask array and rule mask from the string. 184 * @param rule The rule string containing the IPv4 rule. 185 * @throws ConfigException If the rule string is not a valid 186 * IPv4 rule. 187 */ 188 private void processIpv4(String rule) 189 throws ConfigException { 190 String[] s = rule.split("/", -1); 191 this.ruleMask=new byte[IN4ADDRSZ]; 192 this.prefixMask=new byte[IN4ADDRSZ]; 193 prefixMask(processPrefix(s,IPV4MAXPREFIX)); 194 processIPv4Subnet((s.length == 0) ? rule : s[0]); 195 } 196 197 /** 198 * The rule string is all wildcards. Set both address wildcard 199 * bitmask and hostname wildcard array. 200 * @param rule The rule string containing all wildcards. 201 */ 202 private void processAllWilds(String rule) 203 { 204 String s[]=rule.split("\\.", -1); 205 if(s.length == IN4ADDRSZ) 206 { 207 for(int i=0;i<IN4ADDRSZ;i++) 208 wildCard.set(i); 209 } 210 hostName=rule.split("\\.", -1); 211 } 212 213 /** 214 * Examine the rule string of a host pattern and set the 215 * host pattern from the rule. 216 * @param rule The rule string to examine. 217 * @throws ConfigException If the rule string is not a valid 218 * host pattern rule. 219 */ 220 private void processHostPattern(String rule) 221 throws ConfigException 222 { 223 //quick check for invalid chars like " " 224 String s[]=rule.split("^[0-9a-zA-z-.]+"); 225 if(s.length > 0) 226 { 227 Message message = 228 ERR_ADDRESSMASK_FORMAT_DECODE_ERROR.get(); 229 throw new ConfigException(message); 230 } 231 hostPattern=rule; 232 } 233 234 /** 235 * Examine rule string and build a hostname string array 236 * of its parts. 237 * @param rule The rule string. 238 * @throws ConfigException If the rule string is not a valid 239 * host name. 240 */ 241 private void processHost(String rule) 242 throws ConfigException 243 { 244 //Note that '*' is valid in host rule 245 String s[]=rule.split("^[0-9a-zA-z-.*]+"); 246 if(s.length > 0) 247 { 248 Message message = 249 ERR_ADDRESSMASK_FORMAT_DECODE_ERROR.get(); 250 throw new ConfigException(message); 251 } 252 hostName=rule.split("\\.", -1); 253 } 254 255 /** 256 * Build the prefix mask of prefix len bits set in the array. 257 * @param prefix The len of the prefix to use. 258 */ 259 private void prefixMask(int prefix) 260 { 261 int i; 262 for( i=0;prefix > 8 ; i++) 263 { 264 this.prefixMask[i] = (byte) 0xff; 265 prefix -= 8; 266 } 267 this.prefixMask[i] = (byte) ((0xff) << (8 - prefix)); 268 } 269 270 /** 271 * Examine the subnet part of a rule string and build a 272 * byte array representation of it. 273 * @param subnet The subnet string part of the rule. 274 * @throws ConfigException If the subnet string is not a valid 275 * IPv4 subnet string. 276 */ 277 private void processIPv4Subnet(String subnet) 278 throws ConfigException { 279 String[] s = subnet.split("\\.", -1); 280 try { 281 //Make sure we have four parts 282 if(s.length != IN4ADDRSZ) { 283 Message message = 284 ERR_ADDRESSMASK_FORMAT_DECODE_ERROR.get(); 285 throw new ConfigException(message); 286 } 287 for(int i=0; i < IN4ADDRSZ; i++) 288 { 289 String quad=s[i].trim(); 290 if(quad.equals("*")) 291 wildCard.set(i) ; //see wildcard mark bitset 292 else 293 { 294 long val=Integer.parseInt(quad); 295 //must be between 0-255 296 if((val < 0) || (val > 0xff)) 297 { 298 Message message = 299 ERR_ADDRESSMASK_FORMAT_DECODE_ERROR.get(); 300 throw new ConfigException(message); 301 } 302 ruleMask[i] = (byte) (val & 0xff); 303 } 304 } 305 } catch (NumberFormatException nfex) 306 { 307 Message message = 308 ERR_ADDRESSMASK_FORMAT_DECODE_ERROR.get(); 309 throw new ConfigException(message); 310 } 311 } 312 313 /** 314 * Examine rule string for correct prefix usage. 315 * @param s The string array with rule string add and prefix 316 * strings. 317 * @param maxPrefix The max value the prefix can be. 318 * @return The prefix integer value. 319 * @throws ConfigException If the string array and prefix 320 * are not valid. 321 */ 322 private int processPrefix(String[] s, int maxPrefix) 323 throws ConfigException { 324 int prefix=maxPrefix; 325 try { 326 //can only have one prefix value and a subnet string 327 if((s.length < 1) || (s.length > 2) ) 328 { 329 Message message = 330 ERR_ADDRESSMASK_FORMAT_DECODE_ERROR.get(); 331 throw new ConfigException(message); 332 } 333 else if(s.length == 2) 334 { 335 //can't have wildcard with a prefix 336 if(s[0].indexOf('*') > -1) 337 { 338 Message message = 339 ERR_ADDRESSMASK_WILDCARD_DECODE_ERROR.get(); 340 throw new ConfigException(message); 341 } 342 prefix = Integer.parseInt(s[1]); 343 } 344 //must be between 0-maxprefix 345 if((prefix < 0) || (prefix > maxPrefix)) 346 { 347 Message message = 348 ERR_ADDRESSMASK_PREFIX_DECODE_ERROR.get(); 349 throw new ConfigException(message); 350 } 351 } 352 catch(NumberFormatException nfex) 353 { 354 Message msg = ERR_ADDRESSMASK_FORMAT_DECODE_ERROR.get(); 355 throw new ConfigException(msg); 356 } 357 return prefix; 358 } 359 360 361 /** 362 * Decodes the provided string as an address mask. 363 * 364 * @param maskString The string to decode as an address mask. 365 * 366 * @return AddressMask The address mask decoded from the 367 * provided string. 368 * 369 * @throws ConfigException If the provided string cannot be 370 * decoded as an address mask. 371 */ 372 373 374 public static AddressMask decode(String maskString) 375 throws ConfigException { 376 return new AddressMask(maskString); 377 } 378 379 /** 380 * Indicates whether provided address or hostname matches one of 381 * the address masks in the provided array. 382 * 383 * @param remoteAddr The remote address byte array. 384 * @param remoteName The remote host name string. 385 * @param masks An array of address masks to check. 386 * @return <CODE>true</CODE> if the provided address or hostname 387 * does match one of the given address masks, or 388 * <CODE>false</CODE> if it does not. 389 */ 390 public static boolean maskListContains(byte[] remoteAddr, 391 String remoteName, 392 AddressMask[] masks) 393 { 394 for (AddressMask mask : masks) { 395 if(mask.match(remoteAddr, remoteName)) 396 return true; 397 } 398 return false; 399 } 400 401 /** 402 * Retrieves a string representation of this address mask. 403 * 404 * @return A string representation of this address mask. 405 */ 406 public String toString() 407 { 408 return ruleString; 409 } 410 411 /** 412 * Main match function that determines which rule-type match 413 * function to use. 414 * @param remoteAddr The remote client address byte array. 415 * @param remoteName The remote client host name. 416 * @return <CODE>true</CODE>if one of the match functions found 417 * a match or <CODE>false</CODE>if not. 418 */ 419 private boolean match(byte[] remoteAddr, String remoteName) 420 { 421 boolean ret=false; 422 423 switch(ruleType) { 424 case IPv6: 425 case IPv4: 426 //this Address mask is an IPv4 rule 427 ret=matchAddress(remoteAddr); 428 break; 429 430 case HOST: 431 // HOST rule use hostname 432 ret=matchHostName(remoteName); 433 break; 434 435 case HOSTPATTERN: 436 //HOSTPATTERN rule 437 ret=matchPattern(remoteName); 438 break; 439 440 case ALLWILDCARD: 441 //first try ipv4 addr match, then hostname 442 ret=matchAddress(remoteAddr); 443 if(!ret) 444 ret=matchHostName(remoteName); 445 break; 446 } 447 return ret; 448 } 449 450 /** 451 * Try to match remote host name string against the pattern rule. 452 * @param remoteHostName The remote client host name. 453 * @return <CODE>true</CODE>if the remote host name matches or 454 * <CODE>false</CODE>if not. 455 */ 456 private boolean matchPattern(String remoteHostName) { 457 int len=remoteHostName.length() - hostPattern.length(); 458 return len > 0 && remoteHostName.regionMatches(true, len, 459 hostPattern, 0, hostPattern.length()); 460 } 461 462 /** 463 * Try to match remote client host name against rule host name. 464 * @param remoteHostName The remote host name string. 465 * @return <CODE>true</CODE>if the remote client host name matches 466 * <CODE>false</CODE> if it does not. 467 */ 468 private boolean matchHostName(String remoteHostName) { 469 String[] s = remoteHostName.split("\\.", -1); 470 if(s.length != hostName.length) 471 return false; 472 if(ruleType == RuleType.ALLWILDCARD) 473 return true; 474 for(int i=0;i<s.length;i++) 475 { 476 if(!hostName[i].equals("*")) //skip if wildcard 477 { 478 if(!s[i].equalsIgnoreCase(hostName[i])) 479 return false; 480 } 481 } 482 return true; 483 } 484 485 486 /** 487 * Try to match remote client address using prefix mask and 488 * rule mask. 489 * @param remoteMask The byte array with remote client address. 490 * @return <CODE>true</CODE> if remote client address matches or 491 * <CODE>false</CODE>if not. 492 */ 493 private boolean matchAddress(byte[] remoteMask) 494 { 495 if(prefixMask== null) 496 return false; 497 if(remoteMask.length != prefixMask.length) 498 return false; 499 if(ruleType == RuleType.ALLWILDCARD) 500 return true; 501 for(int i=0;i < prefixMask.length; i++) 502 { 503 if(!wildCard.get(i)) 504 { 505 if((ruleMask[i] & prefixMask[i]) != 506 (remoteMask[i] & prefixMask[i])) 507 return false; 508 } 509 } 510 return true; 511 } 512 513 /** 514 * The rule string is an IPv6 rule. Build both the prefix 515 * mask array and rule mask from the string. 516 * 517 * @param rule The rule string containing the IPv6 rule. 518 * @throws ConfigException If the rule string is not a valid 519 * IPv6 rule. 520 */ 521 private void processIPv6(String rule) throws ConfigException { 522 String[] s = rule.split("/", -1); 523 InetAddress addr; 524 try { 525 addr = InetAddress.getByName(s[0]); 526 } catch (UnknownHostException ex) { 527 Message message = 528 ERR_ADDRESSMASK_FORMAT_DECODE_ERROR.get(); 529 throw new ConfigException(message); 530 } 531 if(addr instanceof Inet6Address) { 532 this.ruleType=RuleType.IPv6; 533 Inet6Address addr6 = (Inet6Address) addr; 534 this.ruleMask=addr6.getAddress(); 535 this.prefixMask=new byte[IN6ADDRSZ]; 536 prefixMask(processPrefix(s,IPV6MAXPREFIX)); 537 } else { 538 //The address might be an IPv4-compat address. 539 //Throw an error if the rule has a prefix. 540 if(s.length == 2) { 541 Message message = 542 ERR_ADDRESSMASK_FORMAT_DECODE_ERROR.get(); 543 throw new ConfigException(message); 544 } 545 this.ruleMask=addr.getAddress(); 546 this.ruleType=RuleType.IPv4; 547 this.prefixMask=new byte[IN4ADDRSZ]; 548 prefixMask(processPrefix(s,IPV4MAXPREFIX)); 549 } 550 } 551 } 552 553