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