/*
- * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2018 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
#include "HttpHdrCc.h"
#include "HttpReply.h"
#include "HttpRequest.h"
+#include "ip/NfMarkConfig.h"
#include "ip/QosConfig.h"
#include "ipcache.h"
#include "log/access_log.h"
store_id_done(false),
no_cache_done(false),
interpreted_req_hdrs(false),
- tosToClientDone(false),
- nfmarkToClientDone(false),
+ toClientMarkingDone(false),
#if USE_OPENSSL
sslBumpCheckDone(false),
#endif
{
assert(request);
return request->cache_control &&
- request->cache_control->onlyIfCached();
+ request->cache_control->hasOnlyIfCached();
}
/**
int
clientBeginRequest(const HttpRequestMethod& method, char const *url, CSCB * streamcallback,
CSD * streamdetach, ClientStreamData streamdata, HttpHeader const *header,
- char *tailbuf, size_t taillen)
+ char *tailbuf, size_t taillen, const MasterXaction::Pointer &mx)
{
size_t url_sz;
ClientHttpRequest *http = new ClientHttpRequest(NULL);
/* allow size for url rewriting */
url_sz = strlen(url) + Config.appendDomainLen + 5;
http->uri = (char *)xcalloc(url_sz, 1);
- strcpy(http->uri, url);
+ strcpy(http->uri, url); // XXX: polluting http->uri before parser validation
- if ((request = HttpRequest::CreateFromUrl(http->uri, method)) == NULL) {
+ if ((request = HttpRequest::FromUrl(http->uri, mx, method)) == NULL) {
debugs(85, 5, "Invalid URL: " << http->uri);
return -1;
}
/*
- * now update the headers in request with our supplied headers. urlParse
- * should return a blank header set, but we use Update to be sure of
- * correctness.
+ * now update the headers in request with our supplied headers.
+ * HttpRequest::FromUrl() should return a blank header set, but
+ * we use Update to be sure of correctness.
*/
if (header)
request->header.update(header);
*/
request->flags.accelerated = http->flags.accel;
- request->flags.internalClient = true;
-
/* this is an internally created
* request, not subject to acceleration
* target overrides */
ClientHttpRequest *http = calloutContext->http;
HttpRequest *request = http->request;
- /*
- * answer should be be ACCESS_ALLOWED or ACCESS_DENIED if we are
- * called as a result of ACL checks, or -1 if we are called when
- * there's nothing left to do.
- */
- if (answer == ACCESS_ALLOWED &&
- request->x_forwarded_for_iterator.size () != 0) {
+ if (answer.allowed() && request->x_forwarded_for_iterator.size() != 0) {
/*
* Remove the last comma-delimited element from the
calloutContext->acl_checklist->nonBlockingCheck(clientFollowXForwardedForCheck, data);
return;
}
- } /*if (answer == ACCESS_ALLOWED &&
- request->x_forwarded_for_iterator.size () != 0)*/
+ }
/* clean up, and pass control to clientAccessCheck */
if (Config.onoff.log_uses_indirect_client) {
* Ensure that the access log shows the indirect client
* instead of the direct client.
*/
- ConnStateData *conn = http->getConn();
- conn->log_addr = request->indirect_client_addr;
- http->al->cache.caddr = conn->log_addr;
+ http->al->cache.caddr = request->indirect_client_addr;
+ if (ConnStateData *conn = http->getConn())
+ conn->log_addr = request->indirect_client_addr;
}
request->x_forwarded_for_iterator.clean();
request->flags.done_follow_x_forwarded_for = true;
- if (answer != ACCESS_ALLOWED && answer != ACCESS_DENIED) {
+ if (answer.conflicted()) {
debugs(28, DBG_CRITICAL, "ERROR: Processing X-Forwarded-For. Stopping at IP address: " << request->indirect_client_addr );
}
// note the DNS details for the transaction stats.
http->request->recordLookup(dns);
- if (ia != NULL && ia->count > 0) {
- // Is the NAT destination IP in DNS?
- for (int i = 0; i < ia->count; ++i) {
- if (clientConn->local.matchIPAddr(ia->in_addrs[i]) == 0) {
- debugs(85, 3, HERE << "validate IP " << clientConn->local << " possible from Host:");
- http->request->flags.hostVerified = true;
- http->doCallouts();
- return;
- }
- debugs(85, 3, HERE << "validate IP " << clientConn->local << " non-match from Host: IP " << ia->in_addrs[i]);
- }
+ // Is the NAT destination IP in DNS?
+ if (ia && ia->have(clientConn->local)) {
+ debugs(85, 3, "validate IP " << clientConn->local << " possible from Host:");
+ http->request->flags.hostVerified = true;
+ http->doCallouts();
+ return;
}
debugs(85, 3, HERE << "FAIL: validate IP " << clientConn->local << " possible from Host:");
hostHeaderVerifyFailed("local IP", "any domain IP");
proxy_auth_msg = http->request->auth_user_request->denyMessage("<null>");
#endif
- if (answer != ACCESS_ALLOWED) {
+ if (!answer.allowed()) {
// auth has a grace period where credentials can be expired but okay not to challenge.
/* Send an auth challenge or error */
ClientHttpRequest *http = context->http;
context->acl_checklist = NULL;
- if (answer == ACCESS_ALLOWED)
+ if (answer.allowed())
redirectStart(http, clientRedirectDoneWrapper, context);
else {
- Helper::Reply nilReply;
- nilReply.result = Helper::Error;
+ Helper::Reply const nilReply(Helper::Error);
context->clientRedirectDone(nilReply);
}
}
ClientRequestContext::clientRedirectStart()
{
debugs(33, 5, HERE << "'" << http->uri << "'");
- (void)SyncNotes(*http->al, *http->request);
+ http->al->syncNotes(http->request);
if (Config.accessList.redirector) {
acl_checklist = clientAclChecklistCreate(Config.accessList.redirector, http);
acl_checklist->nonBlockingCheck(clientRedirectAccessCheckDone, this);
ClientHttpRequest *http = context->http;
context->acl_checklist = NULL;
- if (answer == ACCESS_ALLOWED)
+ if (answer.allowed())
storeIdStart(http, clientStoreIdDoneWrapper, context);
else {
debugs(85, 3, "access denied expected ERR reply handling: " << answer);
- Helper::Reply nilReply;
- nilReply.result = Helper::Error;
+ Helper::Reply const nilReply(Helper::Error);
context->clientStoreIdDone(nilReply);
}
}
// Put helper response Notes into the transaction state record (ALE) eventually
// do it early to ensure that no matter what the outcome the notes are present.
- if (http->al != NULL)
- (void)SyncNotes(*http->al, *old_request);
+ if (http->al)
+ http->al->syncNotes(old_request);
UpdateRequestNotes(http->getConn(), *old_request, reply.notes);
// prevent broken helpers causing too much damage. If old URL == new URL skip the re-write.
if (urlNote != NULL && strcmp(urlNote, http->uri)) {
- // XXX: validate the URL properly *without* generating a whole new request object right here.
- // XXX: the clone() should be done only AFTER we know the new URL is valid.
- HttpRequest *new_request = old_request->clone();
- if (urlParse(old_request->method, const_cast<char*>(urlNote), new_request)) {
+ URL tmpUrl;
+ if (tmpUrl.parse(old_request->method, urlNote)) {
+ HttpRequest *new_request = old_request->clone();
+ new_request->url = tmpUrl;
debugs(61, 2, "URL-rewriter diverts URL from " << old_request->effectiveRequestUri() << " to " << new_request->effectiveRequestUri());
// update the new request to flag the re-writing was done on it
} else {
debugs(85, DBG_CRITICAL, "ERROR: URL-rewrite produces invalid request: " <<
old_request->method << " " << urlNote << " " << old_request->http_ver);
- delete new_request;
}
}
}
// Put helper response Notes into the transaction state record (ALE) eventually
// do it early to ensure that no matter what the outcome the notes are present.
- if (http->al != NULL)
- (void)SyncNotes(*http->al, *old_request);
+ if (http->al)
+ http->al->syncNotes(old_request);
UpdateRequestNotes(http->getConn(), *old_request, reply.notes);
ClientRequestContext::checkNoCacheDone(const allow_t &answer)
{
acl_checklist = NULL;
- http->request->flags.cachable = (answer == ACCESS_ALLOWED);
+ if (answer.denied()) {
+ http->request->flags.noCache = true; // dont read reply from cache
+ http->request->flags.cachable = false; // dont store reply into cache
+ }
http->doCallouts();
}
bool
ClientRequestContext::sslBumpAccessCheck()
{
- // If SSL connection tunneling or bumping decision has been made, obey it.
+ if (!http->getConn()) {
+ http->al->ssl.bumpMode = Ssl::bumpEnd; // SslBump does not apply; log -
+ return false;
+ }
+
const Ssl::BumpMode bumpMode = http->getConn()->sslBumpMode;
+ if (http->request->flags.forceTunnel) {
+ debugs(85, 5, "not needed; already decided to tunnel " << http->getConn());
+ if (bumpMode != Ssl::bumpEnd)
+ http->al->ssl.bumpMode = bumpMode; // inherited from bumped connection
+ return false;
+ }
+
+ // If SSL connection tunneling or bumping decision has been made, obey it.
if (bumpMode != Ssl::bumpEnd) {
debugs(85, 5, HERE << "SslBump already decided (" << bumpMode <<
"), " << "ignoring ssl_bump for " << http->getConn());
- if (!http->getConn()->serverBump())
+
+ // We need the following "if" for transparently bumped TLS connection,
+ // because in this case we are running ssl_bump access list before
+ // the doCallouts runs. It can be removed after the bug #4340 fixed.
+ // We do not want to proceed to bumping steps:
+ // - if the TLS connection with the client is already established
+ // because we are accepting normal HTTP requests on TLS port,
+ // or because of the client-first bumping mode
+ // - When the bumping is already started
+ if (!http->getConn()->switchedToHttps() &&
+ !http->getConn()->serverBump())
http->sslBumpNeed(bumpMode); // for processRequest() to bump if needed and not already bumped
http->al->ssl.bumpMode = bumpMode; // inherited from bumped connection
return false;
return false;
}
+ if (error) {
+ debugs(85, 5, "SslBump applies. Force bump action on error " << errorTypeName(error->type));
+ http->sslBumpNeed(Ssl::bumpBump);
+ http->al->ssl.bumpMode = Ssl::bumpBump;
+ return false;
+ }
+
debugs(85, 5, HERE << "SslBump possible, checking ACL");
ACLFilledChecklist *aclChecklist = clientAclChecklistCreate(Config.accessList.ssl_bump, http);
if (!httpStateIsValid())
return;
- const Ssl::BumpMode bumpMode = answer == ACCESS_ALLOWED ?
- static_cast<Ssl::BumpMode>(answer.kind) : Ssl::bumpNone;
+ const Ssl::BumpMode bumpMode = answer.allowed() ?
+ static_cast<Ssl::BumpMode>(answer.kind) : Ssl::bumpSplice;
http->sslBumpNeed(bumpMode); // for processRequest() to bump if needed
http->al->ssl.bumpMode = bumpMode; // for logging
+ if (bumpMode == Ssl::bumpTerminate) {
+ const Comm::ConnectionPointer clientConn = http->getConn() ? http->getConn()->clientConnection : nullptr;
+ if (Comm::IsConnOpen(clientConn)) {
+ debugs(85, 3, "closing after Ssl::bumpTerminate ");
+ clientConn->close();
+ }
+ return;
+ }
+
http->doCallouts();
}
#endif
{
debugs(85, 4, request->method << ' ' << uri);
- if (request->method == Http::METHOD_CONNECT && !redirect.status) {
+ const bool untouchedConnect = request->method == Http::METHOD_CONNECT && !redirect.status;
+
#if USE_OPENSSL
- if (sslBumpNeeded()) {
- sslBumpStart();
- return;
- }
+ if (untouchedConnect && sslBumpNeeded()) {
+ assert(!request->flags.forceTunnel);
+ sslBumpStart();
+ return;
+ }
#endif
+
+ if (untouchedConnect || request->flags.forceTunnel) {
getConn()->stopReading(); // tunnels read for themselves
tunnelStart(this);
return;
*/
tos_t aclMapTOS (acl_tos * head, ACLChecklist * ch);
-nfmark_t aclMapNfmark (acl_nfmark * head, ACLChecklist * ch);
+Ip::NfMarkConfig aclFindNfMarkConfig (acl_nfmark * head, ACLChecklist * ch);
void
ClientHttpRequest::doCallouts()
{
assert(calloutContext);
+ auto &ale = calloutContext->http->al;
/*Save the original request for logging purposes*/
- if (!calloutContext->http->al->request) {
- calloutContext->http->al->request = request;
- HTTPMSGLOCK(calloutContext->http->al->request);
+ if (!ale->request) {
+ ale->request = request;
+ HTTPMSGLOCK(ale->request);
- NotePairs ¬es = SyncNotes(*calloutContext->http->al, *calloutContext->http->request);
// Make the previously set client connection ID available as annotation.
if (ConnStateData *csd = calloutContext->http->getConn()) {
- if (!csd->connectionTag().isEmpty())
- notes.add("clt_conn_tag", SBuf(csd->connectionTag()).c_str());
+ if (!csd->notes()->empty())
+ calloutContext->http->request->notes()->appendNewOnly(csd->notes().getRaw());
}
+ ale->syncNotes(calloutContext->http->request);
}
if (!calloutContext->error) {
}
} // if !calloutContext->error
- if (!calloutContext->tosToClientDone) {
- calloutContext->tosToClientDone = true;
- if (getConn() != NULL && Comm::IsConnOpen(getConn()->clientConnection)) {
- ACLFilledChecklist ch(NULL, request, NULL);
- ch.src_addr = request->client_addr;
- ch.my_addr = request->my_addr;
+ // Set appropriate MARKs and CONNMARKs if needed.
+ if (getConn() && Comm::IsConnOpen(getConn()->clientConnection)) {
+ ACLFilledChecklist ch(nullptr, request, nullptr);
+ ch.src_addr = request->client_addr;
+ ch.my_addr = request->my_addr;
+
+ if (!calloutContext->toClientMarkingDone) {
+ calloutContext->toClientMarkingDone = true;
tos_t tos = aclMapTOS(Ip::Qos::TheConfig.tosToClient, &ch);
if (tos)
Ip::Qos::setSockTos(getConn()->clientConnection, tos);
- }
- }
- if (!calloutContext->nfmarkToClientDone) {
- calloutContext->nfmarkToClientDone = true;
- if (getConn() != NULL && Comm::IsConnOpen(getConn()->clientConnection)) {
- ACLFilledChecklist ch(NULL, request, NULL);
- ch.src_addr = request->client_addr;
- ch.my_addr = request->my_addr;
- nfmark_t mark = aclMapNfmark(Ip::Qos::TheConfig.nfmarkToClient, &ch);
- if (mark)
- Ip::Qos::setSockNfmark(getConn()->clientConnection, mark);
+ const auto packetMark = aclFindNfMarkConfig(Ip::Qos::TheConfig.nfmarkToClient, &ch);
+ if (!packetMark.isEmpty())
+ Ip::Qos::setSockNfmark(getConn()->clientConnection, packetMark.mark);
+
+ const auto connmark = aclFindNfMarkConfig(Ip::Qos::TheConfig.nfConnmarkToClient, &ch);
+ if (!connmark.isEmpty())
+ Ip::Qos::setNfConnmark(getConn()->clientConnection, Ip::Qos::dirAccepted, connmark);
}
}
#if USE_OPENSSL
- // We need to check for SslBump even if the calloutContext->error is set
- // because bumping may require delaying the error until after CONNECT.
+ // Even with calloutContext->error, we call sslBumpAccessCheck() to decide
+ // whether SslBump applies to this transaction. If it applies, we will
+ // attempt to bump the client to serve the error.
if (!calloutContext->sslBumpCheckDone) {
calloutContext->sslBumpCheckDone = true;
if (calloutContext->sslBumpAccessCheck())
if (calloutContext->error) {
// XXX: prformance regression. c_str() reallocates
- SBuf storeUri(request->storeId());
- StoreEntry *e = storeCreateEntry(storeUri.c_str(), storeUri.c_str(), request->flags, request->method);
+ SBuf storeUriBuf(request->storeId());
+ const char *storeUri = storeUriBuf.c_str();
+ StoreEntry *e = storeCreateEntry(storeUri, storeUri, request->flags, request->method);
#if USE_OPENSSL
if (sslBumpNeeded()) {
// We have to serve an error, so bump the client first.
sslBumpNeed(Ssl::bumpClientFirst);
// set final error but delay sending until we bump
- Ssl::ServerBump *srvBump = new Ssl::ServerBump(request, e);
+ Ssl::ServerBump *srvBump = new Ssl::ServerBump(request, e, Ssl::bumpClientFirst);
errorAppendEntry(e, calloutContext->error);
calloutContext->error = NULL;
getConn()->setServerBump(srvBump);
repContext->setReplyToStoreEntry(e, "immediate SslBump error");
errorAppendEntry(e, calloutContext->error);
calloutContext->error = NULL;
- if (calloutContext->readNextRequest)
+ if (calloutContext->readNextRequest && getConn())
getConn()->flags.readMore = true; // resume any pipeline reads.
node = (clientStreamNode *)client_stream.tail->data;
clientStreamRead(node, this, node->readBuffer);
#endif
}
-#if !_USE_INLINE_
-#include "client_side_request.cci"
-#endif
-
#if USE_ADAPTATION
/// Initiate an asynchronous adaptation transaction which will call us back.
void
switch (answer.kind) {
case Adaptation::Answer::akForward:
- handleAdaptedHeader(const_cast<HttpMsg*>(answer.message.getRaw()));
+ handleAdaptedHeader(const_cast<Http::Message*>(answer.message.getRaw()));
break;
case Adaptation::Answer::akBlock:
}
void
-ClientHttpRequest::handleAdaptedHeader(HttpMsg *msg)
+ClientHttpRequest::handleAdaptedHeader(Http::Message *msg)
{
assert(msg);
doCallouts();
}
+void
+ClientHttpRequest::callException(const std::exception &ex)
+{
+ if (const auto clientConn = getConn() ? getConn()->clientConnection : nullptr) {
+ if (Comm::IsConnOpen(clientConn)) {
+ debugs(85, 3, "closing after exception: " << ex.what());
+ clientConn->close(); // initiate orderly top-to-bottom cleanup
+ return;
+ }
+ }
+ debugs(85, DBG_IMPORTANT, "ClientHttpRequest exception without connection. Ignoring " << ex.what());
+ // XXX: Normally, we mustStop() but we cannot do that here because it is
+ // likely to leave Http::Stream and ConnStateData with a dangling http
+ // pointer. See r13480 or XXX in Http::Stream class description.
+}
#endif
// XXX: modify and use with ClientRequestContext::clientAccessCheckDone too.