Ok, hopefully this meets with everybody's approval. Changes in this version:
- Split urlAbsolute into urlIsRelative and urlMakeAbsolute.
- Make urlIsRelative compliant with the RFC as to what defines a relative URL.
- Use a malloc'ed buffer instead of a stack-allocated array and xstrdup in
urlMakeAbsolute.
- Rework purgeEntriesByHeader to be a little easier on the eyes.
# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: [EMAIL PROTECTED]
# target_branch: file:///home/benno/squid-work/squid3-repo/trunk/
# testament_sha1: bc594f5388639662dda35328f972e5eb73bdc3d4
# timestamp: 2008-09-03 11:03:59 +1000
# base_revision_id: [EMAIL PROTECTED]
# ejhnqdep2qm6qz0h
#
# Begin patch
=== modified file 'src/HttpRequestMethod.cc'
--- src/HttpRequestMethod.cc 2008-06-20 04:43:01 +0000
+++ src/HttpRequestMethod.cc 2008-08-29 00:56:35 +0000
@@ -243,11 +243,16 @@
case METHOD_PURGE:
return true;
- /* all others */
+ /*
+ * RFC 2616 sayeth, in section 13.10, final paragraph:
+ * A cache that passes through requests for methods it does not
+ * understand SHOULD invalidate any entities referred to by the
+ * Request-URI.
+ */
case METHOD_OTHER:
default:
- return false; // be conservative: we do not know some methods specs
+ return true;
}
- return false; // not reached
+ return true; // not reached, but just in case
}
=== modified file 'src/Server.cc'
--- src/Server.cc 2008-07-22 12:33:41 +0000
+++ src/Server.cc 2008-09-03 01:00:39 +0000
@@ -402,11 +402,35 @@
// purges entries that match the value of a given HTTP [response] header
static void
-purgeEntriesByHeader(const char *reqUrl, HttpMsg *rep, http_hdr_type hdr)
+purgeEntriesByHeader(const HttpRequest *req, const char *reqUrl, HttpMsg *rep, http_hdr_type hdr)
{
- if (const char *url = rep->header.getStr(hdr))
- if (sameUrlHosts(reqUrl, url)) // prevent purging DoS, per RFC 2616
- purgeEntriesByUrl(url);
+ const char *hdrUrl, *absUrl;
+
+ absUrl = NULL;
+ hdrUrl = rep->header.getStr(hdr);
+ if (hdrUrl == NULL) {
+ return;
+ }
+
+ /*
+ * If the URL is relative, make it absolute so we can find it.
+ * If it's absolute, make sure the host parts match to avoid DOS attacks
+ * as per RFC 2616 13.10.
+ */
+ if (urlIsRelative(hdrUrl)) {
+ absUrl = urlMakeAbsolute(req, hdrUrl);
+ if (absUrl != NULL) {
+ hdrUrl = absUrl;
+ }
+ } else if (!sameUrlHosts(reqUrl, hdrUrl)) {
+ return;
+ }
+
+ purgeEntriesByUrl(hdrUrl);
+
+ if (absUrl != NULL) {
+ safe_free(absUrl);
+ }
}
// some HTTP methods should purge matching cache entries
@@ -425,8 +449,8 @@
const char *reqUrl = urlCanonical(request);
debugs(88, 5, "maybe purging due to " << RequestMethodStr(request->method) << ' ' << reqUrl);
purgeEntriesByUrl(reqUrl);
- purgeEntriesByHeader(reqUrl, theFinalReply, HDR_LOCATION);
- purgeEntriesByHeader(reqUrl, theFinalReply, HDR_CONTENT_LOCATION);
+ purgeEntriesByHeader(request, reqUrl, theFinalReply, HDR_LOCATION);
+ purgeEntriesByHeader(request, reqUrl, theFinalReply, HDR_CONTENT_LOCATION);
}
// called (usually by kids) when we have final (possibly adapted) reply headers
=== modified file 'src/protos.h'
--- src/protos.h 2008-07-17 12:38:06 +0000
+++ src/protos.h 2008-09-02 11:27:02 +0000
@@ -638,6 +638,8 @@
SQUIDCEXTERN void urlInitialize(void);
SQUIDCEXTERN HttpRequest *urlParse(const HttpRequestMethod&, char *, HttpRequest *request = NULL);
SQUIDCEXTERN const char *urlCanonical(HttpRequest *);
+SQUIDCEXTERN int urlIsRelative(const char *);
+SQUIDCEXTERN const char *urlMakeAbsolute(const HttpRequest *, const char *);
SQUIDCEXTERN char *urlRInternal(const char *host, u_short port, const char *dir, const char *name);
SQUIDCEXTERN char *urlInternal(const char *dir, const char *name);
SQUIDCEXTERN int matchDomainName(const char *host, const char *domain);
=== modified file 'src/url.cc'
--- src/url.cc 2008-04-10 11:29:39 +0000
+++ src/url.cc 2008-09-03 01:00:39 +0000
@@ -533,6 +533,100 @@
}
/*
+ * Test if a URL is relative.
+ *
+ * RFC 2396, Section 5 (Page 17) implies that in a relative URL, a '/' will
+ * appear before a ':'.
+ */
+int
+urlIsRelative(const char *url)
+{
+ const char *p;
+
+ if (url == NULL) {
+ return (0);
+ }
+ if (*url == '\0') {
+ return (0);
+ }
+
+ for (p = url; *p != '\0' && *p != ':' && *p != '/'; p++);
+
+ if (*p == ':') {
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * Convert a relative URL to an absolute URL using the context of a given
+ * request.
+ *
+ * It is assumed that you have already ensured that the URL is relative.
+ *
+ * If NULL is returned, you should use the original URL unchanged.
+ */
+const char *
+urlMakeAbsolute(const HttpRequest * req, const char *relUrl)
+{
+ char *urlbuf;
+ const char *path, *last_slash;
+ size_t urllen, pathlen;
+
+ if (req->method.id() == METHOD_CONNECT) {
+ return (NULL);
+ }
+
+ urlbuf = (char *)xmalloc(MAX_URL * sizeof(char));
+
+ if (req->protocol == PROTO_URN) {
+ snprintf(urlbuf, MAX_URL, "urn:%s", req->urlpath.buf());
+ return (urlbuf);
+ }
+
+ if (req->port != urlDefaultPort(req->protocol)) {
+ urllen = snprintf(urlbuf, MAX_URL, "%s://%s%s%s:%d",
+ ProtocolStr[req->protocol],
+ req->login,
+ *req->login ? "@" : null_string,
+ req->GetHost(),
+ req->port
+ );
+ } else {
+ urllen = snprintf(urlbuf, MAX_URL, "%s://%s%s%s",
+ ProtocolStr[req->protocol],
+ req->login,
+ *req->login ? "@" : null_string,
+ req->GetHost()
+ );
+ }
+
+ if (relUrl[0] == '/') {
+ strncpy(&urlbuf[urllen], relUrl, MAX_URL - urllen - 1);
+ } else {
+ path = req->urlpath.buf();
+ last_slash = strrchr(path, '/');
+ if (last_slash == NULL) {
+ urlbuf[urllen++] = '/';
+ strncpy(&urlbuf[urllen], relUrl, MAX_URL - urllen - 1);
+ } else {
+ last_slash++;
+ pathlen = last_slash - path;
+ if (pathlen > MAX_URL - urllen - 1) {
+ pathlen = MAX_URL - urllen - 1;
+ }
+ strncpy(&urlbuf[urllen], path, pathlen);
+ urllen += pathlen;
+ if (urllen + 1 < MAX_URL) {
+ strncpy(&urlbuf[urllen], relUrl, MAX_URL - urllen - 1);
+ }
+ }
+ }
+
+ return (urlbuf);
+}
+
+/*
* matchDomainName() compares a hostname with a domainname according
* to the following rules:
*
# Begin bundle
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