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(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(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(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;