]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/cache_cf.cc
Merge form trunk
[thirdparty/squid.git] / src / cache_cf.cc
index 76fa702e234b7366878c03c16bb3cc81f46d72d6..5d5d8210097572df2de3eb9ed974b33d62b3759e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * $Id$
  *
- * DEBUG: section     Configuration File Parsing
+ * DEBUG: section 03    Configuration File Parsing
  * AUTHOR: Harvest Derived
  *
  * SQUID Web Proxy Cache          http://www.squid-cache.org/
 #endif
 #include "auth/Config.h"
 #include "auth/Scheme.h"
-#include "CacheManager.h"
 #include "ConfigParser.h"
+#include "CpuAffinityMap.h"
 #include "eui/Config.h"
 #if USE_SQUID_ESI
 #include "esi/Parser.h"
 #endif
 #include "HttpRequestMethod.h"
 #include "ident/Config.h"
-#include "ip/IpIntercept.h"
+#include "ip/Intercept.h"
+#include "ip/QosConfig.h"
+#include "ip/tools.h"
 #include "log/Config.h"
 #include "MemBuf.h"
+#include "mgr/Registration.h"
 #include "Parsing.h"
 #include "ProtoPort.h"
 #include "rfc1738.h"
 #include "StoreFileSystem.h"
 #include "SwapDir.h"
 #include "wordlist.h"
+#include "ipc/Kids.h"
 
 #if HAVE_GLOB_H
 #include <glob.h>
 #endif
 
+#if HAVE_LIMITS_H
+#include <limits>
+#endif
+
 #if USE_ADAPTATION
 static void parse_adaptation_service_set_type();
 static void parse_adaptation_service_chain_type();
@@ -86,6 +94,10 @@ static void dump_icap_service_type(StoreEntry *, const char *, const Adaptation:
 static void free_icap_service_type(Adaptation::Icap::Config *);
 static void parse_icap_class_type();
 static void parse_icap_access_type();
+
+static void parse_icap_service_failure_limit(Adaptation::Icap::Config *);
+static void dump_icap_service_failure_limit(StoreEntry *, const char *, const Adaptation::Icap::Config &);
+static void free_icap_service_failure_limit(Adaptation::Icap::Config *);
 #endif
 
 #if USE_ECAP
@@ -132,12 +144,16 @@ static void parse_string(char **);
 static void default_all(void);
 static void defaults_if_none(void);
 static int parse_line(char *);
+static void parse_obsolete(const char *);
 static void parseBytesLine(size_t * bptr, const char *units);
+#if !USE_DNSSERVERS
+static void parseBytesLineSigned(ssize_t * bptr, const char *units);
+#endif
 static size_t parseBytesUnits(const char *unit);
 static void free_all(void);
 void requirePathnameExists(const char *name, const char *path);
 static OBJH dump_config;
-#if HTTP_VIOLATIONS
+#if USE_HTTP_VIOLATIONS
 static void dump_http_header_access(StoreEntry * entry, const char *name, header_mangler header[]);
 static void parse_http_header_access(header_mangler header[]);
 static void free_http_header_access(header_mangler header[]);
@@ -150,11 +166,11 @@ static void dump_denyinfo(StoreEntry * entry, const char *name, acl_deny_info_li
 static void free_denyinfo(acl_deny_info_list ** var);
 
 #if USE_WCCPv2
-static void parse_IpAddress_list(IpAddress_list **);
-static void dump_IpAddress_list(StoreEntry *, const char *, const IpAddress_list *);
-static void free_IpAddress_list(IpAddress_list **);
+static void parse_IpAddress_list(Ip::Address_list **);
+static void dump_IpAddress_list(StoreEntry *, const char *, const Ip::Address_list *);
+static void free_IpAddress_list(Ip::Address_list **);
 #if CURRENTLY_UNUSED
-static int check_null_IpAddress_list(const IpAddress_list *);
+static int check_null_IpAddress_list(const Ip::Address_list *);
 #endif /* CURRENTLY_UNUSED */
 #endif /* USE_WCCPv2 */
 
@@ -174,6 +190,12 @@ static int check_null_https_port_list(const https_port_list *);
 static void parse_b_size_t(size_t * var);
 static void parse_b_int64_t(int64_t * var);
 
+static bool parseNamedIntList(const char *data, const String &name, Vector<int> &list);
+
+static void parse_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap);
+static void dump_CpuAffinityMap(StoreEntry *const entry, const char *const name, const CpuAffinityMap *const cpuAffinityMap);
+static void free_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap);
+
 static int parseOneConfigFile(const char *file_name, unsigned int depth);
 
 /*
@@ -258,6 +280,110 @@ parseManyConfigFiles(char* files, int depth)
     return error_count;
 }
 
+static void
+ReplaceSubstr(char*& str, int& len, unsigned substrIdx, unsigned substrLen, const char* newSubstr)
+{
+    assert(str != NULL);
+    assert(newSubstr != NULL);
+
+    unsigned newSubstrLen = strlen(newSubstr);
+    if (newSubstrLen > substrLen)
+        str = (char*)realloc(str, len - substrLen + newSubstrLen + 1);
+
+    // move tail part including zero
+    memmove(str + substrIdx + newSubstrLen, str + substrIdx + substrLen, len - substrIdx - substrLen + 1);
+    // copy new substring in place
+    memcpy(str + substrIdx, newSubstr, newSubstrLen);
+
+    len = strlen(str);
+}
+
+static void
+SubstituteMacro(char*& line, int& len, const char* macroName, const char* substStr)
+{
+    assert(line != NULL);
+    assert(macroName != NULL);
+    assert(substStr != NULL);
+    unsigned macroNameLen = strlen(macroName);
+    while (const char* macroPos = strstr(line, macroName)) // we would replace all occurrences
+        ReplaceSubstr(line, len, macroPos - line, macroNameLen, substStr);
+}
+
+static void
+ProcessMacros(char*& line, int& len)
+{
+    SubstituteMacro(line, len, "${process_name}", TheKidName);
+    SubstituteMacro(line, len, "${process_number}", xitoa(KidIdentifier));
+}
+
+static void
+trim_trailing_ws(char* str)
+{
+    assert(str != NULL);
+    unsigned i = strlen(str);
+    while ((i > 0) && xisspace(str[i - 1]))
+        --i;
+    str[i] = '\0';
+}
+
+static const char*
+FindStatement(const char* line, const char* statement)
+{
+    assert(line != NULL);
+    assert(statement != NULL);
+
+    const char* str = skip_ws(line);
+    unsigned len = strlen(statement);
+    if (strncmp(str, statement, len) == 0) {
+        str += len;
+        if (*str == '\0')
+            return str;
+        else if (xisspace(*str))
+            return skip_ws(str);
+    }
+
+    return NULL;
+}
+
+static bool
+StrToInt(const char* str, long& number)
+{
+    assert(str != NULL);
+
+    char* end;
+    number = strtol(str, &end, 0);
+
+    return (end != str) && (*end == '\0'); // returns true if string contains nothing except number
+}
+
+static bool
+EvalBoolExpr(const char* expr)
+{
+    assert(expr != NULL);
+    if (strcmp(expr, "true") == 0) {
+        return true;
+    } else if (strcmp(expr, "false") == 0) {
+        return false;
+    } else if (const char* equation = strchr(expr, '=')) {
+        const char* rvalue = skip_ws(equation + 1);
+        char* lvalue = (char*)xmalloc(equation - expr + 1);
+        xstrncpy(lvalue, expr, equation - expr + 1);
+        trim_trailing_ws(lvalue);
+
+        long number1;
+        if (!StrToInt(lvalue, number1))
+            fatalf("String is not a integer number: '%s'\n", lvalue);
+        long number2;
+        if (!StrToInt(rvalue, number2))
+            fatalf("String is not a integer number: '%s'\n", rvalue);
+
+        xfree(lvalue);
+        return number1 == number2;
+    }
+    fatalf("Unable to evaluate expression '%s'\n", expr);
+    return false; // this place cannot be reached
+}
+
 static int
 parseOneConfigFile(const char *file_name, unsigned int depth)
 {
@@ -298,6 +424,7 @@ parseOneConfigFile(const char *file_name, unsigned int depth)
 
     config_lineno = 0;
 
+    Vector<bool> if_states;
     while (fgets(config_input_line, BUFSIZ, fp)) {
         config_lineno++;
 
@@ -357,20 +484,38 @@ parseOneConfigFile(const char *file_name, unsigned int depth)
             continue;
         }
 
+        trim_trailing_ws(tmp_line);
+        ProcessMacros(tmp_line, tmp_line_len);
         debugs(3, 5, "Processing: '" << tmp_line << "'");
 
-        /* Handle includes here */
-        if (tmp_line_len >= 9 && strncmp(tmp_line, "include", 7) == 0 && xisspace(tmp_line[7])) {
-            err_count += parseManyConfigFiles(tmp_line + 8, depth + 1);
-        } else if (!parse_line(tmp_line)) {
-            debugs(3, 0, HERE << cfg_filename << ":" << config_lineno << " unrecognized: '" << tmp_line << "'");
-            err_count++;
+        if (const char* expr = FindStatement(tmp_line, "if")) {
+            if_states.push_back(EvalBoolExpr(expr)); // store last if-statement meaning
+        } else if (FindStatement(tmp_line, "endif")) {
+            if (!if_states.empty())
+                if_states.pop_back(); // remove last if-statement meaning
+            else
+                fatalf("'endif' without 'if'\n");
+        } else if (FindStatement(tmp_line, "else")) {
+            if (!if_states.empty())
+                if_states.back() = !if_states.back();
+            else
+                fatalf("'else' without 'if'\n");
+        } else if (if_states.empty() || if_states.back()) { // test last if-statement meaning if present
+            /* Handle includes here */
+            if (tmp_line_len >= 9 && strncmp(tmp_line, "include", 7) == 0 && xisspace(tmp_line[7])) {
+                err_count += parseManyConfigFiles(tmp_line + 8, depth + 1);
+            } else if (!parse_line(tmp_line)) {
+                debugs(3, 0, HERE << cfg_filename << ":" << config_lineno << " unrecognized: '" << tmp_line << "'");
+                err_count++;
+            }
         }
 
         safe_free(tmp_line);
         tmp_line_len = 0;
 
     }
