~clint-fewbar/ubuntu/precise/squid3/ignore-sighup-early

« back to all changes in this revision

Viewing changes to src/url.cc

  • Committer: Bazaar Package Importer
  • Author(s): Luigi Gangitano
  • Date: 2010-05-04 11:15:49 UTC
  • mfrom: (1.3.1 upstream)
  • mto: (20.3.1 squeeze) (21.2.1 sid)
  • mto: This revision was merged to the branch mainline in revision 21.
  • Revision ID: james.westby@ubuntu.com-20100504111549-1apjh2g5sndki4te
Tags: upstream-3.1.3
ImportĀ upstreamĀ versionĀ 3.1.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
 
2
2
/*
3
 
 * $Id: url.cc,v 1.162 2007/05/29 13:31:41 amosjeffries Exp $
 
3
 * $Id$
4
4
 *
5
5
 * DEBUG: section 23    URL Parsing
6
6
 * AUTHOR: Duane Wessels
21
21
 *  it under the terms of the GNU General Public License as published by
22
22
 *  the Free Software Foundation; either version 2 of the License, or
23
23
 *  (at your option) any later version.
24
 
 *  
 
24
 *
25
25
 *  This program is distributed in the hope that it will be useful,
26
26
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
27
27
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28
28
 *  GNU General Public License for more details.
29
 
 *  
 
29
 *
30
30
 *  You should have received a copy of the GNU General Public License
31
31
 *  along with this program; if not, write to the Free Software
32
32
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
36
36
#include "URL.h"
37
37
#include "HttpRequest.h"
38
38
#include "URLScheme.h"
 
39
#include "rfc1738.h"
39
40
 
40
 
static HttpRequest *urnParse(method_t method, char *urn);
 
41
static HttpRequest *urnParse(const HttpRequestMethod& method, char *urn);
41
42
static const char valid_hostname_chars_u[] =
42
43
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
43
44
    "abcdefghijklmnopqrstuvwxyz"
44
45
    "0123456789-._"
 
46
#if USE_IPV6
 
47
    "[:]"
 
48
#endif
45
49
    ;
46
50
static const char valid_hostname_chars[] =
47
51
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
48
52
    "abcdefghijklmnopqrstuvwxyz"
49
53
    "0123456789-."
 
54
#if USE_IPV6
 
55
    "[:]"
 
56
#endif
50
57
    ;
51
58
 
52
59
void
81
88
    /* more cases? */
82
89
}
83
90
 
84
 
/*
 
91
/**
85
92
 * urlParseProtocol() takes begin (b) and end (e) pointers, but for
86
93
 * backwards compatibility, e defaults to NULL, in which case we
87
94
 * assume b is NULL-terminated.
174
181
 * If the 'request' arg is non-NULL, put parsed values there instead
175
182
 * of allocating a new HttpRequest.
176
183
 *
177
 
 * This abuses HttpRequest as a way of representing the parsed url 
 
184
 * This abuses HttpRequest as a way of representing the parsed url
178
185
 * and its components.
179
186
 * method is used to switch parsers and to init the HttpRequest.
180
187
 * If method is METHOD_CONNECT, then rather than a URL a hostname:port is
181
188
 * looked for.
182
189
 * The url is non const so that if its too long we can NULL-terminate it in place.
183
190
 */
 
191
 
 
192
/*
 
193
 * This routine parses a URL. Its assumed that the URL is complete -
 
194
 * ie, the end of the string is the end of the URL. Don't pass a partial
 
195
 * URL here as this routine doesn't have any way of knowing whether
 
196
 * its partial or not (ie, it handles the case of no trailing slash as
 
197
 * being "end of host with implied path of /".
 
198
 */
184
199
HttpRequest *
185
 
urlParse(method_t method, char *url, HttpRequest *request)
 
