/* * 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 */ }; /* Used to chain configuration sections definitions. This list * stores struct cfg_section */ struct list sections = LIST_HEAD_INIT(sections); struct list postparsers = LIST_HEAD_INIT(postparsers); char *cursection = NULL; 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 */ 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) { char *next, *dupstr; int port, end; next = dupstr = strdup(str); while (next && *next) { int inherited = 0; struct sockaddr_storage *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; inherited = 1; /* 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); } else if (ss2->ss_family == AF_CUST_SOCKPAIR) { socklen_t addr_len; inherited = 1; 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; } ss2->ss_family = AF_CUST_SOCKPAIR; /* reassign AF_CUST_SOCKPAIR because of getsockname */ port = end = 0; } /* OK the address looks correct */ if (!create_listeners(bind_conf, ss2, port, end, fd, inherited, err)) { memprintf(err, "%s for address '%s'.\n", *err, str); goto fail; } } /* 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]); } ha_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 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. */ 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) ha_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 ha_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) ha_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 ha_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 string representing a process number or a set of processes. It must * be "all", "odd", "even", a number between 1 and or a range with * two such numbers delimited by a dash ('-'). On success, it returns * 0. otherwise it returns 1 with an error message in . * * Note: this function can also be used to parse a thread number or a set of * threads. */ int parse_process_number(const char *arg, unsigned long *proc, int *autoinc, char **err) { if (autoinc) { *autoinc = 0; if (strncmp(arg, "auto:", 5) == 0) { arg += 5; *autoinc = 1; } } if (strcmp(arg, "all") == 0) *proc |= ~0UL; else if (strcmp(arg, "odd") == 0) *proc |= ~0UL/3UL; /* 0x555....555 */ else if (strcmp(arg, "even") == 0) *proc |= (~0UL/3UL) << 1; /* 0xAAA...AAA */ else { char *dash; unsigned int low, high; if (!isdigit((int)*arg)) { memprintf(err, "'%s' is not a valid number.\n", arg); return -1; } low = high = str2uic(arg); if ((dash = strchr(arg, '-')) != NULL) high = ((!*(dash+1)) ? LONGBITS : str2uic(dash + 1)); if (high < low) { unsigned int swap = low; low = high; high = swap; } if (low < 1 || low > LONGBITS || high > LONGBITS) { memprintf(err, "'%s' is not a valid number/range." " It supports numbers from 1 to %d.\n", arg, LONGBITS); return 1; } for (;low <= high; low++) *proc |= 1UL << (low-1); } return 0; } #ifdef USE_CPU_AFFINITY /* Parse cpu sets. Each CPU set is either a unique number between 0 and * or a range with two such numbers delimited by a dash * ('-'). Multiple CPU numbers or ranges may be specified. On success, it * returns 0. otherwise it returns 1 with an error message in . */ unsigned long parse_cpu_set(const char **args, unsigned long *cpu_set, char **err) { int cur_arg = 0; *cpu_set = 0; while (*args[cur_arg]) { char *dash; unsigned int low, high; if (!isdigit((int)*args[cur_arg])) { memprintf(err, "'%s' is not a CPU range.\n", args[cur_arg]); return -1; } low = high = str2uic(args[cur_arg]); if ((dash = strchr(args[cur_arg], '-')) != NULL) high = ((!*(dash+1)) ? LONGBITS-1 : str2uic(dash + 1)); if (high < low) { unsigned int swap = low; low = high; high = swap; } if (high >= LONGBITS) { memprintf(err, "supports CPU numbers from 0 to %d.\n", LONGBITS - 1); return 1; } while (low <= high) *cpu_set |= 1UL << low++; cur_arg++; } return 0; } #endif 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; } /* * 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]) { ha_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) { ha_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) { ha_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) { ha_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]) { ha_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) { ha_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) { ha_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) { ha_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) { ha_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) { ha_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) { ha_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; HA_SPIN_INIT(&newpeer->lock); 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) { ha_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); ha_alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg); } else ha_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->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 { ha_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) { ha_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]) { ha_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) { ha_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) { ha_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) { ha_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 = 0; /* default hold period for valid is 10s */ curr_resolvers->hold.valid = 10000; curr_resolvers->timeout.resolve = 1000; curr_resolvers->timeout.retry = 1000; curr_resolvers->resolve_retries = 3; curr_resolvers->nb_nameservers = 0; LIST_INIT(&curr_resolvers->nameservers); LIST_INIT(&curr_resolvers->resolutions.curr); LIST_INIT(&curr_resolvers->resolutions.wait); HA_SPIN_INIT(&curr_resolvers->lock); } else if (strcmp(args[0], "nameserver") == 0) { /* nameserver definition */ struct sockaddr_storage *sk; int port1, port2; struct protocol *proto; if (!*args[2]) { ha_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) { ha_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->nameservers, list) { /* Error if two resolvers owns the same name */ if (strcmp(newnameserver->id, args[1]) == 0) { ha_alert("Parsing [%s:%d]: nameserver '%s' has same name as another nameserver (declared at %s:%d).\n", file, linenum, args[1], newnameserver->conf.file, newnameserver->conf.line); err_code |= ERR_ALERT | ERR_FATAL; } } if ((newnameserver = calloc(1, sizeof(*newnameserver))) == NULL) { ha_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->nameservers, &newnameserver->list); 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) { ha_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) { ha_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) { ha_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) { ha_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], "parse-resolv-conf") == 0) { const char *whitespace = "\r\n\t "; char *resolv_line = NULL; int resolv_linenum = 0; FILE *f = NULL; char *address = NULL; struct sockaddr_storage *sk = NULL; struct protocol *proto; int duplicate_name = 0; if ((resolv_line = malloc(sizeof(*resolv_line) * LINESIZE)) == NULL) { ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum); err_code |= ERR_ALERT | ERR_FATAL; goto resolv_out; } if ((f = fopen("/etc/resolv.conf", "r")) == NULL) { ha_alert("parsing [%s:%d] : failed to open /etc/resolv.conf.\n", file, linenum); err_code |= ERR_ALERT | ERR_FATAL; goto resolv_out; } sk = calloc(1, sizeof(*sk)); if (sk == NULL) { ha_alert("parsing [/etc/resolv.conf:%d] : out of memory.\n", resolv_linenum); err_code |= ERR_ALERT | ERR_FATAL; goto resolv_out; } while (fgets(resolv_line, LINESIZE, f) != NULL) { resolv_linenum++; if (strncmp(resolv_line, "nameserver", 10) != 0) continue; address = strtok(resolv_line + 10, whitespace); if (address == resolv_line + 10) continue; if (address == NULL) { ha_warning("parsing [/etc/resolv.conf:%d] : nameserver line is missing address.\n", resolv_linenum); err_code |= ERR_WARN; continue; } duplicate_name = 0; list_for_each_entry(newnameserver, &curr_resolvers->nameservers, list) { if (strcmp(newnameserver->id, address) == 0) { ha_warning("Parsing [/etc/resolv.conf:%d] : generated name for /etc/resolv.conf nameserver '%s' conflicts with another nameserver (declared at %s:%d), it appears to be a duplicate and will be excluded.\n", resolv_linenum, address, newnameserver->conf.file, newnameserver->conf.line); err_code |= ERR_WARN; duplicate_name = 1; } } if (duplicate_name) continue; memset(sk, 0, sizeof(*sk)); sk = str2ip2(address, sk, 1); if (!sk) { ha_warning("parsing [/etc/resolv.conf:%d] : address '%s' could not be recognized, namerserver will be excluded.\n", resolv_linenum, address); err_code |= ERR_WARN; continue; } set_host_port(sk, 53); proto = protocol_by_family(sk->ss_family); if (!proto || !proto->connect) { ha_warning("parsing [/etc/resolv.conf:%d] : '%s' : connect() not supported for this address family.\n", resolv_linenum, address); err_code |= ERR_WARN; continue; } if ((newnameserver = calloc(1, sizeof(*newnameserver))) == NULL) { ha_alert("parsing [/etc/resolv.conf:%d] : out of memory.\n", resolv_linenum); err_code |= ERR_ALERT | ERR_FATAL; goto resolv_out; } newnameserver->conf.file = strdup("/etc/resolv.conf"); if (newnameserver->conf.file == NULL) { ha_alert("parsing [/etc/resolv.conf:%d] : out of memory.\n", resolv_linenum); err_code |= ERR_ALERT | ERR_FATAL; goto resolv_out; } newnameserver->id = strdup(address); if (newnameserver->id == NULL) { ha_alert("parsing [/etc/resolv.conf:%d] : out of memory.\n", resolv_linenum); err_code |= ERR_ALERT | ERR_FATAL; goto resolv_out; } newnameserver->resolvers = curr_resolvers; newnameserver->conf.line = resolv_linenum; newnameserver->addr = *sk; LIST_ADDQ(&curr_resolvers->nameservers, &newnameserver->list); } resolv_out: free(sk); free(resolv_line); if (f != NULL) fclose(f); } else if (strcmp(args[0], "hold") == 0) { /* hold periods */ const char *res; unsigned int time; if (!*args[2]) { ha_alert("parsing [%s:%d] : '%s' expects an and a