diff --git a/include/common/standard.h b/include/common/standard.h index be719f714..682711105 100644 --- a/include/common/standard.h +++ b/include/common/standard.h @@ -259,12 +259,19 @@ unsigned int round_2dig(unsigned int i); extern const char *invalid_char(const char *name); /* - * Checks <domainname> for invalid characters. Valid chars are [A-Za-z0-9_.-]. + * Checks <name> for invalid characters. Valid chars are [A-Za-z0-9_.-]. * If an invalid character is found, a pointer to it is returned. * If everything is fine, NULL is returned. */ extern const char *invalid_domainchar(const char *name); +/* + * Checks <name> for invalid characters. Valid chars are [A-Za-z_.-]. + * If an invalid character is found, a pointer to it is returned. + * If everything is fine, NULL is returned. + */ +extern const char *invalid_prefix_char(const char *name); + /* * converts <str> to a locally allocated struct sockaddr_storage *, and a * port range consisting in two integers. The low and high end are always set diff --git a/include/types/server.h b/include/types/server.h index bfaa94115..8d68dcb56 100644 --- a/include/types/server.h +++ b/include/types/server.h @@ -274,6 +274,15 @@ struct server { int line; /* line where the section appears */ struct eb32_node id; /* place in the tree of used IDs */ } conf; /* config information */ + /* Template information used only for server objects which + * serve as template filled at parsing time and used during + * server allocations from server templates. + */ + struct { + char *prefix; + int nb_low; + int nb_high; + } tmpl_info; }; /* Descriptor for a "server" keyword. The ->parse() function returns 0 in case of diff --git a/src/cfgparse.c b/src/cfgparse.c index 348b9e886..d44949a7d 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -2859,7 +2859,9 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) curproxy->conf.args.line = linenum; /* Now let's parse the proxy-specific keywords */ - if (!strcmp(args[0], "server") || !strcmp(args[0], "default-server")) { + if (!strcmp(args[0], "server") || + !strcmp(args[0], "default-server") || + !strcmp(args[0], "server-template")) { err_code |= parse_server(file, linenum, args, curproxy, &defproxy); if (err_code & ERR_FATAL) goto out; diff --git a/src/server.c b/src/server.c index f25a7a000..9f0778874 100644 --- a/src/server.c +++ b/src/server.c @@ -1969,18 +1969,53 @@ static int server_finalize_init(const char *file, int linenum, char **args, int return 0; } +/* + * Parse as much as possible such a range string argument: low[-high] + * Set <nb_low> and <nb_high> values so that they may be reused by this loop + * for(int i = nb_low; i <= nb_high; i++)... with nb_low >= 1. + * Fails if 'low' < 0 or 'high' is present and not higher than 'low'. + * Returns 0 if succeeded, -1 if not. + */ +static int srv_tmpl_parse_range(struct server *srv, const char *arg, int *nb_low, int *nb_high) +{ + char *nb_high_arg; + + *nb_high = 0; + chunk_printf(&trash, "%s", arg); + *nb_low = atoi(trash.str); + + if ((nb_high_arg = strchr(trash.str, '-'))) { + *nb_high_arg++ = '\0'; + *nb_high = atoi(nb_high_arg); + } + else { + *nb_high += *nb_low; + *nb_low = 1; + } + + if (*nb_low < 0 || *nb_high < *nb_low) + return -1; + + return 0; +} + int parse_server(const char *file, int linenum, char **args, struct proxy *curproxy, struct proxy *defproxy) { struct server *newsrv = NULL; - const char *err; + const char *err = NULL; char *errmsg = NULL; int err_code = 0; unsigned val; char *fqdn = NULL; - if (!strcmp(args[0], "server") || !strcmp(args[0], "default-server")) { /* server address */ + if (!strcmp(args[0], "server") || + !strcmp(args[0], "default-server") || + !strcmp(args[0], "server-template")) { int cur_arg; int defsrv = (*args[0] == 'd'); + int srv = !defsrv && !strcmp(args[0], "server"); + int srv_tmpl = !defsrv && !srv; + int tmpl_range_low = 0, tmpl_range_high = 0; if (!defsrv && curproxy == defproxy) { Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]); @@ -1990,19 +2025,47 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr else if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL)) err_code |= ERR_ALERT | ERR_FATAL; - if (!defsrv && !*args[2]) { - Alert("parsing [%s:%d] : '%s' expects <name> and <addr>[:<port>] as arguments.\n", - file, linenum, args[0]); + /* There is no mandatory first arguments for default server. */ + if (srv) { + if (!*args[2]) { + /* 'server' line number of argument check. */ + Alert("parsing [%s:%d] : '%s' expects <name> and <addr>[:<port>] as arguments.\n", + file, linenum, args[0]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + err = invalid_char(args[1]); + } + else if (srv_tmpl) { + if (!*args[3]) { + /* 'server-template' line number of argument check. */ + Alert("parsing [%s:%d] : '%s' expects <prefix> <nb | range> <addr>[:<port>] as arguments.\n", + file, linenum, args[0]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + err = invalid_prefix_char(args[1]); + } + + if (err) { + Alert("parsing [%s:%d] : character '%c' is not permitted in %s %s '%s'.\n", + file, linenum, *err, args[0], srv ? "name" : "prefix", args[1]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } - err = invalid_char(args[1]); - if (err && !defsrv) { - 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; + cur_arg = 2; + if (srv_tmpl) { + /* Parse server-template <nb | range> arg. */ + if (srv_tmpl_parse_range(newsrv, args[cur_arg], &tmpl_range_low, &tmpl_range_high) < 0) { + Alert("parsing [%s:%d] : Wrong %s number or range arg '%s'.\n", + file, linenum, args[0], args[cur_arg]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + cur_arg++; } if (!defsrv) { @@ -2018,12 +2081,24 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr goto out; } + if (srv_tmpl) { + newsrv->tmpl_info.nb_low = tmpl_range_low; + newsrv->tmpl_info.nb_high = tmpl_range_high; + } + /* the servers are linked backwards first */ newsrv->next = curproxy->srv; curproxy->srv = newsrv; newsrv->conf.file = strdup(file); newsrv->conf.line = linenum; - newsrv->id = strdup(args[1]); + /* Note: for a server template, its id is its prefix. + * This is a temporary id which will be used for server allocations to come + * after parsing. + */ + if (srv) + newsrv->id = strdup(args[1]); + else + newsrv->tmpl_info.prefix = strdup(args[1]); /* several ways to check the port component : * - IP => port=+0, relative (IPv4 only) @@ -2032,7 +2107,7 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr * - IP:+N => port=+N, relative * - IP:-N => port=-N, relative */ - sk = str2sa_range(args[2], &port, &port1, &port2, &errmsg, NULL, &fqdn, 0); + sk = str2sa_range(args[cur_arg], &port, &port1, &port2, &errmsg, NULL, &fqdn, 0); if (!sk) { Alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg); err_code |= ERR_ALERT | ERR_FATAL; @@ -2073,7 +2148,7 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr goto skip_name_resolution; if ((dns_str_to_dn_label(newsrv->hostname, curr_resolution->hostname_dn, curr_resolution->hostname_dn_len + 1)) == NULL) { Alert("parsing [%s:%d] : Invalid hostname '%s'\n", - file, linenum, args[2]); + file, linenum, args[cur_arg]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } @@ -2093,14 +2168,14 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr if (!newsrv->hostname && !protocol_by_family(newsrv->addr.ss_family)) { Alert("parsing [%s:%d] : Unknown protocol family %d '%s'\n", - file, linenum, newsrv->addr.ss_family, args[2]); + file, linenum, newsrv->addr.ss_family, args[cur_arg]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } /* Copy default server settings to new server settings. */ srv_settings_cpy(newsrv, &curproxy->defsrv); - cur_arg = 3; + cur_arg++; } else { newsrv = &curproxy->defsrv; cur_arg = 1; diff --git a/src/standard.c b/src/standard.c index 99f7066bb..6abedb4a2 100644 --- a/src/standard.c +++ b/src/standard.c @@ -592,17 +592,18 @@ const char *invalid_char(const char *name) } /* - * Checks <domainname> for invalid characters. Valid chars are [A-Za-z0-9_.-]. + * Checks <name> for invalid characters. Valid chars are [_.-] and those + * accepted by <f> function. * If an invalid character is found, a pointer to it is returned. * If everything is fine, NULL is returned. */ -const char *invalid_domainchar(const char *name) { +static inline const char *__invalid_char(const char *name, int (*f)(int)) { if (!*name) return name; while (*name) { - if (!isalnum((int)(unsigned char)*name) && *name != '.' && + if (!f((int)(unsigned char)*name) && *name != '.' && *name != '_' && *name != '-') return name; @@ -612,6 +613,24 @@ const char *invalid_domainchar(const char *name) { return NULL; } +/* + * Checks <name> for invalid characters. Valid chars are [A-Za-z0-9_.-]. + * If an invalid character is found, a pointer to it is returned. + * If everything is fine, NULL is returned. + */ +const char *invalid_domainchar(const char *name) { + return __invalid_char(name, isalnum); +} + +/* + * Checks <name> for invalid characters. Valid chars are [A-Za-z_.-]. + * If an invalid character is found, a pointer to it is returned. + * If everything is fine, NULL is returned. + */ +const char *invalid_prefix_char(const char *name) { + return __invalid_char(name, isalpha); +} + /* * converts <str> to a struct sockaddr_storage* provided by the caller. The * caller must have zeroed <sa> first, and may have set sa->ss_family to force