~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to srclib/apr-util/uri/apr_uri.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright 2000-2005 The Apache Software Foundation or its licensors, as
 
2
 * applicable.
 
3
 *
 
4
 * Licensed under the Apache License, Version 2.0 (the "License");
 
5
 * you may not use this file except in compliance with the License.
 
6
 * You may obtain a copy of the License at
 
7
 *
 
8
 *     http://www.apache.org/licenses/LICENSE-2.0
 
9
 *
 
10
 * Unless required by applicable law or agreed to in writing, software
 
11
 * distributed under the License is distributed on an "AS IS" BASIS,
 
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
13
 * See the License for the specific language governing permissions and
 
14
 * limitations under the License.
 
15
 */
 
16
 
 
17
/*
 
18
 * apr_uri.c: URI related utility things
 
19
 * 
 
20
 */
 
21
 
 
22
#include <stdlib.h>
 
23
 
 
24
#include "apu.h"
 
25
#include "apr.h"
 
26
#include "apr_general.h"
 
27
#include "apr_strings.h"
 
28
 
 
29
#define APR_WANT_STRFUNC
 
30
#include "apr_want.h"
 
31
 
 
32
#include "apr_uri.h"
 
33
 
 
34
typedef struct schemes_t schemes_t;
 
35
 
 
36
/** Structure to store various schemes and their default ports */
 
37
struct schemes_t {
 
38
    /** The name of the scheme */
 
39
    const char *name;
 
40
    /** The default port for the scheme */
 
41
    apr_port_t default_port;
 
42
};
 
43
 
 
44
/* Some WWW schemes and their default ports; this is basically /etc/services */
 
45
/* This will become global when the protocol abstraction comes */
 
46
/* As the schemes are searched by a linear search, */
 
47
/* they are sorted by their expected frequency */
 
48
static schemes_t schemes[] =
 
49
{
 
50
    {"http",     APR_URI_HTTP_DEFAULT_PORT},
 
51
    {"ftp",      APR_URI_FTP_DEFAULT_PORT},
 
52
    {"https",    APR_URI_HTTPS_DEFAULT_PORT},
 
53
    {"gopher",   APR_URI_GOPHER_DEFAULT_PORT},
 
54
    {"ldap",     APR_URI_LDAP_DEFAULT_PORT},
 
55
    {"nntp",     APR_URI_NNTP_DEFAULT_PORT},
 
56
    {"snews",    APR_URI_SNEWS_DEFAULT_PORT},
 
57
    {"imap",     APR_URI_IMAP_DEFAULT_PORT},
 
58
    {"pop",      APR_URI_POP_DEFAULT_PORT},
 
59
    {"sip",      APR_URI_SIP_DEFAULT_PORT},
 
60
    {"rtsp",     APR_URI_RTSP_DEFAULT_PORT},
 
61
    {"wais",     APR_URI_WAIS_DEFAULT_PORT},
 
62
    {"z39.50r",  APR_URI_WAIS_DEFAULT_PORT},
 
63
    {"z39.50s",  APR_URI_WAIS_DEFAULT_PORT},
 
64
    {"prospero", APR_URI_PROSPERO_DEFAULT_PORT},
 
65
    {"nfs",      APR_URI_NFS_DEFAULT_PORT},
 
66
    {"tip",      APR_URI_TIP_DEFAULT_PORT},
 
67
    {"acap",     APR_URI_ACAP_DEFAULT_PORT},
 
68
    {"telnet",   APR_URI_TELNET_DEFAULT_PORT},
 
69
    {"ssh",      APR_URI_SSH_DEFAULT_PORT},
 
70
    { NULL, 0xFFFF }     /* unknown port */
 
71
};
 
72
 
 
73
APU_DECLARE(apr_port_t) apr_uri_port_of_scheme(const char *scheme_str)
 
74
{
 
75
    schemes_t *scheme;
 
76
 
 
77
    if (scheme_str) {
 
78
        for (scheme = schemes; scheme->name != NULL; ++scheme) {
 
79
            if (strcasecmp(scheme_str, scheme->name) == 0) {
 
80
                return scheme->default_port;
 
81
            }
 
82
        }
 
83
    }
 
84
    return 0;
 
85
}
 
