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