diff options
-rw-r--r-- | include/asterisk/config.h | 82 | ||||
-rw-r--r-- | main/config.c | 119 |
2 files changed, 201 insertions, 0 deletions
diff --git a/include/asterisk/config.h b/include/asterisk/config.h index e162d8c10..71073f43e 100644 --- a/include/asterisk/config.h +++ b/include/asterisk/config.h @@ -235,6 +235,88 @@ int config_text_file_save(const char *filename, const struct ast_config *cfg, co struct ast_config *ast_config_internal_load(const char *configfile, struct ast_config *cfg, int withcomments); +/*! \brief Support code to parse config file arguments + * + * The function ast_parse_arg() provides a generic interface to parse + * strings (e.g. numbers, network addresses and so on) in a flexible + * way, e.g. by doing proper error and bound checks, provide default + * values, and so on. + * The function (described later) takes a string as an argument, + * a set of flags to specify the result format and checks to perform, + * a pointer to the result, and optionally some additional arguments. + * It returns 0 on success, != 0 otherwise. + * + */ +enum ast_parse_flags { + /* low 4 bits of flags are used for the operand type */ + PARSE_TYPE = 0x000f, + /* numeric types, with optional default value and bound checks. + * Additional arguments are passed by value. + */ + PARSE_INT16 = 0x0001, + PARSE_INT32 = 0x0002, + PARSE_UINT16 = 0x0003, + PARSE_UINT32 = 0x0004, + /* Returns a struct sockaddr_in, with optional default value + * (passed by reference) and port handling (accept, ignore, + * require, forbid). The format is 'host.name[:port]' + */ + PARSE_INADDR = 0x0005, + + /* Other data types can be added as needed */ + + /* If PARSE_DEFAULT is set, next argument is a default value + * which is returned in case of error. The argument is passed + * by value in case of numeric types, by reference in other cases. + */ + PARSE_DEFAULT = 0x0010, /* assign default on error */ + + /* Request a range check, applicable to numbers. Two additional + * arguments are passed by value, specifying the low-high end of + * the range (inclusive). An error is returned if the value + * is outside or inside the range, respectively. + */ + PARSE_IN_RANGE = 0x0020, /* accept values inside a range */ + PARSE_OUT_RANGE = 0x0040, /* accept values outside a range */ + + /* Port handling, for sockaddr_in. accept/ignore/require/forbid + * port number after the hostname or address. + */ + PARSE_PORT_MASK = 0x0300, /* 0x000: accept port if present */ + PARSE_PORT_IGNORE = 0x0100, /* 0x100: ignore port if present */ + PARSE_PORT_REQUIRE = 0x0200, /* 0x200: require port number */ + PARSE_PORT_FORBID = 0x0300, /* 0x100: forbid port number */ +}; + +/*! \brief The argument parsing routine. + * \param arg the string to parse. It is not modified. + * \param flags combination of ast_parse_flags to specify the + * return type and additional checks. + * \param result pointer to the result. NULL is valid here, and can + * be used to perform only the validity checks. + * \param ... extra arguments are required according to flags. + * \retval 0 in case of success, != 0 otherwise. + * \retval result returns the parsed value in case of success, + * the default value in case of error, or it is left unchanged + * in case of error and no default specified. Note that in certain + * cases (e.g. sockaddr_in, with multi-field return values) some + * of the fields in result may be changed even if an error occurs. + * + * Examples of use: + * ast_parse_arg("223", PARSE_INT32|PARSE_IN_RANGE, + * &a, -1000, 1000); /* returns 0, a = 223 */ + * ast_parse_arg("22345", PARSE_INT32|PARSE_IN_RANGE|PARSE_DEFAULT, + * &a, 9999, 10, 100); /* returns 1, a = 9999 */ + * ast_parse_arg("22345ssf", PARSE_UINT32|PARSE_IN_RANGE, &b, 10, 100); + * /* returns 1, b unchanged */ + * ast_parse_arg("www.foo.biz:44", PARSE_INADDR, &sa); + * /* returns 0, sa contains address and port */ + * ast_parse_arg("www.foo.biz", PARSE_INADDR|PARSE_PORT_REQUIRE, &sa); + * /* returns 1 because port is missing, sa contains address */ + */ +int ast_parse_arg(const char *arg, enum ast_parse_flags flags, + void *result, ...); + #if defined(__cplusplus) || defined(c_plusplus) } #endif diff --git a/main/config.c b/main/config.c index 2568dc728..e9ba97fcc 100644 --- a/main/config.c +++ b/main/config.c @@ -37,6 +37,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include <errno.h> #include <time.h> #include <sys/stat.h> +#include <sys/socket.h> /* for AF_INET */ #define AST_INCLUDE_GLOB 1 #ifdef AST_INCLUDE_GLOB #if defined(__Darwin__) || defined(__CYGWIN__) @@ -1444,6 +1445,124 @@ int ast_destroy_realtime(const char *family, const char *keyfield, const char *l return res; } +/*! \brief Helper function to parse arguments + * See documentation in config.h + */ +int ast_parse_arg(const char *arg, enum ast_parse_flags flags, + void *p_result, ...) +{ + va_list ap; + int error = 0; + + va_start(ap, p_result); + switch (flags & PARSE_TYPE) { + case PARSE_INT32: + { + int32_t *result = p_result; + int32_t x, def = result ? *result : 0, + high = (int32_t)0x7fffffff, + low = (int32_t)0x80000000; + /* optional argument: first default value, then range */ + if (flags & PARSE_DEFAULT) + def = va_arg(ap, int32_t); + if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) { + /* range requested, update bounds */ + low = va_arg(ap, int32_t); + high = va_arg(ap, int32_t); + } + x = strtol(arg, NULL, 0); + error = (x < low) || (x > high); + if (flags & PARSE_OUT_RANGE) + error = !error; + if (result) + *result = error ? def : x; + ast_debug(3, + "extract int from [%s] in [%d, %d] gives [%d](%d)\n", + arg, low, high, + result ? *result : x, error); + break; + } + + case PARSE_UINT32: + { + uint32_t *result = p_result; + uint32_t x, def = result ? *result : 0, + low = 0, high = (uint32_t)~0; + /* optional argument: first default value, then range */ + if (flags & PARSE_DEFAULT) + def = va_arg(ap, uint32_t); + if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) { + /* range requested, update bounds */ + low = va_arg(ap, uint32_t); + high = va_arg(ap, uint32_t); + } + x = strtoul(arg, NULL, 0); + error = (x < low) || (x > high); + if (flags & PARSE_OUT_RANGE) + error = !error; + if (result) + *result = error ? def : x; + ast_debug(3, + "extract uint from [%s] in [%u, %u] gives [%u](%d)\n", + arg, low, high, + result ? *result : x, error); + break; + } + + case PARSE_INADDR: + { + char *port, *buf; + struct sockaddr_in _sa_buf; /* buffer for the result */ + struct sockaddr_in *sa = p_result ? + (struct sockaddr_in *)p_result : &_sa_buf; + /* default is either the supplied value or the result itself */ + struct sockaddr_in *def = (flags & PARSE_DEFAULT) ? + va_arg(ap, struct sockaddr_in *) : sa; + struct hostent *hp; + struct ast_hostent ahp; + + bzero(&_sa_buf, sizeof(_sa_buf)); /* clear buffer */ + /* duplicate the string to strip away the :port */ + port = ast_strdupa(arg); + buf = strsep(&port, ":"); + sa->sin_family = AF_INET; /* assign family */ + /* + * honor the ports flag setting, assign default value + * in case of errors or field unset. + */ + flags &= PARSE_PORT_MASK; /* the only flags left to process */ + if (port) { + if (flags == PARSE_PORT_FORBID) { + error = 1; /* port was forbidden */ + sa->sin_port = def->sin_port; + } else if (flags == PARSE_PORT_IGNORE) + sa->sin_port = def->sin_port; + else /* accept or require */ + sa->sin_port = htons(strtol(port, NULL, 0)); + } else { + sa->sin_port = def->sin_port; + if (flags == PARSE_PORT_REQUIRE) + error = 1; + } + /* Now deal with host part, even if we have errors before. */ + hp = ast_gethostbyname(buf, &ahp); + if (hp) /* resolved successfully */ + memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr)); + else { + error = 1; + sa->sin_addr = def->sin_addr; + } + ast_debug(3, + "extract inaddr from [%s] gives [%s:%d](%d)\n", + arg, ast_inet_ntoa(sa->sin_addr), + ntohs(sa->sin_port), error); + break; + } + } + va_end(ap); + return error; +} + static int config_command(int fd, int argc, char **argv) { struct ast_config_engine *eng; |