1 /* 2 * Conditions Of Use 3 * 4 * This software was developed by employees of the National Institute of 5 * Standards and Technology (NIST), an agency of the Federal Government. 6 * Pursuant to title 15 Untied States Code Section 105, works of NIST 7 * employees are not subject to copyright protection in the United States 8 * and are considered to be in the public domain. As a result, a formal 9 * license is not needed to use the software. 10 * 11 * This software is provided by NIST as a service and is expressly 12 * provided "AS IS." NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED 13 * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF 14 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT 15 * AND DATA ACCURACY. NIST does not warrant or make any representations 16 * regarding the use of the software or the results thereof, including but 17 * not limited to the correctness, accuracy, reliability or usefulness of 18 * the software. 19 * 20 * Permission to use this software is contingent upon your acceptance 21 * of the terms of this agreement 22 * 23 * . 24 * 25 */ 26 /* 27 * 28 * IPv6 Support added by Emil Ivov (emil_ivov (at) yahoo.com)<br/> 29 * Network Research Team (http://www-r2.u-strasbg.fr))<br/> 30 * Louis Pasteur University - Strasbourg - France<br/> 31 * 32 *Bug fixes for corner cases were contributed by Thomas Froment. 33 */ 34 package gov.nist.core; 35 36 // BEGIN android-deleted 37 //import gov.nist.javax.sdp.parser.Lexer; 38 // END android-deleted 39 40 import java.text.ParseException; 41 42 /** 43 * Parser for host names. 44 * 45 *@version 1.2 46 * 47 *@author M. Ranganathan 48 */ 49 50 public class HostNameParser extends ParserCore { 51 // BEGIN android-added 52 private static LexerCore Lexer; 53 // END android-added 54 55 /** 56 * Determines whether or not we should tolerate and strip address scope 57 * zones from IPv6 addresses. Address scope zones are sometimes returned 58 * at the end of IPv6 addresses generated by InetAddress.getHostAddress(). 59 * They are however not part of the SIP semantics so basically this method 60 * determines whether or not the parser should be stripping them (as 61 * opposed simply being blunt and throwing an exception). 62 */ 63 private boolean stripAddressScopeZones = false; 64 65 public HostNameParser(String hname) { 66 this.lexer = new LexerCore("charLexer", hname); 67 68 stripAddressScopeZones 69 = Boolean.getBoolean("gov.nist.core.STRIP_ADDR_SCOPES"); 70 } 71 72 /** 73 * The lexer is initialized with the buffer. 74 */ 75 public HostNameParser(LexerCore lexer) { 76 this.lexer = lexer; 77 lexer.selectLexer("charLexer"); 78 79 stripAddressScopeZones 80 = Boolean.getBoolean("gov.nist.core.STRIP_ADDR_SCOPES"); 81 } 82 83 private static final char[] VALID_DOMAIN_LABEL_CHAR = 84 new char[] {LexerCore.ALPHADIGIT_VALID_CHARS, '-', '.'}; 85 protected void consumeDomainLabel() throws ParseException { 86 if (debug) 87 dbg_enter("domainLabel"); 88 try { 89 lexer.consumeValidChars(VALID_DOMAIN_LABEL_CHAR); 90 } finally { 91 if (debug) 92 dbg_leave("domainLabel"); 93 } 94 } 95 96 protected String ipv6Reference() throws ParseException { 97 StringBuffer retval = new StringBuffer(); 98 if (debug) 99 dbg_enter("ipv6Reference"); 100 101 try { 102 103 if(stripAddressScopeZones){ 104 while (lexer.hasMoreChars()) { 105 char la = lexer.lookAhead(0); 106 //'%' is ipv6 address scope zone. see detail at 107 //java.sun.com/j2se/1.5.0/docs/api/java/net/Inet6Address.html 108 if (LexerCore.isHexDigit(la) || la == '.' || la == ':' 109 || la == '[' ) { 110 lexer.consume(1); 111 retval.append(la); 112 } else if (la == ']') { 113 lexer.consume(1); 114 retval.append(la); 115 return retval.toString(); 116 } else if (la == '%'){ 117 //we need to strip the address scope zone. 118 lexer.consume(1); 119 120 String rest = lexer.getRest(); 121 122 if(rest == null || rest.length() == 0){ 123 //head for the parse exception 124 break; 125 } 126 127 //we strip everything until either the end of the string 128 //or a closing square bracket (]) 129 int stripLen = rest.indexOf(']'); 130 131 if (stripLen == -1){ 132 //no square bracket -> not a valid ipv6 reference 133 break; 134 } 135 136 lexer.consume(stripLen+1); 137 retval.append("]"); 138 return retval.toString(); 139 140 } else 141 break; 142 } 143 } 144 else 145 { 146 while (lexer.hasMoreChars()) 147 { 148 char la = lexer.lookAhead(0); 149 if (LexerCore.isHexDigit(la) || la == '.' 150 || la == ':' || la == '[') { 151 lexer.consume(1); 152 retval.append(la); 153 } else if (la == ']') { 154 lexer.consume(1); 155 retval.append(la); 156 return retval.toString(); 157 } else 158 break; 159 } 160 } 161 162 throw new ParseException( 163 lexer.getBuffer() + ": Illegal Host name ", 164 lexer.getPtr()); 165 } finally { 166 if (debug) 167 dbg_leave("ipv6Reference"); 168 } 169 } 170 171 public Host host() throws ParseException { 172 if (debug) 173 dbg_enter("host"); 174 try { 175 String hostname; 176 177 //IPv6 referene 178 if (lexer.lookAhead(0) == '[') { 179 hostname = ipv6Reference(); 180 } 181 //IPv6 address (i.e. missing square brackets) 182 else if( isIPv6Address(lexer.getRest()) ) 183 { 184 int startPtr = lexer.getPtr(); 185 lexer.consumeValidChars( 186 new char[] {LexerCore.ALPHADIGIT_VALID_CHARS, ':'}); 187 hostname 188 = new StringBuffer("[").append( 189 lexer.getBuffer().substring(startPtr, lexer.getPtr())) 190 .append("]").toString(); 191 } 192 //IPv4 address or hostname 193 else { 194 int startPtr = lexer.getPtr(); 195 consumeDomainLabel(); 196 hostname = lexer.getBuffer().substring(startPtr, lexer.getPtr()); 197 } 198 199 if (hostname.length() == 0) 200 throw new ParseException( 201 lexer.getBuffer() + ": Missing host name", 202 lexer.getPtr()); 203 else 204 return new Host(hostname); 205 } finally { 206 if (debug) 207 dbg_leave("host"); 208 } 209 } 210 211 /** 212 * Tries to determine whether the address in <tt>uriHeader</tt> could be 213 * an IPv6 address by counting the number of colons that appear in it. 214 * 215 * @param uriHeader the string (supposedly the value of a URI header) that 216 * we have received for parsing. 217 * 218 * @return true if the host part of <tt>uriHeader</tt> could be an IPv6 219 * address (i.e. contains at least two colons) and false otherwise. 220 */ 221 private boolean isIPv6Address(String uriHeader) 222 { 223 // approximately detect the end the host part. 224 //first check if we have an uri param 225 int hostEnd = uriHeader.indexOf(Lexer.QUESTION); 226 227 //if not or if it appears after a semi-colon then the end of the 228 //address would be a header param. 229 int semiColonIndex = uriHeader.indexOf(Lexer.SEMICOLON); 230 if ( hostEnd == -1 231 || (semiColonIndex!= -1 && hostEnd > semiColonIndex) ) 232 hostEnd = semiColonIndex; 233 234 //if there was no header param either the address 235 //continues until the end of the string 236 if ( hostEnd == -1 ) 237 hostEnd = uriHeader.length(); 238 239 //extract the address 240 String host = uriHeader.substring(0, hostEnd); 241 242 int firstColonIndex = host.indexOf(Lexer.COLON); 243 244 if(firstColonIndex == -1) 245 return false; 246 247 int secondColonIndex = host.indexOf(Lexer.COLON, firstColonIndex + 1); 248 249 if(secondColonIndex == -1) 250 return false; 251 252 return true; 253 } 254 /** 255 * Parses a host:port string 256 * 257 * @param allowWS - whether whitespace is allowed around ':', only true for Via headers 258 * @return 259 * @throws ParseException 260 */ 261 public HostPort hostPort( boolean allowWS ) throws ParseException { 262 if (debug) 263 dbg_enter("hostPort"); 264 try { 265 Host host = this.host(); 266 HostPort hp = new HostPort(); 267 hp.setHost(host); 268 // Has a port? 269 if (allowWS) lexer.SPorHT(); // white space before ":port" should be accepted 270 if (lexer.hasMoreChars()) { 271 char la = lexer.lookAhead(0); 272 switch (la) 273 { 274 case ':': 275 lexer.consume(1); 276 if (allowWS) lexer.SPorHT(); // white space before port number should be accepted 277 try { 278 String port = lexer.number(); 279 hp.setPort(Integer.parseInt(port)); 280 } catch (NumberFormatException nfe) { 281 throw new ParseException( 282 lexer.getBuffer() + " :Error parsing port ", 283 lexer.getPtr()); 284 } 285 break; 286 287 case ',': // allowed in case of multi-headers, e.g. Route 288 // Could check that current header is a multi hdr 289 290 case ';': // OK, can appear in URIs (parameters) 291 case '?': // same, header parameters 292 case '>': // OK, can appear in headers 293 case ' ': // OK, allow whitespace 294 case '\t': 295 case '\r': 296 case '\n': 297 case '/': // e.g. http://[::1]/xyz.html 298 break; 299 case '%': 300 if(stripAddressScopeZones){ 301 break;//OK,allow IPv6 address scope zone 302 } 303 304 default: 305 if (!allowWS) { 306 throw new ParseException( lexer.getBuffer() + 307 " Illegal character in hostname:" + lexer.lookAhead(0), 308 lexer.getPtr() ); 309 } 310 } 311 } 312 return hp; 313 } finally { 314 if (debug) 315 dbg_leave("hostPort"); 316 } 317 } 318 319 public static void main(String args[]) throws ParseException { 320 String hostNames[] = 321 { 322 "foo.bar.com:1234", 323 "proxima.chaplin.bt.co.uk", 324 "129.6.55.181:2345", 325 ":1234", 326 "foo.bar.com: 1234", 327 "foo.bar.com : 1234 ", 328 "MIK_S:1234" 329 }; 330 331 for (int i = 0; i < hostNames.length; i++) { 332 try { 333 HostNameParser hnp = new HostNameParser(hostNames[i]); 334 HostPort hp = hnp.hostPort(true); 335 System.out.println("["+hp.encode()+"]"); 336 } catch (ParseException ex) { 337 System.out.println("exception text = " + ex.getMessage()); 338 } 339 } 340 341 } 342 } 343