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