提交 7951559f 编写于 作者: G Guillaume Nault 提交者: Dmitry Kozlov

iprange: rework range parsing using u_parse_*() functions

Now that we have primitives for parsing IPv4 ranges, let's use them to
simplify parse_iprange().

Try u_parse_ip4cidr() first. In case of failure, try u_parse_ip4range().
If any of them succeeds, verify that there aren't spurious data
following the range definition. If everything is valid, either load the
range or disable the module (if the range is 0.0.0.0/0).

The diff is a bit ugly, but the implementation should be much clearer.
Signed-off-by: NGuillaume Nault <g.nault@alphalink.fr>
上级 093bacca
...@@ -29,12 +29,6 @@ static pthread_mutex_t iprange_lock = PTHREAD_MUTEX_INITIALIZER; ...@@ -29,12 +29,6 @@ static pthread_mutex_t iprange_lock = PTHREAD_MUTEX_INITIALIZER;
static bool conf_disable = false; static bool conf_disable = false;
static LIST_HEAD(client_ranges); static LIST_HEAD(client_ranges);
/* Maximum IPv4 address length with CIDR notation but no extra 0,
* e.g. "0xff.0xff.0xff.0xff/32".
*/
#define CIDR_MAXLEN 22
static void free_ranges(struct list_head *head) static void free_ranges(struct list_head *head)
{ {
struct iprange_t *range; struct iprange_t *range;
...@@ -46,115 +40,89 @@ static void free_ranges(struct list_head *head) ...@@ -46,115 +40,89 @@ static void free_ranges(struct list_head *head)
} }
} }
/* Parse a [client-ip-iprange] configuration entry.
* Ranges can be defined in CIDR notation ("192.0.2.0/24") or by specifying an
* upper bound for the last IPv4 byte, after a '-' character ("192.0.2.0-255").
* For simplicity, only mention the CIDR notation in error messages.
*/
static int parse_iprange(const char *str, struct iprange_t **range) static int parse_iprange(const char *str, struct iprange_t **range)
{ {
char ipstr[CIDR_MAXLEN + 1] = { 0 }; struct iprange_t *new_range;
struct iprange_t *_range; struct in_addr base_addr;
struct in_addr addr; const char *ptr;
char *suffix_str; uint32_t ip_min;
uint32_t ipmin; uint32_t ip_max;
uint32_t ipmax; uint8_t suffix;
bool is_cidr; size_t len;
/* Extra spaces and comments must have already been removed */
if (strpbrk(str, " \t#")) {
log_error("iprange: impossible to parse range \"%s\":"
" invalid space or comment character found\n",
str);
return -1;
}
if (!strcmp(str, "disable")) if (!strcmp(str, "disable"))
goto disable; goto disable;
strncpy(ipstr, str, CIDR_MAXLEN + 1); ptr = str;
if (ipstr[CIDR_MAXLEN] != '\0') {
log_error("iprange: impossible to parse range \"%s\":"
" line too long\n",
str);
return -1;
}
suffix_str = strpbrk(ipstr, "-/");
if (!suffix_str) {
log_error("iprange: impossible to parse range \"%s\":"
" unrecognised range format\n",
str);
return -1;
}
is_cidr = *suffix_str == '/';
*suffix_str = '\0';
++suffix_str;
if (!u_parse_ip4addr(ipstr, &addr)) {
log_error("iprange: impossible to parse range \"%s\":"
" invalid IPv4 address \"%s\"\n",
str, ipstr);
return -1;
}
ipmin = ntohl(addr.s_addr);
/* Try IPv4 CIDR notation first */
/* If is_cidr is set, range is given with CIDR notation, len = u_parse_ip4cidr(ptr, &base_addr, &suffix);
* e.g. "192.0.2.0/24". if (len) {
* If unset, range is an IP address where the last octet is replaced by uint32_t addr_hbo;
* an octet range, e.g. "192.0.2.0-255".
*/
if (is_cidr) {
long int prefix_len;
uint32_t mask; uint32_t mask;
if (u_readlong(&prefix_len, suffix_str, 0, 32)) { /* Cast to uint64_t to avoid undefined 32 bits shift on 32 bits
log_error("iprange: impossible to parse range \"%s\":" * integer if 'suffix' is 0.
" invalid CIDR prefix length \"/%s\"\n", */
str, suffix_str); mask = (uint64_t)0xffffffff << (32 - suffix);
return -1; addr_hbo = ntohl(base_addr.s_addr);
} ip_min = addr_hbo & mask;
ip_max = addr_hbo | ~mask;
if (ip_min != addr_hbo) {
struct in_addr min_addr = { .s_addr = htonl(ip_min) };
char ipbuf[INET_ADDRSTRLEN];
/* Interpret /0 as disable request */ log_warn("iprange: network %s is equivalent to %s/%hhu\n",
if (prefix_len == 0) { str, u_ip4str(&min_addr, ipbuf), suffix);
if (ipmin != INADDR_ANY)
log_warn("iprange: %s is equivalent to 0.0.0.0/0 and disables the iprange module\n",
str);
goto disable;
} }
goto addrange;
}
mask = INADDR_BROADCAST << (32 - prefix_len); /* Not an IPv4 CIDR, try the IPv4 range notation */
if (ipmin != (ipmin & mask)) { len = u_parse_ip4range(ptr, &base_addr, &suffix);
char buf[INET_ADDRSTRLEN] = { 0 }; if (len) {
ip_min = ntohl(base_addr.s_addr);
ip_max = (ip_min & 0xffffff00) | suffix;
goto addrange;
}
ipmin &= mask; log_error("iprange: parsing range \"%s\" failed:"
addr.s_addr = htonl(ipmin); " expecting an IPv4 network prefix in CIDR notation\n",
log_warn("iprange: first IP of range %s will be %s\n", str);
str, inet_ntop(AF_INET, &addr, buf,
sizeof(buf)));
}
ipmax = ipmin | ~mask; return -1;
} else {
long int max;
if (u_readlong(&max, suffix_str, ipmin & 0xff, 255)) { addrange:
log_error("iprange: impossible to parse range \"%s\":" ptr += len;
" invalid upper bound \"-%s\"\n",
str, suffix_str);
return -1;
}
ipmax = (ipmin & 0xffffff00) | max; if (!u_parse_endstr(ptr)) {
log_error("iprange: parsing range \"%s\" failed:"
" unexpected data at \"%s\"\n",
str, ptr);
return -1;
} }
_range = _malloc(sizeof(*_range)); if (ip_min == INADDR_ANY && ip_max == INADDR_BROADCAST)
if (!_range) { goto disable;
log_error("iprange: impossible to allocate range \"%s\":"
" memory allocation failed\n", str); new_range = _malloc(sizeof(*new_range));
if (!new_range) {
log_error("iprange: impossible to load range \"%s\":"
" memory allocation failed\n",
str);
return -1; return -1;
} }
_range->begin = ipmin; new_range->begin = ip_min;
_range->end = ipmax; new_range->end = ip_max;
*range = _range;
*range = new_range;
return 0; return 0;
...@@ -175,7 +143,7 @@ static bool load_ranges(struct list_head *list, const char *conf_sect) ...@@ -175,7 +143,7 @@ static bool load_ranges(struct list_head *list, const char *conf_sect)
list_for_each_entry(opt, &s->items, entry) { list_for_each_entry(opt, &s->items, entry) {
/* Ignore parsing errors, parse_iprange() already logs suitable /* Ignore parsing errors, parse_iprange() already logs suitable
* error message. * error messages.
*/ */
if (parse_iprange(opt->name, &r) < 0) if (parse_iprange(opt->name, &r) < 0)
continue; continue;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册