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