Home | History | Annotate | Download | only in core
      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