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