+    if (!if_states.empty())
+        fatalf("if-statement without 'endif'\n");
 
     if (is_pipe) {
         int ret = pclose(fp);
@@ -391,7 +536,8 @@ int
 parseConfigFile(const char *file_name)
 {
     int err_count = 0;
-    CacheManager *manager=CacheManager::GetInstance();
+
+    debugs(5, 4, HERE);
 
     configFreeMemory();
 
@@ -417,10 +563,10 @@ parseConfigFile(const char *file_name)
     }
 
     if (opt_send_signal == -1) {
-        manager->registerAction("config",
-                                "Current Squid Configuration",
-                                dump_config,
-                                1, 1);
+        Mgr::RegisterAction("config",
+                            "Current Squid Configuration",
+                            dump_config,
+                            1, 1);
     }
 
     return err_count;
@@ -519,12 +665,9 @@ configDoConfigure(void)
     else
         Config.appendDomainLen = 0;
 
-    if (Config.retry.maxtries > 10)
-        fatal("maximum_single_addr_tries cannot be larger than 10");
-
-    if (Config.retry.maxtries < 1) {
-        debugs(3, 0, "WARNING: resetting 'maximum_single_addr_tries to 1");
-        Config.retry.maxtries = 1;
+    if (Config.connect_retries > 10) {
+        debugs(0,DBG_CRITICAL, "WARNING: connect_retries cannot be larger than 10. Resetting to 10.");
+        Config.connect_retries = 10;
     }
 
     requirePathnameExists("MIME Config Table", Config.mimeTablePathname);
@@ -545,7 +688,7 @@ configDoConfigure(void)
     if (Config.errorDirectory)
         requirePathnameExists("Error Directory", Config.errorDirectory);
 
-#if HTTP_VIOLATIONS
+#if USE_HTTP_VIOLATIONS
 
     {
         const refresh_t *R;
@@ -631,7 +774,7 @@ configDoConfigure(void)
 
     }
 #endif
-#if !HTTP_VIOLATIONS
+#if !USE_HTTP_VIOLATIONS
     Config.onoff.via = 1;
 #else
 
@@ -747,6 +890,24 @@ configDoConfigure(void)
 #endif
 }
 
+/** Parse a line containing an obsolete directive.
+ * To upgrade it where possible instead of just "Bungled config" for
+ * directives which cannot be marked as simply aliases of the some name.
+ * For example if the parameter order and content has changed.
+ * Or if the directive has been completely removed.
+ */
+void
+parse_obsolete(const char *name)
+{
+    // Directives which have been radically changed rather than removed
+    if (!strcmp(name, "url_rewrite_concurrency")) {
+        int cval;
+        parse_int(&cval);
+        debugs(3, DBG_CRITICAL, "WARNING: url_rewrite_concurrency upgrade overriding url_rewrite_children settings.");
+        Config.redirectChildren.concurrency = cval;
+    }
+}
+
 /* Parse a time specification from the config file.  Store the
  * result in 'tptr', after converting it to 'units' */
 static void
@@ -903,6 +1064,52 @@ parseBytesLine(size_t * bptr, const char *units)
         self_destruct();
 }
 
