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