cache_cf.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 /* DEBUG: section 03 Configuration File Parsing */
10 
11 #include "squid.h"
12 #include "acl/Acl.h"
13 #include "acl/AclDenyInfoList.h"
14 #include "acl/AclSizeLimit.h"
15 #include "acl/Address.h"
16 #include "acl/Gadgets.h"
17 #include "acl/MethodData.h"
18 #include "acl/Tree.h"
19 #include "anyp/PortCfg.h"
20 #include "anyp/UriScheme.h"
21 #include "auth/Config.h"
22 #include "auth/Scheme.h"
23 #include "AuthReg.h"
24 #include "base/PackableStream.h"
25 #include "base/RunnersRegistry.h"
26 #include "cache_cf.h"
27 #include "CachePeer.h"
28 #include "ConfigOption.h"
29 #include "ConfigParser.h"
30 #include "CpuAffinityMap.h"
31 #include "DebugMessages.h"
32 #include "DiskIO/DiskIOModule.h"
33 #include "eui/Config.h"
34 #include "ExternalACL.h"
35 #include "format/Format.h"
36 #include "ftp/Elements.h"
37 #include "globals.h"
38 #include "HttpHeaderTools.h"
40 #include "icmp/IcmpConfig.h"
41 #include "ident/Config.h"
42 #include "ip/Intercept.h"
43 #include "ip/NfMarkConfig.h"
44 #include "ip/QosConfig.h"
45 #include "ip/tools.h"
46 #include "ipc/Kids.h"
47 #include "log/Config.h"
48 #include "log/CustomLog.h"
49 #include "MemBuf.h"
50 #include "MessageDelayPools.h"
51 #include "mgr/ActionPasswordList.h"
52 #include "mgr/Registration.h"
53 #include "neighbors.h"
54 #include "NeighborTypeDomainList.h"
55 #include "Parsing.h"
56 #include "pconn.h"
57 #include "PeerDigest.h"
58 #include "PeerPoolMgr.h"
59 #include "redirect.h"
60 #include "RefreshPattern.h"
61 #include "rfc1738.h"
62 #include "sbuf/List.h"
63 #include "sbuf/Stream.h"
64 #include "SquidConfig.h"
65 #include "SquidString.h"
66 #include "ssl/ProxyCerts.h"
67 #include "Store.h"
68 #include "store/Disks.h"
69 #include "tools.h"
70 #include "util.h"
71 #include "wordlist.h"
72 /* wccp2 has its own conditional definitions */
73 #include "wccp2.h"
74 #if USE_ADAPTATION
75 #include "adaptation/Config.h"
76 #endif
77 #if ICAP_CLIENT
78 #include "adaptation/icap/Config.h"
79 #endif
80 #if USE_ECAP
81 #include "adaptation/ecap/Config.h"
82 #endif
83 #if USE_OPENSSL
84 #include "ssl/Config.h"
85 #include "ssl/support.h"
86 #endif
87 #if USE_SQUID_ESI
88 #include "esi/Parser.h"
89 #endif
90 #if SQUID_SNMP
91 #include "snmp.h"
92 #endif
93 
94 #if HAVE_GLOB_H
95 #include <glob.h>
96 #endif
97 #include <chrono>
98 #include <limits>
99 #include <list>
100 #if HAVE_PWD_H
101 #include <pwd.h>
102 #endif
103 #if HAVE_GRP_H
104 #include <grp.h>
105 #endif
106 #if HAVE_SYS_SOCKET_H
107 #include <sys/socket.h>
108 #endif
109 #if HAVE_SYS_STAT_H
110 #include <sys/stat.h>
111 #endif
112 
113 #if USE_OPENSSL
114 #include "ssl/gadgets.h"
115 #endif
116 
117 #if USE_ADAPTATION
120 static void parse_adaptation_access_type();
121 #endif
122 
123 #if ICAP_CLIENT
125 static void dump_icap_service_type(StoreEntry *, const char *, const Adaptation::Icap::Config &);
127 static void parse_icap_class_type();
128 static void parse_icap_access_type();
129 
131 static void dump_icap_service_failure_limit(StoreEntry *, const char *, const Adaptation::Icap::Config &);
133 #endif
134 
135 #if USE_ECAP
137 static void dump_ecap_service_type(StoreEntry *, const char *, const Adaptation::Ecap::Config &);
139 #endif
140 
141 static peer_t parseNeighborType(const char *s);
142 
143 static const char *const T_NANOSECOND_STR = "nanosecond";
144 static const char *const T_MICROSECOND_STR = "microsecond";
145 static const char *const T_MILLISECOND_STR = "millisecond";
146 static const char *const T_SECOND_STR = "second";
147 static const char *const T_MINUTE_STR = "minute";
148 static const char *const T_HOUR_STR = "hour";
149 static const char *const T_DAY_STR = "day";
150 static const char *const T_WEEK_STR = "week";
151 static const char *const T_FORTNIGHT_STR = "fortnight";
152 static const char *const T_MONTH_STR = "month";
153 static const char *const T_YEAR_STR = "year";
154 static const char *const T_DECADE_STR = "decade";
155 
156 static const char *const B_BYTES_STR = "bytes";
157 static const char *const B_KBYTES_STR = "KB";
158 static const char *const B_MBYTES_STR = "MB";
159 static const char *const B_GBYTES_STR = "GB";
160 
161 // std::chrono::years requires C++20. Do our own rough calculation for now.
162 static const double HoursPerYear = 24*365.2522;
163 
164 static void parse_cache_log_message(DebugMessages **messages);
165 static void dump_cache_log_message(StoreEntry *entry, const char *name, const DebugMessages *messages);
166 static void free_cache_log_message(DebugMessages **messages);
167 
168 static void parse_access_log(CustomLog ** customlog_definitions);
169 static int check_null_access_log(CustomLog *customlog_definitions);
170 static void dump_access_log(StoreEntry * entry, const char *name, CustomLog * definitions);
171 static void free_access_log(CustomLog ** definitions);
172 
173 static void configDoConfigure(void);
174 static void parse_refreshpattern(RefreshPattern **);
175 static void parse_u_short(unsigned short * var);
176 static void parse_string(char **);
177 static void default_all(void);
178 static void defaults_if_none(void);
179 static void defaults_postscriptum(void);
180 static int parse_line(char *);
181 static void parse_obsolete(const char *);
182 static void parseBytesLine(size_t * bptr, const char *units);
183 static void parseBytesLineSigned(ssize_t * bptr, const char *units);
184 static size_t parseBytesUnits(const char *unit);
185 static void free_all(void);
186 void requirePathnameExists(const char *name, const char *path);
188 #if USE_HTTP_VIOLATIONS
189 static void free_HeaderManglers(HeaderManglers **pm);
190 static void dump_http_header_access(StoreEntry * entry, const char *name, const HeaderManglers *manglers);
191 static void parse_http_header_access(HeaderManglers **manglers);
192 #define free_http_header_access free_HeaderManglers
193 static void dump_http_header_replace(StoreEntry * entry, const char *name, const HeaderManglers *manglers);
194 static void parse_http_header_replace(HeaderManglers **manglers);
195 #define free_http_header_replace free_HeaderManglers
196 #endif
197 static void dump_HeaderWithAclList(StoreEntry * entry, const char *name, HeaderWithAclList *headers);
198 static void parse_HeaderWithAclList(HeaderWithAclList **header);
199 static void free_HeaderWithAclList(HeaderWithAclList **header);
200 static void parse_note(Notes *);
201 static void dump_note(StoreEntry *, const char *, Notes &);
202 static void free_note(Notes *);
203 static void parse_denyinfo(AclDenyInfoList ** var);
204 static void dump_denyinfo(StoreEntry * entry, const char *name, AclDenyInfoList * var);
205 static void free_denyinfo(AclDenyInfoList ** var);
206 
207 #if USE_WCCPv2
208 static void parse_IpAddress_list(Ip::Address_list **);
209 static void dump_IpAddress_list(StoreEntry *, const char *, const Ip::Address_list *);
210 static void free_IpAddress_list(Ip::Address_list **);
211 #if CURRENTLY_UNUSED
212 static int check_null_IpAddress_list(const Ip::Address_list *);
213 #endif /* CURRENTLY_UNUSED */
214 #endif /* USE_WCCPv2 */
215 
216 static void parsePortCfg(AnyP::PortCfgPointer *, const char *protocol);
217 #define parse_PortCfg(l) parsePortCfg((l), token)
218 static void dump_PortCfg(StoreEntry *, const char *, const AnyP::PortCfgPointer &);
219 #define free_PortCfg(h) *(h)=NULL
220 
221 #if USE_OPENSSL
222 static void parse_sslproxy_cert_sign(sslproxy_cert_sign **cert_sign);
223 static void dump_sslproxy_cert_sign(StoreEntry *entry, const char *name, sslproxy_cert_sign *cert_sign);
224 static void free_sslproxy_cert_sign(sslproxy_cert_sign **cert_sign);
225 static void parse_sslproxy_cert_adapt(sslproxy_cert_adapt **cert_adapt);
226 static void dump_sslproxy_cert_adapt(StoreEntry *entry, const char *name, sslproxy_cert_adapt *cert_adapt);
227 static void free_sslproxy_cert_adapt(sslproxy_cert_adapt **cert_adapt);
228 static void parse_sslproxy_ssl_bump(acl_access **ssl_bump);
229 static void dump_sslproxy_ssl_bump(StoreEntry *entry, const char *name, acl_access *ssl_bump);
230 static void free_sslproxy_ssl_bump(acl_access **ssl_bump);
231 #endif /* USE_OPENSSL */
232 
233 static void parse_ftp_epsv(acl_access **ftp_epsv);
234 static void dump_ftp_epsv(StoreEntry *entry, const char *name, acl_access *ftp_epsv);
235 static void free_ftp_epsv(acl_access **ftp_epsv);
236 
237 static void parse_b_size_t(size_t * var);
238 static void parse_b_int64_t(int64_t * var);
239 
240 static void parse_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap);
241 static void dump_CpuAffinityMap(StoreEntry *const entry, const char *const name, const CpuAffinityMap *const cpuAffinityMap);
242 static void free_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap);
243 
245 static void dump_UrlHelperTimeout(StoreEntry *, const char *, SquidConfig::UrlHelperTimeout &);
247 
248 static int parseOneConfigFile(const char *file_name, unsigned int depth);
249 
250 static void parse_configuration_includes_quoted_values(bool *recognizeQuotedValues);
251 static void dump_configuration_includes_quoted_values(StoreEntry *const entry, const char *const name, bool recognizeQuotedValues);
252 static void free_configuration_includes_quoted_values(bool *recognizeQuotedValues);
253 static void parse_on_unsupported_protocol(acl_access **access);
254 static void dump_on_unsupported_protocol(StoreEntry *entry, const char *name, acl_access *access);
255 static void free_on_unsupported_protocol(acl_access **access);
256 static void ParseAclWithAction(acl_access **access, const Acl::Answer &action, const char *desc, ACL *acl = nullptr);
258 static void dump_http_upgrade_request_protocols(StoreEntry *entry, const char *name, HttpUpgradeProtocolAccess *protoGuards);
260 
261 /*
262  * LegacyParser is a parser for legacy code that uses the global
263  * approach. This is static so that it is only exposed to cache_cf.
264  * Other modules needing access to a ConfigParser should have it
265  * provided to them in their parserFOO methods.
266  */
268 
269 const char *cfg_directive = nullptr;
270 const char *cfg_filename = nullptr;
273 
274 void
276 {
278 }
279 
280 static void
281 SetConfigFilename(char const *file_name, bool is_pipe)
282 {
283  if (is_pipe)
284  cfg_filename = file_name + 1;
285  else
286  cfg_filename = file_name;
287 }
288 
289 static const char*
290 skip_ws(const char* s)
291 {
292  while (xisspace(*s))
293  ++s;
294 
295  return s;
296 }
297 
298 static int
299 parseManyConfigFiles(char* files, int depth)
300 {
301  int error_count = 0;
302  char* saveptr = NULL;
303 #if HAVE_GLOB
304  char *path;
305  glob_t globbuf;
306  int i;
307  memset(&globbuf, 0, sizeof(globbuf));
308  for (path = strwordtok(files, &saveptr); path; path = strwordtok(NULL, &saveptr)) {
309  if (glob(path, globbuf.gl_pathc ? GLOB_APPEND : 0, NULL, &globbuf) != 0) {
310  int xerrno = errno;
311  fatalf("Unable to find configuration file: %s: %s", path, xstrerr(xerrno));
312  }
313  }
314  for (i = 0; i < (int)globbuf.gl_pathc; ++i) {
315  error_count += parseOneConfigFile(globbuf.gl_pathv[i], depth);
316  }
317  globfree(&globbuf);
318 #else
319  char* file = strwordtok(files, &saveptr);
320  while (file != NULL) {
321  error_count += parseOneConfigFile(file, depth);
322  file = strwordtok(NULL, &saveptr);
323  }
324 #endif /* HAVE_GLOB */
325  return error_count;
326 }
327 
328 static void
329 ReplaceSubstr(char*& str, int& len, unsigned substrIdx, unsigned substrLen, const char* newSubstr)
330 {
331  assert(str != NULL);
332  assert(newSubstr != NULL);
333 
334  unsigned newSubstrLen = strlen(newSubstr);
335  if (newSubstrLen > substrLen)
336  str = (char*)realloc(str, len - substrLen + newSubstrLen + 1);
337 
338  // move tail part including zero
339  memmove(str + substrIdx + newSubstrLen, str + substrIdx + substrLen, len - substrIdx - substrLen + 1);
340  // copy new substring in place
341  memcpy(str + substrIdx, newSubstr, newSubstrLen);
342 
343  len = strlen(str);
344 }
345 
346 static void
347 SubstituteMacro(char*& line, int& len, const char* macroName, const char* substStr)
348 {
349  assert(line != NULL);
350  assert(macroName != NULL);
351  assert(substStr != NULL);
352  unsigned macroNameLen = strlen(macroName);
353  while (const char* macroPos = strstr(line, macroName)) // we would replace all occurrences
354  ReplaceSubstr(line, len, macroPos - line, macroNameLen, substStr);
355 }
356 
357 static void
358 ProcessMacros(char*& line, int& len)
359 {
360  SubstituteMacro(line, len, "${service_name}", service_name.c_str());
361  SubstituteMacro(line, len, "${process_name}", TheKidName.c_str());
362  SubstituteMacro(line, len, "${process_number}", xitoa(KidIdentifier));
363 }
364 
365 static void
367 {
368  assert(str != NULL);
369  unsigned i = strlen(str);
370  while ((i > 0) && xisspace(str[i - 1]))
371  --i;
372  str[i] = '\0';
373 }
374 
375 static const char*
376 FindStatement(const char* line, const char* statement)
377 {
378  assert(line != NULL);
379  assert(statement != NULL);
380 
381  const char* str = skip_ws(line);
382  unsigned len = strlen(statement);
383  if (strncmp(str, statement, len) == 0) {
384  str += len;
385  if (*str == '\0')
386  return str;
387  else if (xisspace(*str))
388  return skip_ws(str);
389  }
390 
391  return NULL;
392 }
393 
394 static bool
395 StrToInt(const char* str, long& number)
396 {
397  assert(str != NULL);
398 
399  char* end;
400  number = strtol(str, &end, 0);
401 
402  return (end != str) && (*end == '\0'); // returns true if string contains nothing except number
403 }
404 
405 static bool
406 EvalBoolExpr(const char* expr)
407 {
408  assert(expr != NULL);
409  if (strcmp(expr, "true") == 0) {
410  return true;
411  } else if (strcmp(expr, "false") == 0) {
412  return false;
413  } else if (const char* equation = strchr(expr, '=')) {
414  const char* rvalue = skip_ws(equation + 1);
415  char* lvalue = (char*)xmalloc(equation - expr + 1);
416  xstrncpy(lvalue, expr, equation - expr + 1);
417  trim_trailing_ws(lvalue);
418 
419  long number1;
420  if (!StrToInt(lvalue, number1))
421  fatalf("String is not a integer number: '%s'\n", lvalue);
422  long number2;
423  if (!StrToInt(rvalue, number2))
424  fatalf("String is not a integer number: '%s'\n", rvalue);
425 
426  xfree(lvalue);
427  return number1 == number2;
428  }
429  fatalf("Unable to evaluate expression '%s'\n", expr);
430  return false; // this place cannot be reached
431 }
432 
433 static int
434 parseOneConfigFile(const char *file_name, unsigned int depth)
435 {
436  FILE *fp = NULL;
437  const char *orig_cfg_filename = cfg_filename;
438  const int orig_config_lineno = config_lineno;
439  char *token = NULL;
440  char *tmp_line = NULL;
441  int tmp_line_len = 0;
442  int err_count = 0;
443  int is_pipe = 0;
444 
445  debugs(3, DBG_IMPORTANT, "Processing Configuration File: " << file_name << " (depth " << depth << ")");
446  if (depth > 16) {
447  fatalf("WARNING: can't include %s: includes are nested too deeply (>16)!\n", file_name);
448  return 1;
449  }
450 
451  if (file_name[0] == '!' || file_name[0] == '|') {
452  fp = popen(file_name + 1, "r");
453  is_pipe = 1;
454  } else {
455  fp = fopen(file_name, "r");
456  }
457 
458  if (!fp) {
459  int xerrno = errno;
460  fatalf("Unable to open configuration file: %s: %s", file_name, xstrerr(xerrno));
461  }
462 
463 #if _SQUID_WINDOWS_
464  setmode(fileno(fp), O_TEXT);
465 #endif
466 
467  SetConfigFilename(file_name, bool(is_pipe));
468 
469  memset(config_input_line, '\0', BUFSIZ);
470 
471  config_lineno = 0;
472 
473  std::vector<bool> if_states;
474  while (fgets(config_input_line, BUFSIZ, fp)) {
475  ++config_lineno;
476 
477  if ((token = strchr(config_input_line, '\n')))
478  *token = '\0';
479 
480  if ((token = strchr(config_input_line, '\r')))
481  *token = '\0';
482 
483  // strip any prefix whitespace off the line.
484  const char *p = skip_ws(config_input_line);
485  if (config_input_line != p)
486  memmove(config_input_line, p, strlen(p)+1);
487 
488  if (strncmp(config_input_line, "#line ", 6) == 0) {
489  static char new_file_name[1024];
490  static char *file;
491  static char new_lineno;
492  token = config_input_line + 6;
493  new_lineno = strtol(token, &file, 0) - 1;
494 
495  if (file == token)
496  continue; /* Not a valid #line directive, may be a comment */
497 
498  while (*file && xisspace((unsigned char) *file))
499  ++file;
500 
501  if (*file) {
502  if (*file != '"')
503  continue; /* Not a valid #line directive, may be a comment */
504 
505  xstrncpy(new_file_name, file + 1, sizeof(new_file_name));
506 
507  if ((token = strchr(new_file_name, '"')))
508  *token = '\0';
509 
510  SetConfigFilename(new_file_name, false);
511  }
512 
513  config_lineno = new_lineno;
514  }
515 
516  if (config_input_line[0] == '#')
517  continue;
518 
519  if (config_input_line[0] == '\0')
520  continue;
521 
522  const char* append = tmp_line_len ? skip_ws(config_input_line) : config_input_line;
523 
524  size_t append_len = strlen(append);
525 
526  tmp_line = (char*)xrealloc(tmp_line, tmp_line_len + append_len + 1);
527 
528  strcpy(tmp_line + tmp_line_len, append);
529 
530  tmp_line_len += append_len;
531 
532  if (tmp_line[tmp_line_len-1] == '\\') {
533  debugs(3, 5, "parseConfigFile: tmp_line='" << tmp_line << "'");
534  tmp_line[--tmp_line_len] = '\0';
535  continue;
536  }
537 
538  trim_trailing_ws(tmp_line);
539  ProcessMacros(tmp_line, tmp_line_len);
540  debugs(3, (opt_parse_cfg_only?1:5), "Processing: " << tmp_line);
541 
542  if (const char* expr = FindStatement(tmp_line, "if")) {
543  if_states.push_back(EvalBoolExpr(expr)); // store last if-statement meaning
544  } else if (FindStatement(tmp_line, "endif")) {
545  if (!if_states.empty())
546  if_states.pop_back(); // remove last if-statement meaning
547  else
548  fatalf("'endif' without 'if'\n");
549  } else if (FindStatement(tmp_line, "else")) {
550  if (!if_states.empty())
551  if_states.back() = !if_states.back();
552  else
553  fatalf("'else' without 'if'\n");
554  } else if (if_states.empty() || if_states.back()) { // test last if-statement meaning if present
555  /* Handle includes here */
556  if (tmp_line_len >= 9 && strncmp(tmp_line, "include", 7) == 0 && xisspace(tmp_line[7])) {
557  err_count += parseManyConfigFiles(tmp_line + 8, depth + 1);
558  } else {
559  try {
560  if (!parse_line(tmp_line)) {
561  debugs(3, DBG_CRITICAL, ConfigParser::CurrentLocation() << ": unrecognized: '" << tmp_line << "'");
562  ++err_count;
563  }
564  } catch (...) {
565  // fatal for now
566  debugs(3, DBG_CRITICAL, "ERROR: configuration failure: " << CurrentException);
567  self_destruct();
568  }
569  }
570  }
571 
572  safe_free(tmp_line);
573  tmp_line_len = 0;
574 
575  }
576  if (!if_states.empty())
577  fatalf("if-statement without 'endif'\n");
578 
579  if (is_pipe) {
580  int ret = pclose(fp);
581 
582  if (ret != 0)
583  fatalf("parseConfigFile: '%s' failed with exit code %d\n", file_name, ret);
584  } else {
585  fclose(fp);
586  }
587 
588  SetConfigFilename(orig_cfg_filename, false);
589  config_lineno = orig_config_lineno;
590 
591  xfree(tmp_line);
592  return err_count;
593 }
594 
595 static
596 int
597 parseConfigFileOrThrow(const char *file_name)
598 {
599  int err_count = 0;
600 
601  debugs(5, 4, HERE);
602 
604 
606  default_all();
607 
608  err_count = parseOneConfigFile(file_name, 0);
609 
611 
613 
614  /*
615  * We must call configDoConfigure() before leave_suid() because
616  * configDoConfigure() is where we turn username strings into
617  * uid values.
618  */
620 
621  if (opt_send_signal == -1) {
622  Mgr::RegisterAction("config",
623  "Current Squid Configuration",
624  dump_config,
625  1, 1);
626  }
627 
628  return err_count;
629 }
630 
631 // TODO: Refactor main.cc to centrally handle (and report) all exceptions.
632 int
633 parseConfigFile(const char *file_name)
634 {
635  try {
636  return parseConfigFileOrThrow(file_name);
637  }
638  catch (const std::exception &ex) {
639  debugs(3, DBG_CRITICAL, "FATAL: bad configuration: " << ex.what());
640  self_destruct();
641  return 1; // not reached
642  }
643 }
644 
645 /*
646  * The templated functions below are essentially ConfigParser methods. They are
647  * not implemented as such because our generated code calling them is the only
648  * code that can instantiate implementations for each T -- we cannot place these
649  * definitions into ConfigParser.cc unless cf_parser.cci is moved there.
650  */
651 
652 // TODO: When adding Ts incompatible with this trivial API and implementation,
653 // replace both with a ConfigParser-maintained table of seen directives.
655 template <typename T>
656 static bool
657 SawDirective(const T &raw)
658 {
659  return bool(raw);
660 }
661 
664 template <typename T>
665 static void
666 ParseDirective(T &raw, ConfigParser &parser)
667 {
668  if (SawDirective(raw))
669  parser.rejectDuplicateDirective();
670 
671  // TODO: parser.openDirective(directiveName);
672  Must(!raw);
674  Must(raw);
675  parser.closeDirective();
676 }
677 
680 template <typename T>
681 static void
682 DumpDirective(const T &raw, StoreEntry *entry, const char *name)
683 {
684  if (!SawDirective(raw))
685  return; // not configured
686 
687  entry->append(name, strlen(name));
688  SBufStream os;
690  const auto buf = os.buf();
691  if (buf.length()) {
692  entry->append(" ", 1);
693  entry->append(buf.rawContent(), buf.length());
694  }
695  entry->append("\n", 1);
696 }
697 
699 template <typename T>
700 static void
702 {
704 
705  // While the implementation may change, there is no way to avoid zeroing.
706  // Even migration to a proper SquidConfig class would not help: While
707  // ordinary destructors do not need to zero data members, a SquidConfig
708  // destructor would have to zero to protect any SquidConfig::x destruction
709  // code from accidentally dereferencing an already destroyed Config.y.
710  static_assert(std::is_trivial<T>::value, "SquidConfig member is trivial");
711  memset(&raw, 0, sizeof(raw));
712 }
713 
714 static void
716 {
717  Config2.clear();
718  /* init memory as early as possible */
719  memConfigure();
720  /* Sanity checks */
721 
722  if (Debug::rotateNumber < 0) {
724  }
725 
726 #if SIZEOF_OFF_T <= 4
727  if (Config.Store.maxObjectSize > 0x7FFF0000) {
728  debugs(3, DBG_CRITICAL, "WARNING: This Squid binary can not handle files larger than 2GB. Limiting maximum_object_size to just below 2GB");
729  Config.Store.maxObjectSize = 0x7FFF0000;
730  }
731 #endif
732 
733  if (Config.Announce.period > 0) {
734  Config.onoff.announce = 1;
735  } else {
736  Config.Announce.period = 86400 * 365; /* one year */
737  Config.onoff.announce = 0;
738  }
739 
742  else
743  visible_appname_string = (char const *)APP_FULLNAME;
744 
745  if (Config.Program.redirect) {
746  if (Config.redirectChildren.n_max < 1) {
749  }
750  }
751 
752  if (Config.Program.store_id) {
753  if (Config.storeIdChildren.n_max < 1) {
756  }
757  }
758 
759  if (Config.appendDomain)
760  if (*Config.appendDomain != '.')
761  fatal("append_domain must begin with a '.'");
762 
763  if (Config.errHtmlText == NULL)
765 
766 #if !HAVE_SETRLIMIT || !defined(RLIMIT_NOFILE)
767  if (Config.max_filedescriptors > 0) {
768  debugs(0, DBG_IMPORTANT, "WARNING: max_filedescriptors disabled. Operating System setrlimit(RLIMIT_NOFILE) is missing.");
769  }
770 #elif USE_SELECT || USE_SELECT_WIN32
771  if (Config.max_filedescriptors > FD_SETSIZE) {
772  debugs(0, DBG_IMPORTANT, "WARNING: max_filedescriptors limited to " << FD_SETSIZE << " by select() algorithm.");
773  }
774 #endif
775 
776  storeConfigure();
777 
778  snprintf(ThisCache, sizeof(ThisCache), "%s (%s)",
779  uniqueHostname(),
781 
782  /*
783  * the extra space is for loop detection in client_side.c -- we search
784  * for substrings in the Via header.
785  */
786  snprintf(ThisCache2, sizeof(ThisCache), " %s (%s)",
787  uniqueHostname(),
789 
790  /* Use visible_hostname as default surrogate_id */
791  if (!Config.Accel.surrogate_id) {
792  const char *t = getMyHostname();
793  Config.Accel.surrogate_id = xstrdup( (t?t:"unset-id") );
794  }
795 
798 
799  if (Config.appendDomain)
801  else
803 
804  if (Config.connect_retries > 10) {
805  debugs(0,DBG_CRITICAL, "WARNING: connect_retries cannot be larger than 10. Resetting to 10.");
806  Config.connect_retries = 10;
807  }
808 
809  requirePathnameExists("MIME Config Table", Config.mimeTablePathname);
810 #if USE_UNLINKD
811 
812  requirePathnameExists("unlinkd_program", Config.Program.unlinkd);
813 #endif
814  bool logDaemonUsed = false;
815  for (const auto *log = Config.Log.accesslogs; !logDaemonUsed && log; log = log->next)
816  logDaemonUsed = log->usesDaemon();
817 #if ICAP_CLIENT
818  for (const auto *log = Config.Log.icaplogs; !logDaemonUsed && log; log = log->next)
819  logDaemonUsed = log->usesDaemon();
820 #endif
821  if (logDaemonUsed)
822  requirePathnameExists("logfile_daemon", Log::TheConfig.logfile_daemon);
823 
824  if (Config.Program.redirect)
825  requirePathnameExists("redirect_program", Config.Program.redirect->key);
826 
827  if (Config.Program.store_id)
828  requirePathnameExists("store_id_program", Config.Program.store_id->key);
829 
830  requirePathnameExists("Icon Directory", Config.icons.directory);
831 
833  requirePathnameExists("Error Directory", Config.errorDirectory);
834 
835 #if USE_HTTP_VIOLATIONS
836 
837  {
838  const RefreshPattern *R;
839 
840  for (R = Config.Refresh; R; R = R->next) {
841  if (!R->flags.override_expire)
842  continue;
843 
844  debugs(22, DBG_IMPORTANT, "WARNING: use of 'override-expire' in 'refresh_pattern' violates HTTP");
845 
846  break;
847  }
848 
849  for (R = Config.Refresh; R; R = R->next) {
850  if (!R->flags.override_lastmod)
851  continue;
852 
853  debugs(22, DBG_IMPORTANT, "WARNING: use of 'override-lastmod' in 'refresh_pattern' violates HTTP");
854 
855  break;
856  }
857 
858  for (R = Config.Refresh; R; R = R->next) {
859  if (!R->flags.reload_into_ims)
860  continue;
861 
862  debugs(22, DBG_IMPORTANT, "WARNING: use of 'reload-into-ims' in 'refresh_pattern' violates HTTP");
863 
864  break;
865  }
866 
867  for (R = Config.Refresh; R; R = R->next) {
868  if (!R->flags.ignore_reload)
869  continue;
870 
871  debugs(22, DBG_IMPORTANT, "WARNING: use of 'ignore-reload' in 'refresh_pattern' violates HTTP");
872 
873  break;
874  }
875 
876  for (R = Config.Refresh; R; R = R->next) {
877  if (!R->flags.ignore_no_store)
878  continue;
879 
880  debugs(22, DBG_IMPORTANT, "WARNING: use of 'ignore-no-store' in 'refresh_pattern' violates HTTP");
881 
882  break;
883  }
884 
885  for (R = Config.Refresh; R; R = R->next) {
886  if (!R->flags.ignore_private)
887  continue;
888 
889  debugs(22, DBG_IMPORTANT, "WARNING: use of 'ignore-private' in 'refresh_pattern' violates HTTP");
890 
891  break;
892  }
893  }
894 #endif
895 #if !USE_HTTP_VIOLATIONS
896  Config.onoff.via = 1;
897 #else
898 
899  if (!Config.onoff.via)
900  debugs(22, DBG_IMPORTANT, "WARNING: HTTP requires the use of Via");
901 
902 #endif
903 
904  // we enable runtime PURGE checks if there is at least one PURGE method ACL
905  // TODO: replace with a dedicated "purge" ACL option?
907 
908  if (geteuid() == 0) {
909  if (NULL != Config.effectiveUser) {
910 
911  struct passwd *pwd = getpwnam(Config.effectiveUser);
912 
913  if (NULL == pwd) {
914  /*
915  * Andres Kroonmaa <andre@online.ee>:
916  * Some getpwnam() implementations (Solaris?) require
917  * an available FD < 256 for opening a FILE* to the
918  * passwd file.
919  * DW:
920  * This should be safe at startup, but might still fail
921  * during reconfigure.
922  */
923  fatalf("getpwnam failed to find userid for effective user '%s'",
925  return;
926  }
927 
928  Config2.effectiveUserID = pwd->pw_uid;
929 
930  Config2.effectiveGroupID = pwd->pw_gid;
931 
932 #if HAVE_PUTENV
933  if (pwd->pw_dir && *pwd->pw_dir) {
934  // putenv() leaks by design; avoid leaks when nothing changes
935  static SBuf lastDir;
936  if (lastDir.isEmpty() || lastDir.cmp(pwd->pw_dir) != 0) {
937  lastDir = pwd->pw_dir;
938  int len = strlen(pwd->pw_dir) + 6;
939  char *env_str = (char *)xcalloc(len, 1);
940  snprintf(env_str, len, "HOME=%s", pwd->pw_dir);
941  putenv(env_str);
942  }
943  }
944 #endif
945  }
946  } else {
947  Config2.effectiveUserID = geteuid();
948  Config2.effectiveGroupID = getegid();
949  }
950 
951  if (NULL != Config.effectiveGroup) {
952 
953  struct group *grp = getgrnam(Config.effectiveGroup);
954 
955  if (NULL == grp) {
956  fatalf("getgrnam failed to find groupid for effective group '%s'",
958  return;
959  }
960 
961  Config2.effectiveGroupID = grp->gr_gid;
962  }
963 
964 #if USE_OPENSSL
967 #endif
968 
969  if (Security::ProxyOutgoingConfig.encryptTransport) {
970  debugs(3, DBG_IMPORTANT, "Initializing https:// proxy context");
973 #if USE_OPENSSL
974  fatal("ERROR: Could not initialize https:// proxy context");
975 #else
976  debugs(3, DBG_IMPORTANT, "ERROR: proxying https:// currently still requires --with-openssl");
977 #endif
978  }
979 #if USE_OPENSSL
981 #endif
982  }
983 
984  for (CachePeer *p = Config.peers; p != NULL; p = p->next) {
985 
986  // default value for ssldomain= is the peer host/IP
987  if (p->secure.sslDomain.isEmpty())
988  p->secure.sslDomain = p->host;
989 
990  if (p->secure.encryptTransport) {
991  debugs(3, DBG_IMPORTANT, "Initializing cache_peer " << p->name << " TLS context");
992  p->sslContext = p->secure.createClientContext(true);
993  if (!p->sslContext) {
994  debugs(3, DBG_CRITICAL, "ERROR: Could not initialize cache_peer " << p->name << " TLS context");
995  self_destruct();
996  return;
997  }
998  }
999  }
1000 
1001  for (AnyP::PortCfgPointer s = HttpPortList; s != NULL; s = s->next) {
1002  if (!s->secure.encryptTransport)
1003  continue;
1004  debugs(3, DBG_IMPORTANT, "Initializing " << AnyP::UriScheme(s->transport.protocol) << "_port " << s->s << " TLS contexts");
1005  s->secure.initServerContexts(*s);
1006  }
1007 
1008  // prevent infinite fetch loops in the request parser
1009  // due to buffer full but not enough data received to finish parse
1011  fatalf("Client request buffer of %u bytes cannot hold a request with %u bytes of headers." \
1012  " Change client_request_buffer_max or request_header_max_size limits.",
1014  }
1015 
1016  /*
1017  * Disable client side request pipelining if client_persistent_connections OFF.
1018  * Waste of resources queueing any pipelined requests when the first will close the connection.
1019  */
1021  debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: pipeline_prefetch " << Config.pipeline_max_prefetch <<
1022  " requires client_persistent_connections ON. Forced pipeline_prefetch 0.");
1024  }
1025 
1026 #if USE_AUTH
1027  /*
1028  * disable client side request pipelining. There is a race with
1029  * Negotiate and NTLM when the client sends a second request on an
1030  * connection before the authenticate challenge is sent. With
1031  * pipelining OFF, the client may fail to authenticate, but squid's
1032  * state will be preserved.
1033  */
1034  if (Config.pipeline_max_prefetch > 0) {
1035  Auth::SchemeConfig *nego = Auth::SchemeConfig::Find("Negotiate");
1037  if ((nego && nego->active()) || (ntlm && ntlm->active())) {
1038  debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: pipeline_prefetch breaks NTLM and Negotiate authentication. Forced pipeline_prefetch 0.");
1040  }
1041  }
1042 
1043  for (auto &authSchemes : Auth::TheConfig.schemeLists) {
1044  authSchemes.expand();
1045  if (authSchemes.authConfigs.empty()) {
1046  debugs(3, DBG_CRITICAL, "auth_schemes: at least one scheme name is required; got: " << authSchemes.rawSchemes);
1047  self_destruct();
1048  }
1049  }
1050 #endif
1051 }
1052 
1059 void
1060 parse_obsolete(const char *name)
1061 {
1062  // Directives which have been radically changed rather than removed
1063  if (!strcmp(name, "url_rewrite_concurrency")) {
1064  int cval;
1065  parse_int(&cval);
1066  debugs(3, DBG_CRITICAL, "WARNING: url_rewrite_concurrency upgrade overriding url_rewrite_children settings.");
1068  }
1069 
1070  if (!strcmp(name, "log_access")) {
1071  self_destruct();
1072  return;
1073  }
1074 
1075  if (!strcmp(name, "log_icap")) {
1076  self_destruct();
1077  return;
1078  }
1079 
1080  if (!strcmp(name, "ignore_ims_on_miss")) {
1081  // the replacement directive cache_revalidate_on_miss has opposite meanings for ON/OFF value
1082  // than the 2.7 directive. We need to parse and invert the configured value.
1083  int temp = 0;
1084  parse_onoff(&temp);
1086  }
1087 
1088  if (!strncmp(name, "sslproxy_", 9)) {
1089  // the replacement directive tls_outgoing_options uses options instead of whole-line input
1090  SBuf tmp;
1091  if (!strcmp(name, "sslproxy_cafile"))
1092  tmp.append("cafile=");
1093  else if (!strcmp(name, "sslproxy_capath"))
1094  tmp.append("capath=");
1095  else if (!strcmp(name, "sslproxy_cipher"))
1096  tmp.append("cipher=");
1097  else if (!strcmp(name, "sslproxy_client_certificate"))
1098  tmp.append("cert=");
1099  else if (!strcmp(name, "sslproxy_client_key"))
1100  tmp.append("key=");
1101  else if (!strcmp(name, "sslproxy_flags"))
1102  tmp.append("flags=");
1103  else if (!strcmp(name, "sslproxy_options"))
1104  tmp.append("options=");
1105  else if (!strcmp(name, "sslproxy_version"))
1106  tmp.append("version=");
1107  else {
1108  debugs(3, DBG_CRITICAL, "ERROR: unknown directive: " << name);
1109  self_destruct();
1110  return;
1111  }
1112 
1113  // add the value as unquoted-string because the old values did not support whitespace
1114  const char *token = ConfigParser::NextQuotedOrToEol();
1115  tmp.append(token, strlen(token));
1117  }
1118 }
1119 
1120 template <class MinimalUnit>
1121 static const char *
1123 {
1124  const auto minUnit = MinimalUnit(1);
1125  if(minUnit == std::chrono::nanoseconds(1))
1126  return T_NANOSECOND_STR;
1127  else if (minUnit == std::chrono::microseconds(1))
1128  return T_MICROSECOND_STR;
1129  else if (minUnit == std::chrono::milliseconds(1))
1130  return T_MILLISECOND_STR;
1131  else {
1132  assert(minUnit >= std::chrono::seconds(1));
1133  return T_SECOND_STR;
1134  }
1135 }
1136 
1142 template <class MinimalUnit>
1143 static bool
1144 parseTimeUnit(const char *unitName, std::chrono::nanoseconds &ns)
1145 {
1146  if (!unitName)
1147  throw TexcHere("missing time unit");
1148 
1149  if (!strncasecmp(unitName, T_NANOSECOND_STR, strlen(T_NANOSECOND_STR)))
1150  ns = std::chrono::nanoseconds(1);
1151  else if (!strncasecmp(unitName, T_MICROSECOND_STR, strlen(T_MICROSECOND_STR)))
1152  ns = std::chrono::microseconds(1);
1153  else if (!strncasecmp(unitName, T_MILLISECOND_STR, strlen(T_MILLISECOND_STR)))
1154  ns = std::chrono::milliseconds(1);
1155  else if (!strncasecmp(unitName, T_SECOND_STR, strlen(T_SECOND_STR)))
1156  ns = std::chrono::seconds(1);
1157  else if (!strncasecmp(unitName, T_MINUTE_STR, strlen(T_MINUTE_STR)))
1158  ns = std::chrono::minutes(1);
1159  else if (!strncasecmp(unitName, T_HOUR_STR, strlen(T_HOUR_STR)))
1160  ns = std::chrono::hours(1);
1161  else if (!strncasecmp(unitName, T_DAY_STR, strlen(T_DAY_STR)))
1162  ns = std::chrono::hours(24);
1163  else if (!strncasecmp(unitName, T_WEEK_STR, strlen(T_WEEK_STR)))
1164  ns = std::chrono::hours(24 * 7);
1165  else if (!strncasecmp(unitName, T_FORTNIGHT_STR, strlen(T_FORTNIGHT_STR)))
1166  ns = std::chrono::hours(24 * 14);
1167  else if (!strncasecmp(unitName, T_MONTH_STR, strlen(T_MONTH_STR)))
1168  ns = std::chrono::hours(24 * 30);
1169  else if (!strncasecmp(unitName, T_YEAR_STR, strlen(T_YEAR_STR)))
1170  ns = std::chrono::hours(static_cast<std::chrono::hours::rep>(HoursPerYear));
1171  else if (!strncasecmp(unitName, T_DECADE_STR, strlen(T_DECADE_STR)))
1172  ns = std::chrono::hours(static_cast<std::chrono::hours::rep>(HoursPerYear * 10));
1173  else
1174  return false;
1175 
1176  if (ns < MinimalUnit(1)) {
1177  throw TexcHere(ToSBuf("time unit '", unitName, "' is too small to be used in this context, the minimal unit is ",
1178  TimeUnitToString<MinimalUnit>()));
1179  }
1180 
1181  return true;
1182 }
1183 
1184 static std::chrono::nanoseconds
1185 ToNanoSeconds(const double value, const std::chrono::nanoseconds &unit)
1186 {
1187  if (value < 0.0)
1188  throw TexcHere("time must have a positive value");
1189 
1190  if (value > (static_cast<double>(std::chrono::nanoseconds::max().count()) / unit.count())) {
1191  const auto maxYears = std::chrono::duration_cast<std::chrono::hours>(std::chrono::nanoseconds::max()).count()/HoursPerYear;
1192  throw TexcHere(ToSBuf("time values cannot exceed ", maxYears, " years"));
1193  }
1194 
1195  return std::chrono::duration_cast<std::chrono::nanoseconds>(unit * value);
1196 }
1197 
1198 template <class TimeUnit>
1199 static TimeUnit
1200 FromNanoseconds(const std::chrono::nanoseconds &ns, const double parsedValue)
1201 {
1202  const auto result = std::chrono::duration_cast<TimeUnit>(ns);
1203  if (!result.count()) {
1204  throw TexcHere(ToSBuf("time value '", parsedValue,
1205  "' is too small to be used in this context, the minimal value is 1 ",
1206  TimeUnitToString<TimeUnit>()));
1207  }
1208  return result;
1209 }
1210 
1213 template <class TimeUnit>
1214 static TimeUnit
1216 {
1217  const auto valueToken = ConfigParser::NextToken();
1218  if (!valueToken)
1219  throw TexcHere("cannot read a time value");
1220 
1221  const auto parsedValue = xatof(valueToken);
1222 
1223  if (parsedValue == 0)
1224  return TimeUnit::zero();
1225 
1226  std::chrono::nanoseconds parsedUnitDuration;
1227 
1228  const auto token = ConfigParser::PeekAtToken();
1229 
1230  if (!parseTimeUnit<TimeUnit>(token, parsedUnitDuration))
1231  throw TexcHere(ToSBuf("unknown time unit '", token, "'"));
1232 
1233  (void)ConfigParser::NextToken();
1234 
1235  const auto nanoseconds = ToNanoSeconds(parsedValue, parsedUnitDuration);
1236 
1237  // validate precisions (time-units-small only)
1238  if (TimeUnit(1) <= std::chrono::microseconds(1)) {
1239  if (0 < nanoseconds.count() && nanoseconds.count() < 3) {
1241  "Squid time measurement precision is likely to be far worse than " <<
1242  "the nanosecond-level precision implied by the configured value: " << parsedValue << ' ' << token);
1243  }
1244  }
1245 
1246  return FromNanoseconds<TimeUnit>(nanoseconds, parsedValue);
1247 }
1248 
1249 static void
1250 parseBytesLine64(int64_t * bptr, const char *units)
1251 {
1252  char *token;
1253  double d;
1254  int64_t m;
1255  int64_t u;
1256 
1257  if ((u = parseBytesUnits(units)) == 0) {
1258  self_destruct();
1259  return;
1260  }
1261 
1262  if ((token = ConfigParser::NextToken()) == NULL) {
1263  self_destruct();
1264  return;
1265  }
1266 
1267  if (strcmp(token, "none") == 0 || strcmp(token, "-1") == 0) {
1268  *bptr = -1;
1269  return;
1270  }
1271 
1272  d = xatof(token);
1273 
1274  m = u; /* default to 'units' if none specified */
1275 
1276  if (0.0 == d)
1277  (void) 0;
1278  else if ((token = ConfigParser::NextToken()) == NULL)
1279  debugs(3, DBG_CRITICAL, "WARNING: No units on '" <<
1280  config_input_line << "', assuming " <<
1281  d << " " << units );
1282  else if ((m = parseBytesUnits(token)) == 0) {
1283  self_destruct();
1284  return;
1285  }
1286 
1287  *bptr = static_cast<int64_t>(m * d / u);
1288 
1289  if (static_cast<double>(*bptr) * 2 != (m * d / u) * 2) {
1290  debugs(3, DBG_CRITICAL, "ERROR: Invalid value '" <<
1291  d << " " << token << ": integer overflow (int64_t).");
1292  self_destruct();
1293  }
1294 }
1295 
1296 static void
1297 parseBytesLine(size_t * bptr, const char *units)
1298 {
1299  char *token;
1300  double d;
1301  int m;
1302  int u;
1303 
1304  if ((u = parseBytesUnits(units)) == 0) {
1305  self_destruct();
1306  return;
1307  }
1308 
1309  if ((token = ConfigParser::NextToken()) == NULL) {
1310  self_destruct();
1311  return;
1312  }
1313 
1314  if (strcmp(token, "none") == 0 || strcmp(token, "-1") == 0) {
1315  *bptr = static_cast<size_t>(-1);
1316  return;
1317  }
1318 
1319  d = xatof(token);
1320 
1321  m = u; /* default to 'units' if none specified */
1322 
1323  if (0.0 == d)
1324  (void) 0;
1325  else if ((token = ConfigParser::NextToken()) == NULL)
1326  debugs(3, DBG_CRITICAL, "WARNING: No units on '" <<
1327  config_input_line << "', assuming " <<
1328  d << " " << units );
1329  else if ((m = parseBytesUnits(token)) == 0) {
1330  self_destruct();
1331  return;
1332  }
1333 
1334  *bptr = static_cast<size_t>(m * d / u);
1335 
1336  if (static_cast<double>(*bptr) * 2 != (m * d / u) * 2) {
1337  debugs(3, DBG_CRITICAL, "ERROR: Invalid value '" <<
1338  d << " " << token << ": integer overflow (size_t).");
1339  self_destruct();
1340  }
1341 }
1342 
1343 static void
1344 parseBytesLineSigned(ssize_t * bptr, const char *units)
1345 {
1346  char *token;
1347  double d;
1348  int m;
1349  int u;
1350 
1351  if ((u = parseBytesUnits(units)) == 0) {
1352  self_destruct();
1353  return;
1354  }
1355 
1356  if ((token = ConfigParser::NextToken()) == NULL) {
1357  self_destruct();
1358  return;
1359  }
1360 
1361  if (strcmp(token, "none") == 0 || token[0] == '-' /* -N */) {
1362  *bptr = -1;
1363  return;
1364  }
1365 
1366  d = xatof(token);
1367 
1368  m = u; /* default to 'units' if none specified */
1369 
1370  if (0.0 == d)
1371  (void) 0;
1372  else if ((token = ConfigParser::NextToken()) == NULL)
1373  debugs(3, DBG_CRITICAL, "WARNING: No units on '" <<
1374  config_input_line << "', assuming " <<
1375  d << " " << units );
1376  else if ((m = parseBytesUnits(token)) == 0) {
1377  self_destruct();
1378  return;
1379  }
1380 
1381  *bptr = static_cast<ssize_t>(m * d / u);
1382 
1383  if (static_cast<double>(*bptr) * 2 != (m * d / u) * 2) {
1384  debugs(3, DBG_CRITICAL, "ERROR: Invalid value '" <<
1385  d << " " << token << ": integer overflow (ssize_t).");
1386  self_destruct();
1387  }
1388 }
1389 
1395 void
1396 parseBytesOptionValue(size_t * bptr, const char *units, char const * value)
1397 {
1398  int u;
1399  if ((u = parseBytesUnits(units)) == 0) {
1400  self_destruct();
1401  return;
1402  }
1403 
1404  // Find number from string beginning.
1405  char const * number_begin = value;
1406  char const * number_end = value;
1407 
1408  while ((*number_end >= '0' && *number_end <= '9')) {
1409  ++number_end;
1410  }
1411 
1412  String number;
1413  number.assign(number_begin, number_end - number_begin);
1414 
1415  int d = xatoi(number.termedBuf());
1416  int m;
1417  if ((m = parseBytesUnits(number_end)) == 0) {
1418  self_destruct();
1419  return;
1420  }
1421 
1422  *bptr = static_cast<size_t>(m * d / u);
1423  if (static_cast<double>(*bptr) * 2 != (m * d / u) * 2)
1424  self_destruct();
1425 }
1426 
1427 static size_t
1428 parseBytesUnits(const char *unit)
1429 {
1430  if (!strncasecmp(unit, B_BYTES_STR, strlen(B_BYTES_STR)))
1431  return 1;
1432 
1433  if (!strncasecmp(unit, B_KBYTES_STR, strlen(B_KBYTES_STR)))
1434  return 1 << 10;
1435 
1436  if (!strncasecmp(unit, B_MBYTES_STR, strlen(B_MBYTES_STR)))
1437  return 1 << 20;
1438 
1439  if (!strncasecmp(unit, B_GBYTES_STR, strlen(B_GBYTES_STR)))
1440  return 1 << 30;
1441 
1442  debugs(3, DBG_CRITICAL, "WARNING: Unknown bytes unit '" << unit << "'");
1443 
1444  return 0;
1445 }
1446 
1447 static void
1449 {
1450  while (char *token = ConfigParser::NextQuotedToken())
1451  list->push_back(SBuf(token));
1452 }
1453 
1454 // just dump a list, no directive name
1455 static void
1456 dump_SBufList(StoreEntry * entry, const SBufList &words)
1457 {
1458  for (const auto &i : words) {
1459  entry->append(i.rawContent(), i.length());
1460  entry->append(" ",1);
1461  }
1462  entry->append("\n",1);
1463 }
1464 
1465 // dump a SBufList type directive with name
1466 static void
1467 dump_SBufList(StoreEntry * entry, const char *name, SBufList &list)
1468 {
1469  if (!list.empty()) {
1470  entry->append(name, strlen(name));
1471  entry->append(" ", 1);
1472  dump_SBufList(entry, list);
1473  }
1474 }
1475 
1476 static void
1478 {
1479  if (list)
1480  list->clear();
1481 }
1482 
1483 static void
1484 dump_acl(StoreEntry * entry, const char *name, ACL * ae)
1485 {
1486  while (ae != NULL) {
1487  debugs(3, 3, "dump_acl: " << name << " " << ae->name);
1488  storeAppendPrintf(entry, "%s %s %s ",
1489  name,
1490  ae->name,
1491  ae->typeString());
1492  SBufList tail;
1493  tail.splice(tail.end(), ae->dumpOptions());
1494  tail.splice(tail.end(), ae->dump()); // ACL parameters
1495  dump_SBufList(entry, tail);
1496  ae = ae->next;
1497  }
1498 }
1499 
1500 static void
1502 {
1504 }
1505 
1506 static void
1508 {
1509  aclDestroyAcls(ae);
1510 }
1511 
1512 void
1514 {
1515  // XXX: Should dump ACL names like "foo !bar" but dumps parsing context like
1516  // "(clientside_tos 0x11 line)".
1517  dump_SBufList(entry, head->dump());
1518 }
1519 
1520 void
1521 dump_acl_access(StoreEntry * entry, const char *name, acl_access * head)
1522 {
1523  if (head)
1524  dump_SBufList(entry, head->treeDump(name, &Acl::AllowOrDeny));
1525 }
1526 
1527 static void
1529 {
1531 }
1532 
1533 static void
1535 {
1537 }
1538 
1539 static void
1540 dump_address(StoreEntry * entry, const char *name, Ip::Address &addr)
1541 {
1542  char buf[MAX_IPSTRLEN];
1543  storeAppendPrintf(entry, "%s %s\n", name, addr.toStr(buf,MAX_IPSTRLEN) );
1544 }
1545 
1546 static void
1548 {
1549  char *token = ConfigParser::NextToken();
1550 
1551  if (!token) {
1552  self_destruct();
1553  return;
1554  }
1555 
1556  if (!strcmp(token,"any_addr"))
1557  addr->setAnyAddr();
1558  else if ( (!strcmp(token,"no_addr")) || (!strcmp(token,"full_mask")) )
1559  addr->setNoAddr();
1560  else if ( (*addr = token) ) // try parse numeric/IPA
1561  (void) 0;
1562  else if (addr->GetHostByName(token)) // do not use ipcache
1563  (void) 0;
1564  else { // not an IP and not a hostname
1565  debugs(3, DBG_CRITICAL, "FATAL: invalid IP address or domain name '" << token << "'");
1566  self_destruct();
1567  }
1568 }
1569 
1570 static void
1572 {
1573  addr->setEmpty();
1574 }
1575 
1576 static void
1577 dump_acl_address(StoreEntry * entry, const char *name, Acl::Address * head)
1578 {
1579  char buf[MAX_IPSTRLEN];
1580 
1581  for (Acl::Address *l = head; l; l = l->next) {
1582  if (!l->addr.isAnyAddr())
1583  storeAppendPrintf(entry, "%s %s", name, l->addr.toStr(buf,MAX_IPSTRLEN));
1584  else
1585  storeAppendPrintf(entry, "%s autoselect", name);
1586 
1587  dump_acl_list(entry, l->aclList);
1588 
1589  storeAppendPrintf(entry, "\n");
1590  }
1591 }
1592 
1593 static void
1595 {
1596  Acl::Address *l = new Acl::Address;
1597  parse_address(&l->addr);
1599 
1600  Acl::Address **tail = head;
1601  while (*tail)
1602  tail = &(*tail)->next;
1603 
1604  *tail = l;
1605 }
1606 
1607 static void
1609 {
1610  delete *head;
1611  *head = NULL;
1612 }
1613 
1614 static void
1615 dump_acl_tos(StoreEntry * entry, const char *name, acl_tos * head)
1616 {
1617  acl_tos *l;
1618 
1619  for (l = head; l; l = l->next) {
1620  if (l->tos > 0)
1621  storeAppendPrintf(entry, "%s 0x%02X", name, l->tos);
1622  else
1623  storeAppendPrintf(entry, "%s none", name);
1624 
1625  dump_acl_list(entry, l->aclList);
1626 
1627  storeAppendPrintf(entry, "\n");
1628  }
1629 }
1630 
1631 static void
1633 {
1634  unsigned int tos; /* Initially uint for strtoui. Casted to tos_t before return */
1635  char *token = ConfigParser::NextToken();
1636 
1637  if (!token) {
1638  self_destruct();
1639  return;
1640  }
1641 
1642  if (!xstrtoui(token, NULL, &tos, 0, std::numeric_limits<tos_t>::max())) {
1643  self_destruct();
1644  return;
1645  }
1646 
1647  const unsigned int chTos = tos & 0xFC;
1648  if (chTos != tos) {
1649  debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: Tos value '" << tos << "' adjusted to '" << chTos << "'");
1650  tos = chTos;
1651  }
1652 
1653  acl_tos *l = new acl_tos;
1654 
1655  l->tos = (tos_t)tos;
1656 
1657  aclParseAclList(LegacyParser, &l->aclList, token);
1658 
1659  acl_tos **tail = head; /* sane name below */
1660  while (*tail)
1661  tail = &(*tail)->next;
1662 
1663  *tail = l;
1664 }
1665 
1666 static void
1668 {
1669  delete *head;
1670  *head = NULL;
1671 }
1672 
1673 #if SO_MARK && USE_LIBCAP
1674 
1675 static void
1676 dump_acl_nfmark(StoreEntry * entry, const char *name, acl_nfmark * head)
1677 {
1678  for (acl_nfmark *l = head; l; l = l->next) {
1679  storeAppendPrintf(entry, "%s %s", name, ToSBuf(l->markConfig).c_str());
1680 
1681  dump_acl_list(entry, l->aclList);
1682 
1683  storeAppendPrintf(entry, "\n");
1684  }
1685 }
1686 
1687 static void
1688 parse_acl_nfmark(acl_nfmark ** head)
1689 {
1690  SBuf token(ConfigParser::NextToken());
1691  const auto mc = Ip::NfMarkConfig::Parse(token);
1692 
1693  // Packet marking directives should not allow to use masks.
1694  const auto pkt_dirs = {"mark_client_packet", "clientside_mark", "tcp_outgoing_mark"};
1695  if (mc.hasMask() && std::find(pkt_dirs.begin(), pkt_dirs.end(), cfg_directive) != pkt_dirs.end())
1696  throw TexcHere(ToSBuf("'", cfg_directive, "' does not support masked marks"));
1697 
1698  acl_nfmark *l = new acl_nfmark;
1699  l->markConfig = mc;
1700 
1701  aclParseAclList(LegacyParser, &l->aclList, token.c_str());
1702 
1703  acl_nfmark **tail = head; /* sane name below */
1704  while (*tail)
1705  tail = &(*tail)->next;
1706 
1707  *tail = l;
1708 }
1709 
1710 static void
1711 free_acl_nfmark(acl_nfmark ** head)
1712 {
1713  delete *head;
1714  *head = NULL;
1715 }
1716 #endif /* SO_MARK */
1717 
1718 static void
1719 dump_acl_b_size_t(StoreEntry * entry, const char *name, AclSizeLimit * head)
1720 {
1721  for (AclSizeLimit *l = head; l; l = l->next) {
1722  if (l->size != -1)
1723  storeAppendPrintf(entry, "%s %d %s\n", name, (int) l->size, B_BYTES_STR);
1724  else
1725  storeAppendPrintf(entry, "%s none", name);
1726 
1727  dump_acl_list(entry, l->aclList);
1728 
1729  storeAppendPrintf(entry, "\n");
1730  }
1731 }
1732 
1733 static void
1735 {
1736  AclSizeLimit *l = new AclSizeLimit;
1737 
1738  parse_b_int64_t(&l->size);
1739 
1741 
1742  AclSizeLimit **tail = head; /* sane name below */
1743  while (*tail)
1744  tail = &(*tail)->next;
1745 
1746  *tail = l;
1747 }
1748 
1749 static void
1751 {
1752  delete *head;
1753  *head = NULL;
1754 }
1755 
1756 #if USE_DELAY_POOLS
1757 
1758 #include "DelayConfig.h"
1759 #include "DelayPools.h"
1760 /* do nothing - free_delay_pool_count is the magic free function.
1761  * this is why delay_pool_count isn't just marked TYPE: u_short
1762  */
1763 #define free_delay_pool_class(X)
1764 #define free_delay_pool_access(X)
1765 #define free_delay_pool_rates(X)
1766 #define dump_delay_pool_class(X, Y, Z)
1767 #define dump_delay_pool_access(X, Y, Z)
1768 #define dump_delay_pool_rates(X, Y, Z)
1769 
1770 static void
1772 {
1773  cfg->freePoolCount();
1774 }
1775 
1776 static void
1777 dump_delay_pool_count(StoreEntry * entry, const char *name, DelayConfig &cfg)
1778 {
1779  cfg.dumpPoolCount (entry, name);
1780 }
1781 
1782 static void
1784 {
1785  cfg->parsePoolCount();
1786 }
1787 
1788 static void
1790 {
1791  cfg->parsePoolClass();
1792 }
1793 
1794 static void
1796 {
1797  cfg->parsePoolRates();
1798 }
1799 
1800 static void
1802 {
1804 }
1805 
1806 #endif
1807 
1808 #if USE_DELAY_POOLS
1809 #include "ClientDelayConfig.h"
1810 /* do nothing - free_client_delay_pool_count is the magic free function.
1811  * this is why client_delay_pool_count isn't just marked TYPE: u_short
1812  */
1813 
1814 #define free_client_delay_pool_access(X)
1815 #define free_client_delay_pool_rates(X)
1816 #define dump_client_delay_pool_access(X, Y, Z)
1817 #define dump_client_delay_pool_rates(X, Y, Z)
1818 
1819 static void
1821 {
1822  cfg->freePools();
1823 }
1824 
1825 static void
1827 {
1828  cfg.dumpPoolCount (entry, name);
1829 }
1830 
1831 static void
1833 {
1834  cfg->parsePoolCount();
1835 }
1836 
1837 static void
1839 {
1840  cfg->parsePoolRates();
1841 }
1842 
1843 static void
1845 {
1847 }
1848 #endif
1849 
1850 #if USE_HTTP_VIOLATIONS
1851 static void
1852 dump_http_header_access(StoreEntry * entry, const char *name, const HeaderManglers *manglers)
1853 {
1854  if (manglers)
1855  manglers->dumpAccess(entry, name);
1856 }
1857 
1858 static void
1860 {
1861  char *t = NULL;
1862 
1863  if ((t = ConfigParser::NextToken()) == NULL) {
1864  debugs(3, DBG_CRITICAL, "" << cfg_filename << " line " << config_lineno << ": " << config_input_line);
1865  debugs(3, DBG_CRITICAL, "ERROR: parse_http_header_access: missing header name.");
1866  return;
1867  }
1868 
1869  if (!*pm)
1870  *pm = new HeaderManglers;
1871  HeaderManglers *manglers = *pm;
1872  headerMangler *mangler = manglers->track(t);
1873  assert(mangler);
1874 
1875  std::string directive = "http_header_access ";
1876  directive += t;
1877  aclParseAccessLine(directive.c_str(), LegacyParser, &mangler->access_list);
1878 }
1879 
1880 static void
1882 {
1883  // we delete the entire http_header_* mangler configuration at once
1884  if (const HeaderManglers *manglers = *pm) {
1885  delete manglers;
1886  *pm = NULL;
1887  }
1888 }
1889 
1890 static void
1891 dump_http_header_replace(StoreEntry * entry, const char *name, const HeaderManglers *manglers)
1892 {
1893  if (manglers)
1894  manglers->dumpReplacement(entry, name);
1895 }
1896 
1897 static void
1899 {
1900  char *t = NULL;
1901 
1902  if ((t = ConfigParser::NextToken()) == NULL) {
1903  debugs(3, DBG_CRITICAL, "" << cfg_filename << " line " << config_lineno << ": " << config_input_line);
1904  debugs(3, DBG_CRITICAL, "ERROR: parse_http_header_replace: missing header name.");
1905  return;
1906  }
1907 
1908  const char *value = ConfigParser::NextQuotedOrToEol();
1909 
1910  if (!*pm)
1911  *pm = new HeaderManglers;
1912  HeaderManglers *manglers = *pm;
1913  manglers->setReplacement(t, value);
1914 }
1915 
1916 #endif
1917 
1918 static void
1919 dump_cachedir(StoreEntry * entry, const char *name, const Store::DiskConfig &swap)
1920 {
1921  Store::Disks::Dump(swap, *entry, name);
1922 }
1923 
1924 static int
1926 {
1927  return s == NULL;
1928 }
1929 
1930 #if USE_AUTH
1931 static void
1933 {
1934  char *type_str = ConfigParser::NextToken();
1935  if (!type_str) {
1936  self_destruct();
1937  return;
1938  }
1939 
1940  char *param_str = ConfigParser::NextToken();
1941  if (!param_str) {
1942  self_destruct();
1943  return;
1944  }
1945 
1946  /* find a configuration for the scheme in the currently parsed configs... */
1947  Auth::SchemeConfig *schemeCfg = Auth::SchemeConfig::Find(type_str);
1948 
1949  if (schemeCfg == NULL) {
1950  /* Create a configuration based on the scheme info */
1951  Auth::Scheme::Pointer theScheme = Auth::Scheme::Find(type_str);
1952 
1953  if (theScheme == NULL) {
1954  debugs(3, DBG_CRITICAL, "ERROR: Failure while parsing Config File: Unknown authentication scheme '" << type_str << "'.");
1955  self_destruct();
1956  return;
1957  }
1958 
1959  config->push_back(theScheme->createConfig());
1960  schemeCfg = Auth::SchemeConfig::Find(type_str);
1961  if (schemeCfg == NULL) {
1962  debugs(3, DBG_CRITICAL, "Parsing Config File: Corruption configuring authentication scheme '" << type_str << "'.");
1963  self_destruct();
1964  return;
1965  }
1966  }
1967 
1968  schemeCfg->parse(schemeCfg, config->size(), param_str);
1969 }
1970 
1971 static void
1973 {
1974  /* Wipe the Auth globals and Detach/Destruct component config + state. */
1975  cfg->clear();
1976 
1977  /* on reconfigure initialize new auth schemes for the new config. */
1978  if (reconfiguring) {
1979  Auth::Init();
1980  }
1981 }
1982 
1983 static void
1984 dump_authparam(StoreEntry * entry, const char *name, Auth::ConfigVector cfg)
1985 {
1986  for (auto *scheme : cfg)
1987  scheme->dump(entry, name, scheme);
1988 }
1989 
1990 static void
1992 {
1993  const char *tok = ConfigParser::NextQuotedToken();
1994  if (!tok) {
1995  debugs(29, DBG_CRITICAL, "FATAL: auth_schemes missing the parameter");
1996  self_destruct();
1997  return;
1998  }
2000  const auto action = Acl::Answer(ACCESS_ALLOWED, Auth::TheConfig.schemeLists.size() - 1);
2001  ParseAclWithAction(authSchemes, action, "auth_schemes");
2002 }
2003 
2004 static void
2006 {
2007  Auth::TheConfig.schemeLists.clear();
2008  free_acl_access(authSchemes);
2009 }
2010 
2011 static void
2012 dump_AuthSchemes(StoreEntry *entry, const char *name, acl_access *authSchemes)
2013 {
2014  if (authSchemes)
2015  dump_SBufList(entry, authSchemes->treeDump(name, [](const Acl::Answer &action) {
2016  return Auth::TheConfig.schemeLists.at(action.kind).rawSchemes;
2017  }));
2018 }
2019 
2020 #endif /* USE_AUTH */
2021 
2022 static void
2023 ParseAclWithAction(acl_access **access, const Acl::Answer &action, const char *desc, ACL *acl)
2024 {
2025  assert(access);
2026  SBuf name;
2027  if (!*access) {
2028  *access = new Acl::Tree;
2029  name.Printf("(%s rules)", desc);
2030  (*access)->context(name.c_str(), config_input_line);
2031  }
2032  Acl::AndNode *rule = new Acl::AndNode;
2033  name.Printf("(%s rule)", desc);
2034  rule->context(name.c_str(), config_input_line);
2035  if (acl)
2036  rule->add(acl);
2037  else
2038  rule->lineParse();
2039  (*access)->add(rule, action);
2040 }
2041 
2042 static void
2044 {
2045  assert(swap);
2046  Store::Disks::Parse(*swap);
2047 }
2048 
2049 static const char *
2051 {
2052  const char * result;
2053 
2054  switch (type) {
2055 
2056  case PEER_PARENT:
2057  result = "parent";
2058  break;
2059 
2060  case PEER_SIBLING:
2061  result = "sibling";
2062  break;
2063 
2064  case PEER_MULTICAST:
2065  result = "multicast";
2066  break;
2067 
2068  default:
2069  result = "unknown";
2070  break;
2071  }
2072 
2073  return result;
2074 }
2075 
2076 static void
2077 dump_peer(StoreEntry * entry, const char *name, CachePeer * p)
2078 {
2080  LOCAL_ARRAY(char, xname, 128);
2081 
2082  while (p != NULL) {
2083  storeAppendPrintf(entry, "%s %s %s %d %d name=%s",
2084  name,
2085  p->host,
2086  neighborTypeStr(p),
2087  p->http_port,
2088  p->icp.port,
2089  p->name);
2090  dump_peer_options(entry, p);
2091 
2092  if (p->access) {
2093  snprintf(xname, 128, "cache_peer_access %s", p->name);
2094  dump_acl_access(entry, xname, p->access);
2095  }
2096 
2097  for (t = p->typelist; t; t = t->next) {
2098  storeAppendPrintf(entry, "neighbor_type_domain %s %s %s\n",
2099  p->host,
2100  peer_type_str(t->type),
2101  t->domain);
2102  }
2103 
2104  p = p->next;
2105  }
2106 }
2107 
2112 static bool
2113 isUnsignedNumeric(const char *str, size_t len)
2114 {
2115  if (len < 1) return false;
2116 
2117  for (; len >0 && *str; ++str, --len) {
2118  if (! isdigit(*str))
2119  return false;
2120  }
2121  return true;
2122 }
2123 
2128 static unsigned short
2129 GetService(const char *proto)
2130 {
2131  struct servent *port = NULL;
2133  char *token = ConfigParser::NextToken();
2134  if (token == NULL) {
2135  self_destruct();
2136  return 0; /* NEVER REACHED */
2137  }
2139  if ( !isUnsignedNumeric(token, strlen(token)) )
2140  port = getservbyname(token, proto);
2141  if (port != NULL) {
2142  return ntohs((unsigned short)port->s_port);
2143  }
2145  return xatos(token);
2146 }
2147 
2152 inline unsigned short
2154 {
2155  return GetService("tcp");
2156 }
2157 
2162 inline unsigned short
2164 {
2165  return GetService("udp");
2166 }
2167 
2168 static void
2170 {
2171  char *host_str = ConfigParser::NextToken();
2172  if (!host_str) {
2173  self_destruct();
2174  return;
2175  }
2176 
2177  char *token = ConfigParser::NextToken();
2178  if (!token) {
2179  self_destruct();
2180  return;
2181  }
2182 
2183  CachePeer *p = new CachePeer;
2184  p->host = xstrdup(host_str);
2185  Tolower(p->host);
2186  p->name = xstrdup(host_str);
2187  p->type = parseNeighborType(token);
2188 
2189  if (p->type == PEER_MULTICAST) {
2190  p->options.no_digest = true;
2191  p->options.no_netdb_exchange = true;
2192  }
2193 
2194  p->http_port = GetTcpService();
2195 
2196  if (!p->http_port) {
2197  delete p;
2198  self_destruct();
2199  return;
2200  }
2201 
2202  p->icp.port = GetUdpService();
2203 
2204  while ((token = ConfigParser::NextToken())) {
2205  if (!strcmp(token, "proxy-only")) {
2206  p->options.proxy_only = true;
2207  } else if (!strcmp(token, "no-query")) {
2208  p->options.no_query = true;
2209  } else if (!strcmp(token, "background-ping")) {
2210  p->options.background_ping = true;
2211  } else if (!strcmp(token, "no-digest")) {
2212  p->options.no_digest = true;
2213  } else if (!strcmp(token, "no-tproxy")) {
2214  p->options.no_tproxy = true;
2215  } else if (!strcmp(token, "multicast-responder")) {
2216  p->options.mcast_responder = true;
2217 #if PEER_MULTICAST_SIBLINGS
2218  } else if (!strcmp(token, "multicast-siblings")) {
2219  p->options.mcast_siblings = true;
2220 #endif
2221  } else if (!strncmp(token, "weight=", 7)) {
2222  p->weight = xatoi(token + 7);
2223  } else if (!strncmp(token, "basetime=", 9)) {
2224  p->basetime = xatoi(token + 9);
2225  } else if (!strcmp(token, "closest-only")) {
2226  p->options.closest_only = true;
2227  } else if (!strncmp(token, "ttl=", 4)) {
2228  p->mcast.ttl = xatoi(token + 4);
2229 
2230  if (p->mcast.ttl < 0)
2231  p->mcast.ttl = 0;
2232 
2233  if (p->mcast.ttl > 128)
2234  p->mcast.ttl = 128;
2235  } else if (!strcmp(token, "default")) {
2236  p->options.default_parent = true;
2237  } else if (!strcmp(token, "round-robin")) {
2238  p->options.roundrobin = true;
2239  } else if (!strcmp(token, "weighted-round-robin")) {
2240  p->options.weighted_roundrobin = true;
2241 #if USE_HTCP
2242  } else if (!strcmp(token, "htcp")) {
2243  p->options.htcp = true;
2244  } else if (!strncmp(token, "htcp=", 5) || !strncmp(token, "htcp-", 5)) {
2245  /* Note: The htcp- form is deprecated, replaced by htcp= */
2246  p->options.htcp = true;
2247  char *tmp = xstrdup(token+5);
2248  char *mode, *nextmode;
2249  for (mode = nextmode = tmp; mode; mode = nextmode) {
2250  nextmode = strchr(mode, ',');
2251  if (nextmode) {
2252  *nextmode = '\0';
2253  ++nextmode;
2254  }
2255  if (!strcmp(mode, "no-clr")) {
2256  if (p->options.htcp_only_clr)
2257  fatalf("parse_peer: can't set htcp-no-clr and htcp-only-clr simultaneously");
2258  p->options.htcp_no_clr = true;
2259  } else if (!strcmp(mode, "no-purge-clr")) {
2260  p->options.htcp_no_purge_clr = true;
2261  } else if (!strcmp(mode, "only-clr")) {
2262  if (p->options.htcp_no_clr)
2263  fatalf("parse_peer: can't set htcp no-clr and only-clr simultaneously");
2264  p->options.htcp_only_clr = true;
2265  } else if (!strcmp(mode, "forward-clr")) {
2266  p->options.htcp_forward_clr = true;
2267  } else if (!strcmp(mode, "oldsquid")) {
2268  p->options.htcp_oldsquid = true;
2269  } else {
2270  fatalf("invalid HTCP mode '%s'", mode);
2271  }
2272  }
2273  safe_free(tmp);
2274 #endif
2275  } else if (!strcmp(token, "no-netdb-exchange")) {
2276  p->options.no_netdb_exchange = true;
2277 
2278  } else if (!strcmp(token, "carp")) {
2279  if (p->type != PEER_PARENT)
2280  fatalf("parse_peer: non-parent carp peer %s/%d\n", p->host, p->http_port);
2281 
2282  p->options.carp = true;
2283  } else if (!strncmp(token, "carp-key=", 9)) {
2284  if (p->options.carp != true)
2285  fatalf("parse_peer: carp-key specified on non-carp peer %s/%d\n", p->host, p->http_port);
2286  p->options.carp_key.set = true;
2287  char *nextkey=token+strlen("carp-key="), *key=nextkey;
2288  for (; key; key = nextkey) {
2289  nextkey=strchr(key,',');
2290  if (nextkey) ++nextkey; // skip the comma, any
2291  if (0==strncmp(key,"scheme",6)) {
2292  p->options.carp_key.scheme = true;
2293  } else if (0==strncmp(key,"host",4)) {
2294  p->options.carp_key.host = true;
2295  } else if (0==strncmp(key,"port",4)) {
2296  p->options.carp_key.port = true;
2297  } else if (0==strncmp(key,"path",4)) {
2298  p->options.carp_key.path = true;
2299  } else if (0==strncmp(key,"params",6)) {
2300  p->options.carp_key.params = true;
2301  } else {
2302  fatalf("invalid carp-key '%s'",key);
2303  }
2304  }
2305  } else if (!strcmp(token, "userhash")) {
2306 #if USE_AUTH
2307  if (p->type != PEER_PARENT)
2308  fatalf("parse_peer: non-parent userhash peer %s/%d\n", p->host, p->http_port);
2309 
2310  p->options.userhash = true;
2311 #else
2312  fatalf("parse_peer: userhash requires authentication. peer %s/%d\n", p->host, p->http_port);
2313 #endif
2314  } else if (!strcmp(token, "sourcehash")) {
2315  if (p->type != PEER_PARENT)
2316  fatalf("parse_peer: non-parent sourcehash peer %s/%d\n", p->host, p->http_port);
2317 
2318  p->options.sourcehash = true;
2319 
2320  } else if (!strcmp(token, "no-delay")) {
2321 #if USE_DELAY_POOLS
2322  p->options.no_delay = true;
2323 #else
2324  debugs(0, DBG_CRITICAL, "WARNING: cache_peer option 'no-delay' requires --enable-delay-pools");
2325 #endif
2326  } else if (!strncmp(token, "login=", 6)) {
2327  p->login = xstrdup(token + 6);
2328  rfc1738_unescape(p->login);
2329  } else if (!strcmp(token, "auth-no-keytab")) {
2330  p->options.auth_no_keytab = 1;
2331  } else if (!strncmp(token, "connect-timeout=", 16)) {
2332  p->connect_timeout_raw = xatoi(token + 16);
2333  } else if (!strncmp(token, "connect-fail-limit=", 19)) {
2334  p->connect_fail_limit = xatoi(token + 19);
2335 #if USE_CACHE_DIGESTS
2336  } else if (!strncmp(token, "digest-url=", 11)) {
2337  p->digest_url = xstrdup(token + 11);
2338 #endif
2339 
2340  } else if (!strcmp(token, "allow-miss")) {
2341  p->options.allow_miss = true;
2342  } else if (!strncmp(token, "max-conn=", 9)) {
2343  p->max_conn = xatoi(token + 9);
2344  } else if (!strncmp(token, "standby=", 8)) {
2345  p->standby.limit = xatoi(token + 8);
2346  } else if (!strcmp(token, "originserver")) {
2347  p->options.originserver = true;
2348  } else if (!strncmp(token, "name=", 5)) {
2349  safe_free(p->name);
2350 
2351  if (token[5])
2352  p->name = xstrdup(token + 5);
2353  } else if (!strncmp(token, "forceddomain=", 13)) {
2354  safe_free(p->domain);
2355  if (token[13])
2356  p->domain = xstrdup(token + 13);
2357 
2358  } else if (strncmp(token, "ssl", 3) == 0) {
2359 #if !USE_OPENSSL
2360  debugs(0, DBG_CRITICAL, "WARNING: cache_peer option '" << token << "' requires --with-openssl");
2361 #else
2362  p->secure.parse(token+3);
2363 #endif
2364  } else if (strncmp(token, "tls-", 4) == 0) {
2365  p->secure.parse(token+4);
2366  } else if (strncmp(token, "tls", 3) == 0) {
2367  p->secure.parse(token+3);
2368  } else if (strcmp(token, "front-end-https") == 0) {
2369  p->front_end_https = 1;
2370  } else if (strcmp(token, "front-end-https=on") == 0) {
2371  p->front_end_https = 1;
2372  } else if (strcmp(token, "front-end-https=auto") == 0) {
2373  p->front_end_https = 2;
2374  } else if (strcmp(token, "connection-auth=off") == 0) {
2375  p->connection_auth = 0;
2376  } else if (strcmp(token, "connection-auth") == 0) {
2377  p->connection_auth = 1;
2378  } else if (strcmp(token, "connection-auth=on") == 0) {
2379  p->connection_auth = 1;
2380  } else if (strcmp(token, "connection-auth=auto") == 0) {
2381  p->connection_auth = 2;
2382  } else if (token[0] == '#') {
2383  // start of a text comment. stop reading this line.
2384  break;
2385  } else {
2386  debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "ERROR: Ignoring unknown cache_peer option '" << token << "'");
2387  }
2388  }
2389 
2390  if (peerFindByName(p->name))
2391  fatalf("ERROR: cache_peer %s specified twice\n", p->name);
2392 
2393  if (p->max_conn > 0 && p->max_conn < p->standby.limit)
2394  fatalf("ERROR: cache_peer %s max-conn=%d is lower than its standby=%d\n", p->host, p->max_conn, p->standby.limit);
2395 
2396  if (p->weight < 1)
2397  p->weight = 1;
2398 
2399  if (p->connect_fail_limit < 1)
2400  p->connect_fail_limit = 10;
2401 
2402 #if USE_CACHE_DIGESTS
2403  if (!p->options.no_digest)
2404  peerDigestCreate(p);
2405 #endif
2406 
2407  if (p->secure.encryptTransport)
2408  p->secure.parseOptions();
2409 
2410  p->index = ++Config.npeers;
2411 
2412  while (*head != NULL)
2413  head = &(*head)->next;
2414 
2415  *head = p;
2416 
2417  peerClearRRStart();
2418 }
2419 
2420 static void
2422 {
2423  delete *P;
2424  *P = NULL;
2425  Config.npeers = 0;
2426 }
2427 
2428 static void
2429 dump_cachemgrpasswd(StoreEntry * entry, const char *name, Mgr::ActionPasswordList * list)
2430 {
2431  while (list) {
2432  if (strcmp(list->passwd, "none") && strcmp(list->passwd, "disable"))
2433  storeAppendPrintf(entry, "%s XXXXXXXXXX", name);
2434  else
2435  storeAppendPrintf(entry, "%s %s", name, list->passwd);
2436 
2437  for (auto w : list->actions)
2438  entry->appendf(" " SQUIDSBUFPH, SQUIDSBUFPRINT(w));
2439 
2440  storeAppendPrintf(entry, "\n");
2441  list = list->next;
2442  }
2443 }
2444 
2445 static void
2447 {
2448  char *passwd = nullptr;
2449  parse_string(&passwd);
2450 
2452  p->passwd = passwd;
2453 
2454  while (char *token = ConfigParser::NextQuotedToken())
2455  p->actions.push_back(SBuf(token));
2456 
2458  for (P = head; *P; P = &(*P)->next) {
2459  /*
2460  * See if any of the actions from this line already have a
2461  * password from previous lines. The password checking
2462  * routines in cache_manager.c take the the password from
2463  * the first Mgr::ActionPasswordList that contains the
2464  * requested action. Thus, we should warn users who might
2465  * think they can have two passwords for the same action.
2466  */
2467  for (const auto &w : (*P)->actions) {
2468  for (const auto &u : p->actions) {
2469  if (w != u)
2470  continue;
2471 
2472  debugs(0, DBG_PARSE_NOTE(1), "ERROR: action '" << u << "' (line " << config_lineno << ") already has a password");
2473  }
2474  }
2475  }
2476 
2477  *P = p;
2478 }
2479 
2480 static void
2482 {
2483  delete *head;
2484  *head = nullptr;
2485 }
2486 
2487 static void
2488 dump_denyinfo(StoreEntry * entry, const char *name, AclDenyInfoList * var)
2489 {
2490  while (var != NULL) {
2491  storeAppendPrintf(entry, "%s %s", name, var->err_page_name);
2492 
2493  for (const auto &aclName: var->acl_list)
2494  storeAppendPrintf(entry, " " SQUIDSBUFPH, SQUIDSBUFPRINT(aclName));
2495 
2496  storeAppendPrintf(entry, "\n");
2497 
2498  var = var->next;
2499  }
2500 }
2501 
2502 static void
2504 {
2505  aclParseDenyInfoLine(var);
2506 }
2507 
2508 void
2510 {
2511  delete *list;
2512  *list = nullptr;
2513 }
2514 
2515 static void
2517 {
2518  char *host = ConfigParser::NextToken();
2519  if (!host) {
2520  self_destruct();
2521  return;
2522  }
2523 
2524  CachePeer *p = peerFindByName(host);
2525  if (!p) {
2526  debugs(15, DBG_CRITICAL, "ERROR: " << cfg_filename << ", line " << config_lineno << ": No cache_peer '" << host << "'");
2527  return;
2528  }
2529 
2530  std::string directive = "peer_access ";
2531  directive += host;
2532  aclParseAccessLine(directive.c_str(), LegacyParser, &p->access);
2533 }
2534 
2535 static void
2537 {
2538  char *host = ConfigParser::NextToken();
2539  if (!host) {
2540  self_destruct();
2541  return;
2542  }
2543 
2544  char *type = ConfigParser::NextToken();
2545  if (!type) {
2546  self_destruct();
2547  return;
2548  }
2549 
2550  char *domain = nullptr;
2551  while ((domain = ConfigParser::NextToken())) {
2552  CachePeer *p = peerFindByName(host);
2553  if (!p) {
2554  debugs(15, DBG_CRITICAL, "" << cfg_filename << ", line " << config_lineno << ": No cache_peer '" << host << "'");
2555  return;
2556  }
2557 
2558  auto *l = static_cast<NeighborTypeDomainList *>(xcalloc(1, sizeof(NeighborTypeDomainList)));
2559  l->type = parseNeighborType(type);
2560  l->domain = xstrdup(domain);
2561 
2562  NeighborTypeDomainList **L = nullptr;
2563  for (L = &(p->typelist); *L; L = &((*L)->next));
2564  *L = l;
2565  }
2566 }
2567 
2568 static void
2569 dump_int(StoreEntry * entry, const char *name, int var)
2570 {
2571  storeAppendPrintf(entry, "%s %d\n", name, var);
2572 }
2573 
2574 void
2575 parse_int(int *var)
2576 {
2577  int i;
2578  i = GetInteger();
2579  *var = i;
2580 }
2581 
2582 static void
2583 free_int(int *var)
2584 {
2585  *var = 0;
2586 }
2587 
2588 static void
2589 dump_int64_t(StoreEntry * entry, const char *name, int64_t var)
2590 {
2591  storeAppendPrintf(entry, "%s %" PRId64 "\n", name, var);
2592 }
2593 
2594 static void
2595 parse_int64_t(int64_t *var)
2596 {
2597  int64_t i;
2598  i = GetInteger64();
2599  *var = i;
2600 }
2601 
2602 static void
2603 free_int64_t(int64_t *var)
2604 {
2605  *var = 0;
2606 }
2607 
2608 static void
2609 dump_onoff(StoreEntry * entry, const char *name, int var)
2610 {
2611  storeAppendPrintf(entry, "%s %s\n", name, var ? "on" : "off");
2612 }
2613 
2614 void
2615 parse_onoff(int *var)
2616 {
2617  char *token = ConfigParser::NextToken();
2618  if (!token) {
2619  self_destruct();
2620  return;
2621  }
2622 
2623  if (!strcmp(token, "on")) {
2624  *var = 1;
2625  } else if (!strcmp(token, "enable")) {
2626  debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: 'enable' is deprecated. Please update to use 'on'.");
2627  *var = 1;
2628  } else if (!strcmp(token, "off")) {
2629  *var = 0;
2630  } else if (!strcmp(token, "disable")) {
2631  debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: 'disable' is deprecated. Please update to use 'off'.");
2632  *var = 0;
2633  } else {
2634  debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), "ERROR: Invalid option: Boolean options can only be 'on' or 'off'.");
2635  self_destruct();
2636  }
2637 }
2638 
2639 #define free_onoff free_int
2640 
2641 static void
2642 dump_tristate(StoreEntry * entry, const char *name, int var)
2643 {
2644  const char *state;
2645 
2646  if (var > 0)
2647  state = "on";
2648  else if (var < 0)
2649  state = "warn";
2650  else
2651  state = "off";
2652 
2653  storeAppendPrintf(entry, "%s %s\n", name, state);
2654 }
2655 
2656 static void
2658 {
2659  char *token = ConfigParser::NextToken();
2660  if (!token) {
2661  self_destruct();
2662  return;
2663  }
2664 
2665  if (!strcmp(token, "on")) {
2666  *var = 1;
2667  } else if (!strcmp(token, "enable")) {
2668  debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: 'enable' is deprecated. Please update to use value 'on'.");
2669  *var = 1;
2670  } else if (!strcmp(token, "warn")) {
2671  *var = -1;
2672  } else if (!strcmp(token, "off")) {
2673  *var = 0;
2674  } else if (!strcmp(token, "disable")) {
2675  debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: 'disable' is deprecated. Please update to use value 'off'.");
2676  *var = 0;
2677  } else {
2678  debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), "ERROR: Invalid option: Tristate options can only be 'on', 'off', or 'warn'.");
2679  self_destruct();
2680  }
2681 }
2682 
2683 #define free_tristate free_int
2684 
2685 static void
2687 {
2688  char *token = ConfigParser::PeekAtToken();
2689  if (!token) {
2690  self_destruct();
2691  return;
2692  }
2693 
2694  if (!strcmp(token, "on")) {
2695  debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: 'pipeline_prefetch on' is deprecated. Please update to use 1 (or a higher number).");
2696  *var = 1;
2697  //pop the token
2698  (void)ConfigParser::NextToken();
2699  } else if (!strcmp(token, "off")) {
2700  debugs(0, DBG_PARSE_NOTE(2), "WARNING: 'pipeline_prefetch off' is deprecated. Please update to use '0'.");
2701  *var = 0;
2702  //pop the token
2703  (void)ConfigParser::NextToken();
2704  } else
2705  parse_int(var);
2706 }
2707 
2708 #define free_pipelinePrefetch free_int
2709 #define dump_pipelinePrefetch dump_int
2710 
2711 static void
2712 dump_refreshpattern(StoreEntry * entry, const char *name, RefreshPattern * head)
2713 {
2714  while (head != NULL) {
2715  storeAppendPrintf(entry, "%s%s %s %d %d%% %d",
2716  name,
2717  head->pattern.flags&REG_ICASE ? " -i" : null_string,
2718  head->pattern.c_str(),
2719  (int) head->min / 60,
2720  (int) (100.0 * head->pct + 0.5),
2721  (int) head->max / 60);
2722 
2723  if (head->max_stale >= 0)
2724  storeAppendPrintf(entry, " max-stale=%d", head->max_stale);
2725 
2726  if (head->flags.refresh_ims)
2727  storeAppendPrintf(entry, " refresh-ims");
2728 
2729  if (head->flags.store_stale)
2730  storeAppendPrintf(entry, " store-stale");
2731 
2732 #if USE_HTTP_VIOLATIONS
2733 
2734  if (head->flags.override_expire)
2735  storeAppendPrintf(entry, " override-expire");
2736 
2737  if (head->flags.override_lastmod)
2738  storeAppendPrintf(entry, " override-lastmod");
2739 
2740  if (head->flags.reload_into_ims)
2741  storeAppendPrintf(entry, " reload-into-ims");
2742 
2743  if (head->flags.ignore_reload)
2744  storeAppendPrintf(entry, " ignore-reload");
2745 
2746  if (head->flags.ignore_no_store)
2747  storeAppendPrintf(entry, " ignore-no-store");
2748 
2749  if (head->flags.ignore_private)
2750  storeAppendPrintf(entry, " ignore-private");
2751 #endif
2752 
2753  storeAppendPrintf(entry, "\n");
2754 
2755  head = head->next;
2756  }
2757 }
2758 
2759 static void
2761 {
2762  char *token;
2763  char *pattern;
2764  time_t min = 0;
2765  double pct = 0.0;
2766  time_t max = 0;
2767  int refresh_ims = 0;
2768  int store_stale = 0;
2769  int max_stale = -1;
2770 
2771 #if USE_HTTP_VIOLATIONS
2772 
2773  int override_expire = 0;
2774  int override_lastmod = 0;
2775  int reload_into_ims = 0;
2776  int ignore_reload = 0;
2777  int ignore_no_store = 0;
2778  int ignore_private = 0;
2779 #endif
2780 
2781  int i;
2782  RefreshPattern *t;
2783  regex_t comp;
2784  int errcode;
2785  int flags = REG_EXTENDED | REG_NOSUB;
2786 
2787  if ((token = ConfigParser::RegexPattern()) != NULL) {
2788 
2789  if (strcmp(token, "-i") == 0) {
2790  flags |= REG_ICASE;
2791  token = ConfigParser::RegexPattern();
2792  } else if (strcmp(token, "+i") == 0) {
2793  flags &= ~REG_ICASE;
2794  token = ConfigParser::RegexPattern();
2795  }
2796 
2797  }
2798 
2799  if (token == NULL) {
2800  debugs(3, DBG_CRITICAL, "FATAL: refresh_pattern missing the regex pattern parameter");
2801  self_destruct();
2802  return;
2803  }
2804 
2805  pattern = xstrdup(token);
2806 
2807  i = GetInteger(); /* token: min */
2808 
2809  /* catch negative and insanely huge values close to 32-bit wrap */
2810  if (i < 0) {
2811  debugs(3, DBG_IMPORTANT, "WARNING: refresh_pattern minimum age negative. Cropped back to zero.");
2812  i = 0;
2813  }
2814  if (i > 60*24*365) {
2815  debugs(3, DBG_IMPORTANT, "WARNING: refresh_pattern minimum age too high. Cropped back to 1 year.");
2816  i = 60*24*365;
2817  }
2818 
2819  min = (time_t) (i * 60); /* convert minutes to seconds */
2820 
2821  pct = GetPercentage(false); /* token: pct . with no limit on size */
2822 
2823  i = GetInteger(); /* token: max */
2824 
2825  /* catch negative and insanely huge values close to 32-bit wrap */
2826  if (i < 0) {
2827  debugs(3, DBG_IMPORTANT, "WARNING: refresh_pattern maximum age negative. Cropped back to zero.");
2828  i = 0;
2829  }
2830  if (i > 60*24*365) {
2831  debugs(3, DBG_IMPORTANT, "WARNING: refresh_pattern maximum age too high. Cropped back to 1 year.");
2832  i = 60*24*365;
2833  }
2834 
2835  max = (time_t) (i * 60); /* convert minutes to seconds */
2836 
2837  /* Options */
2838  while ((token = ConfigParser::NextToken()) != NULL) {
2839  if (!strcmp(token, "refresh-ims")) {
2840  refresh_ims = 1;
2841  } else if (!strcmp(token, "store-stale")) {
2842  store_stale = 1;
2843  } else if (!strncmp(token, "max-stale=", 10)) {
2844  max_stale = xatoi(token + 10);
2845 
2846 #if USE_HTTP_VIOLATIONS
2847 
2848  } else if (!strcmp(token, "override-expire"))
2849  override_expire = 1;
2850  else if (!strcmp(token, "override-lastmod"))
2851  override_lastmod = 1;
2852  else if (!strcmp(token, "ignore-no-store"))
2853  ignore_no_store = 1;
2854  else if (!strcmp(token, "ignore-private"))
2855  ignore_private = 1;
2856  else if (!strcmp(token, "reload-into-ims")) {
2857  reload_into_ims = 1;
2859  /* tell client_side.c that this is used */
2860  } else if (!strcmp(token, "ignore-reload")) {
2861  ignore_reload = 1;
2863  /* tell client_side.c that this is used */
2864 #endif
2865 
2866  } else if (!strcmp(token, "ignore-no-cache") ||
2867  !strcmp(token, "ignore-must-revalidate") ||
2868  !strcmp(token, "ignore-auth")
2869  ) {
2870  debugs(22, DBG_PARSE_NOTE(2), "UPGRADE: refresh_pattern option '" << token << "' is obsolete. Remove it.");
2871  } else
2872  debugs(22, DBG_CRITICAL, "ERROR: refreshAddToList: Unknown option '" << pattern << "': " << token);
2873  }
2874 
2875  if ((errcode = regcomp(&comp, pattern, flags)) != 0) {
2876  char errbuf[256];
2877  regerror(errcode, &comp, errbuf, sizeof errbuf);
2878  debugs(22, DBG_CRITICAL, "" << cfg_filename << " line " << config_lineno << ": " << config_input_line);
2879  debugs(22, DBG_CRITICAL, "ERROR: refreshAddToList: Invalid regular expression '" << pattern << "': " << errbuf);
2880  xfree(pattern);
2881  return;
2882  }
2883 
2884  pct = pct < 0.0 ? 0.0 : pct;
2885  max = max < 0 ? 0 : max;
2886  t = new RefreshPattern(pattern, flags);
2887  t->pattern.regex = comp;
2888  t->min = min;
2889  t->pct = pct;
2890  t->max = max;
2891 
2892  if (refresh_ims)
2893  t->flags.refresh_ims = true;
2894 
2895  if (store_stale)
2896  t->flags.store_stale = true;
2897 
2898  t->max_stale = max_stale;
2899 
2900 #if USE_HTTP_VIOLATIONS
2901 
2902  if (override_expire)
2903  t->flags.override_expire = true;
2904 
2905  if (override_lastmod)
2906  t->flags.override_lastmod = true;
2907 
2908  if (reload_into_ims)
2909  t->flags.reload_into_ims = true;
2910 
2911  if (ignore_reload)
2912  t->flags.ignore_reload = true;
2913 
2914  if (ignore_no_store)
2915  t->flags.ignore_no_store = true;
2916 
2917  if (ignore_private)
2918  t->flags.ignore_private = true;
2919 #endif
2920 
2921  t->next = NULL;
2922 
2923  while (*head)
2924  head = &(*head)->next;
2925 
2926  *head = t;
2927 
2928  xfree(pattern);
2929 }
2930 
2931 static void
2933 {
2934  delete *head;
2935  *head = nullptr;
2936 
2937 #if USE_HTTP_VIOLATIONS
2939 
2940 #endif
2941 }
2942 
2943 static void
2944 dump_string(StoreEntry * entry, const char *name, char *var)
2945 {
2946  if (var != NULL)
2947  storeAppendPrintf(entry, "%s %s\n", name, var);
2948 }
2949 
2950 static void
2951 parse_string(char **var)
2952 {
2953  safe_free(*var);
2954 
2955  char *token = ConfigParser::NextToken();
2956  if (!token) {
2957  self_destruct();
2958  return;
2959  }
2960 
2961  *var = xstrdup(token);
2962 }
2963 
2964 static void
2965 free_string(char **var)
2966 {
2967  safe_free(*var);
2968 }
2969 
2970 void
2971 parse_eol(char *volatile *var)
2972 {
2973  if (!var) {
2974  self_destruct();
2975  return;
2976  }
2977 
2978  unsigned char *token = (unsigned char *) ConfigParser::NextQuotedOrToEol();
2979  safe_free(*var);
2980 
2981  if (!token) {
2982  self_destruct();
2983  return;
2984  }
2985 
2986  while (*token && xisspace(*token))
2987  ++token;
2988 
2989  if (!*token) {
2990  self_destruct();
2991  return;
2992  }
2993 
2994  *var = xstrdup((char *) token);
2995 }
2996 
2997 #define dump_eol dump_string
2998 #define free_eol free_string
2999 
3000 static void
3002 {
3003  safe_free(*var);
3004 
3005  char *token = ConfigParser::NextQuotedToken();
3006  if (!token) {
3007  self_destruct();
3008  return;
3009  }
3010 
3011  *var = xstrdup(token);
3012 }
3013 
3014 #define dump_TokenOrQuotedString dump_string
3015 #define free_TokenOrQuotedString free_string
3016 
3017 static void
3018 dump_time_t(StoreEntry * entry, const char *name, time_t var)
3019 {
3020  storeAppendPrintf(entry, "%s %d seconds\n", name, (int) var);
3021 }
3022 
3023 void
3024 parse_time_t(time_t * var)
3025 {
3026  const auto maxTime = std::numeric_limits<time_t>::max();
3027  const auto seconds = parseTimeLine<std::chrono::seconds>();
3028  if (maxTime < seconds.count())
3029  throw TexcHere(ToSBuf("directive supports time values up to ", maxTime, " but is given ", seconds.count(), " seconds"));
3030  *var = static_cast<time_t>(seconds.count());
3031 }
3032 
3033 static void
3034 free_time_t(time_t * var)
3035 {
3036  *var = 0;
3037 }
3038 
3039 static void
3040 dump_time_msec(StoreEntry * entry, const char *name, time_msec_t var)
3041 {
3042  if (var % 1000)
3043  storeAppendPrintf(entry, "%s %" PRId64 " milliseconds\n", name, var);
3044  else
3045  storeAppendPrintf(entry, "%s %d seconds\n", name, (int)(var/1000) );
3046 }
3047 
3048 static void
3050 {
3051  *var = parseTimeLine<std::chrono::milliseconds>().count();
3052 }
3053 
3054 static void
3056 {
3057  *var = 0;
3058 }
3059 
3060 static void
3061 dump_time_nanoseconds(StoreEntry *entry, const char *name, const std::chrono::nanoseconds &var)
3062 {
3063  // std::chrono::nanoseconds::rep is unknown a priori so we cast to (and print) the largest supported integer
3064  storeAppendPrintf(entry, "%s %jd nanoseconds\n", name, static_cast<intmax_t>(var.count()));
3065 }
3066 
3067 static void
3068 parse_time_nanoseconds(std::chrono::nanoseconds *var)
3069 {
3070  *var = parseTimeLine<std::chrono::nanoseconds>();
3071 }
3072 
3073 static void
3074 free_time_nanoseconds(std::chrono::nanoseconds *var)
3075 {
3076  *var = std::chrono::nanoseconds::zero();
3077 }
3078 
3079 static void
3080 dump_b_size_t(StoreEntry * entry, const char *name, size_t var)
3081 {
3082  storeAppendPrintf(entry, "%s %d %s\n", name, (int) var, B_BYTES_STR);
3083 }
3084 
3085 static void
3086 dump_b_ssize_t(StoreEntry * entry, const char *name, ssize_t var)
3087 {
3088  storeAppendPrintf(entry, "%s %d %s\n", name, (int) var, B_BYTES_STR);
3089 }
3090 
3091 static void
3092 dump_b_int64_t(StoreEntry * entry, const char *name, int64_t var)
3093 {
3094  storeAppendPrintf(entry, "%s %" PRId64 " %s\n", name, var, B_BYTES_STR);
3095 }
3096 
3097 static void
3098 dump_kb_int64_t(StoreEntry * entry, const char *name, int64_t var)
3099 {
3100  storeAppendPrintf(entry, "%s %" PRId64 " %s\n", name, var, B_KBYTES_STR);
3101 }
3102 
3103 static void
3104 parse_b_size_t(size_t * var)
3105 {
3107 }
3108 
3109 static void
3110 parse_b_ssize_t(ssize_t * var)
3111 {
3113 }
3114 
3115 static void
3116 parse_b_int64_t(int64_t * var)
3117 {
3119 }
3120 
3121 static void
3122 parse_kb_int64_t(int64_t * var)
3123 {
3125 }
3126 
3127 static void
3128 free_size_t(size_t * var)
3129 {
3130  *var = 0;
3131 }
3132 
3133 static void
3134 free_ssize_t(ssize_t * var)
3135 {
3136  *var = 0;
3137 }
3138 
3139 static void
3140 free_b_int64_t(int64_t * var)
3141 {
3142  *var = 0;
3143 }
3144 
3145 #define free_b_size_t free_size_t
3146 #define free_b_ssize_t free_ssize_t
3147 #define free_kb_size_t free_size_t
3148 #define free_mb_size_t free_size_t
3149 #define free_gb_size_t free_size_t
3150 #define free_kb_int64_t free_b_int64_t
3151 
3152 static void
3153 dump_u_short(StoreEntry * entry, const char *name, unsigned short var)
3154 {
3155  storeAppendPrintf(entry, "%s %d\n", name, var);
3156 }
3157 
3158 static void
3159 free_u_short(unsigned short * u)
3160 {
3161  *u = 0;
3162 }
3163 
3164 static void
3165 parse_u_short(unsigned short * var)
3166 {
3168 }
3169 
3170 void
3171 ConfigParser::ParseUShort(unsigned short *var)
3172 {
3173  *var = GetShort();
3174 }
3175 
3176 void
3178 {
3179  int i = GetInteger();
3180 
3181  if (0 == i)
3182  *var = false;
3183  else if (1 == i)
3184  *var = true;
3185  else
3186  self_destruct();
3187 }
3188 
3189 static void
3190 dump_wordlist(StoreEntry * entry, const char *name, wordlist * list)
3191 {
3192  while (list != NULL) {
3193  storeAppendPrintf(entry, "%s %s\n", name, list->key);
3194  list = list->next;
3195  }
3196 }
3197 
3198 void
3200 {
3201  parse_wordlist(list);
3202 }
3203 
3204 void
3206 {
3207  char *token;
3208  while ((token = ConfigParser::NextQuotedToken()))
3209  wordlistAdd(list, token);
3210 }
3211 
3212 static int
3214 {
3215  return a == NULL;
3216 }
3217 
3218 #define free_wordlist wordlistDestroy
3219 
3220 #define free_uri_whitespace free_int
3221 
3222 static void
3224 {
3225  char *token = ConfigParser::NextToken();
3226  if (!token) {
3227  self_destruct();
3228  return;
3229  }
3230 
3231  if (!strcmp(token, "strip"))
3232  *var = URI_WHITESPACE_STRIP;
3233  else if (!strcmp(token, "deny"))
3234  *var = URI_WHITESPACE_DENY;
3235  else if (!strcmp(token, "allow"))
3236  *var = URI_WHITESPACE_ALLOW;
3237  else if (!strcmp(token, "encode"))
3238  *var = URI_WHITESPACE_ENCODE;
3239  else if (!strcmp(token, "chop"))
3240  *var = URI_WHITESPACE_CHOP;
3241  else {
3242  debugs(0, DBG_PARSE_NOTE(2), "ERROR: Invalid option '" << token << "': 'uri_whitespace' accepts 'strip', 'deny', 'allow', 'encode', and 'chop'.");
3243  self_destruct();
3244  }
3245 }
3246 
3247 static void
3248 dump_uri_whitespace(StoreEntry * entry, const char *name, int var)
3249 {
3250  const char *s;
3251 
3252  if (var == URI_WHITESPACE_ALLOW)
3253  s = "allow";
3254  else if (var == URI_WHITESPACE_ENCODE)
3255  s = "encode";
3256  else if (var == URI_WHITESPACE_CHOP)
3257  s = "chop";
3258  else if (var == URI_WHITESPACE_DENY)
3259  s = "deny";
3260  else
3261  s = "strip";
3262 
3263  storeAppendPrintf(entry, "%s %s\n", name, s);
3264 }
3265 
3266 static void
3268 {
3269  if (!*settings)
3270  return;
3271 
3272  free_string(&(*settings)->type);
3273 
3274  free_wordlist(&(*settings)->args);
3275 
3276  delete *settings;
3277 
3278  *settings = NULL;
3279 }
3280 
3281 static void
3283 {
3284  if (*settings)
3285  free_removalpolicy(settings);
3286 
3287  *settings = new RemovalPolicySettings;
3288 
3289  parse_string(&(*settings)->type);
3290 
3291  parse_wordlist(&(*settings)->args);
3292 }
3293 
3294 static void
3295 dump_removalpolicy(StoreEntry * entry, const char *name, RemovalPolicySettings * settings)
3296 {
3297  wordlist *args;
3298  storeAppendPrintf(entry, "%s %s", name, settings->type);
3299  args = settings->args;
3300 
3301  while (args) {
3302  storeAppendPrintf(entry, " %s", args->key);
3303  args = args->next;
3304  }
3305 
3306  storeAppendPrintf(entry, "\n");
3307 }
3308 
3309 inline void
3311 {}
3312 
3313 static void
3315 {
3316  int value = 0;
3317  parse_onoff(&value);
3318  option->configure(value > 0);
3319 }
3320 
3321 static void
3322 dump_YesNoNone(StoreEntry * entry, const char *name, YesNoNone &option)
3323 {
3324  if (option.configured())
3325  dump_onoff(entry, name, option ? 1 : 0);
3326 }
3327 
3328 static void
3330 {}
3331 
3332 static void
3334 {
3335  char *token = ConfigParser::NextToken();
3336  if (!token) {
3337  self_destruct();
3338  return;
3339  }
3340 
3341  if (strcmp(token, "always") == 0) {
3344  } else if (strcmp(token, "disk") == 0) {
3347  } else if (strncmp(token, "net", 3) == 0) {
3350  } else if (strcmp(token, "never") == 0) {
3353  } else {
3354  debugs(0, DBG_PARSE_NOTE(2), "ERROR: Invalid option '" << token << "': 'memory_cache_mode' accepts 'always', 'disk', 'network', and 'never'.");
3355  self_destruct();
3356  }
3357 }
3358 
3359 static void
3360 dump_memcachemode(StoreEntry * entry, const char *name, SquidConfig &)
3361 {
3362  storeAppendPrintf(entry, "%s ", name);
3364  storeAppendPrintf(entry, "always");
3366  storeAppendPrintf(entry, "disk");
3368  storeAppendPrintf(entry, "network");
3370  storeAppendPrintf(entry, "none");
3371  storeAppendPrintf(entry, "\n");
3372 }
3373 
3374 #include "cf_parser.cci"
3375 
3376 peer_t
3377 parseNeighborType(const char *s)
3378 {
3379  if (!strcmp(s, "parent"))
3380  return PEER_PARENT;
3381 
3382  if (!strcmp(s, "neighbor"))
3383  return PEER_SIBLING;
3384 
3385  if (!strcmp(s, "neighbour"))
3386  return PEER_SIBLING;
3387 
3388  if (!strcmp(s, "sibling"))
3389  return PEER_SIBLING;
3390 
3391  if (!strcmp(s, "multicast"))
3392  return PEER_MULTICAST;
3393 
3394  debugs(15, DBG_CRITICAL, "WARNING: Unknown neighbor type: " << s);
3395 
3396  return PEER_SIBLING;
3397 }
3398 
3399 #if USE_WCCPv2
3400 static void
3402 {
3403  char *token;
3405  Ip::Address ipa;
3406 
3407  while ((token = ConfigParser::NextToken())) {
3408  if (GetHostWithPort(token, &ipa)) {
3409 
3410  while (*head)
3411  head = &(*head)->next;
3412 
3413  s = static_cast<Ip::Address_list *>(xcalloc(1, sizeof(*s)));
3414  s->s = ipa;
3415 
3416  *head = s;
3417  } else {
3418  self_destruct();
3419  return;
3420  }
3421  }
3422 }
3423 
3424 static void
3425 dump_IpAddress_list(StoreEntry * e, const char *n, const Ip::Address_list * s)
3426 {
3427  char ntoabuf[MAX_IPSTRLEN];
3428 
3429  while (s) {
3430  storeAppendPrintf(e, "%s %s\n",
3431  n,
3432  s->s.toStr(ntoabuf,MAX_IPSTRLEN));
3433  s = s->next;
3434  }
3435 }
3436 
3437 static void
3439 {
3440  if (*head) delete *head;
3441  *head = NULL;
3442 }
3443 
3444 #if CURRENTLY_UNUSED
3445 /* This code was previously used by http_port. Left as it really should
3446  * be used by icp_port and htcp_port
3447  */
3448 static int
3449 check_null_IpAddress_list(const Ip::Address_list * s)
3450 {
3451  return NULL == s;
3452 }
3453 
3454 #endif /* CURRENTLY_UNUSED */
3455 #endif /* USE_WCCPv2 */
3456 
3457 static void
3459 {
3460  char *host = NULL;
3461  unsigned short port = 0;
3462  char *t = NULL;
3463  char *junk = NULL;
3464 
3465  s->disable_pmtu_discovery = DISABLE_PMTU_OFF;
3466  s->name = xstrdup(token);
3467  s->connection_auth_disabled = false;
3468 
3469  const SBuf &portType = AnyP::UriScheme(s->transport.protocol).image();
3470 
3471  if (*token == '[') {
3472  /* [ipv6]:port */
3473  host = token + 1;
3474  t = strchr(host, ']');
3475  if (!t) {
3476  debugs(3, DBG_CRITICAL, "FATAL: " << portType << "_port: missing ']' on IPv6 address: " << token);
3477  self_destruct();
3478  return;
3479  }
3480  *t = '\0';
3481  ++t;
3482  if (*t != ':') {
3483  debugs(3, DBG_CRITICAL, "FATAL: " << portType << "_port: missing Port in: " << token);
3484  self_destruct();
3485  return;
3486  }
3487  if (!Ip::EnableIpv6) {
3488  debugs(3, DBG_CRITICAL, "FATAL: " << portType << "_port: IPv6 is not available.");
3489  self_destruct();
3490  return;
3491  }
3492  port = xatos(t + 1);
3493  } else if ((t = strchr(token, ':'))) {
3494  /* host:port */
3495  /* ipv4:port */
3496  host = token;
3497  *t = '\0';
3498  port = xatos(t + 1);
3499 
3500  } else if (strtol(token, &junk, 10) && !*junk) {
3501  port = xatos(token);
3502  debugs(3, 3, portType << "_port: found Listen on Port: " << port);
3503  } else {
3504  debugs(3, DBG_CRITICAL, "FATAL: " << portType << "_port: missing Port: " << token);
3505  self_destruct();
3506  return;
3507  }
3508 
3509  if (port == 0 && host != NULL) {
3510  debugs(3, DBG_CRITICAL, "FATAL: " << portType << "_port: Port cannot be 0: " << token);
3511  self_destruct();
3512  return;
3513  }
3514 
3515  if (NULL == host) {
3516  s->s.setAnyAddr();
3517  s->s.port(port);
3518  if (!Ip::EnableIpv6)
3519  s->s.setIPv4();
3520  debugs(3, 3, portType << "_port: found Listen on wildcard address: *:" << s->s.port());
3521  } else if ( (s->s = host) ) { /* check/parse numeric IPA */
3522  s->s.port(port);
3523  if (!Ip::EnableIpv6)
3524  s->s.setIPv4();
3525  debugs(3, 3, portType << "_port: Listen on Host/IP: " << host << " --> " << s->s);
3526  } else if ( s->s.GetHostByName(host) ) { /* check/parse for FQDN */
3527  /* do not use ipcache */
3528  s->defaultsite = xstrdup(host);
3529  s->s.port(port);
3530  if (!Ip::EnableIpv6)
3531  s->s.setIPv4();
3532  debugs(3, 3, portType << "_port: found Listen as Host " << s->defaultsite << " on IP: " << s->s);
3533  } else {
3534  debugs(3, DBG_CRITICAL, "FATAL: " << portType << "_port: failed to resolve Host/IP: " << host);
3535  self_destruct();
3536  }
3537 }
3538 
3542 static AnyP::ProtocolVersion
3544 {
3545  // HTTP/1.0 not supported because we are version 1.1 which contains a superset of 1.0
3546  // and RFC 2616 requires us to upgrade 1.0 to 1.1
3547  if (value.cmp("HTTP") == 0 || value.cmp("HTTP/1.1") == 0)
3548  return Http::ProtocolVersion(1,1);
3549 
3550  if (value.cmp("HTTPS") == 0 || value.cmp("HTTPS/1.1") == 0)
3552 
3553  if (value.cmp("FTP") == 0)
3554  return Ftp::ProtocolVersion();
3555 
3556  fatalf("%s directive does not support protocol=" SQUIDSBUFPH "\n", cfg_directive, SQUIDSBUFPRINT(value));
3557  return AnyP::ProtocolVersion(); // not reached
3558 }
3559 
3560 static void
3562 {
3563  /* modes first */
3564 
3565  if (strcmp(token, "accel") == 0) {
3566  if (s->flags.isIntercepted()) {
3567  debugs(3, DBG_CRITICAL, "FATAL: " << cfg_directive << ": Accelerator mode requires its own port. It cannot be shared with other modes.");
3568  self_destruct();
3569  return;
3570  }
3571  s->flags.accelSurrogate = true;
3572  s->vhost = true;
3573  } else if (strcmp(token, "transparent") == 0 || strcmp(token, "intercept") == 0) {
3574  if (s->flags.accelSurrogate || s->flags.tproxyIntercept) {
3575  debugs(3, DBG_CRITICAL, "FATAL: " << cfg_directive << ": Intercept mode requires its own interception port. It cannot be shared with other modes.");
3576  self_destruct();
3577  return;
3578  }
3579  s->flags.natIntercept = true;
3581  /* Log information regarding the port modes under interception. */
3582  debugs(3, DBG_IMPORTANT, "Starting Authentication on port " << s->s);
3583  debugs(3, DBG_IMPORTANT, "Disabling Authentication on port " << s->s << " (interception enabled)");
3584  } else if (strcmp(token, "tproxy") == 0) {
3585  if (s->flags.natIntercept || s->flags.accelSurrogate) {
3586  debugs(3,DBG_CRITICAL, "FATAL: " << cfg_directive << ": TPROXY option requires its own interception port. It cannot be shared with other modes.");
3587  self_destruct();
3588  return;
3589  }
3590  s->flags.tproxyIntercept = true;
3592  /* Log information regarding the port modes under transparency. */
3593  debugs(3, DBG_IMPORTANT, "Disabling Authentication on port " << s->s << " (TPROXY enabled)");
3594 
3595  if (s->flags.proxySurrogate) {
3596  debugs(3, DBG_IMPORTANT, "Disabling TPROXY Spoofing on port " << s->s << " (require-proxy-header enabled)");
3597  }
3598 
3599  if (!Ip::Interceptor.ProbeForTproxy(s->s)) {
3600  debugs(3, DBG_CRITICAL, "FATAL: " << cfg_directive << ": TPROXY support in the system does not work.");
3601  self_destruct();
3602  return;
3603  }
3604 
3605  } else if (strcmp(token, "require-proxy-header") == 0) {
3606  s->flags.proxySurrogate = true;
3607  if (s->flags.tproxyIntercept) {
3608  // receiving is still permitted, so we do not unset the TPROXY flag
3609  // spoofing access control override takes care of the spoof disable later
3610  debugs(3, DBG_IMPORTANT, "Disabling TPROXY Spoofing on port " << s->s << " (require-proxy-header enabled)");
3611  }
3612 
3613  } else if (strncmp(token, "defaultsite=", 12) == 0) {
3614  if (!s->flags.accelSurrogate) {
3615  debugs(3, DBG_CRITICAL, "FATAL: " << cfg_directive << ": defaultsite option requires Acceleration mode flag.");
3616  self_destruct();
3617  return;
3618  }
3619  safe_free(s->defaultsite);
3620  s->defaultsite = xstrdup(token + 12);
3621  } else if (strcmp(token, "vhost") == 0) {
3622  if (!s->flags.accelSurrogate) {
3623  debugs(3, DBG_CRITICAL, "WARNING: " << cfg_directive << ": vhost option is deprecated. Use 'accel' mode flag instead.");
3624  }
3625  s->flags.accelSurrogate = true;
3626  s->vhost = true;
3627  } else if (strcmp(token, "no-vhost") == 0) {
3628  if (!s->flags.accelSurrogate) {
3629  debugs(3, DBG_IMPORTANT, "ERROR: " << cfg_directive << ": no-vhost option requires Acceleration mode flag.");
3630  }
3631  s->vhost = false;
3632  } else if (strcmp(token, "vport") == 0) {
3633  if (!s->flags.accelSurrogate) {
3634  debugs(3, DBG_CRITICAL, "FATAL: " << cfg_directive << ": vport option requires Acceleration mode flag.");
3635  self_destruct();
3636  return;
3637  }
3638  s->vport = -1;
3639  } else if (strncmp(token, "vport=", 6) == 0) {
3640  if (!s->flags.accelSurrogate) {
3641  debugs(3, DBG_CRITICAL, "FATAL: " << cfg_directive << ": vport option requires Acceleration mode flag.");
3642  self_destruct();
3643  return;
3644  }
3645  s->vport = xatos(token + 6);
3646  } else if (strncmp(token, "protocol=", 9) == 0) {
3647  if (!s->flags.accelSurrogate) {
3648  debugs(3, DBG_CRITICAL, "FATAL: " << cfg_directive << ": protocol option requires Acceleration mode flag.");
3649  self_destruct();
3650  return;
3651  }
3652  s->transport = parsePortProtocol(ToUpper(SBuf(token + 9)));
3653  } else if (strcmp(token, "allow-direct") == 0) {
3654  if (!s->flags.accelSurrogate) {
3655  debugs(3, DBG_CRITICAL, "FATAL: " << cfg_directive << ": allow-direct option requires Acceleration mode flag.");
3656  self_destruct();
3657  return;
3658  }
3659  s->allow_direct = true;
3660  } else if (strcmp(token, "act-as-origin") == 0) {
3661  if (!s->flags.accelSurrogate) {
3662  debugs(3, DBG_IMPORTANT, "ERROR: " << cfg_directive << ": act-as-origin option requires Acceleration mode flag.");
3663  } else
3664  s->actAsOrigin = true;
3665  } else if (strcmp(token, "ignore-cc") == 0) {
3666 #if !USE_HTTP_VIOLATIONS
3667  if (!s->flags.accelSurrogate) {
3668  debugs(3, DBG_CRITICAL, "FATAL: " << cfg_directive << ": ignore-cc option requires Acceleration mode flag.");
3669  self_destruct();
3670  return;
3671  }
3672 #endif
3673  s->ignore_cc = true;
3674  } else if (strncmp(token, "name=", 5) == 0) {
3675  safe_free(s->name);
3676  s->name = xstrdup(token + 5);
3677  } else if (strcmp(token, "no-connection-auth") == 0) {
3678  s->connection_auth_disabled = true;
3679  } else if (strcmp(token, "connection-auth=off") == 0) {
3680  s->connection_auth_disabled = true;
3681  } else if (strcmp(token, "connection-auth") == 0) {
3682  s->connection_auth_disabled = false;
3683  } else if (strcmp(token, "connection-auth=on") == 0) {
3684  s->connection_auth_disabled = false;
3685  } else if (strncmp(token, "disable-pmtu-discovery=", 23) == 0) {
3686  if (!strcmp(token + 23, "off"))
3687  s->disable_pmtu_discovery = DISABLE_PMTU_OFF;
3688  else if (!strcmp(token + 23, "transparent"))
3689  s->disable_pmtu_discovery = DISABLE_PMTU_TRANSPARENT;
3690  else if (!strcmp(token + 23, "always"))
3691  s->disable_pmtu_discovery = DISABLE_PMTU_ALWAYS;
3692  else {
3693  self_destruct();
3694  return;
3695  }
3696  } else if (strcmp(token, "ipv4") == 0) {
3697  if ( !s->s.setIPv4() ) {
3698  debugs(3, DBG_CRITICAL, "FATAL: " << cfg_directive << ": IPv6 addresses cannot be used as IPv4-Only. " << s->s );
3699  self_destruct();
3700  return;
3701  }
3702  } else if (strcmp(token, "tcpkeepalive") == 0) {
3703  s->tcp_keepalive.enabled = true;
3704  } else if (strncmp(token, "tcpkeepalive=", 13) == 0) {
3705  char *t = token + 13;
3706  s->tcp_keepalive.enabled = true;
3707  s->tcp_keepalive.idle = xatoui(t,',');
3708  t = strchr(t, ',');
3709  if (t) {
3710  ++t;
3711  s->tcp_keepalive.interval = xatoui(t,',');
3712  t = strchr(t, ',');
3713  }
3714  if (t) {
3715  ++t;
3716  s->tcp_keepalive.timeout = xatoui(t);
3717  }
3718 #if USE_OPENSSL
3719  } else if (strcmp(token, "sslBump") == 0) {
3720  debugs(3, DBG_PARSE_NOTE(1), "WARNING: '" << token << "' is deprecated " <<
3721  "in " << cfg_directive << ". Use 'ssl-bump' instead.");
3722  s->flags.tunnelSslBumping = true;
3723  } else if (strcmp(token, "ssl-bump") == 0) {
3724  s->flags.tunnelSslBumping = true;
3725  } else if (strncmp(token, "cert=", 5) == 0) {
3726  s->secure.parse(token);
3727  } else if (strncmp(token, "key=", 4) == 0) {
3728  s->secure.parse(token);
3729  } else if (strncmp(token, "version=", 8) == 0) {
3730  debugs(3, DBG_PARSE_NOTE(1), "WARNING: UPGRADE: '" << token << "' is deprecated " <<
3731  "in " << cfg_directive << ". Use 'options=' instead.");
3732  s->secure.parse(token);
3733  } else if (strncmp(token, "options=", 8) == 0) {
3734  s->secure.parse(token);
3735  } else if (strncmp(token, "cipher=", 7) == 0) {
3736  s->secure.parse(token);
3737  } else if (strncmp(token, "clientca=", 9) == 0) {
3738  s->secure.parse(token);
3739  } else if (strncmp(token, "cafile=", 7) == 0) {
3740  debugs(3, DBG_PARSE_NOTE(1), "WARNING: UPGRADE: '" << token << "' is deprecated " <<
3741  "in " << cfg_directive << ". Use 'tls-cafile=' instead.");
3742  s->secure.parse(token);
3743  } else if (strncmp(token, "capath=", 7) == 0) {
3744  s->secure.parse(token);
3745  } else if (strncmp(token, "crlfile=", 8) == 0) {
3746  s->secure.parse(token);
3747  } else if (strncmp(token, "dhparams=", 9) == 0) {
3748  debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: '" << token << "' is deprecated " <<
3749  "in " << cfg_directive << ". Use 'tls-dh=' instead.");
3750  s->secure.parse(token);
3751  } else if (strncmp(token, "sslflags=", 9) == 0) {
3752  // NP: deprecation warnings output by secure.parse() when relevant
3753  s->secure.parse(token+3);
3754  } else if (strncmp(token, "sslcontext=", 11) == 0) {
3755  // NP: deprecation warnings output by secure.parse() when relevant
3756  s->secure.parse(token+3);
3757  } else if (strncmp(token, "generate-host-certificates", 26) == 0) {
3758  s->secure.parse(token);
3759 #endif
3760  } else if (strncmp(token, "dynamic_cert_mem_cache_size=", 28) == 0) {
3761  s->secure.parse(token);
3762  } else if (strncmp(token, "tls-", 4) == 0) {
3763  s->secure.parse(token+4);
3764  } else if (strcmp(token, "ftp-track-dirs") == 0) {
3765  s->ftp_track_dirs = true;
3766  } else if (strcmp(token, "worker-queues") == 0) {
3767 #if !defined(SO_REUSEADDR)
3768 #error missing system #include that #defines SO_* constants
3769 #endif
3770 #if !defined(SO_REUSEPORT)
3771  throw TexcHere(ToSBuf(cfg_directive, ' ', token, " option requires building Squid where SO_REUSEPORT is supported by the TCP stack"));
3772 #endif
3773  s->workerQueues = true;
3774  } else {
3775  debugs(3, DBG_CRITICAL, "FATAL: Unknown " << cfg_directive << " option '" << token << "'.");
3776  self_destruct();
3777  }
3778 }
3779 
3780 void
3781 add_http_port(char *portspec)
3782 {
3784  s->transport = parsePortProtocol(SBuf("HTTP"));
3785  parsePortSpecification(s, portspec);
3786  // we may need to merge better if the above returns a list with clones
3787  assert(s->next == NULL);
3788  s->next = HttpPortList;
3789  HttpPortList = s;
3790 }
3791 
3792 static void
3793 parsePortCfg(AnyP::PortCfgPointer *head, const char *optionName)
3794 {
3795  SBuf protoName;
3796  if (strcmp(optionName, "http_port") == 0 ||
3797  strcmp(optionName, "ascii_port") == 0)
3798  protoName = "HTTP";
3799  else if (strcmp(optionName, "https_port") == 0)
3800  protoName = "HTTPS";
3801  else if (strcmp(optionName, "ftp_port") == 0)
3802  protoName = "FTP";
3803  if (protoName.isEmpty()) {
3804  self_destruct();
3805  return;
3806  }
3807 
3808  char *token = ConfigParser::NextToken();
3809 
3810  if (!token) {
3811  self_destruct();
3812  return;
3813  }
3814 
3816  s->transport = parsePortProtocol(protoName); // default; protocol=... overwrites
3817  parsePortSpecification(s, token);
3818 
3819  /* parse options ... */
3820  while ((token = ConfigParser::NextToken())) {
3821  parse_port_option(s, token);
3822  }
3823 
3824  s->secure.syncCaFiles();
3825 
3826  if (s->transport.protocol == AnyP::PROTO_HTTPS) {
3827  s->secure.encryptTransport = true;
3828 #if USE_OPENSSL
3829  /* ssl-bump on https_port configuration requires either tproxy or intercept, and vice versa */
3830  const bool hijacked = s->flags.isIntercepted();
3831  if (s->flags.tunnelSslBumping && !hijacked) {
3832  debugs(3, DBG_CRITICAL, "FATAL: ssl-bump on https_port requires tproxy/intercept which is missing.");
3833  self_destruct();
3834  return;
3835  }
3836  if (hijacked && !s->flags.tunnelSslBumping) {
3837  debugs(3, DBG_CRITICAL, "FATAL: tproxy/intercept on https_port requires ssl-bump which is missing.");
3838  self_destruct();
3839  return;
3840  }
3841 #endif
3842  if (s->flags.proxySurrogate) {
3843  debugs(3,DBG_CRITICAL, "FATAL: https_port: require-proxy-header option is not supported on HTTPS ports.");
3844  self_destruct();
3845  return;
3846  }
3847  } else if (protoName.cmp("FTP") == 0) {
3848  /* ftp_port does not support ssl-bump */
3849  if (s->flags.tunnelSslBumping) {
3850  debugs(3, DBG_CRITICAL, "FATAL: ssl-bump is not supported for ftp_port.");
3851  self_destruct();
3852  return;
3853  }
3854  if (s->flags.proxySurrogate) {
3855  // Passive FTP data channel does not work without deep protocol inspection in the frontend.
3856  debugs(3,DBG_CRITICAL, "FATAL: require-proxy-header option is not supported on ftp_port.");
3857  self_destruct();
3858  return;
3859  }
3860  }
3861 
3862  if (s->secure.encryptTransport) {
3863  if (s->secure.certs.empty()) {
3864  debugs(3, DBG_CRITICAL, "FATAL: " << AnyP::UriScheme(s->transport.protocol) << "_port requires a cert= parameter");
3865  self_destruct();
3866  return;
3867  }
3868  s->secure.parseOptions();
3869  }
3870 
3871  // *_port line should now be fully valid so we can clone it if necessary
3872  if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && s->s.isAnyAddr()) {
3873  // clone the port options from *s to *(s->next)
3874  s->next = s->clone();
3875  s->next->s.setIPv4();
3876  debugs(3, 3, AnyP::UriScheme(s->transport.protocol).image() << "_port: clone wildcard address for split-stack: " << s->s << " and " << s->next->s);
3877  }
3878 
3879  while (*head != NULL)
3880  head = &((*head)->next);
3881 
3882  *head = s;
3883 }
3884 
3885 static void
3887 {
3888  char buf[MAX_IPSTRLEN];
3889 
3890  storeAppendPrintf(e, "%s %s",
3891  n,
3892  s->s.toUrl(buf,MAX_IPSTRLEN));
3893 
3894  // MODES and specific sub-options.
3895  if (s->flags.natIntercept)
3896  storeAppendPrintf(e, " intercept");
3897 
3898  else if (s->flags.tproxyIntercept)
3899  storeAppendPrintf(e, " tproxy");
3900 
3901  else if (s->flags.proxySurrogate)
3902  storeAppendPrintf(e, " require-proxy-header");
3903 
3904  else if (s->flags.accelSurrogate) {
3905  storeAppendPrintf(e, " accel");
3906 
3907  if (s->vhost)
3908  storeAppendPrintf(e, " vhost");
3909 
3910  if (s->vport < 0)
3911  storeAppendPrintf(e, " vport");
3912  else if (s->vport > 0)
3913  storeAppendPrintf(e, " vport=%d", s->vport);
3914 
3915  if (s->defaultsite)
3916  storeAppendPrintf(e, " defaultsite=%s", s->defaultsite);
3917 
3918  // TODO: compare against prefix of 'n' instead of assuming http_port
3919  if (s->transport.protocol != AnyP::PROTO_HTTP)
3920  storeAppendPrintf(e, " protocol=%s", AnyP::ProtocolType_str[s->transport.protocol]);
3921 
3922  if (s->allow_direct)
3923  storeAppendPrintf(e, " allow-direct");
3924 
3925  if (s->ignore_cc)
3926  storeAppendPrintf(e, " ignore-cc");
3927 
3928  }
3929 
3930  // Generic independent options
3931 
3932  if (s->name)
3933  storeAppendPrintf(e, " name=%s", s->name);
3934 
3935 #if USE_HTTP_VIOLATIONS
3936  if (!s->flags.accelSurrogate && s->ignore_cc)
3937  storeAppendPrintf(e, " ignore-cc");
3938 #endif
3939 
3940  if (s->connection_auth_disabled)
3941  storeAppendPrintf(e, " connection-auth=off");
3942  else
3943  storeAppendPrintf(e, " connection-auth=on");
3944 
3945  if (s->disable_pmtu_discovery != DISABLE_PMTU_OFF) {
3946  const char *pmtu;
3947 
3948  if (s->disable_pmtu_discovery == DISABLE_PMTU_ALWAYS)
3949  pmtu = "always";
3950  else
3951  pmtu = "transparent";
3952 
3953  storeAppendPrintf(e, " disable-pmtu-discovery=%s", pmtu);
3954  }
3955 
3956  if (s->s.isAnyAddr() && !s->s.isIPv6())
3957  storeAppendPrintf(e, " ipv4");
3958 
3959  if (s->tcp_keepalive.enabled) {
3960  if (s->tcp_keepalive.idle || s->tcp_keepalive.interval || s->tcp_keepalive.timeout) {
3961  storeAppendPrintf(e, " tcpkeepalive=%d,%d,%d", s->tcp_keepalive.idle, s->tcp_keepalive.interval, s->tcp_keepalive.timeout);
3962  } else {
3963  storeAppendPrintf(e, " tcpkeepalive");
3964  }
3965  }
3966 
3967 #if USE_OPENSSL
3968  if (s->flags.tunnelSslBumping)
3969  storeAppendPrintf(e, " ssl-bump");
3970 #endif
3971 
3972  s->secure.dumpCfg(e, "tls-");
3973 }
3974 
3975 static void
3976 dump_PortCfg(StoreEntry * e, const char *n, const AnyP::PortCfgPointer &s)
3977 {
3978  for (AnyP::PortCfgPointer p = s; p != NULL; p = p->next) {
3979  dump_generic_port(e, n, p);
3980  storeAppendPrintf(e, "\n");
3981  }
3982 }
3983 
3984 void
3986 {
3987  free_all();
3988  Config.ssl_client.sslContext.reset();
3989 #if USE_OPENSSL
3991 #endif
3992 }
3993 
3994 void
3995 requirePathnameExists(const char *name, const char *path)
3996 {
3997 
3998  struct stat sb;
3999  char pathbuf[BUFSIZ];
4000  assert(path != NULL);
4001 
4002  if (Config.chroot_dir && (geteuid() == 0)) {
4003  snprintf(pathbuf, BUFSIZ, "%s/%s", Config.chroot_dir, path);
4004  path = pathbuf;
4005  }
4006 
4007  if (stat(path, &sb) < 0) {
4008  int xerrno = errno;
4009  debugs(0, DBG_CRITICAL, (opt_parse_cfg_only?"FATAL: ":"ERROR: ") << name << " " << path << ": " << xstrerr(xerrno));
4010  // keep going to find more issues if we are only checking the config file with "-k parse"
4011  if (opt_parse_cfg_only)
4012  return;
4013  // this is fatal if it is found during startup or reconfigure
4014  if (opt_send_signal == -1 || opt_send_signal == SIGHUP)
4015  fatalf("%s %s: %s", name, path, xstrerr(xerrno));
4016  }
4017 }
4018 
4019 #include "AccessLogEntry.h"
4020 
4043 static void
4045 {
4046  const char *filename = ConfigParser::NextToken();
4047  if (!filename) {
4048  self_destruct();
4049  return;
4050  }
4051 
4052  const auto cl = new CustomLog();
4053 
4054  cl->filename = xstrdup(filename);
4055 
4056  if (strcmp(filename, "none") == 0) {
4057  cl->type = Log::Format::CLF_NONE;
4058  aclParseAclList(LegacyParser, &cl->aclList, filename);
4059  while (*logs)
4060  logs = &(*logs)->next;
4061  *logs = cl;
4062  return;
4063  }
4064 
4065  const char *token = ConfigParser::PeekAtToken();
4066  if (token && !strchr(token, '=')) { // style #3
4067  // TODO: Deprecate this style to avoid this dangerous guessing.
4068  if (Log::TheConfig.knownFormat(token)) {
4069  cl->setLogformat(token);
4070  (void)ConfigParser::NextToken(); // consume the token used above
4071  } else {
4072  // assume there is no explicit logformat name and use the default
4073  cl->setLogformat("squid");
4074  }
4075  } else { // style #1 or style #4
4076  // TODO: Drop deprecated style #1 support. We already warn about it, and
4077  // its exceptional treatment makes detecting "module" typos impractical!
4078  cl->parseOptions(LegacyParser, "squid");
4079  }
4080  assert(cl->type); // setLogformat() was called
4081 
4082  aclParseAclList(LegacyParser, &cl->aclList, cl->filename);
4083 
4084  while (*logs)
4085  logs = &(*logs)->next;
4086 
4087  *logs = cl;
4088 }
4089 
4090 static int
4091 check_null_access_log(CustomLog *customlog_definitions)
4092 {
4093  return customlog_definitions == NULL;
4094 }
4095 
4096 static void
4097 dump_access_log(StoreEntry * entry, const char *name, CustomLog * logs)
4098 {
4099  assert(entry);
4100  for (auto log = logs; log; log = log->next) {
4101  {
4102  PackableStream os(*entry);
4103  os << name; // directive name
4104  os << ' ' << log->filename; // including "none"
4105  log->dumpOptions(os);
4106  }
4107 
4108  if (log->aclList)
4109  dump_acl_list(entry, log->aclList);
4110 
4111  storeAppendPrintf(entry, "\n");
4112  }
4113 }
4114 
4115 static void
4117 {
4118  while (*definitions) {
4119  CustomLog *log = *definitions;
4120  *definitions = log->next;
4121  delete log;
4122  }
4123 }
4124 
4125 #if HAVE_CPU_AFFINITY /* until somebody else needs this general code */
4126 static bool
4128 parseNamedIntList(const char *data, const String &name, std::vector<int> &list)
4129 {
4130  if (data && (strncmp(data, name.rawBuf(), name.size()) == 0)) {
4131  data += name.size();
4132  if (*data == '=') {
4133  while (true) {
4134  ++data;
4135  int value = 0;
4136  if (!StringToInt(data, value, &data, 10))
4137  break;
4138  list.push_back(value);
4139  if (*data == '\0' || *data != ',')
4140  break;
4141  }
4142  }
4143  }
4144  return data && *data == '\0';
4145 }
4146 #endif
4147 
4148 static void
4149 parse_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap)
4150 {
4151 #if !HAVE_CPU_AFFINITY
4152  (void)cpuAffinityMap;
4153  debugs(3, DBG_CRITICAL, "FATAL: Squid built with no CPU affinity " <<
4154  "support, do not set 'cpu_affinity_map'");
4155  self_destruct();
4156 
4157 #else /* HAVE_CPU_AFFINITY */
4158  if (!*cpuAffinityMap)
4159  *cpuAffinityMap = new CpuAffinityMap;
4160 
4161  const char *const pToken = ConfigParser::NextToken();
4162  const char *const cToken = ConfigParser::NextToken();
4163  std::vector<int> processes, cores;
4164  if (!parseNamedIntList(pToken, "process_numbers", processes)) {
4165  debugs(3, DBG_CRITICAL, "FATAL: bad 'process_numbers' parameter " <<
4166  "in 'cpu_affinity_map'");
4167  self_destruct();
4168  } else if (!parseNamedIntList(cToken, "cores", cores)) {
4169  debugs(3, DBG_CRITICAL, "FATAL: bad 'cores' parameter in " <<
4170  "'cpu_affinity_map'");
4171  self_destruct();
4172  } else if (!(*cpuAffinityMap)->add(processes, cores)) {
4173  debugs(3, DBG_CRITICAL, "FATAL: bad 'cpu_affinity_map'; " <<
4174  "process_numbers and cores lists differ in length or " <<
4175  "contain numbers <= 0");
4176  self_destruct();
4177  }
4178 #endif
4179 }
4180 
4181 static void
4182 dump_CpuAffinityMap(StoreEntry *const entry, const char *const name, const CpuAffinityMap *const cpuAffinityMap)
4183 {
4184  if (cpuAffinityMap) {
4185  storeAppendPrintf(entry, "%s process_numbers=", name);
4186  for (size_t i = 0; i < cpuAffinityMap->processes().size(); ++i) {
4187  storeAppendPrintf(entry, "%s%i", (i ? "," : ""),
4188  cpuAffinityMap->processes()[i]);
4189  }
4190  storeAppendPrintf(entry, " cores=");
4191  for (size_t i = 0; i < cpuAffinityMap->cores().size(); ++i) {
4192  storeAppendPrintf(entry, "%s%i", (i ? "," : ""),
4193  cpuAffinityMap->cores()[i]);
4194  }
4195  storeAppendPrintf(entry, "\n");
4196  }
4197 }
4198 
4199 static void
4200 free_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap)
4201 {
4202  delete *cpuAffinityMap;
4203  *cpuAffinityMap = NULL;
4204 }
4205 
4206 #if USE_ADAPTATION
4207 
4208 static void
4210 {
4212 }
4213 
4214 static void
4216 {
4218 }
4219 
4220 static void
4222 {
4224 }
4225 #endif /* USE_ADAPTATION */
4226 
4227 #if ICAP_CLIENT
4228 
4229 static void
4231 {
4232  cfg->parseService();
4233 }
4234 
4235 static void
4237 {
4238  cfg->freeService();
4239 }
4240 
4241 static void
4242 dump_icap_service_type(StoreEntry * entry, const char *name, const Adaptation::Icap::Config &cfg)
4243 {
4244  cfg.dumpService(entry, name);
4245 }
4246 
4247 static void
4249 {
4250  debugs(93, DBG_CRITICAL, "WARNING: 'icap_class' is deprecated. " <<
4251  "Use 'adaptation_service_set' instead");
4253 }
4254 
4255 static void
4257 {
4258  debugs(93, DBG_CRITICAL, "WARNING: 'icap_access' is deprecated. " <<
4259  "Use 'adaptation_access' instead");
4261 }
4262 
4263 #endif
4264 
4265 #if USE_ECAP
4266 
4267 static void
4269 {
4270  cfg->parseService();
4271 }
4272 
4273 static void
4275 {
4276  cfg->freeService();
4277 }
4278 
4279 static void
4280 dump_ecap_service_type(StoreEntry * entry, const char *name, const Adaptation::Ecap::Config &cfg)
4281 {
4282  cfg.dumpService(entry, name);
4283 }
4284 
4285 #endif /* USE_ECAP */
4286 
4287 #if ICAP_CLIENT
4289 {
4290  char *token;
4292 
4293  if ((token = ConfigParser::NextToken()) == NULL)
4294  return;
4295 
4296  if (strcmp(token,"in") != 0) {
4297  debugs(3, DBG_CRITICAL, "expecting 'in' on'" << config_input_line << "'");
4298  self_destruct();
4299  return;
4300  }
4301 
4303 }
4304 
4305 static void dump_icap_service_failure_limit(StoreEntry *entry, const char *name, const Adaptation::Icap::Config &cfg)
4306 {
4307  storeAppendPrintf(entry, "%s %d", name, cfg.service_failure_limit);
4308  if (cfg.oldest_service_failure > 0) {
4309  storeAppendPrintf(entry, " in %d seconds", (int)cfg.oldest_service_failure);
4310  }
4311  storeAppendPrintf(entry, "\n");
4312 }
4313 
4315 {
4316  cfg->oldest_service_failure = 0;
4317  cfg->service_failure_limit = 0;
4318 }
4319 #endif
4320 
4321 #if USE_OPENSSL
4323 {
4324  char *al;
4326  if ((al = ConfigParser::NextToken()) == NULL) {
4327  xfree(ca);
4328  self_destruct();
4329  return;
4330  }
4331 
4332  const char *param;
4333  if ( char *s = strchr(al, '{')) {
4334  *s = '\0'; // terminate the al string
4335  ++s;
4336  param = s;
4337  s = strchr(s, '}');
4338  if (!s) {
4339  xfree(ca);
4340  self_destruct();
4341  return;
4342  }
4343  *s = '\0';
4344  } else
4345  param = NULL;
4346 
4347  if (strcmp(al, Ssl::CertAdaptAlgorithmStr[Ssl::algSetValidAfter]) == 0) {
4348  ca->alg = Ssl::algSetValidAfter;
4349  ca->param = xstrdup("on");
4350  } else if (strcmp(al, Ssl::CertAdaptAlgorithmStr[Ssl::algSetValidBefore]) == 0) {
4352  ca->param = xstrdup("on");
4353  } else if (strcmp(al, Ssl::CertAdaptAlgorithmStr[Ssl::algSetCommonName]) == 0) {
4354  ca->alg = Ssl::algSetCommonName;
4355  if (param) {
4356  if (strlen(param) > 64) {
4357  debugs(3, DBG_CRITICAL, "FATAL: sslproxy_cert_adapt: setCommonName{" <<param << "} : using common name longer than 64 bytes is not supported");
4358  xfree(ca);
4359  self_destruct();
4360  return;
4361  }
4362  ca->param = xstrdup(param);
4363  }
4364  } else {
4365  debugs(3, DBG_CRITICAL, "FATAL: sslproxy_cert_adapt: unknown cert adaptation algorithm: " << al);
4366  xfree(ca);
4367  self_destruct();
4368  return;
4369  }
4370 
4372 
4373  while (*cert_adapt)
4374  cert_adapt = &(*cert_adapt)->next;
4375 
4376  *cert_adapt = ca;
4377 }
4378 
4379 static void dump_sslproxy_cert_adapt(StoreEntry *entry, const char *name, sslproxy_cert_adapt *cert_adapt)
4380 {
4381  for (sslproxy_cert_adapt *ca = cert_adapt; ca != NULL; ca = ca->next) {
4382  storeAppendPrintf(entry, "%s ", name);
4383  storeAppendPrintf(entry, "%s{%s} ", Ssl::sslCertAdaptAlgoritm(ca->alg), ca->param);
4384  if (ca->aclList)
4385  dump_acl_list(entry, ca->aclList);
4386  storeAppendPrintf(entry, "\n");
4387  }
4388 }
4389 
4391 {
4392  while (*cert_adapt) {
4393  sslproxy_cert_adapt *ca = *cert_adapt;
4394  *cert_adapt = ca->next;
4395  safe_free(ca->param);
4396 
4397  if (ca->aclList)
4398  aclDestroyAclList(&ca->aclList);
4399 
4400  safe_free(ca);
4401  }
4402 }
4403 
4405 {
4406  char *al;
4408  if ((al = ConfigParser::NextToken()) == NULL) {
4409  xfree(cs);
4410  self_destruct();
4411  return;
4412  }
4413 
4414  if (strcmp(al, Ssl::CertSignAlgorithmStr[Ssl::algSignTrusted]) == 0)
4415  cs->alg = Ssl::algSignTrusted;
4416  else if (strcmp(al, Ssl::CertSignAlgorithmStr[Ssl::algSignUntrusted]) == 0)
4417  cs->alg = Ssl::algSignUntrusted;
4418  else if (strcmp(al, Ssl::CertSignAlgorithmStr[Ssl::algSignSelf]) == 0)
4419  cs->alg = Ssl::algSignSelf;
4420  else {
4421  debugs(3, DBG_CRITICAL, "FATAL: sslproxy_cert_sign: unknown cert signing algorithm: " << al);
4422  xfree(cs);
4423  self_destruct();
4424  return;
4425  }
4426 
4428 
4429  while (*cert_sign)
4430  cert_sign = &(*cert_sign)->next;
4431 
4432  *cert_sign = cs;
4433 }
4434 
4435 static void dump_sslproxy_cert_sign(StoreEntry *entry, const char *name, sslproxy_cert_sign *cert_sign)
4436 {
4437  sslproxy_cert_sign *cs;
4438  for (cs = cert_sign; cs != NULL; cs = cs->next) {
4439  storeAppendPrintf(entry, "%s ", name);
4440  storeAppendPrintf(entry, "%s ", Ssl::certSignAlgorithm(cs->alg));
4441  if (cs->aclList)
4442  dump_acl_list(entry, cs->aclList);
4443  storeAppendPrintf(entry, "\n");
4444  }
4445 }
4446 
4448 {
4449  while (*cert_sign) {
4450  sslproxy_cert_sign *cs = *cert_sign;
4451  *cert_sign = cs->next;
4452 
4453  if (cs->aclList)
4454  aclDestroyAclList(&cs->aclList);
4455 
4456  safe_free(cs);
4457  }
4458 }
4459 
4461 {
4462 public:
4464  /* RegisteredRunner API */
4465  virtual void finalizeConfig();
4466 };
4467 
4469 
4471 
4472 void
4474 {
4477  static char buf[1024];
4479  strcpy(buf, "ssl_bump deny all");
4480  debugs(3, DBG_CRITICAL, "WARNING: auto-converting deprecated implicit "
4481  "\"ssl_bump deny all\" to \"ssl_bump none all\". New ssl_bump configurations "
4482  "must not use implicit rules. Update your ssl_bump rules.");
4483  } else {
4484  strcpy(buf, "ssl_bump allow all");
4485  debugs(3, DBG_CRITICAL, "SECURITY NOTICE: auto-converting deprecated implicit "
4486  "\"ssl_bump allow all\" to \"ssl_bump client-first all\" which is usually "
4487  "inferior to the newer server-first bumping mode. New ssl_bump"
4488  " configurations must not use implicit rules. Update your ssl_bump rules.");
4489  }
4490  parse_line(buf);
4491  }
4492 }
4493 
4494 static void parse_sslproxy_ssl_bump(acl_access **ssl_bump)
4495 {
4496  typedef const char *BumpCfgStyle;
4497  BumpCfgStyle bcsNone = NULL;
4498  BumpCfgStyle bcsNew = "new client/server-first/none";
4499  BumpCfgStyle bcsOld = "deprecated allow/deny";
4500  static BumpCfgStyle bumpCfgStyleLast = bcsNone;
4501  BumpCfgStyle bumpCfgStyleNow = bcsNone;
4502  char *bm;
4503  if ((bm = ConfigParser::NextToken()) == NULL) {
4504  self_destruct();
4505  return;
4506  }
4507 
4508  // if this is the first rule processed
4509  if (*ssl_bump == NULL) {
4510  bumpCfgStyleLast = bcsNone;
4512  }
4513 
4515 
4516  if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpClientFirst]) == 0) {
4518  bumpCfgStyleNow = bcsNew;
4519  } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpServerFirst]) == 0) {
4521  bumpCfgStyleNow = bcsNew;
4522  } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpPeek]) == 0) {
4523  action.kind = Ssl::bumpPeek;
4524  bumpCfgStyleNow = bcsNew;
4525  } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpStare]) == 0) {
4526  action.kind = Ssl::bumpStare;
4527  bumpCfgStyleNow = bcsNew;
4528  } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpSplice]) == 0) {
4529  action.kind = Ssl::bumpSplice;
4530  bumpCfgStyleNow = bcsNew;
4531  } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpBump]) == 0) {
4532  action.kind = Ssl::bumpBump;
4533  bumpCfgStyleNow = bcsNew;
4534  } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpTerminate]) == 0) {
4535  action.kind = Ssl::bumpTerminate;
4536  bumpCfgStyleNow = bcsNew;
4537  } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpNone]) == 0) {
4538  action.kind = Ssl::bumpNone;
4539  bumpCfgStyleNow = bcsNew;
4540  } else if (strcmp(bm, "allow") == 0) {
4541  debugs(3, DBG_CRITICAL, "SECURITY NOTICE: auto-converting deprecated "
4542  "\"ssl_bump allow <acl>\" to \"ssl_bump client-first <acl>\" which "
4543  "is usually inferior to the newer server-first "
4544  "bumping mode. Update your ssl_bump rules.");
4546  bumpCfgStyleNow = bcsOld;
4548  } else if (strcmp(bm, "deny") == 0) {
4549  debugs(3, DBG_CRITICAL, "WARNING: auto-converting deprecated "
4550  "\"ssl_bump deny <acl>\" to \"ssl_bump none <acl>\". Update "
4551  "your ssl_bump rules.");
4552  action.kind = Ssl::bumpNone;
4553  bumpCfgStyleNow = bcsOld;
4555  } else {
4556  debugs(3, DBG_CRITICAL, "FATAL: unknown ssl_bump mode: " << bm);
4557  self_destruct();
4558  return;
4559  }
4560 
4561  if (bumpCfgStyleLast != bcsNone && bumpCfgStyleNow != bumpCfgStyleLast) {
4562  debugs(3, DBG_CRITICAL, "FATAL: do not mix " << bumpCfgStyleNow << " actions with " <<
4563  bumpCfgStyleLast << " actions. Update your ssl_bump rules.");
4564  self_destruct();
4565  return;
4566  }
4567 
4568  bumpCfgStyleLast = bumpCfgStyleNow;
4569 
4570  // empty rule OK
4571  ParseAclWithAction(ssl_bump, action, "ssl_bump");
4572 }
4573 
4574 static void dump_sslproxy_ssl_bump(StoreEntry *entry, const char *name, acl_access *ssl_bump)
4575 {
4576  if (ssl_bump)
4577  dump_SBufList(entry, ssl_bump->treeDump(name, [](const Acl::Answer &action) {
4578  return Ssl::BumpModeStr.at(action.kind);
4579  }));
4580 }
4581 
4582 static void free_sslproxy_ssl_bump(acl_access **ssl_bump)
4583 {
4584  free_acl_access(ssl_bump);
4585 }
4586 
4587 #endif
4588 
4589 static void dump_HeaderWithAclList(StoreEntry * entry, const char *name, HeaderWithAclList *headers)
4590 {
4591  if (!headers)
4592  return;
4593 
4594  for (HeaderWithAclList::iterator hwa = headers->begin(); hwa != headers->end(); ++hwa) {
4595  storeAppendPrintf(entry, "%s %s %s", name, hwa->fieldName.c_str(), hwa->fieldValue.c_str());
4596  if (hwa->aclList)
4597  dump_acl_list(entry, hwa->aclList);
4598  storeAppendPrintf(entry, "\n");
4599  }
4600 }
4601 
4603 {
4604  char *fn;
4605  if (!*headers) {
4606  *headers = new HeaderWithAclList;
4607  }
4608  if ((fn = ConfigParser::NextToken()) == NULL) {
4609  self_destruct();
4610  return;
4611  }
4612  HeaderWithAcl hwa;
4613  hwa.fieldName = fn;
4615  if (hwa.fieldId == Http::HdrType::BAD_HDR)
4617 
4618  Format::Format *nlf = new ::Format::Format("hdrWithAcl");
4622  hwa.fieldValue = buf.termedBuf();
4624  if (hwa.quoted) {
4625  if (!nlf->parse(hwa.fieldValue.c_str())) {
4626  self_destruct();
4627  return;
4628  }
4629  hwa.valueFormat = nlf;
4630  } else
4631  delete nlf;
4632  aclParseAclList(LegacyParser, &hwa.aclList, (hwa.fieldName + ':' + hwa.fieldValue).c_str());
4633  (*headers)->push_back(hwa);
4634 }
4635 
4637 {
4638  if (!(*header))
4639  return;
4640 
4641  for (HeaderWithAclList::iterator hwa = (*header)->begin(); hwa != (*header)->end(); ++hwa) {
4642  if (hwa->aclList)
4643  aclDestroyAclList(&hwa->aclList);
4644 
4645  if (hwa->valueFormat) {
4646  delete hwa->valueFormat;
4647  hwa->valueFormat = NULL;
4648  }
4649  }
4650  delete *header;
4651  *header = NULL;
4652 }
4653 
4654 static void parse_note(Notes *notes)
4655 {
4656  assert(notes);
4657  notes->parse(LegacyParser);
4658 }
4659 
4660 static void dump_note(StoreEntry *entry, const char *name, Notes &notes)
4661 {
4662  notes.dump(entry, name);
4663 }
4664 
4665 static void free_note(Notes *notes)
4666 {
4667  notes->clean();
4668 }
4669 
4670 static DebugMessageId ParseDebugMessageId(const char *value, const char eov)
4671 {
4672  const auto id = xatoui(value, eov);
4673  if (!(0 < id && id < DebugMessageIdUpperBound))
4674  throw TextException(ToSBuf("unknown cache_log_message ID: ", value), Here());
4675  return static_cast<DebugMessageId>(id);
4676 }
4677 
4678 static void parse_cache_log_message(DebugMessages **debugMessages)
4679 {
4680  DebugMessage msg;
4681  DebugMessageId minId = 0;
4682  DebugMessageId maxId = 0;
4683 
4684  char *key = nullptr;
4685  char *value = nullptr;
4686  while (ConfigParser::NextKvPair(key, value)) {
4687  if (strcmp(key, "id") == 0) {
4688  if (minId > 0)
4689  break;
4690  minId = maxId = ParseDebugMessageId(value, '\0');
4691  } else if (strcmp(key, "ids") == 0) {
4692  if (minId > 0)
4693  break;
4694  const auto dash = strchr(value, '-');
4695  if (!dash)
4696  throw TextException(ToSBuf("malformed cache_log_message ID range: ", key, '=', value), Here());
4697  minId = ParseDebugMessageId(value, '-');
4698  maxId = ParseDebugMessageId(dash+1, '\0');
4699  if (minId > maxId)
4700  throw TextException(ToSBuf("invalid cache_log_message ID range: ", key, '=', value), Here());
4701  } else if (strcmp(key, "level") == 0) {
4702  if (msg.levelled())
4703  break;
4704  const auto level = xatoi(value);
4705  if (level < 0)
4706  throw TextException(ToSBuf("negative cache_log_message level: ", value), Here());
4707  msg.level = level;
4708  } else if (strcmp(key, "limit") == 0) {
4709  if (msg.limited())
4710  break;
4711  msg.limit = xatoull(value, 10);
4712  } else {
4713  throw TextException(ToSBuf("unsupported cache_log_message option: ", key), Here());
4714  }
4715  key = value = nullptr;
4716  }
4717 
4718  if (key && value)
4719  throw TextException(ToSBuf("repeated or conflicting cache_log_message option: ", key, '=', value), Here());
4720 
4721  if (!minId)
4722  throw TextException("cache_log_message is missing a required id=... or ids=... option", Here());
4723 
4724  if (!(msg.levelled() || msg.limited()))
4725  throw TextException("cache_log_message is missing a required level=... or limit=... option", Here());
4726 
4727  assert(debugMessages);
4728  if (!*debugMessages)
4729  *debugMessages = new DebugMessages();
4730 
4731  for (auto id = minId; id <= maxId; ++id) {
4732  msg.id = id;
4733  (*debugMessages)->messages.at(id) = msg;
4734  }
4735 }
4736 
4737 static void dump_cache_log_message(StoreEntry *entry, const char *name, const DebugMessages *debugMessages)
4738 {
4739  if (!debugMessages)
4740  return;
4741 
4742  SBufStream out;
4743  for (const auto &msg: debugMessages->messages) {
4744  if (!msg.configured())
4745  continue;
4746  out << name << " id=" << msg.id;
4747  if (msg.levelled())
4748  out << " level=" << msg.level;
4749  if (msg.limited())
4750  out << " limit=" << msg.limit;
4751  out << "\n";
4752  }
4753  const auto buf = out.buf();
4754  entry->append(buf.rawContent(), buf.length()); // may be empty
4755 }
4756 
4757 static void free_cache_log_message(DebugMessages **debugMessages)
4758 {
4759  // clear old messages to avoid cumulative effect across (re)configurations
4760  assert(debugMessages);
4761  delete *debugMessages;
4762  *debugMessages = nullptr;
4763 }
4764 
4765 static bool FtpEspvDeprecated = false;
4766 static void parse_ftp_epsv(acl_access **ftp_epsv)
4767 {
4768  Acl::Answer ftpEpsvDeprecatedAction;
4769  bool ftpEpsvIsDeprecatedRule = false;
4770 
4771  char *t = ConfigParser::PeekAtToken();
4772  if (!t) {
4773  self_destruct();
4774  return;
4775  }
4776 
4777  if (!strcmp(t, "off")) {
4778  (void)ConfigParser::NextToken();
4779  ftpEpsvIsDeprecatedRule = true;
4780  ftpEpsvDeprecatedAction = Acl::Answer(ACCESS_DENIED);
4781  } else if (!strcmp(t, "on")) {
4782  (void)ConfigParser::NextToken();
4783  ftpEpsvIsDeprecatedRule = true;
4784  ftpEpsvDeprecatedAction = Acl::Answer(ACCESS_ALLOWED);
4785  }
4786 
4787  // Check for mixing "ftp_epsv on|off" and "ftp_epsv allow|deny .." rules:
4788  // 1) if this line is "ftp_epsv allow|deny ..." and already exist rules of "ftp_epsv on|off"
4789  // 2) if this line is "ftp_epsv on|off" and already exist rules of "ftp_epsv allow|deny ..."
4790  // then abort
4791  if ((!ftpEpsvIsDeprecatedRule && FtpEspvDeprecated) ||
4792  (ftpEpsvIsDeprecatedRule && !FtpEspvDeprecated && *ftp_epsv != NULL)) {
4793  debugs(3, DBG_CRITICAL, "FATAL: do not mix \"ftp_epsv on|off\" cfg lines with \"ftp_epsv allow|deny ...\" cfg lines. Update your ftp_epsv rules.");
4794  self_destruct();
4795  return;
4796  }
4797 
4798  if (ftpEpsvIsDeprecatedRule) {
4799  // overwrite previous ftp_epsv lines
4800  delete *ftp_epsv;
4801  *ftp_epsv = nullptr;
4802 
4803  if (ftpEpsvDeprecatedAction == Acl::Answer(ACCESS_DENIED)) {
4804  if (ACL *a = ACL::FindByName("all"))
4805  ParseAclWithAction(ftp_epsv, ftpEpsvDeprecatedAction, "ftp_epsv", a);
4806  else {
4807  self_destruct();
4808  return;
4809  }
4810  }
4811  FtpEspvDeprecated = true;
4812  } else {
4814  }
4815 }
4816 
4817 static void dump_ftp_epsv(StoreEntry *entry, const char *name, acl_access *ftp_epsv)
4818 {
4819  if (ftp_epsv)
4820  dump_SBufList(entry, ftp_epsv->treeDump(name, Acl::AllowOrDeny));
4821 }
4822 
4823 static void free_ftp_epsv(acl_access **ftp_epsv)
4824 {
4825  free_acl_access(ftp_epsv);
4826  FtpEspvDeprecated = false;
4827 }
4828 
4831 static std::chrono::seconds
4833 {
4834  const auto timeValueToken = ConfigParser::NextToken();
4835  if (!timeValueToken)
4836  throw TexcHere("cannot read a time value");
4837 
4838  using Seconds = std::chrono::seconds;
4839 
4840  const auto parsedTimeValue = xatof(timeValueToken);
4841 
4842  if (parsedTimeValue == 0)
4843  return std::chrono::seconds::zero();
4844 
4845  std::chrono::nanoseconds parsedUnitDuration;
4846 
4847  const auto unitToken = ConfigParser::PeekAtToken();
4848  if (parseTimeUnit<Seconds>(unitToken, parsedUnitDuration))
4849  (void)ConfigParser::NextToken();
4850  else {
4851  const auto defaultParsed = parseTimeUnit<Seconds>(T_SECOND_STR, parsedUnitDuration);
4852  assert(defaultParsed);
4854  ": WARNING: missing time unit, using deprecated default '" << T_SECOND_STR << "'");
4855  }
4856 
4857  const auto nanoseconds = ToNanoSeconds(parsedTimeValue, parsedUnitDuration);
4858 
4859  return FromNanoseconds<Seconds>(nanoseconds, parsedTimeValue);
4860 }
4861 
4862 static void
4864 {
4865  // TODO: do not allow optional timeunit (as the documentation prescribes)
4866  // and use parseTimeLine() instead.
4868 
4869  char *key, *value;
4870  while(ConfigParser::NextKvPair(key, value)) {
4871  if (strcasecmp(key, "on_timeout") == 0) {
4872  if (strcasecmp(value, "bypass") == 0)
4873  config->action = toutActBypass;
4874  else if (strcasecmp(value, "fail") == 0)
4875  config->action = toutActFail;
4876  else if (strcasecmp(value, "retry") == 0)
4877  config->action = toutActRetry;
4878  else if (strcasecmp(value, "use_configured_response") == 0) {
4880  } else {
4881  debugs(3, DBG_CRITICAL, "FATAL: unsupported \"on_timeout\" action: " << value);
4882  self_destruct();
4883  return;
4884  }
4885  } else if (strcasecmp(key, "response") == 0) {
4886  config->response = xstrdup(value);
4887  } else {
4888  debugs(3, DBG_CRITICAL, "FATAL: unsupported option " << key);
4889  self_destruct();
4890  return;
4891  }
4892  }
4893 
4894  if (config->action == toutActUseConfiguredResponse && !config->response) {
4895  debugs(3, DBG_CRITICAL, "FATAL: Expected 'response=' option after 'on_timeout=use_configured_response' option");
4896  self_destruct();
4897  }
4898 
4899  if (config->action != toutActUseConfiguredResponse && config->response) {
4900  debugs(3, DBG_CRITICAL, "FATAL: 'response=' option is valid only when used with the 'on_timeout=use_configured_response' option");
4901  self_destruct();
4902  }
4903 }
4904 
4905 static void
4907 {
4908  const char *onTimedOutActions[] = {"bypass", "fail", "retry", "use_configured_response"};
4909  assert(config.action >= 0 && config.action <= toutActUseConfiguredResponse);
4910 
4911  dump_time_t(entry, name, Config.Timeout.urlRewrite);
4912  storeAppendPrintf(entry, " on_timeout=%s", onTimedOutActions[config.action]);
4913 
4914  if (config.response)
4915  storeAppendPrintf(entry, " response=\"%s\"", config.response);
4916 
4917  storeAppendPrintf(entry, "\n");
4918 }
4919 
4920 static void
4922 {
4924  config->action = 0;
4925  safe_free(config->response);
4926 }
4927 
4928 static void
4930 {
4931  int val = 0;
4932  parse_onoff(&val);
4933 
4934  // If quoted values is set to on then enable new strict mode parsing
4935  if (val) {
4937  ConfigParser::StrictMode = true;
4938  } else {
4940  ConfigParser::StrictMode = false;
4941  }
4942 }
4943 
4944 static void
4945 dump_configuration_includes_quoted_values(StoreEntry *const entry, const char *const name, bool)
4946 {
4947  int val = ConfigParser::RecognizeQuotedValues ? 1 : 0;
4948  dump_onoff(entry, name, val);
4949 }
4950 
4951 static void
4953 {
4955  ConfigParser::StrictMode = false;
4956 }
4957 
4958 static void
4960 {
4961  char *tm;
4962  if ((tm = ConfigParser::NextToken()) == NULL) {
4963  self_destruct();
4964  return;
4965  }
4966 
4968  if (strcmp(tm, "tunnel") == 0)
4969  action.kind = 1;
4970  else if (strcmp(tm, "respond") == 0)
4971  action.kind = 2;
4972  else {
4973  debugs(3, DBG_CRITICAL, "FATAL: unknown on_unsupported_protocol mode: " << tm);
4974  self_destruct();
4975  return;
4976  }
4977 
4978  // empty rule OK
4979  ParseAclWithAction(access, action, "on_unsupported_protocol");
4980 }
4981 
4982 static void
4983 dump_on_unsupported_protocol(StoreEntry *entry, const char *name, acl_access *access)
4984 {
4985  static const std::vector<const char *> onErrorTunnelMode = {
4986  "none",
4987  "tunnel",
4988  "respond"
4989  };
4990  if (access) {
4991  SBufList lines = access->treeDump(name, [](const Acl::Answer &action) {
4992  return onErrorTunnelMode.at(action.kind);
4993  });
4994  dump_SBufList(entry, lines);
4995  }
4996 }
4997 
4998 static void
5000 {
5001  free_acl_access(access);
5002 }
5003 
5004 static void
5006 {
5007  assert(protoGuardsPtr);
5008  auto &protoGuards = *protoGuardsPtr;
5009  if (!protoGuards)
5010  protoGuards = new HttpUpgradeProtocolAccess();
5011  protoGuards->configureGuard(LegacyParser);
5012 }
5013 
5014 static void
5016 {
5017  if (!protoGuards)
5018  return;
5019 
5020  const SBuf name(rawName);
5021  protoGuards->forEach([entry,&name](const SBuf &proto, const acl_access *acls) {
5022  SBufList line;
5023  line.push_back(name);
5024  line.push_back(proto);
5025  const auto acld = acls->treeDump("", &Acl::AllowOrDeny);
5026  line.insert(line.end(), acld.begin(), acld.end());
5027  dump_SBufList(entry, line);
5028  });
5029 }
5030 
5031 static void
5033 {
5034  assert(protoGuardsPtr);
5035  auto &protoGuards = *protoGuardsPtr;
5036  delete protoGuards;
5037  protoGuards = nullptr;
5038 }
5039 
char * errorDirectory
Definition: SquidConfig.h:436
static peer_t parseNeighborType(const char *s)
Definition: cache_cf.cc:3377
acl_access * access_list
static void parse_http_header_access(HeaderManglers **manglers)
Definition: cache_cf.cc:1859
bool proxy_only
Definition: CachePeer.h:82
static void dump_wordlist(StoreEntry *entry, const char *name, wordlist *list)
Definition: cache_cf.cc:3190
void fatal(const char *message)
Definition: fatal.cc:28
const char * xstrerr(int error)
Definition: xstrerror.cc:83
bool no_query
Definition: CachePeer.h:83
#define URI_WHITESPACE_ENCODE
Definition: defines.h:135
@ bumpPeek
Definition: support.h:126
static void parse_adaptation_access_type()
Definition: cache_cf.cc:4221
static void dump_access_log(StoreEntry *entry, const char *name, CustomLog *definitions)
Definition: cache_cf.cc:4097
void parse_int(int *var)
Definition: cache_cf.cc:2575
static const char * FindStatement(const char *line, const char *statement)
Definition: cache_cf.cc:376
static void parsePortSpecification(const AnyP::PortCfgPointer &s, char *token)
Definition: cache_cf.cc:3458
static void dump_icap_service_type(StoreEntry *, const char *, const Adaptation::Icap::Config &)
Definition: cache_cf.cc:4242
bool htcp_no_clr
Definition: CachePeer.h:94
time_t period
Definition: SquidConfig.h:234
static void dump_time_msec(StoreEntry *entry, const char *name, time_msec_t var)
Definition: cache_cf.cc:3040
static bool action(int fd, size_t metasize, const char *fn, const char *url, const SquidMetaList &meta)
Definition: purge.cc:311
int connection_auth
0 - off, 1 - on, 2 - auto
Definition: CachePeer.h:195
@ PEER_MULTICAST
Definition: enums.h:31
virtual bool active() const =0
static void ParseAccess(ConfigParser &parser)
Definition: Config.cc:278
void * xcalloc(size_t n, size_t sz)
Definition: xalloc.cc:71
#define Here()
source code location of the caller
Definition: Here.h:15
void wordlistDestroy(wordlist **list)
destroy a wordlist
Definition: wordlist.cc:16
bool background_ping
Definition: CachePeer.h:84
static std::chrono::nanoseconds ToNanoSeconds(const double value, const std::chrono::nanoseconds &unit)
Definition: cache_cf.cc:1185
static SBuf CurrentLocation()
static void parse_TokenOrQuotedString(char **var)
Definition: cache_cf.cc:3001
CachePeer * peerFindByName(const char *name)
Definition: neighbors.cc:1133
int opt_send_signal
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
Definition: Packable.h:61
struct SquidConfig::@97 Timeout
RunnerRegistrationEntry(sslBumpCfgRr)
#define BUFSIZ
Definition: defines.h:20
ACLList * aclList
Definition: QosConfig.h:37
static const char * peer_type_str(const peer_t type)
Definition: cache_cf.cc:2050
const char * uniqueHostname(void)
Definition: tools.cc:494
static void free_int(int *var)
Definition: cache_cf.cc:2583
static size_t parseBytesUnits(const char *unit)
Definition: cache_cf.cc:1428
static char * NextQuotedToken()
static void parse_icap_service_type(Adaptation::Icap::Config *)
Definition: cache_cf.cc:4230
static void parse_u_short(unsigned short *var)
Definition: cache_cf.cc:3165
CustomLog * icaplogs
Definition: SquidConfig.h:186
#define xmalloc
static void free_acl_tos(acl_tos **head)
Definition: cache_cf.cc:1667
struct squidaio_request_t * next
Definition: aiops.cc:51
acl_access * access
Definition: CachePeer.h:79
#define URI_WHITESPACE_STRIP
Definition: defines.h:133
bool xstrtoui(const char *s, char **end, unsigned int *value, unsigned int min, unsigned int max)
Definition: xstrto.cc:86
static T Parse(ConfigParser &)
creates a new T instance using the given parser; never returns nil
static void dump_tristate(StoreEntry *entry, const char *name, int var)
Definition: cache_cf.cc:2642
static void parse_http_upgrade_request_protocols(HttpUpgradeProtocolAccess **protoGuards)
Definition: cache_cf.cc:5005
void parsePoolClass()
Definition: DelayConfig.cc:31
static void dump_time_nanoseconds(StoreEntry *entry, const char *name, const std::chrono::nanoseconds &var)
Definition: cache_cf.cc:3061
static void free_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap)
Definition: cache_cf.cc:4200
char * unlinkd
Definition: SquidConfig.h:203
void peerDigestCreate(CachePeer *p)
Definition: peer_digest.cc:130
static void parse_ecap_service_type(Adaptation::Ecap::Config *)
Definition: cache_cf.cc:4268
static void parse_delay_pool_class(DelayConfig *cfg)
Definition: cache_cf.cc:1789
#define URI_WHITESPACE_CHOP
Definition: defines.h:136
void useSquidUntrusted(SSL_CTX *sslContext)
Definition: support.cc:1343
Security::ContextPointer createClientContext(bool setOptions)
generate a security client-context from these configured options
Definition: PeerOptions.cc:271
u_int index
Definition: CachePeer.h:38
static void dump_kb_int64_t(StoreEntry *entry, const char *name, int64_t var)
Definition: cache_cf.cc:3098
static int parseManyConfigFiles(char *files, int depth)
Definition: cache_cf.cc:299
static ConfigParser LegacyParser
Definition: cache_cf.cc:267
double GetPercentage(bool limit)
Definition: Parsing.cc:178
static int parse_line(char *)
AnyP::ProtocolVersion ProtocolVersion()
Protocol version to use in Http::Message structures wrapping FTP messages.
Definition: Elements.cc:24
static void free_removalpolicy(RemovalPolicySettings **settings)
Definition: cache_cf.cc:3267
#define LOCAL_ARRAY(type, name, size)
Definition: squid.h:75
static const char *const T_HOUR_STR
Definition: cache_cf.cc:148
static void parse_kb_int64_t(int64_t *var)
Definition: cache_cf.cc:3122
AnyP::PortCfgPointer HttpPortList
list of Squid http(s)_port configured
Definition: PortCfg.cc:21
static int check_null_acl_access(acl_access *a)
Definition: cache_cf.cc:3213
static void parse_refreshpattern(RefreshPattern **)
Definition: cache_cf.cc:2760
static void parse_address(Ip::Address *addr)
Definition: cache_cf.cc:1547
int memory_cache_first
Definition: SquidConfig.h:342
static void parse_acl_tos(acl_tos **head)
Definition: cache_cf.cc:1632
peer_t type
Definition: CachePeer.h:41
SBuf TheKidName
current Squid process name (e.g., "squid-coord")
Definition: Kids.cc:19
void log(char *format,...)
bool isEmpty() const
Definition: SBuf.h:424
static void free_acl_access(acl_access **head)
Definition: cache_cf.cc:1534
virtual char const * typeString() const =0
struct CachePeer::@30 options
static ACL * FindByName(const char *name)
Definition: Acl.cc:92
static void free_cachemgrpasswd(Mgr::ActionPasswordList **head)
Definition: cache_cf.cc:2481
representation of a class of Size-limit ACLs
Definition: AclSizeLimit.h:17
void dump(StoreEntry *entry, const char *name)
Dump the notes list to the given StoreEntry object.
Definition: Notes.cc:255
static void parse_pipelinePrefetch(int *var)
Definition: cache_cf.cc:2686
ACLList * aclList
Definition: QosConfig.h:51
int KidIdentifier
void aclParseAccessLine(const char *directive, ConfigParser &, acl_access **treep)
Definition: Gadgets.cc:138
std::list< HeaderWithAcl > HeaderWithAclList
static void EnableMacros()
Allow macros inside quoted strings.
Definition: ConfigParser.h:141
static int check_null_string(char *s)
Definition: cache_cf.cc:1925
#define O_TEXT
Definition: defines.h:140
std::list< SBuf > SBufList
Definition: forward.h:22
static void trim_trailing_ws(char *str)
Definition: cache_cf.cc:366
static void parse_acl_access(acl_access **head)
Definition: cache_cf.cc:1528
static void free_sslproxy_ssl_bump(acl_access **ssl_bump)
Definition: cache_cf.cc:4582
static void ParseBool(bool *var)
Definition: cache_cf.cc:3177
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition: store.cc:869
static void dump_time_t(StoreEntry *entry, const char *name, time_t var)
Definition: cache_cf.cc:3018
static bool StrictMode
Definition: ConfigParser.h:157
const char * sslCertAdaptAlgoritm(int alg)
Definition: gadgets.h:200
static void dump_ftp_epsv(StoreEntry *entry, const char *name, acl_access *ftp_epsv)
Definition: cache_cf.cc:4817
Note::Pointer parse(ConfigParser &parser)
Parses a notes line and returns a pointer to the parsed Note object.
Definition: Notes.cc:202
static void dump_http_upgrade_request_protocols(StoreEntry *entry, const char *name, HttpUpgradeProtocolAccess *protoGuards)
Definition: cache_cf.cc:5015
char * login
Definition: CachePeer.h:174
struct RefreshPattern::@93 flags
bool GetHostByName(const char *s)
Definition: Address.cc:372
Helper::ChildConfig storeId