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