86
 
 
87
/* Unparse a apr_uri_t structure to an URI string.
 
88
 * Optionally suppress the password for security reasons.
 
89
 */
 
90
APU_DECLARE(char *) apr_uri_unparse(apr_pool_t *p, 
 
91
                                    const apr_uri_t *uptr, 
 
92
                                    unsigned flags)
 
93
{
 
94
    char *ret = "";
 
95
 
 
96
    /* If suppressing the site part, omit both user name & scheme://hostname */
 
97
    if (!(flags & APR_URI_UNP_OMITSITEPART)) {
 
98
 
 
99
        /* Construct a "user:password@" string, honoring the passed
 
100
         * APR_URI_UNP_ flags: */
 
101
        if (uptr->user || uptr->password) {
 
102
            ret = apr_pstrcat(p,
 
103
                      (uptr->user     && !(flags & APR_URI_UNP_OMITUSER))
 
104
                          ? uptr->user : "",
 
105
                      (uptr->password && !(flags & APR_URI_UNP_OMITPASSWORD))
 
106
                          ? ":" : "",
 
107
                      (uptr->password && !(flags & APR_URI_UNP_OMITPASSWORD))
 
108
                          ? ((flags & APR_URI_UNP_REVEALPASSWORD)
 
109
                              ? uptr->password : "XXXXXXXX")
 
110
                          : "",
 
111
                      ((uptr->user     && !(flags & APR_URI_UNP_OMITUSER)) ||
 
112
                       (uptr->password && !(flags & APR_URI_UNP_OMITPASSWORD)))
 
113
                          ? "@" : "", 
 
114
                      NULL);
 
115
        }
 
116
 
 
117
        /* Construct scheme://site string */
 
118
        if (uptr->hostname) {
 
119
            int is_default_port;
 
120
            const char *lbrk = "", *rbrk = "";
 
121
 
 
122
            if (strchr(uptr->hostname, ':')) { /* v6 literal */
 
123
                lbrk = "[";
 
124
                rbrk = "]";
 
125
            }
 
126
 
 
127
            is_default_port =
 
128
                (uptr->port_str == NULL ||
 
129
                 uptr->port == 0 ||
 
130
                 uptr->port == apr_uri_port_of_scheme(uptr->scheme));
 
131
 
 
132
            if (uptr->scheme) {
 
133
                ret = apr_pstrcat(p,
 
134
                              uptr->scheme, "://", ret,
 
135
                              lbrk, uptr->hostname, rbrk,
 
136
                              is_default_port ? "" : ":",
 
137
                              is_default_port ? "" : uptr->port_str,
 
138
                              NULL);
 
139
            }
 
140
            else {
 
141
                /* A violation of RFC2396, but it is clear from section 3.2
 
142
                 * that the : belongs above to the scheme, while // belongs
 
143
                 * to the authority, so include the authority prefix while
 
144
                 * omitting the "scheme:" that the user neglected to pass us.
 
145
                 */
 
146
                ret = apr_pstrcat(p,
 
147
                              "//", ret, lbrk, uptr->hostname, rbrk,
 
148
                              is_default_port ? "" : ":",
 
149
                              is_default_port ? "" : uptr->port_str,
 
150
                              NULL);
 
151
            }
 
152
        }
 
153
    }
 
154
 
 
155
    /* Should we suppress all path info? */
 
156
    if (!(flags & APR_URI_UNP_OMITPATHINFO)) {
 
157
        /* Append path, query and fragment strings: */
 
158
        ret = apr_pstrcat(p,
 
159
                          ret,
 
160
                          (uptr->path)
 
161
                              ? uptr->path : "",
 
162
                          (uptr->query    && !(flags & APR_URI_UNP_OMITQUERY))
 
163
                              ? "?" : "",
 
164
                          (uptr->query    && !(flags & APR_URI_UNP_OMITQUERY))
 
165
                              ? uptr->query : "",
 
166
                          (uptr->fragment && !(flags & APR_URI_UNP_OMITQUERY))
 
167
                              ? "#" : NULL,
 
168
                          (uptr->fragment && !(flags & APR_URI_UNP_OMITQUERY))
 
169
                              ? uptr->fragment : NULL,
 
170
                          NULL);
 
171
    }
 
172
    return ret;
 
173
}
 
