Compliance: handle HTTP OPTIONS and TRACE requests with asterisk URIs.
Handle '*' URIs in urlParse(). This allows Squid properly respond to
OPTIONS and TRACE requests with '*' URIs and Max-Forwards value of zero.
Forwarding similar requests with positive Max-Forwards value is out of
this change scope and still does not work, because the upstream host and
port are not set.
Co-Advisor test cases:
test_case/rfc2616/options-bodyless-asterisk
test_case/rfc2616/maxForwardsZero-OPTIONS-asterisk
test_case/rfc2616/maxForwardsZero-TRACE-asterisk
Compliance: handle HTTP OPTIONS and TRACE requests with asterisk URIs,
Handle '*' URIs in urlParse(). This allows Squid properly respond to OPTIONS
and TRACE requests with '*' URIs and Max-Forwards value of zero. Forwarding
similar requests with positive Max-Forwards value is out of this change scope
and still does not work, because the upstream host and port are not set.
Co-Advisor test cases: test_case/rfc2616/options-bodyless-asterisk
test_case/rfc2616/maxForwardsZero-OPTIONS-asterisk
test_case/rfc2616/maxForwardsZero-TRACE-asterisk
=== modified file 'src/client_side.cc'
--- src/client_side.cc 2010-08-24 04:18:51 +0000
+++ src/client_side.cc 2010-08-30 23:00:10 +0000
@@ -2378,40 +2378,41 @@ ConnStateData::clientAfterReadingRequest
/* Partial request received. Abort client connection! */
debugs(33, 3, "clientAfterReadingRequests: FD " << fd << " aborted, partial request");
comm_close(fd);
return;
}
}
clientMaybeReadData (do_next_read);
}
static void
clientProcessRequest(ConnStateData *conn, HttpParser *hp, ClientSocketContext *context, const HttpRequestMethod& method, HttpVersion http_ver)
{
ClientHttpRequest *http = context->http;
HttpRequest *request = NULL;
bool notedUseOfBuffer = false;
bool tePresent = false;
bool deChunked = false;
bool mustReplyToOptions = false;
bool unsupportedTe = false;
+ bool unsupportedUri = false;
/* We have an initial client stream in place should it be needed */
/* setup our private context */
context->registerWithConn();
if (context->flags.parsed_ok == 0) {
clientStreamNode *node = context->getClientReplyContext();
debugs(33, 1, "clientProcessRequest: Invalid Request");
clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
assert (repContext);
switch (hp->request_parse_status) {
case HTTP_HEADER_TOO_LARGE:
repContext->setReplyToError(ERR_TOO_BIG, HTTP_HEADER_TOO_LARGE, method, http->uri, conn->peer, NULL, conn->in.buf, NULL);
break;
case HTTP_METHOD_NOT_ALLOWED:
repContext->setReplyToError(ERR_UNSUP_REQ, HTTP_METHOD_NOT_ALLOWED, method, http->uri, conn->peer, NULL, conn->in.buf, NULL);
break;
default:
repContext->setReplyToError(ERR_INVALID_REQ, HTTP_BAD_REQUEST, method, http->uri, conn->peer, NULL, conn->in.buf, NULL);
}
@@ -2502,46 +2503,49 @@ clientProcessRequest(ConnStateData *conn
#if USE_SQUID_EUI
request->client_eui48 = conn->peer_eui48;
request->client_eui64 = conn->peer_eui64;
#endif
#if FOLLOW_X_FORWARDED_FOR
request->indirect_client_addr = conn->peer;
#endif /* FOLLOW_X_FORWARDED_FOR */
request->my_addr = conn->me;
request->http_ver = http_ver;
tePresent = request->header.has(HDR_TRANSFER_ENCODING);
deChunked = conn->in.dechunkingState == ConnStateData::chunkReady;
if (deChunked) {
assert(tePresent);
request->setContentLength(conn->in.dechunked.contentSize());
request->header.delById(HDR_TRANSFER_ENCODING);
conn->finishDechunkingRequest(hp);
} else
conn->cleanDechunkingRequest();
- if (method == METHOD_TRACE || method == METHOD_OPTIONS)
+ if (method == METHOD_TRACE || method == METHOD_OPTIONS) {
request->max_forwards = request->header.getInt64(HDR_MAX_FORWARDS);
+ unsupportedUri = (request->max_forwards != 0) && (request->urlpath == "*");
+ }
mustReplyToOptions = (method == METHOD_OPTIONS) && (request->max_forwards == 0);
unsupportedTe = tePresent && !deChunked;
- if (!urlCheckRequest(request) || mustReplyToOptions || unsupportedTe) {
+ if (!urlCheckRequest(request) || mustReplyToOptions || unsupportedTe ||
+ unsupportedUri) {
clientStreamNode *node = context->getClientReplyContext();
clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
assert (repContext);
repContext->setReplyToError(ERR_UNSUP_REQ,
HTTP_NOT_IMPLEMENTED, request->method, NULL,
conn->peer, request, NULL, NULL);
assert(context->http->out.offset == 0);
context->pullData();
conn->flags.readMoreRequests = false;
goto finish;
}
if (!clientIsContentLengthValid(request)) {
clientStreamNode *node = context->getClientReplyContext();
clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
assert (repContext);
repContext->setReplyToError(ERR_INVALID_REQ,
HTTP_LENGTH_REQUIRED, request->method, NULL,
conn->peer, request, NULL, NULL);
=== modified file 'src/url.cc'
--- src/url.cc 2010-07-25 08:10:12 +0000
+++ src/url.cc 2010-08-30 22:44:58 +0000
@@ -207,40 +207,43 @@ urlParse(const HttpRequestMethod& method
int i;
const char *src;
char *dst;
proto[0] = host[0] = urlpath[0] = login[0] = '\0';
if ((l = strlen(url)) + Config.appendDomainLen > (MAX_URL - 1)) {
/* terminate so it doesn't overflow other buffers */
*(url + (MAX_URL >> 1)) = '\0';
debugs(23, 1, "urlParse: URL too large (" << l << " bytes)");
return NULL;
}
if (method == METHOD_CONNECT) {
port = CONNECT_PORT;
if (sscanf(url, "[%[^]]]:%d", host, &port) < 1)
if (sscanf(url, "%[^:]:%d", host, &port) < 1)
return NULL;
} else if (!strncmp(url, "urn:", 4)) {
return urnParse(method, url);
+ } else if ((method == METHOD_OPTIONS || method == METHOD_TRACE) &&
+ strcmp(url, "*") == 0) {
+ strcpy(urlpath, url);
} else {
/* Parse the URL: */
src = url;
i = 0;
/* Find first : - everything before is protocol */
for (i = 0, dst = proto; i < l && *src != ':'; i++, src++, dst++) {
*dst = *src;
}
if (i >= l)
return NULL;
*dst = '\0';
/* Then its :// */
/* (XXX yah, I'm not checking we've got enough data left before checking the array..) */
if (*src != ':' || *(src + 1) != '/' || *(src + 2) != '/')
return NULL;
i += 3;
src += 3;
/* Then everything until first /; thats host (and port; which we'll look for here later) */
@@ -339,41 +342,41 @@ urlParse(const HttpRequestMethod& method
if (Config.onoff.check_hostnames && strspn(host, Config.onoff.allow_underscore ? valid_hostname_chars_u : valid_hostname_chars) != strlen(host)) {
debugs(23, 1, "urlParse: Illegal character in hostname '" << host << "'");
return NULL;
}
/* For IPV6 addresses also check for a colon */
if (Config.appendDomain && !strchr(host, '.') && !strchr(host, ':'))
strncat(host, Config.appendDomain, SQUIDHOSTNAMELEN - strlen(host) - 1);
/* remove trailing dots from hostnames */
while ((l = strlen(host)) > 0 && host[--l] == '.')
host[l] = '\0';
/* reject duplicate or leading dots */
if (strstr(host, "..") || *host == '.') {
debugs(23, 1, "urlParse: Illegal hostname '" << host << "'");
return NULL;
}
- if (port < 1 || port > 65535) {
+ if ((port < 1 || port > 65535) && strcmp(urlpath, "*") != 0) {
debugs(23, 3, "urlParse: Invalid port '" << port << "'");
return NULL;
}
#if HARDCODE_DENY_PORTS
/* These ports are filtered in the default squid.conf, but
* maybe someone wants them hardcoded... */
if (port == 7 || port == 9 || port == 19) {
debugs(23, 0, "urlParse: Deny access to port " << port);
return NULL;
}
#endif
if (stringHasWhitespace(urlpath)) {
debugs(23, 2, "urlParse: URI has whitespace: {" << url << "}");
switch (Config.uri_whitespace) {
case URI_WHITESPACE_DENY:
return NULL;