/* * Configuration parser * * Copyright 2000-2011 Willy Tarreau * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * */ #ifdef CONFIG_HAP_CRYPT /* This is to have crypt() defined on Linux */ #define _GNU_SOURCE #ifdef NEED_CRYPT_H /* some platforms such as Solaris need this */ #include #endif #endif /* CONFIG_HAP_CRYPT */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the * ssl-hello-chk option to ensure that the remote server speaks SSL. * * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details. */ const char sslv3_client_hello_pkt[] = { "\x16" /* ContentType : 0x16 = Hanshake */ "\x03\x00" /* ProtocolVersion : 0x0300 = SSLv3 */ "\x00\x79" /* ContentLength : 0x79 bytes after this one */ "\x01" /* HanshakeType : 0x01 = CLIENT HELLO */ "\x00\x00\x75" /* HandshakeLength : 0x75 bytes after this one */ "\x03\x00" /* Hello Version : 0x0300 = v3 */ "\x00\x00\x00\x00" /* Unix GMT Time (s) : filled with (@0x0B) */ "HAPROXYSSLCHK\nHAPROXYSSLCHK\n" /* Random : must be exactly 28 bytes */ "\x00" /* Session ID length : empty (no session ID) */ "\x00\x4E" /* Cipher Suite Length : 78 bytes after this one */ "\x00\x01" "\x00\x02" "\x00\x03" "\x00\x04" /* 39 most common ciphers : */ "\x00\x05" "\x00\x06" "\x00\x07" "\x00\x08" /* 0x01...0x1B, 0x2F...0x3A */ "\x00\x09" "\x00\x0A" "\x00\x0B" "\x00\x0C" /* This covers RSA/DH, */ "\x00\x0D" "\x00\x0E" "\x00\x0F" "\x00\x10" /* various bit lengths, */ "\x00\x11" "\x00\x12" "\x00\x13" "\x00\x14" /* SHA1/MD5, DES/3DES/AES... */ "\x00\x15" "\x00\x16" "\x00\x17" "\x00\x18" "\x00\x19" "\x00\x1A" "\x00\x1B" "\x00\x2F" "\x00\x30" "\x00\x31" "\x00\x32" "\x00\x33" "\x00\x34" "\x00\x35" "\x00\x36" "\x00\x37" "\x00\x38" "\x00\x39" "\x00\x3A" "\x01" /* Compression Length : 0x01 = 1 byte for types */ "\x00" /* Compression Type : 0x00 = NULL compression */ }; /* various keyword modifiers */ enum kw_mod { KWM_STD = 0, /* normal */ KWM_NO, /* "no" prefixed before the keyword */ KWM_DEF, /* "default" prefixed before the keyword */ }; /* permit to store configuration section */ struct cfg_section { struct list list; char *section_name; int (*section_parser)(const char *, int, char **, int); }; /* Used to chain configuration sections definitions. This list * stores struct cfg_section */ struct list sections = LIST_HEAD_INIT(sections); /* some of the most common options which are also the easiest to handle */ struct cfg_opt { const char *name; unsigned int val; unsigned int cap; unsigned int checks; unsigned int mode; }; /* proxy->options */ static const struct cfg_opt cfg_opts[] = { { "abortonclose", PR_O_ABRT_CLOSE, PR_CAP_BE, 0, 0 }, { "allbackups", PR_O_USE_ALL_BK, PR_CAP_BE, 0, 0 }, { "checkcache", PR_O_CHK_CACHE, PR_CAP_BE, 0, PR_MODE_HTTP }, { "clitcpka", PR_O_TCP_CLI_KA, PR_CAP_FE, 0, 0 }, { "contstats", PR_O_CONTSTATS, PR_CAP_FE, 0, 0 }, { "dontlognull", PR_O_NULLNOLOG, PR_CAP_FE, 0, 0 }, { "http_proxy", PR_O_HTTP_PROXY, PR_CAP_FE | PR_CAP_BE, 0, PR_MODE_HTTP }, { "http-buffer-request", PR_O_WREQ_BODY, PR_CAP_FE | PR_CAP_BE, 0, PR_MODE_HTTP }, { "http-ignore-probes", PR_O_IGNORE_PRB, PR_CAP_FE, 0, PR_MODE_HTTP }, { "prefer-last-server", PR_O_PREF_LAST, PR_CAP_BE, 0, PR_MODE_HTTP }, { "logasap", PR_O_LOGASAP, PR_CAP_FE, 0, 0 }, { "nolinger", PR_O_TCP_NOLING, PR_CAP_FE | PR_CAP_BE, 0, 0 }, { "persist", PR_O_PERSIST, PR_CAP_BE, 0, 0 }, { "srvtcpka", PR_O_TCP_SRV_KA, PR_CAP_BE, 0, 0 }, #ifdef TPROXY { "transparent", PR_O_TRANSP, PR_CAP_BE, 0, 0 }, #else { "transparent", 0, 0, 0, 0 }, #endif { NULL, 0, 0, 0, 0 } }; /* proxy->options2 */ static const struct cfg_opt cfg_opts2[] = { #ifdef CONFIG_HAP_LINUX_SPLICE { "splice-request", PR_O2_SPLIC_REQ, PR_CAP_FE|PR_CAP_BE, 0, 0 }, { "splice-response", PR_O2_SPLIC_RTR, PR_CAP_FE|PR_CAP_BE, 0, 0 }, { "splice-auto", PR_O2_SPLIC_AUT, PR_CAP_FE|PR_CAP_BE, 0, 0 }, #else { "splice-request", 0, 0, 0, 0 }, { "splice-response", 0, 0, 0, 0 }, { "splice-auto", 0, 0, 0, 0 }, #endif { "accept-invalid-http-request", PR_O2_REQBUG_OK, PR_CAP_FE, 0, PR_MODE_HTTP }, { "accept-invalid-http-response", PR_O2_RSPBUG_OK, PR_CAP_BE, 0, PR_MODE_HTTP }, { "dontlog-normal", PR_O2_NOLOGNORM, PR_CAP_FE, 0, 0 }, { "log-separate-errors", PR_O2_LOGERRORS, PR_CAP_FE, 0, 0 }, { "log-health-checks", PR_O2_LOGHCHKS, PR_CAP_BE, 0, 0 }, { "socket-stats", PR_O2_SOCKSTAT, PR_CAP_FE, 0, 0 }, { "tcp-smart-accept", PR_O2_SMARTACC, PR_CAP_FE, 0, 0 }, { "tcp-smart-connect", PR_O2_SMARTCON, PR_CAP_BE, 0, 0 }, { "independant-streams", PR_O2_INDEPSTR, PR_CAP_FE|PR_CAP_BE, 0, 0 }, { "independent-streams", PR_O2_INDEPSTR, PR_CAP_FE|PR_CAP_BE, 0, 0 }, { "http-use-proxy-header", PR_O2_USE_PXHDR, PR_CAP_FE, 0, PR_MODE_HTTP }, { "http-pretend-keepalive", PR_O2_FAKE_KA, PR_CAP_FE|PR_CAP_BE, 0, PR_MODE_HTTP }, { "http-no-delay", PR_O2_NODELAY, PR_CAP_FE|PR_CAP_BE, 0, PR_MODE_HTTP }, { NULL, 0, 0, 0 } }; static char *cursection = NULL; static struct proxy defproxy; /* fake proxy used to assign default values on all instances */ int cfg_maxpconn = DEFAULT_MAXCONN; /* # of simultaneous connections per proxy (-N) */ int cfg_maxconn = 0; /* # of simultaneous connections, (-n) */ char *cfg_scope = NULL; /* the current scope during the configuration parsing */ /* List head of all known configuration keywords */ static struct cfg_kw_list cfg_keywords = { .list = LIST_HEAD_INIT(cfg_keywords.list) }; /* * converts to a list of listeners which are dynamically allocated. * The format is "{addr|'*'}:port[-end][,{addr|'*'}:port[-end]]*", where : * - can be empty or "*" to indicate INADDR_ANY ; * - is a numerical port from 1 to 65535 ; * - indicates to use the range from to instead (inclusive). * This can be repeated as many times as necessary, separated by a coma. * Function returns 1 for success or 0 if error. In case of errors, if is * not NULL, it must be a valid pointer to either NULL or a freeable area that * will be replaced with an error message. */ int str2listener(char *str, struct proxy *curproxy, struct bind_conf *bind_conf, const char *file, int line, char **err) { struct listener *l; char *next, *dupstr; int port, end; next = dupstr = strdup(str); while (next && *next) { struct sockaddr_storage ss, *ss2; int fd = -1; str = next; /* 1) look for the end of the first address */ if ((next = strchr(str, ',')) != NULL) { *next++ = 0; } ss2 = str2sa_range(str, NULL, &port, &end, err, curproxy == global.stats_fe ? NULL : global.unix_bind.prefix, NULL, 1); if (!ss2) goto fail; if (ss2->ss_family == AF_INET || ss2->ss_family == AF_INET6) { if (!port && !end) { memprintf(err, "missing port number: '%s'\n", str); goto fail; } if (!port || !end) { memprintf(err, "port offsets are not allowed in 'bind': '%s'\n", str); goto fail; } if (port < 1 || port > 65535) { memprintf(err, "invalid port '%d' specified for address '%s'.\n", port, str); goto fail; } if (end < 1 || end > 65535) { memprintf(err, "invalid port '%d' specified for address '%s'.\n", end, str); goto fail; } } else if (ss2->ss_family == AF_UNSPEC) { socklen_t addr_len; /* We want to attach to an already bound fd whose number * is in the addr part of ss2 when cast to sockaddr_in. * Note that by definition there is a single listener. * We still have to determine the address family to * register the correct protocol. */ fd = ((struct sockaddr_in *)ss2)->sin_addr.s_addr; addr_len = sizeof(*ss2); if (getsockname(fd, (struct sockaddr *)ss2, &addr_len) == -1) { memprintf(err, "cannot use file descriptor '%d' : %s.\n", fd, strerror(errno)); goto fail; } port = end = get_host_port(ss2); } /* OK the address looks correct */ memcpy(&ss, ss2, sizeof(ss)); for (; port <= end; port++) { l = calloc(1, sizeof(*l)); l->obj_type = OBJ_TYPE_LISTENER; LIST_ADDQ(&curproxy->conf.listeners, &l->by_fe); LIST_ADDQ(&bind_conf->listeners, &l->by_bind); l->bind_conf = bind_conf; l->fd = fd; memcpy(&l->addr, &ss, sizeof(ss)); l->state = LI_INIT; if (ss.ss_family == AF_INET) { ((struct sockaddr_in *)(&l->addr))->sin_port = htons(port); tcpv4_add_listener(l); } else if (ss.ss_family == AF_INET6) { ((struct sockaddr_in6 *)(&l->addr))->sin6_port = htons(port); tcpv6_add_listener(l); } else { uxst_add_listener(l); } jobs++; listeners++; } /* end for(port) */ } /* end while(next) */ free(dupstr); return 1; fail: free(dupstr); return 0; } /* * Report an error in when there are too many arguments. This version is * intended to be used by keyword parsers so that the message will be included * into the general error message. The index is the current keyword in args. * Return 0 if the number of argument is correct, otherwise build a message and * return 1. Fill err_code with an ERR_ALERT and an ERR_FATAL if not null. The * message may also be null, it will simply not be produced (useful to check only). * and are only affected on error. */ int too_many_args_idx(int maxarg, int index, char **args, char **msg, int *err_code) { int i; if (!*args[index + maxarg + 1]) return 0; if (msg) { *msg = NULL; memprintf(msg, "%s", args[0]); for (i = 1; i <= index; i++) memprintf(msg, "%s %s", *msg, args[i]); memprintf(msg, "'%s' cannot handle unexpected argument '%s'.", *msg, args[index + maxarg + 1]); } if (err_code) *err_code |= ERR_ALERT | ERR_FATAL; return 1; } /* * same as too_many_args_idx with a 0 index */ int too_many_args(int maxarg, char **args, char **msg, int *err_code) { return too_many_args_idx(maxarg, 0, args, msg, err_code); } /* * Report a fatal Alert when there is too much arguments * The index is the current keyword in args * Return 0 if the number of argument is correct, otherwise emit an alert and return 1 * Fill err_code with an ERR_ALERT and an ERR_FATAL */ int alertif_too_many_args_idx(int maxarg, int index, const char *file, int linenum, char **args, int *err_code) { char *kw = NULL; int i; if (!*args[index + maxarg + 1]) return 0; memprintf(&kw, "%s", args[0]); for (i = 1; i <= index; i++) { memprintf(&kw, "%s %s", kw, args[i]); } Alert("parsing [%s:%d] : '%s' cannot handle unexpected argument '%s'.\n", file, linenum, kw, args[index + maxarg + 1]); free(kw); *err_code |= ERR_ALERT | ERR_FATAL; return 1; } /* * same as alertif_too_many_args_idx with a 0 index */ int alertif_too_many_args(int maxarg, const char *file, int linenum, char **args, int *err_code) { return alertif_too_many_args_idx(maxarg, 0, file, linenum, args, err_code); } /* Report a warning if a rule is placed after a 'tcp-request session' rule. * Return 1 if the warning has been emitted, otherwise 0. */ int warnif_rule_after_tcp_sess(struct proxy *proxy, const char *file, int line, const char *arg) { if (!LIST_ISEMPTY(&proxy->tcp_req.l5_rules)) { Warning("parsing [%s:%d] : a '%s' rule placed after a 'tcp-request session' rule will still be processed before.\n", file, line, arg); return 1; } return 0; } /* Report a warning if a rule is placed after a 'tcp-request content' rule. * Return 1 if the warning has been emitted, otherwise 0. */ int warnif_rule_after_tcp_cont(struct proxy *proxy, const char *file, int line, const char *arg) { if (!LIST_ISEMPTY(&proxy->tcp_req.inspect_rules)) { Warning("parsing [%s:%d] : a '%s' rule placed after a 'tcp-request content' rule will still be processed before.\n", file, line, arg); return 1; } return 0; } /* Report a warning if a rule is placed after a 'block' rule. * Return 1 if the warning has been emitted, otherwise 0. */ int warnif_rule_after_block(struct proxy *proxy, const char *file, int line, const char *arg) { if (!LIST_ISEMPTY(&proxy->block_rules)) { Warning("parsing [%s:%d] : a '%s' rule placed after a 'block' rule will still be processed before.\n", file, line, arg); return 1; } return 0; } /* Report a warning if a rule is placed after an 'http_request' rule. * Return 1 if the warning has been emitted, otherwise 0. */ int warnif_rule_after_http_req(struct proxy *proxy, const char *file, int line, const char *arg) { if (!LIST_ISEMPTY(&proxy->http_req_rules)) { Warning("parsing [%s:%d] : a '%s' rule placed after an 'http-request' rule will still be processed before.\n", file, line, arg); return 1; } return 0; } /* Report a warning if a rule is placed after a reqrewrite rule. * Return 1 if the warning has been emitted, otherwise 0. */ int warnif_rule_after_reqxxx(struct proxy *proxy, const char *file, int line, const char *arg) { if (proxy->req_exp) { Warning("parsing [%s:%d] : a '%s' rule placed after a 'reqxxx' rule will still be processed before.\n", file, line, arg); return 1; } return 0; } /* Report a warning if a rule is placed after a reqadd rule. * Return 1 if the warning has been emitted, otherwise 0. */ int warnif_rule_after_reqadd(struct proxy *proxy, const char *file, int line, const char *arg) { if (!LIST_ISEMPTY(&proxy->req_add)) { Warning("parsing [%s:%d] : a '%s' rule placed after a 'reqadd' rule will still be processed before.\n", file, line, arg); return 1; } return 0; } /* Report a warning if a rule is placed after a redirect rule. * Return 1 if the warning has been emitted, otherwise 0. */ int warnif_rule_after_redirect(struct proxy *proxy, const char *file, int line, const char *arg) { if (!LIST_ISEMPTY(&proxy->redirect_rules)) { Warning("parsing [%s:%d] : a '%s' rule placed after a 'redirect' rule will still be processed before.\n", file, line, arg); return 1; } return 0; } /* Report a warning if a rule is placed after a 'use_backend' rule. * Return 1 if the warning has been emitted, otherwise 0. */ int warnif_rule_after_use_backend(struct proxy *proxy, const char *file, int line, const char *arg) { if (!LIST_ISEMPTY(&proxy->switching_rules)) { Warning("parsing [%s:%d] : a '%s' rule placed after a 'use_backend' rule will still be processed before.\n", file, line, arg); return 1; } return 0; } /* Report a warning if a rule is placed after a 'use-server' rule. * Return 1 if the warning has been emitted, otherwise 0. */ int warnif_rule_after_use_server(struct proxy *proxy, const char *file, int line, const char *arg) { if (!LIST_ISEMPTY(&proxy->server_rules)) { Warning("parsing [%s:%d] : a '%s' rule placed after a 'use-server' rule will still be processed before.\n", file, line, arg); return 1; } return 0; } /* report a warning if a redirect rule is dangerously placed */ int warnif_misplaced_redirect(struct proxy *proxy, const char *file, int line, const char *arg) { return warnif_rule_after_use_backend(proxy, file, line, arg) || warnif_rule_after_use_server(proxy, file, line, arg); } /* report a warning if a reqadd rule is dangerously placed */ int warnif_misplaced_reqadd(struct proxy *proxy, const char *file, int line, const char *arg) { return warnif_rule_after_redirect(proxy, file, line, arg) || warnif_misplaced_redirect(proxy, file, line, arg); } /* report a warning if a reqxxx rule is dangerously placed */ int warnif_misplaced_reqxxx(struct proxy *proxy, const char *file, int line, const char *arg) { return warnif_rule_after_reqadd(proxy, file, line, arg) || warnif_misplaced_reqadd(proxy, file, line, arg); } /* report a warning if an http-request rule is dangerously placed */ int warnif_misplaced_http_req(struct proxy *proxy, const char *file, int line, const char *arg) { return warnif_rule_after_reqxxx(proxy, file, line, arg) || warnif_misplaced_reqxxx(proxy, file, line, arg);; } /* report a warning if a block rule is dangerously placed */ int warnif_misplaced_block(struct proxy *proxy, const char *file, int line, const char *arg) { return warnif_rule_after_http_req(proxy, file, line, arg) || warnif_misplaced_http_req(proxy, file, line, arg); } /* report a warning if a "tcp request content" rule is dangerously placed */ int warnif_misplaced_tcp_cont(struct proxy *proxy, const char *file, int line, const char *arg) { return warnif_rule_after_block(proxy, file, line, arg) || warnif_misplaced_block(proxy, file, line, arg); } /* report a warning if a "tcp request session" rule is dangerously placed */ int warnif_misplaced_tcp_sess(struct proxy *proxy, const char *file, int line, const char *arg) { return warnif_rule_after_tcp_cont(proxy, file, line, arg) || warnif_misplaced_tcp_cont(proxy, file, line, arg); } /* report a warning if a "tcp request connection" rule is dangerously placed */ int warnif_misplaced_tcp_conn(struct proxy *proxy, const char *file, int line, const char *arg) { return warnif_rule_after_tcp_sess(proxy, file, line, arg) || warnif_misplaced_tcp_sess(proxy, file, line, arg); } /* Report it if a request ACL condition uses some keywords that are incompatible * with the place where the ACL is used. It returns either 0 or ERR_WARN so that * its result can be or'ed with err_code. Note that may be NULL and then * will be ignored. */ static int warnif_cond_conflicts(const struct acl_cond *cond, unsigned int where, const char *file, int line) { const struct acl *acl; const char *kw; if (!cond) return 0; acl = acl_cond_conflicts(cond, where); if (acl) { if (acl->name && *acl->name) Warning("parsing [%s:%d] : acl '%s' will never match because it only involves keywords that are incompatible with '%s'\n", file, line, acl->name, sample_ckp_names(where)); else Warning("parsing [%s:%d] : anonymous acl will never match because it uses keyword '%s' which is incompatible with '%s'\n", file, line, LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw, sample_ckp_names(where)); return ERR_WARN; } if (!acl_cond_kw_conflicts(cond, where, &acl, &kw)) return 0; if (acl->name && *acl->name) Warning("parsing [%s:%d] : acl '%s' involves keywords '%s' which is incompatible with '%s'\n", file, line, acl->name, kw, sample_ckp_names(where)); else Warning("parsing [%s:%d] : anonymous acl involves keyword '%s' which is incompatible with '%s'\n", file, line, kw, sample_ckp_names(where)); return ERR_WARN; } /* * parse a line in a section. Returns the error code, 0 if OK, or * any combination of : * - ERR_ABORT: must abort ASAP * - ERR_FATAL: we can continue parsing but not start the service * - ERR_WARN: a warning has been emitted * - ERR_ALERT: an alert has been emitted * Only the two first ones can stop processing, the two others are just * indicators. */ int cfg_parse_global(const char *file, int linenum, char **args, int kwm) { int err_code = 0; char *errmsg = NULL; if (!strcmp(args[0], "global")) { /* new section */ /* no option, nothing special to do */ alertif_too_many_args(0, file, linenum, args, &err_code); goto out; } else if (!strcmp(args[0], "daemon")) { if (alertif_too_many_args(0, file, linenum, args, &err_code)) goto out; global.mode |= MODE_DAEMON; } else if (!strcmp(args[0], "master-worker")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (*args[1]) { if (!strcmp(args[1], "exit-on-failure")) { global.tune.options |= GTUNE_EXIT_ONFAILURE; } else { Alert("parsing [%s:%d] : '%s' only supports 'exit-on-failure' option.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } } global.mode |= MODE_MWORKER; } else if (!strcmp(args[0], "debug")) { if (alertif_too_many_args(0, file, linenum, args, &err_code)) goto out; global.mode |= MODE_DEBUG; } else if (!strcmp(args[0], "noepoll")) { if (alertif_too_many_args(0, file, linenum, args, &err_code)) goto out; global.tune.options &= ~GTUNE_USE_EPOLL; } else if (!strcmp(args[0], "nokqueue")) { if (alertif_too_many_args(0, file, linenum, args, &err_code)) goto out; global.tune.options &= ~GTUNE_USE_KQUEUE; } else if (!strcmp(args[0], "nopoll")) { if (alertif_too_many_args(0, file, linenum, args, &err_code)) goto out; global.tune.options &= ~GTUNE_USE_POLL; } else if (!strcmp(args[0], "nosplice")) { if (alertif_too_many_args(0, file, linenum, args, &err_code)) goto out; global.tune.options &= ~GTUNE_USE_SPLICE; } else if (!strcmp(args[0], "nogetaddrinfo")) { if (alertif_too_many_args(0, file, linenum, args, &err_code)) goto out; global.tune.options &= ~GTUNE_USE_GAI; } else if (!strcmp(args[0], "noreuseport")) { if (alertif_too_many_args(0, file, linenum, args, &err_code)) goto out; global.tune.options &= ~GTUNE_USE_REUSEPORT; } else if (!strcmp(args[0], "quiet")) { if (alertif_too_many_args(0, file, linenum, args, &err_code)) goto out; global.mode |= MODE_QUIET; } else if (!strcmp(args[0], "tune.maxpollevents")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (global.tune.maxpollevents != 0) { Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); err_code |= ERR_ALERT; goto out; } if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.tune.maxpollevents = atol(args[1]); } else if (!strcmp(args[0], "tune.maxaccept")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (global.tune.maxaccept != 0) { Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); err_code |= ERR_ALERT; goto out; } if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.tune.maxaccept = atol(args[1]); } else if (!strcmp(args[0], "tune.chksize")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.tune.chksize = atol(args[1]); } else if (!strcmp(args[0], "tune.recv_enough")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.tune.recv_enough = atol(args[1]); } else if (!strcmp(args[0], "tune.buffers.limit")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.tune.buf_limit = atol(args[1]); if (global.tune.buf_limit) { if (global.tune.buf_limit < 3) global.tune.buf_limit = 3; if (global.tune.buf_limit <= global.tune.reserved_bufs) global.tune.buf_limit = global.tune.reserved_bufs + 1; } } else if (!strcmp(args[0], "tune.buffers.reserve")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.tune.reserved_bufs = atol(args[1]); if (global.tune.reserved_bufs < 2) global.tune.reserved_bufs = 2; if (global.tune.buf_limit && global.tune.buf_limit <= global.tune.reserved_bufs) global.tune.buf_limit = global.tune.reserved_bufs + 1; } else if (!strcmp(args[0], "tune.bufsize")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.tune.bufsize = atol(args[1]); if (global.tune.bufsize <= 0) { Alert("parsing [%s:%d] : '%s' expects a positive integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } chunk_init(&trash, realloc(trash.str, global.tune.bufsize), global.tune.bufsize); alloc_trash_buffers(global.tune.bufsize); } else if (!strcmp(args[0], "tune.maxrewrite")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.tune.maxrewrite = atol(args[1]); if (global.tune.maxrewrite < 0) { Alert("parsing [%s:%d] : '%s' expects a positive integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } } else if (!strcmp(args[0], "tune.idletimer")) { unsigned int idle; const char *res; if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects a timer value between 0 and 65535 ms.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } res = parse_time_err(args[1], &idle, TIME_UNIT_MS); if (res) { Alert("parsing [%s:%d]: unexpected character '%c' in argument to <%s>.\n", file, linenum, *res, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } if (idle > 65535) { Alert("parsing [%s:%d] : '%s' expects a timer value between 0 and 65535 ms.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.tune.idle_timer = idle; } else if (!strcmp(args[0], "tune.rcvbuf.client")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (global.tune.client_rcvbuf != 0) { Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); err_code |= ERR_ALERT; goto out; } if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.tune.client_rcvbuf = atol(args[1]); } else if (!strcmp(args[0], "tune.rcvbuf.server")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (global.tune.server_rcvbuf != 0) { Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); err_code |= ERR_ALERT; goto out; } if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.tune.server_rcvbuf = atol(args[1]); } else if (!strcmp(args[0], "tune.sndbuf.client")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (global.tune.client_sndbuf != 0) { Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); err_code |= ERR_ALERT; goto out; } if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.tune.client_sndbuf = atol(args[1]); } else if (!strcmp(args[0], "tune.sndbuf.server")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (global.tune.server_sndbuf != 0) { Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); err_code |= ERR_ALERT; goto out; } if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.tune.server_sndbuf = atol(args[1]); } else if (!strcmp(args[0], "tune.pipesize")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.tune.pipesize = atol(args[1]); } else if (!strcmp(args[0], "tune.http.cookielen")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.tune.cookie_len = atol(args[1]) + 1; } else if (!strcmp(args[0], "tune.http.logurilen")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.tune.requri_len = atol(args[1]) + 1; } else if (!strcmp(args[0], "tune.http.maxhdr")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.tune.max_http_hdr = atoi(args[1]); if (global.tune.max_http_hdr < 1 || global.tune.max_http_hdr > 32767) { Alert("parsing [%s:%d] : '%s' expects a numeric value between 1 and 32767\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } } else if (!strcmp(args[0], "tune.comp.maxlevel")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (*args[1]) { global.tune.comp_maxlevel = atoi(args[1]); if (global.tune.comp_maxlevel < 1 || global.tune.comp_maxlevel > 9) { Alert("parsing [%s:%d] : '%s' expects a numeric value between 1 and 9\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } } else { Alert("parsing [%s:%d] : '%s' expects a numeric value between 1 and 9\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } } else if (!strcmp(args[0], "tune.pattern.cache-size")) { if (*args[1]) { global.tune.pattern_cache = atoi(args[1]); if (global.tune.pattern_cache < 0) { Alert("parsing [%s:%d] : '%s' expects a positive numeric value\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } } else { Alert("parsing [%s:%d] : '%s' expects a positive numeric value\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } } else if (!strcmp(args[0], "uid")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (global.uid != 0) { Alert("parsing [%s:%d] : user/uid already specified. Continuing.\n", file, linenum); err_code |= ERR_ALERT; goto out; } if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } if (strl2irc(args[1], strlen(args[1]), &global.uid) != 0) { Warning("parsing [%s:%d] : uid: string '%s' is not a number.\n | You might want to use the 'user' parameter to use a system user name.\n", file, linenum, args[1]); err_code |= ERR_WARN; goto out; } } else if (!strcmp(args[0], "gid")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (global.gid != 0) { Alert("parsing [%s:%d] : group/gid already specified. Continuing.\n", file, linenum); err_code |= ERR_ALERT; goto out; } if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } if (strl2irc(args[1], strlen(args[1]), &global.gid) != 0) { Warning("parsing [%s:%d] : gid: string '%s' is not a number.\n | You might want to use the 'group' parameter to use a system group name.\n", file, linenum, args[1]); err_code |= ERR_WARN; goto out; } } else if (!strcmp(args[0], "external-check")) { if (alertif_too_many_args(0, file, linenum, args, &err_code)) goto out; global.external_check = 1; } /* user/group name handling */ else if (!strcmp(args[0], "user")) { struct passwd *ha_user; if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (global.uid != 0) { Alert("parsing [%s:%d] : user/uid already specified. Continuing.\n", file, linenum); err_code |= ERR_ALERT; goto out; } errno = 0; ha_user = getpwnam(args[1]); if (ha_user != NULL) { global.uid = (int)ha_user->pw_uid; } else { Alert("parsing [%s:%d] : cannot find user id for '%s' (%d:%s)\n", file, linenum, args[1], errno, strerror(errno)); err_code |= ERR_ALERT | ERR_FATAL; } } else if (!strcmp(args[0], "group")) { struct group *ha_group; if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (global.gid != 0) { Alert("parsing [%s:%d] : gid/group was already specified. Continuing.\n", file, linenum); err_code |= ERR_ALERT; goto out; } errno = 0; ha_group = getgrnam(args[1]); if (ha_group != NULL) { global.gid = (int)ha_group->gr_gid; } else { Alert("parsing [%s:%d] : cannot find group id for '%s' (%d:%s)\n", file, linenum, args[1], errno, strerror(errno)); err_code |= ERR_ALERT | ERR_FATAL; } } /* end of user/group name handling*/ else if (!strcmp(args[0], "nbproc")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.nbproc = atol(args[1]); if (global.nbproc < 1 || global.nbproc > LONGBITS) { Alert("parsing [%s:%d] : '%s' must be between 1 and %d (was %d).\n", file, linenum, args[0], LONGBITS, global.nbproc); err_code |= ERR_ALERT | ERR_FATAL; goto out; } } else if (!strcmp(args[0], "maxconn")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (global.maxconn != 0) { Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); err_code |= ERR_ALERT; goto out; } if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.maxconn = atol(args[1]); #ifdef SYSTEM_MAXCONN if (global.maxconn > DEFAULT_MAXCONN && cfg_maxconn <= DEFAULT_MAXCONN) { Alert("parsing [%s:%d] : maxconn value %d too high for this system.\nLimiting to %d. Please use '-n' to force the value.\n", file, linenum, global.maxconn, DEFAULT_MAXCONN); global.maxconn = DEFAULT_MAXCONN; err_code |= ERR_ALERT; } #endif /* SYSTEM_MAXCONN */ } else if (!strcmp(args[0], "ssl-server-verify")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } if (strcmp(args[1],"none") == 0) global.ssl_server_verify = SSL_SERVER_VERIFY_NONE; else if (strcmp(args[1],"required") == 0) global.ssl_server_verify = SSL_SERVER_VERIFY_REQUIRED; else { Alert("parsing [%s:%d] : '%s' expects 'none' or 'required' as argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } } else if (!strcmp(args[0], "maxconnrate")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (global.cps_lim != 0) { Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); err_code |= ERR_ALERT; goto out; } if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.cps_lim = atol(args[1]); } else if (!strcmp(args[0], "maxsessrate")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (global.sps_lim != 0) { Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); err_code |= ERR_ALERT; goto out; } if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.sps_lim = atol(args[1]); } else if (!strcmp(args[0], "maxsslrate")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (global.ssl_lim != 0) { Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); err_code |= ERR_ALERT; goto out; } if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.ssl_lim = atol(args[1]); } else if (!strcmp(args[0], "maxcomprate")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument in kb/s.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.comp_rate_lim = atoi(args[1]) * 1024; } else if (!strcmp(args[0], "maxpipes")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (global.maxpipes != 0) { Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); err_code |= ERR_ALERT; goto out; } if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.maxpipes = atol(args[1]); } else if (!strcmp(args[0], "maxzlibmem")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.maxzlibmem = atol(args[1]) * 1024L * 1024L; } else if (!strcmp(args[0], "maxcompcpuusage")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument between 0 and 100.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } compress_min_idle = 100 - atoi(args[1]); if (compress_min_idle > 100) { Alert("parsing [%s:%d] : '%s' expects an integer argument between 0 and 100.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } } else if (!strcmp(args[0], "ulimit-n")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (global.rlimit_nofile != 0) { Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); err_code |= ERR_ALERT; goto out; } if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.rlimit_nofile = atol(args[1]); } else if (!strcmp(args[0], "chroot")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (global.chroot != NULL) { Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); err_code |= ERR_ALERT; goto out; } if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects a directory as an argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.chroot = strdup(args[1]); } else if (!strcmp(args[0], "description")) { int i, len=0; char *d; if (!*args[1]) { Alert("parsing [%s:%d]: '%s' expects a string argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } for (i = 1; *args[i]; i++) len += strlen(args[i]) + 1; if (global.desc) free(global.desc); global.desc = d = calloc(1, len); d += snprintf(d, global.desc + len - d, "%s", args[1]); for (i = 2; *args[i]; i++) d += snprintf(d, global.desc + len - d, " %s", args[i]); } else if (!strcmp(args[0], "node")) { int i; char c; if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; for (i=0; args[1][i]; i++) { c = args[1][i]; if (!isupper((unsigned char)c) && !islower((unsigned char)c) && !isdigit((unsigned char)c) && c != '_' && c != '-' && c != '.') break; } if (!i || args[1][i]) { Alert("parsing [%s:%d]: '%s' requires valid node name - non-empty string" " with digits(0-9), letters(A-Z, a-z), dot(.), hyphen(-) or underscode(_).\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } if (global.node) free(global.node); global.node = strdup(args[1]); } else if (!strcmp(args[0], "pidfile")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (global.pidfile != NULL) { Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); err_code |= ERR_ALERT; goto out; } if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects a file name as an argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.pidfile = strdup(args[1]); } else if (!strcmp(args[0], "unix-bind")) { int cur_arg = 1; while (*(args[cur_arg])) { if (!strcmp(args[cur_arg], "prefix")) { if (global.unix_bind.prefix != NULL) { Alert("parsing [%s:%d] : unix-bind '%s' already specified. Continuing.\n", file, linenum, args[cur_arg]); err_code |= ERR_ALERT; cur_arg += 2; continue; } if (*(args[cur_arg+1]) == 0) { Alert("parsing [%s:%d] : unix_bind '%s' expects a path as an argument.\n", file, linenum, args[cur_arg]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.unix_bind.prefix = strdup(args[cur_arg+1]); cur_arg += 2; continue; } if (!strcmp(args[cur_arg], "mode")) { global.unix_bind.ux.mode = strtol(args[cur_arg + 1], NULL, 8); cur_arg += 2; continue; } if (!strcmp(args[cur_arg], "uid")) { global.unix_bind.ux.uid = atol(args[cur_arg + 1 ]); cur_arg += 2; continue; } if (!strcmp(args[cur_arg], "gid")) { global.unix_bind.ux.gid = atol(args[cur_arg + 1 ]); cur_arg += 2; continue; } if (!strcmp(args[cur_arg], "user")) { struct passwd *user; user = getpwnam(args[cur_arg + 1]); if (!user) { Alert("parsing [%s:%d] : '%s' : '%s' unknown user.\n", file, linenum, args[0], args[cur_arg + 1 ]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.unix_bind.ux.uid = user->pw_uid; cur_arg += 2; continue; } if (!strcmp(args[cur_arg], "group")) { struct group *group; group = getgrnam(args[cur_arg + 1]); if (!group) { Alert("parsing [%s:%d] : '%s' : '%s' unknown group.\n", file, linenum, args[0], args[cur_arg + 1 ]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.unix_bind.ux.gid = group->gr_gid; cur_arg += 2; continue; } Alert("parsing [%s:%d] : '%s' only supports the 'prefix', 'mode', 'uid', 'gid', 'user' and 'group' options.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } } else if (!strcmp(args[0], "log") && kwm == KWM_NO) { /* no log */ /* delete previous herited or defined syslog servers */ struct logsrv *back; struct logsrv *tmp; if (*(args[1]) != 0) { Alert("parsing [%s:%d]:%s : 'no log' does not expect arguments.\n", file, linenum, args[1]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } list_for_each_entry_safe(tmp, back, &global.logsrvs, list) { LIST_DEL(&tmp->list); free(tmp); } } else if (!strcmp(args[0], "log")) { /* syslog server address */ struct sockaddr_storage *sk; int port1, port2; struct logsrv *logsrv; int arg = 0; int len = 0; if (alertif_too_many_args(8, file, linenum, args, &err_code)) /* does not strictly check optional arguments */ goto out; if (*(args[1]) == 0 || *(args[2]) == 0) { Alert("parsing [%s:%d] : '%s' expects
and as arguments.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } logsrv = calloc(1, sizeof(*logsrv)); /* just after the address, a length may be specified */ if (strcmp(args[arg+2], "len") == 0) { len = atoi(args[arg+3]); if (len < 80 || len > 65535) { Alert("parsing [%s:%d] : invalid log length '%s', must be between 80 and 65535.\n", file, linenum, args[arg+3]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } logsrv->maxlen = len; /* skip these two args */ arg += 2; } else logsrv->maxlen = MAX_SYSLOG_LEN; if (logsrv->maxlen > global.max_syslog_len) { global.max_syslog_len = logsrv->maxlen; logheader = my_realloc2(logheader, global.max_syslog_len + 1); logheader_rfc5424 = my_realloc2(logheader_rfc5424, global.max_syslog_len + 1); logline = my_realloc2(logline, global.max_syslog_len + 1); logline_rfc5424 = my_realloc2(logline_rfc5424, global.max_syslog_len + 1); } /* after the length, a format may be specified */ if (strcmp(args[arg+2], "format") == 0) { logsrv->format = get_log_format(args[arg+3]); if (logsrv->format < 0) { Alert("parsing [%s:%d] : unknown log format '%s'\n", file, linenum, args[arg+3]); err_code |= ERR_ALERT | ERR_FATAL; free(logsrv); goto out; } /* skip these two args */ arg += 2; } if (alertif_too_many_args_idx(3, arg + 1, file, linenum, args, &err_code)) { free(logsrv); goto out; } logsrv->facility = get_log_facility(args[arg+2]); if (logsrv->facility < 0) { Alert("parsing [%s:%d] : unknown log facility '%s'\n", file, linenum, args[arg+2]); err_code |= ERR_ALERT | ERR_FATAL; logsrv->facility = 0; } logsrv->level = 7; /* max syslog level = debug */ if (*(args[arg+3])) { logsrv->level = get_log_level(args[arg+3]); if (logsrv->level < 0) { Alert("parsing [%s:%d] : unknown optional log level '%s'\n", file, linenum, args[arg+3]); err_code |= ERR_ALERT | ERR_FATAL; logsrv->level = 0; } } logsrv->minlvl = 0; /* limit syslog level to this level (emerg) */ if (*(args[arg+4])) { logsrv->minlvl = get_log_level(args[arg+4]); if (logsrv->minlvl < 0) { Alert("parsing [%s:%d] : unknown optional minimum log level '%s'\n", file, linenum, args[arg+4]); err_code |= ERR_ALERT | ERR_FATAL; logsrv->minlvl = 0; } } sk = str2sa_range(args[1], NULL, &port1, &port2, &errmsg, NULL, NULL, 1); if (!sk) { Alert("parsing [%s:%d] : '%s': %s\n", file, linenum, args[0], errmsg); err_code |= ERR_ALERT | ERR_FATAL; free(logsrv); goto out; } logsrv->addr = *sk; if (sk->ss_family == AF_INET || sk->ss_family == AF_INET6) { if (port1 != port2) { Alert("parsing [%s:%d] : '%s' : port ranges and offsets are not allowed in '%s'\n", file, linenum, args[0], args[1]); err_code |= ERR_ALERT | ERR_FATAL; free(logsrv); goto out; } logsrv->addr = *sk; if (!port1) set_host_port(&logsrv->addr, SYSLOG_PORT); } LIST_ADDQ(&global.logsrvs, &logsrv->list); } else if (!strcmp(args[0], "log-send-hostname")) { /* set the hostname in syslog header */ char *name; if (global.log_send_hostname != NULL) { Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); err_code |= ERR_ALERT; goto out; } if (*(args[1])) name = args[1]; else name = hostname; free(global.log_send_hostname); global.log_send_hostname = strdup(name); } else if (!strcmp(args[0], "server-state-base")) { /* path base where HAProxy can find server state files */ if (global.server_state_base != NULL) { Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); err_code |= ERR_ALERT; goto out; } if (!*(args[1])) { Alert("parsing [%s:%d] : '%s' expects one argument: a directory path.\n", file, linenum, args[0]); err_code |= ERR_FATAL; goto out; } global.server_state_base = strdup(args[1]); } else if (!strcmp(args[0], "server-state-file")) { /* path to the file where HAProxy can load the server states */ if (global.server_state_file != NULL) { Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); err_code |= ERR_ALERT; goto out; } if (!*(args[1])) { Alert("parsing [%s:%d] : '%s' expect one argument: a file path.\n", file, linenum, args[0]); err_code |= ERR_FATAL; goto out; } global.server_state_file = strdup(args[1]); } else if (!strcmp(args[0], "log-tag")) { /* tag to report to syslog */ if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (*(args[1]) == 0) { Alert("parsing [%s:%d] : '%s' expects a tag for use in syslog.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } chunk_destroy(&global.log_tag); chunk_initstr(&global.log_tag, strdup(args[1])); } else if (!strcmp(args[0], "spread-checks")) { /* random time between checks (0-50) */ if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (global.spread_checks != 0) { Alert("parsing [%s:%d]: spread-checks already specified. Continuing.\n", file, linenum); err_code |= ERR_ALERT; goto out; } if (*(args[1]) == 0) { Alert("parsing [%s:%d]: '%s' expects an integer argument (0..50).\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } global.spread_checks = atol(args[1]); if (global.spread_checks < 0 || global.spread_checks > 50) { Alert("parsing [%s:%d]: 'spread-checks' needs a positive value in range 0..50.\n", file, linenum); err_code |= ERR_ALERT | ERR_FATAL; } } else if (!strcmp(args[0], "max-spread-checks")) { /* maximum time between first and last check */ const char *err; unsigned int val; if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; if (*(args[1]) == 0) { Alert("parsing [%s:%d]: '%s' expects an integer argument (0..50).\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } err = parse_time_err(args[1], &val, TIME_UNIT_MS); if (err) { Alert("parsing [%s:%d]: unsupported character '%c' in '%s' (wants an integer delay).\n", file, linenum, *err, args[0]); err_code |= ERR_ALERT | ERR_FATAL; } global.max_spread_checks = val; if (global.max_spread_checks < 0) { Alert("parsing [%s:%d]: '%s' needs a positive delay in milliseconds.\n",file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; } } else if (strcmp(args[0], "cpu-map") == 0) { /* map a process list to a CPU set */ #ifdef USE_CPU_AFFINITY int cur_arg, i; unsigned long proc = 0; unsigned long cpus = 0; if (strcmp(args[1], "all") == 0) proc = ~0UL; else if (strcmp(args[1], "odd") == 0) proc = ~0UL/3UL; /* 0x555....555 */ else if (strcmp(args[1], "even") == 0) proc = (~0UL/3UL) << 1; /* 0xAAA...AAA */ else { proc = atol(args[1]); if (proc >= 1 && proc <= LONGBITS) proc = 1UL << (proc - 1); } if (!proc || !*args[2]) { Alert("parsing [%s:%d]: %s expects a process number including 'all', 'odd', 'even', or a number from 1 to %d, followed by a list of CPU ranges with numbers from 0 to %d.\n", file, linenum, args[0], LONGBITS, LONGBITS - 1); err_code |= ERR_ALERT | ERR_FATAL; goto out; } cur_arg = 2; while (*args[cur_arg]) { unsigned int low, high; if (isdigit((int)*args[cur_arg])) { char *dash = strchr(args[cur_arg], '-'); low = high = str2uic(args[cur_arg]); if (dash) high = str2uic(dash + 1); if (high < low) { unsigned int swap = low; low = high; high = swap; } if (high >= LONGBITS) { Alert("parsing [%s:%d]: %s supports CPU numbers from 0 to %d.\n", file, linenum, args[0], LONGBITS - 1); err_code |= ERR_ALERT | ERR_FATAL; goto out; } while (low <= high) cpus |= 1UL << low++; } else { Alert("parsing [%s:%d]: %s : '%s' is not a CPU range.\n", file, linenum, args[0], args[cur_arg]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } cur_arg++; } for (i = 0; i < LONGBITS; i++) if (proc & (1UL << i)) global.cpu_map[i] = cpus; #else Alert("parsing [%s:%d] : '%s' is not enabled, please check build options for USE_CPU_AFFINITY.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; #endif } else if (strcmp(args[0], "setenv") == 0 || strcmp(args[0], "presetenv") == 0) { if (alertif_too_many_args(3, file, linenum, args, &err_code)) goto out; if (*(args[2]) == 0) { Alert("parsing [%s:%d]: '%s' expects a name and a value.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } /* "setenv" overwrites, "presetenv" only sets if not yet set */ if (setenv(args[1], args[2], (args[0][0] == 's')) != 0) { Alert("parsing [%s:%d]: '%s' failed on variable '%s' : %s.\n", file, linenum, args[0], args[1], strerror(errno)); err_code |= ERR_ALERT | ERR_FATAL; goto out; } } else if (!strcmp(args[0], "unsetenv")) { int arg; if (*(args[1]) == 0) { Alert("parsing [%s:%d]: '%s' expects at least one variable name.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } for (arg = 1; *args[arg]; arg++) { if (unsetenv(args[arg]) != 0) { Alert("parsing [%s:%d]: '%s' failed on variable '%s' : %s.\n", file, linenum, args[0], args[arg], strerror(errno)); err_code |= ERR_ALERT | ERR_FATAL; goto out; } } } else if (!strcmp(args[0], "resetenv")) { extern char **environ; char **env = environ; /* args contain variable names to keep, one per argument */ while (*env) { int arg; /* look for current variable in among all those we want to keep */ for (arg = 1; *args[arg]; arg++) { if (strncmp(*env, args[arg], strlen(args[arg])) == 0 && (*env)[strlen(args[arg])] == '=') break; } /* delete this variable */ if (!*args[arg]) { char *delim = strchr(*env, '='); if (!delim || delim - *env >= trash.size) { Alert("parsing [%s:%d]: '%s' failed to unset invalid variable '%s'.\n", file, linenum, args[0], *env); err_code |= ERR_ALERT | ERR_FATAL; goto out; } memcpy(trash.str, *env, delim - *env); trash.str[delim - *env] = 0; if (unsetenv(trash.str) != 0) { Alert("parsing [%s:%d]: '%s' failed to unset variable '%s' : %s.\n", file, linenum, args[0], *env, strerror(errno)); err_code |= ERR_ALERT | ERR_FATAL; goto out; } } else env++; } } else { struct cfg_kw_list *kwl; int index; int rc; list_for_each_entry(kwl, &cfg_keywords.list, list) { for (index = 0; kwl->kw[index].kw != NULL; index++) { if (kwl->kw[index].section != CFG_GLOBAL) continue; if (strcmp(kwl->kw[index].kw, args[0]) == 0) { rc = kwl->kw[index].parse(args, CFG_GLOBAL, NULL, NULL, file, linenum, &errmsg); if (rc < 0) { Alert("parsing [%s:%d] : %s\n", file, linenum, errmsg); err_code |= ERR_ALERT | ERR_FATAL; } else if (rc > 0) { Warning("parsing [%s:%d] : %s\n", file, linenum, errmsg); err_code |= ERR_WARN; goto out; } goto out; } } } Alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section\n", file, linenum, args[0], "global"); err_code |= ERR_ALERT | ERR_FATAL; } out: free(errmsg); return err_code; } void init_default_instance() { init_new_proxy(&defproxy); defproxy.mode = PR_MODE_TCP; defproxy.state = PR_STNEW; defproxy.maxconn = cfg_maxpconn; defproxy.conn_retries = CONN_RETRIES; defproxy.redispatch_after = 0; defproxy.lbprm.chash.balance_factor = 0; defproxy.defsrv.check.inter = DEF_CHKINTR; defproxy.defsrv.check.fastinter = 0; defproxy.defsrv.check.downinter = 0; defproxy.defsrv.agent.inter = DEF_CHKINTR; defproxy.defsrv.agent.fastinter = 0; defproxy.defsrv.agent.downinter = 0; defproxy.defsrv.check.rise = DEF_RISETIME; defproxy.defsrv.check.fall = DEF_FALLTIME; defproxy.defsrv.agent.rise = DEF_AGENT_RISETIME; defproxy.defsrv.agent.fall = DEF_AGENT_FALLTIME; defproxy.defsrv.check.port = 0; defproxy.defsrv.agent.port = 0; defproxy.defsrv.maxqueue = 0; defproxy.defsrv.minconn = 0; defproxy.defsrv.maxconn = 0; defproxy.defsrv.slowstart = 0; defproxy.defsrv.onerror = DEF_HANA_ONERR; defproxy.defsrv.consecutive_errors_limit = DEF_HANA_ERRLIMIT; defproxy.defsrv.uweight = defproxy.defsrv.iweight = 1; defproxy.email_alert.level = LOG_ALERT; defproxy.load_server_state_from_file = PR_SRV_STATE_FILE_UNSPEC; } /* This function createss a new req* or rsp* rule to the proxy. It compiles the * regex and may return the ERR_WARN bit, and error bits such as ERR_ALERT and * ERR_FATAL in case of error. */ static int create_cond_regex_rule(const char *file, int line, struct proxy *px, int dir, int action, int flags, const char *cmd, const char *reg, const char *repl, const char **cond_start) { struct my_regex *preg = NULL; char *errmsg = NULL; const char *err; char *error; int ret_code = 0; struct acl_cond *cond = NULL; int cs; int cap; if (px == &defproxy) { Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, line, cmd); ret_code |= ERR_ALERT | ERR_FATAL; goto err; } if (*reg == 0) { Alert("parsing [%s:%d] : '%s' expects as an argument.\n", file, line, cmd); ret_code |= ERR_ALERT | ERR_FATAL; goto err; } if (warnifnotcap(px, PR_CAP_FE | PR_CAP_BE, file, line, cmd, NULL)) ret_code |= ERR_WARN; if (cond_start && (strcmp(*cond_start, "if") == 0 || strcmp(*cond_start, "unless") == 0)) { if ((cond = build_acl_cond(file, line, px, cond_start, &errmsg)) == NULL) { Alert("parsing [%s:%d] : error detected while parsing a '%s' condition : %s.\n", file, line, cmd, errmsg); ret_code |= ERR_ALERT | ERR_FATAL; goto err; } } else if (cond_start && **cond_start) { Alert("parsing [%s:%d] : '%s' : Expecting nothing, 'if', or 'unless', got '%s'.\n", file, line, cmd, *cond_start); ret_code |= ERR_ALERT | ERR_FATAL; goto err; } ret_code |= warnif_cond_conflicts(cond, (dir == SMP_OPT_DIR_REQ) ? ((px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR) : ((px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR), file, line); preg = calloc(1, sizeof(*preg)); if (!preg) { Alert("parsing [%s:%d] : '%s' : not enough memory to build regex.\n", file, line, cmd); ret_code = ERR_ALERT | ERR_FATAL; goto err; } cs = !(flags & REG_ICASE); cap = !(flags & REG_NOSUB); error = NULL; if (!regex_comp(reg, preg, cs, cap, &error)) { Alert("parsing [%s:%d] : '%s' : regular expression '%s' : %s\n", file, line, cmd, reg, error); free(error); ret_code = ERR_ALERT | ERR_FATAL; goto err; } err = chain_regex((dir == SMP_OPT_DIR_REQ) ? &px->req_exp : &px->rsp_exp, preg, action, repl ? strdup(repl) : NULL, cond); if (repl && err) { Alert("parsing [%s:%d] : '%s' : invalid character or unterminated sequence in replacement string near '%c'.\n", file, line, cmd, *err); ret_code |= ERR_ALERT | ERR_FATAL; goto err_free; } if (dir == SMP_OPT_DIR_REQ && warnif_misplaced_reqxxx(px, file, line, cmd)) ret_code |= ERR_WARN; return ret_code; err_free: regex_free(preg); err: free(preg); free(errmsg); return ret_code; } /* * Parse a line in a , or section. * Returns the error code, 0 if OK, or any combination of : * - ERR_ABORT: must abort ASAP * - ERR_FATAL: we can continue parsing but not start the service * - ERR_WARN: a warning has been emitted * - ERR_ALERT: an alert has been emitted * Only the two first ones can stop processing, the two others are just * indicators. */ int cfg_parse_peers(const char *file, int linenum, char **args, int kwm) { static struct peers *curpeers = NULL; struct peer *newpeer = NULL; const char *err; struct bind_conf *bind_conf; struct listener *l; int err_code = 0; char *errmsg = NULL; if (strcmp(args[0], "peers") == 0) { /* new peers section */ if (!*args[1]) { Alert("parsing [%s:%d] : missing name for peers section.\n", file, linenum); err_code |= ERR_ALERT | ERR_ABORT; goto out; } if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; err = invalid_char(args[1]); if (err) { Alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n", file, linenum, *err, args[0], args[1]); err_code |= ERR_ALERT | ERR_ABORT; goto out; } for (curpeers = cfg_peers; curpeers != NULL; curpeers = curpeers->next) { /* * If there are two proxies with the same name only following * combinations are allowed: */ if (strcmp(curpeers->id, args[1]) == 0) { Alert("Parsing [%s:%d]: peers section '%s' has the same name as another peers section declared at %s:%d.\n", file, linenum, args[1], curpeers->conf.file, curpeers->conf.line); err_code |= ERR_ALERT | ERR_FATAL; } } if ((curpeers = calloc(1, sizeof(*curpeers))) == NULL) { Alert("parsing [%s:%d] : out of memory.\n", file, linenum); err_code |= ERR_ALERT | ERR_ABORT; goto out; } curpeers->next = cfg_peers; cfg_peers = curpeers; curpeers->conf.file = strdup(file); curpeers->conf.line = linenum; curpeers->last_change = now.tv_sec; curpeers->id = strdup(args[1]); curpeers->state = PR_STNEW; } else if (strcmp(args[0], "peer") == 0) { /* peer definition */ struct sockaddr_storage *sk; int port1, port2; struct protocol *proto; if (!*args[2]) { Alert("parsing [%s:%d] : '%s' expects and [:] as arguments.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } err = invalid_char(args[1]); if (err) { Alert("parsing [%s:%d] : character '%c' is not permitted in server name '%s'.\n", file, linenum, *err, args[1]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } if ((newpeer = calloc(1, sizeof(*newpeer))) == NULL) { Alert("parsing [%s:%d] : out of memory.\n", file, linenum); err_code |= ERR_ALERT | ERR_ABORT; goto out; } /* the peers are linked backwards first */ curpeers->count++; newpeer->next = curpeers->remote; curpeers->remote = newpeer; newpeer->conf.file = strdup(file); newpeer->conf.line = linenum; newpeer->last_change = now.tv_sec; newpeer->id = strdup(args[1]); sk = str2sa_range(args[2], NULL, &port1, &port2, &errmsg, NULL, NULL, 1); if (!sk) { Alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg); err_code |= ERR_ALERT | ERR_FATAL; goto out; } proto = protocol_by_family(sk->ss_family); if (!proto || !proto->connect) { Alert("parsing [%s:%d] : '%s %s' : connect() not supported for this address family.\n", file, linenum, args[0], args[1]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } if (port1 != port2) { Alert("parsing [%s:%d] : '%s %s' : port ranges and offsets are not allowed in '%s'\n", file, linenum, args[0], args[1], args[2]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } if (!port1) { Alert("parsing [%s:%d] : '%s %s' : missing or invalid port in '%s'\n", file, linenum, args[0], args[1], args[2]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } newpeer->addr = *sk; newpeer->proto = proto; newpeer->xprt = xprt_get(XPRT_RAW); newpeer->sock_init_arg = NULL; if (strcmp(newpeer->id, localpeer) == 0) { /* Current is local peer, it define a frontend */ newpeer->local = 1; cfg_peers->local = newpeer; if (!curpeers->peers_fe) { if ((curpeers->peers_fe = calloc(1, sizeof(struct proxy))) == NULL) { Alert("parsing [%s:%d] : out of memory.\n", file, linenum); err_code |= ERR_ALERT | ERR_ABORT; goto out; } init_new_proxy(curpeers->peers_fe); curpeers->peers_fe->parent = curpeers; curpeers->peers_fe->id = strdup(args[1]); curpeers->peers_fe->conf.args.file = curpeers->peers_fe->conf.file = strdup(file); curpeers->peers_fe->conf.args.line = curpeers->peers_fe->conf.line = linenum; peers_setup_frontend(curpeers->peers_fe); bind_conf = bind_conf_alloc(curpeers->peers_fe, file, linenum, args[2], xprt_get(XPRT_RAW)); if (!str2listener(args[2], curpeers->peers_fe, bind_conf, file, linenum, &errmsg)) { if (errmsg && *errmsg) { indent_msg(&errmsg, 2); Alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg); } else Alert("parsing [%s:%d] : '%s %s' : error encountered while parsing listening address %s.\n", file, linenum, args[0], args[1], args[2]); err_code |= ERR_FATAL; goto out; } list_for_each_entry(l, &bind_conf->listeners, by_bind) { l->maxaccept = 1; l->maxconn = curpeers->peers_fe->maxconn; l->backlog = curpeers->peers_fe->backlog; l->accept = session_accept_fd; l->handler = process_stream; l->analysers |= curpeers->peers_fe->fe_req_ana; l->default_target = curpeers->peers_fe->default_target; l->options |= LI_O_UNLIMITED; /* don't make the peers subject to global limits */ global.maxsock += l->maxconn; } } else { Alert("parsing [%s:%d] : '%s %s' : local peer name already referenced at %s:%d.\n", file, linenum, args[0], args[1], curpeers->peers_fe->conf.file, curpeers->peers_fe->conf.line); err_code |= ERR_FATAL; goto out; } } } /* neither "peer" nor "peers" */ else if (!strcmp(args[0], "disabled")) { /* disables this peers section */ curpeers->state = PR_STSTOPPED; } else if (!strcmp(args[0], "enabled")) { /* enables this peers section (used to revert a disabled default) */ curpeers->state = PR_STNEW; } else if (*args[0] != 0) { Alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section\n", file, linenum, args[0], cursection); err_code |= ERR_ALERT | ERR_FATAL; goto out; } out: free(errmsg); return err_code; } /* * Parse a section. * Returns the error code, 0 if OK, or any combination of : * - ERR_ABORT: must abort ASAP * - ERR_FATAL: we can continue parsing but not start the service * - ERR_WARN: a warning has been emitted * - ERR_ALERT: an alert has been emitted * Only the two first ones can stop processing, the two others are just * indicators. */ int cfg_parse_resolvers(const char *file, int linenum, char **args, int kwm) { static struct dns_resolvers *curr_resolvers = NULL; struct dns_nameserver *newnameserver = NULL; const char *err; int err_code = 0; char *errmsg = NULL; if (strcmp(args[0], "resolvers") == 0) { /* new resolvers section */ if (!*args[1]) { Alert("parsing [%s:%d] : missing name for resolvers section.\n", file, linenum); err_code |= ERR_ALERT | ERR_ABORT; goto out; } err = invalid_char(args[1]); if (err) { Alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n", file, linenum, *err, args[0], args[1]); err_code |= ERR_ALERT | ERR_ABORT; goto out; } list_for_each_entry(curr_resolvers, &dns_resolvers, list) { /* Error if two resolvers owns the same name */ if (strcmp(curr_resolvers->id, args[1]) == 0) { Alert("Parsing [%s:%d]: resolvers '%s' has same name as another resolvers (declared at %s:%d).\n", file, linenum, args[1], curr_resolvers->conf.file, curr_resolvers->conf.line); err_code |= ERR_ALERT | ERR_ABORT; } } if ((curr_resolvers = calloc(1, sizeof(*curr_resolvers))) == NULL) { Alert("parsing [%s:%d] : out of memory.\n", file, linenum); err_code |= ERR_ALERT | ERR_ABORT; goto out; } /* default values */ LIST_ADDQ(&dns_resolvers, &curr_resolvers->list); curr_resolvers->conf.file = strdup(file); curr_resolvers->conf.line = linenum; curr_resolvers->id = strdup(args[1]); curr_resolvers->query_ids = EB_ROOT; /* default maximum response size */ curr_resolvers->accepted_payload_size = 512; /* default hold period for nx, other, refuse and timeout is 30s */ curr_resolvers->hold.nx = 30000; curr_resolvers->hold.other = 30000; curr_resolvers->hold.refused = 30000; curr_resolvers->hold.timeout = 30000; curr_resolvers->hold.obsolete = 30000; /* default hold period for valid is 10s */ curr_resolvers->hold.valid = 10000; curr_resolvers->timeout.retry = 1000; curr_resolvers->resolve_retries = 3; /* default resolution pool size */ curr_resolvers->resolution_pool_size = DNS_DEFAULT_RESOLUTION_POOL_SIZE; LIST_INIT(&curr_resolvers->nameserver_list); LIST_INIT(&curr_resolvers->resolution.curr); LIST_INIT(&curr_resolvers->resolution.wait); LIST_INIT(&curr_resolvers->resolution.pool); } else if (strcmp(args[0], "nameserver") == 0) { /* nameserver definition */ struct sockaddr_storage *sk; int port1, port2; struct protocol *proto; if (!*args[2]) { Alert("parsing [%s:%d] : '%s' expects and [:] as arguments.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } err = invalid_char(args[1]); if (err) { Alert("parsing [%s:%d] : character '%c' is not permitted in server name '%s'.\n", file, linenum, *err, args[1]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } list_for_each_entry(newnameserver, &curr_resolvers->nameserver_list, list) { /* Error if two resolvers owns the same name */ if (strcmp(newnameserver->id, args[1]) == 0) { Alert("Parsing [%s:%d]: nameserver '%s' has same name as another nameserver (declared at %s:%d).\n", file, linenum, args[1], curr_resolvers->conf.file, curr_resolvers->conf.line); err_code |= ERR_ALERT | ERR_FATAL; } } if ((newnameserver = calloc(1, sizeof(*newnameserver))) == NULL) { Alert("parsing [%s:%d] : out of memory.\n", file, linenum); err_code |= ERR_ALERT | ERR_ABORT; goto out; } /* the nameservers are linked backward first */ LIST_ADDQ(&curr_resolvers->nameserver_list, &newnameserver->list); curr_resolvers->count_nameservers++; newnameserver->resolvers = curr_resolvers; newnameserver->conf.file = strdup(file); newnameserver->conf.line = linenum; newnameserver->id = strdup(args[1]); sk = str2sa_range(args[2], NULL, &port1, &port2, &errmsg, NULL, NULL, 1); if (!sk) { Alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg); err_code |= ERR_ALERT | ERR_FATAL; goto out; } proto = protocol_by_family(sk->ss_family); if (!proto || !proto->connect) { Alert("parsing [%s:%d] : '%s %s' : connect() not supported for this address family.\n", file, linenum, args[0], args[1]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } if (port1 != port2) { Alert("parsing [%s:%d] : '%s %s' : port ranges and offsets are not allowed in '%s'\n", file, linenum, args[0], args[1], args[2]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } if (!port1 && !port2) { Alert("parsing [%s:%d] : '%s %s' : no UDP port specified\n", file, linenum, args[0], args[1]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } newnameserver->addr = *sk; } else if (strcmp(args[0], "hold") == 0) { /* hold periods */ const char *res; unsigned int time; if (!*args[2]) { Alert("parsing [%s:%d] : '%s' expects an and a