+#if !USE_DNSSERVERS
+static void
+parseBytesLineSigned(ssize_t * bptr, const char *units)
+{
+    char *token;
+    double d;
+    int m;
+    int u;
+
+    if ((u = parseBytesUnits(units)) == 0) {
+        self_destruct();
+        return;
+    }
+
+    if ((token = strtok(NULL, w_space)) == NULL) {
+        self_destruct();
+        return;
+    }
+
+    if (strcmp(token, "none") == 0 || token[0] == '-' /* -N */) {
+        *bptr = -1;
+        return;
+    }
+
+    d = xatof(token);
+
+    m = u;                     /* default to 'units' if none specified */
+
+    if (0.0 == d)
+        (void) 0;
+    else if ((token = strtok(NULL, w_space)) == NULL)
+        debugs(3, 0, "WARNING: No units on '" <<
+               config_input_line << "', assuming " <<
+               d << " " <<  units  );
+    else if ((m = parseBytesUnits(token)) == 0) {
+        self_destruct();
+        return;
+    }
+
+    *bptr = static_cast<size_t>(m * d / u);
+
+    if (static_cast<double>(*bptr) * 2 != m * d / u * 2)
+        self_destruct();
+}
+#endif
+
 static size_t
 parseBytesUnits(const char *unit)
 {
@@ -965,7 +1172,7 @@ free_acl(ACL ** ae)
     aclDestroyAcls(ae);
 }
 
-static void
+void
 dump_acl_list(StoreEntry * entry, ACLList * head)
 {
     ACLList *l;
@@ -1004,14 +1211,14 @@ free_acl_access(acl_access ** head)
 }
 
 static void
-dump_address(StoreEntry * entry, const char *name, IpAddress &addr)
+dump_address(StoreEntry * entry, const char *name, Ip::Address &addr)
 {
     char buf[MAX_IPSTRLEN];
     storeAppendPrintf(entry, "%s %s\n", name, addr.NtoA(buf,MAX_IPSTRLEN) );
 }
 
 static void
-parse_address(IpAddress *addr)
+parse_address(Ip::Address *addr)
 {
     char *token = strtok(NULL, w_space);
 
@@ -1031,7 +1238,7 @@ parse_address(IpAddress *addr)
 }
 
 static void
-free_address(IpAddress *addr)
+free_address(Ip::Address *addr)
 {
     addr->SetEmpty();
 }
@@ -1120,8 +1327,7 @@ parse_acl_tos(acl_tos ** head)
 {
     acl_tos *l;
     acl_tos **tail = head;     /* sane name below */
-    int tos;
-    char junk;
+    unsigned int tos;           /* Initially uint for strtoui. Casted to tos_t before return */
     char *token = strtok(NULL, w_space);
 
     if (!token) {
@@ -1129,12 +1335,7 @@ parse_acl_tos(acl_tos ** head)
         return;
     }
 
-    if (sscanf(token, "0x%x%c", &tos, &junk) != 1) {
-        self_destruct();
-        return;
-    }
-
-    if (tos < 0 || tos > 255) {
+    if (!xstrtoui(token, NULL, &tos, 0, std::numeric_limits<tos_t>::max())) {
         self_destruct();
         return;
     }
@@ -1143,7 +1344,7 @@ parse_acl_tos(acl_tos ** head)
 
     l = cbdataAlloc(acl_tos);
 
-    l->tos = tos;
+    l->tos = (tos_t)tos;
 
     aclParseAclList(LegacyParser, &l->aclList);
 
@@ -1164,6 +1365,78 @@ free_acl_tos(acl_tos ** head)
     }
 }
 
+#if defined(SO_MARK)
+
+CBDATA_TYPE(acl_nfmark);
+
+static void
+dump_acl_nfmark(StoreEntry * entry, const char *name, acl_nfmark * head)
+{
+    acl_nfmark *l;
+
+    for (l = head; l; l = l->next) {
+        if (l->nfmark > 0)
+            storeAppendPrintf(entry, "%s 0x%02X", name, l->nfmark);
+        else
+            storeAppendPrintf(entry, "%s none", name);
+
+        dump_acl_list(entry, l->aclList);
+
+        storeAppendPrintf(entry, "\n");
+    }
+}
+
+static void
+freed_acl_nfmark(void *data)
+{
+    acl_nfmark *l = static_cast<acl_nfmark *>(data);
+    aclDestroyAclList(&l->aclList);
+}
+
+static void
+parse_acl_nfmark(acl_nfmark ** head)
+{
+    acl_nfmark *l;
+    acl_nfmark **tail = head;  /* sane name below */
+    nfmark_t mark;
+    char *token = strtok(NULL, w_space);
+
+    if (!token) {
+        self_destruct();
+        return;
+    }
+
+    if (!xstrtoui(token, NULL, &mark, 0, std::numeric_limits<nfmark_t>::max())) {
+        self_destruct();
+        return;
+    }
+
+    CBDATA_INIT_TYPE_FREECB(acl_nfmark, freed_acl_nfmark);
+
+    l = cbdataAlloc(acl_nfmark);
+
+    l->nfmark = mark;
+
+    aclParseAclList(LegacyParser, &l->aclList);
+
+    while (*tail)
+        tail = &(*tail)->next;
+
+    *tail = l;
+}
+
+static void
+free_acl_nfmark(acl_nfmark ** head)
+{
+    while (*head) {
+        acl_nfmark *l = *head;
+        *head = l->next;
+        l->next = NULL;
+        cbdataFree(l);
+    }
+}
+#endif /* SO_MARK */
+
 CBDATA_TYPE(acl_size_t);
 
 static void
@@ -1273,7 +1546,49 @@ parse_delay_pool_access(DelayConfig * cfg)
 
 #endif
 
-#if HTTP_VIOLATIONS
+#if DELAY_POOLS
+#include "ClientDelayConfig.h"
+/* do nothing - free_client_delay_pool_count is the magic free function.
+ * this is why client_delay_pool_count isn't just marked TYPE: ushort
+ */
+
+#define free_client_delay_pool_access(X)
+#define free_client_delay_pool_rates(X)
+#define dump_client_delay_pool_access(X, Y, Z)
+#define dump_client_delay_pool_rates(X, Y, Z)
+
+static void
+free_client_delay_pool_count(ClientDelayConfig * cfg)
+{
+    cfg->freePoolCount();
+}
+
+static void
+dump_client_delay_pool_count(StoreEntry * entry, const char *name, ClientDelayConfig &cfg)
+{
+    cfg.dumpPoolCount (entry, name);
+}
+
+static void
+parse_client_delay_pool_count(ClientDelayConfig * cfg)
+{
+    cfg->parsePoolCount();
+}
+
+static void
+parse_client_delay_pool_rates(ClientDelayConfig * cfg)
+{
+    cfg->parsePoolRates();
+}
+
+static void
+parse_client_delay_pool_access(ClientDelayConfig * cfg)
+{
+    cfg->parsePoolAccess(LegacyParser);
+}
+#endif
+
+#if USE_HTTP_VIOLATIONS
 static void
 dump_http_header_access(StoreEntry * entry, const char *name, header_mangler header[])
 {
@@ -1431,7 +1746,7 @@ check_null_string(char *s)
 }
 
 static void
