/* DEBUG: section 73 HTTP Request */
#include "squid.h"
+#include "debug/Stream.h"
#include "RequestFlags.h"
+#include <iostream>
+
// When adding new flags, please update cloneAdaptationImmune() as needed.
// returns a partial copy of the flags that includes only those flags
// that are safe for a related (e.g., ICAP-adapted) request to inherit
return *this;
}
+void
+RequestFlags::disableCacheUse(const char * const reason)
+{
+ debugs(16, 3, "for " << reason);
+ cachable.veto();
+ noCache = true; // may already be true
+}
+
#ifndef SQUID_REQUESTFLAGS_H_
#define SQUID_REQUESTFLAGS_H_
+#include "base/SupportOrVeto.h"
+
/** request-related flags
*
* Contains both flags marking a request's current state,
bool auth = false;
/** do not use keytabs for peer Kerberos authentication */
bool auth_no_keytab = false;
- /** he response to the request may be stored in the cache */
- bool cachable = false;
+
+ /// whether the response may be stored in the cache
+ SupportOrVeto cachable;
+
/** the request can be forwarded through the hierarchy */
bool hierarchical = false;
/** a loop was detected on this request */
bool noCacheHack() const {
return USE_HTTP_VIOLATIONS && nocacheHack;
}
+
+ /// ban satisfying the request from the cache and ban storing the response
+ /// in the cache
+ /// \param reason summarizes the marking decision context (for debugging)
+ void disableCacheUse(const char *reason);
};
#endif /* SQUID_REQUESTFLAGS_H_ */
RunnersRegistry.cc \
RunnersRegistry.h \
Subscription.h \
+ SupportOrVeto.h \
TextException.cc \
TextException.h \
TypeTraits.h \
--- /dev/null
+/*
+ * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
+#ifndef SQUID_SRC_BASE_SUPPORTORVETO_H
+#define SQUID_SRC_BASE_SUPPORTORVETO_H
+
+#include "base/Optional.h"
+
+/// a boolean flag that is false by default and becomes permanently false if vetoed
+class SupportOrVeto
+{
+public:
+ /// either the current explicit decision or, by default, false
+ bool decision() const { return decision_.value_or(false); }
+
+ /// \copydoc decision()
+ operator bool() const { return decision(); }
+
+ /// Makes (or keeps) decision() true in the absence of veto() calls.
+ /// No effect if veto() has been called.
+ void support() { if (!decision_) decision_ = true; }
+
+ /// makes decision() false regardless of past or future support() calls
+ void veto() { decision_ = false; }
+
+private:
+ /// current decision (if any)
+ Optional<bool> decision_;
+};
+
+#endif /* SQUID_SRC_BASE_SUPPORTORVETO_H */
+
debugs(85, 3, "SECURITY ALERT: Host header forgery detected on " << http->getConn()->clientConnection <<
" (" << A << " does not match " << B << ") on URL: " << http->request->effectiveRequestUri());
- // NP: it is tempting to use 'flags.noCache' but that is all about READing cache data.
- // The problems here are about WRITE for new cache content, which means flags.cachable
- http->request->flags.cachable = false; // MUST NOT cache (for now)
+ // MUST NOT cache (for now). It is tempting to set flags.noCache, but
+ // that flag is about satisfying _this_ request. We are actually OK with
+ // satisfying this request from the cache, but want to prevent _other_
+ // requests from being satisfied using this response.
+ http->request->flags.cachable.veto();
+
// XXX: when we have updated the cache key to base on raw-IP + URI this cacheable limit can go.
http->request->flags.hierarchical = false; // MUST NOT pass to peers (for now)
// XXX: when we have sorted out the best way to relay requests properly to peers this hierarchical limit can go.
#endif
- request->flags.cachable = http->request->maybeCacheable();
+ if (http->request->maybeCacheable())
+ request->flags.cachable.support();
+ else
+ request->flags.cachable.veto();
if (clientHierarchical(http))
request->flags.hierarchical = true;
http->doCallouts();
}
-/** Test cache allow/deny configuration
- * Sets flags.cachable=1 if caching is not denied.
- */
+/// applies "cache allow/deny" rules, asynchronously if needed
void
ClientRequestContext::checkNoCache()
{
{
acl_checklist = nullptr;
if (answer.denied()) {
- http->request->flags.noCache = true; // do not read reply from cache
- http->request->flags.cachable = false; // do not store reply into cache
+ http->request->flags.disableCacheUse("a cache deny rule matched");
}
http->doCallouts();
}
*/
if (!we_do_ranges && request->multipartRangeRequest()) {
/* don't cache the result */
- request->flags.cachable = false;
+ request->flags.cachable.veto();
/* pretend it's not a range request */
request->ignoreRange("want to request the whole object");
request->flags.isRanged = false;
/* update timestamps */
pd->times.requested = squid_curtime;
pd_last_req_time = squid_curtime;
- req->flags.cachable = true;
+ req->flags.cachable.support(); // prevent RELEASE_REQUEST in storeCreateEntry()
/* the rest is based on clientReplyContext::processExpired() */
req->flags.refresh = true;
request->http_ver = Http::ProtocolVersion(Ftp::ProtocolVersion().major, Ftp::ProtocolVersion().minor);
// Our fake Request-URIs are not distinctive enough for caching to work
- request->flags.cachable = false; // XXX: reset later by maybeCacheable()
- request->flags.noCache = true;
+ request->flags.disableCacheUse("FTP command wrapper");
request->header.putStr(Http::HdrType::FTP_COMMAND, cmd.c_str());
request->header.putStr(Http::HdrType::FTP_ARGUMENTS, params.c_str()); // may be ""
assert(repContext != nullptr);
RequestFlags reqFlags;
- reqFlags.cachable = false; // force releaseRequest() in storeCreateEntry()
- reqFlags.noCache = true;
+ reqFlags.disableCacheUse("FTP response wrapper");
repContext->createStoreEntry(http->request->method, reqFlags);
http->storeEntry()->replaceHttpReply(reply);
}
auto req = HttpRequest::FromUrlXXX(url, mx);
RequestFlags flags;
- flags.cachable = true;
+ flags.cachable.support(); // prevent RELEASE_REQUEST in storeCreateEntry()
StoreEntry *e = storeCreateEntry(url, url, flags, Http::METHOD_GET);
assert(e);
testRock::createEntry(const int i)
{
RequestFlags flags;
- flags.cachable = true;
+ flags.cachable.support();
StoreEntry *const pe =
storeCreateEntry(storeId(i), "dummy log url", flags, Http::METHOD_GET);
auto &rep = pe->mem().adjustableBaseReply();
{
/* Create "vary" base object */
RequestFlags flags;
- flags.cachable = true;
+ flags.cachable.support();
StoreEntry *pe = storeCreateEntry("dummy url", "dummy log url", flags, Http::METHOD_GET);
auto &reply = pe->mem().adjustableBaseReply();
reply.setHeaders(Http::scOkay, "dummy test object", "x-squid-internal/test", 0, -1, squid_curtime + 100000);