~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: 2009-09-24 14:51:06 UTC
  • mfrom: (1.1.12 upstream)
  • mto: (20.2.1 sid)
  • mto: This revision was merged to the branch mainline in revision 21.
  • Revision ID: james.westby@ubuntu.com-20090924145106-38jgrzmj0d73pha5
Tags: 3.1.0.13-1
* Upload to experimental

* New upstream release
  - Fixes Follow-X-Forwarded-For support (Closes: #523943)
  - Adds IPv6 support (Closes: #432351)

* debian/rules
  - Removed obsolete configuration options
  - Enable db and radius basic authentication modules

* debian/patches/01-cf.data.debian
  - Adapted to new upstream version

* debian/patches/02-makefile-defaults
  - Adapted to new upstream version

* debian/{squid.postinst,squid.rc,README.Debian,watch}
  - Updated references to squid 3.1

* debian/squid3.install
  - Install CSS file for error pages
  - Install manual pages for new authentication modules

* debian/squid3-common.install
  - Install documented version of configuration file in /usr/share/doc/squid3

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