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