]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/url.cc
3 * DEBUG: section 23 URL Parsing
4 * AUTHOR: Duane Wessels
6 * SQUID Web Proxy Cache http://www.squid-cache.org/
7 * ----------------------------------------------------------
9 * Squid is the result of efforts by numerous individuals from
10 * the Internet community; see the CONTRIBUTORS file for full
11 * details. Many organizations have provided support for Squid's
12 * development; see the SPONSORS file for full details. Squid is
13 * Copyrighted (C) 2001 by the Regents of the University of
14 * California; see the COPYRIGHT file for full details. Squid
15 * incorporates software developed and/or copyrighted by other
16 * sources; see the CREDITS file for full details.
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
36 #include "HttpRequest.h"
38 #include "SquidString.h"
40 #include "URLScheme.h"
42 static HttpRequest
*urlParseFinish(const HttpRequestMethod
& method
,
43 const AnyP::ProtocolType protocol
,
44 const char *const urlpath
,
45 const char *const host
,
46 const char *const login
,
48 HttpRequest
*request
);
49 static HttpRequest
*urnParse(const HttpRequestMethod
& method
, char *urn
, HttpRequest
*request
);
50 static const char valid_hostname_chars_u
[] =
51 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
52 "abcdefghijklmnopqrstuvwxyz"
56 static const char valid_hostname_chars
[] =
57 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
58 "abcdefghijklmnopqrstuvwxyz"
66 debugs(23, 5, "urlInitialize: Initializing...");
67 /* this ensures that the number of protocol strings is the same as
68 * the enum slots allocated because the last enum is always 'MAX'.
70 assert(strcmp(AnyP::ProtocolType_str
[AnyP::PROTO_MAX
], "MAX") == 0);
72 * These test that our matchDomainName() function works the
73 * way we expect it to.
75 assert(0 == matchDomainName("foo.com", "foo.com"));
76 assert(0 == matchDomainName(".foo.com", "foo.com"));
77 assert(0 == matchDomainName("foo.com", ".foo.com"));
78 assert(0 == matchDomainName(".foo.com", ".foo.com"));
79 assert(0 == matchDomainName("x.foo.com", ".foo.com"));
80 assert(0 != matchDomainName("x.foo.com", "foo.com"));
81 assert(0 != matchDomainName("foo.com", "x.foo.com"));
82 assert(0 != matchDomainName("bar.com", "foo.com"));
83 assert(0 != matchDomainName(".bar.com", "foo.com"));
84 assert(0 != matchDomainName(".bar.com", ".foo.com"));
85 assert(0 != matchDomainName("bar.com", ".foo.com"));
86 assert(0 < matchDomainName("zzz.com", "foo.com"));
87 assert(0 > matchDomainName("aaa.com", "foo.com"));
88 assert(0 == matchDomainName("FOO.com", "foo.COM"));
89 assert(0 < matchDomainName("bfoo.com", "afoo.com"));
90 assert(0 > matchDomainName("afoo.com", "bfoo.com"));
91 assert(0 < matchDomainName("x-foo.com", ".foo.com"));
96 * urlParseProtocol() takes begin (b) and end (e) pointers, but for
97 * backwards compatibility, e defaults to NULL, in which case we
98 * assume b is NULL-terminated.
101 urlParseProtocol(const char *b
, const char *e
)
104 * if e is NULL, b must be NULL terminated and we
105 * make e point to the first whitespace character
110 e
= b
+ strcspn(b
, ":");
114 /* test common stuff first */
116 if (strncasecmp(b
, "http", len
) == 0)
117 return AnyP::PROTO_HTTP
;
119 if (strncasecmp(b
, "ftp", len
) == 0)
120 return AnyP::PROTO_FTP
;
122 if (strncasecmp(b
, "https", len
) == 0)
123 return AnyP::PROTO_HTTPS
;
125 if (strncasecmp(b
, "file", len
) == 0)
126 return AnyP::PROTO_FTP
;
128 if (strncasecmp(b
, "coap", len
) == 0)
129 return AnyP::PROTO_COAP
;
131 if (strncasecmp(b
, "coaps", len
) == 0)
132 return AnyP::PROTO_COAPS
;
134 if (strncasecmp(b
, "gopher", len
) == 0)
135 return AnyP::PROTO_GOPHER
;
137 if (strncasecmp(b
, "wais", len
) == 0)
138 return AnyP::PROTO_WAIS
;
140 if (strncasecmp(b
, "cache_object", len
) == 0)
141 return AnyP::PROTO_CACHE_OBJECT
;
143 if (strncasecmp(b
, "urn", len
) == 0)
144 return AnyP::PROTO_URN
;
146 if (strncasecmp(b
, "whois", len
) == 0)
147 return AnyP::PROTO_WHOIS
;
149 if (strncasecmp(b
, "internal", len
) == 0)
150 return AnyP::PROTO_INTERNAL
;
152 return AnyP::PROTO_NONE
;
156 urlDefaultPort(AnyP::ProtocolType p
)
160 case AnyP::PROTO_HTTP
:
163 case AnyP::PROTO_HTTPS
:
166 case AnyP::PROTO_FTP
:
169 case AnyP::PROTO_COAP
:
170 case AnyP::PROTO_COAPS
:
171 // coaps:// default is TBA as of draft-ietf-core-coap-08.
172 // Assuming IANA policy of allocating same port for base and TLS protocol versions will occur.
175 case AnyP::PROTO_GOPHER
:
178 case AnyP::PROTO_WAIS
:
181 case AnyP::PROTO_CACHE_OBJECT
:
183 case AnyP::PROTO_INTERNAL
:
184 return CACHE_HTTP_PORT
;
186 case AnyP::PROTO_WHOIS
:
197 * If the 'request' arg is non-NULL, put parsed values there instead
198 * of allocating a new HttpRequest.
200 * This abuses HttpRequest as a way of representing the parsed url
201 * and its components.
202 * method is used to switch parsers and to init the HttpRequest.
203 * If method is METHOD_CONNECT, then rather than a URL a hostname:port is
205 * The url is non const so that if its too long we can NULL-terminate it in place.
209 * This routine parses a URL. Its assumed that the URL is complete -
210 * ie, the end of the string is the end of the URL. Don't pass a partial
211 * URL here as this routine doesn't have any way of knowing whether
212 * its partial or not (ie, it handles the case of no trailing slash as
213 * being "end of host with implied path of /".
216 urlParse(const HttpRequestMethod
& method
, char *url
, HttpRequest
*request
)
218 LOCAL_ARRAY(char, proto
, MAX_URL
);
219 LOCAL_ARRAY(char, login
, MAX_URL
);
220 LOCAL_ARRAY(char, host
, MAX_URL
);
221 LOCAL_ARRAY(char, urlpath
, MAX_URL
);
225 AnyP::ProtocolType protocol
= AnyP::PROTO_NONE
;
230 proto
[0] = host
[0] = urlpath
[0] = login
[0] = '\0';
232 if ((l
= strlen(url
)) + Config
.appendDomainLen
> (MAX_URL
- 1)) {
233 /* terminate so it doesn't overflow other buffers */
234 *(url
+ (MAX_URL
>> 1)) = '\0';
235 debugs(23, DBG_IMPORTANT
, "urlParse: URL too large (" << l
<< " bytes)");
238 if (method
== METHOD_CONNECT
) {
241 if (sscanf(url
, "[%[^]]]:%d", host
, &port
) < 1)
242 if (sscanf(url
, "%[^:]:%d", host
, &port
) < 1)
245 } else if ((method
== METHOD_OPTIONS
|| method
== METHOD_TRACE
) &&
246 strcmp(url
, "*") == 0) {
247 protocol
= AnyP::PROTO_HTTP
;
248 port
= urlDefaultPort(protocol
);
249 return urlParseFinish(method
, protocol
, url
, host
, login
, port
, request
);
250 } else if (!strncmp(url
, "urn:", 4)) {
251 return urnParse(method
, url
, request
);
256 /* Find first : - everything before is protocol */
257 for (i
= 0, dst
= proto
; i
< l
&& *src
!= ':'; ++i
, ++src
, ++dst
) {
265 if ((i
+3) > l
|| *src
!= ':' || *(src
+ 1) != '/' || *(src
+ 2) != '/')
270 /* Then everything until first /; thats host (and port; which we'll look for here later) */
271 // bug 1881: If we don't get a "/" then we imply it was there
272 // bug 3074: We could just be given a "?" or "#". These also imply "/"
273 // bug 3233: whitespace is also a hostname delimiter.
274 for (dst
= host
; i
< l
&& *src
!= '/' && *src
!= '?' && *src
!= '#' && *src
!= '\0' && !xisspace(*src
); ++i
, ++src
, ++dst
) {
279 * We can't check for "i >= l" here because we could be at the end of the line
280 * and have a perfectly valid URL w/ no trailing '/'. In this case we assume we've
281 * been -given- a valid URL and the path is just '/'.
287 // bug 3074: received 'path' starting with '?', '#', or '\0' implies '/'
288 if (*src
== '?' || *src
== '#' || *src
== '\0') {
294 /* Then everything from / (inclusive) until \r\n or \0 - thats urlpath */
295 for (; i
< l
&& *src
!= '\r' && *src
!= '\n' && *src
!= '\0'; ++i
, ++src
, ++dst
) {
299 /* We -could- be at the end of the buffer here */
302 /* If the URL path is empty we set it to be "/" */
303 if (dst
== urlpath
) {
309 protocol
= urlParseProtocol(proto
);
310 port
= urlDefaultPort(protocol
);
312 /* Is there any login information? (we should eventually parse it above) */
313 if ((t
= strrchr(host
, '@'))) {
314 strcpy((char *) login
, (char *) host
);
315 t
= strrchr(login
, '@');
317 strcpy((char *) host
, t
+ 1);
320 /* Is there any host information? (we should eventually parse it above) */
322 /* strip any IPA brackets. valid under IPv6. */
324 /* only for IPv6 sadly, pre-IPv6/URL code can't handle the clean result properly anyway. */
329 for (; i
< l
&& *src
!= ']' && *src
!= '\0'; ++i
, ++src
, ++dst
) {
333 /* we moved in-place, so truncate the actual hostname found */
337 /* skip ahead to either start of port, or original EOS */
338 while (*dst
!= '\0' && *dst
!= ':')
342 t
= strrchr(host
, ':');
344 if (t
!= strchr(host
,':') ) {
345 /* RFC 2732 states IPv6 "SHOULD" be bracketed. allowing for times when its not. */
346 /* RFC 3986 'update' simply modifies this to an "is" with no emphasis at all! */
347 /* therefore we MUST accept the case where they are not bracketed at all. */
352 // Bug 3183 sanity check: If scheme is present, host must be too.
353 if (protocol
!= AnyP::PROTO_NONE
&& (host
== NULL
|| *host
== '\0')) {
354 debugs(23, DBG_IMPORTANT
, "SECURITY ALERT: Missing hostname in URL '" << url
<< "'. see access.log for details.");
358 if (t
&& *t
== ':') {
365 for (t
= host
; *t
; ++t
)
368 if (stringHasWhitespace(host
)) {
369 if (URI_WHITESPACE_STRIP
== Config
.uri_whitespace
) {
382 debugs(23, 3, "urlParse: Split URL '" << url
<< "' into proto='" << proto
<< "', host='" << host
<< "', port='" << port
<< "', path='" << urlpath
<< "'");
384 if (Config
.onoff
.check_hostnames
&& strspn(host
, Config
.onoff
.allow_underscore
? valid_hostname_chars_u
: valid_hostname_chars
) != strlen(host
)) {
385 debugs(23, DBG_IMPORTANT
, "urlParse: Illegal character in hostname '" << host
<< "'");
389 /* For IPV6 addresses also check for a colon */
390 if (Config
.appendDomain
&& !strchr(host
, '.') && !strchr(host
, ':'))
391 strncat(host
, Config
.appendDomain
, SQUIDHOSTNAMELEN
- strlen(host
) - 1);
393 /* remove trailing dots from hostnames */
394 while ((l
= strlen(host
)) > 0 && host
[--l
] == '.')
397 /* reject duplicate or leading dots */
398 if (strstr(host
, "..") || *host
== '.') {
399 debugs(23, DBG_IMPORTANT
, "urlParse: Illegal hostname '" << host
<< "'");
403 if (port
< 1 || port
> 65535) {
404 debugs(23, 3, "urlParse: Invalid port '" << port
<< "'");
408 #if HARDCODE_DENY_PORTS
409 /* These ports are filtered in the default squid.conf, but
410 * maybe someone wants them hardcoded... */
411 if (port
== 7 || port
== 9 || port
== 19) {
412 debugs(23, DBG_CRITICAL
, "urlParse: Deny access to port " << port
);
417 if (stringHasWhitespace(urlpath
)) {
418 debugs(23, 2, "urlParse: URI has whitespace: {" << url
<< "}");
420 switch (Config
.uri_whitespace
) {
422 case URI_WHITESPACE_DENY
:
425 case URI_WHITESPACE_ALLOW
:
428 case URI_WHITESPACE_ENCODE
:
429 t
= rfc1738_escape_unescaped(urlpath
);
430 xstrncpy(urlpath
, t
, MAX_URL
);
433 case URI_WHITESPACE_CHOP
:
434 *(urlpath
+ strcspn(urlpath
, w_space
)) = '\0';
437 case URI_WHITESPACE_STRIP
:
451 return urlParseFinish(method
, protocol
, urlpath
, host
, login
, port
, request
);
455 * Update request with parsed URI data. If the request arg is
456 * non-NULL, put parsed values there instead of allocating a new
460 urlParseFinish(const HttpRequestMethod
& method
,
461 const AnyP::ProtocolType protocol
,
462 const char *const urlpath
,
463 const char *const host
,
464 const char *const login
,
466 HttpRequest
*request
)
469 request
= new HttpRequest(method
, protocol
, urlpath
);
471 request
->initHTTP(method
, protocol
, urlpath
);
472 safe_free(request
->canonical
);
475 request
->SetHost(host
);
476 xstrncpy(request
->login
, login
, MAX_LOGIN_SZ
);
477 request
->port
= (unsigned short) port
;
482 urnParse(const HttpRequestMethod
& method
, char *urn
, HttpRequest
*request
)
484 debugs(50, 5, "urnParse: " << urn
);
486 request
->initHTTP(method
, AnyP::PROTO_URN
, urn
+ 4);
487 safe_free(request
->canonical
);
491 return new HttpRequest(method
, AnyP::PROTO_URN
, urn
+ 4);
495 urlCanonical(HttpRequest
* request
)
497 LOCAL_ARRAY(char, portbuf
, 32);
498 /// \todo AYJ: Performance: making this a ptr and allocating when needed will be better than a write and future xstrdup().
499 LOCAL_ARRAY(char, urlbuf
, MAX_URL
);
501 if (request
->canonical
)
502 return request
->canonical
;
504 if (request
->protocol
== AnyP::PROTO_URN
) {
505 snprintf(urlbuf
, MAX_URL
, "urn:" SQUIDSTRINGPH
,
506 SQUIDSTRINGPRINT(request
->urlpath
));
508 /// \todo AYJ: this could use "if..else and method == METHOD_CONNECT" easier.
509 switch (request
->method
.id()) {
512 snprintf(urlbuf
, MAX_URL
, "%s:%d", request
->GetHost(), request
->port
);
518 if (request
->port
!= urlDefaultPort(request
->protocol
))
519 snprintf(portbuf
, 32, ":%d", request
->port
);
521 const URLScheme sch
= request
->protocol
; // temporary, until bug 1961 URL handling is fixed.
522 snprintf(urlbuf
, MAX_URL
, "%s://%s%s%s%s" SQUIDSTRINGPH
,
525 *request
->login
? "@" : null_string
,
528 SQUIDSTRINGPRINT(request
->urlpath
));
534 return (request
->canonical
= xstrdup(urlbuf
));
537 /** \todo AYJ: Performance: This is an *almost* duplicate of urlCanonical. But elides the query-string.
538 * After copying it on in the first place! Would be less code to merge the two with a flag parameter.
539 * and never copy the query-string part in the first place
542 urlCanonicalClean(const HttpRequest
* request
)
544 LOCAL_ARRAY(char, buf
, MAX_URL
);
545 LOCAL_ARRAY(char, portbuf
, 32);
546 LOCAL_ARRAY(char, loginbuf
, MAX_LOGIN_SZ
+ 1);
549 if (request
->protocol
== AnyP::PROTO_URN
) {
550 snprintf(buf
, MAX_URL
, "urn:" SQUIDSTRINGPH
,
551 SQUIDSTRINGPRINT(request
->urlpath
));
553 /// \todo AYJ: this could use "if..else and method == METHOD_CONNECT" easier.
554 switch (request
->method
.id()) {
557 snprintf(buf
, MAX_URL
, "%s:%d",
565 if (request
->port
!= urlDefaultPort(request
->protocol
))
566 snprintf(portbuf
, 32, ":%d", request
->port
);
570 if ((int) strlen(request
->login
) > 0) {
571 strcpy(loginbuf
, request
->login
);
573 if ((t
= strchr(loginbuf
, ':')))
576 strcat(loginbuf
, "@");
579 const URLScheme sch
= request
->protocol
; // temporary, until bug 1961 URL handling is fixed.
580 snprintf(buf
, MAX_URL
, "%s://%s%s%s" SQUIDSTRINGPH
,
585 SQUIDSTRINGPRINT(request
->urlpath
));
587 * strip arguments AFTER a question-mark
590 if (Config
.onoff
.strip_query_terms
)
591 if ((t
= strchr(buf
, '?'))) {
600 if (stringHasCntl(buf
))
601 xstrncpy(buf
, rfc1738_escape_unescaped(buf
), MAX_URL
);
607 * Yet another alternative to urlCanonical.
608 * This one addes the https:// parts to METHOD_CONNECT URL
609 * for use in error page outputs.
610 * Luckily we can leverage the others instead of duplicating.
613 urlCanonicalFakeHttps(const HttpRequest
* request
)
615 LOCAL_ARRAY(char, buf
, MAX_URL
);
617 // method CONNECT and port HTTPS
618 if (request
->method
== METHOD_CONNECT
&& request
->port
== 443) {
619 snprintf(buf
, MAX_URL
, "https://%s/*", request
->GetHost());
623 // else do the normal complete canonical thing.
624 return urlCanonicalClean(request
);
628 * Test if a URL is relative.
630 * RFC 2396, Section 5 (Page 17) implies that in a relative URL, a '/' will
631 * appear before a ':'.
634 urlIsRelative(const char *url
)
645 for (p
= url
; *p
!= '\0' && *p
!= ':' && *p
!= '/'; ++p
);
654 * Convert a relative URL to an absolute URL using the context of a given
657 * It is assumed that you have already ensured that the URL is relative.
659 * If NULL is returned it is an indication that the method in use in the
660 * request does not distinguish between relative and absolute and you should
661 * use the url unchanged.
663 * If non-NULL is returned, it is up to the caller to free the resulting
664 * memory using safe_free().
667 urlMakeAbsolute(const HttpRequest
* req
, const char *relUrl
)
670 if (req
->method
.id() == METHOD_CONNECT
) {
674 char *urlbuf
= (char *)xmalloc(MAX_URL
* sizeof(char));
676 if (req
->protocol
== AnyP::PROTO_URN
) {
677 snprintf(urlbuf
, MAX_URL
, "urn:" SQUIDSTRINGPH
,
678 SQUIDSTRINGPRINT(req
->urlpath
));
684 const URLScheme sch
= req
->protocol
; // temporary, until bug 1961 URL handling is fixed.
685 if (req
->port
!= urlDefaultPort(req
->protocol
)) {
686 urllen
= snprintf(urlbuf
, MAX_URL
, "%s://%s%s%s:%d",
689 *req
->login
? "@" : null_string
,
694 urllen
= snprintf(urlbuf
, MAX_URL
, "%s://%s%s%s",
697 *req
->login
? "@" : null_string
,
702 if (relUrl
[0] == '/') {
703 strncpy(&urlbuf
[urllen
], relUrl
, MAX_URL
- urllen
- 1);
705 const char *path
= req
->urlpath
.termedBuf();
706 const char *last_slash
= strrchr(path
, '/');
708 if (last_slash
== NULL
) {
709 urlbuf
[urllen
] = '/';
711 strncpy(&urlbuf
[urllen
], relUrl
, MAX_URL
- urllen
- 1);
714 size_t pathlen
= last_slash
- path
;
715 if (pathlen
> MAX_URL
- urllen
- 1) {
716 pathlen
= MAX_URL
- urllen
- 1;
718 strncpy(&urlbuf
[urllen
], path
, pathlen
);
720 if (urllen
+ 1 < MAX_URL
) {
721 strncpy(&urlbuf
[urllen
], relUrl
, MAX_URL
- urllen
- 1);
730 * matchDomainName() compares a hostname with a domainname according
731 * to the following rules:
734 * ------------- ------------- ------
735 * foo.com foo.com YES
736 * .foo.com foo.com YES
737 * x.foo.com foo.com NO
738 * foo.com .foo.com YES
739 * .foo.com .foo.com YES
740 * x.foo.com .foo.com YES
742 * We strip leading dots on hosts (but not domains!) so that
743 * ".foo.com" is is always the same as "foo.com".
746 * 0 means the host matches the domain
747 * 1 means the host is greater than the domain
748 * -1 means the host is less than the domain
752 matchDomainName(const char *h
, const char *d
)
765 * Start at the ends of the two strings and work towards the
768 while (xtolower(h
[--hl
]) == xtolower(d
[--dl
])) {
769 if (hl
== 0 && dl
== 0) {
771 * We made it all the way to the beginning of both
772 * strings without finding any difference.
779 * The host string is shorter than the domain string.
780 * There is only one case when this can be a match.
781 * If the domain is just one character longer, and if
782 * that character is a leading '.' then we call it a
786 if (1 == dl
&& '.' == d
[0])
794 * The domain string is shorter than the host string.
795 * This is a match only if the first domain character
807 * We found different characters in the same position (from the end).
810 * If one of those character is '.' then its special. In order
811 * for splay tree sorting to work properly, "x-foo.com" must
812 * be greater than ".foo.com" even though '-' is less than '.'.
820 return (xtolower(h
[hl
]) - xtolower(d
[dl
]));
824 * return true if we can serve requests for this method.
827 urlCheckRequest(const HttpRequest
* r
)
830 /* protocol "independent" methods
832 * actually these methods are specific to HTTP:
833 * they are methods we recieve on our HTTP port,
834 * and if we had a FTP listener would not be relevant
837 * So, we should delegate them to HTTP. The problem is that we
838 * do not have a default protocol from the client side of HTTP.
841 if (r
->method
== METHOD_CONNECT
)
844 // we support OPTIONS and TRACE directed at us (with a 501 reply, for now)
845 // we also support forwarding OPTIONS and TRACE, except for the *-URI ones
846 if (r
->method
== METHOD_OPTIONS
|| r
->method
== METHOD_TRACE
)
847 return (r
->header
.getInt64(HDR_MAX_FORWARDS
) == 0 || r
->urlpath
!= "*");
849 if (r
->method
== METHOD_PURGE
)
852 /* does method match the protocol? */
853 switch (r
->protocol
) {
855 case AnyP::PROTO_URN
:
857 case AnyP::PROTO_HTTP
:
859 case AnyP::PROTO_CACHE_OBJECT
:
863 case AnyP::PROTO_FTP
:
865 if (r
->method
== METHOD_PUT
)
868 case AnyP::PROTO_GOPHER
:
870 case AnyP::PROTO_WAIS
:
872 case AnyP::PROTO_WHOIS
:
873 if (r
->method
== METHOD_GET
)
875 else if (r
->method
== METHOD_HEAD
)
880 case AnyP::PROTO_HTTPS
:
889 * Squid can't originate an SSL connection, so it should
890 * never receive an "https:" URL. It should always be
905 * Quick-n-dirty host extraction from a URL. Steps:
907 * Skip any '/' after the colon
908 * Copy the next SQUID_MAXHOSTNAMELEN bytes to host[]
909 * Look for an ending '/' or ':' and terminate
910 * Look for login info preceeded by '@'
917 char * extract(char const *url
);
920 static char Host
[SQUIDHOSTNAMELEN
];
921 void init(char const *);
922 void findHostStart();
923 void trimTrailingChars();
925 char const *hostStart
;
930 urlHostname(const char *url
)
932 return URLHostName().extract(url
);
935 char URLHostName::Host
[SQUIDHOSTNAMELEN
];
938 URLHostName::init(char const *aUrl
)
945 URLHostName::findHostStart()
947 if (NULL
== (hostStart
= strchr(url
, ':')))
952 while (*hostStart
!= '\0' && *hostStart
== '/')
955 if (*hostStart
== ']')
960 URLHostName::trimTrailingChars()
964 if ((t
= strchr(Host
, '/')))
967 if ((t
= strrchr(Host
, ':')))
970 if ((t
= strchr(Host
, ']')))
975 URLHostName::trimAuth()
979 if ((t
= strrchr(Host
, '@'))) {
981 memmove(Host
, t
, strlen(t
) + 1);
986 URLHostName::extract(char const *aUrl
)
991 if (hostStart
== NULL
)
994 xstrncpy(Host
, hostStart
, SQUIDHOSTNAMELEN
);
1003 URL::URL() : scheme()
1006 URL::URL(URLScheme
const &aScheme
): scheme(aScheme
)