200
urlParse(const HttpRequestMethod& method, char *url, HttpRequest *request)
186
201
{
187
202
    LOCAL_ARRAY(char, proto, MAX_URL);
188
203
    LOCAL_ARRAY(char, login, MAX_URL);
193
208
    int port;
194
209
    protocol_t protocol = PROTO_NONE;
195
210
    int l;
 
211
    int i;
 
212
    const char *src;
 
213
    char *dst;
196
214
    proto[0] = host[0] = urlpath[0] = login[0] = '\0';
197
215
 
198
216
    if ((l = strlen(url)) + Config.appendDomainLen > (MAX_URL - 1)) {
201
219
        debugs(23, 1, "urlParse: URL too large (" << l << " bytes)");
202
220
        return NULL;
203
221
    }
204
 
 
205
222
    if (method == METHOD_CONNECT) {
206
223
        port = CONNECT_PORT;
207
224
 
208
 
        if (sscanf(url, "%[^:]:%d", host, &port) < 1)
209
 
            return NULL;
 
225
        if (sscanf(url, "[%[^]]]:%d", host, &port) < 1)
 
226
            if (sscanf(url, "%[^:]:%d", host, &port) < 1)
 
227
                return NULL;
 
228
 
210
229
    } else if (!strncmp(url, "urn:", 4)) {
211
230
        return urnParse(method, url);
212
231
    } else {
213
 
        if (sscanf(url, "%[^:]://%[^/]%[^\r\n]", proto, host, urlpath) < 2)
214
 
            return NULL;
 
232
        /* Parse the URL: */
 
233
        src = url;
 
234
        i = 0;
 
235
        /* Find first : - everything before is protocol */
 
236
        for (i = 0, dst = proto; i < l && *src != ':'; i++, src++, dst++) {
 
237
            *dst = *src;
 
238
        }
 
239
        if (i >= l)
 
240
            return NULL;
 
241
        *dst = '\0';
 
242
 
 
243
        /* Then its :// */
 
244
        /* (XXX yah, I'm not checking we've got enough data left before checking the array..) */
 
245
        if (*src != ':' || *(src + 1) != '/' || *(src + 2) != '/')
 
246
            return NULL;
 
247
        i += 3;
 
248
        src += 3;
 
249
 
 
250
        /* Then everything until first /; thats host (and port; which we'll look for here later) */
 
251
        /* bug 1881: If we don't get a "/" then we imply it was there */
 
252
        for (dst = host; i < l && *src != '/' && *src != '\0'; i++, src++, dst++) {
 
253
            *dst = *src;
 
254
        }
 
255
 
 
256
        /*
 
257
         * We can't check for "i >= l" here because we could be at the end of the line
 
258
         * and have a perfectly valid URL w/ no trailing '/'. In this case we assume we've
 
259
         * been -given- a valid URL and the path is just '/'.
 
260
         */
 
261
        if (i > l)
 
262
            return NULL;
 
263
        *dst = '\0';
 
264
 
 
265
        /* Then everything from / (inclusive) until \r\n or \0 - thats urlpath */
 
266
        for (dst = urlpath; i < l && *src != '\r' && *src != '\n' && *src != '\0'; i++, src++, dst++) {
 
267
            *dst = *src;
 
268
        }
 
269
 
 
270
        /* We -could- be at the end of the buffer here */
 
271
        if (i > l)
 
272
            return NULL;
 
273
        /* If the URL path is empty we set it to be "/" */
 
274
        if (dst == urlpath) {
 
275
            *(dst++) = '/';
 
276
        }
 
277
        *dst = '\0';
215
278
 
216
279
        protocol = urlParseProtocol(proto);
217
 
 
218
280
        port = urlDefaultPort(protocol);
219
281
 
220
 
        /* Is there any login informaiton? */
 
282
        /* Is there any login information? (we should eventually parse it above) */
221
283
        if ((t = strrchr(host, '@'))) {
222
284
            strcpy((char *) login, (char *) host);
223
285
            t = strrchr(login, '@');
225
287
            strcpy((char *) host, t + 1);
226
288
        }
227
289
 
228
 
        if ((t = strrchr(host, ':'))) {
229
 
            *t++ = '\0';
230
 
 
231
 
            if (*t != '\0')
232
 
                port = atoi(t);
 
290
        /* Is there any host information? (we should eventually parse it above) */
 
291
        if (*host == '[') {
 
292
            /* strip any IPA brackets. valid under IPv6. */
 
293
            dst = host;
 
294
#if USE_IPV6
 
295
            /* only for IPv6 sadly, pre-IPv6/URL code can't handle the clean result properly anyway. */
 
296
            src = host;
 
297
            src++;
 
298
            l = strlen(host);
 
299
            i = 1;
 
300
            for (; i < l && *src != ']' && *src != '\0'; i++, src++, dst++) {
 
301
                *dst = *src;
 
302
            }
 
303
 
 
304
            /* we moved in-place, so truncate the actual hostname found */
 
305
            *(dst++) = '\0';
 
306
#else
 
307
            /* IPv4-pure needs to skip the whole hostname to ']' inclusive for now */
 
308
            while (*dst != '\0' && *dst != ']') dst++;
 
309
#endif
 
310
 
 
311
            /* skip ahead to either start of port, or original EOS */
 
312
            while (*dst != '\0' && *dst != ':') dst++;
 
313
            t = dst;
 
314
        } else {
 
315
            t = strrchr(host, ':');
 
316
 
 
317
            if (t != strchr(host,':') ) {
 
318
                /* RFC 2732 states IPv6 "SHOULD" be bracketed. allowing for times when its not. */
 
319
                /* RFC 3986 'update' simply modifies this to an "is" with no emphasis at all! */
 
320
                /* therefore we MUST accept the case where they are not bracketed at all. */
 
321
                t = NULL;
 
322
            }
 
323
        }
 
324
 
 
325
        if (t && *t == ':') {
 
326
            *t = '\0';
 
327
            t++;
 
328
            port = atoi(t);
233
329
        }
234
330
    }
235
331
 
239
335
    if (stringHasWhitespace(host)) {
240
336
        if (URI_WHITESPACE_STRIP == Config.uri_whitespace) {
241
337
            t = q = host;
242
 
 
243
338
            while (*t) {
244
339
                if (!xisspace(*t))
245
340
                    *q++ = *t;
246
 
 
247
341
                t++;
248
342
            }
249
 
 
250
343
            *q = '\0';
251
344
        }
252
345
    }
253
346
 
 
347
    debugs(23, 3, "urlParse: Split URL '" << url << "' into proto='" << proto << "', host='" << host << "', port='" << port << "', path='" << urlpath << "'");
 
348
 
254
349
    if (Config.onoff.check_hostnames && strspn(host, Config.onoff.allow_underscore ? valid_hostname_chars_u : valid_hostname_chars) != strlen(host)) {
255
350
        debugs(23, 1, "urlParse: Illegal character in hostname '" << host << "'");
256
351
        return NULL;
257
352
    }
258
353
 
259
 
#if DONT_DO_THIS_IT_BREAKS_SEMANTIC_TRANSPARENCY
 
354
    if (Config.appendDomain && !strchr(host, '.'))
 
355
        strncat(host, Config.appendDomain, SQUIDHOSTNAMELEN - strlen(host) - 1);
 
356
 
260
357
    /* remove trailing dots from hostnames */
261
358
    while ((l = strlen(host)) > 0 && host[--l] == '.')
262
359
        host[l] = '\0';
263
360
 
264
 
    /* remove duplicate dots */
265
 
    while ((t = strstr(host, "..")))
266
 
        xmemmove(t, t + 1, strlen(t));
267
 
 
268
 
#endif
269
 
 
270
 
    if (Config.appendDomain && !strchr(host, '.'))
271
 
        strncat(host, Config.appendDomain, SQUIDHOSTNAMELEN - strlen(host) - 1);
 
361
    /* reject duplicate or leading dots */
 
362
    if (strstr(host, "..") || *host == '.') {
 
363
        debugs(23, 1, "urlParse: Illegal hostname '" << host << "'");
 
364
        return NULL;
 
365
    }
272
366
 
273
367
    if (port < 1 || port > 65535) {
274
368
        debugs(23, 3, "urlParse: Invalid port '" << port << "'");
282
376
        debugs(23, 0, "urlParse: Deny access to port " << port);
283
377
        return NULL;
284
378
    }
 
379
#endif
285
380
 
286
 
#endif
287
381
    if (stringHasWhitespace(urlpath)) {
288
382
        debugs(23, 2, "urlParse: URI has whitespace: {" << url << "}");
289
383
 
305
399
            break;
306
400
 
307
401
        case URI_WHITESPACE_STRIP:
308
 
 
309
402
        default:
310
403
            t = q = urlpath;
311
 
 
312
404
            while (*t) {
313
405
                if (!xisspace(*t))
314
406
                    *q++ = *t;
315
 
 
316
407
                t++;
317
408
            }
318
 
 
319
409
            *q = '\0';
320
410
        }
321
411
    }
326
416
        request->initHTTP(method, protocol, urlpath);
327
417
    }
