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