-parse_authparam(authConfig * config)
+parse_authparam(Auth::authConfig * config)
 {
     char *type_str;
     char *param_str;
@@ -1442,38 +1757,43 @@ parse_authparam(authConfig * config)
     if ((param_str = strtok(NULL, w_space)) == NULL)
         self_destruct();
 
-    /* find a configuration for the scheme */
-    AuthConfig *scheme = AuthConfig::Find (type_str);
+    /* find a configuration for the scheme in the currently parsed configs... */
+    AuthConfig *schemeCfg = AuthConfig::Find(type_str);
 
-    if (scheme == NULL) {
-        /* Create a configuration */
-        AuthScheme *theScheme;
+    if (schemeCfg == NULL) {
+        /* Create a configuration based on the scheme info */
+        AuthScheme::Pointer theScheme = AuthScheme::Find(type_str);
 
-        if ((theScheme = AuthScheme::Find(type_str)) == NULL) {
-            debugs(3, 0, "Parsing Config File: Unknown authentication scheme '" << type_str << "'.");
-            return;
+        if (theScheme == NULL) {
+            debugs(3, DBG_CRITICAL, "Parsing Config File: Unknown authentication scheme '" << type_str << "'.");
+            self_destruct();
         }
 
         config->push_back(theScheme->createConfig());
-        scheme = config->back();
-        assert (scheme);
+        schemeCfg = AuthConfig::Find(type_str);
+        if (schemeCfg == NULL) {
+            debugs(3, DBG_CRITICAL, "Parsing Config File: Corruption configuring authentication scheme '" << type_str << "'.");
+            self_destruct();
+        }
     }
 
-    scheme->parse(scheme, config->size(), param_str);
+    schemeCfg->parse(schemeCfg, config->size(), param_str);
 }
 
 static void
-free_authparam(authConfig * cfg)
+free_authparam(Auth::authConfig * cfg)
 {
-    AuthConfig *scheme;
-    /* DON'T FREE THESE FOR RECONFIGURE */
-
-    if (reconfiguring)
-        return;
+    /* Wipe the Auth globals and Detach/Destruct component config + state. */
+    cfg->clean();
 
+    /* remove our pointers to the probably-dead sub-configs */
     while (cfg->size()) {
-        scheme = cfg->pop_back();
-        scheme->done();
+        cfg->pop_back();
+    }
+
+    /* on reconfigure initialize new auth schemes for the new config. */
+    if (reconfiguring) {
+        InitAuthSchemes();
     }
 }
 
@@ -1498,6 +1818,18 @@ find_fstype(char *type)
 static void
 parse_cachedir(SquidConfig::_cacheSwap * swap)
 {
+    // The workers option must preceed cache_dir for the IamWorkerProcess check
+    // below to work. TODO: Redo IamWorkerProcess to work w/o Config and remove
+    if (KidIdentifier > 1 && Config.workers == 1) {
+        debugs(3, DBG_CRITICAL,
+               "FATAL: cache_dir found before the workers option. Reorder.");
+        self_destruct();
+    }
+
+    // Among all processes, only workers may need and can handle cache_dir.
+    if (!IamWorkerProcess())
+        return;
+
     char *type_str;
     char *path_str;
     RefCount<SwapDir> sd;
@@ -1771,30 +2103,37 @@ parse_peer(peer ** head)
         } else if (!strcasecmp(token, "weighted-round-robin")) {
             p->options.weighted_roundrobin = 1;
 #if USE_HTCP
-
         } else if (!strcasecmp(token, "htcp")) {
             p->options.htcp = 1;
-        } else if (!strcasecmp(token, "htcp-oldsquid")) {
-            p->options.htcp = 1;
-            p->options.htcp_oldsquid = 1;
-        } else if (!strcasecmp(token, "htcp-no-clr")) {
-            if (p->options.htcp_only_clr)
-                fatalf("parse_peer: can't set htcp-no-clr and htcp-only-clr simultaneously");
-            p->options.htcp = 1;
-            p->options.htcp_no_clr = 1;
-        } else if (!strcasecmp(token, "htcp-no-purge-clr")) {
+        } else if (!strncasecmp(token, "htcp=", 5) || !strncasecmp(token, "htcp-", 5)) {
+            /* Note: The htcp- form is deprecated, replaced by htcp= */
             p->options.htcp = 1;
-            p->options.htcp_no_purge_clr = 1;
-        } else if (!strcasecmp(token, "htcp-only-clr")) {
-            if (p->options.htcp_no_clr)
-                fatalf("parse_peer: can't set htcp-no-clr and htcp-only-clr simultaneously");
-            p->options.htcp = 1;
-            p->options.htcp_only_clr = 1;
-        } else if (!strcasecmp(token, "htcp-forward-clr")) {
-            p->options.htcp = 1;
-            p->options.htcp_forward_clr = 1;
+            char *tmp = xstrdup(token+5);
+            char *mode, *nextmode;
+            for (mode = nextmode = tmp; mode; mode = nextmode) {
+                nextmode = strchr(mode, ',');
+                if (nextmode)
+                    *nextmode++ = '\0';
+                if (!strcasecmp(mode, "no-clr")) {
+                    if (p->options.htcp_only_clr)
+                        fatalf("parse_peer: can't set htcp-no-clr and htcp-only-clr simultaneously");
+                    p->options.htcp_no_clr = 1;
+                } else if (!strcasecmp(mode, "no-purge-clr")) {
+                    p->options.htcp_no_purge_clr = 1;
+                } else if (!strcasecmp(mode, "only-clr")) {
+                    if (p->options.htcp_no_clr)
+                        fatalf("parse_peer: can't set htcp no-clr and only-clr simultaneously");
+                    p->options.htcp_only_clr = 1;
+                } else if (!strcasecmp(mode, "forward-clr")) {
+                    p->options.htcp_forward_clr = 1;
+                } else if (!strcasecmp(mode, "oldsquid")) {
+                    p->options.htcp_oldsquid = 1;
+                } else {
+                    fatalf("invalid HTCP mode '%s'", mode);
+                }
+            }
+            safe_free(tmp);
 #endif
-
         } else if (!strcasecmp(token, "no-netdb-exchange")) {
             p->options.no_netdb_exchange = 1;
 
@@ -1917,7 +2256,7 @@ parse_peer(peer ** head)
 
     p->icp.version = ICP_VERSION_CURRENT;
 
-    p->test_fd = -1;
+    p->testing_now = false;
 
 #if USE_CACHE_DIGESTS
 
@@ -2238,7 +2577,7 @@ static void
 dump_refreshpattern(StoreEntry * entry, const char *name, refresh_t * head)
 {
     while (head != NULL) {
-        storeAppendPrintf(entry, "%s%s %s %d %d%% %d\n",
+        storeAppendPrintf(entry, "%s%s %s %d %d%% %d",
                           name,
                           head->flags.icase ? " -i" : null_string,
                           head->pattern,
@@ -2249,7 +2588,10 @@ dump_refreshpattern(StoreEntry * entry, const char *name, refresh_t * head)
         if (head->flags.refresh_ims)
             storeAppendPrintf(entry, " refresh-ims");
 
-#if HTTP_VIOLATIONS
+        if (head->flags.store_stale)
+            storeAppendPrintf(entry, " store-stale");
+
+#if USE_HTTP_VIOLATIONS
 
         if (head->flags.override_expire)
             storeAppendPrintf(entry, " override-expire");
@@ -2295,7 +2637,9 @@ parse_refreshpattern(refresh_t ** head)
     double pct = 0.0;
     time_t max = 0;
     int refresh_ims = 0;
-#if HTTP_VIOLATIONS
+    int store_stale = 0;
+
+#if USE_HTTP_VIOLATIONS
 
     int override_expire = 0;
     int override_lastmod = 0;
@@ -2336,6 +2680,16 @@ parse_refreshpattern(refresh_t ** head)
 
     i = GetInteger();          /* token: min */
 
+    /* catch negative and insanely huge values close to 32-bit wrap */
+    if (i < 0) {
+        debugs(3, DBG_IMPORTANT, "WARNING: refresh_pattern minimum age negative. Cropped back to zero.");
+        i = 0;
+    }
+    if (i > 60*24*365) {
+        debugs(3, DBG_IMPORTANT, "WARNING: refresh_pattern minimum age too high. Cropped back to 1 year.");
+        i = 60*24*365;
+    }
+
     min = (time_t) (i * 60);   /* convert minutes to seconds */
 
     i = GetInteger();          /* token: pct */
@@ -2344,13 +2698,25 @@ parse_refreshpattern(refresh_t ** head)
 
     i = GetInteger();          /* token: max */
 
+    /* catch negative and insanely huge values close to 32-bit wrap */
+    if (i < 0) {
+        debugs(3, DBG_IMPORTANT, "WARNING: refresh_pattern maximum age negative. Cropped back to zero.");
+        i = 0;
+    }
+    if (i > 60*24*365) {
+        debugs(3, DBG_IMPORTANT, "WARNING: refresh_pattern maximum age too high. Cropped back to 1 year.");
+        i = 60*24*365;
+    }
+
     max = (time_t) (i * 60);   /* convert minutes to seconds */
 
     /* Options */
     while ((token = strtok(NULL, w_space)) != NULL) {
         if (!strcmp(token, "refresh-ims")) {
             refresh_ims = 1;
-#if HTTP_VIOLATIONS
+        } else if (!strcmp(token, "store-stale")) {
+            store_stale = 1;
+#if USE_HTTP_VIOLATIONS
 
         } else if (!strcmp(token, "override-expire"))
             override_expire = 1;
@@ -2403,7 +2769,10 @@ parse_refreshpattern(refresh_t ** head)
     if (refresh_ims)
         t->flags.refresh_ims = 1;
 
-#if HTTP_VIOLATIONS
+    if (store_stale)
+        t->flags.store_stale = 1;
+
+#if USE_HTTP_VIOLATIONS
 
     if (override_expire)
         t->flags.override_expire = 1;
@@ -2456,7 +2825,7 @@ free_refreshpattern(refresh_t ** head)
         safe_free(t);
     }
 
-#if HTTP_VIOLATIONS
+#if USE_HTTP_VIOLATIONS
     refresh_nocache_hack = 0;
 
 #endif
@@ -2561,6 +2930,14 @@ dump_b_size_t(StoreEntry * entry, const char *name, size_t var)
     storeAppendPrintf(entry, "%s %d %s\n", name, (int) var, B_BYTES_STR);
 }
 
+#if !USE_DNSSERVERS
+static void
+dump_b_ssize_t(StoreEntry * entry, const char *name, ssize_t var)
+{
+    storeAppendPrintf(entry, "%s %d %s\n", name, (int) var, B_BYTES_STR);
+}
+#endif
+
 #if UNUSED_CODE
 static void
 dump_kb_size_t(StoreEntry * entry, const char *name, size_t var)
@@ -2597,6 +2974,14 @@ parse_b_size_t(size_t * var)
     parseBytesLine(var, B_BYTES_STR);
 }
 
+#if !USE_DNSSERVERS
+static void
+parse_b_ssize_t(ssize_t * var)
+{
+    parseBytesLineSigned(var, B_BYTES_STR);
+}
+#endif
+
 #if UNUSED_CODE
 static void
 parse_kb_size_t(size_t * var)
@@ -2623,6 +3008,14 @@ free_size_t(size_t * var)
     *var = 0;
 }
 
+#if !USE_DNSSERVERS
+static void
+free_ssize_t(ssize_t * var)
+{
+    *var = 0;
+}
+#endif
+
 static void
 free_b_int64_t(int64_t * var)
 {
@@ -2630,6 +3023,7 @@ free_b_int64_t(int64_t * var)
 }
 
 #define free_b_size_t free_size_t
+#define free_b_ssize_t free_ssize_t
 #define free_kb_size_t free_size_t
 #define free_mb_size_t free_size_t
 #define free_gb_size_t free_size_t
@@ -2843,7 +3237,7 @@ dump_memcachemode(StoreEntry * entry, const char *name, SquidConfig &config)
     storeAppendPrintf(entry, "\n");
 }
 
-#include "cf_parser.h"
+#include "cf_parser.cci"
 
 peer_t
 parseNeighborType(const char *s)
@@ -2870,11 +3264,11 @@ parseNeighborType(const char *s)
 
 #if USE_WCCPv2
 static void
-parse_IpAddress_list(IpAddress_list ** head)
+parse_IpAddress_list(Ip::Address_list ** head)
 {
     char *token;
-    IpAddress_list *s;
-    IpAddress ipa;
+    Ip::Address_list *s;
+    Ip::Address ipa;
 
     while ((token = strtok(NULL, w_space))) {
         if (GetHostWithPort(token, &ipa)) {
@@ -2882,7 +3276,7 @@ parse_IpAddress_list(IpAddress_list ** head)
             while (*head)
                 head = &(*head)->next;
 
-            s = static_cast<IpAddress_list *>(xcalloc(1, sizeof(*s)));
+            s = static_cast<Ip::Address_list *>(xcalloc(1, sizeof(*s)));
             s->s = ipa;
 
             *head = s;
@@ -2892,7 +3286,7 @@ parse_IpAddress_list(IpAddress_list ** head)
 }
 
 static void
-dump_IpAddress_list(StoreEntry * e, const char *n, const IpAddress_list * s)
+dump_IpAddress_list(StoreEntry * e, const char *n, const Ip::Address_list * s)
 {
     char ntoabuf[MAX_IPSTRLEN];
 
@@ -2905,7 +3299,7 @@ dump_IpAddress_list(StoreEntry * e, const char *n, const IpAddress_list * s)
 }
 
 static void
-free_IpAddress_list(IpAddress_list ** head)
+free_IpAddress_list(Ip::Address_list ** head)
 {
     if (*head) delete *head;
     *head = NULL;
@@ -2916,7 +3310,7 @@ free_IpAddress_list(IpAddress_list ** head)
  * be used by icp_port and htcp_port
  */
 static int
-check_null_IpAddress_list(const IpAddress_list * s)
+check_null_IpAddress_list(const Ip::Address_list * s)
 {
     return NULL == s;
 }
@@ -2938,7 +3332,6 @@ parse_http_port_specification(http_port_list * s, char *token)
     s->name = xstrdup(token);
     s->connection_auth_disabled = false;
 
-#if USE_IPV6
     if (*token == '[') {
         /* [ipv6]:port */
         host = token + 1;
@@ -2952,23 +3345,25 @@ parse_http_port_specification(http_port_list * s, char *token)
             debugs(3, 0, "http(s)_port: missing Port in: " << token);
             self_destruct();
         }
-        port = xatos(t + 1);
-    } else
-#endif
-        if ((t = strchr(token, ':'))) {
-            /* host:port */
-            /* ipv4:port */
-            host = token;
-            *t = '\0';
-            port = xatos(t + 1);
-
-        } else if ((port = strtol(token, &junk, 10)), !*junk) {
-            /* port */
-            debugs(3, 3, "http(s)_port: found Listen on Port: " << port);
-        } else {
-            debugs(3, 0, "http(s)_port: missing Port: " << token);
+        if (!Ip::EnableIpv6) {
+            debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: IPv6 is not available.");
             self_destruct();
         }
+        port = xatos(t + 1);
+    } else if ((t = strchr(token, ':'))) {
+        /* host:port */
+        /* ipv4:port */
+        host = token;
+        *t = '\0';
+        port = xatos(t + 1);
+
+    } else if ((port = strtol(token, &junk, 10)), !*junk) {
+        /* port */
+        debugs(3, 3, "http(s)_port: found Listen on Port: " << port);
+    } else {
+        debugs(3, 0, "http(s)_port: missing Port: " << token);
+        self_destruct();
+    }
 
     if (port == 0) {
         debugs(3, 0, "http(s)_port: Port cannot be 0: " << token);
@@ -2978,14 +3373,20 @@ parse_http_port_specification(http_port_list * s, char *token)
     if (NULL == host) {
         s->s.SetAnyAddr();
         s->s.SetPort(port);
+        if (!Ip::EnableIpv6)
+            s->s.SetIPv4();
         debugs(3, 3, "http(s)_port: found Listen on wildcard address: *:" << s->s.GetPort() );
     } else if ( s->s = host ) { /* check/parse numeric IPA */
         s->s.SetPort(port);
+        if (!Ip::EnableIpv6)
+            s->s.SetIPv4();
         debugs(3, 3, "http(s)_port: Listen on Host/IP: " << host << " --> " << s->s);
     } else if ( s->s.GetHostByName(host) ) { /* check/parse for FQDN */
         /* dont use ipcache */
         s->defaultsite = xstrdup(host);
         s->s.SetPort(port);
+        if (!Ip::EnableIpv6)
+            s->s.SetIPv4();
         debugs(3, 3, "http(s)_port: found Listen as Host " << s->defaultsite << " on IP: " << s->s);
     } else {
         debugs(3, 0, "http(s)_port: failed to resolve Host/IP: " << host);
@@ -2996,37 +3397,96 @@ parse_http_port_specification(http_port_list * s, char *token)
 static void
 parse_http_port_option(http_port_list * s, char *token)
 {
-    if (strncmp(token, "defaultsite=", 12) == 0) {
+    /* modes first */
+
+    if (strcmp(token, "accel") == 0) {
+        if (s->intercepted || s->spoof_client_ip) {
+            debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: Accelerator mode requires its own port. It cannot be shared with other modes.");
+            self_destruct();
+        }
+        s->accel = 1;
+    } else if (strcmp(token, "transparent") == 0 || strcmp(token, "intercept") == 0) {
+        if (s->accel || s->spoof_client_ip) {
+            debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: Intercept mode requires its own interception port. It cannot be shared with other modes.");
+            self_destruct();
+        }
+        s->intercepted = 1;
+        Ip::Interceptor.StartInterception();
+        /* Log information regarding the port modes under interception. */
+        debugs(3, DBG_IMPORTANT, "Starting Authentication on port " << s->s);
+        debugs(3, DBG_IMPORTANT, "Disabling Authentication on port " << s->s << " (interception enabled)");
+
+        /* INET6: until transparent REDIRECT works on IPv6 SOCKET, force wildcard to IPv4 */
+        if (Ip::EnableIpv6)
+            debugs(3, DBG_IMPORTANT, "Disabling IPv6 on port " << s->s << " (interception enabled)");
+        if ( !s->s.SetIPv4() ) {
+            debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: IPv6 addresses cannot be transparent (protocol does not provide NAT)" << s->s );
+            self_destruct();
+        }
+    } else if (strcmp(token, "tproxy") == 0) {
+        if (s->intercepted || s->accel) {
+            debugs(3,DBG_CRITICAL, "FATAL: http(s)_port: TPROXY option requires its own interception port. It cannot be shared with other modes.");
+            self_destruct();
+        }
+        s->spoof_client_ip = 1;
+        Ip::Interceptor.StartTransparency();
+        /* Log information regarding the port modes under transparency. */
+        debugs(3, DBG_IMPORTANT, "Starting IP Spoofing on port " << s->s);
+        debugs(3, DBG_IMPORTANT, "Disabling Authentication on port " << s->s << " (IP spoofing enabled)");
+
+        if (!Ip::Interceptor.ProbeForTproxy(s->s)) {
+            debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: TPROXY support in the system does not work.");
+            self_destruct();
+        }
+
+    } else if (strncmp(token, "defaultsite=", 12) == 0) {
+        if (!s->accel) {
+            debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: defaultsite option requires Acceleration mode flag.");
+            self_destruct();
+        }
         safe_free(s->defaultsite);
         s->defaultsite = xstrdup(token + 12);
-        s->accel = 1;
-    } else if (strncmp(token, "name=", 5) == 0) {
-        safe_free(s->name);
-        s->name = xstrdup(token + 5);
     } else if (strcmp(token, "vhost") == 0) {
+        if (!s->accel) {
+            debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: vhost option requires Acceleration mode flag.");
+            self_destruct();
+        }
         s->vhost = 1;
-        s->accel = 1;
     } else if (strcmp(token, "vport") == 0) {
+        if (!s->accel) {
+            debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: vport option requires Acceleration mode flag.");
+            self_destruct();
+        }
         s->vport = -1;
-        s->accel = 1;
     } else if (strncmp(token, "vport=", 6) == 0) {
+        if (!s->accel) {
+            debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: vport option requires Acceleration mode flag.");
+            self_destruct();
+        }
         s->vport = xatos(token + 6);
-        s->accel = 1;
     } else if (strncmp(token, "protocol=", 9) == 0) {
+        if (!s->accel) {
+            debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: protocol option requires Acceleration mode flag.");
+            self_destruct();
+        }
         s->protocol = xstrdup(token + 9);
-        s->accel = 1;
-    } else if (strcmp(token, "accel") == 0) {
-        s->accel = 1;
     } else if (strcmp(token, "allow-direct") == 0) {
+        if (!s->accel) {
+            debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: vport option requires Acceleration mode flag.");
+            self_destruct();
+        }
         s->allow_direct = 1;
     } else if (strcmp(token, "ignore-cc") == 0) {
-        s->ignore_cc = 1;
-#if !HTTP_VIOLATIONS
+#if !USE_HTTP_VIOLATIONS
         if (!s->accel) {
-            debugs(3, DBG_CRITICAL, "FATAL: ignore-cc is only valid in accelerator mode");
+            debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: ignore-cc option requires Scceleration mode flag.");
             self_destruct();
         }
 #endif
+        s->ignore_cc = 1;
+    } else if (strncmp(token, "name=", 5) == 0) {
+        safe_free(s->name);
+        s->name = xstrdup(token + 5);
     } else if (strcmp(token, "no-connection-auth") == 0) {
         s->connection_auth_disabled = true;
     } else if (strcmp(token, "connection-auth=off") == 0) {
@@ -3044,45 +3504,11 @@ parse_http_port_option(http_port_list * s, char *token)
             s->disable_pmtu_discovery = DISABLE_PMTU_ALWAYS;
         else
             self_destruct();
-
-    } else if (strcmp(token, "transparent") == 0 || strcmp(token, "intercept") == 0) {
-        s->intercepted = 1;
-        IpInterceptor.StartInterception();
-        /* Log information regarding the port modes under interception. */
-        debugs(3, DBG_IMPORTANT, "Starting Authentication on port " << s->s);
-        debugs(3, DBG_IMPORTANT, "Disabling Authentication on port " << s->s << " (interception enabled)");
-
-#if USE_IPV6
-        /* INET6: until transparent REDIRECT works on IPv6 SOCKET, force wildcard to IPv4 */
-        debugs(3, DBG_IMPORTANT, "Disabling IPv6 on port " << s->s << " (interception enabled)");
-        if ( !s->s.SetIPv4() ) {
-            debugs(3, DBG_CRITICAL, "http(s)_port: IPv6 addresses cannot be transparent (protocol does not provide NAT)" << s->s );
-            self_destruct();
-        }
-#endif
-    } else if (strcmp(token, "tproxy") == 0) {
-        if (s->intercepted || s->accel) {
-            debugs(3,DBG_CRITICAL, "http(s)_port: TPROXY option requires its own interception port. It cannot be shared.");
-            self_destruct();
-        }
-        s->spoof_client_ip = 1;
-        IpInterceptor.StartTransparency();
-        /* Log information regarding the port modes under transparency. */
-        debugs(3, DBG_IMPORTANT, "Starting IP Spoofing on port " << s->s);
-        debugs(3, DBG_IMPORTANT, "Disabling Authentication on port " << s->s << " (IP spoofing enabled)");
-
-        if (!IpInterceptor.ProbeForTproxy(s->s)) {
-            debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: TPROXY support in the system does not work.");
-            self_destruct();
-        }
-
     } else if (strcmp(token, "ipv4") == 0) {
-#if USE_IPV6
         if ( !s->s.SetIPv4() ) {
-            debugs(3, 0, "http(s)_port: IPv6 addresses cannot be used a IPv4-Only." << s->s );
+            debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: IPv6 addresses cannot be used as IPv4-Only. " << s->s );
             self_destruct();
         }
-#endif
     } else if (strcmp(token, "tcpkeepalive") == 0) {
         s->tcp_keepalive.enabled = 1;
     } else if (strncmp(token, "tcpkeepalive=", 13) == 0) {
@@ -3101,6 +3527,12 @@ parse_http_port_option(http_port_list * s, char *token)
             t = strchr(t, ',');
         }
 #if USE_SSL
+    } else if (strcasecmp(token, "sslBump") == 0) {
+        debugs(3, DBG_CRITICAL, "WARNING: '" << token << "' is deprecated " <<
+               "in http_port. Use 'ssl-bump' instead.");
+        s->sslBump = 1; // accelerated when bumped, otherwise not
+    } else if (strcmp(token, "ssl-bump") == 0) {
+        s->sslBump = 1; // accelerated when bumped, otherwise not
     } else if (strncmp(token, "cert=", 5) == 0) {
         safe_free(s->cert);
         s->cert = xstrdup(token + 5);
@@ -3109,7 +3541,6 @@ parse_http_port_option(http_port_list * s, char *token)
         s->key = xstrdup(token + 4);
     } else if (strncmp(token, "version=", 8) == 0) {
         s->version = xatoi(token + 8);
-
         if (s->version < 1 || s->version > 4)
             self_destruct();
     } else if (strncmp(token, "options=", 8) == 0) {
@@ -3139,17 +3570,10 @@ parse_http_port_option(http_port_list * s, char *token)
     } else if (strncmp(token, "sslcontext=", 11) == 0) {
         safe_free(s->sslcontext);
         s->sslcontext = xstrdup(token + 11);
-    } else if (strcmp(token, "sslBump") == 0) {
-        s->sslBump = 1; // accelerated when bumped, otherwise not
 #endif
     } else {
         self_destruct();
     }
-
-    if ( s->spoof_client_ip && (s->intercepted || s->accel) ) {
-        debugs(3,DBG_CRITICAL, "http(s)_port: TPROXY option requires its own interception port. It cannot be shared.");
-        self_destruct();
-    }
 }
 
 static http_port_list *
@@ -3170,7 +3594,6 @@ add_http_port(char *portspec)
     Config.Sockaddr.http = s;
 }
 
-#if IPV6_SPECIAL_SPLITSTACK
 http_port_list *
 clone_http_port_list(http_port_list *a)
 {
@@ -3220,7 +3643,6 @@ clone_http_port_list(http_port_list *a)
 
     return b;
 }
-#endif
 
 static void
 parse_http_port_list(http_port_list ** head)
@@ -3239,14 +3661,12 @@ parse_http_port_list(http_port_list ** head)
         parse_http_port_option(s, token);
     }
 
-#if IPV6_SPECIAL_SPLITSTACK
-    if (s->s.IsAnyAddr()) {
+    if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && s->s.IsAnyAddr()) {
         // clone the port options from *s to *(s->next)
         s->next = clone_http_port_list(s);
         s->next->s.SetIPv4();
         debugs(3, 3, "http(s)_port: clone wildcard address for split-stack: " << s->s << " and " << s->next->s);
     }
-#endif
 
     while (*head)
         head = &(*head)->next;
@@ -3263,18 +3683,24 @@ dump_generic_http_port(StoreEntry * e, const char *n, const http_port_list * s)
                       n,
                       s->s.ToURL(buf,MAX_IPSTRLEN));
 
-    if (s->defaultsite)
-        storeAppendPrintf(e, " defaultsite=%s", s->defaultsite);
-
     if (s->intercepted)
         storeAppendPrintf(e, " intercept");
 
+    if (s->spoof_client_ip)
+        storeAppendPrintf(e, " tproxy");
+
+    if (s->accel)
+        storeAppendPrintf(e, " accel");
+
     if (s->vhost)
         storeAppendPrintf(e, " vhost");
 
     if (s->vport)
         storeAppendPrintf(e, " vport");
 
+    if (s->defaultsite)
+        storeAppendPrintf(e, " defaultsite=%s", s->defaultsite);
+
     if (s->connection_auth_disabled)
         storeAppendPrintf(e, " connection-auth=off");
     else
@@ -3300,6 +3726,9 @@ dump_generic_http_port(StoreEntry * e, const char *n, const http_port_list * s)
     }
 
 #if USE_SSL
+    if (s->sslBump)
+        storeAppendPrintf(e, " sslBump");
+
     if (s->cert)
         storeAppendPrintf(e, " cert=%s", s->cert);
 
@@ -3332,9 +3761,6 @@ dump_generic_http_port(StoreEntry * e, const char *n, const http_port_list * s)
 
     if (s->sslcontext)
         storeAppendPrintf(e, " sslcontext=%s", s->sslcontext);
-
-    if (s->sslBump)
-        storeAppendPrintf(e, " sslBump");
 #endif
 }
 
@@ -3636,6 +4062,83 @@ free_access_log(customlog ** definitions)
     }
 }
 
