]> git.ipfire.org Git - thirdparty/squid.git/blob - src/cache_cf.cc
Moved some prototypes to newly-created int.h, SquidString.h, disk.h
[thirdparty/squid.git] / src / cache_cf.cc
1 /*
2 * $Id$
3 *
4 * DEBUG: section 03 Configuration File Parsing
5 * AUTHOR: Harvest Derived
6 *
7 * SQUID Web Proxy Cache http://www.squid-cache.org/
8 * ----------------------------------------------------------
9 *
10 * Squid is the result of efforts by numerous individuals from
11 * the Internet community; see the CONTRIBUTORS file for full
12 * details. Many organizations have provided support for Squid's
13 * development; see the SPONSORS file for full details. Squid is
14 * Copyrighted (C) 2001 by the Regents of the University of
15 * California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other
17 * sources; see the CREDITS file for full details.
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
32 *
33 */
34
35 #include "squid.h"
36 #include "acl/Acl.h"
37 #include "acl/Gadgets.h"
38 #include "acl/MethodData.h"
39 #include "anyp/PortCfg.h"
40 #include "base/RunnersRegistry.h"
41 #include "cache_cf.h"
42 #include "ConfigParser.h"
43 #include "CpuAffinityMap.h"
44 #include "DiskIO/DiskIOModule.h"
45 #include "eui/Config.h"
46 #include "ExternalACL.h"
47 #include "format/Format.h"
48 #include "globals.h"
49 #include "HttpHeaderTools.h"
50 #include "HttpRequestMethod.h"
51 #include "ident/Config.h"
52 #include "ip/Intercept.h"
53 #include "ip/QosConfig.h"
54 #include "ip/tools.h"
55 #include "ipc/Kids.h"
56 #include "log/Config.h"
57 #include "Mem.h"
58 #include "MemBuf.h"
59 #include "mgr/Registration.h"
60 #include "Parsing.h"
61 #include "PeerDigest.h"
62 #include "protos.h"
63 #include "rfc1738.h"
64 #include "SquidString.h"
65 #include "Store.h"
66 #include "StoreFileSystem.h"
67 #include "structs.h"
68 #include "SwapDir.h"
69 #include "wordlist.h"
70 #include "neighbors.h"
71 #include "tools.h"
72 /* wccp2 has its own conditional definitions */
73 #include "wccp2.h"
74 #if USE_ADAPTATION
75 #include "adaptation/Config.h"
76 #endif
77 #if ICAP_CLIENT
78 #include "adaptation/icap/Config.h"
79 #endif
80 #if USE_ECAP
81 #include "adaptation/ecap/Config.h"
82 #endif
83 #if USE_SSL
84 #include "ssl/support.h"
85 #include "ssl/Config.h"
86 #endif
87 #if USE_AUTH
88 #include "auth/Config.h"
89 #include "auth/Scheme.h"
90 #endif
91 #if USE_SQUID_ESI
92 #include "esi/Parser.h"
93 #endif
94 #if SQUID_SNMP
95 #include "snmp.h"
96 #endif
97
98 #if HAVE_GLOB_H
99 #include <glob.h>
100 #endif
101 #if HAVE_LIMITS_H
102 #include <limits>
103 #endif
104 #if HAVE_LIST
105 #include <list>
106 #endif
107 #if HAVE_SYS_TYPES_H
108 #include <sys/types.h>
109 #endif
110 #if HAVE_PWD_H
111 #include <pwd.h>
112 #endif
113 #if HAVE_GRP_H
114 #include <grp.h>
115 #endif
116 #if HAVE_SYS_STAT_H
117 #include <sys/stat.h>
118 #endif
119
120 #if USE_SSL
121 #include "ssl/gadgets.h"
122 #endif
123
124 #if USE_ADAPTATION
125 static void parse_adaptation_service_set_type();
126 static void parse_adaptation_service_chain_type();
127 static void parse_adaptation_access_type();
128 static void parse_adaptation_meta_type(Adaptation::Config::MetaHeaders *);
129 static void dump_adaptation_meta_type(StoreEntry *, const char *, Adaptation::Config::MetaHeaders &);
130 static void free_adaptation_meta_type(Adaptation::Config::MetaHeaders *);
131 #endif
132
133 #if ICAP_CLIENT
134 static void parse_icap_service_type(Adaptation::Icap::Config *);
135 static void dump_icap_service_type(StoreEntry *, const char *, const Adaptation::Icap::Config &);
136 static void free_icap_service_type(Adaptation::Icap::Config *);
137 static void parse_icap_class_type();
138 static void parse_icap_access_type();
139
140 static void parse_icap_service_failure_limit(Adaptation::Icap::Config *);
141 static void dump_icap_service_failure_limit(StoreEntry *, const char *, const Adaptation::Icap::Config &);
142 static void free_icap_service_failure_limit(Adaptation::Icap::Config *);
143 #endif
144
145 #if USE_ECAP
146 static void parse_ecap_service_type(Adaptation::Ecap::Config *);
147 static void dump_ecap_service_type(StoreEntry *, const char *, const Adaptation::Ecap::Config &);
148 static void free_ecap_service_type(Adaptation::Ecap::Config *);
149 #endif
150
151 static peer_t parseNeighborType(const char *s);
152
153 CBDATA_TYPE(peer);
154
155 static const char *const T_MILLISECOND_STR = "millisecond";
156 static const char *const T_SECOND_STR = "second";
157 static const char *const T_MINUTE_STR = "minute";
158 static const char *const T_HOUR_STR = "hour";
159 static const char *const T_DAY_STR = "day";
160 static const char *const T_WEEK_STR = "week";
161 static const char *const T_FORTNIGHT_STR = "fortnight";
162 static const char *const T_MONTH_STR = "month";
163 static const char *const T_YEAR_STR = "year";
164 static const char *const T_DECADE_STR = "decade";
165
166 static const char *const B_BYTES_STR = "bytes";
167 static const char *const B_KBYTES_STR = "KB";
168 static const char *const B_MBYTES_STR = "MB";
169 static const char *const B_GBYTES_STR = "GB";
170
171 static const char *const list_sep = ", \t\n\r";
172
173 static void parse_access_log(customlog ** customlog_definitions);
174 static int check_null_access_log(customlog *customlog_definitions);
175 static void dump_access_log(StoreEntry * entry, const char *name, customlog * definitions);
176 static void free_access_log(customlog ** definitions);
177
178 static void update_maxobjsize(void);
179 static void configDoConfigure(void);
180 static void parse_refreshpattern(refresh_t **);
181 static uint64_t parseTimeUnits(const char *unit, bool allowMsec);
182 static void parseTimeLine(time_msec_t * tptr, const char *units, bool allowMsec);
183 static void parse_u_short(unsigned short * var);
184 static void parse_string(char **);
185 static void default_all(void);
186 static void defaults_if_none(void);
187 static void defaults_postscriptum(void);
188 static int parse_line(char *);
189 static void parse_obsolete(const char *);
190 static void parseBytesLine(size_t * bptr, const char *units);
191 #if USE_SSL
192 static void parseBytesOptionValue(size_t * bptr, const char *units, char const * value);
193 #endif
194 #if !USE_DNSHELPER
195 static void parseBytesLineSigned(ssize_t * bptr, const char *units);
196 #endif
197 static size_t parseBytesUnits(const char *unit);
198 static void free_all(void);
199 void requirePathnameExists(const char *name, const char *path);
200 static OBJH dump_config;
201 #if USE_HTTP_VIOLATIONS
202 static void free_HeaderManglers(HeaderManglers **pm);
203 static void dump_http_header_access(StoreEntry * entry, const char *name, const HeaderManglers *manglers);
204 static void parse_http_header_access(HeaderManglers **manglers);
205 #define free_http_header_access free_HeaderManglers
206 static void dump_http_header_replace(StoreEntry * entry, const char *name, const HeaderManglers *manglers);
207 static void parse_http_header_replace(HeaderManglers **manglers);
208 #define free_http_header_replace free_HeaderManglers
209 #endif
210 static void dump_HeaderWithAclList(StoreEntry * entry, const char *name, HeaderWithAclList *headers);
211 static void parse_HeaderWithAclList(HeaderWithAclList **header);
212 static void free_HeaderWithAclList(HeaderWithAclList **header);
213 static void parse_denyinfo(acl_deny_info_list ** var);
214 static void dump_denyinfo(StoreEntry * entry, const char *name, acl_deny_info_list * var);
215 static void free_denyinfo(acl_deny_info_list ** var);
216
217 #if USE_WCCPv2
218 static void parse_IpAddress_list(Ip::Address_list **);
219 static void dump_IpAddress_list(StoreEntry *, const char *, const Ip::Address_list *);
220 static void free_IpAddress_list(Ip::Address_list **);
221 #if CURRENTLY_UNUSED
222 static int check_null_IpAddress_list(const Ip::Address_list *);
223 #endif /* CURRENTLY_UNUSED */
224 #endif /* USE_WCCPv2 */
225
226 static void parsePortCfg(AnyP::PortCfg **, const char *protocol);
227 #define parse_PortCfg(l) parsePortCfg((l), token)
228 static void dump_PortCfg(StoreEntry *, const char *, const AnyP::PortCfg *);
229 static void free_PortCfg(AnyP::PortCfg **);
230
231 #if USE_SSL
232 static void parse_sslproxy_cert_sign(sslproxy_cert_sign **cert_sign);
233 static void dump_sslproxy_cert_sign(StoreEntry *entry, const char *name, sslproxy_cert_sign *cert_sign);
234 static void free_sslproxy_cert_sign(sslproxy_cert_sign **cert_sign);
235 static void parse_sslproxy_cert_adapt(sslproxy_cert_adapt **cert_adapt);
236 static void dump_sslproxy_cert_adapt(StoreEntry *entry, const char *name, sslproxy_cert_adapt *cert_adapt);
237 static void free_sslproxy_cert_adapt(sslproxy_cert_adapt **cert_adapt);
238 static void parse_sslproxy_ssl_bump(acl_access **ssl_bump);
239 static void dump_sslproxy_ssl_bump(StoreEntry *entry, const char *name, acl_access *ssl_bump);
240 static void free_sslproxy_ssl_bump(acl_access **ssl_bump);
241 #endif /* USE_SSL */
242
243 static void parse_b_size_t(size_t * var);
244 static void parse_b_int64_t(int64_t * var);
245
246 static bool parseNamedIntList(const char *data, const String &name, Vector<int> &list);
247
248 static void parse_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap);
249 static void dump_CpuAffinityMap(StoreEntry *const entry, const char *const name, const CpuAffinityMap *const cpuAffinityMap);
250 static void free_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap);
251
252 static int parseOneConfigFile(const char *file_name, unsigned int depth);
253
254 /*
255 * LegacyParser is a parser for legacy code that uses the global
256 * approach. This is static so that it is only exposed to cache_cf.
257 * Other modules needing access to a ConfigParser should have it
258 * provided to them in their parserFOO methods.
259 */
260 static ConfigParser LegacyParser = ConfigParser();
261
262 void
263 self_destruct(void)
264 {
265 LegacyParser.destruct();
266 }
267
268 static void
269 update_maxobjsize(void)
270 {
271 int i;
272 int64_t ms = -1;
273
274 for (i = 0; i < Config.cacheSwap.n_configured; ++i) {
275 assert (Config.cacheSwap.swapDirs[i].getRaw());
276
277 if (dynamic_cast<SwapDir *>(Config.cacheSwap.swapDirs[i].getRaw())->
278 max_objsize > ms)
279 ms = dynamic_cast<SwapDir *>(Config.cacheSwap.swapDirs[i].getRaw())->max_objsize;
280 }
281 store_maxobjsize = ms;
282 }
283
284 static void
285 SetConfigFilename(char const *file_name, bool is_pipe)
286 {
287 cfg_filename = file_name;
288
289 char const *token;
290
291 if (is_pipe)
292 cfg_filename = file_name + 1;
293 else if ((token = strrchr(cfg_filename, '/')))
294 cfg_filename = token + 1;
295 }
296
297 static const char*
298 skip_ws(const char* s)
299 {
300 while (xisspace(*s))
301 ++s;
302
303 return s;
304 }
305
306 static int
307 parseManyConfigFiles(char* files, int depth)
308 {
309 int error_count = 0;
310 char* saveptr = NULL;
311 #if HAVE_GLOB
312 char *path;
313 glob_t globbuf;
314 int i;
315 memset(&globbuf, 0, sizeof(globbuf));
316 for (path = strwordtok(files, &saveptr); path; path = strwordtok(NULL, &saveptr)) {
317 if (glob(path, globbuf.gl_pathc ? GLOB_APPEND : 0, NULL, &globbuf) != 0) {
318 fatalf("Unable to find configuration file: %s: %s",
319 path, xstrerror());
320 }
321 }
322 for (i = 0; i < (int)globbuf.gl_pathc; ++i) {
323 error_count += parseOneConfigFile(globbuf.gl_pathv[i], depth);
324 }
325 globfree(&globbuf);
326 #else
327 char* file = strwordtok(files, &saveptr);
328 while (file != NULL) {
329 error_count += parseOneConfigFile(file, depth);
330 file = strwordtok(NULL, &saveptr);
331 }
332 #endif /* HAVE_GLOB */
333 return error_count;
334 }
335
336 static void
337 ReplaceSubstr(char*& str, int& len, unsigned substrIdx, unsigned substrLen, const char* newSubstr)
338 {
339 assert(str != NULL);
340 assert(newSubstr != NULL);
341
342 unsigned newSubstrLen = strlen(newSubstr);
343 if (newSubstrLen > substrLen)
344 str = (char*)realloc(str, len - substrLen + newSubstrLen + 1);
345
346 // move tail part including zero
347 memmove(str + substrIdx + newSubstrLen, str + substrIdx + substrLen, len - substrIdx - substrLen + 1);
348 // copy new substring in place
349 memcpy(str + substrIdx, newSubstr, newSubstrLen);
350
351 len = strlen(str);
352 }
353
354 static void
355 SubstituteMacro(char*& line, int& len, const char* macroName, const char* substStr)
356 {
357 assert(line != NULL);
358 assert(macroName != NULL);
359 assert(substStr != NULL);
360 unsigned macroNameLen = strlen(macroName);
361 while (const char* macroPos = strstr(line, macroName)) // we would replace all occurrences
362 ReplaceSubstr(line, len, macroPos - line, macroNameLen, substStr);
363 }
364
365 static void
366 ProcessMacros(char*& line, int& len)
367 {
368 SubstituteMacro(line, len, "${process_name}", TheKidName);
369 SubstituteMacro(line, len, "${process_number}", xitoa(KidIdentifier));
370 }
371
372 static void
373 trim_trailing_ws(char* str)
374 {
375 assert(str != NULL);
376 unsigned i = strlen(str);
377 while ((i > 0) && xisspace(str[i - 1]))
378 --i;
379 str[i] = '\0';
380 }
381
382 static const char*
383 FindStatement(const char* line, const char* statement)
384 {
385 assert(line != NULL);
386 assert(statement != NULL);
387
388 const char* str = skip_ws(line);
389 unsigned len = strlen(statement);
390 if (strncmp(str, statement, len) == 0) {
391 str += len;
392 if (*str == '\0')
393 return str;
394 else if (xisspace(*str))
395 return skip_ws(str);
396 }
397
398 return NULL;
399 }
400
401 static bool
402 StrToInt(const char* str, long& number)
403 {
404 assert(str != NULL);
405
406 char* end;
407 number = strtol(str, &end, 0);
408
409 return (end != str) && (*end == '\0'); // returns true if string contains nothing except number
410 }
411
412 static bool
413 EvalBoolExpr(const char* expr)
414 {
415 assert(expr != NULL);
416 if (strcmp(expr, "true") == 0) {
417 return true;
418 } else if (strcmp(expr, "false") == 0) {
419 return false;
420 } else if (const char* equation = strchr(expr, '=')) {
421 const char* rvalue = skip_ws(equation + 1);
422 char* lvalue = (char*)xmalloc(equation - expr + 1);
423 xstrncpy(lvalue, expr, equation - expr + 1);
424 trim_trailing_ws(lvalue);
425
426 long number1;
427 if (!StrToInt(lvalue, number1))
428 fatalf("String is not a integer number: '%s'\n", lvalue);
429 long number2;
430 if (!StrToInt(rvalue, number2))
431 fatalf("String is not a integer number: '%s'\n", rvalue);
432
433 xfree(lvalue);
434 return number1 == number2;
435 }
436 fatalf("Unable to evaluate expression '%s'\n", expr);
437 return false; // this place cannot be reached
438 }
439
440 static int
441 parseOneConfigFile(const char *file_name, unsigned int depth)
442 {
443 FILE *fp = NULL;
444 const char *orig_cfg_filename = cfg_filename;
445 const int orig_config_lineno = config_lineno;
446 char *token = NULL;
447 char *tmp_line = NULL;
448 int tmp_line_len = 0;
449 int err_count = 0;
450 int is_pipe = 0;
451
452 debugs(3, DBG_IMPORTANT, "Processing Configuration File: " << file_name << " (depth " << depth << ")");
453 if (depth > 16) {
454 fatalf("WARNING: can't include %s: includes are nested too deeply (>16)!\n", file_name);
455 return 1;
456 }
457
458 if (file_name[0] == '!' || file_name[0] == '|') {
459 fp = popen(file_name + 1, "r");
460 is_pipe = 1;
461 } else {
462 fp = fopen(file_name, "r");
463 }
464
465 if (fp == NULL)
466 fatalf("Unable to open configuration file: %s: %s", file_name, xstrerror());
467
468 #if _SQUID_WINDOWS_
469 setmode(fileno(fp), O_TEXT);
470 #endif
471
472 SetConfigFilename(file_name, bool(is_pipe));
473
474 memset(config_input_line, '\0', BUFSIZ);
475
476 config_lineno = 0;
477
478 Vector<bool> if_states;
479 while (fgets(config_input_line, BUFSIZ, fp)) {
480 ++config_lineno;
481
482 if ((token = strchr(config_input_line, '\n')))
483 *token = '\0';
484
485 if ((token = strchr(config_input_line, '\r')))
486 *token = '\0';
487
488 // strip any prefix whitespace off the line.
489 const char *p = skip_ws(config_input_line);
490 if (config_input_line != p)
491 memmove(config_input_line, p, strlen(p)+1);
492
493 if (strncmp(config_input_line, "#line ", 6) == 0) {
494 static char new_file_name[1024];
495 static char *file;
496 static char new_lineno;
497 token = config_input_line + 6;
498 new_lineno = strtol(token, &file, 0) - 1;
499
500 if (file == token)
501 continue; /* Not a valid #line directive, may be a comment */
502
503 while (*file && xisspace((unsigned char) *file))
504 ++file;
505
506 if (*file) {
507 if (*file != '"')
508 continue; /* Not a valid #line directive, may be a comment */
509
510 xstrncpy(new_file_name, file + 1, sizeof(new_file_name));
511
512 if ((token = strchr(new_file_name, '"')))
513 *token = '\0';
514
515 cfg_filename = new_file_name;
516 }
517
518 config_lineno = new_lineno;
519 }
520
521 if (config_input_line[0] == '#')
522 continue;
523
524 if (config_input_line[0] == '\0')
525 continue;
526
527 const char* append = tmp_line_len ? skip_ws(config_input_line) : config_input_line;
528
529 size_t append_len = strlen(append);
530
531 tmp_line = (char*)xrealloc(tmp_line, tmp_line_len + append_len + 1);
532
533 strcpy(tmp_line + tmp_line_len, append);
534
535 tmp_line_len += append_len;
536
537 if (tmp_line[tmp_line_len-1] == '\\') {
538 debugs(3, 5, "parseConfigFile: tmp_line='" << tmp_line << "'");
539 tmp_line[--tmp_line_len] = '\0';
540 continue;
541 }
542
543 trim_trailing_ws(tmp_line);
544 ProcessMacros(tmp_line, tmp_line_len);
545 debugs(3, (opt_parse_cfg_only?1:5), "Processing: " << tmp_line);
546
547 if (const char* expr = FindStatement(tmp_line, "if")) {
548 if_states.push_back(EvalBoolExpr(expr)); // store last if-statement meaning
549 } else if (FindStatement(tmp_line, "endif")) {
550 if (!if_states.empty())
551 if_states.pop_back(); // remove last if-statement meaning
552 else
553 fatalf("'endif' without 'if'\n");
554 } else if (FindStatement(tmp_line, "else")) {
555 if (!if_states.empty())
556 if_states.back() = !if_states.back();
557 else
558 fatalf("'else' without 'if'\n");
559 } else if (if_states.empty() || if_states.back()) { // test last if-statement meaning if present
560 /* Handle includes here */
561 if (tmp_line_len >= 9 && strncmp(tmp_line, "include", 7) == 0 && xisspace(tmp_line[7])) {
562 err_count += parseManyConfigFiles(tmp_line + 8, depth + 1);
563 } else if (!parse_line(tmp_line)) {
564 debugs(3, DBG_CRITICAL, HERE << cfg_filename << ":" << config_lineno << " unrecognized: '" << tmp_line << "'");
565 ++err_count;
566 }
567 }
568
569 safe_free(tmp_line);
570 tmp_line_len = 0;
571
572 }
573 if (!if_states.empty())
574 fatalf("if-statement without 'endif'\n");
575
576 if (is_pipe) {
577 int ret = pclose(fp);
578
579 if (ret != 0)
580 fatalf("parseConfigFile: '%s' failed with exit code %d\n", file_name, ret);
581 } else {
582 fclose(fp);
583 }
584
585 cfg_filename = orig_cfg_filename;
586 config_lineno = orig_config_lineno;
587
588 return err_count;
589 }
590
591 int
592 parseConfigFile(const char *file_name)
593 {
594 int err_count = 0;
595
596 debugs(5, 4, HERE);
597
598 configFreeMemory();
599
600 ACLMethodData::ThePurgeCount = 0;
601 default_all();
602
603 err_count = parseOneConfigFile(file_name, 0);
604
605 defaults_if_none();
606
607 defaults_postscriptum();
608
609 /*
610 * We must call configDoConfigure() before leave_suid() because
611 * configDoConfigure() is where we turn username strings into
612 * uid values.
613 */
614 configDoConfigure();
615
616 if (!Config.chroot_dir) {
617 leave_suid();
618 setUmask(Config.umask);
619 _db_init(Debug::cache_log, Debug::debugOptions);
620 enter_suid();
621 }
622
623 if (opt_send_signal == -1) {
624 Mgr::RegisterAction("config",
625 "Current Squid Configuration",
626 dump_config,
627 1, 1);
628 }
629
630 return err_count;
631 }
632
633 static void
634 configDoConfigure(void)
635 {
636 memset(&Config2, '\0', sizeof(SquidConfig2));
637 /* init memory as early as possible */
638 memConfigure();
639 /* Sanity checks */
640
641 if (Config.cacheSwap.swapDirs == NULL) {
642 /* Memory-only cache probably in effect. */
643 /* turn off the cache rebuild delays... */
644 StoreController::store_dirs_rebuilding = 0;
645 } else if (InDaemonMode()) { // no diskers in non-daemon mode
646 for (int i = 0; i < Config.cacheSwap.n_configured; ++i) {
647 const RefCount<SwapDir> sd = Config.cacheSwap.swapDirs[i];
648 if (sd->needsDiskStrand())
649 sd->disker = Config.workers + (++Config.cacheSwap.n_strands);
650 }
651 }
652
653 if (Debug::rotateNumber < 0) {
654 Debug::rotateNumber = Config.Log.rotateNumber;
655 }
656
657 #if SIZEOF_OFF_T <= 4
658 if (Config.Store.maxObjectSize > 0x7FFF0000) {
659 debugs(3, DBG_CRITICAL, "WARNING: This Squid binary can not handle files larger than 2GB. Limiting maximum_object_size to just below 2GB");
660 Config.Store.maxObjectSize = 0x7FFF0000;
661 }
662 #endif
663 if (0 == Store::Root().maxSize())
664 /* people might want a zero-sized cache on purpose */
665 (void) 0;
666 else if (Store::Root().maxSize() < Config.memMaxSize)
667 /* This is bogus. folk with NULL caches will want this */
668 debugs(3, DBG_CRITICAL, "WARNING cache_mem is larger than total disk cache space!");
669
670 if (Config.Announce.period > 0) {
671 Config.onoff.announce = 1;
672 } else if (Config.Announce.period < 1) {
673 Config.Announce.period = 86400 * 365; /* one year */
674 Config.onoff.announce = 0;
675 }
676
677 if (Config.onoff.httpd_suppress_version_string)
678 visible_appname_string = (char *)appname_string;
679 else
680 visible_appname_string = (char const *)APP_FULLNAME;
681
682 #if USE_DNSHELPER
683 if (Config.dnsChildren.n_max < 1)
684 fatal("No DNS helpers allocated");
685 #endif
686
687 if (Config.Program.redirect) {
688 if (Config.redirectChildren.n_max < 1) {
689 Config.redirectChildren.n_max = 0;
690 wordlistDestroy(&Config.Program.redirect);
691 }
692 }
693
694 if (Config.appendDomain)
695 if (*Config.appendDomain != '.')
696 fatal("append_domain must begin with a '.'");
697
698 if (Config.errHtmlText == NULL)
699 Config.errHtmlText = xstrdup(null_string);
700
701 #if !HAVE_SETRLIMIT || !defined(RLIMIT_NOFILE)
702 if (Config.max_filedescriptors > 0) {
703 debugs(0, DBG_IMPORTANT, "WARNING: max_filedescriptors disabled. Operating System setrlimit(RLIMIT_NOFILE) is missing.");
704 }
705 #elif USE_SELECT || USE_SELECT_WIN32
706 if (Config.max_filedescriptors > FD_SETSIZE) {
707 debugs(0, DBG_IMPORTANT, "WARNING: max_filedescriptors limited to " << FD_SETSIZE << " by select() algorithm.");
708 }
709 #endif
710
711 storeConfigure();
712
713 snprintf(ThisCache, sizeof(ThisCache), "%s (%s)",
714 uniqueHostname(),
715 visible_appname_string);
716
717 /*
718 * the extra space is for loop detection in client_side.c -- we search
719 * for substrings in the Via header.
720 */
721 snprintf(ThisCache2, sizeof(ThisCache), " %s (%s)",
722 uniqueHostname(),
723 visible_appname_string);
724
725 /* Use visible_hostname as default surrogate_id */
726 if (!Config.Accel.surrogate_id) {
727 const char *t = getMyHostname();
728 Config.Accel.surrogate_id = xstrdup( (t?t:"unset-id") );
729 }
730
731 if (!Config.udpMaxHitObjsz || Config.udpMaxHitObjsz > SQUID_UDP_SO_SNDBUF)
732 Config.udpMaxHitObjsz = SQUID_UDP_SO_SNDBUF;
733
734 if (Config.appendDomain)
735 Config.appendDomainLen = strlen(Config.appendDomain);
736 else
737 Config.appendDomainLen = 0;
738
739 if (Config.connect_retries > 10) {
740 debugs(0,DBG_CRITICAL, "WARNING: connect_retries cannot be larger than 10. Resetting to 10.");
741 Config.connect_retries = 10;
742 }
743
744 requirePathnameExists("MIME Config Table", Config.mimeTablePathname);
745 #if USE_DNSHELPER
746 requirePathnameExists("cache_dns_program", Config.Program.dnsserver);
747 #endif
748 #if USE_UNLINKD
749
750 requirePathnameExists("unlinkd_program", Config.Program.unlinkd);
751 #endif
752 requirePathnameExists("logfile_daemon", Log::TheConfig.logfile_daemon);
753 if (Config.Program.redirect)
754 requirePathnameExists("redirect_program", Config.Program.redirect->key);
755
756 requirePathnameExists("Icon Directory", Config.icons.directory);
757
758 if (Config.errorDirectory)
759 requirePathnameExists("Error Directory", Config.errorDirectory);
760
761 #if USE_HTTP_VIOLATIONS
762
763 {
764 const refresh_t *R;
765
766 for (R = Config.Refresh; R; R = R->next) {
767 if (!R->flags.override_expire)
768 continue;
769
770 debugs(22, DBG_IMPORTANT, "WARNING: use of 'override-expire' in 'refresh_pattern' violates HTTP");
771
772 break;
773 }
774
775 for (R = Config.Refresh; R; R = R->next) {
776 if (!R->flags.override_lastmod)
777 continue;
778
779 debugs(22, DBG_IMPORTANT, "WARNING: use of 'override-lastmod' in 'refresh_pattern' violates HTTP");
780
781 break;
782 }
783
784 for (R = Config.Refresh; R; R = R->next) {
785 if (!R->flags.reload_into_ims)
786 continue;
787
788 debugs(22, DBG_IMPORTANT, "WARNING: use of 'reload-into-ims' in 'refresh_pattern' violates HTTP");
789
790 break;
791 }
792
793 for (R = Config.Refresh; R; R = R->next) {
794 if (!R->flags.ignore_reload)
795 continue;
796
797 debugs(22, DBG_IMPORTANT, "WARNING: use of 'ignore-reload' in 'refresh_pattern' violates HTTP");
798
799 break;
800 }
801
802 for (R = Config.Refresh; R; R = R->next) {
803 if (!R->flags.ignore_no_cache)
804 continue;
805
806 debugs(22, DBG_IMPORTANT, "WARNING: use of 'ignore-no-cache' in 'refresh_pattern' violates HTTP");
807
808 break;
809 }
810
811 for (R = Config.Refresh; R; R = R->next) {
812 if (!R->flags.ignore_no_store)
813 continue;
814
815 debugs(22, DBG_IMPORTANT, "WARNING: use of 'ignore-no-store' in 'refresh_pattern' violates HTTP");
816
817 break;
818 }
819
820 for (R = Config.Refresh; R; R = R->next) {
821 if (!R->flags.ignore_must_revalidate)
822 continue;
823 debugs(22, DBG_IMPORTANT, "WARNING: use of 'ignore-must-revalidate' in 'refresh_pattern' violates HTTP");
824 break;
825 }
826
827 for (R = Config.Refresh; R; R = R->next) {
828 if (!R->flags.ignore_private)
829 continue;
830
831 debugs(22, DBG_IMPORTANT, "WARNING: use of 'ignore-private' in 'refresh_pattern' violates HTTP");
832
833 break;
834 }
835
836 for (R = Config.Refresh; R; R = R->next) {
837 if (!R->flags.ignore_auth)
838 continue;
839
840 debugs(22, DBG_IMPORTANT, "WARNING: use of 'ignore-auth' in 'refresh_pattern' violates HTTP");
841
842 break;
843 }
844
845 }
846 #endif
847 #if !USE_HTTP_VIOLATIONS
848 Config.onoff.via = 1;
849 #else
850
851 if (!Config.onoff.via)
852 debugs(22, DBG_IMPORTANT, "WARNING: HTTP requires the use of Via");
853
854 #endif
855
856 // we enable runtime PURGE checks if there is at least one PURGE method ACL
857 // TODO: replace with a dedicated "purge" ACL option?
858 Config2.onoff.enable_purge = (ACLMethodData::ThePurgeCount > 0);
859
860 Config2.onoff.mangle_request_headers = (Config.request_header_access != NULL);
861
862 if (geteuid() == 0) {
863 if (NULL != Config.effectiveUser) {
864
865 struct passwd *pwd = getpwnam(Config.effectiveUser);
866
867 if (NULL == pwd) {
868 /*
869 * Andres Kroonmaa <andre@online.ee>:
870 * Some getpwnam() implementations (Solaris?) require
871 * an available FD < 256 for opening a FILE* to the
872 * passwd file.
873 * DW:
874 * This should be safe at startup, but might still fail
875 * during reconfigure.
876 */
877 fatalf("getpwnam failed to find userid for effective user '%s'",
878 Config.effectiveUser);
879 return;
880 }
881
882 Config2.effectiveUserID = pwd->pw_uid;
883
884 Config2.effectiveGroupID = pwd->pw_gid;
885
886 #if HAVE_PUTENV
887
888 if (pwd->pw_dir && *pwd->pw_dir) {
889 int len;
890 char *env_str = (char *)xcalloc((len = strlen(pwd->pw_dir) + 6), 1);
891 snprintf(env_str, len, "HOME=%s", pwd->pw_dir);
892 putenv(env_str);
893 }
894
895 #endif
896
897 }
898 } else {
899 Config2.effectiveUserID = geteuid();
900 Config2.effectiveGroupID = getegid();
901 }
902
903 if (NULL != Config.effectiveGroup) {
904
905 struct group *grp = getgrnam(Config.effectiveGroup);
906
907 if (NULL == grp) {
908 fatalf("getgrnam failed to find groupid for effective group '%s'",
909 Config.effectiveGroup);
910 return;
911 }
912
913 Config2.effectiveGroupID = grp->gr_gid;
914 }
915
916 HttpRequestMethod::Configure(Config);
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 (peer *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(acl_address);
1366
1367 static void
1368 dump_acl_address(StoreEntry * entry, const char *name, acl_address * head)
1369 {
1370 char buf[MAX_IPSTRLEN];
1371 acl_address *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 acl_address *l = static_cast<acl_address *>(data);
1389 aclDestroyAclList(&l->aclList);
1390 }
1391
1392 static void
1393 parse_acl_address(acl_address ** head)
1394 {
1395 acl_address *l;
1396 acl_address **tail = head; /* sane name below */
1397 CBDATA_INIT_TYPE_FREECB(acl_address, freed_acl_address);
1398 l = cbdataAlloc(acl_address);
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(acl_address ** head)
1410 {
1411 while (*head) {
1412 acl_address *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(acl_size_t);
1560
1561 static void
1562 dump_acl_b_size_t(StoreEntry * entry, const char *name, acl_size_t * head)
1563 {
1564 acl_size_t *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 acl_size_t *l = static_cast<acl_size_t *>(data);
1582 aclDestroyAclList(&l->aclList);
1583 }
1584
1585 static void
1586 parse_acl_b_size_t(acl_size_t ** head)
1587 {
1588 acl_size_t *l;
1589 acl_size_t **tail = head; /* sane name below */
1590
1591 CBDATA_INIT_TYPE_FREECB(acl_size_t, freed_acl_b_size_t);
1592
1593 l = cbdataAlloc(acl_size_t);
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(acl_size_t ** head)
1607 {
1608 while (*head) {
1609 acl_size_t *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 header_mangler *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, peer * p)
1971 {
1972 domain_ping *d;
1973 domain_type *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(peer ** head)
2071 {
2072 char *token = NULL;
2073 peer *p;
2074 CBDATA_INIT_TYPE_FREECB(peer, peerDestroy);
2075 p = cbdataAlloc(peer);
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(peer ** P)
2350 {
2351 peer *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, cachemgr_passwd * 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(cachemgr_passwd ** head)
2388 {
2389 char *passwd = NULL;
2390 wordlist *actions = NULL;
2391 cachemgr_passwd *p;
2392 cachemgr_passwd **P;
2393 parse_string(&passwd);
2394 parse_wordlist(&actions);
2395 p = static_cast<cachemgr_passwd *>(xcalloc(1, sizeof(cachemgr_passwd)));
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 cachemgr_passwd struct 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(cachemgr_passwd ** head)
2426 {
2427 cachemgr_passwd *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, acl_deny_info_list * var)
2439 {
2440 acl_name_list *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(acl_deny_info_list ** var)
2456 {
2457 aclParseDenyInfoLine(var);
2458 }
2459
2460 void
2461 free_denyinfo(acl_deny_info_list ** list)
2462 {
2463 acl_deny_info_list *a = NULL;
2464 acl_deny_info_list *a_next = NULL;
2465 acl_name_list *l = NULL;
2466 acl_name_list *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 peer *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 domain_ping *l = NULL;
2511 domain_ping **L = NULL;
2512 peer *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<domain_ping *>(xcalloc(1, sizeof(domain_ping)));
2520 l->do_ping = 1;
2521
2522 if (*domain == '!') { /* check for !.edu */
2523 l->do_ping = 0;
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 domain_type *l = NULL;
2549 domain_type **L = NULL;
2550 peer *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<domain_type *>(xcalloc(1, sizeof(domain_type)));
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, refresh_t * 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_cache)
2677 storeAppendPrintf(entry, " ignore-no-cache");
2678
2679 if (head->flags.ignore_no_store)
2680 storeAppendPrintf(entry, " ignore-no-store");
2681
2682 if (head->flags.ignore_must_revalidate)
2683 storeAppendPrintf(entry, " ignore-must-revalidate");
2684
2685 if (head->flags.ignore_private)
2686 storeAppendPrintf(entry, " ignore-private");
2687
2688 if (head->flags.ignore_auth)
2689 storeAppendPrintf(entry, " ignore-auth");
2690
2691 #endif
2692
2693 storeAppendPrintf(entry, "\n");
2694
2695 head = head->next;
2696 }
2697 }
2698
2699 static void
2700 parse_refreshpattern(refresh_t ** head)
2701 {
2702 char *token;
2703 char *pattern;
2704 time_t min = 0;
2705 double pct = 0.0;
2706 time_t max = 0;
2707 int refresh_ims = 0;
2708 int store_stale = 0;
2709 int max_stale = -1;
2710
2711 #if USE_HTTP_VIOLATIONS
2712
2713 int override_expire = 0;
2714 int override_lastmod = 0;
2715 int reload_into_ims = 0;
2716 int ignore_reload = 0;
2717 int ignore_no_cache = 0;
2718 int ignore_no_store = 0;
2719 int ignore_must_revalidate = 0;
2720 int ignore_private = 0;
2721 int ignore_auth = 0;
2722 #endif
2723
2724 int i;
2725 refresh_t *t;
2726 regex_t comp;
2727 int errcode;
2728 int flags = REG_EXTENDED | REG_NOSUB;
2729
2730 if ((token = strtok(NULL, w_space)) == NULL) {
2731 self_destruct();
2732 return;
2733 }
2734
2735 if (strcmp(token, "-i") == 0) {
2736 flags |= REG_ICASE;
2737 token = strtok(NULL, w_space);
2738 } else if (strcmp(token, "+i") == 0) {
2739 flags &= ~REG_ICASE;
2740 token = strtok(NULL, w_space);
2741 }
2742
2743 if (token == NULL) {
2744 self_destruct();
2745 return;
2746 }
2747
2748 pattern = xstrdup(token);
2749
2750 i = GetInteger(); /* token: min */
2751
2752 /* catch negative and insanely huge values close to 32-bit wrap */
2753 if (i < 0) {
2754 debugs(3, DBG_IMPORTANT, "WARNING: refresh_pattern minimum age negative. Cropped back to zero.");
2755 i = 0;
2756 }
2757 if (i > 60*24*365) {
2758 debugs(3, DBG_IMPORTANT, "WARNING: refresh_pattern minimum age too high. Cropped back to 1 year.");
2759 i = 60*24*365;
2760 }
2761
2762 min = (time_t) (i * 60); /* convert minutes to seconds */
2763
2764 i = GetInteger(); /* token: pct */
2765
2766 pct = (double) i / 100.0;
2767
2768 i = GetInteger(); /* token: max */
2769
2770 /* catch negative and insanely huge values close to 32-bit wrap */
2771 if (i < 0) {
2772 debugs(3, DBG_IMPORTANT, "WARNING: refresh_pattern maximum age negative. Cropped back to zero.");
2773 i = 0;
2774 }
2775 if (i > 60*24*365) {
2776 debugs(3, DBG_IMPORTANT, "WARNING: refresh_pattern maximum age too high. Cropped back to 1 year.");
2777 i = 60*24*365;
2778 }
2779
2780 max = (time_t) (i * 60); /* convert minutes to seconds */
2781
2782 /* Options */
2783 while ((token = strtok(NULL, w_space)) != NULL) {
2784 if (!strcmp(token, "refresh-ims")) {
2785 refresh_ims = 1;
2786 } else if (!strcmp(token, "store-stale")) {
2787 store_stale = 1;
2788 } else if (!strncmp(token, "max-stale=", 10)) {
2789 max_stale = atoi(token + 10);
2790 #if USE_HTTP_VIOLATIONS
2791
2792 } else if (!strcmp(token, "override-expire"))
2793 override_expire = 1;
2794 else if (!strcmp(token, "override-lastmod"))
2795 override_lastmod = 1;
2796 else if (!strcmp(token, "ignore-no-cache"))
2797 ignore_no_cache = 1;
2798 else if (!strcmp(token, "ignore-no-store"))
2799 ignore_no_store = 1;
2800 else if (!strcmp(token, "ignore-must-revalidate"))
2801 ignore_must_revalidate = 1;
2802 else if (!strcmp(token, "ignore-private"))
2803 ignore_private = 1;
2804 else if (!strcmp(token, "ignore-auth"))
2805 ignore_auth = 1;
2806 else if (!strcmp(token, "reload-into-ims")) {
2807 reload_into_ims = 1;
2808 refresh_nocache_hack = 1;
2809 /* tell client_side.c that this is used */
2810 } else if (!strcmp(token, "ignore-reload")) {
2811 ignore_reload = 1;
2812 refresh_nocache_hack = 1;
2813 /* tell client_side.c that this is used */
2814 #endif
2815
2816 } else
2817 debugs(22, DBG_CRITICAL, "refreshAddToList: Unknown option '" << pattern << "': " << token);
2818 }
2819
2820 if ((errcode = regcomp(&comp, pattern, flags)) != 0) {
2821 char errbuf[256];
2822 regerror(errcode, &comp, errbuf, sizeof errbuf);
2823 debugs(22, DBG_CRITICAL, "" << cfg_filename << " line " << config_lineno << ": " << config_input_line);
2824 debugs(22, DBG_CRITICAL, "refreshAddToList: Invalid regular expression '" << pattern << "': " << errbuf);
2825 return;
2826 }
2827
2828 pct = pct < 0.0 ? 0.0 : pct;
2829 max = max < 0 ? 0 : max;
2830 t = static_cast<refresh_t *>(xcalloc(1, sizeof(refresh_t)));
2831 t->pattern = (char *) xstrdup(pattern);
2832 t->compiled_pattern = comp;
2833 t->min = min;
2834 t->pct = pct;
2835 t->max = max;
2836
2837 if (flags & REG_ICASE)
2838 t->flags.icase = 1;
2839
2840 if (refresh_ims)
2841 t->flags.refresh_ims = 1;
2842
2843 if (store_stale)
2844 t->flags.store_stale = 1;
2845
2846 t->max_stale = max_stale;
2847
2848 #if USE_HTTP_VIOLATIONS
2849
2850 if (override_expire)
2851 t->flags.override_expire = 1;
2852
2853 if (override_lastmod)
2854 t->flags.override_lastmod = 1;
2855
2856 if (reload_into_ims)
2857 t->flags.reload_into_ims = 1;
2858
2859 if (ignore_reload)
2860 t->flags.ignore_reload = 1;
2861
2862 if (ignore_no_cache)
2863 t->flags.ignore_no_cache = 1;
2864
2865 if (ignore_no_store)
2866 t->flags.ignore_no_store = 1;
2867
2868 if (ignore_must_revalidate)
2869 t->flags.ignore_must_revalidate = 1;
2870
2871 if (ignore_private)
2872 t->flags.ignore_private = 1;
2873
2874 if (ignore_auth)
2875 t->flags.ignore_auth = 1;
2876
2877 #endif
2878
2879 t->next = NULL;
2880
2881 while (*head)
2882 head = &(*head)->next;
2883
2884 *head = t;
2885
2886 safe_free(pattern);
2887 }
2888
2889 static void
2890 free_refreshpattern(refresh_t ** head)
2891 {
2892 refresh_t *t;
2893
2894 while ((t = *head) != NULL) {
2895 *head = t->next;
2896 safe_free(t->pattern);
2897 regfree(&t->compiled_pattern);
2898 safe_free(t);
2899 }
2900
2901 #if USE_HTTP_VIOLATIONS
2902 refresh_nocache_hack = 0;
2903
2904 #endif
2905 }
2906
2907 static void
2908 dump_string(StoreEntry * entry, const char *name, char *var)
2909 {
2910 if (var != NULL)
2911 storeAppendPrintf(entry, "%s %s\n", name, var);
2912 }
2913
2914 static void
2915 parse_string(char **var)
2916 {
2917 char *token = strtok(NULL, w_space);
2918 safe_free(*var);
2919
2920 if (token == NULL)
2921 self_destruct();
2922
2923 *var = xstrdup(token);
2924 }
2925
2926 void
2927 ConfigParser::ParseString(char **var)
2928 {
2929 parse_string(var);
2930 }
2931
2932 void
2933 ConfigParser::ParseString(String *var)
2934 {
2935 char *token = strtok(NULL, w_space);
2936
2937 if (token == NULL)
2938 self_destruct();
2939
2940 var->reset(token);
2941 }
2942
2943 static void
2944 free_string(char **var)
2945 {
2946 safe_free(*var);
2947 }
2948
2949 void
2950 parse_eol(char *volatile *var)
2951 {
2952 if (!var) {
2953 self_destruct();
2954 return;
2955 }
2956
2957 unsigned char *token = (unsigned char *) strtok(NULL, null_string);
2958 safe_free(*var);
2959
2960 if (!token) {
2961 self_destruct();
2962 return;
2963 }
2964
2965 while (*token && xisspace(*token))
2966 ++token;
2967
2968 if (!*token) {
2969 self_destruct();
2970 return;
2971 }
2972
2973 *var = xstrdup((char *) token);
2974 }
2975
2976 #define dump_eol dump_string
2977 #define free_eol free_string
2978
2979 static void
2980 dump_time_t(StoreEntry * entry, const char *name, time_t var)
2981 {
2982 storeAppendPrintf(entry, "%s %d seconds\n", name, (int) var);
2983 }
2984
2985 void
2986 parse_time_t(time_t * var)
2987 {
2988 time_msec_t tval;
2989 parseTimeLine(&tval, T_SECOND_STR, false);
2990 *var = static_cast<time_t>(tval/1000);
2991 }
2992
2993 static void
2994 free_time_t(time_t * var)
2995 {
2996 *var = 0;
2997 }
2998
2999 #if !USE_DNSHELPER
3000 static void
3001 dump_time_msec(StoreEntry * entry, const char *name, time_msec_t var)
3002 {
3003 if (var % 1000)
3004 storeAppendPrintf(entry, "%s %" PRId64 " milliseconds\n", name, var);
3005 else
3006 storeAppendPrintf(entry, "%s %d seconds\n", name, (int)(var/1000) );
3007 }
3008
3009 void
3010 parse_time_msec(time_msec_t * var)
3011 {
3012 parseTimeLine(var, T_SECOND_STR, true);
3013 }
3014
3015 static void
3016 free_time_msec(time_msec_t * var)
3017 {
3018 *var = 0;
3019 }
3020 #endif
3021
3022 #if UNUSED_CODE
3023 static void
3024 dump_size_t(StoreEntry * entry, const char *name, size_t var)
3025 {
3026 storeAppendPrintf(entry, "%s %d\n", name, (int) var);
3027 }
3028 #endif
3029
3030 static void
3031 dump_b_size_t(StoreEntry * entry, const char *name, size_t var)
3032 {
3033 storeAppendPrintf(entry, "%s %d %s\n", name, (int) var, B_BYTES_STR);
3034 }
3035
3036 #if !USE_DNSHELPER
3037 static void
3038 dump_b_ssize_t(StoreEntry * entry, const char *name, ssize_t var)
3039 {
3040 storeAppendPrintf(entry, "%s %d %s\n", name, (int) var, B_BYTES_STR);
3041 }
3042 #endif
3043
3044 #if UNUSED_CODE
3045 static void
3046 dump_kb_size_t(StoreEntry * entry, const char *name, size_t var)
3047 {
3048 storeAppendPrintf(entry, "%s %d %s\n", name, (int) var, B_KBYTES_STR);
3049 }
3050 #endif
3051
3052 static void
3053 dump_b_int64_t(StoreEntry * entry, const char *name, int64_t var)
3054 {
3055 storeAppendPrintf(entry, "%s %" PRId64 " %s\n", name, var, B_BYTES_STR);
3056 }
3057
3058 static void
3059 dump_kb_int64_t(StoreEntry * entry, const char *name, int64_t var)
3060 {
3061 storeAppendPrintf(entry, "%s %" PRId64 " %s\n", name, var, B_KBYTES_STR);
3062 }
3063
3064 #if UNUSED_CODE
3065 static void
3066 parse_size_t(size_t * var)
3067 {
3068 int i;
3069 i = GetInteger();
3070 *var = (size_t) i;
3071 }
3072 #endif
3073
3074 static void
3075 parse_b_size_t(size_t * var)
3076 {
3077 parseBytesLine(var, B_BYTES_STR);
3078 }
3079
3080 #if !USE_DNSHELPER
3081 static void
3082 parse_b_ssize_t(ssize_t * var)
3083 {
3084 parseBytesLineSigned(var, B_BYTES_STR);
3085 }
3086 #endif
3087
3088 #if UNUSED_CODE
3089 static void
3090 parse_kb_size_t(size_t * var)
3091 {
3092 parseBytesLine(var, B_KBYTES_STR);
3093 }
3094 #endif
3095
3096 static void
3097 parse_b_int64_t(int64_t * var)
3098 {
3099 parseBytesLine64(var, B_BYTES_STR);
3100 }
3101
3102 static void
3103 parse_kb_int64_t(int64_t * var)
3104 {
3105 parseBytesLine64(var, B_KBYTES_STR);
3106 }
3107
3108 static void
3109 free_size_t(size_t * var)
3110 {
3111 *var = 0;
3112 }
3113
3114 #if !USE_DNSHELPER
3115 static void
3116 free_ssize_t(ssize_t * var)
3117 {
3118 *var = 0;
3119 }
3120 #endif
3121
3122 static void
3123 free_b_int64_t(int64_t * var)
3124 {
3125 *var = 0;
3126 }
3127
3128 #define free_b_size_t free_size_t
3129 #define free_b_ssize_t free_ssize_t
3130 #define free_kb_size_t free_size_t
3131 #define free_mb_size_t free_size_t
3132 #define free_gb_size_t free_size_t
3133 #define free_kb_int64_t free_b_int64_t
3134
3135 static void
3136 dump_u_short(StoreEntry * entry, const char *name, unsigned short var)
3137 {
3138 storeAppendPrintf(entry, "%s %d\n", name, var);
3139 }
3140
3141 static void
3142 free_u_short(unsigned short * u)
3143 {
3144 *u = 0;
3145 }
3146
3147 static void
3148 parse_u_short(unsigned short * var)
3149 {
3150 ConfigParser::ParseUShort(var);
3151 }
3152
3153 void
3154 ConfigParser::ParseUShort(unsigned short *var)
3155 {
3156 *var = GetShort();
3157 }
3158
3159 void
3160 ConfigParser::ParseBool(bool *var)
3161 {
3162 int i = GetInteger();
3163
3164 if (0 == i)
3165 *var = false;
3166 else if (1 == i)
3167 *var = true;
3168 else
3169 self_destruct();
3170 }
3171
3172 static void
3173 dump_wordlist(StoreEntry * entry, const char *name, wordlist * list)
3174 {
3175 while (list != NULL) {
3176 storeAppendPrintf(entry, "%s %s\n", name, list->key);
3177 list = list->next;
3178 }
3179 }
3180
3181 void
3182 ConfigParser::ParseWordList(wordlist ** list)
3183 {
3184 parse_wordlist(list);
3185 }
3186
3187 void
3188 parse_wordlist(wordlist ** list)
3189 {
3190 char *token;
3191 char *t = strtok(NULL, "");
3192
3193 while ((token = strwordtok(NULL, &t)))
3194 wordlistAdd(list, token);
3195 }
3196
3197 #if 0 /* now unused */
3198 static int
3199 check_null_wordlist(wordlist * w)
3200 {
3201 return w == NULL;
3202 }
3203 #endif
3204
3205 static int
3206 check_null_acl_access(acl_access * a)
3207 {
3208 return a == NULL;
3209 }
3210
3211 #define free_wordlist wordlistDestroy
3212
3213 #define free_uri_whitespace free_int
3214
3215 static void
3216 parse_uri_whitespace(int *var)
3217 {
3218 char *token = strtok(NULL, w_space);
3219
3220 if (token == NULL)
3221 self_destruct();
3222
3223 if (!strcasecmp(token, "strip"))
3224 *var = URI_WHITESPACE_STRIP;
3225 else if (!strcasecmp(token, "deny"))
3226 *var = URI_WHITESPACE_DENY;
3227 else if (!strcasecmp(token, "allow"))
3228 *var = URI_WHITESPACE_ALLOW;
3229 else if (!strcasecmp(token, "encode"))
3230 *var = URI_WHITESPACE_ENCODE;
3231 else if (!strcasecmp(token, "chop"))
3232 *var = URI_WHITESPACE_CHOP;
3233 else
3234 self_destruct();
3235 }
3236
3237 static void
3238 dump_uri_whitespace(StoreEntry * entry, const char *name, int var)
3239 {
3240 const char *s;
3241
3242 if (var == URI_WHITESPACE_ALLOW)
3243 s = "allow";
3244 else if (var == URI_WHITESPACE_ENCODE)
3245 s = "encode";
3246 else if (var == URI_WHITESPACE_CHOP)
3247 s = "chop";
3248 else if (var == URI_WHITESPACE_DENY)
3249 s = "deny";
3250 else
3251 s = "strip";
3252
3253 storeAppendPrintf(entry, "%s %s\n", name, s);
3254 }
3255
3256 static void
3257 free_removalpolicy(RemovalPolicySettings ** settings)
3258 {
3259 if (!*settings)
3260 return;
3261
3262 free_string(&(*settings)->type);
3263
3264 free_wordlist(&(*settings)->args);
3265
3266 delete *settings;
3267
3268 *settings = NULL;
3269 }
3270
3271 static void
3272 parse_removalpolicy(RemovalPolicySettings ** settings)
3273 {
3274 if (*settings)
3275 free_removalpolicy(settings);
3276
3277 *settings = new RemovalPolicySettings;
3278
3279 parse_string(&(*settings)->type);
3280
3281 parse_wordlist(&(*settings)->args);
3282 }
3283
3284 static void
3285 dump_removalpolicy(StoreEntry * entry, const char *name, RemovalPolicySettings * settings)
3286 {
3287 wordlist *args;
3288 storeAppendPrintf(entry, "%s %s", name, settings->type);
3289 args = settings->args;
3290
3291 while (args) {
3292 storeAppendPrintf(entry, " %s", args->key);
3293 args = args->next;
3294 }
3295
3296 storeAppendPrintf(entry, "\n");
3297 }
3298
3299 void
3300 YesNoNone::configure(bool beSet)
3301 {
3302 option = beSet ? +1 : -1;
3303 }
3304
3305 YesNoNone::operator void*() const
3306 {
3307 assert(option != 0); // must call configure() first
3308 return option > 0 ? (void*)this : NULL;
3309 }
3310
3311 inline void
3312 free_YesNoNone(YesNoNone *)
3313 {
3314 // do nothing: no explicit cleanup is required
3315 }
3316
3317 static void
3318 parse_YesNoNone(YesNoNone *option)
3319 {
3320 int value = 0;
3321 parse_onoff(&value);
3322 option->configure(value > 0);
3323 }
3324
3325 static void
3326 dump_YesNoNone(StoreEntry * entry, const char *name, YesNoNone &option)
3327 {
3328 if (option.configured())
3329 dump_onoff(entry, name, option ? 1 : 0);
3330 }
3331
3332 static void
3333 free_memcachemode(SquidConfig * config)
3334 {
3335 return;
3336 }
3337
3338 static void
3339 parse_memcachemode(SquidConfig * config)
3340 {
3341 char *token = strtok(NULL, w_space);
3342 if (!token)
3343 self_destruct();
3344
3345 if (strcmp(token, "always") == 0) {
3346 Config.onoff.memory_cache_first = 1;
3347 Config.onoff.memory_cache_disk = 1;
3348 } else if (strcmp(token, "disk") == 0) {
3349 Config.onoff.memory_cache_first = 0;
3350 Config.onoff.memory_cache_disk = 1;
3351 } else if (strncmp(token, "net", 3) == 0) {
3352 Config.onoff.memory_cache_first = 1;
3353 Config.onoff.memory_cache_disk = 0;
3354 } else if (strcmp(token, "never") == 0) {
3355 Config.onoff.memory_cache_first = 0;
3356 Config.onoff.memory_cache_disk = 0;
3357 } else
3358 self_destruct();
3359 }
3360
3361 static void
3362 dump_memcachemode(StoreEntry * entry, const char *name, SquidConfig &config)
3363 {
3364 storeAppendPrintf(entry, "%s ", name);
3365 if (Config.onoff.memory_cache_first && Config.onoff.memory_cache_disk)
3366 storeAppendPrintf(entry, "always");
3367 else if (!Config.onoff.memory_cache_first && Config.onoff.memory_cache_disk)
3368 storeAppendPrintf(entry, "disk");
3369 else if (Config.onoff.memory_cache_first && !Config.onoff.memory_cache_disk)
3370 storeAppendPrintf(entry, "network");
3371 else if (!Config.onoff.memory_cache_first && !Config.onoff.memory_cache_disk)
3372 storeAppendPrintf(entry, "none");
3373 storeAppendPrintf(entry, "\n");
3374 }
3375
3376 #include "cf_parser.cci"
3377
3378 peer_t
3379 parseNeighborType(const char *s)
3380 {
3381 if (!strcasecmp(s, "parent"))
3382 return PEER_PARENT;
3383
3384 if (!strcasecmp(s, "neighbor"))
3385 return PEER_SIBLING;
3386
3387 if (!strcasecmp(s, "neighbour"))
3388 return PEER_SIBLING;
3389
3390 if (!strcasecmp(s, "sibling"))
3391 return PEER_SIBLING;
3392
3393 if (!strcasecmp(s, "multicast"))
3394 return PEER_MULTICAST;
3395
3396 debugs(15, DBG_CRITICAL, "WARNING: Unknown neighbor type: " << s);
3397
3398 return PEER_SIBLING;
3399 }
3400
3401 #if USE_WCCPv2
3402 static void
3403 parse_IpAddress_list(Ip::Address_list ** head)
3404 {
3405 char *token;
3406 Ip::Address_list *s;
3407 Ip::Address ipa;
3408
3409 while ((token = strtok(NULL, w_space))) {
3410 if (GetHostWithPort(token, &ipa)) {
3411
3412 while (*head)
3413 head = &(*head)->next;
3414
3415 s = static_cast<Ip::Address_list *>(xcalloc(1, sizeof(*s)));
3416 s->s = ipa;
3417
3418 *head = s;
3419 } else
3420 self_destruct();
3421 }
3422 }
3423
3424 static void
3425 dump_IpAddress_list(StoreEntry * e, const char *n, const Ip::Address_list * s)
3426 {
3427 char ntoabuf[MAX_IPSTRLEN];
3428
3429 while (s) {
3430 storeAppendPrintf(e, "%s %s\n",
3431 n,
3432 s->s.NtoA(ntoabuf,MAX_IPSTRLEN));
3433 s = s->next;
3434 }
3435 }
3436
3437 static void
3438 free_IpAddress_list(Ip::Address_list ** head)
3439 {
3440 if (*head) delete *head;
3441 *head = NULL;
3442 }
3443
3444 #if CURRENTLY_UNUSED
3445 /* This code was previously used by http_port. Left as it really should
3446 * be used by icp_port and htcp_port
3447 */
3448 static int
3449 check_null_IpAddress_list(const Ip::Address_list * s)
3450 {
3451 return NULL == s;
3452 }
3453
3454 #endif /* CURRENTLY_UNUSED */
3455 #endif /* USE_WCCPv2 */
3456
3457 static void
3458 parsePortSpecification(AnyP::PortCfg * s, char *token)
3459 {
3460 char *host = NULL;
3461 unsigned short port = 0;
3462 char *t = NULL;
3463 char *junk = NULL;
3464
3465 s->disable_pmtu_discovery = DISABLE_PMTU_OFF;
3466 s->name = xstrdup(token);
3467 s->connection_auth_disabled = false;
3468
3469 if (*token == '[') {
3470 /* [ipv6]:port */
3471 host = token + 1;
3472 t = strchr(host, ']');
3473 if (!t) {
3474 debugs(3, DBG_CRITICAL, s->protocol << "_port: missing ']' on IPv6 address: " << token);
3475 self_destruct();
3476 }
3477 *t = '\0';
3478 ++t;
3479 if (*t != ':') {
3480 debugs(3, DBG_CRITICAL, s->protocol << "_port: missing Port in: " << token);
3481 self_destruct();
3482 }
3483 if (!Ip::EnableIpv6) {
3484 debugs(3, DBG_CRITICAL, "FATAL: " << s->protocol << "_port: IPv6 is not available.");
3485 self_destruct();
3486 }
3487 port = xatos(t + 1);
3488 } else if ((t = strchr(token, ':'))) {
3489 /* host:port */
3490 /* ipv4:port */
3491 host = token;
3492 *t = '\0';
3493 port = xatos(t + 1);
3494
3495 } else if ((port = strtol(token, &junk, 10)), !*junk) {
3496 /* port */
3497 debugs(3, 3, s->protocol << "_port: found Listen on Port: " << port);
3498 } else {
3499 debugs(3, DBG_CRITICAL, s->protocol << "_port: missing Port: " << token);
3500 self_destruct();
3501 }
3502
3503 if (port == 0 && host != NULL) {
3504 debugs(3, DBG_CRITICAL, s->protocol << "_port: Port cannot be 0: " << token);
3505 self_destruct();
3506 }
3507
3508 if (NULL == host) {
3509 s->s.SetAnyAddr();
3510 s->s.SetPort(port);
3511 if (!Ip::EnableIpv6)
3512 s->s.SetIPv4();
3513 debugs(3, 3, s->protocol << "_port: found Listen on wildcard address: *:" << s->s.GetPort() );
3514 } else if ( (s->s = host) ) { /* check/parse numeric IPA */
3515 s->s.SetPort(port);
3516 if (!Ip::EnableIpv6)
3517 s->s.SetIPv4();
3518 debugs(3, 3, s->protocol << "_port: Listen on Host/IP: " << host << " --> " << s->s);
3519 } else if ( s->s.GetHostByName(host) ) { /* check/parse for FQDN */
3520 /* dont use ipcache */
3521 s->defaultsite = xstrdup(host);
3522 s->s.SetPort(port);
3523 if (!Ip::EnableIpv6)
3524 s->s.SetIPv4();
3525 debugs(3, 3, s->protocol << "_port: found Listen as Host " << s->defaultsite << " on IP: " << s->s);
3526 } else {
3527 debugs(3, DBG_CRITICAL, s->protocol << "_port: failed to resolve Host/IP: " << host);
3528 self_destruct();
3529 }
3530 }
3531
3532 static void
3533 parse_port_option(AnyP::PortCfg * s, char *token)
3534 {
3535 /* modes first */
3536
3537 if (strcmp(token, "accel") == 0) {
3538 if (s->intercepted || s->spoof_client_ip) {
3539 debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: Accelerator mode requires its own port. It cannot be shared with other modes.");
3540 self_destruct();
3541 }
3542 s->accel = s->vhost = 1;
3543 } else if (strcmp(token, "transparent") == 0 || strcmp(token, "intercept") == 0) {
3544 if (s->accel || s->spoof_client_ip) {
3545 debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: Intercept mode requires its own interception port. It cannot be shared with other modes.");
3546 self_destruct();
3547 }
3548 s->intercepted = 1;
3549 Ip::Interceptor.StartInterception();
3550 /* Log information regarding the port modes under interception. */
3551 debugs(3, DBG_IMPORTANT, "Starting Authentication on port " << s->s);
3552 debugs(3, DBG_IMPORTANT, "Disabling Authentication on port " << s->s << " (interception enabled)");
3553
3554 /* INET6: until transparent REDIRECT works on IPv6 SOCKET, force wildcard to IPv4 */
3555 if (Ip::EnableIpv6)
3556 debugs(3, DBG_IMPORTANT, "Disabling IPv6 on port " << s->s << " (interception enabled)");
3557 if ( !s->s.SetIPv4() ) {
3558 debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: IPv6 addresses cannot NAT intercept (protocol does not provide NAT)" << s->s );
3559 self_destruct();
3560 }
3561 } else if (strcmp(token, "tproxy") == 0) {
3562 if (s->intercepted || s->accel) {
3563 debugs(3,DBG_CRITICAL, "FATAL: http(s)_port: TPROXY option requires its own interception port. It cannot be shared with other modes.");
3564 self_destruct();
3565 }
3566 s->spoof_client_ip = 1;
3567 Ip::Interceptor.StartTransparency();
3568 /* Log information regarding the port modes under transparency. */
3569 debugs(3, DBG_IMPORTANT, "Starting IP Spoofing on port " << s->s);
3570 debugs(3, DBG_IMPORTANT, "Disabling Authentication on port " << s->s << " (IP spoofing enabled)");
3571
3572 if (!Ip::Interceptor.ProbeForTproxy(s->s)) {
3573 debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: TPROXY support in the system does not work.");
3574 self_destruct();
3575 }
3576
3577 } else if (strncmp(token, "defaultsite=", 12) == 0) {
3578 if (!s->accel) {
3579 debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: defaultsite option requires Acceleration mode flag.");
3580 self_destruct();
3581 }
3582 safe_free(s->defaultsite);
3583 s->defaultsite = xstrdup(token + 12);
3584 } else if (strcmp(token, "vhost") == 0) {
3585 if (!s->accel) {
3586 debugs(3, DBG_CRITICAL, "WARNING: http(s)_port: vhost option is deprecated. Use 'accel' mode flag instead.");
3587 }
3588 s->accel = s->vhost = 1;
3589 } else if (strcmp(token, "no-vhost") == 0) {
3590 if (!s->accel) {
3591 debugs(3, DBG_IMPORTANT, "ERROR: http(s)_port: no-vhost option requires Acceleration mode flag.");
3592 }
3593 s->vhost = 0;
3594 } else if (strcmp(token, "vport") == 0) {
3595 if (!s->accel) {
3596 debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: vport option requires Acceleration mode flag.");
3597 self_destruct();
3598 }
3599 s->vport = -1;
3600 } else if (strncmp(token, "vport=", 6) == 0) {
3601 if (!s->accel) {
3602 debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: vport option requires Acceleration mode flag.");
3603 self_destruct();
3604 }
3605 s->vport = xatos(token + 6);
3606 } else if (strncmp(token, "protocol=", 9) == 0) {
3607 if (!s->accel) {
3608 debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: protocol option requires Acceleration mode flag.");
3609 self_destruct();
3610 }
3611 s->protocol = xstrdup(token + 9);
3612 } else if (strcmp(token, "allow-direct") == 0) {
3613 if (!s->accel) {
3614 debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: allow-direct option requires Acceleration mode flag.");
3615 self_destruct();
3616 }
3617 s->allow_direct = 1;
3618 } else if (strcmp(token, "act-as-origin") == 0) {
3619 if (!s->accel) {
3620 debugs(3, DBG_IMPORTANT, "ERROR: http(s)_port: act-as-origin option requires Acceleration mode flag.");
3621 } else
3622 s->actAsOrigin = 1;
3623 } else if (strcmp(token, "ignore-cc") == 0) {
3624 #if !USE_HTTP_VIOLATIONS
3625 if (!s->accel) {
3626 debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: ignore-cc option requires Scceleration mode flag.");
3627 self_destruct();
3628 }
3629 #endif
3630 s->ignore_cc = 1;
3631 } else if (strncmp(token, "name=", 5) == 0) {
3632 safe_free(s->name);
3633 s->name = xstrdup(token + 5);
3634 } else if (strcmp(token, "no-connection-auth") == 0) {
3635 s->connection_auth_disabled = true;
3636 } else if (strcmp(token, "connection-auth=off") == 0) {
3637 s->connection_auth_disabled = true;
3638 } else if (strcmp(token, "connection-auth") == 0) {
3639 s->connection_auth_disabled = false;
3640 } else if (strcmp(token, "connection-auth=on") == 0) {
3641 s->connection_auth_disabled = false;
3642 } else if (strncmp(token, "disable-pmtu-discovery=", 23) == 0) {
3643 if (!strcasecmp(token + 23, "off"))
3644 s->disable_pmtu_discovery = DISABLE_PMTU_OFF;
3645 else if (!strcasecmp(token + 23, "transparent"))
3646 s->disable_pmtu_discovery = DISABLE_PMTU_TRANSPARENT;
3647 else if (!strcasecmp(token + 23, "always"))
3648 s->disable_pmtu_discovery = DISABLE_PMTU_ALWAYS;
3649 else
3650 self_destruct();
3651 } else if (strcmp(token, "ipv4") == 0) {
3652 if ( !s->s.SetIPv4() ) {
3653 debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: IPv6 addresses cannot be used as IPv4-Only. " << s->s );
3654 self_destruct();
3655 }
3656 } else if (strcmp(token, "tcpkeepalive") == 0) {
3657 s->tcp_keepalive.enabled = 1;
3658 } else if (strncmp(token, "tcpkeepalive=", 13) == 0) {
3659 char *t = token + 13;
3660 s->tcp_keepalive.enabled = 1;
3661 s->tcp_keepalive.idle = atoi(t);
3662 t = strchr(t, ',');
3663 if (t) {
3664 ++t;
3665 s->tcp_keepalive.interval = atoi(t);
3666 t = strchr(t, ',');
3667 }
3668 if (t) {
3669 ++t;
3670 s->tcp_keepalive.timeout = atoi(t);
3671 t = strchr(t, ',');
3672 }
3673 #if USE_SSL
3674 } else if (strcasecmp(token, "sslBump") == 0) {
3675 debugs(3, DBG_CRITICAL, "WARNING: '" << token << "' is deprecated " <<
3676 "in http_port. Use 'ssl-bump' instead.");
3677 s->sslBump = 1; // accelerated when bumped, otherwise not
3678 } else if (strcmp(token, "ssl-bump") == 0) {
3679 s->sslBump = 1; // accelerated when bumped, otherwise not
3680 } else if (strncmp(token, "cert=", 5) == 0) {
3681 safe_free(s->cert);
3682 s->cert = xstrdup(token + 5);
3683 } else if (strncmp(token, "key=", 4) == 0) {
3684 safe_free(s->key);
3685 s->key = xstrdup(token + 4);
3686 } else if (strncmp(token, "version=", 8) == 0) {
3687 s->version = xatoi(token + 8);
3688 if (s->version < 1 || s->version > 4)
3689 self_destruct();
3690 } else if (strncmp(token, "options=", 8) == 0) {
3691 safe_free(s->options);
3692 s->options = xstrdup(token + 8);
3693 } else if (strncmp(token, "cipher=", 7) == 0) {
3694 safe_free(s->cipher);
3695 s->cipher = xstrdup(token + 7);
3696 } else if (strncmp(token, "clientca=", 9) == 0) {
3697 safe_free(s->clientca);
3698 s->clientca = xstrdup(token + 9);
3699 } else if (strncmp(token, "cafile=", 7) == 0) {
3700 safe_free(s->cafile);
3701 s->cafile = xstrdup(token + 7);
3702 } else if (strncmp(token, "capath=", 7) == 0) {
3703 safe_free(s->capath);
3704 s->capath = xstrdup(token + 7);
3705 } else if (strncmp(token, "crlfile=", 8) == 0) {
3706 safe_free(s->crlfile);
3707 s->crlfile = xstrdup(token + 8);
3708 } else if (strncmp(token, "dhparams=", 9) == 0) {
3709 safe_free(s->dhfile);
3710 s->dhfile = xstrdup(token + 9);
3711 } else if (strncmp(token, "sslflags=", 9) == 0) {
3712 safe_free(s->sslflags);
3713 s->sslflags = xstrdup(token + 9);
3714 } else if (strncmp(token, "sslcontext=", 11) == 0) {
3715 safe_free(s->sslContextSessionId);
3716 s->sslContextSessionId = xstrdup(token + 11);
3717 } else if (strcmp(token, "generate-host-certificates") == 0) {
3718 s->generateHostCertificates = true;
3719 } else if (strcmp(token, "generate-host-certificates=on") == 0) {
3720 s->generateHostCertificates = true;
3721 } else if (strcmp(token, "generate-host-certificates=off") == 0) {
3722 s->generateHostCertificates = false;
3723 } else if (strncmp(token, "dynamic_cert_mem_cache_size=", 28) == 0) {
3724 parseBytesOptionValue(&s->dynamicCertMemCacheSize, B_BYTES_STR, token + 28);
3725 #endif
3726 } else {
3727 self_destruct();
3728 }
3729 }
3730
3731 void
3732 add_http_port(char *portspec)
3733 {
3734 AnyP::PortCfg *s = new AnyP::PortCfg("http_port");
3735 parsePortSpecification(s, portspec);
3736 // we may need to merge better if the above returns a list with clones
3737 assert(s->next == NULL);
3738 s->next = cbdataReference(Config.Sockaddr.http);
3739 cbdataReferenceDone(Config.Sockaddr.http);
3740 Config.Sockaddr.http = cbdataReference(s);
3741 }
3742
3743 static void
3744 parsePortCfg(AnyP::PortCfg ** head, const char *optionName)
3745 {
3746 const char *protocol = NULL;
3747 if (strcmp(optionName, "http_port") == 0 ||
3748 strcmp(optionName, "ascii_port") == 0)
3749 protocol = "http";
3750 else if (strcmp(optionName, "https_port") == 0)
3751 protocol = "https";
3752 if (!protocol) {
3753 self_destruct();
3754 return;
3755 }
3756
3757 char *token = strtok(NULL, w_space);
3758
3759 if (!token) {
3760 self_destruct();
3761 return;
3762 }
3763
3764 AnyP::PortCfg *s = new AnyP::PortCfg(protocol);
3765 parsePortSpecification(s, token);
3766
3767 /* parse options ... */
3768 while ((token = strtok(NULL, w_space))) {
3769 parse_port_option(s, token);
3770 }
3771
3772 #if USE_SSL
3773 if (strcasecmp(protocol, "https") == 0) {
3774 /* ssl-bump on https_port configuration requires either tproxy or intercept, and vice versa */
3775 const bool hijacked = s->spoof_client_ip || s->intercepted;
3776 if (s->sslBump && !hijacked) {
3777 debugs(3, DBG_CRITICAL, "FATAL: ssl-bump on https_port requires tproxy/intercept which is missing.");
3778 self_destruct();
3779 }
3780 if (hijacked && !s->sslBump) {
3781 debugs(3, DBG_CRITICAL, "FATAL: tproxy/intercept on https_port requires ssl-bump which is missing.");
3782 self_destruct();
3783 }
3784 }
3785 #endif
3786
3787 if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && s->s.IsAnyAddr()) {
3788 // clone the port options from *s to *(s->next)
3789 s->next = cbdataReference(s->clone());
3790 s->next->s.SetIPv4();
3791 debugs(3, 3, protocol << "_port: clone wildcard address for split-stack: " << s->s << " and " << s->next->s);
3792 }
3793
3794 while (*head)
3795 head = &(*head)->next;
3796
3797 *head = cbdataReference(s);
3798 }
3799
3800 static void
3801 dump_generic_port(StoreEntry * e, const char *n, const AnyP::PortCfg * s)
3802 {
3803 char buf[MAX_IPSTRLEN];
3804
3805 storeAppendPrintf(e, "%s %s",
3806 n,
3807 s->s.ToURL(buf,MAX_IPSTRLEN));
3808
3809 // MODES and specific sub-options.
3810 if (s->intercepted)
3811 storeAppendPrintf(e, " intercept");
3812
3813 else if (s->spoof_client_ip)
3814 storeAppendPrintf(e, " tproxy");
3815
3816 else if (s->accel) {
3817 storeAppendPrintf(e, " accel");
3818
3819 if (s->vhost)
3820 storeAppendPrintf(e, " vhost");
3821
3822 if (s->vport < 0)
3823 storeAppendPrintf(e, " vport");
3824 else if (s->vport > 0)
3825 storeAppendPrintf(e, " vport=%d", s->vport);
3826
3827 if (s->defaultsite)
3828 storeAppendPrintf(e, " defaultsite=%s", s->defaultsite);
3829
3830 if (s->protocol && strcmp(s->protocol,"http") != 0)
3831 storeAppendPrintf(e, " protocol=%s", s->protocol);
3832
3833 if (s->allow_direct)
3834 storeAppendPrintf(e, " allow-direct");
3835
3836 if (s->ignore_cc)
3837 storeAppendPrintf(e, " ignore-cc");
3838
3839 }
3840
3841 // Generic independent options
3842
3843 if (s->name)
3844 storeAppendPrintf(e, " name=%s", s->name);
3845
3846 #if USE_HTTP_VIOLATIONS
3847 if (!s->accel && s->ignore_cc)
3848 storeAppendPrintf(e, " ignore-cc");
3849 #endif
3850
3851 if (s->connection_auth_disabled)
3852 storeAppendPrintf(e, " connection-auth=off");
3853 else
3854 storeAppendPrintf(e, " connection-auth=on");
3855
3856 if (s->disable_pmtu_discovery != DISABLE_PMTU_OFF) {
3857 const char *pmtu;
3858
3859 if (s->disable_pmtu_discovery == DISABLE_PMTU_ALWAYS)
3860 pmtu = "always";
3861 else
3862 pmtu = "transparent";
3863
3864 storeAppendPrintf(e, " disable-pmtu-discovery=%s", pmtu);
3865 }
3866
3867 if (s->s.IsAnyAddr() && !s->s.IsIPv6())
3868 storeAppendPrintf(e, " ipv4");
3869
3870 if (s->tcp_keepalive.enabled) {
3871 if (s->tcp_keepalive.idle || s->tcp_keepalive.interval || s->tcp_keepalive.timeout) {
3872 storeAppendPrintf(e, " tcpkeepalive=%d,%d,%d", s->tcp_keepalive.idle, s->tcp_keepalive.interval, s->tcp_keepalive.timeout);
3873 } else {
3874 storeAppendPrintf(e, " tcpkeepalive");
3875 }
3876 }
3877
3878 #if USE_SSL
3879 if (s->sslBump)
3880 storeAppendPrintf(e, " ssl-bump");
3881
3882 if (s->cert)
3883 storeAppendPrintf(e, " cert=%s", s->cert);
3884
3885 if (s->key)
3886 storeAppendPrintf(e, " key=%s", s->key);
3887
3888 if (s->version)
3889 storeAppendPrintf(e, " version=%d", s->version);
3890
3891 if (s->options)
3892 storeAppendPrintf(e, " options=%s", s->options);
3893
3894 if (s->cipher)
3895 storeAppendPrintf(e, " cipher=%s", s->cipher);
3896
3897 if (s->cafile)
3898 storeAppendPrintf(e, " cafile=%s", s->cafile);
3899
3900 if (s->capath)
3901 storeAppendPrintf(e, " capath=%s", s->capath);
3902
3903 if (s->crlfile)
3904 storeAppendPrintf(e, " crlfile=%s", s->crlfile);
3905
3906 if (s->dhfile)
3907 storeAppendPrintf(e, " dhparams=%s", s->dhfile);
3908
3909 if (s->sslflags)
3910 storeAppendPrintf(e, " sslflags=%s", s->sslflags);
3911
3912 if (s->sslContextSessionId)
3913 storeAppendPrintf(e, " sslcontext=%s", s->sslContextSessionId);
3914
3915 if (s->generateHostCertificates)
3916 storeAppendPrintf(e, " generate-host-certificates");
3917
3918 if (s->dynamicCertMemCacheSize != std::numeric_limits<size_t>::max())
3919 storeAppendPrintf(e, "dynamic_cert_mem_cache_size=%lu%s\n", (unsigned long)s->dynamicCertMemCacheSize, B_BYTES_STR);
3920 #endif
3921 }
3922
3923 static void
3924 dump_PortCfg(StoreEntry * e, const char *n, const AnyP::PortCfg * s)
3925 {
3926 while (s) {
3927 dump_generic_port(e, n, s);
3928 storeAppendPrintf(e, "\n");
3929 s = s->next;
3930 }
3931 }
3932
3933 static void
3934 free_PortCfg(AnyP::PortCfg ** head)
3935 {
3936 AnyP::PortCfg *s;
3937
3938 while ((s = *head) != NULL) {
3939 *head = s->next;
3940 cbdataReferenceDone(s);
3941 }
3942 }
3943
3944 void
3945 configFreeMemory(void)
3946 {
3947 free_all();
3948 #if USE_SSL
3949 SSL_CTX_free(Config.ssl_client.sslContext);
3950 #endif
3951 }
3952
3953 void
3954 requirePathnameExists(const char *name, const char *path)
3955 {
3956
3957 struct stat sb;
3958 char pathbuf[BUFSIZ];
3959 assert(path != NULL);
3960
3961 if (Config.chroot_dir && (geteuid() == 0)) {
3962 snprintf(pathbuf, BUFSIZ, "%s/%s", Config.chroot_dir, path);
3963 path = pathbuf;
3964 }
3965
3966 if (stat(path, &sb) < 0) {
3967 debugs(0, DBG_CRITICAL, (opt_parse_cfg_only?"FATAL ":"") << "ERROR: " << name << " " << path << ": " << xstrerror());
3968 // keep going to find more issues if we are only checking the config file with "-k parse"
3969 if (opt_parse_cfg_only)
3970 return;
3971 // this is fatal if it is found during startup or reconfigure
3972 if (opt_send_signal == -1 || opt_send_signal == SIGHUP)
3973 fatalf("%s %s: %s", name, path, xstrerror());
3974 }
3975 }
3976
3977 char *
3978 strtokFile(void)
3979 {
3980 return ConfigParser::strtokFile();
3981 }
3982
3983 #include "AccessLogEntry.h"
3984
3985 static void
3986 parse_access_log(customlog ** logs)
3987 {
3988 const char *filename, *logdef_name;
3989
3990 customlog *cl = (customlog *)xcalloc(1, sizeof(*cl));
3991
3992 if ((filename = strtok(NULL, w_space)) == NULL) {
3993 self_destruct();
3994 return;
3995 }
3996
3997 if (strcmp(filename, "none") == 0) {
3998 cl->type = Log::Format::CLF_NONE;
3999 aclParseAclList(LegacyParser, &cl->aclList);
4000 while (*logs)
4001 logs = &(*logs)->next;
4002 *logs = cl;
4003 return;
4004 }
4005
4006 if ((logdef_name = strtok(NULL, w_space)) == NULL)
4007 logdef_name = "squid";
4008
4009 debugs(3, 9, "Log definition name '" << logdef_name << "' file '" << filename << "'");
4010
4011 cl->filename = xstrdup(filename);
4012
4013 /* look for the definition pointer corresponding to this name */
4014 Format::Format *lf = Log::TheConfig.logformats;
4015
4016 while (lf != NULL) {
4017 debugs(3, 9, "Comparing against '" << lf->name << "'");
4018
4019 if (strcmp(lf->name, logdef_name) == 0)
4020 break;
4021
4022 lf = lf->next;
4023 }
4024
4025 if (lf != NULL) {
4026 cl->type = Log::Format::CLF_CUSTOM;
4027 cl->logFormat = lf;
4028 } else if (strcmp(logdef_name, "auto") == 0) {
4029 debugs(0, DBG_CRITICAL, "WARNING: Log format 'auto' no longer exists. Using 'squid' instead.");
4030 cl->type = Log::Format::CLF_SQUID;
4031 } else if (strcmp(logdef_name, "squid") == 0) {
4032 cl->type = Log::Format::CLF_SQUID;
4033 } else if (strcmp(logdef_name, "common") == 0) {
4034 cl->type = Log::Format::CLF_COMMON;
4035 } else if (strcmp(logdef_name, "combined") == 0) {
4036 cl->type = Log::Format::CLF_COMBINED;
4037 #if ICAP_CLIENT
4038 } else if (strcmp(logdef_name, "icap_squid") == 0) {
4039 cl->type = Log::Format::CLF_ICAP_SQUID;
4040 #endif
4041 } else if (strcmp(logdef_name, "useragent") == 0) {
4042 cl->type = Log::Format::CLF_USERAGENT;
4043 } else if (strcmp(logdef_name, "referrer") == 0) {
4044 cl->type = Log::Format::CLF_REFERER;
4045 } else {
4046 debugs(3, DBG_CRITICAL, "Log format '" << logdef_name << "' is not defined");
4047 self_destruct();
4048 return;
4049 }
4050
4051 aclParseAclList(LegacyParser, &cl->aclList);
4052
4053 while (*logs)
4054 logs = &(*logs)->next;
4055
4056 *logs = cl;
4057 }
4058
4059 static int
4060 check_null_access_log(customlog *customlog_definitions)
4061 {
4062 return customlog_definitions == NULL;
4063 }
4064
4065 static void
4066 dump_access_log(StoreEntry * entry, const char *name, customlog * logs)
4067 {
4068 customlog *log;
4069
4070 for (log = logs; log; log = log->next) {
4071 storeAppendPrintf(entry, "%s ", name);
4072
4073 switch (log->type) {
4074
4075 case Log::Format::CLF_CUSTOM:
4076 storeAppendPrintf(entry, "%s %s", log->filename, log->logFormat->name);
4077 break;
4078
4079 case Log::Format::CLF_NONE:
4080 storeAppendPrintf(entry, "none");
4081 break;
4082
4083 case Log::Format::CLF_SQUID:
4084 storeAppendPrintf(entry, "%s squid", log->filename);
4085 break;
4086
4087 case Log::Format::CLF_COMBINED:
4088 storeAppendPrintf(entry, "%s combined", log->filename);
4089 break;
4090
4091 case Log::Format::CLF_COMMON:
4092 storeAppendPrintf(entry, "%s common", log->filename);
4093 break;
4094
4095 #if ICAP_CLIENT
4096 case Log::Format::CLF_ICAP_SQUID:
4097 storeAppendPrintf(entry, "%s icap_squid", log->filename);
4098 break;
4099 #endif
4100 case Log::Format::CLF_USERAGENT:
4101 storeAppendPrintf(entry, "%s useragent", log->filename);
4102 break;
4103
4104 case Log::Format::CLF_REFERER:
4105 storeAppendPrintf(entry, "%s referrer", log->filename);
4106 break;
4107
4108 case Log::Format::CLF_UNKNOWN:
4109 break;
4110 }
4111
4112 if (log->aclList)
4113 dump_acl_list(entry, log->aclList);
4114
4115 storeAppendPrintf(entry, "\n");
4116 }
4117 }
4118
4119 static void
4120 free_access_log(customlog ** definitions)
4121 {
4122 while (*definitions) {
4123 customlog *log = *definitions;
4124 *definitions = log->next;
4125
4126 log->logFormat = NULL;
4127 log->type = Log::Format::CLF_UNKNOWN;
4128
4129 if (log->aclList)
4130 aclDestroyAclList(&log->aclList);
4131
4132 safe_free(log->filename);
4133
4134 xfree(log);
4135 }
4136 }
4137
4138 /// parses list of integers form name=N1,N2,N3,...
4139 static bool
4140 parseNamedIntList(const char *data, const String &name, Vector<int> &list)
4141 {
4142 if (data && (strncmp(data, name.rawBuf(), name.size()) == 0)) {
4143 data += name.size();
4144 if (*data == '=') {
4145 while (true) {
4146 ++data;
4147 int value = 0;
4148 if (!StringToInt(data, value, &data, 10))
4149 break;
4150 list.push_back(value);
4151 if (*data == '\0' || *data != ',')
4152 break;
4153 }
4154 }
4155 }
4156 return data && *data == '\0';
4157 }
4158
4159 static void
4160 parse_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap)
4161 {
4162 #if !HAVE_CPU_AFFINITY
4163 debugs(3, DBG_CRITICAL, "FATAL: Squid built with no CPU affinity " <<
4164 "support, do not set 'cpu_affinity_map'");
4165 self_destruct();
4166 #endif /* HAVE_CPU_AFFINITY */
4167
4168 if (!*cpuAffinityMap)
4169 *cpuAffinityMap = new CpuAffinityMap;
4170
4171 const char *const pToken = strtok(NULL, w_space);
4172 const char *const cToken = strtok(NULL, w_space);
4173 Vector<int> processes, cores;
4174 if (!parseNamedIntList(pToken, "process_numbers", processes)) {
4175 debugs(3, DBG_CRITICAL, "FATAL: bad 'process_numbers' parameter " <<
4176 "in 'cpu_affinity_map'");
4177 self_destruct();
4178 } else if (!parseNamedIntList(cToken, "cores", cores)) {
4179 debugs(3, DBG_CRITICAL, "FATAL: bad 'cores' parameter in " <<
4180 "'cpu_affinity_map'");
4181 self_destruct();
4182 } else if (!(*cpuAffinityMap)->add(processes, cores)) {
4183 debugs(3, DBG_CRITICAL, "FATAL: bad 'cpu_affinity_map'; " <<
4184 "process_numbers and cores lists differ in length or " <<
4185 "contain numbers <= 0");
4186 self_destruct();
4187 }
4188 }
4189
4190 static void
4191 dump_CpuAffinityMap(StoreEntry *const entry, const char *const name, const CpuAffinityMap *const cpuAffinityMap)
4192 {
4193 if (cpuAffinityMap) {
4194 storeAppendPrintf(entry, "%s process_numbers=", name);
4195 for (size_t i = 0; i < cpuAffinityMap->processes().size(); ++i) {
4196 storeAppendPrintf(entry, "%s%i", (i ? "," : ""),
4197 cpuAffinityMap->processes()[i]);
4198 }
4199 storeAppendPrintf(entry, " cores=");
4200 for (size_t i = 0; i < cpuAffinityMap->processes().size(); ++i) {
4201 storeAppendPrintf(entry, "%s%i", (i ? "," : ""),
4202 cpuAffinityMap->cores()[i]);
4203 }
4204 storeAppendPrintf(entry, "\n");
4205 }
4206 }
4207
4208 static void
4209 free_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap)
4210 {
4211 delete *cpuAffinityMap;
4212 *cpuAffinityMap = NULL;
4213 }
4214
4215 #if USE_ADAPTATION
4216
4217 static void
4218 parse_adaptation_service_set_type()
4219 {
4220 Adaptation::Config::ParseServiceSet();
4221 }
4222
4223 static void
4224 parse_adaptation_service_chain_type()
4225 {
4226 Adaptation::Config::ParseServiceChain();
4227 }
4228
4229 static void
4230 parse_adaptation_access_type()
4231 {
4232 Adaptation::Config::ParseAccess(LegacyParser);
4233 }
4234
4235 static void
4236 parse_adaptation_meta_type(Adaptation::Config::MetaHeaders *)
4237 {
4238 Adaptation::Config::ParseMetaHeader(LegacyParser);
4239 }
4240
4241 static void
4242 dump_adaptation_meta_type(StoreEntry *entry, const char *name, Adaptation::Config::MetaHeaders &)
4243 {
4244 Adaptation::Config::DumpMetaHeader(entry, name);
4245 }
4246
4247 static void
4248 free_adaptation_meta_type(Adaptation::Config::MetaHeaders *)
4249 {
4250 // Nothing to do, it is released inside Adaptation::Config::freeService()
4251 }
4252 #endif /* USE_ADAPTATION */
4253
4254 #if ICAP_CLIENT
4255
4256 static void
4257 parse_icap_service_type(Adaptation::Icap::Config * cfg)
4258 {
4259 cfg->parseService();
4260 }
4261
4262 static void
4263 free_icap_service_type(Adaptation::Icap::Config * cfg)
4264 {
4265 cfg->freeService();
4266 }
4267
4268 static void
4269 dump_icap_service_type(StoreEntry * entry, const char *name, const Adaptation::Icap::Config &cfg)
4270 {
4271 cfg.dumpService(entry, name);
4272 }
4273
4274 static void
4275 parse_icap_class_type()
4276 {
4277 debugs(93, DBG_CRITICAL, "WARNING: 'icap_class' is depricated. " <<
4278 "Use 'adaptation_service_set' instead");
4279 Adaptation::Config::ParseServiceSet();
4280 }
4281
4282 static void
4283 parse_icap_access_type()
4284 {
4285 debugs(93, DBG_CRITICAL, "WARNING: 'icap_access' is depricated. " <<
4286 "Use 'adaptation_access' instead");
4287 Adaptation::Config::ParseAccess(LegacyParser);
4288 }
4289
4290 #endif
4291
4292 #if USE_ECAP
4293
4294 static void
4295 parse_ecap_service_type(Adaptation::Ecap::Config * cfg)
4296 {
4297 cfg->parseService();
4298 }
4299
4300 static void
4301 free_ecap_service_type(Adaptation::Ecap::Config * cfg)
4302 {
4303 cfg->freeService();
4304 }
4305
4306 static void
4307 dump_ecap_service_type(StoreEntry * entry, const char *name, const Adaptation::Ecap::Config &cfg)
4308 {
4309 cfg.dumpService(entry, name);
4310 }
4311
4312 #endif /* USE_ECAP */
4313
4314 #if ICAP_CLIENT
4315 static void parse_icap_service_failure_limit(Adaptation::Icap::Config *cfg)
4316 {
4317 char *token;
4318 time_t d;
4319 time_t m;
4320 cfg->service_failure_limit = GetInteger();
4321
4322 if ((token = strtok(NULL, w_space)) == NULL)
4323 return;
4324
4325 if (strcmp(token,"in") != 0) {
4326 debugs(3, DBG_CRITICAL, "expecting 'in' on'" << config_input_line << "'");
4327 self_destruct();
4328 }
4329
4330 if ((token = strtok(NULL, w_space)) == NULL) {
4331 self_destruct();
4332 }
4333
4334 d = static_cast<time_t> (xatoi(token));
4335
4336 m = static_cast<time_t> (1);
4337
4338 if (0 == d)
4339 (void) 0;
4340 else if ((token = strtok(NULL, w_space)) == NULL) {
4341 debugs(3, DBG_CRITICAL, "No time-units on '" << config_input_line << "'");
4342 self_destruct();
4343 } else if ((m = parseTimeUnits(token, false)) == 0)
4344 self_destruct();
4345
4346 cfg->oldest_service_failure = (m * d);
4347 }
4348
4349 static void dump_icap_service_failure_limit(StoreEntry *entry, const char *name, const Adaptation::Icap::Config &cfg)
4350 {
4351 storeAppendPrintf(entry, "%s %d", name, cfg.service_failure_limit);
4352 if (cfg.oldest_service_failure > 0) {
4353 storeAppendPrintf(entry, " in %d seconds", (int)cfg.oldest_service_failure);
4354 }
4355 storeAppendPrintf(entry, "\n");
4356 }
4357
4358 static void free_icap_service_failure_limit(Adaptation::Icap::Config *cfg)
4359 {
4360 cfg->oldest_service_failure = 0;
4361 cfg->service_failure_limit = 0;
4362 }
4363 #endif
4364
4365 #if USE_SSL
4366 static void parse_sslproxy_cert_adapt(sslproxy_cert_adapt **cert_adapt)
4367 {
4368 char *al;
4369 sslproxy_cert_adapt *ca = (sslproxy_cert_adapt *) xcalloc(1, sizeof(sslproxy_cert_adapt));
4370 if ((al = strtok(NULL, w_space)) == NULL) {
4371 self_destruct();
4372 return;
4373 }
4374
4375 const char *param;
4376 if ( char *s = strchr(al, '{')) {
4377 *s = '\0'; // terminate the al string
4378 ++s;
4379 param = s;
4380 s = strchr(s, '}');
4381 if (!s) {
4382 self_destruct();
4383 return;
4384 }
4385 *s = '\0';
4386 } else
4387 param = NULL;
4388
4389 if (strcmp(al, Ssl::CertAdaptAlgorithmStr[Ssl::algSetValidAfter]) == 0) {
4390 ca->alg = Ssl::algSetValidAfter;
4391 ca->param = strdup("on");
4392 } else if (strcmp(al, Ssl::CertAdaptAlgorithmStr[Ssl::algSetValidBefore]) == 0) {
4393 ca->alg = Ssl::algSetValidBefore;
4394 ca->param = strdup("on");
4395 } else if (strcmp(al, Ssl::CertAdaptAlgorithmStr[Ssl::algSetCommonName]) == 0) {
4396 ca->alg = Ssl::algSetCommonName;
4397 if (param) {
4398 if (strlen(param) > 64) {
4399 debugs(3, DBG_CRITICAL, "FATAL: sslproxy_cert_adapt: setCommonName{" <<param << "} : using common name longer than 64 bytes is not supported");
4400 self_destruct();
4401 return;
4402 }
4403 ca->param = strdup(param);
4404 }
4405 } else {
4406 debugs(3, DBG_CRITICAL, "FATAL: sslproxy_cert_adapt: unknown cert adaptation algorithm: " << al);
4407 self_destruct();
4408 return;
4409 }
4410
4411 aclParseAclList(LegacyParser, &ca->aclList);
4412
4413 while (*cert_adapt)
4414 cert_adapt = &(*cert_adapt)->next;
4415
4416 *cert_adapt = ca;
4417 }
4418
4419 static void dump_sslproxy_cert_adapt(StoreEntry *entry, const char *name, sslproxy_cert_adapt *cert_adapt)
4420 {
4421 for (sslproxy_cert_adapt *ca = cert_adapt; ca != NULL; ca = ca->next) {
4422 storeAppendPrintf(entry, "%s ", name);
4423 storeAppendPrintf(entry, "%s{%s} ", Ssl::sslCertAdaptAlgoritm(ca->alg), ca->param);
4424 if (ca->aclList)
4425 dump_acl_list(entry, ca->aclList);
4426 storeAppendPrintf(entry, "\n");
4427 }
4428 }
4429
4430 static void free_sslproxy_cert_adapt(sslproxy_cert_adapt **cert_adapt)
4431 {
4432 while (*cert_adapt) {
4433 sslproxy_cert_adapt *ca = *cert_adapt;
4434 *cert_adapt = ca->next;
4435 safe_free(ca->param);
4436
4437 if (ca->aclList)
4438 aclDestroyAclList(&ca->aclList);
4439
4440 safe_free(ca);
4441 }
4442 }
4443
4444 static void parse_sslproxy_cert_sign(sslproxy_cert_sign **cert_sign)
4445 {
4446 char *al;
4447 sslproxy_cert_sign *cs = (sslproxy_cert_sign *) xcalloc(1, sizeof(sslproxy_cert_sign));
4448 if ((al = strtok(NULL, w_space)) == NULL) {
4449 self_destruct();
4450 return;
4451 }
4452
4453 if (strcmp(al, Ssl::CertSignAlgorithmStr[Ssl::algSignTrusted]) == 0)
4454 cs->alg = Ssl::algSignTrusted;
4455 else if (strcmp(al, Ssl::CertSignAlgorithmStr[Ssl::algSignUntrusted]) == 0)
4456 cs->alg = Ssl::algSignUntrusted;
4457 else if (strcmp(al, Ssl::CertSignAlgorithmStr[Ssl::algSignSelf]) == 0)
4458 cs->alg = Ssl::algSignSelf;
4459 else {
4460 debugs(3, DBG_CRITICAL, "FATAL: sslproxy_cert_sign: unknown cert signing algorithm: " << al);
4461 self_destruct();
4462 return;
4463 }
4464
4465 aclParseAclList(LegacyParser, &cs->aclList);
4466
4467 while (*cert_sign)
4468 cert_sign = &(*cert_sign)->next;
4469
4470 *cert_sign = cs;
4471 }
4472
4473 static void dump_sslproxy_cert_sign(StoreEntry *entry, const char *name, sslproxy_cert_sign *cert_sign)
4474 {
4475 sslproxy_cert_sign *cs;
4476 for (cs = cert_sign; cs != NULL; cs = cs->next) {
4477 storeAppendPrintf(entry, "%s ", name);
4478 storeAppendPrintf(entry, "%s ", Ssl::certSignAlgorithm(cs->alg));
4479 if (cs->aclList)
4480 dump_acl_list(entry, cs->aclList);
4481 storeAppendPrintf(entry, "\n");
4482 }
4483 }
4484
4485 static void free_sslproxy_cert_sign(sslproxy_cert_sign **cert_sign)
4486 {
4487 while (*cert_sign) {
4488 sslproxy_cert_sign *cs = *cert_sign;
4489 *cert_sign = cs->next;
4490
4491 if (cs->aclList)
4492 aclDestroyAclList(&cs->aclList);
4493
4494 safe_free(cs);
4495 }
4496 }
4497
4498 class sslBumpCfgRr: public ::RegisteredRunner
4499 {
4500 public:
4501 static Ssl::BumpMode lastDeprecatedRule;
4502 /* RegisteredRunner API */
4503 virtual void run(const RunnerRegistry &);
4504 };
4505
4506 Ssl::BumpMode sslBumpCfgRr::lastDeprecatedRule = Ssl::bumpEnd;
4507
4508 RunnerRegistrationEntry(rrFinalizeConfig, sslBumpCfgRr);
4509
4510 void sslBumpCfgRr::run(const RunnerRegistry &r)
4511 {
4512 if (lastDeprecatedRule != Ssl::bumpEnd) {
4513 assert( lastDeprecatedRule == Ssl::bumpClientFirst || lastDeprecatedRule == Ssl::bumpNone);
4514 static char buf[1024];
4515 if (lastDeprecatedRule == Ssl::bumpClientFirst) {
4516 strcpy(buf, "ssl_bump deny all");
4517 debugs(3, DBG_CRITICAL, "WARNING: auto-converting deprecated implicit "
4518 "\"ssl_bump deny all\" to \"ssl_bump none all\". New ssl_bump configurations "
4519 "must not use implicit rules. Update your ssl_bump rules.");
4520 } else {
4521 strcpy(buf, "ssl_bump allow all");
4522 debugs(3, DBG_CRITICAL, "SECURITY NOTICE: auto-converting deprecated implicit "
4523 "\"ssl_bump allow all\" to \"ssl_bump client-first all\" which is usually "
4524 "inferior to the newer server-first bumping mode. New ssl_bump"
4525 " configurations must not use implicit rules. Update your ssl_bump rules.");
4526 }
4527 parse_line(buf);
4528 }
4529 }
4530
4531 static void parse_sslproxy_ssl_bump(acl_access **ssl_bump)
4532 {
4533 typedef const char *BumpCfgStyle;
4534 BumpCfgStyle bcsNone = NULL;
4535 BumpCfgStyle bcsNew = "new client/server-first/none";
4536 BumpCfgStyle bcsOld = "deprecated allow/deny";
4537 static BumpCfgStyle bumpCfgStyleLast = bcsNone;
4538 BumpCfgStyle bumpCfgStyleNow = bcsNone;
4539 char *bm;
4540 if ((bm = strtok(NULL, w_space)) == NULL) {
4541 self_destruct();
4542 return;
4543 }
4544
4545 // if this is the first rule proccessed
4546 if (*ssl_bump == NULL) {
4547 bumpCfgStyleLast = bcsNone;
4548 sslBumpCfgRr::lastDeprecatedRule = Ssl::bumpEnd;
4549 }
4550
4551 acl_access *A = new acl_access;
4552 A->allow = allow_t(ACCESS_ALLOWED);
4553
4554 if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpClientFirst]) == 0) {
4555 A->allow.kind = Ssl::bumpClientFirst;
4556 bumpCfgStyleNow = bcsNew;
4557 } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpServerFirst]) == 0) {
4558 A->allow.kind = Ssl::bumpServerFirst;
4559 bumpCfgStyleNow = bcsNew;
4560 } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpNone]) == 0) {
4561 A->allow.kind = Ssl::bumpNone;
4562 bumpCfgStyleNow = bcsNew;
4563 } else if (strcmp(bm, "allow") == 0) {
4564 debugs(3, DBG_CRITICAL, "SECURITY NOTICE: auto-converting deprecated "
4565 "\"ssl_bump allow <acl>\" to \"ssl_bump client-first <acl>\" which "
4566 "is usually inferior to the newer server-first "
4567 "bumping mode. Update your ssl_bump rules.");
4568 A->allow.kind = Ssl::bumpClientFirst;
4569 bumpCfgStyleNow = bcsOld;
4570 sslBumpCfgRr::lastDeprecatedRule = Ssl::bumpClientFirst;
4571 } else if (strcmp(bm, "deny") == 0) {
4572 debugs(3, DBG_CRITICAL, "WARNING: auto-converting deprecated "
4573 "\"ssl_bump deny <acl>\" to \"ssl_bump none <acl>\". Update "
4574 "your ssl_bump rules.");
4575 A->allow.kind = Ssl::bumpNone;
4576 bumpCfgStyleNow = bcsOld;
4577 sslBumpCfgRr::lastDeprecatedRule = Ssl::bumpNone;
4578 } else {
4579 debugs(3, DBG_CRITICAL, "FATAL: unknown ssl_bump mode: " << bm);
4580 self_destruct();
4581 return;
4582 }
4583
4584 if (bumpCfgStyleLast != bcsNone && bumpCfgStyleNow != bumpCfgStyleLast) {
4585 debugs(3, DBG_CRITICAL, "FATAL: do not mix " << bumpCfgStyleNow << " actions with " <<
4586 bumpCfgStyleLast << " actions. Update your ssl_bump rules.");
4587 self_destruct();
4588 return;
4589 }
4590
4591 bumpCfgStyleLast = bumpCfgStyleNow;
4592
4593 aclParseAclList(LegacyParser, &A->aclList);
4594
4595 acl_access *B, **T;
4596 for (B = *ssl_bump, T = ssl_bump; B; T = &B->next, B = B->next);
4597 *T = A;
4598 }
4599
4600 static void dump_sslproxy_ssl_bump(StoreEntry *entry, const char *name, acl_access *ssl_bump)
4601 {
4602 acl_access *sb;
4603 for (sb = ssl_bump; sb != NULL; sb = sb->next) {
4604 storeAppendPrintf(entry, "%s ", name);
4605 storeAppendPrintf(entry, "%s ", Ssl::bumpMode(sb->allow.kind));
4606 if (sb->aclList)
4607 dump_acl_list(entry, sb->aclList);
4608 storeAppendPrintf(entry, "\n");
4609 }
4610 }
4611
4612 static void free_sslproxy_ssl_bump(acl_access **ssl_bump)
4613 {
4614 free_acl_access(ssl_bump);
4615 }
4616
4617 #endif
4618
4619 static void dump_HeaderWithAclList(StoreEntry * entry, const char *name, HeaderWithAclList *headers)
4620 {
4621 if (!headers)
4622 return;
4623
4624 for (HeaderWithAclList::iterator hwa = headers->begin(); hwa != headers->end(); ++hwa) {
4625 storeAppendPrintf(entry, "%s ", hwa->fieldName.c_str());
4626 storeAppendPrintf(entry, "%s ", hwa->fieldValue.c_str());
4627 if (hwa->aclList)
4628 dump_acl_list(entry, hwa->aclList);
4629 storeAppendPrintf(entry, "\n");
4630 }
4631 }
4632
4633 static void parse_HeaderWithAclList(HeaderWithAclList **headers)
4634 {
4635 char *fn;
4636 if (!*headers) {
4637 *headers = new HeaderWithAclList;
4638 }
4639 if ((fn = strtok(NULL, w_space)) == NULL) {
4640 self_destruct();
4641 return;
4642 }
4643 HeaderWithAcl hwa;
4644 hwa.fieldName = fn;
4645 hwa.fieldId = httpHeaderIdByNameDef(fn, strlen(fn));
4646 if (hwa.fieldId == HDR_BAD_HDR)
4647 hwa.fieldId = HDR_OTHER;
4648
4649 String buf;
4650 bool wasQuoted;
4651 ConfigParser::ParseQuotedString(&buf, &wasQuoted);
4652 hwa.fieldValue = buf.termedBuf();
4653 hwa.quoted = wasQuoted;
4654 if (hwa.quoted) {
4655 Format::Format *nlf = new ::Format::Format("hdrWithAcl");
4656 if (!nlf->parse(hwa.fieldValue.c_str())) {
4657 self_destruct();
4658 return;
4659 }
4660 hwa.valueFormat = nlf;
4661 }
4662 aclParseAclList(LegacyParser, &hwa.aclList);
4663 (*headers)->push_back(hwa);
4664 }
4665
4666 static void free_HeaderWithAclList(HeaderWithAclList **header)
4667 {
4668 if (!(*header))
4669 return;
4670
4671 for (HeaderWithAclList::iterator hwa = (*header)->begin(); hwa != (*header)->end(); ++hwa) {
4672 if (hwa->aclList)
4673 aclDestroyAclList(&hwa->aclList);
4674
4675 if (hwa->valueFormat) {
4676 delete hwa->valueFormat;
4677 hwa->valueFormat = NULL;
4678 }
4679 }
4680 delete *header;
4681 *header = NULL;
4682 }