174
 
 
175
/* Here is the hand-optimized parse_uri_components().  There are some wild
 
176
 * tricks we could pull in assembly language that we don't pull here... like we
 
177
 * can do word-at-time scans for delimiter characters using the same technique
 
178
 * that fast memchr()s use.  But that would be way non-portable. -djg
 
179
 */
 
180
 
 
181
/* We have a apr_table_t that we can index by character and it tells us if the
 
182
 * character is one of the interesting delimiters.  Note that we even get
 
183
 * compares for NUL for free -- it's just another delimiter.
 
184
 */
 
185
 
 
186
#define T_COLON           0x01        /* ':' */
 
187
#define T_SLASH           0x02        /* '/' */
 
188
#define T_QUESTION        0x04        /* '?' */
 
189
#define T_HASH            0x08        /* '#' */
 
190
#define T_NUL             0x80        /* '\0' */
 
191
 
 
192
#if APR_CHARSET_EBCDIC
 
193
/* Delimiter table for the EBCDIC character set */
 
194
static const unsigned char uri_delims[256] = {
 
195
    T_NUL,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
196
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
197
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
198
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
199
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
200
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
201
    0,T_SLASH,0,0,0,0,0,0,0,0,0,0,0,0,0,T_QUESTION,
 
202
    0,0,0,0,0,0,0,0,0,0,T_COLON,T_HASH,0,0,0,0,
 
203
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
204
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
205
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
206
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
207
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
208
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
209
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
210
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
211
};
 
212
#else
 
213
/* Delimiter table for the ASCII character set */
 
214
static const unsigned char uri_delims[256] = {
 
215
    T_NUL,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
216
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
217
    0,0,0,T_HASH,0,0,0,0,0,0,0,0,0,0,0,T_SLASH,
 
218
    0,0,0,0,0,0,0,0,0,0,T_COLON,0,0,0,0,T_QUESTION,
 
219
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
220
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
221
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
222
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
223
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
224
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
225
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
226
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
227
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
228
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
229
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 
230
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
231
};
 
232
#endif
 
233
 
 
234
 
 
235
/* it works like this:
 
236
    if (uri_delims[ch] & NOTEND_foobar) {
 
237
        then we're not at a delimiter for foobar
 
238
    }
 
239
*/
 
240
 
 
241
/* Note that we optimize the scheme scanning here, we cheat and let the
 
242
 * compiler know that it doesn't have to do the & masking.
 
243
 */
 
244
#define NOTEND_SCHEME     (0xff)
 
245
#define NOTEND_HOSTINFO   (T_SLASH | T_QUESTION | T_HASH | T_NUL)
 
246
#define NOTEND_PATH       (T_QUESTION | T_HASH | T_NUL)
 
247
 
 
248
/* parse_uri_components():
 
249
 * Parse a given URI, fill in all supplied fields of a uri_components
 
250
 * structure. This eliminates the necessity of extracting host, port,
 
251
 * path, query info repeatedly in the modules.
 
252
 * Side effects:
 
253
 *  - fills in fields of uri_components *uptr
 
254
 *  - none on any of the r->* fields
 
255
 */
 
256
APU_DECLARE(apr_status_t) apr_uri_parse(apr_pool_t *p, const char *uri, 
 
257
                                        apr_uri_t *uptr)
 