328
418
 
329
 
    xstrncpy(request->host, host, SQUIDHOSTNAMELEN);
 
419
    request->SetHost(host);
330
420
    xstrncpy(request->login, login, MAX_LOGIN_SZ);
331
421
    request->port = (u_short) port;
332
422
    return request;
333
423
}
334
424
 
335
425
static HttpRequest *
336
 
urnParse(method_t method, char *urn)
 
426
urnParse(const HttpRequestMethod& method, char *urn)
337
427
{
338
428
    debugs(50, 5, "urnParse: " << urn);
339
429
    return new HttpRequest(method, PROTO_URN, urn + 4);
343
433
urlCanonical(HttpRequest * request)
344
434
{
345
435
    LOCAL_ARRAY(char, portbuf, 32);
 
436
/// \todo AYJ: Performance: making this a ptr and allocating when needed will be better than a write and future xstrdup().
346
437
    LOCAL_ARRAY(char, urlbuf, MAX_URL);
347
438
 
348
439
    if (request->canonical)
349
440
        return request->canonical;
350
441
 
351
442
    if (request->protocol == PROTO_URN) {
352
 
        snprintf(urlbuf, MAX_URL, "urn:%s", request->urlpath.buf());
 
443
        snprintf(urlbuf, MAX_URL, "urn:" SQUIDSTRINGPH,
 
444
                 SQUIDSTRINGPRINT(request->urlpath));
353
445
    } else {
354
 
        switch (request->method) {
 
446
/// \todo AYJ: this could use "if..else and method == METHOD_CONNECT" easier.
 
447
        switch (request->method.id()) {
355
448
 
356
449
        case METHOD_CONNECT:
357
 
            snprintf(urlbuf, MAX_URL, "%s:%d", request->host, request->port);
 
450
            snprintf(urlbuf, MAX_URL, "%s:%d", request->GetHost(), request->port);
358
451
            break;
359
452
 
360
453
        default:
363
456
            if (request->port != urlDefaultPort(request->protocol))
364
457
                snprintf(portbuf, 32, ":%d", request->port);
365
458
 
366
 
            snprintf(urlbuf, MAX_URL, "%s://%s%s%s%s%s",
 
459
            snprintf(urlbuf, MAX_URL, "%s://%s%s%s%s" SQUIDSTRINGPH,
367
460
                     ProtocolStr[request->protocol],
368
461
                     request->login,
369
462
                     *request->login ? "@" : null_string,
370
 
                     request->host,
 
463
                     request->GetHost(),
371
464
                     portbuf,
372
 
                     request->urlpath.buf());
 
465
                     SQUIDSTRINGPRINT(request->urlpath));
373
466
 
374
467
            break;
375
468
        }
378
471
    return (request->canonical = xstrdup(urlbuf));
379
472
}
380
473
 
 
474
/** \todo AYJ: Performance: This is an *almost* duplicate of urlCanonical. But elides the query-string.
 
475
 *        After copying it on in the first place! Would be less code to merge the two with a flag parameter.
 
476
 *        and never copy the query-string part in the first place
 
477
 */
381
478
char *
382
479
urlCanonicalClean(const HttpRequest * request)
383
480
{
387
484
    char *t;
388
485
 
389
486
    if (request->protocol == PROTO_URN) {
390
 
        snprintf(buf, MAX_URL, "urn:%s", request->urlpath.buf());
 
487
        snprintf(buf, MAX_URL, "urn:" SQUIDSTRINGPH,
 
488
                 SQUIDSTRINGPRINT(request->urlpath));
391
489
    } else {
392
 
        switch (request->method) {
 
490
/// \todo AYJ: this could use "if..else and method == METHOD_CONNECT" easier.
 
491
        switch (request->method.id()) {
393
492
 
394
493
        case METHOD_CONNECT:
395
 
            snprintf(buf, MAX_URL, "%s:%d", request->host, request->port);
 
494
            snprintf(buf, MAX_URL, "%s:%d",
 
495
                     request->GetHost(),
 
496
                     request->port);
396
497
            break;
397
498
 
398
499
        default:
412
513
                strcat(loginbuf, "@");
413
514
            }
414
515
 
415
 
            snprintf(buf, MAX_URL, "%s://%s%s%s%s",
 
516
            snprintf(buf, MAX_URL, "%s://%s%s%s" SQUIDSTRINGPH,
416
517
                     ProtocolStr[request->protocol],
417
518
                     loginbuf,
418
 
                     request->host,
 
519
                     request->GetHost(),
419
520
                     portbuf,
420
 
                     request->urlpath.buf());
 
521
                     SQUIDSTRINGPRINT(request->urlpath));
421
522
            /*
422
523
             * strip arguments AFTER a question-mark
423
524
             */
448
549
    LOCAL_ARRAY(char, buf, MAX_URL);
449
550
 
450
551
    // method CONNECT and port HTTPS
451
 
    if(request->method == METHOD_CONNECT && request->port == 443) {
452
 
        snprintf(buf, MAX_URL, "https://%s/*", request->host);
 
552
    if (request->method == METHOD_CONNECT && request->port == 443) {
 
553
        snprintf(buf, MAX_URL, "https://%s/*", request->GetHost());
453
554
        return buf;
454
555
    }
455
556
 
459
560
 
460
561
 
461
562
/*
 
563
 * Test if a URL is relative.
 
564
 *
 
565
 * RFC 2396, Section 5 (Page 17) implies that in a relative URL, a '/' will
 
566
 * appear before a ':'.
 
567
 */
 
568
bool
 
569
urlIsRelative(const char *url)
 
570
{
 
571
    const char *p;
 
572
 
 
573
    if (url == NULL) {
 
574
        return (false);
 
575
    }
 
576
    if (*url == '\0') {
 
577
        return (false);
 
578
    }
 
579
 
 
580
    for (p = url; *p != '\0' && *p != ':' && *p != '/'; p++);
 
581
 
 
582
    if (*p == ':') {
 
583
        return (false);
 
584
    }
 
585
    return (true);
 
586
}
 
587
 
 
588
/*
 
589
 * Convert a relative URL to an absolute URL using the context of a given
 
590
 * request.
 
591
 *
 
592
 * It is assumed that you have already ensured that the URL is relative.
 
593
 *
 
594
 * If NULL is returned it is an indication that the method in use in the
 
595
 * request does not distinguish between relative and absolute and you should
 
596
 * use the url unchanged.
 
597
 *
 
598
 * If non-NULL is returned, it is up to the caller to free the resulting
 
599
 * memory using safe_free().
 
600
 */
 
601
char *
 
602
urlMakeAbsolute(const HttpRequest * req, const char *relUrl)
 
603
{
 
604
 
 
605
    if (req->method.id() == METHOD_CONNECT) {
 
606
        return (NULL);
 
607
    }
 
608
 
 
609
    char *urlbuf = (char *)xmalloc(MAX_URL * sizeof(char));
 
610
 
 
611
    if (req->protocol == PROTO_URN) {
 
612
        snprintf(urlbuf, MAX_URL, "urn:" SQUIDSTRINGPH,
 
613
                 SQUIDSTRINGPRINT(req->urlpath));
 
614
        return (urlbuf);
 
615
    }
 
616
 
 
617
    size_t urllen;
 
618
 
 
619
    if (req->port != urlDefaultPort(req->protocol)) {
 
620
        urllen = snprintf(urlbuf, MAX_URL, "%s://%s%s%s:%d",
 
621
                          ProtocolStr[req->protocol],
 
622
                          req->login,
 
623
                          *req->login ? "@" : null_string,
 
624
                          req->GetHost(),
 
625
                          req->port
 
626
                         );
 
627
    } else {
 
628
        urllen = snprintf(urlbuf, MAX_URL, "%s://%s%s%s",
 
629
                          ProtocolStr[req->protocol],
 
630
                          req->login,
 
631
                          *req->login ? "@" : null_string,
 
632
                          req->GetHost()
 
633
                         );
 
634
    }
 
635
 
 
636
    if (relUrl[0] == '/') {
 
637
        strncpy(&urlbuf[urllen], relUrl, MAX_URL - urllen - 1);
 
638
    } else {
 
639
        const char *path = req->urlpath.termedBuf();
 
640
        const char *last_slash = strrchr(path, '/');
 
641
 
 
642
        if (last_slash == NULL) {
 
643
            urlbuf[urllen++] = '/';
 
644
            strncpy(&urlbuf[urllen], relUrl, MAX_URL - urllen - 1);
 
645
        } else {
 
646
            last_slash++;
 
647
            size_t pathlen = last_slash - path;
 
648
            if (pathlen > MAX_URL - urllen - 1) {
 
649
                pathlen = MAX_URL - urllen - 1;
 
650
            }
 
651
            strncpy(&urlbuf[urllen], path, pathlen);
 
652
            urllen += pathlen;
 
653
            if (urllen + 1 < MAX_URL) {
 
654
                strncpy(&urlbuf[urllen], relUrl, MAX_URL - urllen - 1);
 
655
            }
 
656
        }
 
657
    }
 
658
 
 
659
    return (urlbuf);
 
660
}
 
661
 
 
662
/*
462
663
 * matchDomainName() compares a hostname with a domainname according
463
664
 * to the following rules:
464
 
 * 
 
665
 *
465
666
 *    HOST          DOMAIN        MATCH?
466
667
 * ------------- -------------    ------
467
668
 *    foo.com       foo.com         YES
682
883
 
683
884
    while (*hostStart != '\0' && *hostStart == '/')
684
885
        ++hostStart;
 
886
 
 
887
#if USE_IPV6
 
888
    if (*hostStart == ']')
 
889
        ++hostStart;
 
890
#endif
 
891
 
685
892
}
686
893
 
687
894
void
692
899
    if ((t = strchr(Host, '/')))
693
900
        *t = '\0';
694
901
 
695
 
    if ((t = strchr(Host, ':')))
696
 
        *t = '\0';
 
902
    if ((t = strrchr(Host, ':')))
 
903
        *t = '\0';
 
904
 
 
905
#if USE_IPV6
 
906
    if ((t = strchr(Host, ']')))
 
907
        *t = '\0';
 
908
#endif
 
909
 
697
910
}
698
911
 
699
912
void