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