258
{
 
259
    const char *s;
 
260
    const char *s1;
 
261
    const char *hostinfo;
 
262
    char *endstr;
 
263
    int port;
 
264
    int v6_offset1 = 0, v6_offset2 = 0;
 
265
 
 
266
    /* Initialize the structure. parse_uri() and parse_uri_components()
 
267
     * can be called more than once per request.
 
268
     */
 
269
    memset (uptr, '\0', sizeof(*uptr));
 
270
    uptr->is_initialized = 1;
 
271
 
 
272
    /* We assume the processor has a branch predictor like most --
 
273
     * it assumes forward branches are untaken and backwards are taken.  That's
 
274
     * the reason for the gotos.  -djg
 
275
     */
 
276
    if (uri[0] == '/') {
 
277
        /* RFC2396 #4.3 says that two leading slashes mean we have an
 
278
         * authority component, not a path!  Fixing this looks scary
 
279
         * with the gotos here.  But if the existing logic is valid,
 
280
         * then presumably a goto pointing to deal_with_authority works.
 
281
         *
 
282
         * RFC2396 describes this as resolving an ambiguity.  In the
 
283
         * case of three or more slashes there would seem to be no
 
284
         * ambiguity, so it is a path after all.
 
285
         */
 
286
        if (uri[1] == '/' && uri[2] != '/') {
 
287
            s = uri + 2 ;
 
288
            goto deal_with_authority ;
 
289
        }
 
290
 
 
291
deal_with_path:
 
292
        /* we expect uri to point to first character of path ... remember
 
293
         * that the path could be empty -- http://foobar?query for example
 
294
         */
 
295
        s = uri;
 
296
        while ((uri_delims[*(unsigned char *)s] & NOTEND_PATH) == 0) {
 
297
            ++s;
 
298
        }
 
299
        if (s != uri) {
 
300
            uptr->path = apr_pstrmemdup(p, uri, s - uri);
 
301
        }
 
302
        if (*s == 0) {
 
303
            return APR_SUCCESS;
 
304
        }
 
305
        if (*s == '?') {
 
306
            ++s;
 
307
            s1 = strchr(s, '#');
 
308
            if (s1) {
 
309
                uptr->fragment = apr_pstrdup(p, s1 + 1);
 
310
                uptr->query = apr_pstrmemdup(p, s, s1 - s);
 
311
            }
 
312
            else {
 
313
                uptr->query = apr_pstrdup(p, s);
 
314
            }
 
315
            return APR_SUCCESS;
 
316
        }
 
317
        /* otherwise it's a fragment */
 
318
        uptr->fragment = apr_pstrdup(p, s + 1);
 
319
        return APR_SUCCESS;
 
320
    }
 
321
 
 
322
    /* find the scheme: */
 
323
    s = uri;
 
324
    while ((uri_delims[*(unsigned char *)s] & NOTEND_SCHEME) == 0) {
 
325
        ++s;
 
326
    }
 
327
    /* scheme must be non-empty and followed by :// */
 
328
    if (s == uri || s[0] != ':' || s[1] != '/' || s[2] != '/') {
 
329
        goto deal_with_path;        /* backwards predicted taken! */
 
330
    }
 
331
 
 
332
    uptr->scheme = apr_pstrmemdup(p, uri, s - uri);
 
333
    s += 3;
 
334
 
 
335
deal_with_authority:
 
336
    hostinfo = s;
 
337
    while ((uri_delims[*(unsigned char *)s] & NOTEND_HOSTINFO) == 0) {
 
338
        ++s;
 
339
    }
 
340
    uri = s;        /* whatever follows hostinfo is start of uri */
 
341
    uptr->hostinfo = apr_pstrmemdup(p, hostinfo, uri - hostinfo);
 
342
 
 
343
    /* If there's a username:password@host:port, the @ we want is the last @...
 
344
     * too bad there's no memrchr()... For the C purists, note that hostinfo
 
345
     * is definately not the first character of the original uri so therefore
 
346
     * &hostinfo[-1] < &hostinfo[0] ... and this loop is valid C.
 
347
     */
 
348
    do {
 
349
        --s;
 
350
    } while (s >= hostinfo && *s != '@');
 
351
    if (s < hostinfo) {
 
352
        /* again we want the common case to be fall through */
 
353
deal_with_host:
 
354
        /* We expect hostinfo to point to the first character of
 
355
         * the hostname.  If there's a port it is the first colon,
 
356
         * except with IPv6.
 
357
         */
 
358
        if (*hostinfo == '[') {
 
359
            v6_offset1 = 1;
 
360
            v6_offset2 = 2;
 
361
            s = memchr(hostinfo, ']', uri - hostinfo);
 
362
            if (s == NULL) {
 
363
                return APR_EGENERAL;
 
364
            }
 
365
            if (*++s != ':') {
 
366
                s = NULL; /* no port */
 
367
            }
 
368
        }
 
369
        else {
 
370
            s = memchr(hostinfo, ':', uri - hostinfo);
 
371
        }
 
372
        if (s == NULL) {
 
373
            /* we expect the common case to have no port */
 
374
            uptr->hostname = apr_pstrmemdup(p,
 
375
                                            hostinfo + v6_offset1,
 
376
                                            uri - hostinfo - v6_offset2);
 
377
            goto deal_with_path;
 
378
        }
 
379
        uptr->hostname = apr_pstrmemdup(p,
 
380
                                        hostinfo + v6_offset1,
 
381
                                        s - hostinfo - v6_offset2);
 
382
        ++s;
 
383
        uptr->port_str = apr_pstrmemdup(p, s, uri - s);
 
384
        if (uri != s) {
 
385
            port = strtol(uptr->port_str, &endstr, 10);
 
386
            uptr->port = port;
 
387
            if (*endstr == '\0') {
 
388
                goto deal_with_path;
 
389
            }
 
390
            /* Invalid characters after ':' found */
 
391
            return APR_EGENERAL;
 
392
        }
 
393
        uptr->port = apr_uri_port_of_scheme(uptr->scheme);
 
394
        goto deal_with_path;
 
395
    }
 
396
 
 
397
    /* first colon delimits username:password */
 
398
    s1 = memchr(hostinfo, ':', s - hostinfo);
 
399
    if (s1) {
 
400
        uptr->user = apr_pstrmemdup(p, hostinfo, s1 - hostinfo);
 
401
        ++s1;
 
402
        uptr->password = apr_pstrmemdup(p, s1, s - s1);
 
403
    }
 
404
    else {
 
405
        uptr->user = apr_pstrmemdup(p, hostinfo, s - hostinfo);
 
406
    }
 
407
    hostinfo = s + 1;
 
408
    goto deal_with_host;
 
409
}
 
