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