1 /* $MirOS: src/usr.sbin/httpd/src/main/util_uri.c,v 1.2 2005/03/13 19:16:49 tg Exp $ */
2 
3 /* ====================================================================
4  * The Apache Software License, Version 1.1
5  *
6  * Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
7  * reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in
18  *    the documentation and/or other materials provided with the
19  *    distribution.
20  *
21  * 3. The end-user documentation included with the redistribution,
22  *    if any, must include the following acknowledgment:
23  *       "This product includes software developed by the
24  *        Apache Software Foundation (http://www.apache.org/)."
25  *    Alternately, this acknowledgment may appear in the software itself,
26  *    if and wherever such third-party acknowledgments normally appear.
27  *
28  * 4. The names "Apache" and "Apache Software Foundation" must
29  *    not be used to endorse or promote products derived from this
30  *    software without prior written permission. For written
31  *    permission, please contact apache@apache.org.
32  *
33  * 5. Products derived from this software may not be called "Apache",
34  *    nor may "Apache" appear in their name, without prior written
35  *    permission of the Apache Software Foundation.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48  * SUCH DAMAGE.
49  * ====================================================================
50  *
51  * This software consists of voluntary contributions made by many
52  * individuals on behalf of the Apache Software Foundation.  For more
53  * information on the Apache Software Foundation, please see
54  * <http://www.apache.org/>.
55  *
56  * Portions of this software are based upon public domain software
57  * originally written at the National Center for Supercomputing Applications,
58  * University of Illinois, Urbana-Champaign.
59  */
60 
61 /*
62  * util_uri.c: URI related utility things
63  *
64  */
65 
66 #include "httpd.h"
67 #include "http_log.h"
68 #include "http_conf_globals.h"  /* for user_id & group_id */
69 #include "util_uri.h"
70 
71 /* Some WWW schemes and their default ports; this is basically /etc/services */
72 /* This will become global when the protocol abstraction comes */
73 /* As the schemes are searched by a linear search, */
74 /* they are sorted by their expected frequency */
75 static schemes_t schemes[] = {
76     {"http", DEFAULT_HTTP_PORT},
77     {"ftp", DEFAULT_FTP_PORT},
78     {"https", DEFAULT_HTTPS_PORT},
79     {"gopher", DEFAULT_GOPHER_PORT},
80     {"wais", DEFAULT_WAIS_PORT},
81     {"nntp", DEFAULT_NNTP_PORT},
82     {"snews", DEFAULT_SNEWS_PORT},
83     {"prospero", DEFAULT_PROSPERO_PORT},
84     {NULL, 0xFFFF}              /* unknown port */
85 };
86 
87 
ap_default_port_for_scheme(const char * scheme_str)88 API_EXPORT(unsigned short) ap_default_port_for_scheme(const char *scheme_str)
89 {
90     schemes_t *scheme;
91 
92     if (scheme_str == NULL)
93         return 0;
94 
95     for (scheme = schemes; scheme->name != NULL; ++scheme)
96         if (strcasecmp(scheme_str, scheme->name) == 0)
97             return scheme->default_port;
98 
99     return 0;
100 }
101 
ap_default_port_for_request(const request_rec * r)102 API_EXPORT(unsigned short) ap_default_port_for_request(const request_rec *r)
103 {
104     return (r->parsed_uri.scheme)
105         ? ap_default_port_for_scheme(r->parsed_uri.scheme)
106         : 0;
107 }
108 
109 /* Create a copy of a "struct hostent" record; it was presumably returned
110  * from a call to gethostbyname() and lives in static storage.
111  * By creating a copy we can tuck it away for later use.
112  */
ap_pduphostent(pool * p,const struct hostent * hp)113 API_EXPORT(struct hostent *) ap_pduphostent(pool *p, const struct hostent *hp)
114 {
115     struct hostent *newent;
116     char **ptrs;
117     char **aliases;
118     struct in_addr *addrs;
119     int i = 0, j = 0;
120 
121     if (hp == NULL)
122         return NULL;
123 
124     /* Count number of alias entries */
125     if (hp->h_aliases != NULL)
126         for (; hp->h_aliases[j] != NULL; ++j)
127             continue;
128 
129     /* Count number of in_addr entries */
130     if (hp->h_addr_list != NULL)
131         for (; hp->h_addr_list[i] != NULL; ++i)
132             continue;
133 
134     /* Allocate hostent structure, alias ptrs, addr ptrs, addrs */
135     newent = (struct hostent *) ap_palloc(p, sizeof(*hp));
136     aliases = (char **) ap_palloc(p, (j + 1) * sizeof(char *));
137     ptrs = (char **) ap_palloc(p, (i + 1) * sizeof(char *));
138     addrs = (struct in_addr *) ap_palloc(p, (i + 1) * sizeof(struct in_addr));
139 
140     *newent = *hp;
141     newent->h_name = ap_pstrdup(p, hp->h_name);
142     newent->h_aliases = aliases;
143     newent->h_addr_list = (char **) ptrs;
144 
145     /* Copy Alias Names: */
146     for (j = 0; hp->h_aliases[j] != NULL; ++j) {
147         aliases[j] = ap_pstrdup(p, hp->h_aliases[j]);
148     }
149     aliases[j] = NULL;
150 
151     /* Copy address entries */
152     for (i = 0; hp->h_addr_list[i] != NULL; ++i) {
153         ptrs[i] = (char *) &addrs[i];
154         addrs[i] = *(struct in_addr *) hp->h_addr_list[i];
155     }
156     ptrs[i] = NULL;
157 
158     return newent;
159 }
160 
161 
162 /* pgethostbyname(): resolve hostname, if successful return an ALLOCATED
163  * COPY OF the hostent structure, intended to be stored and used later.
164  * (gethostbyname() uses static storage that would be overwritten on each call)
165  */
ap_pgethostbyname(pool * p,const char * hostname)166 API_EXPORT(struct hostent *) ap_pgethostbyname(pool *p, const char *hostname)
167 {
168     struct hostent *hp = gethostbyname(hostname);
169     return (hp == NULL) ? NULL : ap_pduphostent(p, hp);
170 }
171 
172 
173 /* Unparse a uri_components structure to an URI string.
174  * Optionally suppress the password for security reasons.
175  * See also RFC 2396.
176  */
ap_unparse_uri_components(pool * p,const uri_components * uptr,unsigned flags)177 API_EXPORT(char *) ap_unparse_uri_components(pool *p,
178                                              const uri_components * uptr,
179                                              unsigned flags)
180 {
181     char *parts[16];     /* 16 distinct parts of a URI */
182     char *scheme = NULL; /* to hold the scheme without modifying const args */
183     int j = 0;           /* an index into parts */
184 
185     memset(parts, 0, sizeof(parts));
186 
187     /* If suppressing the site part, omit all of scheme://user:pass@host:port */
188     if (!(flags & UNP_OMITSITEPART)) {
189 
190         /* if the user passes in a scheme, we'll assume an absoluteURI */
191         if (uptr->scheme) {
192             scheme = uptr->scheme;
193 
194             parts[j++] = uptr->scheme;
195             parts[j++] = ":";
196         }
197 
198         /* handle the hier_part */
199         if (uptr->user || uptr->password || uptr->hostname) {
200 
201             /* this stuff requires absoluteURI, so we have to add the scheme */
202             if (!uptr->scheme) {
203                 scheme = DEFAULT_URI_SCHEME;
204 
205                 parts[j++] = DEFAULT_URI_SCHEME;
206                 parts[j++] = ":";
207             }
208 
209             parts[j++] = "//";
210 
211             /* userinfo requires hostport */
212             if (uptr->hostname && (uptr->user || uptr->password)) {
213                 if (uptr->user && !(flags & UNP_OMITUSER))
214                     parts[j++] = uptr->user;
215 
216                 if (uptr->password && !(flags & UNP_OMITPASSWORD)) {
217                     parts[j++] = ":";
218 
219                     if (flags & UNP_REVEALPASSWORD)
220                         parts[j++] = uptr->password;
221                     else
222                         parts[j++] = "XXXXXXXX";
223                 }
224 
225                 parts[j++] = "@";
226             }
227 
228             /* If we get here, there must be a hostname. */
229             parts[j++] = uptr->hostname;
230 
231             /* Emit the port.  A small beautification
232              * prevents http://host:80/ and similar visual blight.
233              */
234             if (uptr->port_str &&
235                 !(uptr->port   &&
236                   scheme       &&
237                   uptr->port == ap_default_port_for_scheme(scheme))) {
238 
239                 parts[j++] = ":";
240                 parts[j++] = uptr->port_str;
241             }
242         }
243     }
244 
245     if (!(flags & UNP_OMITPATHINFO)) {
246 
247 
248         /* We must ensure we don't put out a hier_part and a rel_path */
249         if (j && uptr->path && *uptr->path != '/')
250             parts[j++] = "/";
251 
252         if (uptr->path != NULL)
253             parts[j++] = uptr->path;
254 
255         if (!(flags & UNP_OMITQUERY)) {
256             if (uptr->query) {
257                 parts[j++] = "?";
258                 parts[j++] = uptr->query;
259             }
260 
261             if (uptr->fragment) {
262                 parts[j++] = "#";
263                 parts[j++] = uptr->fragment;
264             }
265         }
266     }
267 
268     /* Ugly, but correct and probably faster than vsnprintf. */
269     return ap_pstrcat(p,
270         parts[0],
271         parts[1],
272         parts[2],
273         parts[3],
274         parts[4],
275         parts[5],
276         parts[6],
277         parts[7],
278         parts[8],
279         parts[9],
280         parts[10],
281         parts[11],
282         parts[12],
283         parts[13],
284         parts[14],
285         parts[15],
286         NULL
287     );
288 }
289 
290 /* Here is the hand-optimized parse_uri_components().  There are some wild
291  * tricks we could pull in assembly language that we don't pull here... like we
292  * can do word-at-time scans for delimiter characters using the same technique
293  * that fast memchr()s use.  But that would be way non-portable. -djg
294  */
295 
296 /* We have a table that we can index by character and it tells us if the
297  * character is one of the interesting delimiters.  Note that we even get
298  * compares for NUL for free -- it's just another delimiter.
299  */
300 
301 #define T_COLON         0x01    /* ':' */
302 #define T_SLASH         0x02    /* '/' */
303 #define T_QUESTION      0x04    /* '?' */
304 #define T_HASH          0x08    /* '#' */
305 #define T_NUL           0x80    /* '\0' */
306 
307 /* the uri_delims.h file is autogenerated by gen_uri_delims.c */
308 #include "uri_delims.h"
309 
310 /* it works like this:
311     if (uri_delims[ch] & NOTEND_foobar) {
312         then we're not at a delimiter for foobar
313     }
314 */
315 
316 /* Note that we optimize the scheme scanning here, we cheat and let the
317  * compiler know that it doesn't have to do the & masking.
318  */
319 #define NOTEND_SCHEME   (0xff)
320 #define NOTEND_HOSTINFO (T_SLASH | T_QUESTION | T_HASH | T_NUL)
321 #define NOTEND_PATH     (T_QUESTION | T_HASH | T_NUL)
322 
ap_util_uri_init(void)323 void ap_util_uri_init(void)
324 {
325     /* Nothing to do - except....
326        UTIL_URI_REGEX was removed, but third parties may depend on this symbol
327        being present. So, we'll leave it in.... - vjo
328      */
329 }
330 
331 /* parse_uri_components():
332  * Parse a given URI, fill in all supplied fields of a uri_components
333  * structure. This eliminates the necessity of extracting host, port,
334  * path, query info repeatedly in the modules.
335  * Side effects:
336  *  - fills in fields of uri_components *uptr
337  *  - none on any of the r->* fields
338  */
ap_parse_uri_components(pool * p,const char * uri,uri_components * uptr)339 API_EXPORT(int) ap_parse_uri_components(pool *p, const char *uri,
340                                         uri_components * uptr)
341 {
342     const char *s;
343     const char *s1;
344     const char *hostinfo;
345     char *endstr;
346     int port;
347 
348     /* Initialize the structure. parse_uri() and parse_uri_components()
349      * can be called more than once per request.
350      */
351     memset(uptr, '\0', sizeof(*uptr));
352     uptr->is_initialized = 1;
353 
354     /* We assume the processor has a branch predictor like most --
355      * it assumes forward branches are untaken and backwards are taken.  That's
356      * the reason for the gotos.  -djg
357      */
358     if (uri[0] == '/') {
359       deal_with_path:
360         /* we expect uri to point to first character of path ... remember
361          * that the path could be empty -- http://foobar?query for example
362          */
363         s = uri;
364         while ((uri_delims[*(unsigned char *) s] & NOTEND_PATH) == 0) {
365             ++s;
366         }
367         if (s != uri) {
368             uptr->path = ap_pstrndup(p, uri, s - uri);
369         }
370         if (*s == 0) {
371             return HTTP_OK;
372         }
373         if (*s == '?') {
374             ++s;
375             s1 = strchr(s, '#');
376             if (s1) {
377                 uptr->fragment = ap_pstrdup(p, s1 + 1);
378                 uptr->query = ap_pstrndup(p, s, s1 - s);
379             }
380             else {
381                 uptr->query = ap_pstrdup(p, s);
382             }
383             return HTTP_OK;
384         }
385         /* otherwise it's a fragment */
386         uptr->fragment = ap_pstrdup(p, s + 1);
387         return HTTP_OK;
388     }
389 
390     /* find the scheme: */
391     s = uri;
392     while ((uri_delims[*(unsigned char *) s] & NOTEND_SCHEME) == 0) {
393         ++s;
394     }
395     /* scheme must be non-empty and followed by :// */
396     if (s == uri || s[0] != ':' || s[1] != '/' || s[2] != '/') {
397         goto deal_with_path;    /* backwards predicted taken! */
398     }
399 
400     uptr->scheme = ap_pstrndup(p, uri, s - uri);
401     s += 3;
402     hostinfo = s;
403     while ((uri_delims[*(unsigned char *) s] & NOTEND_HOSTINFO) == 0) {
404         ++s;
405     }
406     uri = s;                    /* whatever follows hostinfo is start of uri */
407     uptr->hostinfo = ap_pstrndup(p, hostinfo, uri - hostinfo);
408 
409     /* If there's a username:password@host:port, the @ we want is the last @...
410      * too bad there's no memrchr()... For the C purists, note that hostinfo
411      * is definately not the first character of the original uri so therefore
412      * &hostinfo[-1] < &hostinfo[0] ... and this loop is valid C.
413      */
414     do {
415 	--s;
416     } while (s >= hostinfo && *s != '@');
417     if (s < hostinfo) {
418 	/* again we want the common case to be fall through */
419 deal_with_host:
420 	/* We expect hostinfo to point to the first character of
421 	 * the hostname.  If there's a port it is the first colon.
422 	 */
423 	if (*hostinfo == '[') {
424 	    s = memchr(hostinfo+1, ']', uri - hostinfo - 1);
425 	    if (s)
426 		s = strchr(s, ':');
427 	} else
428 	    s = memchr(hostinfo, ':', uri - hostinfo);
429 	if (s == NULL) {
430 	    /* we expect the common case to have no port */
431 	    uptr->hostname = ap_pstrndup(p, hostinfo, uri - hostinfo);
432 	    goto deal_with_path;
433 	}
434 	uptr->hostname = ap_pstrndup(p, hostinfo, s - hostinfo);
435 	++s;
436 	uptr->port_str = ap_pstrndup(p, s, uri - s);
437 	if (uri != s) {
438 	    port = ap_strtol(uptr->port_str, &endstr, 10);
439 	    uptr->port = port;
440 	    if (*endstr == '\0') {
441 		goto deal_with_path;
442 	    }
443 	    /* Invalid characters after ':' found */
444 	    return HTTP_BAD_REQUEST;
445 	}
446 	uptr->port = ap_default_port_for_scheme(uptr->scheme);
447 	goto deal_with_path;
448     }
449 
450     /* first colon delimits username:password */
451     s1 = memchr(hostinfo, ':', s - hostinfo);
452     if (s1) {
453 	uptr->user = ap_pstrndup(p, hostinfo, s1 - hostinfo);
454 	++s1;
455 	uptr->password = ap_pstrndup(p, s1, s - s1);
456     }
457     else {
458 	uptr->user = ap_pstrndup(p, hostinfo, s - hostinfo);
459     }
460     hostinfo = s + 1;
461     goto deal_with_host;
462 }
463 
464 /* Special case for CONNECT parsing: it comes with the hostinfo part only */
465 /* See the INTERNET-DRAFT document "Tunneling SSL Through a WWW Proxy"
466  * currently at http://www.mcom.com/newsref/std/tunneling_ssl.html
467  * for the format of the "CONNECT host:port HTTP/1.0" request
468  */
ap_parse_hostinfo_components(pool * p,const char * hostinfo,uri_components * uptr)469 API_EXPORT(int) ap_parse_hostinfo_components(pool *p, const char *hostinfo,
470                                              uri_components * uptr)
471 {
472     const char *s;
473     char *endstr;
474 
475     /* Initialize the structure. parse_uri() and parse_uri_components()
476      * can be called more than once per request.
477      */
478     memset(uptr, '\0', sizeof(*uptr));
479     uptr->is_initialized = 1;
480     uptr->hostinfo = ap_pstrdup(p, hostinfo);
481 
482     /* We expect hostinfo to point to the first character of
483      * the hostname.  There must be a port, separated by a colon
484      */
485     if (*hostinfo == '[') {
486         s = strchr(hostinfo+1, ']');
487         if (s)
488             s = strchr(s, ':');
489     } else
490         s = strchr(hostinfo, ':');
491     if (s == NULL) {
492         return HTTP_BAD_REQUEST;
493     }
494     uptr->hostname = ap_pstrndup(p, hostinfo, s - hostinfo);
495     ++s;
496     uptr->port_str = ap_pstrdup(p, s);
497     if (*s != '\0') {
498         uptr->port = (unsigned short)ap_strtol(uptr->port_str, &endstr, 10);
499         if (*endstr == '\0') {
500             return HTTP_OK;
501         }
502         /* Invalid characters after ':' found */
503     }
504     return HTTP_BAD_REQUEST;
505 }
506