/*
- * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
#include "acl/Tree.h"
#include "anyp/PortCfg.h"
#include "anyp/UriScheme.h"
+#include "auth/Config.h"
+#include "auth/Scheme.h"
#include "AuthReg.h"
#include "base/RunnersRegistry.h"
#include "cache_cf.h"
#include "icmp/IcmpConfig.h"
#include "ident/Config.h"
#include "ip/Intercept.h"
+#include "ip/NfMarkConfig.h"
#include "ip/QosConfig.h"
#include "ip/tools.h"
#include "ipc/Kids.h"
#include "log/Config.h"
#include "log/CustomLog.h"
#include "MemBuf.h"
+#include "MessageDelayPools.h"
#include "mgr/ActionPasswordList.h"
#include "mgr/Registration.h"
#include "neighbors.h"
#include "RefreshPattern.h"
#include "rfc1738.h"
#include "sbuf/List.h"
+#include "sbuf/Stream.h"
#include "SquidConfig.h"
#include "SquidString.h"
#include "ssl/ProxyCerts.h"
#include "ssl/Config.h"
#include "ssl/support.h"
#endif
-#if USE_AUTH
-#include "auth/Config.h"
-#include "auth/Scheme.h"
-#endif
#if USE_SQUID_ESI
#include "esi/Parser.h"
#endif
#if HAVE_GLOB_H
#include <glob.h>
#endif
+#include <chrono>
#include <limits>
#include <list>
#if HAVE_PWD_H
#if HAVE_GRP_H
#include <grp.h>
#endif
+#if HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
#if HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
static peer_t parseNeighborType(const char *s);
+static const char *const T_NANOSECOND_STR = "nanosecond";
+static const char *const T_MICROSECOND_STR = "microsecond";
static const char *const T_MILLISECOND_STR = "millisecond";
static const char *const T_SECOND_STR = "second";
static const char *const T_MINUTE_STR = "minute";
static const char *const list_sep = ", \t\n\r";
+// std::chrono::years requires C++20. Do our own rough calculation for now.
+static const double HoursPerYear = 24*365.2522;
+
static void parse_access_log(CustomLog ** customlog_definitions);
static int check_null_access_log(CustomLog *customlog_definitions);
static void dump_access_log(StoreEntry * entry, const char *name, CustomLog * definitions);
static void configDoConfigure(void);
static void parse_refreshpattern(RefreshPattern **);
-static uint64_t parseTimeUnits(const char *unit, bool allowMsec);
-static void parseTimeLine(time_msec_t * tptr, const char *units, bool allowMsec, bool expectMoreArguments);
static void parse_u_short(unsigned short * var);
static void parse_string(char **);
static void default_all(void);
static int parse_line(char *);
static void parse_obsolete(const char *);
static void parseBytesLine(size_t * bptr, const char *units);
-#if USE_OPENSSL
-static void parseBytesOptionValue(size_t * bptr, const char *units, char const * value);
-#endif
static void parseBytesLineSigned(ssize_t * bptr, const char *units);
static size_t parseBytesUnits(const char *unit);
static void free_all(void);
static void parse_on_unsupported_protocol(acl_access **access);
static void dump_on_unsupported_protocol(StoreEntry *entry, const char *name, acl_access *access);
static void free_on_unsupported_protocol(acl_access **access);
+static void ParseAclWithAction(acl_access **access, const Acl::Answer &action, const char *desc, ACL *acl = nullptr);
/*
* LegacyParser is a parser for legacy code that uses the global
ProcessMacros(char*& line, int& len)
{
SubstituteMacro(line, len, "${service_name}", service_name.c_str());
- SubstituteMacro(line, len, "${process_name}", TheKidName);
+ SubstituteMacro(line, len, "${process_name}", TheKidName.c_str());
SubstituteMacro(line, len, "${process_number}", xitoa(KidIdentifier));
}
/* 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, DBG_CRITICAL, HERE << cfg_filename << ":" << config_lineno << " unrecognized: '" << tmp_line << "'");
- ++err_count;
+ } else {
+ try {
+ if (!parse_line(tmp_line)) {
+ debugs(3, DBG_CRITICAL, ConfigParser::CurrentLocation() << ": unrecognized: '" << tmp_line << "'");
+ ++err_count;
+ }
+ } catch (...) {
+ // fatal for now
+ debugs(3, DBG_CRITICAL, "configuration error: " << CurrentException);
+ self_destruct();
+ }
}
}
return err_count;
}
+static
int
-parseConfigFile(const char *file_name)
+parseConfigFileOrThrow(const char *file_name)
{
int err_count = 0;
*/
configDoConfigure();
- if (!Config.chroot_dir) {
- leave_suid();
- setUmask(Config.umask);
- _db_init(Debug::cache_log, Debug::debugOptions);
- enter_suid();
- }
-
if (opt_send_signal == -1) {
Mgr::RegisterAction("config",
"Current Squid Configuration",
return err_count;
}
+// TODO: Refactor main.cc to centrally handle (and report) all exceptions.
+int
+parseConfigFile(const char *file_name)
+{
+ try {
+ return parseConfigFileOrThrow(file_name);
+ }
+ catch (const std::exception &ex) {
+ debugs(3, DBG_CRITICAL, "FATAL: bad configuration: " << ex.what());
+ self_destruct();
+ return 1; // not reached
+ }
+}
+
static void
configDoConfigure(void)
{
requirePathnameExists("unlinkd_program", Config.Program.unlinkd);
#endif
- requirePathnameExists("logfile_daemon", Log::TheConfig.logfile_daemon);
+ bool logDaemonUsed = false;
+ for (const auto *log = Config.Log.accesslogs; !logDaemonUsed && log; log = log->next)
+ logDaemonUsed = log->usesDaemon();
+#if ICAP_CLIENT
+ for (const auto *log = Config.Log.icaplogs; !logDaemonUsed && log; log = log->next)
+ logDaemonUsed = log->usesDaemon();
+#endif
+ if (logDaemonUsed)
+ requirePathnameExists("logfile_daemon", Log::TheConfig.logfile_daemon);
+
if (Config.Program.redirect)
requirePathnameExists("redirect_program", Config.Program.redirect->key);
}
}
-#if USE_OPENSSL
for (AnyP::PortCfgPointer s = HttpPortList; s != NULL; s = s->next) {
if (!s->secure.encryptTransport)
continue;
- debugs(3, DBG_IMPORTANT, "Initializing " << AnyP::UriScheme(s->transport.protocol) << "_port " << s->s << " TLS context");
- s->configureSslServerContext();
+ debugs(3, DBG_IMPORTANT, "Initializing " << AnyP::UriScheme(s->transport.protocol) << "_port " << s->s << " TLS contexts");
+ s->secure.initServerContexts(*s);
}
-#endif
// prevent infinite fetch loops in the request parser
// due to buffer full but not enough data recived to finish parse
* state will be preserved.
*/
if (Config.pipeline_max_prefetch > 0) {
- Auth::Config *nego = Auth::Config::Find("Negotiate");
- Auth::Config *ntlm = Auth::Config::Find("NTLM");
+ Auth::SchemeConfig *nego = Auth::SchemeConfig::Find("Negotiate");
+ Auth::SchemeConfig *ntlm = Auth::SchemeConfig::Find("NTLM");
if ((nego && nego->active()) || (ntlm && ntlm->active())) {
debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: pipeline_prefetch breaks NTLM and Negotiate authentication. Forced pipeline_prefetch 0.");
Config.pipeline_max_prefetch = 0;
}
}
+
+ for (auto &authSchemes : Auth::TheConfig.schemeLists) {
+ authSchemes.expand();
+ if (authSchemes.authConfigs.empty()) {
+ debugs(3, DBG_CRITICAL, "auth_schemes: at least one scheme name is required; got: " << authSchemes.rawSchemes);
+ self_destruct();
+ }
+ }
#endif
}
}
}
-/* Parse a time specification from the config file. Store the
- * result in 'tptr', after converting it to 'units' */
-static void
-parseTimeLine(time_msec_t * tptr, const char *units, bool allowMsec, bool expectMoreArguments = false)
-{
- time_msec_t u = parseTimeUnits(units, allowMsec);
- if (u == 0) {
- self_destruct();
- return;
- }
-
- char *token = ConfigParser::NextToken();;
- if (!token) {
- self_destruct();
- return;
+template <class MinimalUnit>
+static const char *
+TimeUnitToString()
+{
+ const auto minUnit = MinimalUnit(1);
+ if(minUnit == std::chrono::nanoseconds(1))
+ return T_NANOSECOND_STR;
+ else if (minUnit == std::chrono::microseconds(1))
+ return T_MICROSECOND_STR;
+ else if (minUnit == std::chrono::milliseconds(1))
+ return T_MILLISECOND_STR;
+ else {
+ assert(minUnit >= std::chrono::seconds(1));
+ return T_SECOND_STR;
}
+}
- double d = xatof(token);
+/// Assigns 'ns' the number of nanoseconds corresponding to 'unitName'.
+/// \param MinimalUnit is a chrono duration type specifying the minimal
+/// allowed time unit.
+/// \returns true if unitName is correct and its time unit is not less
+/// than MinimalUnit.
+template <class MinimalUnit>
+static bool
+parseTimeUnit(const char *unitName, std::chrono::nanoseconds &ns)
+{
+ if (!unitName)
+ throw TexcHere("missing time unit");
+
+ if (!strncasecmp(unitName, T_NANOSECOND_STR, strlen(T_NANOSECOND_STR)))
+ ns = std::chrono::nanoseconds(1);
+ else if (!strncasecmp(unitName, T_MICROSECOND_STR, strlen(T_MICROSECOND_STR)))
+ ns = std::chrono::microseconds(1);
+ else if (!strncasecmp(unitName, T_MILLISECOND_STR, strlen(T_MILLISECOND_STR)))
+ ns = std::chrono::milliseconds(1);
+ else if (!strncasecmp(unitName, T_SECOND_STR, strlen(T_SECOND_STR)))
+ ns = std::chrono::seconds(1);
+ else if (!strncasecmp(unitName, T_MINUTE_STR, strlen(T_MINUTE_STR)))
+ ns = std::chrono::minutes(1);
+ else if (!strncasecmp(unitName, T_HOUR_STR, strlen(T_HOUR_STR)))
+ ns = std::chrono::hours(1);
+ else if (!strncasecmp(unitName, T_DAY_STR, strlen(T_DAY_STR)))
+ ns = std::chrono::hours(24);
+ else if (!strncasecmp(unitName, T_WEEK_STR, strlen(T_WEEK_STR)))
+ ns = std::chrono::hours(24 * 7);
+ else if (!strncasecmp(unitName, T_FORTNIGHT_STR, strlen(T_FORTNIGHT_STR)))
+ ns = std::chrono::hours(24 * 14);
+ else if (!strncasecmp(unitName, T_MONTH_STR, strlen(T_MONTH_STR)))
+ ns = std::chrono::hours(24 * 30);
+ else if (!strncasecmp(unitName, T_YEAR_STR, strlen(T_YEAR_STR)))
+ ns = std::chrono::hours(static_cast<std::chrono::hours::rep>(HoursPerYear));
+ else if (!strncasecmp(unitName, T_DECADE_STR, strlen(T_DECADE_STR)))
+ ns = std::chrono::hours(static_cast<std::chrono::hours::rep>(HoursPerYear * 10));
+ else
+ return false;
- time_msec_t m = u; /* default to 'units' if none specified */
+ if (ns < MinimalUnit(1)) {
+ throw TexcHere(ToSBuf("time unit '", unitName, "' is too small to be used in this context, the minimal unit is ",
+ TimeUnitToString<MinimalUnit>()));
+ }
- if (d) {
- if ((token = ConfigParser::PeekAtToken()) && (m = parseTimeUnits(token, allowMsec))) {
- (void)ConfigParser::NextToken();
+ return true;
+}
- } else if (!expectMoreArguments) {
- self_destruct();
- return;
+static std::chrono::nanoseconds
+CheckTimeValue(const double value, const std::chrono::nanoseconds &unit)
+{
+ if (value < 0)
+ throw TexcHere("time must have a positive value");
- } else {
- token = NULL; // show default units if dying below
- debugs(3, DBG_CRITICAL, "WARNING: No units on '" << config_input_line << "', assuming " << d << " " << units);
- }
- } else
- token = NULL; // show default units if dying below.
+ const auto maxNanoseconds = std::chrono::nanoseconds::max().count();
+ if (value > maxNanoseconds/static_cast<double>(unit.count())) {
+ const auto maxYears = maxNanoseconds/(HoursPerYear*3600*1000000000);
+ throw TexcHere(ToSBuf("time values cannot exceed ", maxYears, " years"));
+ }
- *tptr = static_cast<time_msec_t>(m * d);
+ return std::chrono::nanoseconds(static_cast<std::chrono::nanoseconds::rep>(unit.count() * value));
+}
- if (static_cast<double>(*tptr) * 2 != m * d * 2) {
- debugs(3, DBG_CRITICAL, "FATAL: Invalid value '" <<
- d << " " << (token ? token : units) << ": integer overflow (time_msec_t).");
- self_destruct();
+template <class TimeUnit>
+static TimeUnit
+FromNanoseconds(const std::chrono::nanoseconds &ns, const double parsedValue)
+{
+ const auto result = std::chrono::duration_cast<TimeUnit>(ns);
+ if (!result.count()) {
+ throw TexcHere(ToSBuf("time value '", parsedValue,
+ "' is too small to be used in this context, the minimal value is 1 ",
+ TimeUnitToString<TimeUnit>()));
}
+ return result;
}
-static uint64_t
-parseTimeUnits(const char *unit, bool allowMsec)
+/// Parses a time specification from the config file and
+/// returns the time as a chrono duration object of 'TimeUnit' type.
+template <class TimeUnit>
+static TimeUnit
+parseTimeLine()
{
- if (allowMsec && !strncasecmp(unit, T_MILLISECOND_STR, strlen(T_MILLISECOND_STR)))
- return 1;
-
- if (!strncasecmp(unit, T_SECOND_STR, strlen(T_SECOND_STR)))
- return 1000;
-
- if (!strncasecmp(unit, T_MINUTE_STR, strlen(T_MINUTE_STR)))
- return 60 * 1000;
+ const auto valueToken = ConfigParser::NextToken();
+ if (!valueToken)
+ throw TexcHere("cannot read a time value");
- if (!strncasecmp(unit, T_HOUR_STR, strlen(T_HOUR_STR)))
- return 3600 * 1000;
+ const auto parsedValue = xatof(valueToken);
- if (!strncasecmp(unit, T_DAY_STR, strlen(T_DAY_STR)))
- return 86400 * 1000;
+ if (parsedValue == 0)
+ return TimeUnit::zero();
- if (!strncasecmp(unit, T_WEEK_STR, strlen(T_WEEK_STR)))
- return 86400 * 7 * 1000;
+ std::chrono::nanoseconds parsedUnitDuration;
- if (!strncasecmp(unit, T_FORTNIGHT_STR, strlen(T_FORTNIGHT_STR)))
- return 86400 * 14 * 1000;
+ const auto token = ConfigParser::PeekAtToken();
- if (!strncasecmp(unit, T_MONTH_STR, strlen(T_MONTH_STR)))
- return static_cast<uint64_t>(86400) * 30 * 1000;
+ if (!parseTimeUnit<TimeUnit>(token, parsedUnitDuration))
+ throw TexcHere(ToSBuf("unknown time unit '", token, "'"));
- if (!strncasecmp(unit, T_YEAR_STR, strlen(T_YEAR_STR)))
- return static_cast<uint64_t>(86400 * 1000 * 365.2522);
+ (void)ConfigParser::NextToken();
- if (!strncasecmp(unit, T_DECADE_STR, strlen(T_DECADE_STR)))
- return static_cast<uint64_t>(86400 * 1000 * 365.2522 * 10);
+ const auto nanoseconds = CheckTimeValue(parsedValue, parsedUnitDuration);
- debugs(3, DBG_IMPORTANT, "parseTimeUnits: unknown time unit '" << unit << "'");
+ // validate precisions (time-units-small only)
+ if (TimeUnit(1) <= std::chrono::microseconds(1)) {
+ if (0 < nanoseconds.count() && nanoseconds.count() < 3) {
+ debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), ConfigParser::CurrentLocation() << ": WARNING: " <<
+ "Squid time measurement precision is likely to be far worse than " <<
+ "the nanosecond-level precision implied by the configured value: " << parsedValue << ' ' << token);
+ }
+ }
- return 0;
+ return FromNanoseconds<TimeUnit>(nanoseconds, parsedValue);
}
static void
* Similar to the parseBytesLine function but parses the string value instead of
* the current token value.
*/
-static void parseBytesOptionValue(size_t * bptr, const char *units, char const * value)
+void
+parseBytesOptionValue(size_t * bptr, const char *units, char const * value)
{
int u;
if ((u = parseBytesUnits(units)) == 0) {
}
String number;
- number.limitInit(number_begin, number_end - number_begin);
+ number.assign(number_begin, number_end - number_begin);
int d = xatoi(number.termedBuf());
int m;
return 0;
}
+static void
+parse_SBufList(SBufList * list)
+{
+ while (char *token = ConfigParser::NextQuotedToken())
+ list->push_back(SBuf(token));
+}
+
+// just dump a list, no directive name
static void
dump_SBufList(StoreEntry * entry, const SBufList &words)
{
- for (SBufList::const_iterator i = words.begin(); i != words.end(); ++i) {
- entry->append(i->rawContent(), i->length());
+ for (const auto &i : words) {
+ entry->append(i.rawContent(), i.length());
entry->append(" ",1);
}
entry->append("\n",1);
}
+// dump a SBufList type directive with name
+static void
+dump_SBufList(StoreEntry * entry, const char *name, SBufList &list)
+{
+ if (!list.empty()) {
+ entry->append(name, strlen(name));
+ entry->append(" ", 1);
+ dump_SBufList(entry, list);
+ }
+}
+
+static void
+free_SBufList(SBufList *list)
+{
+ if (list)
+ list->clear();
+}
+
static void
dump_acl(StoreEntry * entry, const char *name, ACL * ae)
{
while (ae != NULL) {
debugs(3, 3, "dump_acl: " << name << " " << ae->name);
- storeAppendPrintf(entry, "%s %s %s %s ",
+ storeAppendPrintf(entry, "%s %s %s ",
name,
ae->name,
- ae->typeString(),
- ae->flags.flagsStr());
- dump_SBufList(entry, ae->dump());
+ ae->typeString());
+ SBufList tail;
+ tail.splice(tail.end(), ae->dumpOptions());
+ tail.splice(tail.end(), ae->dump()); // ACL parameters
+ dump_SBufList(entry, tail);
ae = ae->next;
}
}
addr->setNoAddr();
else if ( (*addr = token) ) // try parse numeric/IPA
(void) 0;
- else if (addr->GetHostByName(token)) // dont use ipcache
+ else if (addr->GetHostByName(token)) // do not use ipcache
(void) 0;
else { // not an IP and not a hostname
debugs(3, DBG_CRITICAL, "FATAL: invalid IP address or domain name '" << token << "'");
dump_acl_nfmark(StoreEntry * entry, const char *name, acl_nfmark * head)
{
for (acl_nfmark *l = head; l; l = l->next) {
- if (l->nfmark > 0)
- storeAppendPrintf(entry, "%s 0x%02X", name, l->nfmark);
- else
- storeAppendPrintf(entry, "%s none", name);
+ storeAppendPrintf(entry, "%s %s", name, ToSBuf(l->markConfig).c_str());
dump_acl_list(entry, l->aclList);
static void
parse_acl_nfmark(acl_nfmark ** head)
{
- nfmark_t mark;
- char *token = ConfigParser::NextToken();
+ SBuf token(ConfigParser::NextToken());
+ const auto mc = Ip::NfMarkConfig::Parse(token);
- if (!token) {
- self_destruct();
- return;
- }
-
- if (!xstrtoui(token, NULL, &mark, 0, std::numeric_limits<nfmark_t>::max())) {
- self_destruct();
- return;
- }
+ // Packet marking directives should not allow to use masks.
+ const auto pkt_dirs = {"mark_client_packet", "clientside_mark", "tcp_outgoing_mark"};
+ if (mc.hasMask() && std::find(pkt_dirs.begin(), pkt_dirs.end(), cfg_directive) != pkt_dirs.end())
+ throw TexcHere(ToSBuf("'", cfg_directive, "' does not support masked marks"));
acl_nfmark *l = new acl_nfmark;
+ l->markConfig = mc;
- l->nfmark = mark;
-
- aclParseAclList(LegacyParser, &l->aclList, token);
+ aclParseAclList(LegacyParser, &l->aclList, token.c_str());
acl_nfmark **tail = head; /* sane name below */
while (*tail)
static void
free_client_delay_pool_count(ClientDelayConfig * cfg)
{
- cfg->freePoolCount();
+ cfg->freePools();
}
static void
}
/* find a configuration for the scheme in the currently parsed configs... */
- Auth::Config *schemeCfg = Auth::Config::Find(type_str);
+ Auth::SchemeConfig *schemeCfg = Auth::SchemeConfig::Find(type_str);
if (schemeCfg == NULL) {
/* Create a configuration based on the scheme info */
}
config->push_back(theScheme->createConfig());
- schemeCfg = Auth::Config::Find(type_str);
+ schemeCfg = Auth::SchemeConfig::Find(type_str);
if (schemeCfg == NULL) {
debugs(3, DBG_CRITICAL, "Parsing Config File: Corruption configuring authentication scheme '" << type_str << "'.");
self_destruct();
static void
dump_authparam(StoreEntry * entry, const char *name, Auth::ConfigVector cfg)
{
- for (Auth::ConfigVector::iterator i = cfg.begin(); i != cfg.end(); ++i)
- (*i)->dump(entry, name, (*i));
+ for (auto *scheme : cfg)
+ scheme->dump(entry, name, scheme);
+}
+
+static void
+parse_AuthSchemes(acl_access **authSchemes)
+{
+ const char *tok = ConfigParser::NextQuotedToken();
+ if (!tok) {
+ debugs(29, DBG_CRITICAL, "FATAL: auth_schemes missing the parameter");
+ self_destruct();
+ return;
+ }
+ Auth::TheConfig.schemeLists.emplace_back(tok, ConfigParser::LastTokenWasQuoted());
+ const auto action = Acl::Answer(ACCESS_ALLOWED, Auth::TheConfig.schemeLists.size() - 1);
+ ParseAclWithAction(authSchemes, action, "auth_schemes");
+}
+
+static void
+free_AuthSchemes(acl_access **authSchemes)
+{
+ Auth::TheConfig.schemeLists.clear();
+ free_acl_access(authSchemes);
+}
+
+static void
+dump_AuthSchemes(StoreEntry *entry, const char *name, acl_access *authSchemes)
+{
+ if (authSchemes)
+ dump_SBufList(entry, authSchemes->treeDump(name, [](const Acl::Answer &action) {
+ return Auth::TheConfig.schemeLists.at(action.kind).rawSchemes;
+ }));
}
+
#endif /* USE_AUTH */
static void
-ParseAclWithAction(acl_access **access, const allow_t &action, const char *desc, ACL *acl = nullptr)
+ParseAclWithAction(acl_access **access, const Acl::Answer &action, const char *desc, ACL *acl)
{
assert(access);
SBuf name;
CachePeer *p = new CachePeer;
p->host = xstrdup(host_str);
+ Tolower(p->host);
p->name = xstrdup(host_str);
p->type = parseNeighborType(token);
} else if (!strcmp(token, "auth-no-keytab")) {
p->options.auth_no_keytab = 1;
} else if (!strncmp(token, "connect-timeout=", 16)) {
- p->connect_timeout = xatoi(token + 16);
+ p->connect_timeout_raw = xatoi(token + 16);
} else if (!strncmp(token, "connect-fail-limit=", 19)) {
p->connect_fail_limit = xatoi(token + 19);
#if USE_CACHE_DIGESTS
p->connect_fail_limit = 10;
#if USE_CACHE_DIGESTS
-
- if (!p->options.no_digest) {
- /* XXX This looks odd.. who has the original pointer
- * then?
- */
- PeerDigest *pd = peerDigestCreate(p);
- p->digest = cbdataReference(pd);
- }
-
+ if (!p->options.no_digest)
+ peerDigestCreate(p);
#endif
+ if (p->secure.encryptTransport)
+ p->secure.parseOptions();
+
p->index = ++Config.npeers;
while (*head != NULL)
while (var != NULL) {
storeAppendPrintf(entry, "%s %s", name, var->err_page_name);
- for (auto *a = var->acl_list; a != NULL; a = a->next)
- storeAppendPrintf(entry, " %s", a->name);
+ for (const auto &aclName: var->acl_list)
+ storeAppendPrintf(entry, " " SQUIDSBUFPH, SQUIDSBUFPRINT(aclName));
storeAppendPrintf(entry, "\n");
CachePeer *p = peerFindByName(host);
if (!p) {
- debugs(15, DBG_CRITICAL, "" << cfg_filename << ", line " << config_lineno << ": No cache_peer '" << host << "'");
+ debugs(15, DBG_CRITICAL, "ERROR: " << cfg_filename << ", line " << config_lineno << ": No cache_peer '" << host << "'");
return;
}
void
parse_time_t(time_t * var)
{
- time_msec_t tval;
- parseTimeLine(&tval, T_SECOND_STR, false);
- *var = static_cast<time_t>(tval/1000);
+ const auto maxTime = std::numeric_limits<time_t>::max();
+ const auto seconds = parseTimeLine<std::chrono::seconds>();
+ if (maxTime < seconds.count())
+ throw TexcHere(ToSBuf("directive supports time values up to ", maxTime, " but is given ", seconds.count(), " seconds"));
+ *var = static_cast<time_t>(seconds.count());
}
static void
void
parse_time_msec(time_msec_t * var)
{
- parseTimeLine(var, T_SECOND_STR, true);
+ *var = parseTimeLine<std::chrono::milliseconds>().count();
}
static void
*var = 0;
}
+static void
+dump_time_nanoseconds(StoreEntry *entry, const char *name, const std::chrono::nanoseconds &var)
+{
+ storeAppendPrintf(entry, "%s %" PRId64 " nanoseconds\n", name, var.count());
+}
+
+static void
+parse_time_nanoseconds(std::chrono::nanoseconds *var)
+{
+ *var = parseTimeLine<std::chrono::nanoseconds>();
+}
+
+static void
+free_time_nanoseconds(std::chrono::nanoseconds *var)
+{
+ *var = std::chrono::nanoseconds::zero();
+}
+
#if UNUSED_CODE
static void
dump_size_t(StoreEntry * entry, const char *name, size_t var)
s->s.setIPv4();
debugs(3, 3, portType << "_port: Listen on Host/IP: " << host << " --> " << s->s);
} else if ( s->s.GetHostByName(host) ) { /* check/parse for FQDN */
- /* dont use ipcache */
+ /* do not use ipcache */
s->defaultsite = xstrdup(host);
s->s.port(port);
if (!Ip::EnableIpv6)
// HTTP/1.0 not supported because we are version 1.1 which contains a superset of 1.0
// and RFC 2616 requires us to upgrade 1.0 to 1.1
if (value.cmp("HTTP") == 0 || value.cmp("HTTP/1.1") == 0)
- return AnyP::ProtocolVersion(AnyP::PROTO_HTTP, 1,1);
+ return Http::ProtocolVersion(1,1);
if (value.cmp("HTTPS") == 0 || value.cmp("HTTPS/1.1") == 0)
return AnyP::ProtocolVersion(AnyP::PROTO_HTTPS, 1,1);
} else if (strncmp(token, "cipher=", 7) == 0) {
s->secure.parse(token);
} else if (strncmp(token, "clientca=", 9) == 0) {
- safe_free(s->clientca);
- s->clientca = xstrdup(token + 9);
+ s->secure.parse(token);
} else if (strncmp(token, "cafile=", 7) == 0) {
debugs(3, DBG_PARSE_NOTE(1), "UPGRADE WARNING: '" << token << "' is deprecated " <<
"in " << cfg_directive << ". Use 'tls-cafile=' instead.");
// NP: deprecation warnings output by secure.parse() when relevant
s->secure.parse(token+3);
} else if (strncmp(token, "sslcontext=", 11) == 0) {
- safe_free(s->sslContextSessionId);
- s->sslContextSessionId = xstrdup(token + 11);
- } else if (strcmp(token, "generate-host-certificates") == 0) {
- s->generateHostCertificates = true;
- } else if (strcmp(token, "generate-host-certificates=on") == 0) {
- s->generateHostCertificates = true;
- } else if (strcmp(token, "generate-host-certificates=off") == 0) {
- s->generateHostCertificates = false;
- } else if (strncmp(token, "dynamic_cert_mem_cache_size=", 28) == 0) {
- parseBytesOptionValue(&s->dynamicCertMemCacheSize, B_BYTES_STR, token + 28);
+ // NP: deprecation warnings output by secure.parse() when relevant
+ s->secure.parse(token+3);
+ } else if (strncmp(token, "generate-host-certificates", 26) == 0) {
+ s->secure.parse(token);
#endif
+ } else if (strncmp(token, "dynamic_cert_mem_cache_size=", 28) == 0) {
+ s->secure.parse(token);
} else if (strncmp(token, "tls-", 4) == 0) {
s->secure.parse(token+4);
} else if (strcmp(token, "ftp-track-dirs") == 0) {
s->ftp_track_dirs = true;
+ } else if (strcmp(token, "worker-queues") == 0) {
+#if !defined(SO_REUSEADDR)
+#error missing system #include that #defines SO_* constants
+#endif
+#if !defined(SO_REUSEPORT)
+ throw TexcHere(ToSBuf(cfg_directive, ' ', token, " option requires building Squid where SO_REUSEPORT is supported by the TCP stack"));
+#endif
+ s->workerQueues = true;
} else {
debugs(3, DBG_CRITICAL, "FATAL: Unknown " << cfg_directive << " option '" << token << "'.");
self_destruct();
parse_port_option(s, token);
}
-#if USE_OPENSSL
- // if clientca has been defined but not cafile, then use it to verify
- // but if cafile has been defined, only use that to verify
- if (s->clientca && !s->secure.caFiles.size())
- s->secure.caFiles.emplace_back(SBuf(s->clientca));
-#endif
+ s->secure.syncCaFiles();
if (s->transport.protocol == AnyP::PROTO_HTTPS) {
s->secure.encryptTransport = true;
}
}
+ if (s->secure.encryptTransport) {
+ if (s->secure.certs.empty()) {
+ debugs(3, DBG_CRITICAL, "FATAL: " << AnyP::UriScheme(s->transport.protocol) << "_port requires a cert= parameter");
+ self_destruct();
+ return;
+ }
+ s->secure.parseOptions();
+ }
+
+ // *_port line should now be fully valid so we can clone it if necessary
if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && s->s.isAnyAddr()) {
// clone the port options from *s to *(s->next)
s->next = s->clone();
#endif
s->secure.dumpCfg(e, "tls-");
-
-#if USE_OPENSSL
- if (s->sslContextSessionId)
- storeAppendPrintf(e, " sslcontext=%s", s->sslContextSessionId);
-
- if (!s->generateHostCertificates)
- storeAppendPrintf(e, " generate-host-certificates=off");
-
- if (s->dynamicCertMemCacheSize != 4*1024*1024) // 4MB default
- storeAppendPrintf(e, "dynamic_cert_mem_cache_size=%" PRIuSIZE "%s\n", s->dynamicCertMemCacheSize, B_BYTES_STR);
-#endif
}
static void
debugs(3, 9, "possible " << cl->filename << " logformat: " << logdef_name);
if (cl->type != Log::Format::CLF_UNKNOWN) {
- debugs(3, DBG_CRITICAL, "Second logformat name in one access_log: " <<
+ debugs(3, DBG_CRITICAL, "FATAL: Second logformat name in one access_log: " <<
logdef_name << " " << cl->type << " ? " << Log::Format::CLF_NONE);
self_destruct();
return false;
} else if (strcmp(logdef_name, "referrer") == 0) {
cl->type = Log::Format::CLF_REFERER;
} else if (dieWhenMissing) {
- debugs(3, DBG_CRITICAL, "Log format '" << logdef_name << "' is not defined");
+ debugs(3, DBG_CRITICAL, "FATAL: Log format '" << logdef_name << "' is not defined");
self_destruct();
return false;
} else {
break;
case Log::Format::CLF_SQUID:
- storeAppendPrintf(entry, "%s logformat=squid", log->filename);
+ // this is the default, no need to add to the dump
+ //storeAppendPrintf(entry, "%s logformat=squid", log->filename);
break;
case Log::Format::CLF_COMBINED:
static void
parse_icap_class_type()
{
- debugs(93, DBG_CRITICAL, "WARNING: 'icap_class' is depricated. " <<
+ debugs(93, DBG_CRITICAL, "WARNING: 'icap_class' is deprecated. " <<
"Use 'adaptation_service_set' instead");
Adaptation::Config::ParseServiceSet();
}
static void
parse_icap_access_type()
{
- debugs(93, DBG_CRITICAL, "WARNING: 'icap_access' is depricated. " <<
+ debugs(93, DBG_CRITICAL, "WARNING: 'icap_access' is deprecated. " <<
"Use 'adaptation_access' instead");
Adaptation::Config::ParseAccess(LegacyParser);
}
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 = ConfigParser::NextToken()) == NULL)
return;
}
- if ((token = ConfigParser::NextToken()) == NULL) {
- self_destruct();
- return;
- }
-
- d = static_cast<time_t> (xatoi(token));
-
- m = static_cast<time_t> (1);
-
- if (0 == d)
- (void) 0;
- else if ((token = ConfigParser::NextToken()) == NULL) {
- debugs(3, DBG_CRITICAL, "No time-units on '" << config_input_line << "'");
- self_destruct();
- return;
- } else if ((m = parseTimeUnits(token, false)) == 0) {
- self_destruct();
- return;
- }
-
- cfg->oldest_service_failure = (m * d);
+ parse_time_t(&cfg->oldest_service_failure);
}
static void dump_icap_service_failure_limit(StoreEntry *entry, const char *name, const Adaptation::Icap::Config &cfg)
sslBumpCfgRr::lastDeprecatedRule = Ssl::bumpEnd;
}
- allow_t action = allow_t(ACCESS_ALLOWED);
+ auto action = Acl::Answer(ACCESS_ALLOWED);
if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpClientFirst]) == 0) {
action.kind = Ssl::bumpClientFirst;
static void dump_sslproxy_ssl_bump(StoreEntry *entry, const char *name, acl_access *ssl_bump)
{
if (ssl_bump)
- dump_SBufList(entry, ssl_bump->treeDump(name, [](const allow_t &action) {
- return Ssl::BumpModeStr.at(action.kind);
- }));
+ dump_SBufList(entry, ssl_bump->treeDump(name, [](const Acl::Answer &action) {
+ return Ssl::BumpModeStr.at(action.kind);
+ }));
}
static void free_sslproxy_ssl_bump(acl_access **ssl_bump)
static bool FtpEspvDeprecated = false;
static void parse_ftp_epsv(acl_access **ftp_epsv)
{
- allow_t ftpEpsvDeprecatedAction;
+ Acl::Answer ftpEpsvDeprecatedAction;
bool ftpEpsvIsDeprecatedRule = false;
char *t = ConfigParser::PeekAtToken();
if (!strcmp(t, "off")) {
(void)ConfigParser::NextToken();
ftpEpsvIsDeprecatedRule = true;
- ftpEpsvDeprecatedAction = allow_t(ACCESS_DENIED);
+ ftpEpsvDeprecatedAction = Acl::Answer(ACCESS_DENIED);
} else if (!strcmp(t, "on")) {
(void)ConfigParser::NextToken();
ftpEpsvIsDeprecatedRule = true;
- ftpEpsvDeprecatedAction = allow_t(ACCESS_ALLOWED);
+ ftpEpsvDeprecatedAction = Acl::Answer(ACCESS_ALLOWED);
}
// Check for mixing "ftp_epsv on|off" and "ftp_epsv allow|deny .." rules:
delete *ftp_epsv;
*ftp_epsv = nullptr;
- if (ftpEpsvDeprecatedAction == allow_t(ACCESS_DENIED)) {
+ if (ftpEpsvDeprecatedAction == Acl::Answer(ACCESS_DENIED)) {
if (ACL *a = ACL::FindByName("all"))
ParseAclWithAction(ftp_epsv, ftpEpsvDeprecatedAction, "ftp_epsv", a);
else {
FtpEspvDeprecated = false;
}
+/// Like parseTimeLine() but does not require the timeunit to be specified.
+/// If missed, the default 'second' timeunit is assumed.
+static std::chrono::seconds
+ParseUrlRewriteTimeout()
+{
+ const auto timeValueToken = ConfigParser::NextToken();
+ if (!timeValueToken)
+ throw TexcHere("cannot read a time value");
+
+ using Seconds = std::chrono::seconds;
+
+ const auto parsedTimeValue = xatof(timeValueToken);
+
+ if (parsedTimeValue == 0)
+ return std::chrono::seconds::zero();
+
+ std::chrono::nanoseconds parsedUnitDuration;
+
+ const auto unitToken = ConfigParser::PeekAtToken();
+ if (parseTimeUnit<Seconds>(unitToken, parsedUnitDuration))
+ (void)ConfigParser::NextToken();
+ else {
+ const auto defaultParsed = parseTimeUnit<Seconds>(T_SECOND_STR, parsedUnitDuration);
+ assert(defaultParsed);
+ debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), ConfigParser::CurrentLocation() <<
+ ": WARNING: missing time unit, using deprecated default '" << T_SECOND_STR << "'");
+ }
+
+ const auto nanoseconds = CheckTimeValue(parsedTimeValue, parsedUnitDuration);
+
+ return FromNanoseconds<Seconds>(nanoseconds, parsedTimeValue);
+}
+
static void
parse_UrlHelperTimeout(SquidConfig::UrlHelperTimeout *config)
{
- time_msec_t tval;
- parseTimeLine(&tval, T_SECOND_STR, false, true);
- Config.Timeout.urlRewrite = static_cast<time_t>(tval/1000);
+ // TODO: do not allow optional timeunit (as the documentation prescribes)
+ // and use parseTimeLine() instead.
+ Config.Timeout.urlRewrite = ParseUrlRewriteTimeout().count();
char *key, *value;
while(ConfigParser::NextKvPair(key, value)) {
else if (strcasecmp(value, "use_configured_response") == 0) {
config->action = toutActUseConfiguredResponse;
} else {
- debugs(3, DBG_CRITICAL, "FATAL: unsuported \"on_timeout\" action:" << value);
+ debugs(3, DBG_CRITICAL, "FATAL: unsupported \"on_timeout\" action: " << value);
self_destruct();
return;
}
} else if (strcasecmp(key, "response") == 0) {
config->response = xstrdup(value);
} else {
- debugs(3, DBG_CRITICAL, "FATAL: unsuported option " << key);
+ debugs(3, DBG_CRITICAL, "FATAL: unsupported option " << key);
self_destruct();
return;
}
}
if (config->action != toutActUseConfiguredResponse && config->response) {
- debugs(3, DBG_CRITICAL, "FATAL: 'response=' option is valid only when used with the 'on_timeout=use_configured_response' option");
+ debugs(3, DBG_CRITICAL, "FATAL: 'response=' option is valid only when used with the 'on_timeout=use_configured_response' option");
self_destruct();
}
}
return;
}
- allow_t action = allow_t(ACCESS_ALLOWED);
+ auto action = Acl::Answer(ACCESS_ALLOWED);
if (strcmp(tm, "tunnel") == 0)
action.kind = 1;
else if (strcmp(tm, "respond") == 0)
"respond"
};
if (access) {
- SBufList lines = access->treeDump(name, [](const allow_t &action) {
- return onErrorTunnelMode.at(action.kind);
+ SBufList lines = access->treeDump(name, [](const Acl::Answer &action) {
+ return onErrorTunnelMode.at(action.kind);
});
dump_SBufList(entry, lines);
}