]> git.ipfire.org Git - thirdparty/squid.git/blob - src/cache_cf.cc
merge from trunk r12500
[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->sslBump)
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
1022 static uint64_t
1023 parseTimeUnits(const char *unit, bool allowMsec)
1024 {
1025 if (allowMsec && !strncasecmp(unit, T_MILLISECOND_STR, strlen(T_MILLISECOND_STR)))
1026 return 1;
1027
1028 if (!strncasecmp(unit, T_SECOND_STR, strlen(T_SECOND_STR)))
1029 return 1000;
1030
1031 if (!strncasecmp(unit, T_MINUTE_STR, strlen(T_MINUTE_STR)))
1032 return 60 * 1000;
1033
1034 if (!strncasecmp(unit, T_HOUR_STR, strlen(T_HOUR_STR)))
1035 return 3600 * 1000;
1036
1037 if (!strncasecmp(unit, T_DAY_STR, strlen(T_DAY_STR)))
1038 return 86400 * 1000;
1039
1040 if (!strncasecmp(unit, T_WEEK_STR, strlen(T_WEEK_STR)))
1041 return 86400 * 7 * 1000;
1042
1043 if (!strncasecmp(unit, T_FORTNIGHT_STR, strlen(T_FORTNIGHT_STR)))
1044 return 86400 * 14 * 1000;
1045
1046 if (!strncasecmp(unit, T_MONTH_STR, strlen(T_MONTH_STR)))
1047 return static_cast<uint64_t>(86400) * 30 * 1000;
1048
1049 if (!strncasecmp(unit, T_YEAR_STR, strlen(T_YEAR_STR)))
1050 return static_cast<uint64_t>(86400 * 1000 * 365.2522);
1051
1052 if (!strncasecmp(unit, T_DECADE_STR, strlen(T_DECADE_STR)))
1053 return static_cast<uint64_t>(86400 * 1000 * 365.2522 * 10);
1054
1055 debugs(3, DBG_IMPORTANT, "parseTimeUnits: unknown time unit '" << unit << "'");
1056
1057 return 0;
1058 }
1059
1060 static void
1061 parseBytesLine64(int64_t * bptr, const char *units)
1062 {
1063 char *token;
1064 double d;
1065 int64_t m;
1066 int64_t u;
1067
1068 if ((u = parseBytesUnits(units)) == 0) {
1069 self_destruct();
1070 return;
1071 }
1072
1073 if ((token = strtok(NULL, w_space)) == NULL) {
1074 self_destruct();
1075 return;
1076 }
1077
1078 if (strcmp(token, "none") == 0 || strcmp(token, "-1") == 0) {
1079 *bptr = -1;
1080 return;
1081 }
1082
1083 d = xatof(token);
1084
1085 m = u; /* default to 'units' if none specified */
1086
1087 if (0.0 == d)
1088 (void) 0;
1089 else if ((token = strtok(NULL, w_space)) == NULL)
1090 debugs(3, DBG_CRITICAL, "WARNING: No units on '" <<
1091 config_input_line << "', assuming " <<
1092 d << " " << units );
1093 else if ((m = parseBytesUnits(token)) == 0) {
1094 self_destruct();
1095 return;
1096 }
1097
1098 *bptr = static_cast<int64_t>(m * d / u);
1099
1100 if (static_cast<double>(*bptr) * 2 != m * d / u * 2)
1101 self_destruct();
1102 }
1103
1104 static void
1105 parseBytesLine(size_t * bptr, const char *units)
1106 {
1107 char *token;
1108 double d;
1109 int m;
1110 int u;
1111
1112 if ((u = parseBytesUnits(units)) == 0) {
1113 self_destruct();
1114 return;
1115 }
1116
1117 if ((token = strtok(NULL, w_space)) == NULL) {
1118 self_destruct();
1119 return;
1120 }
1121
1122 if (strcmp(token, "none") == 0 || strcmp(token, "-1") == 0) {
1123 *bptr = static_cast<size_t>(-1);
1124 return;
1125 }
1126
1127 d = xatof(token);
1128
1129 m = u; /* default to 'units' if none specified */
1130
1131 if (0.0 == d)
1132 (void) 0;
1133 else if ((token = strtok(NULL, w_space)) == NULL)
1134 debugs(3, DBG_CRITICAL, "WARNING: No units on '" <<
1135 config_input_line << "', assuming " <<
1136 d << " " << units );
1137 else if ((m = parseBytesUnits(token)) == 0) {
1138 self_destruct();
1139 return;
1140 }
1141
1142 *bptr = static_cast<size_t>(m * d / u);
1143
1144 if (static_cast<double>(*bptr) * 2 != m * d / u * 2)
1145 self_destruct();
1146 }
1147
1148 #if !USE_DNSHELPER
1149 static void
1150 parseBytesLineSigned(ssize_t * bptr, const char *units)
1151 {
1152 char *token;
1153 double d;
1154 int m;
1155 int u;
1156
1157 if ((u = parseBytesUnits(units)) == 0) {
1158 self_destruct();
1159 return;
1160 }
1161
1162 if ((token = strtok(NULL, w_space)) == NULL) {
1163 self_destruct();
1164 return;
1165 }
1166
1167 if (strcmp(token, "none") == 0 || token[0] == '-' /* -N */) {
1168 *bptr = -1;
1169 return;
1170 }
1171
1172 d = xatof(token);
1173
1174 m = u; /* default to 'units' if none specified */
1175
1176 if (0.0 == d)
1177 (void) 0;
1178 else if ((token = strtok(NULL, w_space)) == NULL)
1179 debugs(3, DBG_CRITICAL, "WARNING: No units on '" <<
1180 config_input_line << "', assuming " <<
1181 d << " " << units );
1182 else if ((m = parseBytesUnits(token)) == 0) {
1183 self_destruct();
1184 return;
1185 }
1186
1187 *bptr = static_cast<size_t>(m * d / u);
1188
1189 if (static_cast<double>(*bptr) * 2 != m * d / u * 2)
1190 self_destruct();
1191 }
1192 #endif
1193
1194 #if USE_SSL
1195 /**
1196 * Parse bytes from a string.
1197 * Similar to the parseBytesLine function but parses the string value instead of
1198 * the current token value.
1199 */
1200 static void parseBytesOptionValue(size_t * bptr, const char *units, char const * value)
1201 {
1202 int u;
1203 if ((u = parseBytesUnits(units)) == 0) {
1204 self_destruct();
1205 return;
1206 }
1207
1208 // Find number from string beginning.
1209 char const * number_begin = value;
1210 char const * number_end = value;
1211
1212 while ((*number_end >= '0' && *number_end <= '9')) {
1213 ++number_end;
1214 }
1215
1216 String number;
1217 number.limitInit(number_begin, number_end - number_begin);
1218
1219 int d = xatoi(number.termedBuf());
1220 int m;
1221 if ((m = parseBytesUnits(number_end)) == 0) {
1222 self_destruct();
1223 return;
1224 }
1225
1226 *bptr = static_cast<size_t>(m * d / u);
1227 if (static_cast<double>(*bptr) * 2 != m * d / u * 2)
1228 self_destruct();
1229 }
1230 #endif
1231
1232 static size_t
1233 parseBytesUnits(const char *unit)
1234 {
1235 if (!strncasecmp(unit, B_BYTES_STR, strlen(B_BYTES_STR)))
1236 return 1;
1237
1238 if (!strncasecmp(unit, B_KBYTES_STR, strlen(B_KBYTES_STR)))
1239 return 1 << 10;
1240
1241 if (!strncasecmp(unit, B_MBYTES_STR, strlen(B_MBYTES_STR)))
1242 return 1 << 20;
1243
1244 if (!strncasecmp(unit, B_GBYTES_STR, strlen(B_GBYTES_STR)))
1245 return 1 << 30;
1246
1247 debugs(3, DBG_CRITICAL, "WARNING: Unknown bytes unit '" << unit << "'");
1248
1249 return 0;
1250 }
1251
1252 /*****************************************************************************
1253 * Max
1254 *****************************************************************************/
1255
1256 static void
1257 dump_acl(StoreEntry * entry, const char *name, ACL * ae)
1258 {
1259 wordlist *w;
1260 wordlist *v;
1261
1262 while (ae != NULL) {
1263 debugs(3, 3, "dump_acl: " << name << " " << ae->name);
1264 storeAppendPrintf(entry, "%s %s %s ",
1265 name,
1266 ae->name,
1267 ae->typeString());
1268 v = w = ae->dump();
1269
1270 while (v != NULL) {
1271 debugs(3, 3, "dump_acl: " << name << " " << ae->name << " " << v->key);
1272 storeAppendPrintf(entry, "%s ", v->key);
1273 v = v->next;
1274 }
1275
1276 storeAppendPrintf(entry, "\n");
1277 wordlistDestroy(&w);
1278 ae = ae->next;
1279 }
1280 }
1281
1282 static void
1283 parse_acl(ACL ** ae)
1284 {
1285 ACL::ParseAclLine(LegacyParser, ae);
1286 }
1287
1288 static void
1289 free_acl(ACL ** ae)
1290 {
1291 aclDestroyAcls(ae);
1292 }
1293
1294 void
1295 dump_acl_list(StoreEntry * entry, ACLList * head)
1296 {
1297 ACLList *l;
1298
1299 for (l = head; l; l = l->next) {
1300 storeAppendPrintf(entry, " %s%s",
1301 l->op ? null_string : "!",
1302 l->_acl->name);
1303 }
1304 }
1305
1306 void
1307 dump_acl_access(StoreEntry * entry, const char *name, acl_access * head)
1308 {
1309 acl_access *l;
1310
1311 for (l = head; l; l = l->next) {
1312 storeAppendPrintf(entry, "%s %s",
1313 name,
1314 l->allow ? "Allow" : "Deny");
1315 dump_acl_list(entry, l->aclList);
1316 storeAppendPrintf(entry, "\n");
1317 }
1318 }
1319
1320 static void
1321 parse_acl_access(acl_access ** head)
1322 {
1323 aclParseAccessLine(LegacyParser, head);
1324 }
1325
1326 static void
1327 free_acl_access(acl_access ** head)
1328 {
1329 aclDestroyAccessList(head);
1330 }
1331
1332 static void
1333 dump_address(StoreEntry * entry, const char *name, Ip::Address &addr)
1334 {
1335 char buf[MAX_IPSTRLEN];
1336 storeAppendPrintf(entry, "%s %s\n", name, addr.NtoA(buf,MAX_IPSTRLEN) );
1337 }
1338
1339 static void
1340 parse_address(Ip::Address *addr)
1341 {
1342 char *token = strtok(NULL, w_space);
1343
1344 if (!token) {
1345 self_destruct();
1346 return;
1347 }
1348
1349 if (!strcmp(token,"any_addr"))
1350 addr->SetAnyAddr();
1351 else if ( (!strcmp(token,"no_addr")) || (!strcmp(token,"full_mask")) )
1352 addr->SetNoAddr();
1353 else if ( (*addr = token) ) // try parse numeric/IPA
1354 (void) 0;
1355 else
1356 addr->GetHostByName(token); // dont use ipcache
1357 }
1358
1359 static void
1360 free_address(Ip::Address *addr)
1361 {
1362 addr->SetEmpty();
1363 }
1364
1365 CBDATA_TYPE(AclAddress);
1366
1367 static void
1368 dump_acl_address(StoreEntry * entry, const char *name, AclAddress * head)
1369 {
1370 char buf[MAX_IPSTRLEN];
1371 AclAddress *l;
1372
1373 for (l = head; l; l = l->next) {
1374 if (!l->addr.IsAnyAddr())
1375 storeAppendPrintf(entry, "%s %s", name, l->addr.NtoA(buf,MAX_IPSTRLEN));
1376 else
1377 storeAppendPrintf(entry, "%s autoselect", name);
1378
1379 dump_acl_list(entry, l->aclList);
1380
1381 storeAppendPrintf(entry, "\n");
1382 }
1383 }
1384
1385 static void
1386 freed_acl_address(void *data)
1387 {
1388 AclAddress *l = static_cast<AclAddress *>(data);
1389 aclDestroyAclList(&l->aclList);
1390 }
1391
1392 static void
1393 parse_acl_address(AclAddress ** head)
1394 {
1395 AclAddress *l;
1396 AclAddress **tail = head; /* sane name below */
1397 CBDATA_INIT_TYPE_FREECB(AclAddress, freed_acl_address);
1398 l = cbdataAlloc(AclAddress);
1399 parse_address(&l->addr);
1400 aclParseAclList(LegacyParser, &l->aclList);
1401
1402 while (*tail)
1403 tail = &(*tail)->next;
1404
1405 *tail = l;
1406 }
1407
1408 static void
1409 free_acl_address(AclAddress ** head)
1410 {
1411 while (*head) {
1412 AclAddress *l = *head;
1413 *head = l->next;
1414 cbdataFree(l);
1415 }
1416 }
1417
1418 CBDATA_TYPE(acl_tos);
1419
1420 static void
1421 dump_acl_tos(StoreEntry * entry, const char *name, acl_tos * head)
1422 {
1423 acl_tos *l;
1424
1425 for (l = head; l; l = l->next) {
1426 if (l->tos > 0)
1427 storeAppendPrintf(entry, "%s 0x%02X", name, l->tos);
1428 else
1429 storeAppendPrintf(entry, "%s none", name);
1430
1431 dump_acl_list(entry, l->aclList);
1432
1433 storeAppendPrintf(entry, "\n");
1434 }
1435 }
1436
1437 static void
1438 freed_acl_tos(void *data)
1439 {
1440 acl_tos *l = static_cast<acl_tos *>(data);
1441 aclDestroyAclList(&l->aclList);
1442 }
1443
1444 static void
1445 parse_acl_tos(acl_tos ** head)
1446 {
1447 acl_tos *l;
1448 acl_tos **tail = head; /* sane name below */
1449 unsigned int tos; /* Initially uint for strtoui. Casted to tos_t before return */
1450 char *token = strtok(NULL, w_space);
1451
1452 if (!token) {
1453 self_destruct();
1454 return;
1455 }
1456
1457 if (!xstrtoui(token, NULL, &tos, 0, std::numeric_limits<tos_t>::max())) {
1458 self_destruct();
1459 return;
1460 }
1461
1462 CBDATA_INIT_TYPE_FREECB(acl_tos, freed_acl_tos);
1463
1464 l = cbdataAlloc(acl_tos);
1465
1466 l->tos = (tos_t)tos;
1467
1468 aclParseAclList(LegacyParser, &l->aclList);
1469
1470 while (*tail)
1471 tail = &(*tail)->next;
1472
1473 *tail = l;
1474 }
1475
1476 static void
1477 free_acl_tos(acl_tos ** head)
1478 {
1479 while (*head) {
1480 acl_tos *l = *head;
1481 *head = l->next;
1482 l->next = NULL;
1483 cbdataFree(l);
1484 }
1485 }
1486
1487 #if SO_MARK && USE_LIBCAP
1488
1489 CBDATA_TYPE(acl_nfmark);
1490
1491 static void
1492 dump_acl_nfmark(StoreEntry * entry, const char *name, acl_nfmark * head)
1493 {
1494 acl_nfmark *l;
1495
1496 for (l = head; l; l = l->next) {
1497 if (l->nfmark > 0)
1498 storeAppendPrintf(entry, "%s 0x%02X", name, l->nfmark);
1499 else
1500 storeAppendPrintf(entry, "%s none", name);
1501
1502 dump_acl_list(entry, l->aclList);
1503
1504 storeAppendPrintf(entry, "\n");
1505 }
1506 }
1507
1508 static void
1509 freed_acl_nfmark(void *data)
1510 {
1511 acl_nfmark *l = static_cast<acl_nfmark *>(data);
1512 aclDestroyAclList(&l->aclList);
1513 }
1514
1515 static void
1516 parse_acl_nfmark(acl_nfmark ** head)
1517 {
1518 acl_nfmark *l;
1519 acl_nfmark **tail = head; /* sane name below */
1520 nfmark_t mark;
1521 char *token = strtok(NULL, w_space);
1522
1523 if (!token) {
1524 self_destruct();
1525 return;
1526 }
1527
1528 if (!xstrtoui(token, NULL, &mark, 0, std::numeric_limits<nfmark_t>::max())) {
1529 self_destruct();
1530 return;
1531 }
1532
1533 CBDATA_INIT_TYPE_FREECB(acl_nfmark, freed_acl_nfmark);
1534
1535 l = cbdataAlloc(acl_nfmark);
1536
1537 l->nfmark = mark;
1538
1539 aclParseAclList(LegacyParser, &l->aclList);
1540
1541 while (*tail)
1542 tail = &(*tail)->next;
1543
1544 *tail = l;
1545 }
1546
1547 static void
1548 free_acl_nfmark(acl_nfmark ** head)
1549 {
1550 while (*head) {
1551 acl_nfmark *l = *head;
1552 *head = l->next;
1553 l->next = NULL;
1554 cbdataFree(l);
1555 }
1556 }
1557 #endif /* SO_MARK */
1558
1559 CBDATA_TYPE(AclSizeLimit);
1560
1561 static void
1562 dump_acl_b_size_t(StoreEntry * entry, const char *name, AclSizeLimit * head)
1563 {
1564 AclSizeLimit *l;
1565
1566 for (l = head; l; l = l->next) {
1567 if (l->size != -1)
1568 storeAppendPrintf(entry, "%s %d %s\n", name, (int) l->size, B_BYTES_STR);
1569 else
1570 storeAppendPrintf(entry, "%s none", name);
1571
1572 dump_acl_list(entry, l->aclList);
1573
1574 storeAppendPrintf(entry, "\n");
1575 }
1576 }
1577
1578 static void
1579 freed_acl_b_size_t(void *data)
1580 {
1581 AclSizeLimit *l = static_cast<AclSizeLimit *>(data);
1582 aclDestroyAclList(&l->aclList);
1583 }
1584
1585 static void
1586 parse_acl_b_size_t(AclSizeLimit ** head)
1587 {
1588 AclSizeLimit *l;
1589 AclSizeLimit **tail = head; /* sane name below */
1590
1591 CBDATA_INIT_TYPE_FREECB(AclSizeLimit, freed_acl_b_size_t);
1592
1593 l = cbdataAlloc(AclSizeLimit);
1594
1595 parse_b_int64_t(&l->size);
1596
1597 aclParseAclList(LegacyParser, &l->aclList);
1598
1599 while (*tail)
1600 tail = &(*tail)->next;
1601
1602 *tail = l;
1603 }
1604
1605 static void
1606 free_acl_b_size_t(AclSizeLimit ** head)
1607 {
1608 while (*head) {
1609 AclSizeLimit *l = *head;
1610 *head = l->next;
1611 l->next = NULL;
1612 cbdataFree(l);
1613 }
1614 }
1615
1616 #if USE_DELAY_POOLS
1617
1618 #include "DelayPools.h"
1619 #include "DelayConfig.h"
1620 /* do nothing - free_delay_pool_count is the magic free function.
1621 * this is why delay_pool_count isn't just marked TYPE: u_short
1622 */
1623 #define free_delay_pool_class(X)
1624 #define free_delay_pool_access(X)
1625 #define free_delay_pool_rates(X)
1626 #define dump_delay_pool_class(X, Y, Z)
1627 #define dump_delay_pool_access(X, Y, Z)
1628 #define dump_delay_pool_rates(X, Y, Z)
1629
1630 static void
1631 free_delay_pool_count(DelayConfig * cfg)
1632 {
1633 cfg->freePoolCount();
1634 }
1635
1636 static void
1637 dump_delay_pool_count(StoreEntry * entry, const char *name, DelayConfig &cfg)
1638 {
1639 cfg.dumpPoolCount (entry, name);
1640 }
1641
1642 static void
1643 parse_delay_pool_count(DelayConfig * cfg)
1644 {
1645 cfg->parsePoolCount();
1646 }
1647
1648 static void
1649 parse_delay_pool_class(DelayConfig * cfg)
1650 {
1651 cfg->parsePoolClass();
1652 }
1653
1654 static void
1655 parse_delay_pool_rates(DelayConfig * cfg)
1656 {
1657 cfg->parsePoolRates();
1658 }
1659
1660 static void
1661 parse_delay_pool_access(DelayConfig * cfg)
1662 {
1663 cfg->parsePoolAccess(LegacyParser);
1664 }
1665
1666 #endif
1667
1668 #if USE_DELAY_POOLS
1669 #include "ClientDelayConfig.h"
1670 /* do nothing - free_client_delay_pool_count is the magic free function.
1671 * this is why client_delay_pool_count isn't just marked TYPE: u_short
1672 */
1673
1674 #define free_client_delay_pool_access(X)
1675 #define free_client_delay_pool_rates(X)
1676 #define dump_client_delay_pool_access(X, Y, Z)
1677 #define dump_client_delay_pool_rates(X, Y, Z)
1678
1679 static void
1680 free_client_delay_pool_count(ClientDelayConfig * cfg)
1681 {
1682 cfg->freePoolCount();
1683 }
1684
1685 static void
1686 dump_client_delay_pool_count(StoreEntry * entry, const char *name, ClientDelayConfig &cfg)
1687 {
1688 cfg.dumpPoolCount (entry, name);
1689 }
1690
1691 static void
1692 parse_client_delay_pool_count(ClientDelayConfig * cfg)
1693 {
1694 cfg->parsePoolCount();
1695 }
1696
1697 static void
1698 parse_client_delay_pool_rates(ClientDelayConfig * cfg)
1699 {
1700 cfg->parsePoolRates();
1701 }
1702
1703 static void
1704 parse_client_delay_pool_access(ClientDelayConfig * cfg)
1705 {
1706 cfg->parsePoolAccess(LegacyParser);
1707 }
1708 #endif
1709
1710 #if USE_HTTP_VIOLATIONS
1711 static void
1712 dump_http_header_access(StoreEntry * entry, const char *name, const HeaderManglers *manglers)
1713 {
1714 if (manglers)
1715 manglers->dumpAccess(entry, name);
1716 }
1717
1718 static void
1719 parse_http_header_access(HeaderManglers **pm)
1720 {
1721 char *t = NULL;
1722
1723 if ((t = strtok(NULL, w_space)) == NULL) {
1724 debugs(3, DBG_CRITICAL, "" << cfg_filename << " line " << config_lineno << ": " << config_input_line);
1725 debugs(3, DBG_CRITICAL, "parse_http_header_access: missing header name.");
1726 return;
1727 }
1728
1729 if (!*pm)
1730 *pm = new HeaderManglers;
1731 HeaderManglers *manglers = *pm;
1732 headerMangler *mangler = manglers->track(t);
1733 assert(mangler);
1734 parse_acl_access(&mangler->access_list);
1735 }
1736
1737 static void
1738 free_HeaderManglers(HeaderManglers **pm)
1739 {
1740 // we delete the entire http_header_* mangler configuration at once
1741 if (const HeaderManglers *manglers = *pm) {
1742 delete manglers;
1743 *pm = NULL;
1744 }
1745 }
1746
1747 static void
1748 dump_http_header_replace(StoreEntry * entry, const char *name, const HeaderManglers *manglers)
1749 {
1750 if (manglers)
1751 manglers->dumpReplacement(entry, name);
1752 }
1753
1754 static void
1755 parse_http_header_replace(HeaderManglers **pm)
1756 {
1757 char *t = NULL;
1758
1759 if ((t = strtok(NULL, w_space)) == NULL) {
1760 debugs(3, DBG_CRITICAL, "" << cfg_filename << " line " << config_lineno << ": " << config_input_line);
1761 debugs(3, DBG_CRITICAL, "parse_http_header_replace: missing header name.");
1762 return;
1763 }
1764
1765 const char *value = t + strlen(t) + 1;
1766
1767 if (!*pm)
1768 *pm = new HeaderManglers;
1769 HeaderManglers *manglers = *pm;
1770 manglers->setReplacement(t, value);
1771 }
1772
1773 #endif
1774
1775 static void
1776 dump_cachedir(StoreEntry * entry, const char *name, SquidConfig::_cacheSwap swap)
1777 {
1778 SwapDir *s;
1779 int i;
1780 assert (entry);
1781
1782 for (i = 0; i < swap.n_configured; ++i) {
1783 s = dynamic_cast<SwapDir *>(swap.swapDirs[i].getRaw());
1784 if (!s) continue;
1785 storeAppendPrintf(entry, "%s %s %s", name, s->type(), s->path);
1786 s->dump(*entry);
1787 storeAppendPrintf(entry, "\n");
1788 }
1789 }
1790
1791 static int
1792 check_null_string(char *s)
1793 {
1794 return s == NULL;
1795 }
1796
1797 #if USE_AUTH
1798 static void
1799 parse_authparam(Auth::ConfigVector * config)
1800 {
1801 char *type_str;
1802 char *param_str;
1803
1804 if ((type_str = strtok(NULL, w_space)) == NULL)
1805 self_destruct();
1806
1807 if ((param_str = strtok(NULL, w_space)) == NULL)
1808 self_destruct();
1809
1810 /* find a configuration for the scheme in the currently parsed configs... */
1811 Auth::Config *schemeCfg = Auth::Config::Find(type_str);
1812
1813 if (schemeCfg == NULL) {
1814 /* Create a configuration based on the scheme info */
1815 Auth::Scheme::Pointer theScheme = Auth::Scheme::Find(type_str);
1816
1817 if (theScheme == NULL) {
1818 debugs(3, DBG_CRITICAL, "Parsing Config File: Unknown authentication scheme '" << type_str << "'.");
1819 self_destruct();
1820 }
1821
1822 config->push_back(theScheme->createConfig());
1823 schemeCfg = Auth::Config::Find(type_str);
1824 if (schemeCfg == NULL) {
1825 debugs(3, DBG_CRITICAL, "Parsing Config File: Corruption configuring authentication scheme '" << type_str << "'.");
1826 self_destruct();
1827 }
1828 }
1829
1830 schemeCfg->parse(schemeCfg, config->size(), param_str);
1831 }
1832
1833 static void
1834 free_authparam(Auth::ConfigVector * cfg)
1835 {
1836 /* Wipe the Auth globals and Detach/Destruct component config + state. */
1837 cfg->clean();
1838
1839 /* remove our pointers to the probably-dead sub-configs */
1840 while (cfg->size()) {
1841 cfg->pop_back();
1842 }
1843
1844 /* on reconfigure initialize new auth schemes for the new config. */
1845 if (reconfiguring) {
1846 Auth::Init();
1847 }
1848 }
1849
1850 static void
1851 dump_authparam(StoreEntry * entry, const char *name, Auth::ConfigVector cfg)
1852 {
1853 for (Auth::ConfigVector::iterator i = cfg.begin(); i != cfg.end(); ++i)
1854 (*i)->dump(entry, name, (*i));
1855 }
1856 #endif /* USE_AUTH */
1857
1858 /* TODO: just return the object, the # is irrelevant */
1859 static int
1860 find_fstype(char *type)
1861 {
1862 for (size_t i = 0; i < StoreFileSystem::FileSystems().size(); ++i)
1863 if (strcasecmp(type, StoreFileSystem::FileSystems().items[i]->type()) == 0)
1864 return (int)i;
1865
1866 return (-1);
1867 }
1868
1869 static void
1870 parse_cachedir(SquidConfig::_cacheSwap * swap)
1871 {
1872 char *type_str;
1873 char *path_str;
1874 RefCount<SwapDir> sd;
1875 int i;
1876 int fs;
1877
1878 if ((type_str = strtok(NULL, w_space)) == NULL)
1879 self_destruct();
1880
1881 if ((path_str = strtok(NULL, w_space)) == NULL)
1882 self_destruct();
1883
1884 fs = find_fstype(type_str);
1885
1886 if (fs < 0)
1887 self_destruct();
1888
1889 /* reconfigure existing dir */
1890
1891 for (i = 0; i < swap->n_configured; ++i) {
1892 assert (swap->swapDirs[i].getRaw());
1893
1894 if ((strcasecmp(path_str, dynamic_cast<SwapDir *>(swap->swapDirs[i].getRaw())->path)) == 0) {
1895 /* this is specific to on-fs Stores. The right
1896 * way to handle this is probably to have a mapping
1897 * from paths to stores, and have on-fs stores
1898 * register with that, and lookip in that in their
1899 * own setup logic. RBC 20041225. TODO.
1900 */
1901
1902 sd = dynamic_cast<SwapDir *>(swap->swapDirs[i].getRaw());
1903
1904 if (strcmp(sd->type(), StoreFileSystem::FileSystems().items[fs]->type()) != 0) {
1905 debugs(3, DBG_CRITICAL, "ERROR: Can't change type of existing cache_dir " <<
1906 sd->type() << " " << sd->path << " to " << type_str << ". Restart required");
1907 return;
1908 }
1909
1910 sd->reconfigure();
1911
1912 update_maxobjsize();
1913
1914 return;
1915 }
1916 }
1917
1918 /* new cache_dir */
1919 if (swap->n_configured > 63) {
1920 /* 7 bits, signed */
1921 debugs(3, DBG_CRITICAL, "WARNING: There is a fixed maximum of 63 cache_dir entries Squid can handle.");
1922 debugs(3, DBG_CRITICAL, "WARNING: '" << path_str << "' is one to many.");
1923 self_destruct();
1924 return;
1925 }
1926
1927 allocate_new_swapdir(swap);
1928
1929 swap->swapDirs[swap->n_configured] = StoreFileSystem::FileSystems().items[fs]->createSwapDir();
1930
1931 sd = dynamic_cast<SwapDir *>(swap->swapDirs[swap->n_configured].getRaw());
1932
1933 /* parse the FS parameters and options */
1934 sd->parse(swap->n_configured, path_str);
1935
1936 ++swap->n_configured;
1937
1938 /* Update the max object size */
1939 update_maxobjsize();
1940 }
1941
1942 static const char *
1943 peer_type_str(const peer_t type)
1944 {
1945 const char * result;
1946
1947 switch (type) {
1948
1949 case PEER_PARENT:
1950 result = "parent";
1951 break;
1952
1953 case PEER_SIBLING:
1954 result = "sibling";
1955 break;
1956
1957 case PEER_MULTICAST:
1958 result = "multicast";
1959 break;
1960
1961 default:
1962 result = "unknown";
1963 break;
1964 }
1965
1966 return result;
1967 }
1968
1969 static void
1970 dump_peer(StoreEntry * entry, const char *name, CachePeer * p)
1971 {
1972 CachePeerDomainList *d;
1973 NeighborTypeDomainList *t;
1974 LOCAL_ARRAY(char, xname, 128);
1975
1976 while (p != NULL) {
1977 storeAppendPrintf(entry, "%s %s %s %d %d name=%s",
1978 name,
1979 p->host,
1980 neighborTypeStr(p),
1981 p->http_port,
1982 p->icp.port,
1983 p->name);
1984 dump_peer_options(entry, p);
1985
1986 for (d = p->peer_domain; d; d = d->next) {
1987 storeAppendPrintf(entry, "cache_peer_domain %s %s%s\n",
1988 p->host,
1989 d->do_ping ? null_string : "!",
1990 d->domain);
1991 }
1992
1993 if (p->access) {
1994 snprintf(xname, 128, "cache_peer_access %s", p->name);
1995 dump_acl_access(entry, xname, p->access);
1996 }
1997
1998 for (t = p->typelist; t; t = t->next) {
1999 storeAppendPrintf(entry, "neighbor_type_domain %s %s %s\n",
2000 p->host,
2001 peer_type_str(t->type),
2002 t->domain);
2003 }
2004
2005 p = p->next;
2006 }
2007 }
2008
2009 /**
2010 * utility function to prevent getservbyname() being called with a numeric value
2011 * on Windows at least it returns garage results.
2012 */
2013 static bool
2014 isUnsignedNumeric(const char *str, size_t len)
2015 {
2016 if (len < 1) return false;
2017
2018 for (; len >0 && *str; ++str, --len) {
2019 if (! isdigit(*str))
2020 return false;
2021 }
2022 return true;
2023 }
2024
2025 /**
2026 \param proto 'tcp' or 'udp' for protocol
2027 \returns Port the named service is supposed to be listening on.
2028 */
2029 static unsigned short
2030 GetService(const char *proto)
2031 {
2032 struct servent *port = NULL;
2033 /** Parses a port number or service name from the squid.conf */
2034 char *token = strtok(NULL, w_space);
2035 if (token == NULL) {
2036 self_destruct();
2037 return 0; /* NEVER REACHED */
2038 }
2039 /** Returns either the service port number from /etc/services */
2040 if ( !isUnsignedNumeric(token, strlen(token)) )
2041 port = getservbyname(token, proto);
2042 if (port != NULL) {
2043 return ntohs((unsigned short)port->s_port);
2044 }
2045 /** Or a numeric translation of the config text. */
2046 return xatos(token);
2047 }
2048
2049 /**
2050 \returns Port the named TCP service is supposed to be listening on.
2051 \copydoc GetService(const char *proto)
2052 */
2053 inline unsigned short
2054 GetTcpService(void)
2055 {
2056 return GetService("tcp");
2057 }
2058
2059 /**
2060 \returns Port the named UDP service is supposed to be listening on.
2061 \copydoc GetService(const char *proto)
2062 */
2063 inline unsigned short
2064 GetUdpService(void)
2065 {
2066 return GetService("udp");
2067 }
2068
2069 static void
2070 parse_peer(CachePeer ** head)
2071 {
2072 char *token = NULL;
2073 CachePeer *p;
2074 CBDATA_INIT_TYPE_FREECB(CachePeer, peerDestroy);
2075 p = cbdataAlloc(CachePeer);
2076 p->http_port = CACHE_HTTP_PORT;
2077 p->icp.port = CACHE_ICP_PORT;
2078 p->weight = 1;
2079 p->basetime = 0;
2080 p->stats.logged_state = PEER_ALIVE;
2081
2082 if ((token = strtok(NULL, w_space)) == NULL)
2083 self_destruct();
2084
2085 p->host = xstrdup(token);
2086
2087 p->name = xstrdup(token);
2088
2089 if ((token = strtok(NULL, w_space)) == NULL)
2090 self_destruct();
2091
2092 p->type = parseNeighborType(token);
2093
2094 if (p->type == PEER_MULTICAST) {
2095 p->options.no_digest = 1;
2096 p->options.no_netdb_exchange = 1;
2097 }
2098
2099 p->http_port = GetTcpService();
2100
2101 if (!p->http_port)
2102 self_destruct();
2103
2104 p->icp.port = GetUdpService();
2105 p->connection_auth = 2; /* auto */
2106
2107 while ((token = strtok(NULL, w_space))) {
2108 if (!strcasecmp(token, "proxy-only")) {
2109 p->options.proxy_only = 1;
2110 } else if (!strcasecmp(token, "no-query")) {
2111 p->options.no_query = 1;
2112 } else if (!strcasecmp(token, "background-ping")) {
2113 p->options.background_ping = 1;
2114 } else if (!strcasecmp(token, "no-digest")) {
2115 p->options.no_digest = 1;
2116 } else if (!strcasecmp(token, "no-tproxy")) {
2117 p->options.no_tproxy = 1;
2118 } else if (!strcasecmp(token, "multicast-responder")) {
2119 p->options.mcast_responder = 1;
2120 #if PEER_MULTICAST_SIBLINGS
2121 } else if (!strcasecmp(token, "multicast-siblings")) {
2122 p->options.mcast_siblings = 1;
2123 #endif
2124 } else if (!strncasecmp(token, "weight=", 7)) {
2125 p->weight = xatoi(token + 7);
2126 } else if (!strncasecmp(token, "basetime=", 9)) {
2127 p->basetime = xatoi(token + 9);
2128 } else if (!strcasecmp(token, "closest-only")) {
2129 p->options.closest_only = 1;
2130 } else if (!strncasecmp(token, "ttl=", 4)) {
2131 p->mcast.ttl = xatoi(token + 4);
2132
2133 if (p->mcast.ttl < 0)
2134 p->mcast.ttl = 0;
2135
2136 if (p->mcast.ttl > 128)
2137 p->mcast.ttl = 128;
2138 } else if (!strcasecmp(token, "default")) {
2139 p->options.default_parent = 1;
2140 } else if (!strcasecmp(token, "round-robin")) {
2141 p->options.roundrobin = 1;
2142 } else if (!strcasecmp(token, "weighted-round-robin")) {
2143 p->options.weighted_roundrobin = 1;
2144 #if USE_HTCP
2145 } else if (!strcasecmp(token, "htcp")) {
2146 p->options.htcp = 1;
2147 } else if (!strncasecmp(token, "htcp=", 5) || !strncasecmp(token, "htcp-", 5)) {
2148 /* Note: The htcp- form is deprecated, replaced by htcp= */
2149 p->options.htcp = 1;
2150 char *tmp = xstrdup(token+5);
2151 char *mode, *nextmode;
2152 for (mode = nextmode = tmp; mode; mode = nextmode) {
2153 nextmode = strchr(mode, ',');
2154 if (nextmode) {
2155 *nextmode = '\0';
2156 ++nextmode;
2157 }
2158 if (!strcasecmp(mode, "no-clr")) {
2159 if (p->options.htcp_only_clr)
2160 fatalf("parse_peer: can't set htcp-no-clr and htcp-only-clr simultaneously");
2161 p->options.htcp_no_clr = 1;
2162 } else if (!strcasecmp(mode, "no-purge-clr")) {
2163 p->options.htcp_no_purge_clr = 1;
2164 } else if (!strcasecmp(mode, "only-clr")) {
2165 if (p->options.htcp_no_clr)
2166 fatalf("parse_peer: can't set htcp no-clr and only-clr simultaneously");
2167 p->options.htcp_only_clr = 1;
2168 } else if (!strcasecmp(mode, "forward-clr")) {
2169 p->options.htcp_forward_clr = 1;
2170 } else if (!strcasecmp(mode, "oldsquid")) {
2171 p->options.htcp_oldsquid = 1;
2172 } else {
2173 fatalf("invalid HTCP mode '%s'", mode);
2174 }
2175 }
2176 safe_free(tmp);
2177 #endif
2178 } else if (!strcasecmp(token, "no-netdb-exchange")) {
2179 p->options.no_netdb_exchange = 1;
2180
2181 } else if (!strcasecmp(token, "carp")) {
2182 if (p->type != PEER_PARENT)
2183 fatalf("parse_peer: non-parent carp peer %s/%d\n", p->host, p->http_port);
2184
2185 p->options.carp = 1;
2186 } else if (!strncasecmp(token, "carp-key=", 9)) {
2187 if (p->options.carp != 1)
2188 fatalf("parse_peer: carp-key specified on non-carp peer %s/%d\n", p->host, p->http_port);
2189 p->options.carp_key.set=1;
2190 char *nextkey=token+strlen("carp-key="), *key=nextkey;
2191 for (; key; key = nextkey) {
2192 nextkey=strchr(key,',');
2193 if (nextkey) ++nextkey; // skip the comma, any
2194 if (0==strncasecmp(key,"scheme",6)) {
2195 p->options.carp_key.scheme=1;
2196 } else if (0==strncasecmp(key,"host",4)) {
2197 p->options.carp_key.host=1;
2198 } else if (0==strncasecmp(key,"port",4)) {
2199 p->options.carp_key.port=1;
2200 } else if (0==strncasecmp(key,"path",4)) {
2201 p->options.carp_key.path=1;
2202 } else if (0==strncasecmp(key,"params",6)) {
2203 p->options.carp_key.params=1;
2204 } else {
2205 fatalf("invalid carp-key '%s'",key);
2206 }
2207 }
2208 } else if (!strcasecmp(token, "userhash")) {
2209 #if USE_AUTH
2210 if (p->type != PEER_PARENT)
2211 fatalf("parse_peer: non-parent userhash peer %s/%d\n", p->host, p->http_port);
2212
2213 p->options.userhash = 1;
2214 #else
2215 fatalf("parse_peer: userhash requires authentication. peer %s/%d\n", p->host, p->http_port);
2216 #endif
2217 } else if (!strcasecmp(token, "sourcehash")) {
2218 if (p->type != PEER_PARENT)
2219 fatalf("parse_peer: non-parent sourcehash peer %s/%d\n", p->host, p->http_port);
2220
2221 p->options.sourcehash = 1;
2222
2223 } else if (!strcasecmp(token, "no-delay")) {
2224 #if USE_DELAY_POOLS
2225 p->options.no_delay = 1;
2226 #else
2227 debugs(0, DBG_CRITICAL, "WARNING: cache_peer option 'no-delay' requires --enable-delay-pools");
2228 #endif
2229 } else if (!strncasecmp(token, "login=", 6)) {
2230 p->login = xstrdup(token + 6);
2231 rfc1738_unescape(p->login);
2232 } else if (!strncasecmp(token, "connect-timeout=", 16)) {
2233 p->connect_timeout = xatoi(token + 16);
2234 } else if (!strncasecmp(token, "connect-fail-limit=", 19)) {
2235 p->connect_fail_limit = xatoi(token + 19);
2236 #if USE_CACHE_DIGESTS
2237 } else if (!strncasecmp(token, "digest-url=", 11)) {
2238 p->digest_url = xstrdup(token + 11);
2239 #endif
2240
2241 } else if (!strcasecmp(token, "allow-miss")) {
2242 p->options.allow_miss = 1;
2243 } else if (!strncasecmp(token, "max-conn=", 9)) {
2244 p->max_conn = xatoi(token + 9);
2245 } else if (!strcasecmp(token, "originserver")) {
2246 p->options.originserver = 1;
2247 } else if (!strncasecmp(token, "name=", 5)) {
2248 safe_free(p->name);
2249
2250 if (token[5])
2251 p->name = xstrdup(token + 5);
2252 } else if (!strncasecmp(token, "forceddomain=", 13)) {
2253 safe_free(p->domain);
2254
2255 if (token[13])
2256 p->domain = xstrdup(token + 13);
2257
2258 #if USE_SSL
2259
2260 } else if (strcmp(token, "ssl") == 0) {
2261 p->use_ssl = 1;
2262 } else if (strncmp(token, "sslcert=", 8) == 0) {
2263 safe_free(p->sslcert);
2264 p->sslcert = xstrdup(token + 8);
2265 } else if (strncmp(token, "sslkey=", 7) == 0) {
2266 safe_free(p->sslkey);
2267 p->sslkey = xstrdup(token + 7);
2268 } else if (strncmp(token, "sslversion=", 11) == 0) {
2269 p->sslversion = atoi(token + 11);
2270 } else if (strncmp(token, "ssloptions=", 11) == 0) {
2271 safe_free(p->ssloptions);
2272 p->ssloptions = xstrdup(token + 11);
2273 } else if (strncmp(token, "sslcipher=", 10) == 0) {
2274 safe_free(p->sslcipher);
2275 p->sslcipher = xstrdup(token + 10);
2276 } else if (strncmp(token, "sslcafile=", 10) == 0) {
2277 safe_free(p->sslcafile);
2278 p->sslcafile = xstrdup(token + 10);
2279 } else if (strncmp(token, "sslcapath=", 10) == 0) {
2280 safe_free(p->sslcapath);
2281 p->sslcapath = xstrdup(token + 10);
2282 } else if (strncmp(token, "sslcrlfile=", 11) == 0) {
2283 safe_free(p->sslcrlfile);
2284 p->sslcapath = xstrdup(token + 10);
2285 } else if (strncmp(token, "sslflags=", 9) == 0) {
2286 safe_free(p->sslflags);
2287 p->sslflags = xstrdup(token + 9);
2288 } else if (strncmp(token, "ssldomain=", 10) == 0) {
2289 safe_free(p->ssldomain);
2290 p->ssldomain = xstrdup(token + 10);
2291 #endif
2292
2293 } else if (strcmp(token, "front-end-https") == 0) {
2294 p->front_end_https = 1;
2295 } else if (strcmp(token, "front-end-https=on") == 0) {
2296 p->front_end_https = 1;
2297 } else if (strcmp(token, "front-end-https=auto") == 0) {
2298 p->front_end_https = 2;
2299 } else if (strcmp(token, "connection-auth=off") == 0) {
2300 p->connection_auth = 0;
2301 } else if (strcmp(token, "connection-auth") == 0) {
2302 p->connection_auth = 1;
2303 } else if (strcmp(token, "connection-auth=on") == 0) {
2304 p->connection_auth = 1;
2305 } else if (strcmp(token, "connection-auth=auto") == 0) {
2306 p->connection_auth = 2;
2307 } else {
2308 debugs(3, DBG_CRITICAL, "parse_peer: token='" << token << "'");
2309 self_destruct();
2310 }
2311 }
2312
2313 if (peerFindByName(p->name))
2314 fatalf("ERROR: cache_peer %s specified twice\n", p->name);
2315
2316 if (p->weight < 1)
2317 p->weight = 1;
2318
2319 if (p->connect_fail_limit < 1)
2320 p->connect_fail_limit = 10;
2321
2322 p->icp.version = ICP_VERSION_CURRENT;
2323
2324 p->testing_now = false;
2325
2326 #if USE_CACHE_DIGESTS
2327
2328 if (!p->options.no_digest) {
2329 /* XXX This looks odd.. who has the original pointer
2330 * then?
2331 */
2332 PeerDigest *pd = peerDigestCreate(p);
2333 p->digest = cbdataReference(pd);
2334 }
2335
2336 #endif
2337
2338 p->index = ++Config.npeers;
2339
2340 while (*head != NULL)
2341 head = &(*head)->next;
2342
2343 *head = p;
2344
2345 peerClearRRStart();
2346 }
2347
2348 static void
2349 free_peer(CachePeer ** P)
2350 {
2351 CachePeer *p;
2352
2353 while ((p = *P) != NULL) {
2354 *P = p->next;
2355 #if USE_CACHE_DIGESTS
2356
2357 cbdataReferenceDone(p->digest);
2358 #endif
2359
2360 cbdataFree(p);
2361 }
2362
2363 Config.npeers = 0;
2364 }
2365
2366 static void
2367 dump_cachemgrpasswd(StoreEntry * entry, const char *name, Mgr::ActionPasswordList * list)
2368 {
2369 wordlist *w;
2370
2371 while (list != NULL) {
2372 if (strcmp(list->passwd, "none") && strcmp(list->passwd, "disable"))
2373 storeAppendPrintf(entry, "%s XXXXXXXXXX", name);
2374 else
2375 storeAppendPrintf(entry, "%s %s", name, list->passwd);
2376
2377 for (w = list->actions; w != NULL; w = w->next) {
2378 storeAppendPrintf(entry, " %s", w->key);
2379 }
2380
2381 storeAppendPrintf(entry, "\n");
2382 list = list->next;
2383 }
2384 }
2385
2386 static void
2387 parse_cachemgrpasswd(Mgr::ActionPasswordList ** head)
2388 {
2389 char *passwd = NULL;
2390 wordlist *actions = NULL;
2391 Mgr::ActionPasswordList *p;
2392 Mgr::ActionPasswordList **P;
2393 parse_string(&passwd);
2394 parse_wordlist(&actions);
2395 p = new Mgr::ActionPasswordList;
2396 p->passwd = passwd;
2397 p->actions = actions;
2398
2399 for (P = head; *P; P = &(*P)->next) {
2400 /*
2401 * See if any of the actions from this line already have a
2402 * password from previous lines. The password checking
2403 * routines in cache_manager.c take the the password from
2404 * the first Mgr::ActionPasswordList that contains the
2405 * requested action. Thus, we should warn users who might
2406 * think they can have two passwords for the same action.
2407 */
2408 wordlist *w;
2409 wordlist *u;
2410
2411 for (w = (*P)->actions; w; w = w->next) {
2412 for (u = actions; u; u = u->next) {
2413 if (strcmp(w->key, u->key))
2414 continue;
2415
2416 debugs(0, DBG_CRITICAL, "WARNING: action '" << u->key << "' (line " << config_lineno << ") already has a password");
2417 }
2418 }
2419 }
2420
2421 *P = p;
2422 }
2423
2424 static void
2425 free_cachemgrpasswd(Mgr::ActionPasswordList ** head)
2426 {
2427 Mgr::ActionPasswordList *p;
2428
2429 while ((p = *head) != NULL) {
2430 *head = p->next;
2431 xfree(p->passwd);
2432 wordlistDestroy(&p->actions);
2433 xfree(p);
2434 }
2435 }
2436
2437 static void
2438 dump_denyinfo(StoreEntry * entry, const char *name, AclDenyInfoList * var)
2439 {
2440 AclNameList *a;
2441
2442 while (var != NULL) {
2443 storeAppendPrintf(entry, "%s %s", name, var->err_page_name);
2444
2445 for (a = var->acl_list; a != NULL; a = a->next)
2446 storeAppendPrintf(entry, " %s", a->name);
2447
2448 storeAppendPrintf(entry, "\n");
2449
2450 var = var->next;
2451 }
2452 }
2453
2454 static void
2455 parse_denyinfo(AclDenyInfoList ** var)
2456 {
2457 aclParseDenyInfoLine(var);
2458 }
2459
2460 void
2461 free_denyinfo(AclDenyInfoList ** list)
2462 {
2463 AclDenyInfoList *a = NULL;
2464 AclDenyInfoList *a_next = NULL;
2465 AclNameList *l = NULL;
2466 AclNameList *l_next = NULL;
2467
2468 for (a = *list; a; a = a_next) {
2469 for (l = a->acl_list; l; l = l_next) {
2470 l_next = l->next;
2471 memFree(l, MEM_ACL_NAME_LIST);
2472 l = NULL;
2473 }
2474
2475 a_next = a->next;
2476 memFree(a, MEM_ACL_DENY_INFO_LIST);
2477 a = NULL;
2478 }
2479
2480 *list = NULL;
2481 }
2482
2483 static void
2484 parse_peer_access(void)
2485 {
2486 char *host = NULL;
2487 CachePeer *p;
2488
2489 if (!(host = strtok(NULL, w_space)))
2490 self_destruct();
2491
2492 if ((p = peerFindByName(host)) == NULL) {
2493 debugs(15, DBG_CRITICAL, "" << cfg_filename << ", line " << config_lineno << ": No cache_peer '" << host << "'");
2494 return;
2495 }
2496
2497 aclParseAccessLine(LegacyParser, &p->access);
2498 }
2499
2500 static void
2501 parse_hostdomain(void)
2502 {
2503 char *host = NULL;
2504 char *domain = NULL;
2505
2506 if (!(host = strtok(NULL, w_space)))
2507 self_destruct();
2508
2509 while ((domain = strtok(NULL, list_sep))) {
2510 CachePeerDomainList *l = NULL;
2511 CachePeerDomainList **L = NULL;
2512 CachePeer *p;
2513
2514 if ((p = peerFindByName(host)) == NULL) {
2515 debugs(15, DBG_CRITICAL, "" << cfg_filename << ", line " << config_lineno << ": No cache_peer '" << host << "'");
2516 continue;
2517 }
2518
2519 l = static_cast<CachePeerDomainList *>(xcalloc(1, sizeof(CachePeerDomainList)));
2520 l->do_ping = true;
2521
2522 if (*domain == '!') { /* check for !.edu */
2523 l->do_ping = false;
2524 ++domain;
2525 }
2526
2527 l->domain = xstrdup(domain);
2528
2529 for (L = &(p->peer_domain); *L; L = &((*L)->next));
2530 *L = l;
2531 }
2532 }
2533
2534 static void
2535 parse_hostdomaintype(void)
2536 {
2537 char *host = NULL;
2538 char *type = NULL;
2539 char *domain = NULL;
2540
2541 if (!(host = strtok(NULL, w_space)))
2542 self_destruct();
2543
2544 if (!(type = strtok(NULL, w_space)))
2545 self_destruct();
2546
2547 while ((domain = strtok(NULL, list_sep))) {
2548 NeighborTypeDomainList *l = NULL;
2549 NeighborTypeDomainList **L = NULL;
2550 CachePeer *p;
2551
2552 if ((p = peerFindByName(host)) == NULL) {
2553 debugs(15, DBG_CRITICAL, "" << cfg_filename << ", line " << config_lineno << ": No cache_peer '" << host << "'");
2554 return;
2555 }
2556
2557 l = static_cast<NeighborTypeDomainList *>(xcalloc(1, sizeof(NeighborTypeDomainList)));
2558 l->type = parseNeighborType(type);
2559 l->domain = xstrdup(domain);
2560
2561 for (L = &(p->typelist); *L; L = &((*L)->next));
2562 *L = l;
2563 }
2564 }
2565
2566 static void
2567 dump_int(StoreEntry * entry, const char *name, int var)
2568 {
2569 storeAppendPrintf(entry, "%s %d\n", name, var);
2570 }
2571
2572 void
2573 parse_int(int *var)
2574 {
2575 int i;
2576 i = GetInteger();
2577 *var = i;
2578 }
2579
2580 static void
2581 free_int(int *var)
2582 {
2583 *var = 0;
2584 }
2585
2586 static void
2587 dump_onoff(StoreEntry * entry, const char *name, int var)
2588 {
2589 storeAppendPrintf(entry, "%s %s\n", name, var ? "on" : "off");
2590 }
2591
2592 void
2593 parse_onoff(int *var)
2594 {
2595 char *token = strtok(NULL, w_space);
2596
2597 if (token == NULL)
2598 self_destruct();
2599
2600 if (!strcasecmp(token, "on") || !strcasecmp(token, "enable"))
2601 *var = 1;
2602 else
2603 *var = 0;
2604 }
2605
2606 #define free_onoff free_int
2607
2608 static void
2609 dump_tristate(StoreEntry * entry, const char *name, int var)
2610 {
2611 const char *state;
2612
2613 if (var > 0)
2614 state = "on";
2615 else if (var < 0)
2616 state = "warn";
2617 else
2618 state = "off";
2619
2620 storeAppendPrintf(entry, "%s %s\n", name, state);
2621 }
2622
2623 static void
2624 parse_tristate(int *var)
2625 {
2626 char *token = strtok(NULL, w_space);
2627
2628 if (token == NULL)
2629 self_destruct();
2630
2631 if (!strcasecmp(token, "on") || !strcasecmp(token, "enable"))
2632 *var = 1;
2633 else if (!strcasecmp(token, "warn"))
2634 *var = -1;
2635 else
2636 *var = 0;
2637 }
2638
2639 #define free_tristate free_int
2640
2641 static void
2642 dump_refreshpattern(StoreEntry * entry, const char *name, RefreshPattern * head)
2643 {
2644 while (head != NULL) {
2645 storeAppendPrintf(entry, "%s%s %s %d %d%% %d",
2646 name,
2647 head->flags.icase ? " -i" : null_string,
2648 head->pattern,
2649 (int) head->min / 60,
2650 (int) (100.0 * head->pct + 0.5),
2651 (int) head->max / 60);
2652
2653 if (head->max_stale >= 0)
2654 storeAppendPrintf(entry, " max-stale=%d", head->max_stale);
2655
2656 if (head->flags.refresh_ims)
2657 storeAppendPrintf(entry, " refresh-ims");
2658
2659 if (head->flags.store_stale)
2660 storeAppendPrintf(entry, " store-stale");
2661
2662 #if USE_HTTP_VIOLATIONS
2663
2664 if (head->flags.override_expire)
2665 storeAppendPrintf(entry, " override-expire");
2666
2667 if (head->flags.override_lastmod)
2668 storeAppendPrintf(entry, " override-lastmod");
2669
2670 if (head->flags.reload_into_ims)
2671 storeAppendPrintf(entry, " reload-into-ims");
2672
2673 if (head->flags.ignore_reload)
2674 storeAppendPrintf(entry, " ignore-reload");
2675
2676 if (head->flags.ignore_no_store)
2677 storeAppendPrintf(entry, " ignore-no-store");
2678
2679 if (head->flags.ignore_must_revalidate)
2680 storeAppendPrintf(entry, " ignore-must-revalidate");
2681
2682 if (head->flags.ignore_private)
2683 storeAppendPrintf(entry, " ignore-private");
2684
2685 if (head->flags.ignore_auth)
2686 storeAppendPrintf(entry, " ignore-auth");
2687
2688 #endif
2689
2690 storeAppendPrintf(entry, "\n");
2691
2692 head = head->next;
2693 }
2694 }
2695
2696 static void
2697 parse_refreshpattern(RefreshPattern ** head)
2698 {
2699 char *token;
2700 char *pattern;
2701 time_t min = 0;
2702 double pct = 0.0;
2703 time_t max = 0;
2704 int refresh_ims = 0;
2705 int store_stale = 0;
2706 int max_stale = -1;
2707
2708 #if USE_HTTP_VIOLATIONS
2709
2710 int override_expire = 0;
2711 int override_lastmod = 0;
2712 int reload_into_ims = 0;
2713 int ignore_reload = 0;
2714 int ignore_no_store = 0;
2715 int ignore_must_revalidate = 0;
2716 int ignore_private = 0;
2717 int ignore_auth = 0;
2718 #endif
2719
2720 int i;
2721 RefreshPattern *t;
2722 regex_t comp;
2723 int errcode;
2724 int flags = REG_EXTENDED | REG_NOSUB;
2725
2726 if ((token = strtok(NULL, w_space)) == NULL) {
2727 self_destruct();
2728 return;
2729 }
2730
2731 if (strcmp(token, "-i") == 0) {
2732 flags |= REG_ICASE;
2733 token = strtok(NULL, w_space);
2734 } else if (strcmp(token, "+i") == 0) {
2735 flags &= ~REG_ICASE;
2736 token = strtok(NULL, w_space);
2737 }
2738
2739 if (token == NULL) {
2740 self_destruct();
2741 return;
2742 }
2743
2744 pattern = xstrdup(token);
2745
2746 i = GetInteger(); /* token: min */
2747
2748 /* catch negative and insanely huge values close to 32-bit wrap */
2749 if (i < 0) {
2750 debugs(3, DBG_IMPORTANT, "WARNING: refresh_pattern minimum age negative. Cropped back to zero.");
2751 i = 0;
2752 }
2753 if (i > 60*24*365) {
2754 debugs(3, DBG_IMPORTANT, "WARNING: refresh_pattern minimum age too high. Cropped back to 1 year.");
2755 i = 60*24*365;
2756 }
2757
2758 min = (time_t) (i * 60); /* convert minutes to seconds */
2759
2760 i = GetInteger(); /* token: pct */
2761
2762 pct = (double) i / 100.0;
2763
2764 i = GetInteger(); /* token: max */
2765
2766 /* catch negative and insanely huge values close to 32-bit wrap */
2767 if (i < 0) {
2768 debugs(3, DBG_IMPORTANT, "WARNING: refresh_pattern maximum age negative. Cropped back to zero.");
2769 i = 0;
2770 }
2771 if (i > 60*24*365) {
2772 debugs(3, DBG_IMPORTANT, "WARNING: refresh_pattern maximum age too high. Cropped back to 1 year.");
2773 i = 60*24*365;
2774 }
2775
2776 max = (time_t) (i * 60); /* convert minutes to seconds */
2777
2778 /* Options */
2779 while ((token = strtok(NULL, w_space)) != NULL) {
2780 if (!strcmp(token, "refresh-ims")) {
2781 refresh_ims = 1;
2782 } else if (!strcmp(token, "store-stale")) {
2783 store_stale = 1;
2784 } else if (!strncmp(token, "max-stale=", 10)) {
2785 max_stale = atoi(token + 10);
2786 #if USE_HTTP_VIOLATIONS
2787
2788 } else if (!strcmp(token, "override-expire"))
2789 override_expire = 1;
2790 else if (!strcmp(token, "override-lastmod"))
2791 override_lastmod = 1;
2792 else if (!strcmp(token, "ignore-no-store"))
2793 ignore_no_store = 1;
2794 else if (!strcmp(token, "ignore-must-revalidate"))
2795 ignore_must_revalidate = 1;
2796 else if (!strcmp(token, "ignore-private"))
2797 ignore_private = 1;
2798 else if (!strcmp(token, "ignore-auth"))
2799 ignore_auth = 1;
2800 else if (!strcmp(token, "reload-into-ims")) {
2801 reload_into_ims = 1;
2802 refresh_nocache_hack = 1;
2803 /* tell client_side.c that this is used */
2804 } else if (!strcmp(token, "ignore-reload")) {
2805 ignore_reload = 1;
2806 refresh_nocache_hack = 1;
2807 /* tell client_side.c that this is used */
2808 #endif
2809
2810 } else if (!strcmp(token, "ignore-no-cache")) {
2811 debugs(22, DBG_PARSE_NOTE(2), "UPGRADE: refresh_pattern option 'ignore-no-cache' is obsolete. Remove it.");
2812 } else
2813 debugs(22, DBG_CRITICAL, "refreshAddToList: Unknown option '" << pattern << "': " << token);
2814 }
2815
2816 if ((errcode = regcomp(&comp, pattern, flags)) != 0) {
2817 char errbuf[256];
2818 regerror(errcode, &comp, errbuf, sizeof errbuf);
2819 debugs(22, DBG_CRITICAL, "" << cfg_filename << " line " << config_lineno << ": " << config_input_line);
2820 debugs(22, DBG_CRITICAL, "refreshAddToList: Invalid regular expression '" << pattern << "': " << errbuf);
2821 return;
2822 }
2823
2824 pct = pct < 0.0 ? 0.0 : pct;
2825 max = max < 0 ? 0 : max;
2826 t = static_cast<RefreshPattern *>(xcalloc(1, sizeof(RefreshPattern)));
2827 t->pattern = (char *) xstrdup(pattern);
2828 t->compiled_pattern = comp;
2829 t->min = min;
2830 t->pct = pct;
2831 t->max = max;
2832
2833 if (flags & REG_ICASE)
2834 t->flags.icase = 1;
2835
2836 if (refresh_ims)
2837 t->flags.refresh_ims = 1;
2838
2839 if (store_stale)
2840 t->flags.store_stale = 1;
2841
2842 t->max_stale = max_stale;
2843
2844 #if USE_HTTP_VIOLATIONS
2845
2846 if (override_expire)
2847 t->flags.override_expire = 1;
2848
2849 if (override_lastmod)
2850 t->flags.override_lastmod = 1;
2851
2852 if (reload_into_ims)
2853 t->flags.reload_into_ims = 1;
2854
2855 if (ignore_reload)
2856 t->flags.ignore_reload = 1;
2857
2858 if (ignore_no_store)
2859 t->flags.ignore_no_store = 1;
2860
2861 if (ignore_must_revalidate)
2862 t->flags.ignore_must_revalidate = 1;
2863
2864 if (ignore_private)
2865 t->flags.ignore_private = 1;
2866
2867 if (ignore_auth)
2868 t->flags.ignore_auth = 1;
2869
2870 #endif
2871
2872 t->next = NULL;
2873
2874 while (*head)
2875 head = &(*head)->next;
2876
2877 *head = t;
2878
2879 safe_free(pattern);
2880 }
2881
2882 static void
2883 free_refreshpattern(RefreshPattern ** head)
2884 {
2885 RefreshPattern *t;
2886
2887 while ((t = *head) != NULL) {
2888 *head = t->next;
2889 safe_free(t->pattern);
2890 regfree(&t->compiled_pattern);
2891 safe_free(t);
2892 }
2893
2894 #if USE_HTTP_VIOLATIONS
2895 refresh_nocache_hack = 0;
2896
2897 #endif
2898 }
2899
2900 static void
2901 dump_string(StoreEntry * entry, const char *name, char *var)
2902 {
2903 if (var != NULL)
2904 storeAppendPrintf(entry, "%s %s\n", name, var);
2905 }
2906
2907 static void
2908 parse_string(char **var)
2909 {
2910 char *token = strtok(NULL, w_space);
2911 safe_free(*var);
2912
2913 if (token == NULL)
2914 self_destruct();
2915
2916 *var = xstrdup(token);
2917 }
2918
2919 void
2920 ConfigParser::ParseString(char **var)
2921 {
2922 parse_string(var);
2923 }
2924
2925 void
2926 ConfigParser::ParseString(String *var)
2927 {
2928 char *token = strtok(NULL, w_space);
2929
2930 if (token == NULL)
2931 self_destruct();
2932
2933 var->reset(token);
2934 }
2935
2936 static void
2937 free_string(char **var)
2938 {
2939 safe_free(*var);
2940 }
2941
2942 void
2943 parse_eol(char *volatile *var)
2944 {
2945 if (!var) {
2946 self_destruct();
2947 return;
2948 }
2949
2950 unsigned char *token = (unsigned char *) strtok(NULL, null_string);
2951 safe_free(*var);
2952
2953 if (!token) {
2954 self_destruct();
2955 return;
2956 }
2957
2958 while (*token && xisspace(*token))
2959 ++token;
2960
2961 if (!*token) {
2962 self_destruct();
2963 return;
2964 }
2965
2966 *var = xstrdup((char *) token);
2967 }
2968
2969 #define dump_eol dump_string
2970 #define free_eol free_string
2971
2972 static void
2973 dump_time_t(StoreEntry * entry, const char *name, time_t var)
2974 {
2975 storeAppendPrintf(entry, "%s %d seconds\n", name, (int) var);
2976 }
2977
2978 void
2979 parse_time_t(time_t * var)
2980 {
2981 time_msec_t tval;
2982 parseTimeLine(&tval, T_SECOND_STR, false);
2983 *var = static_cast<time_t>(tval/1000);
2984 }
2985
2986 static void
2987 free_time_t(time_t * var)
2988 {
2989 *var = 0;
2990 }
2991
2992 #if !USE_DNSHELPER
2993 static void
2994 dump_time_msec(StoreEntry * entry, const char *name, time_msec_t var)
2995 {
2996 if (var % 1000)
2997 storeAppendPrintf(entry, "%s %" PRId64 " milliseconds\n", name, var);
2998 else
2999 storeAppendPrintf(entry, "%s %d seconds\n", name, (int)(var/1000) );
3000 }
3001
3002 void
3003 parse_time_msec(time_msec_t * var)
3004 {
3005 parseTimeLine(var, T_SECOND_STR, true);
3006 }
3007
3008 static void
3009 free_time_msec(time_msec_t * var)
3010 {
3011 *var = 0;
3012 }
3013 #endif
3014
3015 #if UNUSED_CODE
3016 static void
3017 dump_size_t(StoreEntry * entry, const char *name, size_t var)
3018 {
3019 storeAppendPrintf(entry, "%s %d\n", name, (int) var);
3020 }
3021 #endif
3022
3023 static void
3024 dump_b_size_t(StoreEntry * entry, const char *name, size_t var)
3025 {
3026 storeAppendPrintf(entry, "%s %d %s\n", name, (int) var, B_BYTES_STR);
3027 }
3028
3029 #if !USE_DNSHELPER
3030 static void
3031 dump_b_ssize_t(StoreEntry * entry, const char *name, ssize_t var)
3032 {
3033 storeAppendPrintf(entry, "%s %d %s\n", name, (int) var, B_BYTES_STR);
3034 }
3035 #endif
3036
3037 #if UNUSED_CODE
3038 static void
3039 dump_kb_size_t(StoreEntry * entry, const char *name, size_t var)
3040 {
3041 storeAppendPrintf(entry, "%s %d %s\n", name, (int) var, B_KBYTES_STR);
3042 }
3043 #endif
3044
3045 static void
3046 dump_b_int64_t(StoreEntry * entry, const char *name, int64_t var)
3047 {
3048 storeAppendPrintf(entry, "%s %" PRId64 " %s\n", name, var, B_BYTES_STR);
3049 }
3050
3051 static void
3052 dump_kb_int64_t(StoreEntry * entry, const char *name, int64_t var)
3053 {
3054 storeAppendPrintf(entry, "%s %" PRId64 " %s\n", name, var, B_KBYTES_STR);
3055 }
3056
3057 #if UNUSED_CODE
3058 static void
3059 parse_size_t(size_t * var)
3060 {
3061 int i;
3062 i = GetInteger();
3063 *var = (size_t) i;
3064 }
3065 #endif
3066
3067 static void
3068 parse_b_size_t(size_t * var)
3069 {
3070 parseBytesLine(var, B_BYTES_STR);
3071 }
3072
3073 #if !USE_DNSHELPER
3074 static void
3075 parse_b_ssize_t(ssize_t * var)
3076 {
3077 parseBytesLineSigned(var, B_BYTES_STR);
3078 }
3079 #endif
3080
3081 #if UNUSED_CODE
3082 static void
3083 parse_kb_size_t(size_t * var)
3084 {
3085 parseBytesLine(var, B_KBYTES_STR);
3086 }
3087 #endif
3088
3089 static void
3090 parse_b_int64_t(int64_t * var)
3091 {
3092 parseBytesLine64(var, B_BYTES_STR);
3093 }
3094
3095 static void
3096 parse_kb_int64_t(int64_t * var)
3097 {
3098 parseBytesLine64(var, B_KBYTES_STR);
3099 }
3100
3101 static void
3102 free_size_t(size_t * var)
3103 {
3104 *var = 0;
3105 }
3106
3107 #if !USE_DNSHELPER
3108 static void
3109 free_ssize_t(ssize_t * var)
3110 {
3111 *var = 0;
3112 }
3113 #endif
3114
3115 static void
3116 free_b_int64_t(int64_t * var)
3117 {
3118 *var = 0;
3119 }
3120
3121 #define free_b_size_t free_size_t
3122 #define free_b_ssize_t free_ssize_t
3123 #define free_kb_size_t free_size_t
3124 #define free_mb_size_t free_size_t
3125 #define free_gb_size_t free_size_t
3126 #define free_kb_int64_t free_b_int64_t
3127
3128 static void
3129 dump_u_short(StoreEntry * entry, const char *name, unsigned short var)
3130 {
3131 storeAppendPrintf(entry, "%s %d\n", name, var);
3132 }
3133
3134 static void
3135 free_u_short(unsigned short * u)
3136 {
3137 *u = 0;
3138 }
3139
3140 static void
3141 parse_u_short(unsigned short * var)
3142 {
3143 ConfigParser::ParseUShort(var);
3144 }
3145
3146 void
3147 ConfigParser::ParseUShort(unsigned short *var)
3148 {
3149 *var = GetShort();
3150 }
3151
3152 void
3153 ConfigParser::ParseBool(bool *var)
3154 {
3155 int i = GetInteger();
3156
3157 if (0 == i)
3158 *var = false;
3159 else if (1 == i)
3160 *var = true;
3161 else
3162 self_destruct();
3163 }
3164
3165 static void
3166 dump_wordlist(StoreEntry * entry, const char *name, wordlist * list)
3167 {
3168 while (list != NULL) {
3169 storeAppendPrintf(entry, "%s %s\n", name, list->key);
3170 list = list->next;
3171 }
3172 }
3173
3174 void
3175 ConfigParser::ParseWordList(wordlist ** list)
3176 {
3177 parse_wordlist(list);
3178 }
3179
3180 void
3181 parse_wordlist(wordlist ** list)
3182 {
3183 char *token;
3184 char *t = strtok(NULL, "");
3185
3186 while ((token = strwordtok(NULL, &t)))
3187 wordlistAdd(list, token);
3188 }
3189
3190 #if 0 /* now unused */
3191 static int
3192 check_null_wordlist(wordlist * w)
3193 {
3194 return w == NULL;
3195 }
3196 #endif
3197
3198 static int
3199 check_null_acl_access(acl_access * a)
3200 {
3201 return a == NULL;
3202 }
3203
3204 #define free_wordlist wordlistDestroy
3205
3206 #define free_uri_whitespace free_int
3207
3208 static void
3209 parse_uri_whitespace(int *var)
3210 {
3211 char *token = strtok(NULL, w_space);
3212
3213 if (token == NULL)
3214 self_destruct();
3215
3216 if (!strcasecmp(token, "strip"))
3217 *var = URI_WHITESPACE_STRIP;
3218 else if (!strcasecmp(token, "deny"))
3219 *var = URI_WHITESPACE_DENY;
3220 else if (!strcasecmp(token, "allow"))
3221 *var = URI_WHITESPACE_ALLOW;
3222 else if (!strcasecmp(token, "encode"))
3223 *var = URI_WHITESPACE_ENCODE;
3224 else if (!strcasecmp(token, "chop"))
3225 *var = URI_WHITESPACE_CHOP;
3226 else
3227 self_destruct();
3228 }
3229
3230 static void
3231 dump_uri_whitespace(StoreEntry * entry, const char *name, int var)
3232 {
3233 const char *s;
3234
3235 if (var == URI_WHITESPACE_ALLOW)
3236 s = "allow";
3237 else if (var == URI_WHITESPACE_ENCODE)
3238 s = "encode";
3239 else if (var == URI_WHITESPACE_CHOP)
3240 s = "chop";
3241 else if (var == URI_WHITESPACE_DENY)
3242 s = "deny";
3243 else
3244 s = "strip";
3245
3246 storeAppendPrintf(entry, "%s %s\n", name, s);
3247 }
3248
3249 static void
3250 free_removalpolicy(RemovalPolicySettings ** settings)
3251 {
3252 if (!*settings)
3253 return;
3254
3255 free_string(&(*settings)->type);
3256
3257 free_wordlist(&(*settings)->args);
3258
3259 delete *settings;
3260
3261 *settings = NULL;
3262 }
3263
3264 static void
3265 parse_removalpolicy(RemovalPolicySettings ** settings)
3266 {
3267 if (*settings)
3268 free_removalpolicy(settings);
3269
3270 *settings = new RemovalPolicySettings;
3271
3272 parse_string(&(*settings)->type);
3273
3274 parse_wordlist(&(*settings)->args);
3275 }
3276
3277 static void
3278 dump_removalpolicy(StoreEntry * entry, const char *name, RemovalPolicySettings * settings)
3279 {
3280 wordlist *args;
3281 storeAppendPrintf(entry, "%s %s", name, settings->type);
3282 args = settings->args;
3283
3284 while (args) {
3285 storeAppendPrintf(entry, " %s", args->key);
3286 args = args->next;
3287 }
3288
3289 storeAppendPrintf(entry, "\n");
3290 }
3291
3292 inline void
3293 free_YesNoNone(YesNoNone *)
3294 {
3295 // do nothing: no explicit cleanup is required
3296 }
3297
3298 static void
3299 parse_YesNoNone(YesNoNone *option)
3300 {
3301 int value = 0;
3302 parse_onoff(&value);
3303 option->configure(value > 0);
3304 }
3305
3306 static void
3307 dump_YesNoNone(StoreEntry * entry, const char *name, YesNoNone &option)
3308 {
3309 if (option.configured())
3310 dump_onoff(entry, name, option ? 1 : 0);
3311 }
3312
3313 static void
3314 free_memcachemode(SquidConfig * config)
3315 {
3316 return;
3317 }
3318
3319 static void
3320 parse_memcachemode(SquidConfig * config)
3321 {
3322 char *token = strtok(NULL, w_space);
3323 if (!token)
3324 self_destruct();
3325
3326 if (strcmp(token, "always") == 0) {
3327 Config.onoff.memory_cache_first = 1;
3328 Config.onoff.memory_cache_disk = 1;
3329 } else if (strcmp(token, "disk") == 0) {
3330 Config.onoff.memory_cache_first = 0;
3331 Config.onoff.memory_cache_disk = 1;
3332 } else if (strncmp(token, "net", 3) == 0) {
3333 Config.onoff.memory_cache_first = 1;
3334 Config.onoff.memory_cache_disk = 0;
3335 } else if (strcmp(token, "never") == 0) {
3336 Config.onoff.memory_cache_first = 0;
3337 Config.onoff.memory_cache_disk = 0;
3338 } else
3339 self_destruct();
3340 }
3341
3342 static void
3343 dump_memcachemode(StoreEntry * entry, const char *name, SquidConfig &config)
3344 {
3345 storeAppendPrintf(entry, "%s ", name);
3346 if (Config.onoff.memory_cache_first && Config.onoff.memory_cache_disk)
3347 storeAppendPrintf(entry, "always");
3348 else if (!Config.onoff.memory_cache_first && Config.onoff.memory_cache_disk)
3349 storeAppendPrintf(entry, "disk");
3350 else if (Config.onoff.memory_cache_first && !Config.onoff.memory_cache_disk)
3351 storeAppendPrintf(entry, "network");
3352 else if (!Config.onoff.memory_cache_first && !Config.onoff.memory_cache_disk)
3353 storeAppendPrintf(entry, "none");
3354 storeAppendPrintf(entry, "\n");
3355 }
3356
3357 #include "cf_parser.cci"
3358
3359 peer_t
3360 parseNeighborType(const char *s)
3361 {
3362 if (!strcasecmp(s, "parent"))
3363 return PEER_PARENT;
3364
3365 if (!strcasecmp(s, "neighbor"))
3366 return PEER_SIBLING;
3367
3368 if (!strcasecmp(s, "neighbour"))
3369 return PEER_SIBLING;
3370
3371 if (!strcasecmp(s, "sibling"))
3372 return PEER_SIBLING;
3373
3374 if (!strcasecmp(s, "multicast"))
3375 return PEER_MULTICAST;
3376
3377 debugs(15, DBG_CRITICAL, "WARNING: Unknown neighbor type: " << s);
3378
3379 return PEER_SIBLING;
3380 }
3381
3382 #if USE_WCCPv2
3383 static void
3384 parse_IpAddress_list(Ip::Address_list ** head)
3385 {
3386 char *token;
3387 Ip::Address_list *s;
3388 Ip::Address ipa;
3389
3390 while ((token = strtok(NULL, w_space))) {
3391 if (GetHostWithPort(token, &ipa)) {
3392
3393 while (*head)
3394 head = &(*head)->next;
3395
3396 s = static_cast<Ip::Address_list *>(xcalloc(1, sizeof(*s)));
3397 s->s = ipa;
3398
3399 *head = s;
3400 } else
3401 self_destruct();
3402 }
3403 }
3404
3405 static void
3406 dump_IpAddress_list(StoreEntry * e, const char *n, const Ip::Address_list * s)
3407 {
3408 char ntoabuf[MAX_IPSTRLEN];
3409
3410 while (s) {
3411 storeAppendPrintf(e, "%s %s\n",
3412 n,
3413 s->s.NtoA(ntoabuf,MAX_IPSTRLEN));
3414 s = s->next;
3415 }
3416 }
3417
3418 static void
3419 free_IpAddress_list(Ip::Address_list ** head)
3420 {
3421 if (*head) delete *head;
3422 *head = NULL;
3423 }
3424
3425 #if CURRENTLY_UNUSED
3426 /* This code was previously used by http_port. Left as it really should
3427 * be used by icp_port and htcp_port
3428 */
3429 static int
3430 check_null_IpAddress_list(const Ip::Address_list * s)
3431 {
3432 return NULL == s;
3433 }
3434
3435 #endif /* CURRENTLY_UNUSED */
3436 #endif /* USE_WCCPv2 */
3437
3438 static void
3439 parsePortSpecification(AnyP::PortCfg * s, char *token)
3440 {
3441 char *host = NULL;
3442 unsigned short port = 0;
3443 char *t = NULL;
3444 char *junk = NULL;
3445
3446 s->disable_pmtu_discovery = DISABLE_PMTU_OFF;
3447 s->name = xstrdup(token);
3448 s->connection_auth_disabled = false;
3449
3450 if (*token == '[') {
3451 /* [ipv6]:port */
3452 host = token + 1;
3453 t = strchr(host, ']');
3454 if (!t) {
3455 debugs(3, DBG_CRITICAL, s->protocol << "_port: missing ']' on IPv6 address: " << token);
3456 self_destruct();
3457 }
3458 *t = '\0';
3459 ++t;
3460 if (*t != ':') {
3461 debugs(3, DBG_CRITICAL, s->protocol << "_port: missing Port in: " << token);
3462 self_destruct();
3463 }
3464 if (!Ip::EnableIpv6) {
3465 debugs(3, DBG_CRITICAL, "FATAL: " << s->protocol << "_port: IPv6 is not available.");
3466 self_destruct();
3467 }
3468 port = xatos(t + 1);
3469 } else if ((t = strchr(token, ':'))) {
3470 /* host:port */
3471 /* ipv4:port */
3472 host = token;
3473 *t = '\0';
3474 port = xatos(t + 1);
3475
3476 } else if ((port = strtol(token, &junk, 10)), !*junk) {
3477 /* port */
3478 debugs(3, 3, s->protocol << "_port: found Listen on Port: " << port);
3479 } else {
3480 debugs(3, DBG_CRITICAL, s->protocol << "_port: missing Port: " << token);
3481 self_destruct();
3482 }
3483
3484 if (port == 0 && host != NULL) {
3485 debugs(3, DBG_CRITICAL, s->protocol << "_port: Port cannot be 0: " << token);
3486 self_destruct();
3487 }
3488
3489 if (NULL == host) {
3490 s->s.SetAnyAddr();
3491 s->s.SetPort(port);
3492 if (!Ip::EnableIpv6)
3493 s->s.SetIPv4();
3494 debugs(3, 3, s->protocol << "_port: found Listen on wildcard address: *:" << s->s.GetPort() );
3495 } else if ( (s->s = host) ) { /* check/parse numeric IPA */
3496 s->s.SetPort(port);
3497 if (!Ip::EnableIpv6)
3498 s->s.SetIPv4();
3499 debugs(3, 3, s->protocol << "_port: Listen on Host/IP: " << host << " --> " << s->s);
3500 } else if ( s->s.GetHostByName(host) ) { /* check/parse for FQDN */
3501 /* dont use ipcache */
3502 s->defaultsite = xstrdup(host);
3503 s->s.SetPort(port);
3504 if (!Ip::EnableIpv6)
3505 s->s.SetIPv4();
3506 debugs(3, 3, s->protocol << "_port: found Listen as Host " << s->defaultsite << " on IP: " << s->s);
3507 } else {
3508 debugs(3, DBG_CRITICAL, s->protocol << "_port: failed to resolve Host/IP: " << host);
3509 self_destruct();
3510 }
3511 }
3512
3513 static void
3514 parse_port_option(AnyP::PortCfg * s, char *token)
3515 {
3516 /* modes first */
3517
3518 if (strcmp(token, "accel") == 0) {
3519 if (s->intercepted || s->spoof_client_ip) {
3520 debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: Accelerator mode requires its own port. It cannot be shared with other modes.");
3521 self_destruct();
3522 }
3523 s->accel = s->vhost = 1;
3524 } else if (strcmp(token, "transparent") == 0 || strcmp(token, "intercept") == 0) {
3525 if (s->accel || s->spoof_client_ip) {
3526 debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: Intercept mode requires its own interception port. It cannot be shared with other modes.");
3527 self_destruct();
3528 }
3529 s->intercepted = 1;
3530 Ip::Interceptor.StartInterception();
3531 /* Log information regarding the port modes under interception. */
3532 debugs(3, DBG_IMPORTANT, "Starting Authentication on port " << s->s);
3533 debugs(3, DBG_IMPORTANT, "Disabling Authentication on port " << s->s << " (interception enabled)");
3534
3535 /* INET6: until transparent REDIRECT works on IPv6 SOCKET, force wildcard to IPv4 */
3536 if (Ip::EnableIpv6)
3537 debugs(3, DBG_IMPORTANT, "Disabling IPv6 on port " << s->s << " (interception enabled)");
3538 if ( !s->s.SetIPv4() ) {
3539 debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: IPv6 addresses cannot NAT intercept (protocol does not provide NAT)" << s->s );
3540 self_destruct();
3541 }
3542 } else if (strcmp(token, "tproxy") == 0) {
3543 if (s->intercepted || s->accel) {
3544 debugs(3,DBG_CRITICAL, "FATAL: http(s)_port: TPROXY option requires its own interception port. It cannot be shared with other modes.");
3545 self_destruct();
3546 }
3547 s->spoof_client_ip = 1;
3548 Ip::Interceptor.StartTransparency();
3549 /* Log information regarding the port modes under transparency. */
3550 debugs(3, DBG_IMPORTANT, "Starting IP Spoofing on port " << s->s);
3551 debugs(3, DBG_IMPORTANT, "Disabling Authentication on port " << s->s << " (IP spoofing enabled)");
3552
3553 if (!Ip::Interceptor.ProbeForTproxy(s->s)) {
3554 debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: TPROXY support in the system does not work.");
3555 self_destruct();
3556 }
3557
3558 } else if (strncmp(token, "defaultsite=", 12) == 0) {
3559 if (!s->accel) {
3560 debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: defaultsite option requires Acceleration mode flag.");
3561 self_destruct();
3562 }
3563 safe_free(s->defaultsite);
3564 s->defaultsite = xstrdup(token + 12);
3565 } else if (strcmp(token, "vhost") == 0) {
3566 if (!s->accel) {
3567 debugs(3, DBG_CRITICAL, "WARNING: http(s)_port: vhost option is deprecated. Use 'accel' mode flag instead.");
3568 }
3569 s->accel = s->vhost = 1;
3570 } else if (strcmp(token, "no-vhost") == 0) {
3571 if (!s->accel) {
3572 debugs(3, DBG_IMPORTANT, "ERROR: http(s)_port: no-vhost option requires Acceleration mode flag.");
3573 }
3574 s->vhost = 0;
3575 } else if (strcmp(token, "vport") == 0) {
3576 if (!s->accel) {
3577 debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: vport option requires Acceleration mode flag.");
3578 self_destruct();
3579 }
3580 s->vport = -1;
3581 } else if (strncmp(token, "vport=", 6) == 0) {
3582 if (!s->accel) {
3583 debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: vport option requires Acceleration mode flag.");
3584 self_destruct();
3585 }
3586 s->vport = xatos(token + 6);
3587 } else if (strncmp(token, "protocol=", 9) == 0) {
3588 if (!s->accel) {
3589 debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: protocol option requires Acceleration mode flag.");
3590 self_destruct();
3591 }
3592 s->protocol = xstrdup(token + 9);
3593 } else if (strcmp(token, "allow-direct") == 0) {
3594 if (!s->accel) {
3595 debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: allow-direct option requires Acceleration mode flag.");
3596 self_destruct();
3597 }
3598 s->allow_direct = 1;
3599 } else if (strcmp(token, "act-as-origin") == 0) {
3600 if (!s->accel) {
3601 debugs(3, DBG_IMPORTANT, "ERROR: http(s)_port: act-as-origin option requires Acceleration mode flag.");
3602 } else
3603 s->actAsOrigin = 1;
3604 } else if (strcmp(token, "ignore-cc") == 0) {
3605 #if !USE_HTTP_VIOLATIONS
3606 if (!s->accel) {
3607 debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: ignore-cc option requires Scceleration mode flag.");
3608 self_destruct();
3609 }
3610 #endif
3611 s->ignore_cc = 1;
3612 } else if (strncmp(token, "name=", 5) == 0) {
3613 safe_free(s->name);
3614 s->name = xstrdup(token + 5);
3615 } else if (strcmp(token, "no-connection-auth") == 0) {
3616 s->connection_auth_disabled = true;
3617 } else if (strcmp(token, "connection-auth=off") == 0) {
3618 s->connection_auth_disabled = true;
3619 } else if (strcmp(token, "connection-auth") == 0) {
3620 s->connection_auth_disabled = false;
3621 } else if (strcmp(token, "connection-auth=on") == 0) {
3622 s->connection_auth_disabled = false;
3623 } else if (strncmp(token, "disable-pmtu-discovery=", 23) == 0) {
3624 if (!strcasecmp(token + 23, "off"))
3625 s->disable_pmtu_discovery = DISABLE_PMTU_OFF;
3626 else if (!strcasecmp(token + 23, "transparent"))
3627 s->disable_pmtu_discovery = DISABLE_PMTU_TRANSPARENT;
3628 else if (!strcasecmp(token + 23, "always"))
3629 s->disable_pmtu_discovery = DISABLE_PMTU_ALWAYS;
3630 else
3631 self_destruct();
3632 } else if (strcmp(token, "ipv4") == 0) {
3633 if ( !s->s.SetIPv4() ) {
3634 debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: IPv6 addresses cannot be used as IPv4-Only. " << s->s );
3635 self_destruct();
3636 }
3637 } else if (strcmp(token, "tcpkeepalive") == 0) {
3638 s->tcp_keepalive.enabled = 1;
3639 } else if (strncmp(token, "tcpkeepalive=", 13) == 0) {
3640 char *t = token + 13;
3641 s->tcp_keepalive.enabled = 1;
3642 s->tcp_keepalive.idle = atoi(t);
3643 t = strchr(t, ',');
3644 if (t) {
3645 ++t;
3646 s->tcp_keepalive.interval = atoi(t);
3647 t = strchr(t, ',');
3648 }
3649 if (t) {
3650 ++t;
3651 s->tcp_keepalive.timeout = atoi(t);
3652 // t = strchr(t, ','); // not really needed, left in as documentation
3653 }
3654 #if USE_SSL
3655 } else if (strcasecmp(token, "sslBump") == 0) {
3656 debugs(3, DBG_CRITICAL, "WARNING: '" << token << "' is deprecated " <<
3657 "in http_port. Use 'ssl-bump' instead.");
3658 s->sslBump = 1; // accelerated when bumped, otherwise not
3659 } else if (strcmp(token, "ssl-bump") == 0) {
3660 s->sslBump = 1; // accelerated when bumped, otherwise not
3661 } else if (strncmp(token, "cert=", 5) == 0) {
3662 safe_free(s->cert);
3663 s->cert = xstrdup(token + 5);
3664 } else if (strncmp(token, "key=", 4) == 0) {
3665 safe_free(s->key);
3666 s->key = xstrdup(token + 4);
3667 } else if (strncmp(token, "version=", 8) == 0) {
3668 s->version = xatoi(token + 8);
3669 if (s->version < 1 || s->version > 4)
3670 self_destruct();
3671 } else if (strncmp(token, "options=", 8) == 0) {
3672 safe_free(s->options);
3673 s->options = xstrdup(token + 8);
3674 } else if (strncmp(token, "cipher=", 7) == 0) {
3675 safe_free(s->cipher);
3676 s->cipher = xstrdup(token + 7);
3677 } else if (strncmp(token, "clientca=", 9) == 0) {
3678 safe_free(s->clientca);
3679 s->clientca = xstrdup(token + 9);
3680 } else if (strncmp(token, "cafile=", 7) == 0) {
3681 safe_free(s->cafile);
3682 s->cafile = xstrdup(token + 7);
3683 } else if (strncmp(token, "capath=", 7) == 0) {
3684 safe_free(s->capath);
3685 s->capath = xstrdup(token + 7);
3686 } else if (strncmp(token, "crlfile=", 8) == 0) {
3687 safe_free(s->crlfile);
3688 s->crlfile = xstrdup(token + 8);
3689 } else if (strncmp(token, "dhparams=", 9) == 0) {
3690 safe_free(s->dhfile);
3691 s->dhfile = xstrdup(token + 9);
3692 } else if (strncmp(token, "sslflags=", 9) == 0) {
3693 safe_free(s->sslflags);
3694 s->sslflags = xstrdup(token + 9);
3695 } else if (strncmp(token, "sslcontext=", 11) == 0) {
3696 safe_free(s->sslContextSessionId);
3697 s->sslContextSessionId = xstrdup(token + 11);
3698 } else if (strcmp(token, "generate-host-certificates") == 0) {
3699 s->generateHostCertificates = true;
3700 } else if (strcmp(token, "generate-host-certificates=on") == 0) {
3701 s->generateHostCertificates = true;
3702 } else if (strcmp(token, "generate-host-certificates=off") == 0) {
3703 s->generateHostCertificates = false;
3704 } else if (strncmp(token, "dynamic_cert_mem_cache_size=", 28) == 0) {
3705 parseBytesOptionValue(&s->dynamicCertMemCacheSize, B_BYTES_STR, token + 28);
3706 #endif
3707 } else {
3708 self_destruct();
3709 }
3710 }
3711
3712 void
3713 add_http_port(char *portspec)
3714 {
3715 AnyP::PortCfg *s = new AnyP::PortCfg("http_port");
3716 parsePortSpecification(s, portspec);
3717 // we may need to merge better if the above returns a list with clones
3718 assert(s->next == NULL);
3719 s->next = cbdataReference(Config.Sockaddr.http);
3720 cbdataReferenceDone(Config.Sockaddr.http);
3721 Config.Sockaddr.http = cbdataReference(s);
3722 }
3723
3724 static void
3725 parsePortCfg(AnyP::PortCfg ** head, const char *optionName)
3726 {
3727 const char *protocol = NULL;
3728 if (strcmp(optionName, "http_port") == 0 ||
3729 strcmp(optionName, "ascii_port") == 0)
3730 protocol = "http";
3731 else if (strcmp(optionName, "https_port") == 0)
3732 protocol = "https";
3733 if (!protocol) {
3734 self_destruct();
3735 return;
3736 }
3737
3738 char *token = strtok(NULL, w_space);
3739
3740 if (!token) {
3741 self_destruct();
3742 return;
3743 }
3744
3745 AnyP::PortCfg *s = new AnyP::PortCfg(protocol);
3746 parsePortSpecification(s, token);
3747
3748 /* parse options ... */
3749 while ((token = strtok(NULL, w_space))) {
3750 parse_port_option(s, token);
3751 }
3752
3753 #if USE_SSL
3754 if (strcasecmp(protocol, "https") == 0) {
3755 /* ssl-bump on https_port configuration requires either tproxy or intercept, and vice versa */
3756 const bool hijacked = s->spoof_client_ip || s->intercepted;
3757 if (s->sslBump && !hijacked) {
3758 debugs(3, DBG_CRITICAL, "FATAL: ssl-bump on https_port requires tproxy/intercept which is missing.");
3759 self_destruct();
3760 }
3761 if (hijacked && !s->sslBump) {
3762 debugs(3, DBG_CRITICAL, "FATAL: tproxy/intercept on https_port requires ssl-bump which is missing.");
3763 self_destruct();
3764 }
3765 }
3766 #endif
3767
3768 if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && s->s.IsAnyAddr()) {
3769 // clone the port options from *s to *(s->next)
3770 s->next = cbdataReference(s->clone());
3771 s->next->s.SetIPv4();
3772 debugs(3, 3, protocol << "_port: clone wildcard address for split-stack: " << s->s << " and " << s->next->s);
3773 }
3774
3775 while (*head)
3776 head = &(*head)->next;
3777
3778 *head = cbdataReference(s);
3779 }
3780
3781 static void
3782 dump_generic_port(StoreEntry * e, const char *n, const AnyP::PortCfg * s)
3783 {
3784 char buf[MAX_IPSTRLEN];
3785
3786 storeAppendPrintf(e, "%s %s",
3787 n,
3788 s->s.ToURL(buf,MAX_IPSTRLEN));
3789
3790 // MODES and specific sub-options.
3791 if (s->intercepted)
3792 storeAppendPrintf(e, " intercept");
3793
3794 else if (s->spoof_client_ip)
3795 storeAppendPrintf(e, " tproxy");
3796
3797 else if (s->accel) {
3798 storeAppendPrintf(e, " accel");
3799
3800 if (s->vhost)
3801 storeAppendPrintf(e, " vhost");
3802
3803 if (s->vport < 0)
3804 storeAppendPrintf(e, " vport");
3805 else if (s->vport > 0)
3806 storeAppendPrintf(e, " vport=%d", s->vport);
3807
3808 if (s->defaultsite)
3809 storeAppendPrintf(e, " defaultsite=%s", s->defaultsite);
3810
3811 if (s->protocol && strcmp(s->protocol,"http") != 0)
3812 storeAppendPrintf(e, " protocol=%s", s->protocol);
3813
3814 if (s->allow_direct)
3815 storeAppendPrintf(e, " allow-direct");
3816
3817 if (s->ignore_cc)
3818 storeAppendPrintf(e, " ignore-cc");
3819
3820 }
3821
3822 // Generic independent options
3823
3824 if (s->name)
3825 storeAppendPrintf(e, " name=%s", s->name);
3826
3827 #if USE_HTTP_VIOLATIONS
3828 if (!s->accel && s->ignore_cc)
3829 storeAppendPrintf(e, " ignore-cc");
3830 #endif
3831
3832 if (s->connection_auth_disabled)
3833 storeAppendPrintf(e, " connection-auth=off");
3834 else
3835 storeAppendPrintf(e, " connection-auth=on");
3836
3837 if (s->disable_pmtu_discovery != DISABLE_PMTU_OFF) {
3838 const char *pmtu;
3839
3840 if (s->disable_pmtu_discovery == DISABLE_PMTU_ALWAYS)
3841 pmtu = "always";
3842 else
3843 pmtu = "transparent";
3844
3845 storeAppendPrintf(e, " disable-pmtu-discovery=%s", pmtu);
3846 }
3847
3848 if (s->s.IsAnyAddr() && !s->s.IsIPv6())
3849 storeAppendPrintf(e, " ipv4");
3850
3851 if (s->tcp_keepalive.enabled) {
3852 if (s->tcp_keepalive.idle || s->tcp_keepalive.interval || s->tcp_keepalive.timeout) {
3853 storeAppendPrintf(e, " tcpkeepalive=%d,%d,%d", s->tcp_keepalive.idle, s->tcp_keepalive.interval, s->tcp_keepalive.timeout);
3854 } else {
3855 storeAppendPrintf(e, " tcpkeepalive");
3856 }
3857 }
3858
3859 #if USE_SSL
3860 if (s->sslBump)
3861 storeAppendPrintf(e, " ssl-bump");
3862
3863 if (s->cert)
3864 storeAppendPrintf(e, " cert=%s", s->cert);
3865
3866 if (s->key)
3867 storeAppendPrintf(e, " key=%s", s->key);
3868
3869 if (s->version)
3870 storeAppendPrintf(e, " version=%d", s->version);
3871
3872 if (s->options)
3873 storeAppendPrintf(e, " options=%s", s->options);
3874
3875 if (s->cipher)
3876 storeAppendPrintf(e, " cipher=%s", s->cipher);
3877
3878 if (s->cafile)
3879 storeAppendPrintf(e, " cafile=%s", s->cafile);
3880
3881 if (s->capath)
3882 storeAppendPrintf(e, " capath=%s", s->capath);
3883
3884 if (s->crlfile)
3885 storeAppendPrintf(e, " crlfile=%s", s->crlfile);
3886
3887 if (s->dhfile)
3888 storeAppendPrintf(e, " dhparams=%s", s->dhfile);
3889
3890 if (s->sslflags)
3891 storeAppendPrintf(e, " sslflags=%s", s->sslflags);
3892
3893 if (s->sslContextSessionId)
3894 storeAppendPrintf(e, " sslcontext=%s", s->sslContextSessionId);
3895
3896 if (s->generateHostCertificates)
3897 storeAppendPrintf(e, " generate-host-certificates");
3898
3899 if (s->dynamicCertMemCacheSize != std::numeric_limits<size_t>::max())
3900 storeAppendPrintf(e, "dynamic_cert_mem_cache_size=%lu%s\n", (unsigned long)s->dynamicCertMemCacheSize, B_BYTES_STR);
3901 #endif
3902 }
3903
3904 static void
3905 dump_PortCfg(StoreEntry * e, const char *n, const AnyP::PortCfg * s)
3906 {
3907 while (s) {
3908 dump_generic_port(e, n, s);
3909 storeAppendPrintf(e, "\n");
3910 s = s->next;
3911 }
3912 }
3913
3914 static void
3915 free_PortCfg(AnyP::PortCfg ** head)
3916 {
3917 AnyP::PortCfg *s;
3918
3919 while ((s = *head) != NULL) {
3920 *head = s->next;
3921 cbdataReferenceDone(s);
3922 }
3923 }
3924
3925 void
3926 configFreeMemory(void)
3927 {
3928 free_all();
3929 #if USE_SSL
3930 SSL_CTX_free(Config.ssl_client.sslContext);
3931 #endif
3932 }
3933
3934 void
3935 requirePathnameExists(const char *name, const char *path)
3936 {
3937
3938 struct stat sb;
3939 char pathbuf[BUFSIZ];
3940 assert(path != NULL);
3941
3942 if (Config.chroot_dir && (geteuid() == 0)) {
3943 snprintf(pathbuf, BUFSIZ, "%s/%s", Config.chroot_dir, path);
3944 path = pathbuf;
3945 }
3946
3947 if (stat(path, &sb) < 0) {
3948 debugs(0, DBG_CRITICAL, (opt_parse_cfg_only?"FATAL ":"") << "ERROR: " << name << " " << path << ": " << xstrerror());
3949 // keep going to find more issues if we are only checking the config file with "-k parse"
3950 if (opt_parse_cfg_only)
3951 return;
3952 // this is fatal if it is found during startup or reconfigure
3953 if (opt_send_signal == -1 || opt_send_signal == SIGHUP)
3954 fatalf("%s %s: %s", name, path, xstrerror());
3955 }
3956 }
3957
3958 char *
3959 strtokFile(void)
3960 {
3961 return ConfigParser::strtokFile();
3962 }
3963
3964 #include "AccessLogEntry.h"
3965
3966 static void
3967 parse_access_log(CustomLog ** logs)
3968 {
3969 const char *filename, *logdef_name;
3970
3971 CustomLog *cl = (CustomLog *)xcalloc(1, sizeof(*cl));
3972
3973 if ((filename = strtok(NULL, w_space)) == NULL) {
3974 self_destruct();
3975 return;
3976 }
3977
3978 if (strcmp(filename, "none") == 0) {
3979 cl->type = Log::Format::CLF_NONE;
3980 aclParseAclList(LegacyParser, &cl->aclList);
3981 while (*logs)
3982 logs = &(*logs)->next;
3983 *logs = cl;
3984 return;
3985 }
3986
3987 if ((logdef_name = strtok(NULL, w_space)) == NULL)
3988 logdef_name = "squid";
3989
3990 debugs(3, 9, "Log definition name '" << logdef_name << "' file '" << filename << "'");
3991
3992 cl->filename = xstrdup(filename);
3993
3994 /* look for the definition pointer corresponding to this name */
3995 Format::Format *lf = Log::TheConfig.logformats;
3996
3997 while (lf != NULL) {
3998 debugs(3, 9, "Comparing against '" << lf->name << "'");
3999
4000 if (strcmp(lf->name, logdef_name) == 0)
4001 break;
4002
4003 lf = lf->next;
4004 }
4005
4006 if (lf != NULL) {
4007 cl->type = Log::Format::CLF_CUSTOM;
4008 cl->logFormat = lf;
4009 } else if (strcmp(logdef_name, "auto") == 0) {
4010 debugs(0, DBG_CRITICAL, "WARNING: Log format 'auto' no longer exists. Using 'squid' instead.");
4011 cl->type = Log::Format::CLF_SQUID;
4012 } else if (strcmp(logdef_name, "squid") == 0) {
4013 cl->type = Log::Format::CLF_SQUID;
4014 } else if (strcmp(logdef_name, "common") == 0) {
4015 cl->type = Log::Format::CLF_COMMON;
4016 } else if (strcmp(logdef_name, "combined") == 0) {
4017 cl->type = Log::Format::CLF_COMBINED;
4018 #if ICAP_CLIENT
4019 } else if (strcmp(logdef_name, "icap_squid") == 0) {
4020 cl->type = Log::Format::CLF_ICAP_SQUID;
4021 #endif
4022 } else if (strcmp(logdef_name, "useragent") == 0) {
4023 cl->type = Log::Format::CLF_USERAGENT;
4024 } else if (strcmp(logdef_name, "referrer") == 0) {
4025 cl->type = Log::Format::CLF_REFERER;
4026 } else {
4027 debugs(3, DBG_CRITICAL, "Log format '" << logdef_name << "' is not defined");
4028 self_destruct();
4029 return;
4030 }
4031
4032 aclParseAclList(LegacyParser, &cl->aclList);
4033
4034 while (*logs)
4035 logs = &(*logs)->next;
4036
4037 *logs = cl;
4038 }
4039
4040 static int
4041 check_null_access_log(CustomLog *customlog_definitions)
4042 {
4043 return customlog_definitions == NULL;
4044 }
4045
4046 static void
4047 dump_access_log(StoreEntry * entry, const char *name, CustomLog * logs)
4048 {
4049 CustomLog *log;
4050
4051 for (log = logs; log; log = log->next) {
4052 storeAppendPrintf(entry, "%s ", name);
4053
4054 switch (log->type) {
4055
4056 case Log::Format::CLF_CUSTOM:
4057 storeAppendPrintf(entry, "%s %s", log->filename, log->logFormat->name);
4058 break;
4059
4060 case Log::Format::CLF_NONE:
4061 storeAppendPrintf(entry, "none");
4062 break;
4063
4064 case Log::Format::CLF_SQUID:
4065 storeAppendPrintf(entry, "%s squid", log->filename);
4066 break;
4067
4068 case Log::Format::CLF_COMBINED:
4069 storeAppendPrintf(entry, "%s combined", log->filename);
4070 break;
4071
4072 case Log::Format::CLF_COMMON:
4073 storeAppendPrintf(entry, "%s common", log->filename);
4074 break;
4075
4076 #if ICAP_CLIENT
4077 case Log::Format::CLF_ICAP_SQUID:
4078 storeAppendPrintf(entry, "%s icap_squid", log->filename);
4079 break;
4080 #endif
4081 case Log::Format::CLF_USERAGENT:
4082 storeAppendPrintf(entry, "%s useragent", log->filename);
4083 break;
4084
4085 case Log::Format::CLF_REFERER:
4086 storeAppendPrintf(entry, "%s referrer", log->filename);
4087 break;
4088
4089 case Log::Format::CLF_UNKNOWN:
4090 break;
4091 }
4092
4093 if (log->aclList)
4094 dump_acl_list(entry, log->aclList);
4095
4096 storeAppendPrintf(entry, "\n");
4097 }
4098 }
4099
4100 static void
4101 free_access_log(CustomLog ** definitions)
4102 {
4103 while (*definitions) {
4104 CustomLog *log = *definitions;
4105 *definitions = log->next;
4106
4107 log->logFormat = NULL;
4108 log->type = Log::Format::CLF_UNKNOWN;
4109
4110 if (log->aclList)
4111 aclDestroyAclList(&log->aclList);
4112
4113 safe_free(log->filename);
4114
4115 xfree(log);
4116 }
4117 }
4118
4119 /// parses list of integers form name=N1,N2,N3,...
4120 static bool
4121 parseNamedIntList(const char *data, const String &name, Vector<int> &list)
4122 {
4123 if (data && (strncmp(data, name.rawBuf(), name.size()) == 0)) {
4124 data += name.size();
4125 if (*data == '=') {
4126 while (true) {
4127 ++data;
4128 int value = 0;
4129 if (!StringToInt(data, value, &data, 10))
4130 break;
4131 list.push_back(value);
4132 if (*data == '\0' || *data != ',')
4133 break;
4134 }
4135 }
4136 }
4137 return data && *data == '\0';
4138 }
4139
4140 static void
4141 parse_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap)
4142 {
4143 #if !HAVE_CPU_AFFINITY
4144 debugs(3, DBG_CRITICAL, "FATAL: Squid built with no CPU affinity " <<
4145 "support, do not set 'cpu_affinity_map'");
4146 self_destruct();
4147 #endif /* HAVE_CPU_AFFINITY */
4148
4149 if (!*cpuAffinityMap)
4150 *cpuAffinityMap = new CpuAffinityMap;
4151
4152 const char *const pToken = strtok(NULL, w_space);
4153 const char *const cToken = strtok(NULL, w_space);
4154 Vector<int> processes, cores;
4155 if (!parseNamedIntList(pToken, "process_numbers", processes)) {
4156 debugs(3, DBG_CRITICAL, "FATAL: bad 'process_numbers' parameter " <<
4157 "in 'cpu_affinity_map'");
4158 self_destruct();
4159 } else if (!parseNamedIntList(cToken, "cores", cores)) {
4160 debugs(3, DBG_CRITICAL, "FATAL: bad 'cores' parameter in " <<
4161 "'cpu_affinity_map'");
4162 self_destruct();
4163 } else if (!(*cpuAffinityMap)->add(processes, cores)) {
4164 debugs(3, DBG_CRITICAL, "FATAL: bad 'cpu_affinity_map'; " <<
4165 "process_numbers and cores lists differ in length or " <<
4166 "contain numbers <= 0");
4167 self_destruct();
4168 }
4169 }
4170
4171 static void
4172 dump_CpuAffinityMap(StoreEntry *const entry, const char *const name, const CpuAffinityMap *const cpuAffinityMap)
4173 {
4174 if (cpuAffinityMap) {
4175 storeAppendPrintf(entry, "%s process_numbers=", name);
4176 for (size_t i = 0; i < cpuAffinityMap->processes().size(); ++i) {
4177 storeAppendPrintf(entry, "%s%i", (i ? "," : ""),
4178 cpuAffinityMap->processes()[i]);
4179 }
4180 storeAppendPrintf(entry, " cores=");
4181 for (size_t i = 0; i < cpuAffinityMap->processes().size(); ++i) {
4182 storeAppendPrintf(entry, "%s%i", (i ? "," : ""),
4183 cpuAffinityMap->cores()[i]);
4184 }
4185 storeAppendPrintf(entry, "\n");
4186 }
4187 }
4188
4189 static void
4190 free_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap)
4191 {
4192 delete *cpuAffinityMap;
4193 *cpuAffinityMap = NULL;
4194 }
4195
4196 #if USE_ADAPTATION
4197
4198 static void
4199 parse_adaptation_service_set_type()
4200 {
4201 Adaptation::Config::ParseServiceSet();
4202 }
4203
4204 static void
4205 parse_adaptation_service_chain_type()
4206 {
4207 Adaptation::Config::ParseServiceChain();
4208 }
4209
4210 static void
4211 parse_adaptation_access_type()
4212 {
4213 Adaptation::Config::ParseAccess(LegacyParser);
4214 }
4215 #endif /* USE_ADAPTATION */
4216
4217 #if ICAP_CLIENT
4218
4219 static void
4220 parse_icap_service_type(Adaptation::Icap::Config * cfg)
4221 {
4222 cfg->parseService();
4223 }
4224
4225 static void
4226 free_icap_service_type(Adaptation::Icap::Config * cfg)
4227 {
4228 cfg->freeService();
4229 }
4230
4231 static void
4232 dump_icap_service_type(StoreEntry * entry, const char *name, const Adaptation::Icap::Config &cfg)
4233 {
4234 cfg.dumpService(entry, name);
4235 }
4236
4237 static void
4238 parse_icap_class_type()
4239 {
4240 debugs(93, DBG_CRITICAL, "WARNING: 'icap_class' is depricated. " <<
4241 "Use 'adaptation_service_set' instead");
4242 Adaptation::Config::ParseServiceSet();
4243 }
4244
4245 static void
4246 parse_icap_access_type()
4247 {
4248 debugs(93, DBG_CRITICAL, "WARNING: 'icap_access' is depricated. " <<
4249 "Use 'adaptation_access' instead");
4250 Adaptation::Config::ParseAccess(LegacyParser);
4251 }
4252
4253 #endif
4254
4255 #if USE_ECAP
4256
4257 static void
4258 parse_ecap_service_type(Adaptation::Ecap::Config * cfg)
4259 {
4260 cfg->parseService();
4261 }
4262
4263 static void
4264 free_ecap_service_type(Adaptation::Ecap::Config * cfg)
4265 {
4266 cfg->freeService();
4267 }
4268
4269 static void
4270 dump_ecap_service_type(StoreEntry * entry, const char *name, const Adaptation::Ecap::Config &cfg)
4271 {
4272 cfg.dumpService(entry, name);
4273 }
4274
4275 #endif /* USE_ECAP */
4276
4277 #if ICAP_CLIENT
4278 static void parse_icap_service_failure_limit(Adaptation::Icap::Config *cfg)
4279 {
4280 char *token;
4281 time_t d;
4282 time_t m;
4283 cfg->service_failure_limit = GetInteger();
4284
4285 if ((token = strtok(NULL, w_space)) == NULL)
4286 return;
4287
4288 if (strcmp(token,"in") != 0) {
4289 debugs(3, DBG_CRITICAL, "expecting 'in' on'" << config_input_line << "'");
4290 self_destruct();
4291 }
4292
4293 if ((token = strtok(NULL, w_space)) == NULL) {
4294 self_destruct();
4295 }
4296
4297 d = static_cast<time_t> (xatoi(token));
4298
4299 m = static_cast<time_t> (1);
4300
4301 if (0 == d)
4302 (void) 0;
4303 else if ((token = strtok(NULL, w_space)) == NULL) {
4304 debugs(3, DBG_CRITICAL, "No time-units on '" << config_input_line << "'");
4305 self_destruct();
4306 } else if ((m = parseTimeUnits(token, false)) == 0)
4307 self_destruct();
4308
4309 cfg->oldest_service_failure = (m * d);
4310 }
4311
4312 static void dump_icap_service_failure_limit(StoreEntry *entry, const char *name, const Adaptation::Icap::Config &cfg)
4313 {
4314 storeAppendPrintf(entry, "%s %d", name, cfg.service_failure_limit);
4315 if (cfg.oldest_service_failure > 0) {
4316 storeAppendPrintf(entry, " in %d seconds", (int)cfg.oldest_service_failure);
4317 }
4318 storeAppendPrintf(entry, "\n");
4319 }
4320
4321 static void free_icap_service_failure_limit(Adaptation::Icap::Config *cfg)
4322 {
4323 cfg->oldest_service_failure = 0;
4324 cfg->service_failure_limit = 0;
4325 }
4326 #endif
4327
4328 #if USE_SSL
4329 static void parse_sslproxy_cert_adapt(sslproxy_cert_adapt **cert_adapt)
4330 {
4331 char *al;
4332 sslproxy_cert_adapt *ca = (sslproxy_cert_adapt *) xcalloc(1, sizeof(sslproxy_cert_adapt));
4333 if ((al = strtok(NULL, w_space)) == NULL) {
4334 self_destruct();
4335 return;
4336 }
4337
4338 const char *param;
4339 if ( char *s = strchr(al, '{')) {
4340 *s = '\0'; // terminate the al string
4341 ++s;
4342 param = s;
4343 s = strchr(s, '}');
4344 if (!s) {
4345 self_destruct();
4346 return;
4347 }
4348 *s = '\0';
4349 } else
4350 param = NULL;
4351
4352 if (strcmp(al, Ssl::CertAdaptAlgorithmStr[Ssl::algSetValidAfter]) == 0) {
4353 ca->alg = Ssl::algSetValidAfter;
4354 ca->param = strdup("on");
4355 } else if (strcmp(al, Ssl::CertAdaptAlgorithmStr[Ssl::algSetValidBefore]) == 0) {
4356 ca->alg = Ssl::algSetValidBefore;
4357 ca->param = strdup("on");
4358 } else if (strcmp(al, Ssl::CertAdaptAlgorithmStr[Ssl::algSetCommonName]) == 0) {
4359 ca->alg = Ssl::algSetCommonName;
4360 if (param) {
4361 if (strlen(param) > 64) {
4362 debugs(3, DBG_CRITICAL, "FATAL: sslproxy_cert_adapt: setCommonName{" <<param << "} : using common name longer than 64 bytes is not supported");
4363 self_destruct();
4364 return;
4365 }
4366 ca->param = strdup(param);
4367 }
4368 } else {
4369 debugs(3, DBG_CRITICAL, "FATAL: sslproxy_cert_adapt: unknown cert adaptation algorithm: " << al);
4370 self_destruct();
4371 return;
4372 }
4373
4374 aclParseAclList(LegacyParser, &ca->aclList);
4375
4376 while (*cert_adapt)
4377 cert_adapt = &(*cert_adapt)->next;
4378
4379 *cert_adapt = ca;
4380 }
4381
4382 static void dump_sslproxy_cert_adapt(StoreEntry *entry, const char *name, sslproxy_cert_adapt *cert_adapt)
4383 {
4384 for (sslproxy_cert_adapt *ca = cert_adapt; ca != NULL; ca = ca->next) {
4385 storeAppendPrintf(entry, "%s ", name);
4386 storeAppendPrintf(entry, "%s{%s} ", Ssl::sslCertAdaptAlgoritm(ca->alg), ca->param);
4387 if (ca->aclList)
4388 dump_acl_list(entry, ca->aclList);
4389 storeAppendPrintf(entry, "\n");
4390 }
4391 }
4392
4393 static void free_sslproxy_cert_adapt(sslproxy_cert_adapt **cert_adapt)
4394 {
4395 while (*cert_adapt) {
4396 sslproxy_cert_adapt *ca = *cert_adapt;
4397 *cert_adapt = ca->next;
4398 safe_free(ca->param);
4399
4400 if (ca->aclList)
4401 aclDestroyAclList(&ca->aclList);
4402
4403 safe_free(ca);
4404 }
4405 }
4406
4407 static void parse_sslproxy_cert_sign(sslproxy_cert_sign **cert_sign)
4408 {
4409 char *al;
4410 sslproxy_cert_sign *cs = (sslproxy_cert_sign *) xcalloc(1, sizeof(sslproxy_cert_sign));
4411 if ((al = strtok(NULL, w_space)) == NULL) {
4412 self_destruct();
4413 return;
4414 }
4415
4416 if (strcmp(al, Ssl::CertSignAlgorithmStr[Ssl::algSignTrusted]) == 0)
4417 cs->alg = Ssl::algSignTrusted;
4418 else if (strcmp(al, Ssl::CertSignAlgorithmStr[Ssl::algSignUntrusted]) == 0)
4419 cs->alg = Ssl::algSignUntrusted;
4420 else if (strcmp(al, Ssl::CertSignAlgorithmStr[Ssl::algSignSelf]) == 0)
4421 cs->alg = Ssl::algSignSelf;
4422 else {
4423 debugs(3, DBG_CRITICAL, "FATAL: sslproxy_cert_sign: unknown cert signing algorithm: " << al);
4424 self_destruct();
4425 return;
4426 }
4427
4428 aclParseAclList(LegacyParser, &cs->aclList);
4429
4430 while (*cert_sign)
4431 cert_sign = &(*cert_sign)->next;
4432
4433 *cert_sign = cs;
4434 }
4435
4436 static void dump_sslproxy_cert_sign(StoreEntry *entry, const char *name, sslproxy_cert_sign *cert_sign)
4437 {
4438 sslproxy_cert_sign *cs;
4439 for (cs = cert_sign; cs != NULL; cs = cs->next) {
4440 storeAppendPrintf(entry, "%s ", name);
4441 storeAppendPrintf(entry, "%s ", Ssl::certSignAlgorithm(cs->alg));
4442 if (cs->aclList)
4443 dump_acl_list(entry, cs->aclList);
4444 storeAppendPrintf(entry, "\n");
4445 }
4446 }
4447
4448 static void free_sslproxy_cert_sign(sslproxy_cert_sign **cert_sign)
4449 {
4450 while (*cert_sign) {
4451 sslproxy_cert_sign *cs = *cert_sign;
4452 *cert_sign = cs->next;
4453
4454 if (cs->aclList)
4455 aclDestroyAclList(&cs->aclList);
4456
4457 safe_free(cs);
4458 }
4459 }
4460
4461 class sslBumpCfgRr: public ::RegisteredRunner
4462 {
4463 public:
4464 static Ssl::BumpMode lastDeprecatedRule;
4465 /* RegisteredRunner API */
4466 virtual void run(const RunnerRegistry &);
4467 };
4468
4469 Ssl::BumpMode sslBumpCfgRr::lastDeprecatedRule = Ssl::bumpEnd;
4470
4471 RunnerRegistrationEntry(rrFinalizeConfig, sslBumpCfgRr);
4472
4473 void sslBumpCfgRr::run(const RunnerRegistry &r)
4474 {
4475 if (lastDeprecatedRule != Ssl::bumpEnd) {
4476 assert( lastDeprecatedRule == Ssl::bumpClientFirst || lastDeprecatedRule == Ssl::bumpNone);
4477 static char buf[1024];
4478 if (lastDeprecatedRule == Ssl::bumpClientFirst) {
4479 strcpy(buf, "ssl_bump deny all");
4480 debugs(3, DBG_CRITICAL, "WARNING: auto-converting deprecated implicit "
4481 "\"ssl_bump deny all\" to \"ssl_bump none all\". New ssl_bump configurations "
4482 "must not use implicit rules. Update your ssl_bump rules.");
4483 } else {
4484 strcpy(buf, "ssl_bump allow all");
4485 debugs(3, DBG_CRITICAL, "SECURITY NOTICE: auto-converting deprecated implicit "
4486 "\"ssl_bump allow all\" to \"ssl_bump client-first all\" which is usually "
4487 "inferior to the newer server-first bumping mode. New ssl_bump"
4488 " configurations must not use implicit rules. Update your ssl_bump rules.");
4489 }
4490 parse_line(buf);
4491 }
4492 }
4493
4494 static void parse_sslproxy_ssl_bump(acl_access **ssl_bump)
4495 {
4496 typedef const char *BumpCfgStyle;
4497 BumpCfgStyle bcsNone = NULL;
4498 BumpCfgStyle bcsNew = "new client/server-first/none";
4499 BumpCfgStyle bcsOld = "deprecated allow/deny";
4500 static BumpCfgStyle bumpCfgStyleLast = bcsNone;
4501 BumpCfgStyle bumpCfgStyleNow = bcsNone;
4502 char *bm;
4503 if ((bm = strtok(NULL, w_space)) == NULL) {
4504 self_destruct();
4505 return;
4506 }
4507
4508 // if this is the first rule proccessed
4509 if (*ssl_bump == NULL) {
4510 bumpCfgStyleLast = bcsNone;
4511 sslBumpCfgRr::lastDeprecatedRule = Ssl::bumpEnd;
4512 }
4513
4514 acl_access *A = new acl_access;
4515 A->allow = allow_t(ACCESS_ALLOWED);
4516
4517 if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpClientFirst]) == 0) {
4518 A->allow.kind = Ssl::bumpClientFirst;
4519 bumpCfgStyleNow = bcsNew;
4520 } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpServerFirst]) == 0) {
4521 A->allow.kind = Ssl::bumpServerFirst;
4522 bumpCfgStyleNow = bcsNew;
4523 } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpNone]) == 0) {
4524 A->allow.kind = Ssl::bumpNone;
4525 bumpCfgStyleNow = bcsNew;
4526 } else if (strcmp(bm, "allow") == 0) {
4527 debugs(3, DBG_CRITICAL, "SECURITY NOTICE: auto-converting deprecated "
4528 "\"ssl_bump allow <acl>\" to \"ssl_bump client-first <acl>\" which "
4529 "is usually inferior to the newer server-first "
4530 "bumping mode. Update your ssl_bump rules.");
4531 A->allow.kind = Ssl::bumpClientFirst;
4532 bumpCfgStyleNow = bcsOld;
4533 sslBumpCfgRr::lastDeprecatedRule = Ssl::bumpClientFirst;
4534 } else if (strcmp(bm, "deny") == 0) {
4535 debugs(3, DBG_CRITICAL, "WARNING: auto-converting deprecated "
4536 "\"ssl_bump deny <acl>\" to \"ssl_bump none <acl>\". Update "
4537 "your ssl_bump rules.");
4538 A->allow.kind = Ssl::bumpNone;
4539 bumpCfgStyleNow = bcsOld;
4540 sslBumpCfgRr::lastDeprecatedRule = Ssl::bumpNone;
4541 } else {
4542 debugs(3, DBG_CRITICAL, "FATAL: unknown ssl_bump mode: " << bm);
4543 self_destruct();
4544 return;
4545 }
4546
4547 if (bumpCfgStyleLast != bcsNone && bumpCfgStyleNow != bumpCfgStyleLast) {
4548 debugs(3, DBG_CRITICAL, "FATAL: do not mix " << bumpCfgStyleNow << " actions with " <<
4549 bumpCfgStyleLast << " actions. Update your ssl_bump rules.");
4550 self_destruct();
4551 return;
4552 }
4553
4554 bumpCfgStyleLast = bumpCfgStyleNow;
4555
4556 aclParseAclList(LegacyParser, &A->aclList);
4557
4558 acl_access *B, **T;
4559 for (B = *ssl_bump, T = ssl_bump; B; T = &B->next, B = B->next);
4560 *T = A;
4561 }
4562
4563 static void dump_sslproxy_ssl_bump(StoreEntry *entry, const char *name, acl_access *ssl_bump)
4564 {
4565 acl_access *sb;
4566 for (sb = ssl_bump; sb != NULL; sb = sb->next) {
4567 storeAppendPrintf(entry, "%s ", name);
4568 storeAppendPrintf(entry, "%s ", Ssl::bumpMode(sb->allow.kind));
4569 if (sb->aclList)
4570 dump_acl_list(entry, sb->aclList);
4571 storeAppendPrintf(entry, "\n");
4572 }
4573 }
4574
4575 static void free_sslproxy_ssl_bump(acl_access **ssl_bump)
4576 {
4577 free_acl_access(ssl_bump);
4578 }
4579
4580 #endif
4581
4582 static void dump_HeaderWithAclList(StoreEntry * entry, const char *name, HeaderWithAclList *headers)
4583 {
4584 if (!headers)
4585 return;
4586
4587 for (HeaderWithAclList::iterator hwa = headers->begin(); hwa != headers->end(); ++hwa) {
4588 storeAppendPrintf(entry, "%s ", hwa->fieldName.c_str());
4589 storeAppendPrintf(entry, "%s ", hwa->fieldValue.c_str());
4590 if (hwa->aclList)
4591 dump_acl_list(entry, hwa->aclList);
4592 storeAppendPrintf(entry, "\n");
4593 }
4594 }
4595
4596 static void parse_HeaderWithAclList(HeaderWithAclList **headers)
4597 {
4598 char *fn;
4599 if (!*headers) {
4600 *headers = new HeaderWithAclList;
4601 }
4602 if ((fn = strtok(NULL, w_space)) == NULL) {
4603 self_destruct();
4604 return;
4605 }
4606 HeaderWithAcl hwa;
4607 hwa.fieldName = fn;
4608 hwa.fieldId = httpHeaderIdByNameDef(fn, strlen(fn));
4609 if (hwa.fieldId == HDR_BAD_HDR)
4610 hwa.fieldId = HDR_OTHER;
4611
4612 String buf;
4613 bool wasQuoted;
4614 ConfigParser::ParseQuotedString(&buf, &wasQuoted);
4615 hwa.fieldValue = buf.termedBuf();
4616 hwa.quoted = wasQuoted;
4617 if (hwa.quoted) {
4618 Format::Format *nlf = new ::Format::Format("hdrWithAcl");
4619 if (!nlf->parse(hwa.fieldValue.c_str())) {
4620 self_destruct();
4621 return;
4622 }
4623 hwa.valueFormat = nlf;
4624 }
4625 aclParseAclList(LegacyParser, &hwa.aclList);
4626 (*headers)->push_back(hwa);
4627 }
4628
4629 static void free_HeaderWithAclList(HeaderWithAclList **header)
4630 {
4631 if (!(*header))
4632 return;
4633
4634 for (HeaderWithAclList::iterator hwa = (*header)->begin(); hwa != (*header)->end(); ++hwa) {
4635 if (hwa->aclList)
4636 aclDestroyAclList(&hwa->aclList);
4637
4638 if (hwa->valueFormat) {
4639 delete hwa->valueFormat;
4640 hwa->valueFormat = NULL;
4641 }
4642 }
4643 delete *header;
4644 *header = NULL;
4645 }
4646
4647 static void parse_note(Notes *notes)
4648 {
4649 assert(notes);
4650 notes->parse(LegacyParser);
4651 }
4652
4653 static void dump_note(StoreEntry *entry, const char *name, Notes &notes)
4654 {
4655 notes.dump(entry, name);
4656 }
4657
4658 static void free_note(Notes *notes)
4659 {
4660 notes->clean();
4661 }