Hello,
This patch fixes "miss_access" and "cache" checks when no ACL rules
matched.
The miss_access code allowed transactions to reach origin server when no
"allow" rules matched (and no "deny" rule matched either). This could
happen when a miss_access directive ACL could not make a "match" or
"mismatch" decision (e.g., the admin used a slow ACL or an ACL that
required authentication when the user was not authenticated).
Similarly, the "cache" directive code allowed requests to be served from
the cache (and responses to be stored in the cache) when no "allow" (and
"deny") rules matched. This behavior (established in v5 r14984) does not
contradict "Requests denied by this directive will not be..."
documentation, though.
I believe that both "miss_access" and "cache" directives should behave
like all other directives in similar contexts, i.e., "deny" in such
indeterminate cases because:
* It avoids problems with configurations like:
# broken if badGuys fails to match or mismatch (allows bad guys)
acl_driven_option allow !badGuys
That is what trunk r12176 has partially fixed.
* It makes ACL checking rules consistent with each other, especially if
there is no good reason for making exception for these two cases.
To help avoid similar problems in the future, and to improve code
consistency, I added an API that eliminates the need for direct
comparison with ACCESS_ALLOWED and ACCESS_DENIED.
Thanks,
Eduard.
Fixed "miss_access" and "cache" checks when no ACL rules matched.
The miss_access code allowed transactions to reach origin server when no
"allow" rules matched (and no "deny" rule matched either). This could
happen when a miss_access directive ACL could not make a "match" or
"mismatch" decision (e.g., the admin used a slow ACL or an ACL that
required authentication when the user was not authenticated).
Similarly, the "cache" directive code allowed requests to be served from
the cache (and responses to be stored in the cache) when no "allow" (and
"deny") rules matched. This behavior (established in v5 r14984) does not
contradict "Requests denied by this directive will not be..."
documentation, though.
I believe that both "miss_access" and "cache" directives should behave
similarly as all other directives, i.e., "deny" in such indeterminate
cases because:
* It avoids problems with configurations like:
# broken if badGuys fails to match or mismatch (allows bad guys)
acl_driven_option allow !badGuys
That is what trunk r12176 has partially fixed.
* It makes ACL checking rules consistent with each other, especially if
there is no good reason for making exception for these two cases.
To help avoid similar problems in the future, and to improve code
consistency, I added an API that eliminates the need for direct
comparison with ACCESS_ALLOWED and ACCESS_DENIED.
=== modified file 'src/DelayId.cc'
--- src/DelayId.cc 2017-01-01 00:12:22 +0000
+++ src/DelayId.cc 2017-05-04 17:14:41 +0000
@@ -74,61 +74,61 @@ DelayId::DelayClient(ClientHttpRequest *
if (r->client_addr.isNoAddr()) {
debugs(77, 2, "delayClient: WARNING: Called with 'NO_ADDR' address, ignoring");
return DelayId();
}
for (pool = 0; pool < DelayPools::pools(); ++pool) {
/* pools require explicit 'allow' to assign a client into them */
if (!DelayPools::delay_data[pool].access) {
debugs(77, DBG_IMPORTANT, "delay_pool " << pool <<
" has no delay_access configured. This means that no clients will ever use it.");
continue;
}
ACLFilledChecklist ch(DelayPools::delay_data[pool].access, r, NULL);
if (reply) {
ch.reply = reply;
HTTPMSGLOCK(reply);
}
#if FOLLOW_X_FORWARDED_FOR
if (Config.onoff.delay_pool_uses_indirect_client)
ch.src_addr = r->indirect_client_addr;
else
#endif /* FOLLOW_X_FORWARDED_FOR */
ch.src_addr = r->client_addr;
ch.my_addr = r->my_addr;
if (http->getConn() != NULL)
ch.conn(http->getConn());
- if (DelayPools::delay_data[pool].theComposite().getRaw() && ch.fastCheck() == ACCESS_ALLOWED) {
+ if (DelayPools::delay_data[pool].theComposite().getRaw() && ch.fastCheck().allowed()) {
DelayId result (pool + 1);
CompositePoolNode::CompositeSelectionDetails details;
details.src_addr = ch.src_addr;
#if USE_AUTH
details.user = r->auth_user_request;
#endif
details.tag = r->tag;
result.compositePosition(DelayPools::delay_data[pool].theComposite()->id(details));
return result;
}
}
return DelayId();
}
void
DelayId::setNoDelay(bool const newValue)
{
markedAsNoDelay = newValue;
}
/*
* this returns the number of bytes the client is permitted. it does not take
* into account bytes already buffered - that is up to the caller.
*/
int
DelayId::bytesWanted(int minimum, int maximum) const
{
/* unlimited */
=== modified file 'src/FwdState.cc'
--- src/FwdState.cc 2017-04-12 23:34:50 +0000
+++ src/FwdState.cc 2017-05-04 17:14:41 +0000
@@ -297,61 +297,61 @@ FwdState::~FwdState()
closeServerConnection("~FwdState");
serverDestinations.clear();
debugs(17, 3, "FwdState destructed, this=" << this);
}
/**
* This is the entry point for client-side to start forwarding
* a transaction. It is a static method that may or may not
* allocate a FwdState.
*/
void
FwdState::Start(const Comm::ConnectionPointer &clientConn, StoreEntry *entry, HttpRequest *request, const AccessLogEntryPointer &al)
{
/** \note
* client_addr == no_addr indicates this is an "internal" request
* from peer_digest.c, asn.c, netdb.c, etc and should always
* be allowed. yuck, I know.
*/
if ( Config.accessList.miss && !request->client_addr.isNoAddr() &&
!request->flags.internal && request->url.getScheme() != AnyP::PROTO_CACHE_OBJECT) {
/**
* Check if this host is allowed to fetch MISSES from us (miss_access).
* Intentionally replace the src_addr automatically selected by the checklist code
* we do NOT want the indirect client address to be tested here.
*/
ACLFilledChecklist ch(Config.accessList.miss, request, NULL);
ch.src_addr = request->client_addr;
- if (ch.fastCheck() == ACCESS_DENIED) {
+ if (!ch.fastCheck().allowed()) {
err_type page_id;
page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, 1);
if (page_id == ERR_NONE)
page_id = ERR_FORWARDING_DENIED;
ErrorState *anErr = new ErrorState(page_id, Http::scForbidden, request);
errorAppendEntry(entry, anErr); // frees anErr
return;
}
}
debugs(17, 3, HERE << "'" << entry->url() << "'");
/*
* This seems like an odd place to bind mem_obj and request.
* Might want to assert that request is NULL at this point
*/
entry->mem_obj->request = request;
#if URL_CHECKSUM_DEBUG
entry->mem_obj->checkUrlChecksum();
#endif
if (shutting_down) {
/* more yuck */
ErrorState *anErr = new ErrorState(ERR_SHUTTING_DOWN, Http::scServiceUnavailable, request);
errorAppendEntry(entry, anErr); // frees anErr
return;
}
@@ -1150,176 +1150,176 @@ FwdState::reforwardableStatus(const Http
default:
return false;
}
/* NOTREACHED */
}
/**
* Decide where details need to be gathered to correctly describe a persistent connection.
* What is needed:
* - the address/port details about this link
* - domain name of server at other end of this link (either peer or requested host)
*/
void
FwdState::pconnPush(Comm::ConnectionPointer &conn, const char *domain)
{
if (conn->getPeer()) {
fwdPconnPool->push(conn, NULL);
} else {
fwdPconnPool->push(conn, domain);
}
}
Comm::ConnectionPointer
FwdState::pconnPop(const Comm::ConnectionPointer &dest, const char *domain)
{
bool retriable = checkRetriable();
if (!retriable && Config.accessList.serverPconnForNonretriable) {
ACLFilledChecklist ch(Config.accessList.serverPconnForNonretriable, request, NULL);
- retriable = (ch.fastCheck() == ACCESS_ALLOWED);
+ retriable = ch.fastCheck().allowed();
}
// always call shared pool first because we need to close an idle
// connection there if we have to use a standby connection.
Comm::ConnectionPointer conn = fwdPconnPool->pop(dest, domain, retriable);
if (!Comm::IsConnOpen(conn)) {
// either there was no pconn to pop or this is not a retriable xaction
if (CachePeer *peer = dest->getPeer()) {
if (peer->standby.pool)
conn = peer->standby.pool->pop(dest, domain, true);
}
}
return conn; // open, closed, or nil
}
void
FwdState::initModule()
{
RegisterWithCacheManager();
}
void
FwdState::RegisterWithCacheManager(void)
{
Mgr::RegisterAction("forward", "Request Forwarding Statistics", fwdStats, 0, 1);
}
void
FwdState::logReplyStatus(int tries, const Http::StatusCode status)
{
if (status > Http::scInvalidHeader)
return;
assert(tries >= 0);
if (tries > MAX_FWD_STATS_IDX)
tries = MAX_FWD_STATS_IDX;
++ FwdReplyCodes[tries][status];
}
/**** PRIVATE NON-MEMBER FUNCTIONS ********************************************/
/*
* DPW 2007-05-19
* Formerly static, but now used by client_side_request.cc
*/
/// Checks for a TOS value to apply depending on the ACL
tos_t
aclMapTOS(acl_tos * head, ACLChecklist * ch)
{
for (acl_tos *l = head; l; l = l->next) {
- if (!l->aclList || ch->fastCheck(l->aclList) == ACCESS_ALLOWED)
+ if (!l->aclList || ch->fastCheck(l->aclList).allowed())
return l->tos;
}
return 0;
}
/// Checks for a netfilter mark value to apply depending on the ACL
nfmark_t
aclMapNfmark(acl_nfmark * head, ACLChecklist * ch)
{
for (acl_nfmark *l = head; l; l = l->next) {
- if (!l->aclList || ch->fastCheck(l->aclList) == ACCESS_ALLOWED)
+ if (!l->aclList || ch->fastCheck(l->aclList).allowed())
return l->nfmark;
}
return 0;
}
void
getOutgoingAddress(HttpRequest * request, Comm::ConnectionPointer conn)
{
// skip if an outgoing address is already set.
if (!conn->local.isAnyAddr()) return;
// ensure that at minimum the wildcard local matches remote protocol
if (conn->remote.isIPv4())
conn->local.setIPv4();
// maybe use TPROXY client address
if (request && request->flags.spoofClientIp) {
if (!conn->getPeer() || !conn->getPeer()->options.no_tproxy) {
#if FOLLOW_X_FORWARDED_FOR && LINUX_NETFILTER
if (Config.onoff.tproxy_uses_indirect_client)
conn->local = request->indirect_client_addr;
else
#endif
conn->local = request->client_addr;
// some flags need setting on the socket to use this address
conn->flags |= COMM_DOBIND;
conn->flags |= COMM_TRANSPARENT;
return;
}
// else no tproxy today ...
}
if (!Config.accessList.outgoing_address) {
return; // anything will do.
}
ACLFilledChecklist ch(NULL, request, NULL);
ch.dst_peer_name = conn->getPeer() ? conn->getPeer()->name : NULL;
ch.dst_addr = conn->remote;
// TODO use the connection details in ACL.
// needs a bit of rework in ACLFilledChecklist to use Comm::Connection instead of ConnStateData
for (Acl::Address *l = Config.accessList.outgoing_address; l; l = l->next) {
/* check if the outgoing address is usable to the destination */
if (conn->remote.isIPv4() != l->addr.isIPv4()) continue;
/* check ACLs for this outgoing address */
- if (!l->aclList || ch.fastCheck(l->aclList) == ACCESS_ALLOWED) {
+ if (!l->aclList || ch.fastCheck(l->aclList).allowed()) {
conn->local = l->addr;
return;
}
}
}
tos_t
GetTosToServer(HttpRequest * request)
{
ACLFilledChecklist ch(NULL, request, NULL);
return aclMapTOS(Ip::Qos::TheConfig.tosToServer, &ch);
}
nfmark_t
GetNfmarkToServer(HttpRequest * request)
{
ACLFilledChecklist ch(NULL, request, NULL);
return aclMapNfmark(Ip::Qos::TheConfig.nfmarkToServer, &ch);
}
void
GetMarkingsToServer(HttpRequest * request, Comm::Connection &conn)
{
// Get the server side TOS and Netfilter mark to be set on the connection.
if (Ip::Qos::TheConfig.isAclTosActive()) {
conn.tos = GetTosToServer(request);
debugs(17, 3, "from " << conn.local << " tos " << int(conn.tos));
}
#if SO_MARK && USE_LIBCAP
=== modified file 'src/HttpHeaderTools.cc'
--- src/HttpHeaderTools.cc 2017-01-01 00:12:22 +0000
+++ src/HttpHeaderTools.cc 2017-05-04 17:14:41 +0000
@@ -262,61 +262,61 @@ httpHeaderQuoteString(const char *raw)
quotedStr.append('"');
return quotedStr;
}
/**
* Checks the anonymizer (header_access) configuration.
*
* \retval 0 Header is explicitly blocked for removal
* \retval 1 Header is explicitly allowed
* \retval 1 Header has been replaced, the current version can be used.
* \retval 1 Header has no access controls to test
*/
static int
httpHdrMangle(HttpHeaderEntry * e, HttpRequest * request, HeaderManglers *hms)
{
int retval;
assert(e);
const headerMangler *hm = hms->find(*e);
/* mangler or checklist went away. default allow */
if (!hm || !hm->access_list) {
debugs(66, 7, "couldn't find mangler or access list. Allowing");
return 1;
}
ACLFilledChecklist checklist(hm->access_list, request, NULL);
- if (checklist.fastCheck() == ACCESS_ALLOWED) {
+ if (checklist.fastCheck().allowed()) {
/* aclCheckFast returns true for allow. */
debugs(66, 7, "checklist for mangler is positive. Mangle");
retval = 1;
} else if (NULL == hm->replacement) {
/* It was denied, and we don't have any replacement */
debugs(66, 7, "checklist denied, we have no replacement. Pass");
retval = 0;
} else {
/* It was denied, but we have a replacement. Replace the
* header on the fly, and return that the new header
* is allowed.
*/
debugs(66, 7, "checklist denied but we have replacement. Replace");
e->value = hm->replacement;
retval = 1;
}
return retval;
}
/** Mangles headers for a list of headers. */
void
httpHdrMangleList(HttpHeader *l, HttpRequest *request, const AccessLogEntryPointer &al, req_or_rep_t req_or_rep)
{
HttpHeaderEntry *e;
HttpHeaderPos p = HttpHeaderInitPos;
/* check with anonymizer tables */
HeaderManglers *hms = nullptr;
HeaderWithAclList *headersAdd = nullptr;
@@ -451,53 +451,53 @@ HeaderManglers::find(const HttpHeaderEnt
if (e.id != Http::HdrType::OTHER && Http::any_HdrType_enum_value(e.id) &&
known[e.id].access_list)
return &known[e.id];
// a custom header
if (e.id == Http::HdrType::OTHER) {
// does it have an ACL list configured?
// Optimize: use a name type that we do not need to convert to here
const ManglersByName::const_iterator i = custom.find(e.name.termedBuf());
if (i != custom.end())
return &i->second;
}
// Next-to-last resort: "Other" rules match any custom header
if (e.id == Http::HdrType::OTHER && known[Http::HdrType::OTHER].access_list)
return &known[Http::HdrType::OTHER];
// Last resort: "All" rules match any header
if (all.access_list)
return &all;
return NULL;
}
void
httpHdrAdd(HttpHeader *heads, HttpRequest *request, const AccessLogEntryPointer &al, HeaderWithAclList &headersAdd)
{
ACLFilledChecklist checklist(NULL, request, NULL);
for (HeaderWithAclList::const_iterator hwa = headersAdd.begin(); hwa != headersAdd.end(); ++hwa) {
- if (!hwa->aclList || checklist.fastCheck(hwa->aclList) == ACCESS_ALLOWED) {
+ if (!hwa->aclList || checklist.fastCheck(hwa->aclList).allowed()) {
const char *fieldValue = NULL;
MemBuf mb;
if (hwa->quoted) {
if (al != NULL) {
mb.init();
hwa->valueFormat->assemble(mb, al, 0);
fieldValue = mb.content();
}
} else {
fieldValue = hwa->fieldValue.c_str();
}
if (!fieldValue || fieldValue[0] == '\0')
fieldValue = "-";
HttpHeaderEntry *e = new HttpHeaderEntry(hwa->fieldId, hwa->fieldName.c_str(),
fieldValue);
heads->addEntry(e);
}
}
}
=== modified file 'src/HttpReply.cc'
--- src/HttpReply.cc 2017-02-20 04:56:00 +0000
+++ src/HttpReply.cc 2017-05-04 17:14:41 +0000
@@ -501,61 +501,61 @@ HttpReply::expectedBodyTooLarge(HttpRequ
int64_t expectedSize = -1;
if (!expectingBody(request.method, expectedSize))
return false;
debugs(58, 6, HERE << expectedSize << " >? " << bodySizeMax);
if (expectedSize < 0) // expecting body of an unknown length
return false;
return expectedSize > bodySizeMax;
}
void
HttpReply::calcMaxBodySize(HttpRequest& request) const
{
// hack: -2 is used as "we have not calculated max body size yet" state
if (bodySizeMax != -2) // already tried
return;
bodySizeMax = -1;
// short-circuit ACL testing if there are none configured
if (!Config.ReplyBodySize)
return;
ACLFilledChecklist ch(NULL, &request, NULL);
// XXX: cont-cast becomes irrelevant when checklist is HttpReply::Pointer
ch.reply = const_cast<HttpReply *>(this);
HTTPMSGLOCK(ch.reply);
for (AclSizeLimit *l = Config.ReplyBodySize; l; l = l -> next) {
/* if there is no ACL list or if the ACLs listed match use this size value */
- if (!l->aclList || ch.fastCheck(l->aclList) == ACCESS_ALLOWED) {
+ if (!l->aclList || ch.fastCheck(l->aclList).allowed()) {
debugs(58, 4, HERE << "bodySizeMax=" << bodySizeMax);
bodySizeMax = l->size; // may be -1
break;
}
}
}
// XXX: check that this is sufficient for eCAP cloning
HttpReply *
HttpReply::clone() const
{
HttpReply *rep = new HttpReply();
rep->sline = sline; // used in hdrCacheInit() call below
rep->header.append(&header);
rep->hdrCacheInit();
rep->hdr_sz = hdr_sz;
rep->http_ver = http_ver;
rep->pstate = pstate;
rep->body_pipe = body_pipe;
// keep_alive is handled in hdrCacheInit()
return rep;
}
bool
HttpReply::inheritProperties(const Http::Message *aMsg)
{
const HttpReply *aRep = dynamic_cast<const HttpReply*>(aMsg);
if (!aRep)
return false;
=== modified file 'src/HttpRequest.cc'
--- src/HttpRequest.cc 2017-02-20 04:56:00 +0000
+++ src/HttpRequest.cc 2017-05-04 17:14:41 +0000
@@ -577,61 +577,61 @@ HttpRequest::conditional() const
}
void
HttpRequest::recordLookup(const Dns::LookupDetails &dns)
{
if (dns.wait >= 0) { // known delay
if (dnsWait >= 0) // have recorded DNS wait before
dnsWait += dns.wait;
else
dnsWait = dns.wait;
}
}
int64_t
HttpRequest::getRangeOffsetLimit()
{
/* -2 is the starting value of rangeOffsetLimit.
* If it is -2, that means we haven't checked it yet.
* Otherwise, return the current value */
if (rangeOffsetLimit != -2)
return rangeOffsetLimit;
rangeOffsetLimit = 0; // default value for rangeOffsetLimit
ACLFilledChecklist ch(NULL, this, NULL);
ch.src_addr = client_addr;
ch.my_addr = my_addr;
for (AclSizeLimit *l = Config.rangeOffsetLimit; l; l = l -> next) {
/* if there is no ACL list or if the ACLs listed match use this limit value */
- if (!l->aclList || ch.fastCheck(l->aclList) == ACCESS_ALLOWED) {
+ if (!l->aclList || ch.fastCheck(l->aclList).allowed()) {
debugs(58, 4, HERE << "rangeOffsetLimit=" << rangeOffsetLimit);
rangeOffsetLimit = l->size; // may be -1
break;
}
}
return rangeOffsetLimit;
}
void
HttpRequest::ignoreRange(const char *reason)
{
if (range) {
debugs(73, 3, static_cast<void*>(range) << " for " << reason);
delete range;
range = NULL;
}
// Some callers also reset isRanged but it may not be safe for all callers:
// isRanged is used to determine whether a weak ETag comparison is allowed,
// and that check should not ignore the Range header if it was present.
// TODO: Some callers also delete HDR_RANGE, HDR_REQUEST_RANGE. Should we?
}
bool
HttpRequest::canHandle1xx() const
{
// old clients do not support 1xx unless they sent Expect: 100-continue
// (we reject all other Http::HdrType::EXPECT values so just check for Http::HdrType::EXPECT)
if (http_ver <= Http::ProtocolVersion(1,0) && !header.has(Http::HdrType::EXPECT))
return false;
=== modified file 'src/Notes.cc'
--- src/Notes.cc 2017-02-14 02:03:27 +0000
+++ src/Notes.cc 2017-05-05 13:36:07 +0000
@@ -51,61 +51,61 @@ Note::Value::format(const AccessLogEntry
if (al && valueFormat) {
static MemBuf mb;
mb.reset();
valueFormat->assemble(mb, al, 0);
theFormattedValue.assign(mb.content());
return theFormattedValue;
}
return theValue;
}
Note::Value::Pointer
Note::addValue(const char *value, const bool quoted, const char *descr, const Value::Method m)
{
values.push_back(new Value(value, quoted, descr, m));
return values.back();
}
bool
Note::match(HttpRequest *request, HttpReply *reply, const AccessLogEntry::Pointer &al, SBuf &matched)
{
ACLFilledChecklist ch(nullptr, request, nullptr);
ch.reply = reply;
if (reply)
HTTPMSGLOCK(ch.reply);
for (auto v: values) {
assert(v->aclList);
const int ret = ch.fastCheck(v->aclList);
debugs(93, 5, "Check for header name: " << theKey << ": " << v->value() <<
", HttpRequest: " << request << " HttpReply: " << reply << " matched: " << ret);
- if (ret == ACCESS_ALLOWED) {
+ if (ret.allowed()) {
matched = v->format(al);
return true;
}
}
matched.clear();
return false;
}
void
Note::updateNotePairs(NotePairs::Pointer pairs, const CharacterSet *delimiters, const AccessLogEntryPointer &al)
{
for (auto v: values) {
const SBuf &formatted = v->format(al);
if (!pairs->empty() && v->method() == Value::mhReplace)
pairs->remove(theKey);
if (delimiters)
pairs->addStrList(key(), formatted, *delimiters);
else
pairs->add(key(), formatted);
}
}
void
Note::dump(StoreEntry *entry, const char *k)
{
for (auto v: values) {
storeAppendPrintf(entry, "%s %.*s %s",
k, key().length(), key().rawContent(), ConfigParser::QuoteString(SBufToString(v->value())));
dump_acl_list(entry, v->aclList);
storeAppendPrintf(entry, "\n");
=== modified file 'src/acl/Acl.h'
--- src/acl/Acl.h 2017-01-30 12:46:15 +0000
+++ src/acl/Acl.h 2017-05-05 13:33:45 +0000
@@ -1,48 +1,49 @@
/*
* Copyright (C) 1996-2017 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_ACL_H
#define SQUID_ACL_H
#include "acl/forward.h"
#include "base/CharacterSet.h"
#include "cbdata.h"
#include "defines.h"
#include "dlink.h"
#include "sbuf/List.h"
+#include <algorithm>
#include <map>
#include <ostream>
#include <string>
#include <vector>
class ConfigParser;
typedef char ACLFlag;
// ACLData Flags
#define ACL_F_REGEX_CASE 'i'
#define ACL_F_NO_LOOKUP 'n'
#define ACL_F_STRICT 's'
#define ACL_F_SUBSTRING 'm'
#define ACL_F_END '\0'
/**
* \ingroup ACLAPI
* Used to hold a list of one-letter flags which can be passed as parameters
* to acls (eg '-i', '-n' etc)
*/
class ACLFlags
{
public:
enum Status
{
notSupported,
noParameter,
parameterOptional,
parameterRequired
};
@@ -211,60 +212,68 @@ typedef enum {
// Authentication ACL result states
ACCESS_AUTH_REQUIRED, // Missing Credentials
} aclMatchCode;
/// \ingroup ACLAPI
/// ACL check answer; TODO: Rename to Acl::Answer
class allow_t
{
public:
// not explicit: allow "aclMatchCode to allow_t" conversions (for now)
allow_t(const aclMatchCode aCode, int aKind = 0): code(aCode), kind(aKind) {}
allow_t(): code(ACCESS_DUNNO), kind(0) {}
bool operator ==(const aclMatchCode aCode) const {
return code == aCode;
}
bool operator !=(const aclMatchCode aCode) const {
return !(*this == aCode);
}
bool operator ==(const allow_t allow) const {
return code == allow.code && kind == allow.kind;
}
operator aclMatchCode() const {
return code;
}
+ /// whether an 'allow' rule matched
+ bool allowed() const { return code == ACCESS_ALLOWED; }
+
+ /// whether there is a matched rule in 'codes' vector
+ bool someRuleMatched(const std::vector<aclMatchCode> &codes) const {
+ return std::find(codes.begin(), codes.end(), code) != codes.end();
+ }
+
aclMatchCode code; ///< ACCESS_* code
int kind; ///< which custom access list verb matched
};
inline std::ostream &
operator <<(std::ostream &o, const allow_t a)
{
switch (a) {
case ACCESS_DENIED:
o << "DENIED";
break;
case ACCESS_ALLOWED:
o << "ALLOWED";
break;
case ACCESS_DUNNO:
o << "DUNNO";
break;
case ACCESS_AUTH_REQUIRED:
o << "AUTH_REQUIRED";
break;
}
return o;
}
/// \ingroup ACLAPI
class acl_proxy_auth_match_cache
{
MEMPROXY_CLASS(acl_proxy_auth_match_cache);
public:
=== modified file 'src/acl/Tree.h'
--- src/acl/Tree.h 2017-04-17 21:24:33 +0000
+++ src/acl/Tree.h 2017-05-04 17:14:41 +0000
@@ -25,61 +25,61 @@ class Tree: public OrNode
public:
/// dumps <name, action, rule, new line> tuples
/// the supplied converter maps action.kind to a string
template <class ActionToStringConverter>
SBufList treeDump(const char *name, ActionToStringConverter converter) const;
/// Returns the corresponding action after a successful tree match.
allow_t winningAction() const;
/// what action to use if no nodes matched
allow_t lastAction() const;
/// appends and takes control over the rule with a given action
void add(ACL *rule, const allow_t &action);
void add(ACL *rule); ///< same as InnerNode::add()
protected:
/// Acl::OrNode API
virtual bool bannedAction(ACLChecklist *, Nodes::const_iterator) const override;
allow_t actionAt(const Nodes::size_type pos) const;
/// if not empty, contains actions corresponding to InnerNode::nodes
typedef std::vector<allow_t> Actions;
Actions actions;
};
inline const char *
AllowOrDeny(const allow_t &action)
{
- return action == ACCESS_ALLOWED ? "allow" : "deny";
+ return action.allowed() ? "allow" : "deny";
}
template <class ActionToStringConverter>
inline SBufList
Tree::treeDump(const char *prefix, ActionToStringConverter converter) const
{
SBufList text;
Actions::const_iterator action = actions.begin();
typedef Nodes::const_iterator NCI;
for (NCI node = nodes.begin(); node != nodes.end(); ++node) {
text.push_back(SBuf(prefix));
if (action != actions.end()) {
static const SBuf DefaultActString("???");
const char *act = converter(*action);
text.push_back(act ? SBuf(act) : DefaultActString);
++action;
}
text.splice(text.end(), (*node)->dump());
text.push_back(SBuf("\n"));
}
return text;
}
} // namespace Acl
#endif /* SQUID_ACL_TREE_H */
=== modified file 'src/adaptation/AccessCheck.cc'
--- src/adaptation/AccessCheck.cc 2017-01-01 00:12:22 +0000
+++ src/adaptation/AccessCheck.cc 2017-05-04 17:14:41 +0000
@@ -147,61 +147,61 @@ Adaptation::AccessCheck::checkCandidates
Must(done());
}
void
Adaptation::AccessCheck::AccessCheckCallbackWrapper(allow_t answer, void *data)
{
debugs(93, 8, HERE << "callback answer=" << answer);
AccessCheck *ac = (AccessCheck*)data;
/** \todo AYJ 2008-06-12: If answer == ACCESS_AUTH_REQUIRED
* we should be kicking off an authentication before continuing
* with this request. see bug 2400 for details.
*/
// convert to async call to get async call protections and features
typedef UnaryMemFunT<AccessCheck, allow_t> MyDialer;
AsyncCall::Pointer call =
asyncCall(93,7, "Adaptation::AccessCheck::noteAnswer",
MyDialer(ac, &Adaptation::AccessCheck::noteAnswer, answer));
ScheduleCallHere(call);
}
/// process the results of the ACL check
void
Adaptation::AccessCheck::noteAnswer(allow_t answer)
{
Must(!candidates.empty()); // the candidate we were checking must be there
debugs(93,5, HERE << topCandidate() << " answer=" << answer);
- if (answer == ACCESS_ALLOWED) { // the rule matched
+ if (answer.allowed()) { // the rule matched
ServiceGroupPointer g = topGroup();
if (g != NULL) { // the corresponding group found
callBack(g);
Must(done());
return;
}
}
// no match or the group disappeared during reconfiguration
candidates.erase(candidates.begin());
checkCandidates();
}
/// call back with a possibly nil group; the job ends here because all failures
/// at this point are fatal to the access check process
void
Adaptation::AccessCheck::callBack(const ServiceGroupPointer &g)
{
debugs(93,3, HERE << g);
CallJobHere1(93, 5, theInitiator, Adaptation::Initiator,
noteAdaptationAclCheckDone, g);
mustStop("done"); // called back or will never be able to call back
}
Adaptation::ServiceGroupPointer
Adaptation::AccessCheck::topGroup() const
{
ServiceGroupPointer g;
if (candidates.size()) {
if (AccessRule *r = FindRule(topCandidate())) {
=== modified file 'src/adaptation/icap/Launcher.cc'
--- src/adaptation/icap/Launcher.cc 2017-02-16 11:51:56 +0000
+++ src/adaptation/icap/Launcher.cc 2017-05-04 17:14:41 +0000
@@ -118,61 +118,61 @@ bool Adaptation::Icap::Launcher::canRetr
// We do not check and can exceed zero repeat limit when retrying.
// This is by design as the limit does not apply to pconn retrying.
return !shutting_down && info.isRetriable;
}
bool Adaptation::Icap::Launcher::canRepeat(Adaptation::Icap::XactAbortInfo &info) const
{
debugs(93,9, HERE << shutting_down);
if (theLaunches >= TheConfig.repeat_limit || shutting_down)
return false;
debugs(93,9, HERE << info.isRepeatable); // TODO: update and use status()
if (!info.isRepeatable)
return false;
debugs(93,9, HERE << info.icapReply);
if (!info.icapReply) // did not get to read an ICAP reply; a timeout?
return true;
debugs(93,9, info.icapReply->sline.status());
// XXX: Http::scNone is not the only sign of parse error
// XXX: if there is a specific HTTP error code describing the problem, that may be set
if (info.icapReply->sline.status() == Http::scNone) // failed to parse the reply; I/O err
return true;
ACLFilledChecklist *cl =
new ACLFilledChecklist(TheConfig.repeat, info.icapRequest, dash_str);
cl->reply = info.icapReply;
HTTPMSGLOCK(cl->reply);
- bool result = cl->fastCheck() == ACCESS_ALLOWED;
+ bool result = cl->fastCheck().allowed();
delete cl;
return result;
}
/* ICAPXactAbortInfo */
Adaptation::Icap::XactAbortInfo::XactAbortInfo(HttpRequest *anIcapRequest,
HttpReply *anIcapReply, bool beRetriable, bool beRepeatable):
icapRequest(anIcapRequest),
icapReply(anIcapReply),
isRetriable(beRetriable),
isRepeatable(beRepeatable)
{
if (icapRequest)
HTTPMSGLOCK(icapRequest);
if (icapReply)
HTTPMSGLOCK(icapReply);
}
Adaptation::Icap::XactAbortInfo::XactAbortInfo(const Adaptation::Icap::XactAbortInfo &i):
icapRequest(i.icapRequest),
icapReply(i.icapReply),
isRetriable(i.isRetriable),
isRepeatable(i.isRepeatable)
{
if (icapRequest)
HTTPMSGLOCK(icapRequest);
if (icapReply)
HTTPMSGLOCK(icapReply);
}
=== modified file 'src/auth/UserRequest.cc'
--- src/auth/UserRequest.cc 2017-01-30 13:01:33 +0000
+++ src/auth/UserRequest.cc 2017-05-04 17:14:41 +0000
@@ -442,61 +442,61 @@ Auth::UserRequest::tryToAuthenticateAndS
if (t != NULL && t->lastReply != AUTH_ACL_CANNOT_AUTHENTICATE && t->lastReply != AUTH_ACL_HELPER) {
if (*aUR == NULL)
*aUR = t;
if (request->auth_user_request == NULL && t->lastReply == AUTH_AUTHENTICATED) {
request->auth_user_request = t;
}
return t->lastReply;
}
// ok, call the actual authenticator routine.
AuthAclState result = authenticate(aUR, headertype, request, conn, src_addr, al);
// auth process may have changed the UserRequest we are dealing with
t = authTryGetUser(*aUR, conn, request);
if (t != NULL && result != AUTH_ACL_CANNOT_AUTHENTICATE && result != AUTH_ACL_HELPER)
t->lastReply = result;
return result;
}
static Auth::ConfigVector &
schemesConfig(HttpRequest *request, HttpReply *rep)
{
if (!Auth::TheConfig.schemeLists.empty() && Auth::TheConfig.schemeAccess) {
ACLFilledChecklist ch(NULL, request, NULL);
ch.reply = rep;
HTTPMSGLOCK(ch.reply);
const allow_t answer = ch.fastCheck(Auth::TheConfig.schemeAccess);
- if (answer == ACCESS_ALLOWED)
+ if (answer.allowed())
return Auth::TheConfig.schemeLists.at(answer.kind).authConfigs;
}
return Auth::TheConfig.schemes;
}
void
Auth::UserRequest::addReplyAuthHeader(HttpReply * rep, Auth::UserRequest::Pointer auth_user_request, HttpRequest * request, int accelerated, int internal)
/* send the auth types we are configured to support (and have compiled in!) */
{
Http::HdrType type;
switch (rep->sline.status()) {
case Http::scProxyAuthenticationRequired:
/* Proxy authorisation needed */
type = Http::HdrType::PROXY_AUTHENTICATE;
break;
case Http::scUnauthorized:
/* WWW Authorisation needed */
type = Http::HdrType::WWW_AUTHENTICATE;
break;
default:
/* Keep GCC happy */
/* some other HTTP status */
type = Http::HdrType::BAD_HDR;
break;
}
=== modified file 'src/client_side.cc'
--- src/client_side.cc 2017-04-14 09:27:23 +0000
+++ src/client_side.cc 2017-05-04 17:14:41 +0000
@@ -435,61 +435,61 @@ ClientHttpRequest::logRequest()
for (auto h: Config.notes) {
if (h->match(request, al->reply, NULL, matched)) {
request->notes()->add(h->key(), matched);
debugs(33, 3, h->key() << " " << matched);
}
}
// The al->notes and request->notes must point to the same object.
al->syncNotes(request);
}
ACLFilledChecklist checklist(NULL, request, NULL);
if (al->reply) {
checklist.reply = al->reply;
HTTPMSGLOCK(checklist.reply);
}
if (request) {
HTTPMSGUNLOCK(al->adapted_request);
al->adapted_request = request;
HTTPMSGLOCK(al->adapted_request);
}
accessLogLog(al, &checklist);
bool updatePerformanceCounters = true;
if (Config.accessList.stats_collection) {
ACLFilledChecklist statsCheck(Config.accessList.stats_collection, request, NULL);
if (al->reply) {
statsCheck.reply = al->reply;
HTTPMSGLOCK(statsCheck.reply);
}
- updatePerformanceCounters = (statsCheck.fastCheck() == ACCESS_ALLOWED);
+ updatePerformanceCounters = statsCheck.fastCheck().allowed();
}
if (updatePerformanceCounters) {
if (request)
updateCounters();
if (getConn() != NULL && getConn()->clientConnection != NULL)
clientdbUpdate(getConn()->clientConnection->remote, logType, AnyP::PROTO_HTTP, out.size);
}
}
void
ClientHttpRequest::freeResources()
{
safe_free(uri);
safe_free(log_uri);
safe_free(redirect.location);
range_iter.boundary.clean();
HTTPMSGUNLOCK(request);
if (client_stream.tail)
clientStreamAbort((clientStreamNode *)client_stream.tail->data, this);
}
void
httpRequestFree(void *data)
{
ClientHttpRequest *http = (ClientHttpRequest *)data;
assert(http != NULL);
delete http;
@@ -1500,115 +1500,115 @@ bool ConnStateData::serveDelayedError(Ht
clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
assert(repContext);
debugs(33, 5, "Responding with delated error for " << http->uri);
repContext->setReplyToStoreEntry(sslServerBump->entry, "delayed SslBump error");
// save the original request for logging purposes
if (!context->http->al->request) {
context->http->al->request = http->request;
HTTPMSGLOCK(context->http->al->request);
}
// Get error details from the fake certificate-peeking request.
http->request->detailError(sslServerBump->request->errType, sslServerBump->request->errDetail);
context->pullData();
return true;
}
// In bump-server-first mode, we have not necessarily seen the intended
// server name at certificate-peeking time. Check for domain mismatch now,
// when we can extract the intended name from the bumped HTTP request.
if (const Security::CertPointer &srvCert = sslServerBump->serverCert) {
HttpRequest *request = http->request;
if (!Ssl::checkX509ServerValidity(srvCert.get(), request->url.host())) {
debugs(33, 2, "SQUID_X509_V_ERR_DOMAIN_MISMATCH: Certificate " <<
"does not match domainname " << request->url.host());
bool allowDomainMismatch = false;
if (Config.ssl_client.cert_error) {
ACLFilledChecklist check(Config.ssl_client.cert_error, request, dash_str);
check.sslErrors = new Security::CertErrors(Security::CertError(SQUID_X509_V_ERR_DOMAIN_MISMATCH, srvCert));
- allowDomainMismatch = (check.fastCheck() == ACCESS_ALLOWED);
+ allowDomainMismatch = check.fastCheck().allowed();
delete check.sslErrors;
check.sslErrors = NULL;
}
if (!allowDomainMismatch) {
quitAfterError(request);
clientStreamNode *node = context->getClientReplyContext();
clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
assert (repContext);
// Fill the server IP and hostname for error page generation.
HttpRequest::Pointer const & peekerRequest = sslServerBump->request;
request->hier.note(peekerRequest->hier.tcpServer, request->url.host());
// Create an error object and fill it
ErrorState *err = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, request);
err->src_addr = clientConnection->remote;
Ssl::ErrorDetail *errDetail = new Ssl::ErrorDetail(
SQUID_X509_V_ERR_DOMAIN_MISMATCH,
srvCert.get(), nullptr);
err->detail = errDetail;
// Save the original request for logging purposes.
if (!context->http->al->request) {
context->http->al->request = request;
HTTPMSGLOCK(context->http->al->request);
}
repContext->setReplyToError(request->method, err);
assert(context->http->out.offset == 0);
context->pullData();
return true;
}
}
}
return false;
}
#endif // USE_OPENSSL
/**
* Check on_unsupported_protocol checklist and return true if tunnel mode selected
* or false otherwise
*/
bool
clientTunnelOnError(ConnStateData *conn, Http::StreamPointer &context, HttpRequest::Pointer &request, const HttpRequestMethod& method, err_type requestError)
{
if (conn->mayTunnelUnsupportedProto()) {
ACLFilledChecklist checklist(Config.accessList.on_unsupported_protocol, request.getRaw(), nullptr);
checklist.requestErrorType = requestError;
checklist.src_addr = conn->clientConnection->remote;
checklist.my_addr = conn->clientConnection->local;
checklist.conn(conn);
allow_t answer = checklist.fastCheck();
- if (answer == ACCESS_ALLOWED && answer.kind == 1) {
+ if (answer.allowed() && answer.kind == 1) {
debugs(33, 3, "Request will be tunneled to server");
if (context) {
assert(conn->pipeline.front() == context); // XXX: still assumes HTTP/1 semantics
context->finished(); // Will remove from conn->pipeline queue
}
Comm::SetSelect(conn->clientConnection->fd, COMM_SELECT_READ, NULL, NULL, 0);
return conn->initiateTunneledRequest(request, Http::METHOD_NONE, "unknown-protocol", conn->preservedClientData);
} else {
debugs(33, 3, "Continue with returning the error: " << requestError);
}
}
return false;
}
void
clientProcessRequestFinished(ConnStateData *conn, const HttpRequest::Pointer &request)
{
/*
* DPW 2007-05-18
* Moved the TCP_RESET feature from clientReplyContext::sendMoreData
* to here because calling comm_reset_close() causes http to
* be freed before accessing.
*/
if (request != NULL && request->flags.resetTcp && Comm::IsConnOpen(conn->clientConnection)) {
debugs(33, 3, HERE << "Sending TCP RST on " << conn->clientConnection);
conn->flags.readMore = false;
comm_reset_close(conn->clientConnection);
}
}
@@ -1635,61 +1635,61 @@ clientProcessRequest(ConnStateData *conn
// setup and body_pipe preparation blobs are needed for FTP.
request->clientConnectionManager = conn;
request->flags.accelerated = http->flags.accel;
request->flags.sslBumped=conn->switchedToHttps();
request->flags.ignoreCc = conn->port->ignore_cc;
// TODO: decouple http->flags.accel from request->flags.sslBumped
request->flags.noDirect = (request->flags.accelerated && !request->flags.sslBumped) ?
!conn->port->allow_direct : 0;
request->sources |= isFtp ? Http::Message::srcFtp :
((request->flags.sslBumped || conn->port->transport.protocol == AnyP::PROTO_HTTPS) ? Http::Message::srcHttps : Http::Message::srcHttp);
#if USE_AUTH
if (request->flags.sslBumped) {
if (conn->getAuth() != NULL)
request->auth_user_request = conn->getAuth();
}
#endif
/** \par
* If transparent or interception mode is working clone the transparent and interception flags
* from the port settings to the request.
*/
if (http->clientConnection != NULL) {
request->flags.intercepted = ((http->clientConnection->flags & COMM_INTERCEPTION) != 0);
request->flags.interceptTproxy = ((http->clientConnection->flags & COMM_TRANSPARENT) != 0 ) ;
static const bool proxyProtocolPort = (conn->port != NULL) ? conn->port->flags.proxySurrogate : false;
if (request->flags.interceptTproxy && !proxyProtocolPort) {
if (Config.accessList.spoof_client_ip) {
ACLFilledChecklist *checklist = clientAclChecklistCreate(Config.accessList.spoof_client_ip, http);
- request->flags.spoofClientIp = (checklist->fastCheck() == ACCESS_ALLOWED);
+ request->flags.spoofClientIp = checklist->fastCheck().allowed();
delete checklist;
} else
request->flags.spoofClientIp = true;
} else
request->flags.spoofClientIp = false;
}
if (internalCheck(request->url.path())) {
if (internalHostnameIs(request->url.host()) && request->url.port() == getMyPort()) {
debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->url.authority(true));
http->flags.internal = true;
} else if (Config.onoff.global_internal_static && internalStaticCheck(request->url.path())) {
debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->url.authority(true) << " (global_internal_static on)");
request->url.setScheme(AnyP::PROTO_HTTP, "http");
request->url.host(internalHostname());
request->url.port(getMyPort());
http->flags.internal = true;
} else
debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->url.authority(true) << " (not this proxy)");
}
request->flags.internal = http->flags.internal;
setLogUri (http, urlCanonicalClean(request.getRaw()));
request->client_addr = conn->clientConnection->remote; // XXX: remove request->client_addr member.
#if FOLLOW_X_FORWARDED_FOR
// indirect client gets stored here because it is an HTTP header result (from X-Forwarded-For:)
// not details about the TCP connection itself
request->indirect_client_addr = conn->clientConnection->remote;
#endif /* FOLLOW_X_FORWARDED_FOR */
request->my_addr = conn->clientConnection->local;
@@ -1831,61 +1831,61 @@ ConnStateData::concurrentRequestQueueFil
const int internalRequest = 0;
#endif
const int concurrentRequestLimit = pipelinePrefetchMax() + 1 + internalRequest;
// when queue filled already we cant add more.
if (existingRequestCount >= concurrentRequestLimit) {
debugs(33, 3, clientConnection << " max concurrent requests reached (" << concurrentRequestLimit << ")");
debugs(33, 5, clientConnection << " deferring new request until one is done");
return true;
}
return false;
}
/**
* Perform proxy_protocol_access ACL tests on the client which
* connected to PROXY protocol port to see if we trust the
* sender enough to accept their PROXY header claim.
*/
bool
ConnStateData::proxyProtocolValidateClient()
{
if (!Config.accessList.proxyProtocol)
return proxyProtocolError("PROXY client not permitted by default ACL");
ACLFilledChecklist ch(Config.accessList.proxyProtocol, NULL, clientConnection->rfc931);
ch.src_addr = clientConnection->remote;
ch.my_addr = clientConnection->local;
ch.conn(this);
- if (ch.fastCheck() != ACCESS_ALLOWED)
+ if (!ch.fastCheck().allowed())
return proxyProtocolError("PROXY client not permitted by ACLs");
return true;
}
/**
* Perform cleanup on PROXY protocol errors.
* If header parsing hits a fatal error terminate the connection,
* otherwise wait for more data.
*/
bool
ConnStateData::proxyProtocolError(const char *msg)
{
if (msg) {
// This is important to know, but maybe not so much that flooding the log is okay.
#if QUIET_PROXY_PROTOCOL
// display the first of every 32 occurances at level 1, the others at level 2.
static uint8_t hide = 0;
debugs(33, (hide++ % 32 == 0 ? DBG_IMPORTANT : 2), msg << " from " << clientConnection);
#else
debugs(33, DBG_IMPORTANT, msg << " from " << clientConnection);
#endif
mustStop(msg);
}
return false;
}
/// magic octet prefix for PROXY protocol version 1
static const SBuf Proxy1p0magic("PROXY ", 6);
@@ -2451,89 +2451,89 @@ ConnStateData::start()
debugs(33, DBG_IMPORTANT, "NOTICE: Path MTU discovery disabling is not supported on your platform.");
reported = true;
}
#endif
}
typedef CommCbMemFunT<ConnStateData, CommCloseCbParams> Dialer;
AsyncCall::Pointer call = JobCallback(33, 5, Dialer, this, ConnStateData::connStateClosed);
comm_add_close_handler(clientConnection->fd, call);
needProxyProtocolHeader_ = port->flags.proxySurrogate;
if (needProxyProtocolHeader_) {
if (!proxyProtocolValidateClient()) // will close the connection on failure
return;
} else
whenClientIpKnown();
}
void
ConnStateData::whenClientIpKnown()
{
if (Config.onoff.log_fqdn)
fqdncache_gethostbyaddr(clientConnection->remote, FQDN_LOOKUP_IF_MISS);
#if USE_IDENT
if (Ident::TheConfig.identLookup) {
ACLFilledChecklist identChecklist(Ident::TheConfig.identLookup, NULL, NULL);
identChecklist.src_addr = clientConnection->remote;
identChecklist.my_addr = clientConnection->local;
- if (identChecklist.fastCheck() == ACCESS_ALLOWED)
+ if (identChecklist.fastCheck().allowed())
Ident::Start(clientConnection, clientIdentDone, this);
}
#endif
clientdbEstablished(clientConnection->remote, 1);
#if USE_DELAY_POOLS
fd_table[clientConnection->fd].clientInfo = NULL;
if (Config.onoff.client_db) {
/* it was said several times that client write limiter does not work if client_db is disabled */
auto &pools = ClientDelayPools::Instance()->pools;
ACLFilledChecklist ch(NULL, NULL, NULL);
// TODO: we check early to limit error response bandwith but we
// should recheck when we can honor delay_pool_uses_indirect
// TODO: we should also pass the port details for myportname here.
ch.src_addr = clientConnection->remote;
ch.my_addr = clientConnection->local;
for (unsigned int pool = 0; pool < pools.size(); ++pool) {
/* pools require explicit 'allow' to assign a client into them */
if (pools[pool]->access) {
ch.changeAcl(pools[pool]->access);
allow_t answer = ch.fastCheck();
- if (answer == ACCESS_ALLOWED) {
+ if (answer.allowed()) {
/* request client information from db after we did all checks
this will save hash lookup if client failed checks */
ClientInfo * cli = clientdbGetInfo(clientConnection->remote);
assert(cli);
/* put client info in FDE */
fd_table[clientConnection->fd].clientInfo = cli;
/* setup write limiter for this request */
const double burst = floor(0.5 +
(pools[pool]->highwatermark * Config.ClientDelay.initial)/100.0);
cli->setWriteLimiter(pools[pool]->rate, burst, pools[pool]->highwatermark);
break;
} else {
debugs(83, 4, HERE << "Delay pool " << pool << " skipped because ACL " << answer);
}
}
}
}
#endif
// kids must extend to actually start doing something (e.g., reading)
}
/** Handle a new connection on an HTTP socket. */
void
httpAccept(const CommAcceptCbParams ¶ms)
{
MasterXaction::Pointer xact = params.xaction;
@@ -2715,61 +2715,61 @@ httpsEstablish(ConnStateData *connState,
assert(connState);
const Comm::ConnectionPointer &details = connState->clientConnection;
if (!ctx || !httpsCreate(details, ctx))
return;
typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
AsyncCall::Pointer timeoutCall = JobCallback(33, 5, TimeoutDialer,
connState, ConnStateData::requestTimeout);
commSetConnTimeout(details, Config.Timeout.request, timeoutCall);
Comm::SetSelect(details->fd, COMM_SELECT_READ, clientNegotiateSSL, connState, 0);
}
/**
* A callback function to use with the ACLFilledChecklist callback.
* In the case of ACCESS_ALLOWED answer initializes a bumped SSL connection,
* else reverts the connection to tunnel mode.
*/
static void
httpsSslBumpAccessCheckDone(allow_t answer, void *data)
{
ConnStateData *connState = (ConnStateData *) data;
// if the connection is closed or closing, just return.
if (!connState->isOpen())
return;
// Require both a match and a positive bump mode to work around exceptional
// cases where ACL code may return ACCESS_ALLOWED with zero answer.kind.
- if (answer == ACCESS_ALLOWED && answer.kind != Ssl::bumpNone) {
+ if (answer.allowed() && answer.kind != Ssl::bumpNone) {
debugs(33, 2, "sslBump needed for " << connState->clientConnection << " method " << answer.kind);
connState->sslBumpMode = static_cast<Ssl::BumpMode>(answer.kind);
} else {
debugs(33, 2, HERE << "sslBump not needed for " << connState->clientConnection);
connState->sslBumpMode = Ssl::bumpNone;
}
if (!connState->fakeAConnectRequest("ssl-bump", connState->inBuf))
connState->clientConnection->close();
}
/** handle a new HTTPS connection */
static void
httpsAccept(const CommAcceptCbParams ¶ms)
{
MasterXaction::Pointer xact = params.xaction;
const AnyP::PortCfgPointer s = xact->squidPort;
// NP: it is possible the port was reconfigured when the call or accept() was queued.
if (params.flag != Comm::OK) {
// Its possible the call was still queued when the client disconnected
debugs(33, 2, "httpsAccept: " << s->listenConn << ": accept failure: " << xstrerr(params.xerrno));
return;
}
debugs(33, 4, HERE << params.conn << " accepted, starting SSL negotiation.");
fd_note(params.conn->fd, "client https connect");
if (s->tcp_keepalive.enabled) {
commSetTcpKeepalive(params.conn->fd, s->tcp_keepalive.idle, s->tcp_keepalive.interval, s->tcp_keepalive.timeout);
@@ -2863,84 +2863,84 @@ ConnStateData::sslCrtdHandleReply(const
}
return;
}
}
}
Security::ContextPointer nil;
getSslContextDone(nil);
}
void ConnStateData::buildSslCertGenerationParams(Ssl::CertificateProperties &certProperties)
{
certProperties.commonName = sslCommonName_.isEmpty() ? sslConnectHostOrIp.termedBuf() : sslCommonName_.c_str();
const bool triedToConnect = sslServerBump && sslServerBump->entry;
const bool connectedOK = triedToConnect && sslServerBump->entry->isEmpty();
if (connectedOK) {
if (X509 *mimicCert = sslServerBump->serverCert.get())
certProperties.mimicCert.resetAndLock(mimicCert);
ACLFilledChecklist checklist(NULL, sslServerBump->request.getRaw(),
clientConnection != NULL ? clientConnection->rfc931 : dash_str);
checklist.sslErrors = cbdataReference(sslServerBump->sslErrors());
for (sslproxy_cert_adapt *ca = Config.ssl_client.cert_adapt; ca != NULL; ca = ca->next) {
// If the algorithm already set, then ignore it.
if ((ca->alg == Ssl::algSetCommonName && certProperties.setCommonName) ||
(ca->alg == Ssl::algSetValidAfter && certProperties.setValidAfter) ||
(ca->alg == Ssl::algSetValidBefore && certProperties.setValidBefore) )
continue;
- if (ca->aclList && checklist.fastCheck(ca->aclList) == ACCESS_ALLOWED) {
+ if (ca->aclList && checklist.fastCheck(ca->aclList).allowed()) {
const char *alg = Ssl::CertAdaptAlgorithmStr[ca->alg];
const char *param = ca->param;
// For parameterless CN adaptation, use hostname from the
// CONNECT request.
if (ca->alg == Ssl::algSetCommonName) {
if (!param)
param = sslConnectHostOrIp.termedBuf();
certProperties.commonName = param;
certProperties.setCommonName = true;
} else if (ca->alg == Ssl::algSetValidAfter)
certProperties.setValidAfter = true;
else if (ca->alg == Ssl::algSetValidBefore)
certProperties.setValidBefore = true;
debugs(33, 5, HERE << "Matches certificate adaptation aglorithm: " <<
alg << " param: " << (param ? param : "-"));
}
}
certProperties.signAlgorithm = Ssl::algSignEnd;
for (sslproxy_cert_sign *sg = Config.ssl_client.cert_sign; sg != NULL; sg = sg->next) {
- if (sg->aclList && checklist.fastCheck(sg->aclList) == ACCESS_ALLOWED) {
+ if (sg->aclList && checklist.fastCheck(sg->aclList).allowed()) {
certProperties.signAlgorithm = (Ssl::CertSignAlgorithm)sg->alg;
break;
}
}
} else {// did not try to connect (e.g. client-first) or failed to connect
// In case of an error while connecting to the secure server, use a
// trusted certificate, with no mimicked fields and no adaptation
// algorithms. There is nothing we can mimic, so we want to minimize the
// number of warnings the user will have to see to get to the error page.
// We will close the connection, so that the trust is not extended to
// non-Squid content.
certProperties.signAlgorithm = Ssl::algSignTrusted;
}
assert(certProperties.signAlgorithm != Ssl::algSignEnd);
if (certProperties.signAlgorithm == Ssl::algSignUntrusted) {
assert(port->untrustedSigningCert.get());
certProperties.signWithX509.resetAndLock(port->untrustedSigningCert.get());
certProperties.signWithPkey.resetAndLock(port->untrustedSignPkey.get());
} else {
assert(port->signingCert.get());
certProperties.signWithX509.resetAndLock(port->signingCert.get());
if (port->signPkey.get())
certProperties.signWithPkey.resetAndLock(port->signPkey.get());
}
signAlgorithm = certProperties.signAlgorithm;
certProperties.signHash = Ssl::DefaultSignHash;
@@ -3171,61 +3171,61 @@ ConnStateData::parseTlsHandshake()
debugs(83, 5, "Got something other than TLS Client Hello. Cannot SslBump.");
sslBumpMode = Ssl::bumpNone;
if (!clientTunnelOnError(this, context, request, HttpRequestMethod(), ERR_PROTOCOL_UNKNOWN))
clientConnection->close();
return;
}
if (!sslServerBump || sslServerBump->act.step1 == Ssl::bumpClientFirst) { // Either means client-first.
getSslContextStart();
return;
} else if (sslServerBump->act.step1 == Ssl::bumpServerFirst) {
// will call httpsPeeked() with certificate and connection, eventually
FwdState::fwdStart(clientConnection, sslServerBump->entry, sslServerBump->request.getRaw());
} else {
Must(sslServerBump->act.step1 == Ssl::bumpPeek || sslServerBump->act.step1 == Ssl::bumpStare);
startPeekAndSplice();
}
}
void httpsSslBumpStep2AccessCheckDone(allow_t answer, void *data)
{
ConnStateData *connState = (ConnStateData *) data;
// if the connection is closed or closing, just return.
if (!connState->isOpen())
return;
debugs(33, 5, "Answer: " << answer << " kind:" << answer.kind);
assert(connState->serverBump());
Ssl::BumpMode bumpAction;
- if (answer == ACCESS_ALLOWED) {
+ if (answer.allowed()) {
bumpAction = (Ssl::BumpMode)answer.kind;
} else
bumpAction = Ssl::bumpSplice;
connState->serverBump()->act.step2 = bumpAction;
connState->sslBumpMode = bumpAction;
if (bumpAction == Ssl::bumpTerminate) {
connState->clientConnection->close();
} else if (bumpAction != Ssl::bumpSplice) {
connState->startPeekAndSplice();
} else if (!connState->splice())
connState->clientConnection->close();
}
bool
ConnStateData::splice()
{
// normally we can splice here, because we just got client hello message
if (fd_table[clientConnection->fd].ssl.get()) {
// Restore default read methods
fd_table[clientConnection->fd].read_method = &default_read_method;
fd_table[clientConnection->fd].write_method = &default_write_method;
}
// XXX: assuming that there was an HTTP/1.1 CONNECT to begin with...
// reset the current protocol to HTTP/1.1 (was "HTTPS" for the bumping process)
transferProtocol = Http::ProtocolVersion();
assert(!pipeline.empty());
=== modified file 'src/client_side_reply.cc'
--- src/client_side_reply.cc 2017-03-23 12:55:36 +0000
+++ src/client_side_reply.cc 2017-05-04 17:14:41 +0000
@@ -834,61 +834,61 @@ clientReplyContext::processConditional(S
if (r.flags.ims) {
// handle If-Modified-Since requests from the client
if (e->modifiedSince(r.ims, r.imslen)) {
// Modified-Since is true; treat as an unconditional hit
return false;
} else {
// otherwise reply with 304 Not Modified
sendNotModified();
}
return true;
}
return false;
}
/// whether squid.conf send_hit prevents us from serving this hit
bool
clientReplyContext::blockedHit() const
{
if (!Config.accessList.sendHit)
return false; // hits are not blocked by default
if (http->flags.internal)
return false; // internal content "hits" cannot be blocked
if (const HttpReply *rep = http->storeEntry()->getReply()) {
std::unique_ptr<ACLFilledChecklist> chl(clientAclChecklistCreate(Config.accessList.sendHit, http));
chl->reply = const_cast<HttpReply*>(rep); // ACLChecklist API bug
HTTPMSGLOCK(chl->reply);
- return chl->fastCheck() != ACCESS_ALLOWED; // when in doubt, block
+ return !chl->fastCheck().allowed(); // when in doubt, block
}
// This does not happen, I hope, because we are called from CacheHit, which
// is called via a storeClientCopy() callback, and store should initialize
// the reply before calling that callback.
debugs(88, 3, "Missing reply!");
return false;
}
void
clientReplyContext::purgeRequestFindObjectToPurge()
{
/* Try to find a base entry */
http->flags.purging = true;
lookingforstore = 1;
// TODO: can we use purgeAllCached() here instead of doing the
// getPublicByRequestMethod() dance?
StoreEntry::getPublicByRequestMethod(this, http->request, Http::METHOD_GET);
}
// Purges all entries with a given url
// TODO: move to SideAgent parent, when we have one
/*
* We probably cannot purge Vary-affected responses because their MD5
* keys depend on vary headers.
*/
void
purgeEntriesByUrl(HttpRequest * req, const char *url)
{
@@ -2046,61 +2046,61 @@ clientReplyContext::processReplyAccess (
headers_sz = reply->hdr_sz;
/** check for absent access controls (permit by default) */
if (!Config.accessList.reply) {
processReplyAccessResult(ACCESS_ALLOWED);
return;
}
/** Process http_reply_access lists */
ACLFilledChecklist *replyChecklist =
clientAclChecklistCreate(Config.accessList.reply, http);
replyChecklist->reply = reply;
HTTPMSGLOCK(replyChecklist->reply);
replyChecklist->nonBlockingCheck(ProcessReplyAccessResult, this);
}
void
clientReplyContext::ProcessReplyAccessResult(allow_t rv, void *voidMe)
{
clientReplyContext *me = static_cast<clientReplyContext *>(voidMe);
me->processReplyAccessResult(rv);
}
void
clientReplyContext::processReplyAccessResult(const allow_t &accessAllowed)
{
debugs(88, 2, "The reply for " << http->request->method
<< ' ' << http->uri << " is " << accessAllowed << ", because it matched "
<< (AclMatchedName ? AclMatchedName : "NO ACL's"));
- if (accessAllowed != ACCESS_ALLOWED) {
+ if (!accessAllowed.allowed()) {
ErrorState *err;
err_type page_id;
page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, 1);
http->logType = LOG_TCP_DENIED_REPLY;
if (page_id == ERR_NONE)
page_id = ERR_ACCESS_DENIED;
Ip::Address tmp_noaddr;
tmp_noaddr.setNoAddr();
err = clientBuildError(page_id, Http::scForbidden, NULL,
http->getConn() != NULL ? http->getConn()->clientConnection->remote : tmp_noaddr,
http->request);
removeClientStoreReference(&sc, http);
HTTPMSGUNLOCK(reply);
startError(err);
return;
}
/* Ok, the reply is allowed, */
http->loggingEntry(http->storeEntry());
ssize_t body_size = reqofs - reply->hdr_sz;
if (body_size < 0) {
reqofs = reply->hdr_sz;
=== modified file 'src/client_side_request.cc'
--- src/client_side_request.cc 2017-04-14 09:27:23 +0000
+++ src/client_side_request.cc 2017-05-05 13:39:59 +0000
@@ -429,120 +429,119 @@ ClientRequestContext::httpStateIsValid()
/**
* clientFollowXForwardedForCheck() checks the content of X-Forwarded-For:
* against the followXFF ACL, or cleans up and passes control to
* clientAccessCheck().
*
* The trust model here is a little ambiguous. So to clarify the logic:
* - we may always use the direct client address as the client IP.
* - these trust tests merey tell whether we trust given IP enough to believe the
* IP string which it appended to the X-Forwarded-For: header.
* - if at any point we don't trust what an IP adds we stop looking.
* - at that point the current contents of indirect_client_addr are the value set
* by the last previously trusted IP.
* ++ indirect_client_addr contains the remote direct client from the trusted peers viewpoint.
*/
static void
clientFollowXForwardedForCheck(allow_t answer, void *data)
{
ClientRequestContext *calloutContext = (ClientRequestContext *) data;
if (!calloutContext->httpStateIsValid())
return;
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
* x_forwarded_for_iterator and use it to repeat the cycle.
*/
const char *p;
const char *asciiaddr;
int l;
Ip::Address addr;
p = request->x_forwarded_for_iterator.termedBuf();
l = request->x_forwarded_for_iterator.size();
/*
* XXX x_forwarded_for_iterator should really be a list of
* IP addresses, but it's a String instead. We have to
* walk backwards through the String, biting off the last
* comma-delimited part each time. As long as the data is in
* a String, we should probably implement and use a variant of
* strListGetItem() that walks backwards instead of forwards
* through a comma-separated list. But we don't even do that;
* we just do the work in-line here.
*/
/* skip trailing space and commas */
while (l > 0 && (p[l-1] == ',' || xisspace(p[l-1])))
--l;
request->x_forwarded_for_iterator.cut(l);
/* look for start of last item in list */
while (l > 0 && ! (p[l-1] == ',' || xisspace(p[l-1])))
--l;
asciiaddr = p+l;
if ((addr = asciiaddr)) {
request->indirect_client_addr = addr;
request->x_forwarded_for_iterator.cut(l);
calloutContext->acl_checklist = clientAclChecklistCreate(Config.accessList.followXFF, http);
if (!Config.onoff.acl_uses_indirect_client) {
/* override the default src_addr tested if we have to go deeper than one level into XFF */
Filled(calloutContext->acl_checklist)->src_addr = request->indirect_client_addr;
}
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;
}
request->x_forwarded_for_iterator.clean();
request->flags.done_follow_x_forwarded_for = true;
- if (answer != ACCESS_ALLOWED && answer != ACCESS_DENIED) {
+ if (!answer.someRuleMatched({ACCESS_ALLOWED, ACCESS_DENIED})) {
debugs(28, DBG_CRITICAL, "ERROR: Processing X-Forwarded-For. Stopping at IP address: " << request->indirect_client_addr );
}
/* process actual access ACL as normal. */
calloutContext->clientAccessCheck();
}
#endif /* FOLLOW_X_FORWARDED_FOR */
static void
hostHeaderIpVerifyWrapper(const ipcache_addrs* ia, const Dns::LookupDetails &dns, void *data)
{
ClientRequestContext *c = static_cast<ClientRequestContext*>(data);
c->hostHeaderIpVerify(ia, dns);
}
void
ClientRequestContext::hostHeaderIpVerify(const ipcache_addrs* ia, const Dns::LookupDetails &dns)
{
Comm::ConnectionPointer clientConn = http->getConn()->clientConnection;
// 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();
@@ -744,61 +743,61 @@ ClientRequestContext::clientAccessCheck2
void
clientAccessCheckDoneWrapper(allow_t answer, void *data)
{
ClientRequestContext *calloutContext = (ClientRequestContext *) data;
if (!calloutContext->httpStateIsValid())
return;
calloutContext->clientAccessCheckDone(answer);
}
void
ClientRequestContext::clientAccessCheckDone(const allow_t &answer)
{
acl_checklist = NULL;
err_type page_id;
Http::StatusCode status;
debugs(85, 2, "The request " << http->request->method << ' ' <<
http->uri << " is " << answer <<
"; last ACL checked: " << (AclMatchedName ? AclMatchedName : "[none]"));
#if USE_AUTH
char const *proxy_auth_msg = "<null>";
if (http->getConn() != NULL && http->getConn()->getAuth() != NULL)
proxy_auth_msg = http->getConn()->getAuth()->denyMessage("<null>");
else if (http->request->auth_user_request != NULL)
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 */
// XXX: do we still need aclIsProxyAuth() ?
bool auth_challenge = (answer == ACCESS_AUTH_REQUIRED || aclIsProxyAuth(AclMatchedName));
debugs(85, 5, "Access Denied: " << http->uri);
debugs(85, 5, "AclMatchedName = " << (AclMatchedName ? AclMatchedName : "<null>"));
#if USE_AUTH
if (auth_challenge)
debugs(33, 5, "Proxy Auth Message = " << (proxy_auth_msg ? proxy_auth_msg : "<null>"));
#endif
/*
* NOTE: get page_id here, based on AclMatchedName because if
* USE_DELAY_POOLS is enabled, then AclMatchedName gets clobbered in
* the clientCreateStoreEntry() call just below. Pedro Ribeiro
* <[email protected]>
*/
page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, answer != ACCESS_AUTH_REQUIRED);
http->logType = LOG_TCP_DENIED;
if (auth_challenge) {
#if USE_AUTH
if (http->request->flags.sslBumped) {
/*SSL Bumped request, authentication is not possible*/
status = Http::scForbidden;
} else if (!http->flags.accel) {
/* Proxy authorisation needed */
status = Http::scProxyAuthenticationRequired;
@@ -855,92 +854,92 @@ ClientHttpRequest::noteAdaptationAclChec
ih->rfc931 = getConn()->clientConnection->rfc931;
#if USE_OPENSSL
if (getConn()->clientConnection->isOpen()) {
ih->ssluser = sslGetUserEmail(fd_table[getConn()->clientConnection->fd].ssl.get());
}
#endif
}
ih->log_uri = log_uri;
ih->req_sz = req_sz;
}
#endif
if (!g) {
debugs(85,3, HERE << "no adaptation needed");
doCallouts();
return;
}
startAdaptation(g);
}
#endif
static void
clientRedirectAccessCheckDone(allow_t answer, void *data)
{
ClientRequestContext *context = (ClientRequestContext *)data;
ClientHttpRequest *http = context->http;
context->acl_checklist = NULL;
- if (answer == ACCESS_ALLOWED)
+ if (answer.allowed())
redirectStart(http, clientRedirectDoneWrapper, context);
else {
Helper::Reply const nilReply(Helper::Error);
context->clientRedirectDone(nilReply);
}
}
void
ClientRequestContext::clientRedirectStart()
{
debugs(33, 5, HERE << "'" << http->uri << "'");
http->al->syncNotes(http->request);
if (Config.accessList.redirector) {
acl_checklist = clientAclChecklistCreate(Config.accessList.redirector, http);
acl_checklist->nonBlockingCheck(clientRedirectAccessCheckDone, this);
} else
redirectStart(http, clientRedirectDoneWrapper, this);
}
/**
* This methods handles Access checks result of StoreId access list.
* Will handle as "ERR" (no change) in a case Access is not allowed.
*/
static void
clientStoreIdAccessCheckDone(allow_t answer, void *data)
{
ClientRequestContext *context = static_cast<ClientRequestContext *>(data);
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 const nilReply(Helper::Error);
context->clientStoreIdDone(nilReply);
}
}
/**
* Start locating an alternative storeage ID string (if any) from admin
* configured helper program. This is an asynchronous operation terminating in
* ClientRequestContext::clientStoreIdDone() when completed.
*/
void
ClientRequestContext::clientStoreIdStart()
{
debugs(33, 5,"'" << http->uri << "'");
if (Config.accessList.store_id) {
acl_checklist = clientAclChecklistCreate(Config.accessList.store_id, http);
acl_checklist->nonBlockingCheck(clientStoreIdAccessCheckDone, this);
} else
storeIdStart(http, clientStoreIdDoneWrapper, this);
}
static int
clientHierarchical(ClientHttpRequest * http)
{
HttpRequest *request = http->request;
HttpRequestMethod method = request->method;
@@ -1372,61 +1371,61 @@ ClientRequestContext::clientStoreIdDone(
/** Test cache allow/deny configuration
* Sets flags.cachable=1 if caching is not denied.
*/
void
ClientRequestContext::checkNoCache()
{
if (Config.accessList.noCache) {
acl_checklist = clientAclChecklistCreate(Config.accessList.noCache, http);
acl_checklist->nonBlockingCheck(checkNoCacheDoneWrapper, this);
} else {
/* unless otherwise specified, we try to cache. */
checkNoCacheDone(ACCESS_ALLOWED);
}
}
static void
checkNoCacheDoneWrapper(allow_t answer, void *data)
{
ClientRequestContext *calloutContext = (ClientRequestContext *) data;
if (!calloutContext->httpStateIsValid())
return;
calloutContext->checkNoCacheDone(answer);
}
void
ClientRequestContext::checkNoCacheDone(const allow_t &answer)
{
acl_checklist = NULL;
- if (answer == ACCESS_DENIED) {
+ if (!answer.allowed()) {
http->request->flags.noCache = true; // dont read reply from cache
http->request->flags.cachable = false; // dont store reply into cache
}
http->doCallouts();
}
#if USE_OPENSSL
bool
ClientRequestContext::sslBumpAccessCheck()
{
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())
http->sslBumpNeed(bumpMode); // for processRequest() to bump if needed and not already bumped
http->al->ssl.bumpMode = bumpMode; // inherited from bumped connection
@@ -1461,61 +1460,61 @@ ClientRequestContext::sslBumpAccessCheck
return false;
}
debugs(85, 5, HERE << "SslBump possible, checking ACL");
ACLFilledChecklist *aclChecklist = clientAclChecklistCreate(Config.accessList.ssl_bump, http);
aclChecklist->nonBlockingCheck(sslBumpAccessCheckDoneWrapper, this);
return true;
}
/**
* A wrapper function to use the ClientRequestContext::sslBumpAccessCheckDone method
* as ACLFilledChecklist callback
*/
static void
sslBumpAccessCheckDoneWrapper(allow_t answer, void *data)
{
ClientRequestContext *calloutContext = static_cast<ClientRequestContext *>(data);
if (!calloutContext->httpStateIsValid())
return;
calloutContext->sslBumpAccessCheckDone(answer);
}
void
ClientRequestContext::sslBumpAccessCheckDone(const allow_t &answer)
{
if (!httpStateIsValid())
return;
- const Ssl::BumpMode bumpMode = answer == ACCESS_ALLOWED ?
+ const Ssl::BumpMode bumpMode = answer.allowed() ?
static_cast<Ssl::BumpMode>(answer.kind) : Ssl::bumpNone;
http->sslBumpNeed(bumpMode); // for processRequest() to bump if needed
http->al->ssl.bumpMode = bumpMode; // for logging
http->doCallouts();
}
#endif
/*
* Identify requests that do not go through the store and client side stream
* and forward them to the appropriate location. All other requests, request
* them.
*/
void
ClientHttpRequest::processRequest()
{
debugs(85, 4, request->method << ' ' << uri);
const bool untouchedConnect = request->method == Http::METHOD_CONNECT && !redirect.status;
#if USE_OPENSSL
if (untouchedConnect && sslBumpNeeded()) {
assert(!request->flags.forceTunnel);
sslBumpStart();
return;
}
#endif
if (untouchedConnect || request->flags.forceTunnel) {
getConn()->stopReading(); // tunnels read for themselves
=== modified file 'src/clients/Client.cc'
--- src/clients/Client.cc 2017-04-03 07:22:32 +0000
+++ src/clients/Client.cc 2017-05-04 17:14:41 +0000
@@ -495,61 +495,61 @@ Client::maybePurgeOthers()
const char *reqUrl = tmp.c_str();
debugs(88, 5, "maybe purging due to " << request->method << ' ' << tmp);
purgeEntriesByUrl(request.getRaw(), reqUrl);
purgeEntriesByHeader(request.getRaw(), reqUrl, theFinalReply, Http::HdrType::LOCATION);
purgeEntriesByHeader(request.getRaw(), reqUrl, theFinalReply, Http::HdrType::CONTENT_LOCATION);
}
/// called when we have final (possibly adapted) reply headers; kids extend
void
Client::haveParsedReplyHeaders()
{
Must(theFinalReply);
maybePurgeOthers();
// adaptation may overwrite old offset computed using the virgin response
const bool partial = theFinalReply->content_range &&
theFinalReply->sline.status() == Http::scPartialContent;
currentOffset = partial ? theFinalReply->content_range->spec.offset : 0;
}
/// whether to prevent caching of an otherwise cachable response
bool
Client::blockCaching()
{
if (const Acl::Tree *acl = Config.accessList.storeMiss) {
// This relatively expensive check is not in StoreEntry::checkCachable:
// That method lacks HttpRequest and may be called too many times.
ACLFilledChecklist ch(acl, originalRequest().getRaw());
ch.reply = const_cast<HttpReply*>(entry->getReply()); // ACLFilledChecklist API bug
HTTPMSGLOCK(ch.reply);
- if (ch.fastCheck() != ACCESS_ALLOWED) { // when in doubt, block
+ if (!ch.fastCheck().allowed()) { // when in doubt, block
debugs(20, 3, "store_miss prohibits caching");
return true;
}
}
return false;
}
HttpRequestPointer
Client::originalRequest()
{
return request;
}
#if USE_ADAPTATION
/// Initiate an asynchronous adaptation transaction which will call us back.
void
Client::startAdaptation(const Adaptation::ServiceGroupPointer &group, HttpRequest *cause)
{
debugs(11, 5, "Client::startAdaptation() called");
// check whether we should be sending a body as well
// start body pipe to feed ICAP transaction if needed
assert(!virginBodyDestination);
HttpReply *vrep = virginReply();
assert(!vrep->body_pipe);
int64_t size = 0;
if (vrep->expectingBody(cause->method, size) && size) {
virginBodyDestination = new BodyPipe(this);
vrep->body_pipe = virginBodyDestination;
debugs(93, 6, HERE << "will send virgin reply body to " <<
virginBodyDestination << "; size: " << size);
=== modified file 'src/clients/FtpClient.cc'
--- src/clients/FtpClient.cc 2017-01-01 00:12:22 +0000
+++ src/clients/FtpClient.cc 2017-05-04 17:14:41 +0000
@@ -678,61 +678,61 @@ Ftp::Client::sendPassive()
debugs(9, 5, "FTP Channel is IPv6 (" << ctrl.conn->remote << ") attempting EPSV 2 after EPSV ALL has failed.");
mb.appendf("EPSV 2%s", Ftp::crlf);
state = SENT_EPSV_2;
break;
}
// else fall through to skip EPSV 2
case SENT_EPSV_2: /* EPSV IPv6 failed. Try EPSV IPv4 */
if (ctrl.conn->local.isIPv4()) {
debugs(9, 5, "FTP Channel is IPv4 (" << ctrl.conn->remote << ") attempting EPSV 1 after EPSV ALL has failed.");
mb.appendf("EPSV 1%s", Ftp::crlf);
state = SENT_EPSV_1;
break;
} else if (Config.Ftp.epsv_all) {
debugs(9, DBG_IMPORTANT, "FTP does not allow PASV method after 'EPSV ALL' has been sent.");
failed(ERR_FTP_FAILURE, 0);
return false;
}
// else fall through to skip EPSV 1
case SENT_EPSV_1: /* EPSV options exhausted. Try PASV now. */
debugs(9, 5, "FTP Channel (" << ctrl.conn->remote << ") rejects EPSV connection attempts. Trying PASV instead.");
mb.appendf("PASV%s", Ftp::crlf);
state = SENT_PASV;
break;
default: {
bool doEpsv = true;
if (Config.accessList.ftp_epsv) {
ACLFilledChecklist checklist(Config.accessList.ftp_epsv, fwd->request, NULL);
- doEpsv = (checklist.fastCheck() == ACCESS_ALLOWED);
+ doEpsv = checklist.fastCheck().allowed();
}
if (!doEpsv) {
debugs(9, 5, "EPSV support manually disabled. Sending PASV for FTP Channel (" << ctrl.conn->remote <<")");
mb.appendf("PASV%s", Ftp::crlf);
state = SENT_PASV;
} else if (Config.Ftp.epsv_all) {
debugs(9, 5, "EPSV ALL manually enabled. Attempting with FTP Channel (" << ctrl.conn->remote <<")");
mb.appendf("EPSV ALL%s", Ftp::crlf);
state = SENT_EPSV_ALL;
} else {
if (ctrl.conn->local.isIPv6()) {
debugs(9, 5, "FTP Channel (" << ctrl.conn->remote << "). Sending default EPSV 2");
mb.appendf("EPSV 2%s", Ftp::crlf);
state = SENT_EPSV_2;
}
if (ctrl.conn->local.isIPv4()) {
debugs(9, 5, "Channel (" << ctrl.conn->remote <<"). Sending default EPSV 1");
mb.appendf("EPSV 1%s", Ftp::crlf);
state = SENT_EPSV_1;
}
}
break;
}
}
if (ctrl.message)
wordlistDestroy(&ctrl.message);
ctrl.message = NULL; //No message to return to client.
ctrl.offset = 0; //reset readed response, to make room read the next response
=== modified file 'src/external_acl.cc'
--- src/external_acl.cc 2017-01-11 19:06:57 +0000
+++ src/external_acl.cc 2017-05-04 17:14:41 +0000
@@ -429,61 +429,61 @@ external_acl::add(const ExternalACLEntry
{
trimCache();
assert(anEntry != NULL);
assert (anEntry->def == NULL);
anEntry->def = this;
ExternalACLEntry *e = const_cast<ExternalACLEntry *>(anEntry.getRaw()); // XXX: make hash a std::map of Pointer.
hash_join(cache, e);
dlinkAdd(e, &e->lru, &lru_list);
e->lock(); //cbdataReference(e); // lock it on behalf of the hash
++cache_entries;
}
void
external_acl::trimCache()
{
if (cache_size && cache_entries >= cache_size) {
ExternalACLEntryPointer e(static_cast<ExternalACLEntry *>(lru_list.tail->data));
external_acl_cache_delete(this, e);
}
}
bool
external_acl::maybeCacheable(const allow_t &result) const
{
if (cache_size <= 0)
return false; // cache is disabled
if (result == ACCESS_DUNNO)
return false; // non-cacheable response
- if ((result == ACCESS_ALLOWED ? ttl : negative_ttl) <= 0)
+ if ((result.allowed() ? ttl : negative_ttl) <= 0)
return false; // not caching this type of response
return true;
}
/******************************************************************
* external acl type
*/
class external_acl_data
{
CBDATA_CLASS(external_acl_data);
public:
explicit external_acl_data(external_acl *aDef) : def(cbdataReference(aDef)), name(NULL), arguments(NULL) {}
~external_acl_data();
external_acl *def;
const char *name;
wordlist *arguments;
};
CBDATA_CLASS_INIT(external_acl_data);
external_acl_data::~external_acl_data()
{
xfree(name);
wordlistDestroy(&arguments);
cbdataReferenceDone(def);
}
@@ -588,61 +588,61 @@ aclMatchExternal(external_acl_data *acl,
if (entry != NULL) {
if (entry->def == acl->def) {
/* Ours, use it.. if the key matches */
const char *key = makeExternalAclKey(ch, acl);
if (!key)
return ACCESS_DUNNO; // insufficent data to continue
if (strcmp(key, (char*)entry->key) != 0) {
debugs(82, 9, "entry key='" << (char *)entry->key << "', our key='" << key << "' dont match. Discarded.");
// too bad. need a new lookup.
entry = ch->extacl_entry = NULL;
}
} else {
/* Not ours.. get rid of it */
debugs(82, 9, "entry " << entry << " not valid or not ours. Discarded.");
if (entry != NULL) {
debugs(82, 9, "entry def=" << entry->def << ", our def=" << acl->def);
const char *key = makeExternalAclKey(ch, acl); // may be nil
debugs(82, 9, "entry key='" << (char *)entry->key << "', our key='" << key << "'");
}
entry = ch->extacl_entry = NULL;
}
}
if (!entry) {
debugs(82, 9, HERE << "No helper entry available");
#if USE_AUTH
if (acl->def->require_auth) {
/* Make sure the user is authenticated */
debugs(82, 3, HERE << acl->def->name << " check user authenticated.");
const allow_t ti = AuthenticateAcl(ch);
- if (ti != ACCESS_ALLOWED) {
+ if (!ti.allowed()) {
debugs(82, 2, HERE << acl->def->name << " user not authenticated (" << ti << ")");
return ti;
}
debugs(82, 3, HERE << acl->def->name << " user is authenticated.");
}
#endif
const char *key = makeExternalAclKey(ch, acl);
if (!key) {
/* Not sufficient data to process */
return ACCESS_DUNNO;
}
entry = static_cast<ExternalACLEntry *>(hash_lookup(acl->def->cache, key));
const ExternalACLEntryPointer staleEntry = entry;
if (entry != NULL && external_acl_entry_expired(acl->def, entry))
entry = NULL;
if (entry != NULL && external_acl_grace_expired(acl->def, entry)) {
// refresh in the background
ExternalACLLookup::Start(ch, acl, true);
debugs(82, 4, HERE << "no need to wait for the refresh of '" <<
key << "' in '" << acl->def->name << "' (ch=" << ch << ").");
}
if (!entry) {
debugs(82, 2, HERE << acl->def->name << "(\"" << key << "\") = lookup needed");
// TODO: All other helpers allow temporary overload. Should not we?
@@ -775,74 +775,74 @@ makeExternalAclKey(ACLFilledChecklist *
}
}
ch->al->lastAclData = sb;
}
#if USE_IDENT
if (t->type == Format::LFT_USER_IDENT) {
if (!*ch->rfc931) {
// if we fail to go async, we still return NULL and the caller
// will detect the failure in ACLExternal::match().
(void)ch->goAsync(IdentLookup::Instance());
return NULL;
}
}
#endif
}
// assemble the full helper lookup string
acl_data->def->format.assemble(mb, ch->al, 0);
return mb.buf;
}
static int
external_acl_entry_expired(external_acl * def, const ExternalACLEntryPointer &entry)
{
if (def->cache_size <= 0 || entry->result == ACCESS_DUNNO)
return 1;
- if (entry->date + (entry->result == ACCESS_ALLOWED ? def->ttl : def->negative_ttl) < squid_curtime)
+ if (entry->date + (entry->result.allowed() ? def->ttl : def->negative_ttl) < squid_curtime)
return 1;
else
return 0;
}
static int
external_acl_grace_expired(external_acl * def, const ExternalACLEntryPointer &entry)
{
if (def->cache_size <= 0 || entry->result == ACCESS_DUNNO)
return 1;
int ttl;
- ttl = entry->result == ACCESS_ALLOWED ? def->ttl : def->negative_ttl;
+ ttl = entry->result.allowed() ? def->ttl : def->negative_ttl;
ttl = (ttl * (100 - def->grace)) / 100;
if (entry->date + ttl <= squid_curtime)
return 1;
else
return 0;
}
static ExternalACLEntryPointer
external_acl_cache_add(external_acl * def, const char *key, ExternalACLEntryData const & data)
{
ExternalACLEntryPointer entry;
if (!def->maybeCacheable(data.result)) {
debugs(82,6, HERE);
if (data.result == ACCESS_DUNNO) {
if (const ExternalACLEntryPointer oldentry = static_cast<ExternalACLEntry *>(hash_lookup(def->cache, key)))
external_acl_cache_delete(def, oldentry);
}
entry = new ExternalACLEntry;
entry->key = xstrdup(key);
entry->update(data);
entry->def = def;
return entry;
}
entry = static_cast<ExternalACLEntry *>(hash_lookup(def->cache, key));
debugs(82, 2, "external_acl_cache_add: Adding '" << key << "' = " << data.result);
=== modified file 'src/htcp.cc'
--- src/htcp.cc 2017-01-01 00:12:22 +0000
+++ src/htcp.cc 2017-05-04 17:14:41 +0000
@@ -747,61 +747,61 @@ htcpUnpackDetail(char *buf, int sz)
/* Set CACHE-HDRS */
buf += 2;
d->cache_hdrs = buf;
buf += l;
d->cacheHdrsSz = l;
sz -= l;
debugs(31, 3, "htcpUnpackDetail: " << sz << " bytes left");
/*
* Add terminating null to CACHE-HDRS. This is possible because we allocated
* an extra byte when we received the packet. This will overwrite any following
* AUTH block.
*/
*buf = '\0';
return d;
}
static bool
htcpAccessAllowed(acl_access * acl, const htcpSpecifier::Pointer &s, Ip::Address &from)
{
/* default deny if no access list present */
if (!acl)
return false;
ACLFilledChecklist checklist(acl, s->request.getRaw(), nullptr);
checklist.src_addr = from;
checklist.my_addr.setNoAddr();
- return (checklist.fastCheck() == ACCESS_ALLOWED);
+ return checklist.fastCheck().allowed();
}
static void
htcpTstReply(htcpDataHeader * dhdr, StoreEntry * e, htcpSpecifier * spec, Ip::Address &from)
{
static char pkt[8192];
HttpHeader hdr(hoHtcpReply);
ssize_t pktlen;
htcpStuff stuff(dhdr->msg_id, HTCP_TST, RR_RESPONSE, 0);
stuff.response = e ? 0 : 1;
debugs(31, 3, "htcpTstReply: response = " << stuff.response);
if (spec) {
stuff.S.method = spec->method;
stuff.S.uri = spec->uri;
stuff.S.version = spec->version;
stuff.S.req_hdrs = spec->req_hdrs;
stuff.S.reqHdrsSz = spec->reqHdrsSz;
if (e)
hdr.putInt(Http::HdrType::AGE, (e->timestamp <= squid_curtime ? (squid_curtime - e->timestamp) : 0) );
else
hdr.putInt(Http::HdrType::AGE, 0);
MemBuf mb;
mb.init();
hdr.packInto(&mb);
stuff.D.resp_hdrs = xstrdup(mb.buf);
stuff.D.respHdrsSz = mb.contentSize();
debugs(31, 3, "htcpTstReply: resp_hdrs = {" << stuff.D.resp_hdrs << "}");
mb.reset();
=== modified file 'src/http.cc'
--- src/http.cc 2017-04-02 12:06:21 +0000
+++ src/http.cc 2017-05-04 17:14:41 +0000
@@ -799,61 +799,61 @@ HttpStateData::processReplyHeader()
processSurrogateControl (vrep);
request->hier.peer_reply_status = newrep->sline.status();
ctx_exit(ctx);
}
/// ignore or start forwarding the 1xx response (a.k.a., control message)
void
HttpStateData::handle1xx(HttpReply *reply)
{
HttpReply::Pointer msg(reply); // will destroy reply if unused
// one 1xx at a time: we must not be called while waiting for previous 1xx
Must(!flags.handling1xx);
flags.handling1xx = true;
if (!request->canHandle1xx() || request->forcedBodyContinuation) {
debugs(11, 2, "ignoring 1xx because it is " << (request->forcedBodyContinuation ? "already sent" : "not supported by client"));
proceedAfter1xx();
return;
}
#if USE_HTTP_VIOLATIONS
// check whether the 1xx response forwarding is allowed by squid.conf
if (Config.accessList.reply) {
ACLFilledChecklist ch(Config.accessList.reply, originalRequest().getRaw());
ch.reply = reply;
HTTPMSGLOCK(ch.reply);
- if (ch.fastCheck() != ACCESS_ALLOWED) { // TODO: support slow lookups?
+ if (!ch.fastCheck().allowed()) { // TODO: support slow lookups?
debugs(11, 3, HERE << "ignoring denied 1xx");
proceedAfter1xx();
return;
}
}
#endif // USE_HTTP_VIOLATIONS
debugs(11, 2, HERE << "forwarding 1xx to client");
// the Sink will use this to call us back after writing 1xx to the client
typedef NullaryMemFunT<HttpStateData> CbDialer;
const AsyncCall::Pointer cb = JobCallback(11, 3, CbDialer, this,
HttpStateData::proceedAfter1xx);
CallJobHere1(11, 4, request->clientConnectionManager, ConnStateData,
ConnStateData::sendControlMsg, HttpControlMsg(msg, cb));
// If the call is not fired, then the Sink is gone, and HttpStateData
// will terminate due to an aborted store entry or another similar error.
// If we get stuck, it is not handle1xx fault if we could get stuck
// for similar reasons without a 1xx response.
}
/// restores state and resumes processing after 1xx is ignored or forwarded
void
HttpStateData::proceedAfter1xx()
{
Must(flags.handling1xx);
debugs(11, 2, "continuing with " << payloadSeen << " bytes in buffer after 1xx");
CallJobHere(11, 3, this, HttpStateData, HttpStateData::processReply);
}
@@ -2306,61 +2306,61 @@ httpStart(FwdState *fwd)
void
HttpStateData::start()
{
if (!sendRequest()) {
debugs(11, 3, "httpStart: aborted");
mustStop("HttpStateData::start failed");
return;
}
++ statCounter.server.all.requests;
++ statCounter.server.http.requests;
/*
* We used to set the read timeout here, but not any more.
* Now its set in httpSendComplete() after the full request,
* including request body, has been written to the server.
*/
}
/// if broken posts are enabled for the request, try to fix and return true
bool
HttpStateData::finishingBrokenPost()
{
#if USE_HTTP_VIOLATIONS
if (!Config.accessList.brokenPosts) {
debugs(11, 5, HERE << "No brokenPosts list");
return false;
}
ACLFilledChecklist ch(Config.accessList.brokenPosts, originalRequest().getRaw());
- if (ch.fastCheck() != ACCESS_ALLOWED) {
+ if (!ch.fastCheck().allowed()) {
debugs(11, 5, HERE << "didn't match brokenPosts");
return false;
}
if (!Comm::IsConnOpen(serverConnection)) {
debugs(11, 3, HERE << "ignoring broken POST for closed " << serverConnection);
assert(closeHandler != NULL);
return true; // prevent caller from proceeding as if nothing happened
}
debugs(11, 3, "finishingBrokenPost: fixing broken POST");
typedef CommCbMemFunT<HttpStateData, CommIoCbParams> Dialer;
requestSender = JobCallback(11,5,
Dialer, this, HttpStateData::wroteLast);
Comm::Write(serverConnection, "\r\n", 2, requestSender, NULL);
return true;
#else
return false;
#endif /* USE_HTTP_VIOLATIONS */
}
/// if needed, write last-chunk to end the request body and return true
bool
HttpStateData::finishingChunkedRequest()
{
if (flags.sentLastChunk) {
debugs(11, 5, HERE << "already sent last-chunk");
return false;
}
=== modified file 'src/http/Stream.cc'
--- src/http/Stream.cc 2017-02-19 17:13:27 +0000
+++ src/http/Stream.cc 2017-05-04 17:14:41 +0000
@@ -269,61 +269,61 @@ Http::Stream::sendStartOfMessage(HttpRep
MemBuf *mb = rep->pack();
// dump now, so we dont output any body.
debugs(11, 2, "HTTP Client " << clientConnection);
debugs(11, 2, "HTTP Client REPLY:\n---------\n" << mb->buf << "\n----------");
/* Save length of headers for persistent conn checks */
http->out.headers_sz = mb->contentSize();
#if HEADERS_LOG
headersLog(0, 0, http->request->method, rep);
#endif
if (bodyData.data && bodyData.length) {
if (multipartRangeRequest())
packRange(bodyData, mb);
else if (http->request->flags.chunkedReply) {
packChunk(bodyData, *mb);
} else {
size_t length = lengthToSend(bodyData.range());
noteSentBodyBytes(length);
mb->append(bodyData.data, length);
}
}
#if USE_DELAY_POOLS
for (const auto &pool: MessageDelayPools::Instance()->pools) {
if (pool->access) {
std::unique_ptr<ACLFilledChecklist> chl(clientAclChecklistCreate(pool->access, http));
chl->reply = rep;
HTTPMSGLOCK(chl->reply);
const allow_t answer = chl->fastCheck();
- if (answer == ACCESS_ALLOWED) {
+ if (answer.allowed()) {
writeQuotaHandler = pool->createBucket();
fd_table[clientConnection->fd].writeQuotaHandler = writeQuotaHandler;
break;
} else {
debugs(83, 4, "Response delay pool " << pool->poolName <<
" skipped because ACL " << answer);
}
}
}
#endif
getConn()->write(mb);
delete mb;
}
void
Http::Stream::sendBody(StoreIOBuffer bodyData)
{
if (!multipartRangeRequest() && !http->request->flags.chunkedReply) {
size_t length = lengthToSend(bodyData.range());
noteSentBodyBytes(length);
getConn()->write(bodyData.data, length);
return;
}
MemBuf mb;
mb.init();
if (multipartRangeRequest())
packRange(bodyData, &mb);
else
=== modified file 'src/icp_v2.cc'
--- src/icp_v2.cc 2017-01-01 00:12:22 +0000
+++ src/icp_v2.cc 2017-05-04 17:14:41 +0000
@@ -390,61 +390,61 @@ icpCreateAndSend(icp_opcode opcode, int
icp_common_t *reply = _icp_common_t::createMessage(opcode, flags, url, reqnum, pad);
icpUdpSend(fd, from, reply, icpLogFromICPCode(opcode), 0);
}
void
icpDenyAccess(Ip::Address &from, char *url, int reqnum, int fd)
{
debugs(12, 2, "icpDenyAccess: Access Denied for " << from << " by " << AclMatchedName << ".");
if (clientdbCutoffDenied(from)) {
/*
* count this DENIED query in the clientdb, even though
* we're not sending an ICP reply...
*/
clientdbUpdate(from, LOG_UDP_DENIED, AnyP::PROTO_ICP, 0);
} else {
icpCreateAndSend(ICP_DENIED, 0, url, reqnum, 0, fd, from);
}
}
bool
icpAccessAllowed(Ip::Address &from, HttpRequest * icp_request)
{
/* absent any explicit rules, we deny all */
if (!Config.accessList.icp)
return false;
ACLFilledChecklist checklist(Config.accessList.icp, icp_request, NULL);
checklist.src_addr = from;
checklist.my_addr.setNoAddr();
- return (checklist.fastCheck() == ACCESS_ALLOWED);
+ return checklist.fastCheck().allowed();
}
char const *
icpGetUrlToSend(char *url)
{
if (strpbrk(url, w_space))
return rfc1738_escape(url);
else
return url;
}
HttpRequest *
icpGetRequest(char *url, int reqnum, int fd, Ip::Address &from)
{
if (strpbrk(url, w_space)) {
url = rfc1738_escape(url);
icpCreateAndSend(ICP_ERR, 0, rfc1738_escape(url), reqnum, 0, fd, from);
return NULL;
}
HttpRequest *result;
if ((result = HttpRequest::CreateFromUrl(url)) == NULL)
icpCreateAndSend(ICP_ERR, 0, url, reqnum, 0, fd, from);
return result;
}
static void
=== modified file 'src/log/access_log.cc'
--- src/log/access_log.cc 2017-01-01 00:12:22 +0000
+++ src/log/access_log.cc 2017-05-04 17:14:41 +0000
@@ -57,61 +57,61 @@ typedef struct {
int n;
} fvdb_entry;
static hash_table *via_table = NULL;
static hash_table *forw_table = NULL;
static void fvdbInit();
static void fvdbDumpTable(StoreEntry * e, hash_table * hash);
static void fvdbCount(hash_table * hash, const char *key);
static OBJH fvdbDumpVia;
static OBJH fvdbDumpForw;
static FREE fvdbFreeEntry;
static void fvdbClear(void);
static void fvdbRegisterWithCacheManager();
#endif
int LogfileStatus = LOG_DISABLE;
void
accessLogLogTo(CustomLog* log, AccessLogEntry::Pointer &al, ACLChecklist * checklist)
{
if (al->url.isEmpty())
al->url = Format::Dash;
if (!al->http.content_type || *al->http.content_type == '\0')
al->http.content_type = dash_str;
if (al->hier.host[0] == '\0')
xstrncpy(al->hier.host, dash_str, SQUIDHOSTNAMELEN);
for (; log; log = log->next) {
- if (log->aclList && checklist && checklist->fastCheck(log->aclList) != ACCESS_ALLOWED)
+ if (log->aclList && checklist && !checklist->fastCheck(log->aclList).allowed())
continue;
// The special-case "none" type has no logfile object set
if (log->type == Log::Format::CLF_NONE)
return;
if (log->logfile) {
logfileLineStart(log->logfile);
switch (log->type) {
case Log::Format::CLF_SQUID:
Log::Format::SquidNative(al, log->logfile);
break;
case Log::Format::CLF_COMBINED:
Log::Format::HttpdCombined(al, log->logfile);
break;
case Log::Format::CLF_COMMON:
Log::Format::HttpdCommon(al, log->logfile);
break;
case Log::Format::CLF_REFERER:
Log::Format::SquidReferer(al, log->logfile);
break;
case Log::Format::CLF_USERAGENT:
Log::Format::SquidUserAgent(al, log->logfile);
break;
=== modified file 'src/neighbors.cc'
--- src/neighbors.cc 2017-04-12 23:34:50 +0000
+++ src/neighbors.cc 2017-05-04 17:14:41 +0000
@@ -141,61 +141,61 @@ peerAllowedToUse(const CachePeer * p, Ht
if (neighborType(p, request->url) == PEER_SIBLING) {
#if PEER_MULTICAST_SIBLINGS
if (p->type == PEER_MULTICAST && p->options.mcast_siblings &&
(request->flags.noCache || request->flags.refresh || request->flags.loopDetected || request->flags.needValidation))
debugs(15, 2, "peerAllowedToUse(" << p->name << ", " << request->url.authority() << ") : multicast-siblings optimization match");
#endif
if (request->flags.noCache)
return false;
if (request->flags.refresh)
return false;
if (request->flags.loopDetected)
return false;
if (request->flags.needValidation)
return false;
}
// CONNECT requests are proxy requests. Not to be forwarded to origin servers.
// Unless the destination port matches, in which case we MAY perform a 'DIRECT' to this CachePeer.
if (p->options.originserver && request->method == Http::METHOD_CONNECT && request->url.port() != p->http_port)
return false;
if (p->access == NULL)
return true;
ACLFilledChecklist checklist(p->access, request, NULL);
- return (checklist.fastCheck() == ACCESS_ALLOWED);
+ return checklist.fastCheck().allowed();
}
/* Return TRUE if it is okay to send an ICP request to this CachePeer. */
static int
peerWouldBePinged(const CachePeer * p, HttpRequest * request)
{
if (p->icp.port == 0)
return 0;
if (p->options.no_query)
return 0;
if (p->options.mcast_responder)
return 0;
if (p->n_addresses == 0)
return 0;
if (p->options.background_ping && (squid_curtime - p->stats.last_query < Config.backgroundPingRate))
return 0;
/* the case below seems strange, but can happen if the
* URL host is on the other side of a firewall */
if (p->type == PEER_SIBLING)
if (!request->flags.hierarchical)
return 0;
if (!peerAllowedToUse(p, request))
return 0;
=== modified file 'src/security/PeerConnector.cc'
--- src/security/PeerConnector.cc 2017-02-15 03:10:55 +0000
+++ src/security/PeerConnector.cc 2017-05-04 17:14:41 +0000
@@ -311,61 +311,61 @@ Security::PeerConnector::sslCrtvdHandleR
serverConn->close();
return;
}
#endif
#if USE_OPENSSL
/// Checks errors in the cert. validator response against sslproxy_cert_error.
/// The first honored error, if any, is returned via errDetails parameter.
/// The method returns all seen errors except SSL_ERROR_NONE as Security::CertErrors.
Security::CertErrors *
Security::PeerConnector::sslCrtvdCheckForErrors(Ssl::CertValidationResponse const &resp, Ssl::ErrorDetail *& errDetails)
{
ACLFilledChecklist *check = NULL;
if (acl_access *acl = ::Config.ssl_client.cert_error) {
check = new ACLFilledChecklist(acl, request.getRaw(), dash_str);
check->al = al;
}
Security::CertErrors *errs = nullptr;
Security::SessionPointer session(fd_table[serverConnection()->fd].ssl);
typedef Ssl::CertValidationResponse::RecvdErrors::const_iterator SVCRECI;
for (SVCRECI i = resp.errors.begin(); i != resp.errors.end(); ++i) {
debugs(83, 7, "Error item: " << i->error_no << " " << i->error_reason);
assert(i->error_no != SSL_ERROR_NONE);
if (!errDetails) {
bool allowed = false;
if (check) {
check->sslErrors = new Security::CertErrors(Security::CertError(i->error_no, i->cert, i->error_depth));
- if (check->fastCheck() == ACCESS_ALLOWED)
+ if (check->fastCheck().allowed())
allowed = true;
}
// else the Config.ssl_client.cert_error access list is not defined
// and the first error will cause the error page
if (allowed) {
debugs(83, 3, "bypassing SSL error " << i->error_no << " in " << "buffer");
} else {
debugs(83, 5, "confirming SSL error " << i->error_no);
X509 *brokenCert = i->cert.get();
Security::CertPointer peerCert(SSL_get_peer_certificate(session.get()));
const char *aReason = i->error_reason.empty() ? NULL : i->error_reason.c_str();
errDetails = new Ssl::ErrorDetail(i->error_no, peerCert.get(), brokenCert, aReason);
}
if (check) {
delete check->sslErrors;
check->sslErrors = NULL;
}
}
if (!errs)
errs = new Security::CertErrors(Security::CertError(i->error_no, i->cert, i->error_depth));
else
errs->push_back_unique(Security::CertError(i->error_no, i->cert, i->error_depth));
}
if (check)
delete check;
return errs;
}
=== modified file 'src/servers/FtpServer.cc'
--- src/servers/FtpServer.cc 2017-02-19 06:12:04 +0000
+++ src/servers/FtpServer.cc 2017-05-04 17:14:41 +0000
@@ -1518,61 +1518,61 @@ Ftp::Server::handlePortRequest(String &,
changeState(fssHandlePort, "handlePortRequest");
setDataCommand();
return true; // forward our fake PASV request
}
bool
Ftp::Server::handleDataRequest(String &, String &)
{
if (!checkDataConnPre())
return false;
master->userDataDone = 0;
originDataDownloadAbortedOnError = false;
changeState(fssHandleDataRequest, "handleDataRequest");
return true;
}
bool
Ftp::Server::handleUploadRequest(String &, String &)
{
if (!checkDataConnPre())
return false;
if (Config.accessList.forceRequestBodyContinuation) {
ClientHttpRequest *http = pipeline.front()->http;
HttpRequest *request = http->request;
ACLFilledChecklist bodyContinuationCheck(Config.accessList.forceRequestBodyContinuation, request, NULL);
- if (bodyContinuationCheck.fastCheck() == ACCESS_ALLOWED) {
+ if (bodyContinuationCheck.fastCheck().allowed()) {
request->forcedBodyContinuation = true;
if (checkDataConnPost()) {
// Write control Msg
writeEarlyReply(150, "Data connection opened");
maybeReadUploadData();
} else {
// wait for acceptDataConnection but tell it to call wroteEarlyReply
// after writing "150 Data connection opened"
typedef CommCbMemFunT<Server, CommIoCbParams> Dialer;
AsyncCall::Pointer call = JobCallback(33, 5, Dialer, this, Ftp::Server::wroteEarlyReply);
onDataAcceptCall = call;
}
}
}
changeState(fssHandleUploadRequest, "handleDataRequest");
return true;
}
bool
Ftp::Server::handleEprtRequest(String &, String ¶ms)
{
debugs(9, 3, "Process an EPRT " << params);
if (gotEpsvAll) {
setReply(500, "Rejecting EPRT after EPSV ALL");
return false;
}
=== modified file 'src/servers/Http1Server.cc'
--- src/servers/Http1Server.cc 2017-01-30 12:46:15 +0000
+++ src/servers/Http1Server.cc 2017-05-04 17:14:41 +0000
@@ -228,61 +228,61 @@ Http::One::Server::proceedAfterBodyConti
void
Http::One::Server::processParsedRequest(Http::StreamPointer &context)
{
if (!buildHttpRequest(context))
return;
ClientHttpRequest *http = context->http;
HttpRequest::Pointer request = http->request;
if (request->header.has(Http::HdrType::EXPECT)) {
const String expect = request->header.getList(Http::HdrType::EXPECT);
const bool supportedExpect = (expect.caseCmp("100-continue") == 0);
if (!supportedExpect) {
clientStreamNode *node = context->getClientReplyContext();
quitAfterError(request.getRaw());
// setLogUri should called before repContext->setReplyToError
setLogUri(http, urlCanonicalClean(request.getRaw()));
clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
assert (repContext);
repContext->setReplyToError(ERR_INVALID_REQ, Http::scExpectationFailed, request->method, http->uri,
clientConnection->remote, request.getRaw(), NULL, NULL);
assert(context->http->out.offset == 0);
context->pullData();
clientProcessRequestFinished(this, request);
return;
}
if (Config.accessList.forceRequestBodyContinuation) {
ACLFilledChecklist bodyContinuationCheck(Config.accessList.forceRequestBodyContinuation, request.getRaw(), NULL);
- if (bodyContinuationCheck.fastCheck() == ACCESS_ALLOWED) {
+ if (bodyContinuationCheck.fastCheck().allowed()) {
debugs(33, 5, "Body Continuation forced");
request->forcedBodyContinuation = true;
//sendControlMsg
HttpReply::Pointer rep = new HttpReply;
rep->sline.set(Http::ProtocolVersion(), Http::scContinue);
typedef UnaryMemFunT<Http1::Server, Http::StreamPointer> CbDialer;
const AsyncCall::Pointer cb = asyncCall(11, 3, "Http1::Server::proceedAfterBodyContinuation", CbDialer(this, &Http1::Server::proceedAfterBodyContinuation, Http::StreamPointer(context)));
sendControlMsg(HttpControlMsg(rep, cb));
return;
}
}
}
clientProcessRequest(this, parser_, context.getRaw());
}
void
Http::One::Server::noteBodyConsumerAborted(BodyPipe::Pointer ptr)
{
ConnStateData::noteBodyConsumerAborted(ptr);
stopReceiving("virgin request body consumer aborted"); // closes ASAP
}
void
Http::One::Server::handleReply(HttpReply *rep, StoreIOBuffer receivedData)
{
// the caller guarantees that we are dealing with the current context only
Http::StreamPointer context = pipeline.front();
Must(context != nullptr);
const ClientHttpRequest *http = context->http;
=== modified file 'src/snmp_core.cc'
--- src/snmp_core.cc 2017-01-01 00:12:22 +0000
+++ src/snmp_core.cc 2017-05-04 17:14:41 +0000
@@ -356,82 +356,80 @@ snmpHandleUdp(int sock, void *)
len = comm_udp_recvfrom(sock, buf, sizeof(buf)-1, 0, from);
if (len > 0) {
debugs(49, 3, "snmpHandleUdp: FD " << sock << ": received " << len << " bytes from " << from << ".");
snmp_rq = (SnmpRequest *)xcalloc(1, sizeof(SnmpRequest));
snmp_rq->buf = (u_char *) buf;
snmp_rq->len = len;
snmp_rq->sock = sock;
snmp_rq->outbuf = (unsigned char *)xmalloc(snmp_rq->outlen = SNMP_REQUEST_SIZE);
snmp_rq->from = from;
snmpDecodePacket(snmp_rq);
xfree(snmp_rq->outbuf);
xfree(snmp_rq);
} else {
int xerrno = errno;
debugs(49, DBG_IMPORTANT, "snmpHandleUdp: FD " << sock << " recvfrom: " << xstrerr(xerrno));
}
}
/*
* Turn SNMP packet into a PDU, check available ACL's
*/
static void
snmpDecodePacket(SnmpRequest * rq)
{
struct snmp_pdu *PDU;
u_char *Community;
u_char *buf = rq->buf;
int len = rq->len;
- allow_t allow = ACCESS_DENIED;
if (!Config.accessList.snmp) {
debugs(49, DBG_IMPORTANT, "WARNING: snmp_access not configured. agent query DENIED from : " << rq->from);
return;
}
debugs(49, 5, HERE << "Called.");
PDU = snmp_pdu_create(0);
/* Allways answer on SNMPv1 */
rq->session.Version = SNMP_VERSION_1;
Community = snmp_parse(&rq->session, PDU, buf, len);
/* Check if we have explicit permission to access SNMP data.
* default (set above) is to deny all */
if (Community) {
ACLFilledChecklist checklist(Config.accessList.snmp, NULL, NULL);
checklist.src_addr = rq->from;
checklist.snmp_community = (char *) Community;
- allow = checklist.fastCheck();
- if (allow == ACCESS_ALLOWED && (snmp_coexist_V2toV1(PDU))) {
+ if (checklist.fastCheck().allowed() && (snmp_coexist_V2toV1(PDU))) {
rq->community = Community;
rq->PDU = PDU;
debugs(49, 5, "snmpAgentParse: reqid=[" << PDU->reqid << "]");
snmpConstructReponse(rq);
} else {
debugs(49, DBG_IMPORTANT, "WARNING: SNMP agent query DENIED from : " << rq->from);
}
xfree(Community);
} else {
debugs(49, DBG_IMPORTANT, "WARNING: Failed SNMP agent query from : " << rq->from);
snmp_free_pdu(PDU);
}
}
/*
* Packet OK, ACL Check OK, Create reponse.
*/
static void
snmpConstructReponse(SnmpRequest * rq)
{
struct snmp_pdu *RespPDU;
debugs(49, 5, "snmpConstructReponse: Called.");
if (UsingSmp() && IamWorkerProcess()) {
AsyncJob::Start(new Snmp::Forwarder(static_cast<Snmp::Pdu&>(*rq->PDU),
static_cast<Snmp::Session&>(rq->session), rq->sock, rq->from));
snmp_free_pdu(rq->PDU);
=== modified file 'src/ssl/PeekingPeerConnector.cc'
--- src/ssl/PeekingPeerConnector.cc 2017-02-05 05:57:32 +0000
+++ src/ssl/PeekingPeerConnector.cc 2017-05-04 17:14:41 +0000
@@ -9,61 +9,61 @@
/* DEBUG: section 83 SSL-Bump Server/Peer negotiation */
#include "squid.h"
#include "acl/FilledChecklist.h"
#include "client_side.h"
#include "errorpage.h"
#include "fde.h"
#include "http/Stream.h"
#include "HttpRequest.h"
#include "security/NegotiationHistory.h"
#include "SquidConfig.h"
#include "ssl/bio.h"
#include "ssl/PeekingPeerConnector.h"
#include "ssl/ServerBump.h"
CBDATA_NAMESPACED_CLASS_INIT(Ssl, PeekingPeerConnector);
void switchToTunnel(HttpRequest *request, Comm::ConnectionPointer & clientConn, Comm::ConnectionPointer &srvConn);
void
Ssl::PeekingPeerConnector::cbCheckForPeekAndSpliceDone(allow_t answer, void *data)
{
Ssl::PeekingPeerConnector *peerConnect = (Ssl::PeekingPeerConnector *) data;
// Use job calls to add done() checks and other job logic/protections.
CallJobHere1(83, 7, CbcPointer<PeekingPeerConnector>(peerConnect), Ssl::PeekingPeerConnector, checkForPeekAndSpliceDone, answer);
}
void
Ssl::PeekingPeerConnector::checkForPeekAndSpliceDone(allow_t answer)
{
- const Ssl::BumpMode finalAction = (answer.code == ACCESS_ALLOWED) ?
+ const Ssl::BumpMode finalAction = answer.allowed() ?
static_cast<Ssl::BumpMode>(answer.kind):
checkForPeekAndSpliceGuess();
checkForPeekAndSpliceMatched(finalAction);
}
void
Ssl::PeekingPeerConnector::checkForPeekAndSplice()
{
// Mark Step3 of bumping
if (request->clientConnectionManager.valid()) {
if (Ssl::ServerBump *serverBump = request->clientConnectionManager->serverBump()) {
serverBump->step = Ssl::bumpStep3;
}
}
handleServerCertificate();
ACLFilledChecklist *acl_checklist = new ACLFilledChecklist(
::Config.accessList.ssl_bump,
request.getRaw(), NULL);
acl_checklist->al = al;
acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpNone));
acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpPeek));
acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpStare));
acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpClientFirst));
acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpServerFirst));
Security::SessionPointer session(fd_table[serverConn->fd].ssl);
BIO *b = SSL_get_rbio(session.get());
Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
if (!srvBio->canSplice())
=== modified file 'src/ssl/support.cc'
--- src/ssl/support.cc 2017-02-05 05:57:32 +0000
+++ src/ssl/support.cc 2017-05-04 17:14:41 +0000
@@ -305,61 +305,61 @@ ssl_verify_cb(int ok, X509_STORE_CTX * c
Security::CertPointer broken_cert;
broken_cert.resetAndLock(X509_STORE_CTX_get_current_cert(ctx));
if (!broken_cert)
broken_cert = peer_cert;
Security::CertErrors *errs = static_cast<Security::CertErrors *>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors));
const int depth = X509_STORE_CTX_get_error_depth(ctx);
if (!errs) {
errs = new Security::CertErrors(Security::CertError(error_no, broken_cert, depth));
if (!SSL_set_ex_data(ssl, ssl_ex_index_ssl_errors, (void *)errs)) {
debugs(83, 2, "Failed to set ssl error_no in ssl_verify_cb: Certificate " << buffer);
delete errs;
errs = NULL;
}
} else // remember another error number
errs->push_back_unique(Security::CertError(error_no, broken_cert, depth));
if (const char *err_descr = Ssl::GetErrorDescr(error_no))
debugs(83, 5, err_descr << ": " << buffer);
else
debugs(83, DBG_IMPORTANT, "SSL unknown certificate error " << error_no << " in " << buffer);
// Check if the certificate error can be bypassed.
// Infinity validation loop errors can not bypassed.
if (error_no != SQUID_X509_V_ERR_INFINITE_VALIDATION) {
if (check) {
ACLFilledChecklist *filledCheck = Filled(check);
assert(!filledCheck->sslErrors);
filledCheck->sslErrors = new Security::CertErrors(Security::CertError(error_no, broken_cert));
filledCheck->serverCert = peer_cert;
- if (check->fastCheck() == ACCESS_ALLOWED) {
+ if (check->fastCheck().allowed()) {
debugs(83, 3, "bypassing SSL error " << error_no << " in " << buffer);
ok = 1;
} else {
debugs(83, 5, "confirming SSL error " << error_no);
}
delete filledCheck->sslErrors;
filledCheck->sslErrors = NULL;
filledCheck->serverCert.reset();
}
// If the certificate validator is used then we need to allow all errors and
// pass them to certficate validator for more processing
else if (Ssl::TheConfig.ssl_crt_validator) {
ok = 1;
}
}
}
if (Ssl::TheConfig.ssl_crt_validator) {
// Check if we have stored certificates chain. Store if not.
if (!SSL_get_ex_data(ssl, ssl_ex_index_ssl_cert_chain)) {
STACK_OF(X509) *certStack = X509_STORE_CTX_get1_chain(ctx);
if (certStack && !SSL_set_ex_data(ssl, ssl_ex_index_ssl_cert_chain, certStack))
sk_X509_pop_free(certStack, X509_free);
}
}
if (!ok && !SSL_get_ex_data(ssl, ssl_ex_index_ssl_error_detail) ) {
// Find the broken certificate. It may be intermediate.
Security::CertPointer broken_cert(peer_cert); // reasonable default if search fails
=== modified file 'src/tunnel.cc'
--- src/tunnel.cc 2017-04-12 23:34:50 +0000
+++ src/tunnel.cc 2017-05-04 17:14:41 +0000
@@ -1052,61 +1052,61 @@ tunnelConnectDone(const Comm::Connection
AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout",
CommTimeoutCbPtrFun(tunnelTimeout, tunnelState));
commSetConnTimeout(conn, Config.Timeout.read, timeoutCall);
}
void
tunnelStart(ClientHttpRequest * http)
{
debugs(26, 3, HERE);
/* Create state structure. */
TunnelStateData *tunnelState = NULL;
ErrorState *err = NULL;
HttpRequest *request = http->request;
char *url = http->uri;
/*
* client_addr.isNoAddr() indicates this is an "internal" request
* from peer_digest.c, asn.c, netdb.c, etc and should always
* be allowed. yuck, I know.
*/
if (Config.accessList.miss && !request->client_addr.isNoAddr()) {
/*
* Check if this host is allowed to fetch MISSES from us (miss_access)
* default is to allow.
*/
ACLFilledChecklist ch(Config.accessList.miss, request, NULL);
ch.src_addr = request->client_addr;
ch.my_addr = request->my_addr;
- if (ch.fastCheck() == ACCESS_DENIED) {
+ if (!ch.fastCheck().allowed()) {
debugs(26, 4, HERE << "MISS access forbidden.");
err = new ErrorState(ERR_FORWARDING_DENIED, Http::scForbidden, request);
http->al->http.code = Http::scForbidden;
errorSend(http->getConn()->clientConnection, err);
return;
}
}
debugs(26, 3, request->method << ' ' << url << ' ' << request->http_ver);
++statCounter.server.all.requests;
++statCounter.server.other.requests;
tunnelState = new TunnelStateData(http);
#if USE_DELAY_POOLS
//server.setDelayId called from tunnelConnectDone after server side connection established
#endif
peerSelect(&(tunnelState->serverDestinations), request, http->al,
NULL,
tunnelPeerSelectComplete,
tunnelState);
}
void
TunnelStateData::connectToPeer()
{
if (CachePeer *p = server.conn->getPeer()) {
if (p->secure.encryptTransport) {
AsyncCall::Pointer callback = asyncCall(5,4,
"TunnelStateData::ConnectedToPeer",
_______________________________________________
squid-dev mailing list
[email protected]
http://lists.squid-cache.org/listinfo/squid-dev