MEDIUM: listener: add a minimal framework to register "bind" keyword options

With the arrival of SSL, the "bind" keyword has received even more options,
all of which are processed in cfgparse in a cumbersome way. So it's time to
let modules register their own bind options. This is done very similarly to
the ACLs with a small difference in that we make the difference between an
unknown option and a known, unimplemented option.
This commit is contained in:
Willy Tarreau 2012-09-12 23:17:10 +02:00
parent 88500de69e
commit 269826659d
4 changed files with 117 additions and 0 deletions

View File

@ -105,6 +105,15 @@ void delete_listener(struct listener *listener);
*/
int listener_accept(int fd);
/*
* Registers the bind keyword list <kwl> as a list of valid keywords for next
* parsing sessions.
*/
void bind_register_keywords(struct bind_kw_list *kwl);
/* Return a pointer to the bind keyword <kw>, or NULL if not found. */
struct bind_kw *bind_find_kw(const char *kw);
/* allocate an bind_conf struct for a bind line, and chain it to list head <lh>.
* If <arg> is not NULL, it is duplicated into ->arg to store useful config
* information for error reporting.

View File

@ -156,6 +156,31 @@ struct listener {
} conf; /* config information */
};
/* Descriptor for a "bind" keyword. The ->parse() function returns 0 in case of
* success, or a combination of ERR_* flags if an error is encountered. The
* function pointer can be NULL if not implemented. The function also has an
* access to the current "bind" conf, which is the conf of the last listener,
* reachable via px->listen->bind_conf. The ->skip value tells the parser how
* many words have to be skipped after the keyword.
*/
struct bind_kw {
const char *kw;
int (*parse)(char **args, int cur_arg, struct proxy *px, struct listener *last, char **err);
int skip; /* nb of args to skip */
};
/*
* A keyword list. It is a NULL-terminated array of keywords. It embeds a
* struct list in order to be linked to other lists, allowing it to easily
* be declared where it is needed, and linked without duplicating data nor
* allocating memory.
*/
struct bind_kw_list {
struct list list;
struct bind_kw kw[VAR_ARRAY];
};
#endif /* _TYPES_LISTENER_H */
/*

View File

@ -1711,6 +1711,8 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
}
cur_arg = 2;
while (*(args[cur_arg])) {
struct bind_kw *kw;
if (!strcmp(args[cur_arg], "interface")) { /* specifically bind to this interface */
#ifdef SO_BINDTODEVICE
struct listener *l;
@ -2147,6 +2149,41 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
continue;
}
kw = bind_find_kw(args[cur_arg]);
if (kw) {
char *err = NULL;
int code;
if (!kw->parse) {
Alert("parsing [%s:%d] : '%s' : '%s' option is not implemented in this version (check build options).\n",
file, linenum, args[0], args[cur_arg]);
cur_arg += 1 + kw->skip ;
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
code = kw->parse(args, cur_arg, curproxy, last_listen, &err);
err_code |= code;
if (code) {
if (err && *err) {
indent_msg(&err, 2);
Alert("parsing [%s:%d] : '%s' : %s\n", file, linenum, args[0], err);
}
else
Alert("parsing [%s:%d] : '%s' : error encountered while processing '%s'.\n",
file, linenum, args[0], args[cur_arg]);
if (code & ERR_FATAL) {
free(err);
cur_arg += 1 + kw->skip;
goto out;
}
}
free(err);
cur_arg += 1 + kw->skip;
continue;
}
Alert("parsing [%s:%d] : '%s' only supports the 'transparent', 'accept-proxy', 'defer-accept', 'name', 'id', 'mss', 'mode', 'uid', 'gid', 'user', 'group' and 'interface' options.\n",
file, linenum, args[0]);
err_code |= ERR_ALERT | ERR_FATAL;

View File

@ -29,6 +29,11 @@
#include <proto/log.h>
#include <proto/task.h>
/* List head of all known bind keywords */
static struct bind_kw_list bind_keywords = {
.list = LIST_HEAD_INIT(bind_keywords.list)
};
/* This function adds the specified listener's file descriptor to the polling
* lists if it is in the LI_LISTEN state. The listener enters LI_READY or
* LI_FULL state depending on its number of connections.
@ -409,6 +414,47 @@ void listener_accept(int fd)
return;
}
/*
* Registers the bind keyword list <kwl> as a list of valid keywords for next
* parsing sessions.
*/
void bind_register_keywords(struct bind_kw_list *kwl)
{
LIST_ADDQ(&bind_keywords.list, &kwl->list);
}
/* Return a pointer to the bind keyword <kw>, or NULL if not found. If the
* keyword is found with a NULL ->parse() function, then an attempt is made to
* find one with a valid ->parse() function. This way it is possible to declare
* platform-dependant, known keywords as NULL, then only declare them as valid
* if some options are met. Note that if the requested keyword contains an
* opening parenthesis, everything from this point is ignored.
*/
struct bind_kw *bind_find_kw(const char *kw)
{
int index;
const char *kwend;
struct bind_kw_list *kwl;
struct bind_kw *ret = NULL;
kwend = strchr(kw, '(');
if (!kwend)
kwend = kw + strlen(kw);
list_for_each_entry(kwl, &bind_keywords.list, list) {
for (index = 0; kwl->kw[index].kw != NULL; index++) {
if ((strncmp(kwl->kw[index].kw, kw, kwend - kw) == 0) &&
kwl->kw[index].kw[kwend-kw] == 0) {
if (kwl->kw[index].parse)
return &kwl->kw[index]; /* found it !*/
else
ret = &kwl->kw[index]; /* may be OK */
}
}
}
return ret;
}
/************************************************************************/
/* All supported ACL keywords must be declared here. */
/************************************************************************/