home *** CD-ROM | disk | FTP | other *** search
/ PC Online 1999 April / PCO0499.ISO / filesbbs / os2 / apach134.arj / APACH134.ZIP / src / main / util_uri.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-01-01  |  19.7 KB  |  593 lines

  1. /* ====================================================================
  2.  * Copyright (c) 1998-1999 The Apache Group.  All rights reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  *
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer. 
  10.  *
  11.  * 2. Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in
  13.  *    the documentation and/or other materials provided with the
  14.  *    distribution.
  15.  *
  16.  * 3. All advertising materials mentioning features or use of this
  17.  *    software must display the following acknowledgment:
  18.  *    "This product includes software developed by the Apache Group
  19.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  20.  *
  21.  * 4. The names "Apache Server" and "Apache Group" must not be used to
  22.  *    endorse or promote products derived from this software without
  23.  *    prior written permission.
  24.  *
  25.  * 5. Products derived from this software may not be called "Apache"
  26.  *    nor may "Apache" appear in their names without prior written
  27.  *    permission of the Apache Group.
  28.  *
  29.  * 6. Redistributions of any form whatsoever must retain the following
  30.  *    acknowledgment:
  31.  *    "This product includes software developed by the Apache Group
  32.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  33.  *
  34.  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
  35.  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  36.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  37.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
  38.  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  39.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  40.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  41.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  42.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  43.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  44.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  45.  * OF THE POSSIBILITY OF SUCH DAMAGE.
  46.  * ====================================================================
  47.  *
  48.  * This software consists of voluntary contributions made by many
  49.  * individuals on behalf of the Apache Group and was originally based
  50.  * on public domain software written at the National Center for
  51.  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
  52.  * For more information on the Apache Group and the Apache HTTP server
  53.  * project, please see <http://www.apache.org/>.
  54.  *
  55.  */
  56.  
  57. /*
  58.  * util_uri.c: URI related utility things
  59.  * 
  60.  */
  61.  
  62. #include "httpd.h"
  63. #include "http_log.h"
  64. #include "http_conf_globals.h"    /* for user_id & group_id */
  65. #include "util_uri.h"
  66.  
  67. /* Some WWW schemes and their default ports; this is basically /etc/services */
  68. /* This will become global when the protocol abstraction comes */
  69. /* As the schemes are searched by a linear search, */
  70. /* they are sorted by their expected frequency */
  71. static schemes_t schemes[] =
  72. {
  73.     {"http",   DEFAULT_HTTP_PORT},
  74.     {"ftp",    DEFAULT_FTP_PORT},
  75.     {"https",  DEFAULT_HTTPS_PORT},
  76.     {"gopher", DEFAULT_GOPHER_PORT},
  77.     {"wais",   DEFAULT_WAIS_PORT},
  78.     {"nntp",   DEFAULT_NNTP_PORT},
  79.     {"snews",  DEFAULT_SNEWS_PORT},
  80.     {"prospero", DEFAULT_PROSPERO_PORT},
  81.     { NULL, 0xFFFF }            /* unknown port */
  82. };
  83.  
  84.  
  85. API_EXPORT(unsigned short) ap_default_port_for_scheme(const char *scheme_str)
  86. {
  87.     schemes_t *scheme;
  88.  
  89.     for (scheme = schemes; scheme->name != NULL; ++scheme)
  90.     if (strcasecmp(scheme_str, scheme->name) == 0)
  91.         return scheme->default_port;
  92.  
  93.     return 0;
  94. }
  95.  
  96. API_EXPORT(unsigned short) ap_default_port_for_request(const request_rec *r)
  97. {
  98.     return (r->parsed_uri.scheme)
  99.     ? ap_default_port_for_scheme(r->parsed_uri.scheme)
  100.     : 0;
  101. }
  102.  
  103. /* Create a copy of a "struct hostent" record; it was presumably returned
  104.  * from a call to gethostbyname() and lives in static storage.
  105.  * By creating a copy we can tuck it away for later use.
  106.  */
  107. API_EXPORT(struct hostent *) ap_pduphostent(pool *p, const struct hostent *hp)
  108. {
  109.     struct hostent *newent;
  110.     char      **ptrs;
  111.     char      **aliases;
  112.     struct in_addr *addrs;
  113.     int           i = 0, j = 0;
  114.  
  115.     if (hp == NULL)
  116.     return NULL;
  117.  
  118.     /* Count number of alias entries */
  119.     if (hp->h_aliases != NULL)
  120.     for (; hp->h_aliases[j] != NULL; ++j)
  121.         continue;
  122.  
  123.     /* Count number of in_addr entries */
  124.     if (hp->h_addr_list != NULL)
  125.     for (; hp->h_addr_list[i] != NULL; ++i)
  126.         continue;
  127.  
  128.     /* Allocate hostent structure, alias ptrs, addr ptrs, addrs */
  129.     newent = (struct hostent *) ap_palloc(p, sizeof(*hp));
  130.     aliases = (char **) ap_palloc(p, (j+1) * sizeof(char*));
  131.     ptrs = (char **) ap_palloc(p, (i+1) * sizeof(char*));
  132.     addrs  = (struct in_addr *) ap_palloc(p, (i+1) * sizeof(struct in_addr));
  133.  
  134.     *newent = *hp;
  135.     newent->h_name = ap_pstrdup(p, hp->h_name);
  136.     newent->h_aliases = aliases;
  137.     newent->h_addr_list = (char**) ptrs;
  138.  
  139.     /* Copy Alias Names: */
  140.     for (j = 0; hp->h_aliases[j] != NULL; ++j) {
  141.        aliases[j] = ap_pstrdup(p, hp->h_aliases[j]);
  142.     }
  143.     aliases[j] = NULL;
  144.  
  145.     /* Copy address entries */
  146.     for (i = 0; hp->h_addr_list[i] != NULL; ++i) {
  147.     ptrs[i] = (char*) &addrs[i];
  148.     addrs[i] = *(struct in_addr *) hp->h_addr_list[i];
  149.     }
  150.     ptrs[i] = NULL;
  151.  
  152.     return newent;
  153. }
  154.  
  155.  
  156. /* pgethostbyname(): resolve hostname, if successful return an ALLOCATED
  157.  * COPY OF the hostent structure, intended to be stored and used later.
  158.  * (gethostbyname() uses static storage that would be overwritten on each call)
  159.  */
  160. API_EXPORT(struct hostent *) ap_pgethostbyname(pool *p, const char *hostname)
  161. {
  162.     struct hostent *hp = gethostbyname(hostname);
  163.     return (hp == NULL) ? NULL : ap_pduphostent(p, hp);
  164. }
  165.  
  166.  
  167. /* Unparse a uri_components structure to an URI string.
  168.  * Optionally suppress the password for security reasons.
  169.  */
  170. API_EXPORT(char *) ap_unparse_uri_components(pool *p, const uri_components *uptr, unsigned flags)
  171. {
  172.     char *ret = "";
  173.  
  174.     /* If suppressing the site part, omit both user name & scheme://hostname */
  175.     if (!(flags & UNP_OMITSITEPART)) {
  176.  
  177.     /* Construct a "user:password@" string, honoring the passed UNP_ flags: */
  178.     if (uptr->user||uptr->password)
  179.         ret = ap_pstrcat (p,
  180.             (uptr->user     && !(flags & UNP_OMITUSER)) ? uptr->user : "",
  181.             (uptr->password && !(flags & UNP_OMITPASSWORD)) ? ":" : "",
  182.             (uptr->password && !(flags & UNP_OMITPASSWORD))
  183.                ? ((flags & UNP_REVEALPASSWORD) ? uptr->password : "XXXXXXXX")
  184.                : "",
  185.             "@", NULL);
  186.  
  187.     /* Construct scheme://site string */
  188.     if (uptr->hostname) {
  189.         ret = ap_pstrcat (p,
  190.             uptr->scheme, "://", ret, 
  191.             uptr->hostname ? uptr->hostname : "",
  192.             uptr->port_str ? ":" : "",
  193.             uptr->port_str ? uptr->port_str : "",
  194.             NULL);
  195.     }
  196.     }
  197.  
  198.     /* Should we suppress all path info? */
  199.     if (!(flags & UNP_OMITPATHINFO)) {
  200.     /* Append path, query and fragment strings: */
  201.     ret = ap_pstrcat (p,
  202.         ret,
  203.         uptr->path ? uptr->path : "",
  204.         (uptr->query    && !(flags & UNP_OMITQUERY)) ? "?" : "",
  205.         (uptr->query    && !(flags & UNP_OMITQUERY)) ? uptr->query : "",
  206.         (uptr->fragment && !(flags & UNP_OMITQUERY)) ? "#" : NULL,
  207.         (uptr->fragment && !(flags & UNP_OMITQUERY)) ? uptr->fragment : NULL,
  208.         NULL);
  209.     }
  210.     return ret;
  211. }
  212.  
  213. /* The regex version of parse_uri_components has the advantage that it is
  214.  * relatively easy to understand and extend.  But it has the disadvantage
  215.  * that the regexes are complex enough that regex libraries really
  216.  * don't do a great job with them performancewise.
  217.  *
  218.  * The default is a hand coded scanner that is two orders of magnitude
  219.  * faster.
  220.  */
  221. #ifdef UTIL_URI_REGEX
  222.  
  223. static regex_t re_uri;
  224. static regex_t re_hostpart;
  225.  
  226. void ap_util_uri_init(void)
  227. {
  228.     int ret;
  229.     const char *re_str;
  230.  
  231.     /* This is a modified version of the regex that appeared in
  232.      * draft-fielding-uri-syntax-01.  It doesnt allow the uri to contain a
  233.      * scheme but no hostinfo or vice versa. 
  234.      *
  235.      * draft-fielding-uri-syntax-01.txt, section 4.4 tells us:
  236.      *
  237.      *        Although the BNF defines what is allowed in each component, it is
  238.      *        ambiguous in terms of differentiating between a site component and
  239.      *        a path component that begins with two slash characters.
  240.      *  
  241.      * RFC2068 disambiguates this for the Request-URI, which may only ever be
  242.      * the "abs_path" portion of the URI.  So a request "GET //foo/bar
  243.      * HTTP/1.1" is really referring to the path //foo/bar, not the host foo,
  244.      * path /bar.  Nowhere in RFC2068 is it possible to have a scheme but no
  245.      * hostinfo or a hostinfo but no scheme.  (Unless you're proxying a
  246.      * protocol other than HTTP, but this parsing engine probably won't work
  247.      * for other protocols.)
  248.      *
  249.      *         12            3          4       5   6        7 8 */
  250.     re_str = "^(([^:/?#]+)://([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$";
  251.     /*          ^scheme--^   ^site---^  ^path--^   ^query^    ^frag */
  252.     if ((ret = regcomp(&re_uri, re_str, REG_EXTENDED)) != 0) {
  253.     char line[1024];
  254.  
  255.     /* Make a readable error message */
  256.     ret = regerror(ret, &re_uri, line, sizeof line);
  257.     ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL,
  258.         "Internal error: regcomp(\"%s\") returned non-zero (%s) - "
  259.         "possibly due to broken regex lib! "
  260.         "Did you define WANTHSREGEX=yes?",
  261.         re_str, line);
  262.  
  263.     exit(1);
  264.     }
  265.  
  266.     /* This is a sub-RE which will break down the hostinfo part,
  267.      * i.e., user, password, hostname and port.
  268.      * $          12      3 4        5       6 7    */
  269.     re_str    = "^(([^:]*)(:(.*))?@)?([^@:]*)(:(.*))?$";
  270.     /*             ^^user^ :pw      ^host^   port */
  271.     if ((ret = regcomp(&re_hostpart, re_str, REG_EXTENDED)) != 0) {
  272.     char line[1024];
  273.  
  274.     /* Make a readable error message */
  275.     ret = regerror(ret, &re_hostpart, line, sizeof line);
  276.     ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL,
  277.         "Internal error: regcomp(\"%s\") returned non-zero (%s) - "
  278.         "possibly due to broken regex lib! "
  279.         "Did you define WANTHSREGEX=yes?",
  280.         re_str, line);
  281.  
  282.     exit(1);
  283.     }
  284. }
  285.  
  286.  
  287. /* parse_uri_components():
  288.  * Parse a given URI, fill in all supplied fields of a uri_components
  289.  * structure. This eliminates the necessity of extracting host, port,
  290.  * path, query info repeatedly in the modules.
  291.  * Side effects:
  292.  *  - fills in fields of uri_components *uptr
  293.  *  - none on any of the r->* fields
  294.  */
  295. API_EXPORT(int) ap_parse_uri_components(pool *p, const char *uri, uri_components *uptr)
  296. {
  297.     int ret;
  298.     regmatch_t match[10];    /* This must have at least as much elements
  299.                 * as there are braces in the re_strings */
  300.  
  301.     ap_assert (uptr != NULL);
  302.  
  303.     /* Initialize the structure. parse_uri() and parse_uri_components()
  304.      * can be called more than once per request.
  305.      */
  306.     memset (uptr, '\0', sizeof(*uptr));
  307.     uptr->is_initialized = 1;
  308.  
  309.     ret = regexec(&re_uri, uri, re_uri.re_nsub + 1, match, 0);
  310.  
  311.     if (ret != 0) {
  312.     ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL,
  313.                     "regexec() could not parse uri (\"%s\")",
  314.             uri);
  315.  
  316.     return HTTP_BAD_REQUEST;
  317.     }
  318.  
  319.     if (match[2].rm_so != match[2].rm_eo)
  320.     uptr->scheme = ap_pstrndup (p, uri+match[2].rm_so, match[2].rm_eo - match[2].rm_so);
  321.  
  322.     /* empty hostinfo is valid, that's why we test $1 but use $3 */
  323.     if (match[1].rm_so != match[1].rm_eo)
  324.     uptr->hostinfo = ap_pstrndup (p, uri+match[3].rm_so, match[3].rm_eo - match[3].rm_so);
  325.  
  326.     if (match[4].rm_so != match[4].rm_eo)
  327.     uptr->path = ap_pstrndup (p, uri+match[4].rm_so, match[4].rm_eo - match[4].rm_so);
  328.  
  329.     /* empty query string is valid, that's why we test $5 but use $6 */
  330.     if (match[5].rm_so != match[5].rm_eo)
  331.     uptr->query = ap_pstrndup (p, uri+match[6].rm_so, match[6].rm_eo - match[6].rm_so);
  332.  
  333.     /* empty fragment is valid, test $7 use $8 */
  334.     if (match[7].rm_so != match[7].rm_eo)
  335.     uptr->fragment = ap_pstrndup (p, uri+match[8].rm_so, match[8].rm_eo - match[8].rm_so);
  336.  
  337.     if (uptr->hostinfo) {
  338.     /* Parse the hostinfo part to extract user, password, host, and port */
  339.     ret = regexec(&re_hostpart, uptr->hostinfo, re_hostpart.re_nsub + 1, match, 0);
  340.     if (ret != 0) {
  341.         ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL,
  342.                     "regexec() could not parse (\"%s\") as host part",
  343.             uptr->hostinfo);
  344.  
  345.         return HTTP_BAD_REQUEST;
  346.     }
  347.  
  348.     /* $          12      3 4        5       6 7    */
  349.     /*        = "^(([^:]*)(:(.*))?@)?([^@:]*)(:(.*))?$" */
  350.     /*             ^^user^ :pw      ^host^   port */
  351.  
  352.     /* empty user is valid, that's why we test $1 but use $2 */
  353.     if (match[1].rm_so != match[1].rm_eo)
  354.         uptr->user = ap_pstrndup (p, uptr->hostinfo+match[2].rm_so, match[2].rm_eo - match[2].rm_so);
  355.  
  356.     /* empty password is valid, test $3 but use $4 */
  357.     if (match[3].rm_so != match[3].rm_eo)
  358.         uptr->password = ap_pstrndup (p, uptr->hostinfo+match[4].rm_so, match[4].rm_eo - match[4].rm_so);
  359.  
  360.     /* empty hostname is valid, and implied by the existence of hostinfo */
  361.     uptr->hostname = ap_pstrndup (p, uptr->hostinfo+match[5].rm_so, match[5].rm_eo - match[5].rm_so);
  362.  
  363.     if (match[6].rm_so != match[6].rm_eo) {
  364.         /* Note that the port string can be empty.
  365.          * If it is, we use the default port associated with the scheme
  366.          */
  367.         uptr->port_str = ap_pstrndup (p, uptr->hostinfo+match[7].rm_so, match[7].rm_eo - match[7].rm_so);
  368.         if (uptr->port_str[0] != '\0') {
  369.         char *endstr;
  370.         int port;
  371.  
  372.         port = strtol(uptr->port_str, &endstr, 10);
  373.         uptr->port = port;
  374.         if (*endstr != '\0' || uptr->port != port) {
  375.             /* Invalid characters after ':' found */
  376.             return HTTP_BAD_REQUEST;
  377.         }
  378.         }
  379.         else {
  380.         uptr->port = uptr->scheme ? ap_default_port_for_scheme(uptr->scheme) : DEFAULT_HTTP_PORT;
  381.         }
  382.     }
  383.     }
  384.  
  385.     if (ret == 0)
  386.     ret = HTTP_OK;
  387.     return ret;
  388. }
  389. #else
  390.  
  391. /* Here is the hand-optimized parse_uri_components().  There are some wild
  392.  * tricks we could pull in assembly language that we don't pull here... like we
  393.  * can do word-at-time scans for delimiter characters using the same technique
  394.  * that fast memchr()s use.  But that would be way non-portable. -djg
  395.  */
  396.  
  397. /* We have a table that we can index by character and it tells us if the
  398.  * character is one of the interesting delimiters.  Note that we even get
  399.  * compares for NUL for free -- it's just another delimiter.
  400.  */
  401.  
  402. #define T_COLON        0x01    /* ':' */
  403. #define T_SLASH        0x02    /* '/' */
  404. #define T_QUESTION    0x04    /* '?' */
  405. #define T_HASH        0x08    /* '#' */
  406. #define T_NUL        0x80    /* '\0' */
  407.  
  408. /* the uri_delims.h file is autogenerated by gen_uri_delims.c */
  409. #include "uri_delims.h"
  410.  
  411. /* it works like this:
  412.     if (uri_delims[ch] & NOTEND_foobar) {
  413.     then we're not at a delimiter for foobar
  414.     }
  415. */
  416.  
  417. /* Note that we optimize the scheme scanning here, we cheat and let the
  418.  * compiler know that it doesn't have to do the & masking.
  419.  */
  420. #define NOTEND_SCHEME    (0xff)
  421. #define NOTEND_HOSTINFO    (T_SLASH | T_QUESTION | T_HASH | T_NUL)
  422. #define NOTEND_PATH    (T_QUESTION | T_HASH | T_NUL)
  423.  
  424. void ap_util_uri_init(void)
  425. {
  426.     /* nothing to do */
  427. }
  428.  
  429. /* parse_uri_components():
  430.  * Parse a given URI, fill in all supplied fields of a uri_components
  431.  * structure. This eliminates the necessity of extracting host, port,
  432.  * path, query info repeatedly in the modules.
  433.  * Side effects:
  434.  *  - fills in fields of uri_components *uptr
  435.  *  - none on any of the r->* fields
  436.  */
  437. API_EXPORT(int) ap_parse_uri_components(pool *p, const char *uri, uri_components *uptr)
  438. {
  439.     const char *s;
  440.     const char *s1;
  441.     const char *hostinfo;
  442.     char *endstr;
  443.     int port;
  444.  
  445.     /* Initialize the structure. parse_uri() and parse_uri_components()
  446.      * can be called more than once per request.
  447.      */
  448.     memset (uptr, '\0', sizeof(*uptr));
  449.     uptr->is_initialized = 1;
  450.  
  451.     /* We assume the processor has a branch predictor like most --
  452.      * it assumes forward branches are untaken and backwards are taken.  That's
  453.      * the reason for the gotos.  -djg
  454.      */
  455.     if (uri[0] == '/') {
  456. deal_with_path:
  457.     /* we expect uri to point to first character of path ... remember
  458.      * that the path could be empty -- http://foobar?query for example
  459.      */
  460.     s = uri;
  461.     while ((uri_delims[*(unsigned char *)s] & NOTEND_PATH) == 0) {
  462.         ++s;
  463.     }
  464.     if (s != uri) {
  465.         uptr->path = ap_pstrndup(p, uri, s - uri);
  466.     }
  467.     if (*s == 0) {
  468.         return HTTP_OK;
  469.     }
  470.     if (*s == '?') {
  471.         ++s;
  472.         s1 = strchr(s, '#');
  473.         if (s1) {
  474.         uptr->fragment = ap_pstrdup(p, s1 + 1);
  475.         uptr->query = ap_pstrndup(p, s, s1 - s);
  476.         }
  477.         else {
  478.         uptr->query = ap_pstrdup(p, s);
  479.         }
  480.         return HTTP_OK;
  481.     }
  482.     /* otherwise it's a fragment */
  483.     uptr->fragment = ap_pstrdup(p, s + 1);
  484.     return HTTP_OK;
  485.     }
  486.  
  487.     /* find the scheme: */
  488.     s = uri;
  489.     while ((uri_delims[*(unsigned char *)s] & NOTEND_SCHEME) == 0) {
  490.     ++s;
  491.     }
  492.     /* scheme must be non-empty and followed by :// */
  493.     if (s == uri || s[0] != ':' || s[1] != '/' || s[2] != '/') {
  494.     goto deal_with_path;    /* backwards predicted taken! */
  495.     }
  496.  
  497.     uptr->scheme = ap_pstrndup(p, uri, s - uri);
  498.     s += 3;
  499.     hostinfo = s;
  500.     while ((uri_delims[*(unsigned char *)s] & NOTEND_HOSTINFO) == 0) {
  501.     ++s;
  502.     }
  503.     uri = s;    /* whatever follows hostinfo is start of uri */
  504.     uptr->hostinfo = ap_pstrndup(p, hostinfo, uri - hostinfo);
  505.  
  506.     /* If there's a username:password@host:port, the @ we want is the last @...
  507.      * too bad there's no memrchr()... For the C purists, note that hostinfo
  508.      * is definately not the first character of the original uri so therefore
  509.      * &hostinfo[-1] < &hostinfo[0] ... and this loop is valid C.
  510.      */
  511.     do {
  512.     --s;
  513.     } while (s >= hostinfo && *s != '@');
  514.     if (s < hostinfo) {
  515.     /* again we want the common case to be fall through */
  516. deal_with_host:
  517.     /* We expect hostinfo to point to the first character of
  518.      * the hostname.  If there's a port it is the first colon.
  519.      */
  520.     s = memchr(hostinfo, ':', uri - hostinfo);
  521.     if (s == NULL) {
  522.         /* we expect the common case to have no port */
  523.         uptr->hostname = ap_pstrndup(p, hostinfo, uri - hostinfo);
  524.         goto deal_with_path;
  525.     }
  526.     uptr->hostname = ap_pstrndup(p, hostinfo, s - hostinfo);
  527.     ++s;
  528.     uptr->port_str = ap_pstrndup(p, s, uri - s);
  529.     if (uri != s) {
  530.         port = strtol(uptr->port_str, &endstr, 10);
  531.         uptr->port = port;
  532.         if (*endstr == '\0') {
  533.         goto deal_with_path;
  534.         }
  535.         /* Invalid characters after ':' found */
  536.         return HTTP_BAD_REQUEST;
  537.     }
  538.     uptr->port = ap_default_port_for_scheme(uptr->scheme);
  539.     goto deal_with_path;
  540.     }
  541.  
  542.     /* first colon delimits username:password */
  543.     s1 = memchr(hostinfo, ':', s - hostinfo);
  544.     if (s1) {
  545.     uptr->user = ap_pstrndup(p, hostinfo, s1 - hostinfo);
  546.     ++s1;
  547.     uptr->password = ap_pstrndup(p, s1, s - s1);
  548.     }
  549.     else {
  550.     uptr->user = ap_pstrndup(p, hostinfo, s - hostinfo);
  551.     }
  552.     hostinfo = s + 1;
  553.     goto deal_with_host;
  554. }
  555.  
  556. /* Special case for CONNECT parsing: it comes with the hostinfo part only */
  557. /* See the INTERNET-DRAFT document "Tunneling SSL Through a WWW Proxy"
  558.  * currently at http://www.mcom.com/newsref/std/tunneling_ssl.html
  559.  * for the format of the "CONNECT host:port HTTP/1.0" request
  560.  */
  561. API_EXPORT(int) ap_parse_hostinfo_components(pool *p, const char *hostinfo, uri_components *uptr)
  562. {
  563.     const char *s;
  564.     char *endstr;
  565.  
  566.     /* Initialize the structure. parse_uri() and parse_uri_components()
  567.      * can be called more than once per request.
  568.      */
  569.     memset (uptr, '\0', sizeof(*uptr));
  570.     uptr->is_initialized = 1;
  571.     uptr->hostinfo = ap_pstrdup(p, hostinfo);
  572.  
  573.     /* We expect hostinfo to point to the first character of
  574.      * the hostname.  There must be a port, separated by a colon
  575.      */
  576.     s = strchr(hostinfo, ':');
  577.     if (s == NULL) {
  578.     return HTTP_BAD_REQUEST;
  579.     }
  580.     uptr->hostname = ap_pstrndup(p, hostinfo, s - hostinfo);
  581.     ++s;
  582.     uptr->port_str = ap_pstrdup(p, s);
  583.     if (*s != '\0') {
  584.     uptr->port = strtol(uptr->port_str, &endstr, 10);
  585.     if (*endstr == '\0') {
  586.         return HTTP_OK;
  587.     }
  588.     /* Invalid characters after ':' found */
  589.     }
  590.     return HTTP_BAD_REQUEST;
  591. }
  592. #endif
  593.