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, "configuration error: " << 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, "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, "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, "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, "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, "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), "UPGRADE WARNING: '" << 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), "UPGRADE WARNING: '" << 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  debugs(3, DBG_CRITICAL, "FATAL: Squid built with no CPU affinity " <<
4153  "support, do not set 'cpu_affinity_map'");
4154  self_destruct();
4155 
4156 #else /* HAVE_CPU_AFFINITY */
4157  if (!*cpuAffinityMap)
4158  *cpuAffinityMap = new CpuAffinityMap;
4159 
4160  const char *const pToken = ConfigParser::NextToken();
4161  const char *const cToken = ConfigParser::NextToken();
4162  std::vector<int> processes, cores;
4163  if (!parseNamedIntList(pToken, "process_numbers", processes)) {
4164  debugs(3, DBG_CRITICAL, "FATAL: bad 'process_numbers' parameter " <<
4165  "in 'cpu_affinity_map'");
4166  self_destruct();
4167  } else if (!parseNamedIntList(cToken, "cores", cores)) {
4168  debugs(3, DBG_CRITICAL, "FATAL: bad 'cores' parameter in " <<
4169  "'cpu_affinity_map'");
4170  self_destruct();
4171  } else if (!(*cpuAffinityMap)->add(processes, cores)) {
4172  debugs(3, DBG_CRITICAL, "FATAL: bad 'cpu_affinity_map'; " <<
4173  "process_numbers and cores lists differ in length or " <<
4174  "contain numbers <= 0");
4175  self_destruct();
4176  }
4177 #endif
4178 }
4179 
4180 static void
4181 dump_CpuAffinityMap(StoreEntry *const entry, const char *const name, const CpuAffinityMap *const cpuAffinityMap)
4182 {
4183  if (cpuAffinityMap) {
4184  storeAppendPrintf(entry, "%s process_numbers=", name);
4185  for (size_t i = 0; i < cpuAffinityMap->processes().size(); ++i) {
4186  storeAppendPrintf(entry, "%s%i", (i ? "," : ""),
4187  cpuAffinityMap->processes()[i]);
4188  }
4189  storeAppendPrintf(entry, " cores=");
4190  for (size_t i = 0; i < cpuAffinityMap->cores().size(); ++i) {
4191  storeAppendPrintf(entry, "%s%i", (i ? "," : ""),
4192  cpuAffinityMap->cores()[i]);
4193  }
4194  storeAppendPrintf(entry, "\n");
4195  }
4196 }
4197 
4198 static void
4199 free_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap)
4200 {
4201  delete *cpuAffinityMap;
4202  *cpuAffinityMap = NULL;
4203 }
4204 
4205 #if USE_ADAPTATION
4206 
4207 static void
4209 {
4211 }
4212 
4213 static void
4215 {
4217 }
4218 
4219 static void
4221 {
4223 }
4224 #endif /* USE_ADAPTATION */
4225 
4226 #if ICAP_CLIENT
4227 
4228 static void
4230 {
4231  cfg->parseService();
4232 }
4233 
4234 static void
4236 {
4237  cfg->freeService();
4238 }
4239 
4240 static void
4241 dump_icap_service_type(StoreEntry * entry, const char *name, const Adaptation::Icap::Config &cfg)
4242 {
4243  cfg.dumpService(entry, name);
4244 }
4245 
4246 static void
4248 {
4249  debugs(93, DBG_CRITICAL, "WARNING: 'icap_class' is deprecated. " <<
4250  "Use 'adaptation_service_set' instead");
4252 }
4253 
4254 static void
4256 {
4257  debugs(93, DBG_CRITICAL, "WARNING: 'icap_access' is deprecated. " <<
4258  "Use 'adaptation_access' instead");
4260 }
4261 
4262 #endif
4263 
4264 #if USE_ECAP
4265 
4266 static void
4268 {
4269  cfg->parseService();
4270 }
4271 
4272 static void
4274 {
4275  cfg->freeService();
4276 }
4277 
4278 static void
4279 dump_ecap_service_type(StoreEntry * entry, const char *name, const Adaptation::Ecap::Config &cfg)
4280 {
4281  cfg.dumpService(entry, name);
4282 }
4283 
4284 #endif /* USE_ECAP */
4285 
4286 #if ICAP_CLIENT
4288 {
4289  char *token;
4291 
4292  if ((token = ConfigParser::NextToken()) == NULL)
4293  return;
4294 
4295  if (strcmp(token,"in") != 0) {
4296  debugs(3, DBG_CRITICAL, "expecting 'in' on'" << config_input_line << "'");
4297  self_destruct();
4298  return;
4299  }
4300 
4302 }
4303 
4304 static void dump_icap_service_failure_limit(StoreEntry *entry, const char *name, const Adaptation::Icap::Config &cfg)
4305 {
4306  storeAppendPrintf(entry, "%s %d", name, cfg.service_failure_limit);
4307  if (cfg.oldest_service_failure > 0) {
4308  storeAppendPrintf(entry, " in %d seconds", (int)cfg.oldest_service_failure);
4309  }
4310  storeAppendPrintf(entry, "\n");
4311 }
4312 
4314 {
4315  cfg->oldest_service_failure = 0;
4316  cfg->service_failure_limit = 0;
4317 }
4318 #endif
4319 
4320 #if USE_OPENSSL
4322 {
4323  char *al;
4325  if ((al = ConfigParser::NextToken()) == NULL) {
4326  xfree(ca);
4327  self_destruct();
4328  return;
4329  }
4330 
4331  const char *param;
4332  if ( char *s = strchr(al, '{')) {
4333  *s = '\0'; // terminate the al string
4334  ++s;
4335  param = s;
4336  s = strchr(s, '}');
4337  if (!s) {
4338  xfree(ca);
4339  self_destruct();
4340  return;
4341  }
4342  *s = '\0';
4343  } else
4344  param = NULL;
4345 
4346  if (strcmp(al, Ssl::CertAdaptAlgorithmStr[Ssl::algSetValidAfter]) == 0) {
4347  ca->alg = Ssl::algSetValidAfter;
4348  ca->param = xstrdup("on");
4349  } else if (strcmp(al, Ssl::CertAdaptAlgorithmStr[Ssl::algSetValidBefore]) == 0) {
4351  ca->param = xstrdup("on");
4352  } else if (strcmp(al, Ssl::CertAdaptAlgorithmStr[Ssl::algSetCommonName]) == 0) {
4353  ca->alg = Ssl::algSetCommonName;
4354  if (param) {
4355  if (strlen(param) > 64) {
4356  debugs(3, DBG_CRITICAL, "FATAL: sslproxy_cert_adapt: setCommonName{" <<param << "} : using common name longer than 64 bytes is not supported");
4357  xfree(ca);
4358  self_destruct();
4359  return;
4360  }
4361  ca->param = xstrdup(param);
4362  }
4363  } else {
4364  debugs(3, DBG_CRITICAL, "FATAL: sslproxy_cert_adapt: unknown cert adaptation algorithm: " << al);
4365  xfree(ca);
4366  self_destruct();
4367  return;
4368  }
4369 
4371 
4372  while (*cert_adapt)
4373  cert_adapt = &(*cert_adapt)->next;
4374 
4375  *cert_adapt = ca;
4376 }
4377 
4378 static void dump_sslproxy_cert_adapt(StoreEntry *entry, const char *name, sslproxy_cert_adapt *cert_adapt)
4379 {
4380  for (sslproxy_cert_adapt *ca = cert_adapt; ca != NULL; ca = ca->next) {
4381  storeAppendPrintf(entry, "%s ", name);
4382  storeAppendPrintf(entry, "%s{%s} ", Ssl::sslCertAdaptAlgoritm(ca->alg), ca->param);
4383  if (ca->aclList)
4384  dump_acl_list(entry, ca->aclList);
4385  storeAppendPrintf(entry, "\n");
4386  }
4387 }
4388 
4390 {
4391  while (*cert_adapt) {
4392  sslproxy_cert_adapt *ca = *cert_adapt;
4393  *cert_adapt = ca->next;
4394  safe_free(ca->param);
4395 
4396  if (ca->aclList)
4397  aclDestroyAclList(&ca->aclList);
4398 
4399  safe_free(ca);
4400  }
4401 }
4402 
4404 {
4405  char *al;
4407  if ((al = ConfigParser::NextToken()) == NULL) {
4408  xfree(cs);
4409  self_destruct();
4410  return;
4411  }
4412 
4413  if (strcmp(al, Ssl::CertSignAlgorithmStr[Ssl::algSignTrusted]) == 0)
4414  cs->alg = Ssl::algSignTrusted;
4415  else if (strcmp(al, Ssl::CertSignAlgorithmStr[Ssl::algSignUntrusted]) == 0)
4416  cs->alg = Ssl::algSignUntrusted;
4417  else if (strcmp(al, Ssl::CertSignAlgorithmStr[Ssl::algSignSelf]) == 0)
4418  cs->alg = Ssl::algSignSelf;
4419  else {
4420  debugs(3, DBG_CRITICAL, "FATAL: sslproxy_cert_sign: unknown cert signing algorithm: " << al);
4421  xfree(cs);
4422  self_destruct();
4423  return;
4424  }
4425 
4427 
4428  while (*cert_sign)
4429  cert_sign = &(*cert_sign)->next;
4430 
4431  *cert_sign = cs;
4432 }
4433 
4434 static void dump_sslproxy_cert_sign(StoreEntry *entry, const char *name, sslproxy_cert_sign *cert_sign)
4435 {
4436  sslproxy_cert_sign *cs;
4437  for (cs = cert_sign; cs != NULL; cs = cs->next) {
4438  storeAppendPrintf(entry, "%s ", name);
4439  storeAppendPrintf(entry, "%s ", Ssl::certSignAlgorithm(cs->alg));
4440  if (cs->aclList)
4441  dump_acl_list(entry, cs->aclList);
4442  storeAppendPrintf(entry, "\n");
4443  }
4444 }
4445 
4447 {
4448  while (*cert_sign) {
4449  sslproxy_cert_sign *cs = *cert_sign;
4450  *cert_sign = cs->next;
4451 
4452  if (cs->aclList)
4453  aclDestroyAclList(&cs->aclList);
4454 
4455  safe_free(cs);
4456  }
4457 }
4458 
4460 {
4461 public:
4463  /* RegisteredRunner API */
4464  virtual void finalizeConfig();
4465 };
4466 
4468 
4470 
4471 void
4473 {
4476  static char buf[1024];
4478  strcpy(buf, "ssl_bump deny all");
4479  debugs(3, DBG_CRITICAL, "WARNING: auto-converting deprecated implicit "
4480  "\"ssl_bump deny all\" to \"ssl_bump none all\". New ssl_bump configurations "
4481  "must not use implicit rules. Update your ssl_bump rules.");
4482  } else {
4483  strcpy(buf, "ssl_bump allow all");
4484  debugs(3, DBG_CRITICAL, "SECURITY NOTICE: auto-converting deprecated implicit "
4485  "\"ssl_bump allow all\" to \"ssl_bump client-first all\" which is usually "
4486  "inferior to the newer server-first bumping mode. New ssl_bump"
4487  " configurations must not use implicit rules. Update your ssl_bump rules.");
4488  }
4489  parse_line(buf);
4490  }
4491 }
4492 
4493 static void parse_sslproxy_ssl_bump(acl_access **ssl_bump)
4494 {
4495  typedef const char *BumpCfgStyle;
4496  BumpCfgStyle bcsNone = NULL;
4497  BumpCfgStyle bcsNew = "new client/server-first/none";
4498  BumpCfgStyle bcsOld = "deprecated allow/deny";
4499  static BumpCfgStyle bumpCfgStyleLast = bcsNone;
4500  BumpCfgStyle bumpCfgStyleNow = bcsNone;
4501  char *bm;
4502  if ((bm = ConfigParser::NextToken()) == NULL) {
4503  self_destruct();
4504  return;
4505  }
4506 
4507  // if this is the first rule processed
4508  if (*ssl_bump == NULL) {
4509  bumpCfgStyleLast = bcsNone;
4511  }
4512 
4514 
4515  if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpClientFirst]) == 0) {
4517  bumpCfgStyleNow = bcsNew;
4518  } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpServerFirst]) == 0) {
4520  bumpCfgStyleNow = bcsNew;
4521  } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpPeek]) == 0) {
4522  action.kind = Ssl::bumpPeek;
4523  bumpCfgStyleNow = bcsNew;
4524  } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpStare]) == 0) {
4525  action.kind = Ssl::bumpStare;
4526  bumpCfgStyleNow = bcsNew;
4527  } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpSplice]) == 0) {
4528  action.kind = Ssl::bumpSplice;
4529  bumpCfgStyleNow = bcsNew;
4530  } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpBump]) == 0) {
4531  action.kind = Ssl::bumpBump;
4532  bumpCfgStyleNow = bcsNew;
4533  } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpTerminate]) == 0) {
4534  action.kind = Ssl::bumpTerminate;
4535  bumpCfgStyleNow = bcsNew;
4536  } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpNone]) == 0) {
4537  action.kind = Ssl::bumpNone;
4538  bumpCfgStyleNow = bcsNew;
4539  } else if (strcmp(bm, "allow") == 0) {
4540  debugs(3, DBG_CRITICAL, "SECURITY NOTICE: auto-converting deprecated "
4541  "\"ssl_bump allow <acl>\" to \"ssl_bump client-first <acl>\" which "
4542  "is usually inferior to the newer server-first "
4543  "bumping mode. Update your ssl_bump rules.");
4545  bumpCfgStyleNow = bcsOld;
4547  } else if (strcmp(bm, "deny") == 0) {
4548  debugs(3, DBG_CRITICAL, "WARNING: auto-converting deprecated "
4549  "\"ssl_bump deny <acl>\" to \"ssl_bump none <acl>\". Update "
4550  "your ssl_bump rules.");
4551  action.kind = Ssl::bumpNone;
4552  bumpCfgStyleNow = bcsOld;
4554  } else {
4555  debugs(3, DBG_CRITICAL, "FATAL: unknown ssl_bump mode: " << bm);
4556  self_destruct();
4557  return;
4558  }
4559 
4560  if (bumpCfgStyleLast != bcsNone && bumpCfgStyleNow != bumpCfgStyleLast) {
4561  debugs(3, DBG_CRITICAL, "FATAL: do not mix " << bumpCfgStyleNow << " actions with " <<
4562  bumpCfgStyleLast << " actions. Update your ssl_bump rules.");
4563  self_destruct();
4564  return;
4565  }
4566 
4567  bumpCfgStyleLast = bumpCfgStyleNow;
4568 
4569  // empty rule OK
4570  ParseAclWithAction(ssl_bump, action, "ssl_bump");
4571 }
4572 
4573 static void dump_sslproxy_ssl_bump(StoreEntry *entry, const char *name, acl_access *ssl_bump)
4574 {
4575  if (ssl_bump)
4576  dump_SBufList(entry, ssl_bump->treeDump(name, [](const Acl::Answer &action) {
4577  return Ssl::BumpModeStr.at(action.kind);
4578  }));
4579 }
4580 
4581 static void free_sslproxy_ssl_bump(acl_access **ssl_bump)
4582 {
4583  free_acl_access(ssl_bump);
4584 }
4585 
4586 #endif
4587 
4588 static void dump_HeaderWithAclList(StoreEntry * entry, const char *name, HeaderWithAclList *headers)
4589 {
4590  if (!headers)
4591  return;
4592 
4593  for (HeaderWithAclList::iterator hwa = headers->begin(); hwa != headers->end(); ++hwa) {
4594  storeAppendPrintf(entry, "%s %s %s", name, hwa->fieldName.c_str(), hwa->fieldValue.c_str());
4595  if (hwa->aclList)
4596  dump_acl_list(entry, hwa->aclList);
4597  storeAppendPrintf(entry, "\n");
4598  }
4599 }
4600 
4602 {
4603  char *fn;
4604  if (!*headers) {
4605  *headers = new HeaderWithAclList;
4606  }
4607  if ((fn = ConfigParser::NextToken()) == NULL) {
4608  self_destruct();
4609  return;
4610  }
4611  HeaderWithAcl hwa;
4612  hwa.fieldName = fn;
4614  if (hwa.fieldId == Http::HdrType::BAD_HDR)
4616 
4617  Format::Format *nlf = new ::Format::Format("hdrWithAcl");
4621  hwa.fieldValue = buf.termedBuf();
4623  if (hwa.quoted) {
4624  if (!nlf->parse(hwa.fieldValue.c_str())) {
4625  self_destruct();
4626  return;
4627  }
4628  hwa.valueFormat = nlf;
4629  } else
4630  delete nlf;
4631  aclParseAclList(LegacyParser, &hwa.aclList, (hwa.fieldName + ':' + hwa.fieldValue).c_str());
4632  (*headers)->push_back(hwa);
4633 }
4634 
4636 {
4637  if (!(*header))
4638  return;
4639 
4640  for (HeaderWithAclList::iterator hwa = (*header)->begin(); hwa != (*header)->end(); ++hwa) {
4641  if (hwa->aclList)
4642  aclDestroyAclList(&hwa->aclList);
4643 
4644  if (hwa->valueFormat) {
4645  delete hwa->valueFormat;
4646  hwa->valueFormat = NULL;
4647  }
4648  }
4649  delete *header;
4650  *header = NULL;
4651 }
4652 
4653 static void parse_note(Notes *notes)
4654 {
4655  assert(notes);
4656  notes->parse(LegacyParser);
4657 }
4658 
4659 static void dump_note(StoreEntry *entry, const char *name, Notes &notes)
4660 {
4661  notes.dump(entry, name);
4662 }
4663 
4664 static void free_note(Notes *notes)
4665 {
4666  notes->clean();
4667 }
4668 
4669 static DebugMessageId ParseDebugMessageId(const char *value, const char eov)
4670 {
4671  const auto id = xatoui(value, eov);
4672  if (!(0 < id && id < DebugMessageIdUpperBound))
4673  throw TextException(ToSBuf("unknown cache_log_message ID: ", value), Here());
4674  return static_cast<DebugMessageId>(id);
4675 }
4676 
4677 static void parse_cache_log_message(DebugMessages **debugMessages)
4678 {
4679  DebugMessage msg;
4680  DebugMessageId minId = 0;
4681  DebugMessageId maxId = 0;
4682 
4683  char *key = nullptr;
4684  char *value = nullptr;
4685  while (ConfigParser::NextKvPair(key, value)) {
4686  if (strcmp(key, "id") == 0) {
4687  if (minId > 0)
4688  break;
4689  minId = maxId = ParseDebugMessageId(value, '\0');
4690  } else if (strcmp(key, "ids") == 0) {
4691  if (minId > 0)
4692  break;
4693  const auto dash = strchr(value, '-');
4694  if (!dash)
4695  throw TextException(ToSBuf("malformed cache_log_message ID range: ", key, '=', value), Here());
4696  minId = ParseDebugMessageId(value, '-');
4697  maxId = ParseDebugMessageId(dash+1, '\0');
4698  if (minId > maxId)
4699  throw TextException(ToSBuf("invalid cache_log_message ID range: ", key, '=', value), Here());
4700  } else if (strcmp(key, "level") == 0) {
4701  if (msg.levelled())
4702  break;
4703  const auto level = xatoi(value);
4704  if (level < 0)
4705  throw TextException(ToSBuf("negative cache_log_message level: ", value), Here());
4706  msg.level = level;
4707  } else if (strcmp(key, "limit") == 0) {
4708  if (msg.limited())
4709  break;
4710  msg.limit = xatoull(value, 10);
4711  } else {
4712  throw TextException(ToSBuf("unsupported cache_log_message option: ", key), Here());
4713  }
4714  key = value = nullptr;
4715  }
4716 
4717  if (key && value)
4718  throw TextException(ToSBuf("repeated or conflicting cache_log_message option: ", key, '=', value), Here());
4719 
4720  if (!minId)
4721  throw TextException("cache_log_message is missing a required id=... or ids=... option", Here());
4722 
4723  if (!(msg.levelled() || msg.limited()))
4724  throw TextException("cache_log_message is missing a required level=... or limit=... option", Here());
4725 
4726  assert(debugMessages);
4727  if (!*debugMessages)
4728  *debugMessages = new DebugMessages();
4729 
4730  for (auto id = minId; id <= maxId; ++id) {
4731  msg.id = id;
4732  (*debugMessages)->messages.at(id) = msg;
4733  }
4734 }
4735 
4736 static void dump_cache_log_message(StoreEntry *entry, const char *name, const DebugMessages *debugMessages)
4737 {
4738  if (!debugMessages)
4739  return;
4740 
4741  SBufStream out;
4742  for (const auto &msg: debugMessages->messages) {
4743  if (!msg.configured())
4744  continue;
4745  out << name << " id=" << msg.id;
4746  if (msg.levelled())
4747  out << " level=" << msg.level;
4748  if (msg.limited())
4749  out << " limit=" << msg.limit;
4750  out << "\n";
4751  }
4752  const auto buf = out.buf();
4753  entry->append(buf.rawContent(), buf.length()); // may be empty
4754 }
4755 
4756 static void free_cache_log_message(DebugMessages **debugMessages)
4757 {
4758  // clear old messages to avoid cumulative effect across (re)configurations
4759  assert(debugMessages);
4760  delete *debugMessages;
4761  *debugMessages = nullptr;
4762 }
4763 
4764 static bool FtpEspvDeprecated = false;
4765 static void parse_ftp_epsv(acl_access **ftp_epsv)
4766 {
4767  Acl::Answer ftpEpsvDeprecatedAction;
4768  bool ftpEpsvIsDeprecatedRule = false;
4769 
4770  char *t = ConfigParser::PeekAtToken();
4771  if (!t) {
4772  self_destruct();
4773  return;
4774  }
4775 
4776  if (!strcmp(t, "off")) {
4777  (void)ConfigParser::NextToken();
4778  ftpEpsvIsDeprecatedRule = true;
4779  ftpEpsvDeprecatedAction = Acl::Answer(ACCESS_DENIED);
4780  } else if (!strcmp(t, "on")) {
4781  (void)ConfigParser::NextToken();
4782  ftpEpsvIsDeprecatedRule = true;
4783  ftpEpsvDeprecatedAction = Acl::Answer(ACCESS_ALLOWED);
4784  }
4785 
4786  // Check for mixing "ftp_epsv on|off" and "ftp_epsv allow|deny .." rules:
4787  // 1) if this line is "ftp_epsv allow|deny ..." and already exist rules of "ftp_epsv on|off"
4788  // 2) if this line is "ftp_epsv on|off" and already exist rules of "ftp_epsv allow|deny ..."
4789  // then abort
4790  if ((!ftpEpsvIsDeprecatedRule && FtpEspvDeprecated) ||
4791  (ftpEpsvIsDeprecatedRule && !FtpEspvDeprecated && *ftp_epsv != NULL)) {
4792  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.");
4793  self_destruct();
4794  return;
4795  }
4796 
4797  if (ftpEpsvIsDeprecatedRule) {
4798  // overwrite previous ftp_epsv lines
4799  delete *ftp_epsv;
4800  *ftp_epsv = nullptr;
4801 
4802  if (ftpEpsvDeprecatedAction == Acl::Answer(ACCESS_DENIED)) {
4803  if (ACL *a = ACL::FindByName("all"))
4804  ParseAclWithAction(ftp_epsv, ftpEpsvDeprecatedAction, "ftp_epsv", a);
4805  else {
4806  self_destruct();
4807  return;
4808  }
4809  }
4810  FtpEspvDeprecated = true;
4811  } else {
4813  }
4814 }
4815 
4816 static void dump_ftp_epsv(StoreEntry *entry, const char *name, acl_access *ftp_epsv)
4817 {
4818  if (ftp_epsv)
4819  dump_SBufList(entry, ftp_epsv->treeDump(name, Acl::AllowOrDeny));
4820 }
4821 
4822 static void free_ftp_epsv(acl_access **ftp_epsv)
4823 {
4824  free_acl_access(ftp_epsv);
4825  FtpEspvDeprecated = false;
4826 }
4827 
4830 static std::chrono::seconds
4832 {
4833  const auto timeValueToken = ConfigParser::NextToken();
4834  if (!timeValueToken)
4835  throw TexcHere("cannot read a time value");
4836 
4837  using Seconds = std::chrono::seconds;
4838 
4839  const auto parsedTimeValue = xatof(timeValueToken);
4840 
4841  if (parsedTimeValue == 0)
4842  return std::chrono::seconds::zero();
4843 
4844  std::chrono::nanoseconds parsedUnitDuration;
4845 
4846  const auto unitToken = ConfigParser::PeekAtToken();
4847  if (parseTimeUnit<Seconds>(unitToken, parsedUnitDuration))
4848  (void)ConfigParser::NextToken();
4849  else {
4850  const auto defaultParsed = parseTimeUnit<Seconds>(T_SECOND_STR, parsedUnitDuration);
4851  assert(defaultParsed);
4853  ": WARNING: missing time unit, using deprecated default '" << T_SECOND_STR << "'");
4854  }
4855 
4856  const auto nanoseconds = ToNanoSeconds(parsedTimeValue, parsedUnitDuration);
4857 
4858  return FromNanoseconds<Seconds>(nanoseconds, parsedTimeValue);
4859 }
4860 
4861 static void
4863 {
4864  // TODO: do not allow optional timeunit (as the documentation prescribes)
4865  // and use parseTimeLine() instead.
4867 
4868  char *key, *value;
4869  while(ConfigParser::NextKvPair(key, value)) {
4870  if (strcasecmp(key, "on_timeout") == 0) {
4871  if (strcasecmp(value, "bypass") == 0)
4872  config->action = toutActBypass;
4873  else if (strcasecmp(value, "fail") == 0)
4874  config->action = toutActFail;
4875  else if (strcasecmp(value, "retry") == 0)
4876  config->action = toutActRetry;
4877  else if (strcasecmp(value, "use_configured_response") == 0) {
4879  } else {
4880  debugs(3, DBG_CRITICAL, "FATAL: unsupported \"on_timeout\" action: " << value);
4881  self_destruct();
4882  return;
4883  }
4884  } else if (strcasecmp(key, "response") == 0) {
4885  config->response = xstrdup(value);
4886  } else {
4887  debugs(3, DBG_CRITICAL, "FATAL: unsupported option " << key);
4888  self_destruct();
4889  return;
4890  }
4891  }
4892 
4893  if (config->action == toutActUseConfiguredResponse && !config->response) {
4894  debugs(3, DBG_CRITICAL, "FATAL: Expected 'response=' option after 'on_timeout=use_configured_response' option");
4895  self_destruct();
4896  }
4897 
4898  if (config->action != toutActUseConfiguredResponse && config->response) {
4899  debugs(3, DBG_CRITICAL, "FATAL: 'response=' option is valid only when used with the 'on_timeout=use_configured_response' option");
4900  self_destruct();
4901  }
4902 }
4903 
4904 static void
4906 {
4907  const char *onTimedOutActions[] = {"bypass", "fail", "retry", "use_configured_response"};
4908  assert(config.action >= 0 && config.action <= toutActUseConfiguredResponse);
4909 
4910  dump_time_t(entry, name, Config.Timeout.urlRewrite);
4911  storeAppendPrintf(entry, " on_timeout=%s", onTimedOutActions[config.action]);
4912 
4913  if (config.response)
4914  storeAppendPrintf(entry, " response=\"%s\"", config.response);
4915 
4916  storeAppendPrintf(entry, "\n");
4917 }
4918 
4919 static void
4921 {
4923  config->action = 0;
4924  safe_free(config->response);
4925 }
4926 
4927 static void
4929 {
4930  int val = 0;
4931  parse_onoff(&val);
4932 
4933  // If quoted values is set to on then enable new strict mode parsing
4934  if (val) {
4936  ConfigParser::StrictMode = true;
4937  } else {
4939  ConfigParser::StrictMode = false;
4940  }
4941 }
4942 
4943 static void
4944 dump_configuration_includes_quoted_values(StoreEntry *const entry, const char *const name, bool)
4945 {
4946  int val = ConfigParser::RecognizeQuotedValues ? 1 : 0;
4947  dump_onoff(entry, name, val);
4948 }
4949 
4950 static void
4952 {
4954  ConfigParser::StrictMode = false;
4955 }
4956 
4957 static void
4959 {
4960  char *tm;
4961  if ((tm = ConfigParser::NextToken()) == NULL) {
4962  self_destruct();
4963  return;
4964  }
4965 
4967  if (strcmp(tm, "tunnel") == 0)
4968  action.kind = 1;
4969  else if (strcmp(tm, "respond") == 0)
4970  action.kind = 2;
4971  else {
4972  debugs(3, DBG_CRITICAL, "FATAL: unknown on_unsupported_protocol mode: " << tm);
4973  self_destruct();
4974  return;
4975  }
4976 
4977  // empty rule OK
4978  ParseAclWithAction(access, action, "on_unsupported_protocol");
4979 }
4980 
4981 static void
4982 dump_on_unsupported_protocol(StoreEntry *entry, const char *name, acl_access *access)
4983 {
4984  static const std::vector<const char *> onErrorTunnelMode = {
4985  "none",
4986  "tunnel",
4987  "respond"
4988  };
4989  if (access) {
4990  SBufList lines = access->treeDump(name, [](const Acl::Answer &action) {
4991  return onErrorTunnelMode.at(action.kind);
4992  });
4993  dump_SBufList(entry, lines);
4994  }
4995 }
4996 
4997 static void
4999 {
5000  free_acl_access(access);
5001 }
5002 
5003 static void
5005 {
5006  assert(protoGuardsPtr);
5007  auto &protoGuards = *protoGuardsPtr;
5008  if (!protoGuards)
5009  protoGuards = new HttpUpgradeProtocolAccess();
5010  protoGuards->configureGuard(LegacyParser);
5011 }
5012 
5013 static void
5015 {
5016  if (!protoGuards)
5017  return;
5018 
5019  const SBuf name(rawName);
5020  protoGuards->forEach([entry,&name](const SBuf &proto, const acl_access *acls) {
5021  SBufList line;
5022  line.push_back(name);
5023  line.push_back(proto);
5024  const auto acld = acls->treeDump("", &Acl::AllowOrDeny);
5025  line.insert(line.end(), acld.begin(), acld.end());
5026  dump_SBufList(entry, line);
5027  });
5028 }
5029 
5030 static void
5032 {
5033  assert(protoGuardsPtr);
5034  auto &protoGuards = *protoGuardsPtr;
5035  delete protoGuards;
5036  protoGuards = nullptr;
5037 }
5038 
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:4220
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:4241
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:72
#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:4229
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:5004
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:4199
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:4267
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:1345
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:93
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:147
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:4581
static void ParseBool(bool *var)
Definition: cache_cf.cc:3177
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition: store.cc:872
static void dump_time_t(StoreEntry *entry, const char *name, time_t var)
Definition: cache_cf.cc:3018
static bool StrictMode
Definition: ConfigParser.h:163
const char * sslCertAdaptAlgoritm(int alg)
Definition: gadgets.h:201
static void dump_ftp_epsv(StoreEntry *entry, const char *name, acl_access *ftp_epsv)
Definition: cache_cf.cc:4816
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:5014
char * login
Definition: CachePeer.h:174
struct RefreshPattern::@93 flags
bool GetHostByName(const char *s)
Definition: Address.cc:372
Helper::ChildConfig storeIdChildren
Definition: SquidConfig.h:215