+/// parses list of integers form name=N1,N2,N3,...
+static bool
+parseNamedIntList(const char *data, const String &name, Vector<int> &list)
+{
+    if (data && (strncmp(data, name.rawBuf(), name.size()) == 0)) {
+        data += name.size();
+        if (*data == '=') {
+            while (true) {
+                ++data;
+                int value = 0;
+                if (!StringToInt(data, value, &data, 10))
+                    break;
+                list.push_back(value);
+                if (*data == '\0' || *data != ',')
+                    break;
+            }
+        }
+    }
+    return data && *data == '\0';
+}
+
+static void
+parse_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap)
+{
+#if !HAVE_CPU_AFFINITY
+    debugs(3, DBG_CRITICAL, "FATAL: Squid built with no CPU affinity " <<
+           "support, do not set 'cpu_affinity_map'");
+    self_destruct();
+#endif /* HAVE_CPU_AFFINITY */
+
+    if (!*cpuAffinityMap)
+        *cpuAffinityMap = new CpuAffinityMap;
+
+    const char *const pToken = strtok(NULL, w_space);
+    const char *const cToken = strtok(NULL, w_space);
+    Vector<int> processes, cores;
+    if (!parseNamedIntList(pToken, "process_numbers", processes)) {
+        debugs(3, DBG_CRITICAL, "FATAL: bad 'process_numbers' parameter " <<
+               "in 'cpu_affinity_map'");
+        self_destruct();
+    } else if (!parseNamedIntList(cToken, "cores", cores)) {
+        debugs(3, DBG_CRITICAL, "FATAL: bad 'cores' parameter in " <<
+               "'cpu_affinity_map'");
+        self_destruct();
+    } else if (!(*cpuAffinityMap)->add(processes, cores)) {
+        debugs(3, DBG_CRITICAL, "FATAL: bad 'cpu_affinity_map'; " <<
+               "process_numbers and cores lists differ in length or " <<
+               "contain numbers <= 0");
+        self_destruct();
+    }
+}
+
+static void
+dump_CpuAffinityMap(StoreEntry *const entry, const char *const name, const CpuAffinityMap *const cpuAffinityMap)
+{
+    if (cpuAffinityMap) {
+        storeAppendPrintf(entry, "%s process_numbers=", name);
+        for (size_t i = 0; i < cpuAffinityMap->processes().size(); ++i) {
+            storeAppendPrintf(entry, "%s%i", (i ? "," : ""),
+                              cpuAffinityMap->processes()[i]);
+        }
+        storeAppendPrintf(entry, " cores=");
+        for (size_t i = 0; i < cpuAffinityMap->processes().size(); ++i) {
+            storeAppendPrintf(entry, "%s%i", (i ? "," : ""),
+                              cpuAffinityMap->cores()[i]);
+        }
+        storeAppendPrintf(entry, "\n");
+    }
+}
+
+static void
+free_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap)
+{
+    delete *cpuAffinityMap;
+    *cpuAffinityMap = NULL;
+}
+
 #if USE_ADAPTATION
 
 static void