410
 
 
411
/* Special case for CONNECT parsing: it comes with the hostinfo part only */
 
412
/* See the INTERNET-DRAFT document "Tunneling SSL Through a WWW Proxy"
 
413
 * currently at http://www.mcom.com/newsref/std/tunneling_ssl.html
 
414
 * for the format of the "CONNECT host:port HTTP/1.0" request
 
415
 */
 
416
APU_DECLARE(apr_status_t) apr_uri_parse_hostinfo(apr_pool_t *p, 
 
417
                                                 const char *hostinfo, 
 
418
                                                 apr_uri_t *uptr)
 
419
{
 
420
    const char *s;
 
421
    char *endstr;
 
422
    const char *rsb;
 
423
    int v6_offset1 = 0;
 
424
 
 
425
    /* Initialize the structure. parse_uri() and parse_uri_components()
 
426
     * can be called more than once per request.
 
427
     */
 
428
    memset(uptr, '\0', sizeof(*uptr));
 
429
    uptr->is_initialized = 1;
 
430
    uptr->hostinfo = apr_pstrdup(p, hostinfo);
 
431
 
 
432
    /* We expect hostinfo to point to the first character of
 
433
     * the hostname.  There must be a port, separated by a colon
 
434
     */
 
435
    if (*hostinfo == '[') {
 
436
        if ((rsb = strchr(hostinfo, ']')) == NULL ||
 
437
            *(rsb + 1) != ':') {
 
438
            return APR_EGENERAL;
 
439
        }
 
440
        /* literal IPv6 address */
 
441
        s = rsb + 1;
 
442
        ++hostinfo;
 
443
        v6_offset1 = 1;
 
444
    }
 
445
    else {
 
446
        s = strchr(hostinfo, ':');
 
447
    }
 
448
    if (s == NULL) {
 
449
        return APR_EGENERAL;
 
450
    }
 
451
    uptr->hostname = apr_pstrndup(p, hostinfo, s - hostinfo - v6_offset1);
 
452
    ++s;
 
453
    uptr->port_str = apr_pstrdup(p, s);
 
454
    if (*s != '\0') {
 
455
        uptr->port = (unsigned short) strtol(uptr->port_str, &endstr, 10);
 
456
        if (*endstr == '\0') {
 
457
            return APR_SUCCESS;
 
458
        }
 
459
        /* Invalid characters after ':' found */
 
460
    }
 
461
    return APR_EGENERAL;
 
462
}