@@ -3719,3 +4222,55 @@ dump_ecap_service_type(StoreEntry * entry, const char *name, const Adaptation::E
 }
 
 #endif /* USE_ECAP */
+
+#if ICAP_CLIENT
+static void parse_icap_service_failure_limit(Adaptation::Icap::Config *cfg)
+{
+    char *token;
+    time_t d;
+    time_t m;
+    cfg->service_failure_limit = GetInteger();
+
+    if ((token = strtok(NULL, w_space)) == NULL)
+        return;
+
+    if (strcmp(token,"in") != 0) {
+        debugs(3, 0, "expecting 'in' on'"  << config_input_line << "'");
+        self_destruct();
+    }
+
+    if ((token = strtok(NULL, w_space)) == NULL) {
+        self_destruct();
+    }
+
+    d = static_cast<time_t> (xatoi(token));
+
+    m = static_cast<time_t> (1);
+
+    if (0 == d)
+        (void) 0;
+    else if ((token = strtok(NULL, w_space)) == NULL) {
+        debugs(3, 0, "No time-units on '" << config_input_line << "'");
+        self_destruct();
+    } else if ((m = parseTimeUnits(token)) == 0)
+        self_destruct();
+
+    cfg->oldest_service_failure = (m * d);
+}
+
+static void dump_icap_service_failure_limit(StoreEntry *entry, const char *name, const Adaptation::Icap::Config &cfg)
+{
+    storeAppendPrintf(entry, "%s %d", name, cfg.service_failure_limit);
+    if (cfg.oldest_service_failure > 0) {
+        storeAppendPrintf(entry, " in %d seconds", (int)cfg.oldest_service_failure);
+    }
+    storeAppendPrintf(entry, "\n");
+}
+
+static void free_icap_service_failure_limit(Adaptation::Icap::Config *cfg)
+{
+    cfg->oldest_service_failure = 0;
+    cfg->service_failure_limit = 0;
+}
+
+#endif