diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2014-09-25 08:42:57 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2014-09-25 08:53:52 +0300 |
commit | c711cf9ecbbe0df7f7f27cec789a75c0b4e39040 (patch) | |
tree | 6ca6ae15720c1da84f30e430ef39964867061b24 /src | |
parent | 82658ac36ae527e7d1cd949fb379ef26caad31cb (diff) | |
download | grecs-c711cf9ecbbe0df7f7f27cec789a75c0b4e39040.tar.gz grecs-c711cf9ecbbe0df7f7f27cec789a75c0b4e39040.tar.bz2 |
Add support for IPv6, multiple addresses and CIDRs.
* am/grecs.m4 (GRECS_SETUP): New option sockaddr-list.
Build grecs.h at configure stage.
* src/Make.am (GRECS_SRC): Add cidr.c and ipstr.c
[GRECS_COND_SOCKADDR_LIST] (GRECS_SRC): Add sockaddr.c
Remove rule for building grecs.h (see above).
* src/cidr.c: New file.
* src/ipstr.c: New file.
* src/sockaddr.c: New file.
* src/format.c (grecs_data_type_string): Change spelling of sockaddr.
* src/grecs-lex.l: Treat square brackets as part of word.
* src/grecs.hin (GRECS_SOCKADDR_LIST): New define.
[GRECS_SOCKADDR_LIST] (grecs_sockaddr): New member: next.
(grecs_str_is_ipv4,grecs_str_is_ipv6)
(grecs_str_is_ipaddr,grecs_str_is_num): New functions.
[GRECS_SOCKADDR_LIST] (grecs_sockaddr_hints): New struct.
[GRECS_SOCKADDR_LIST] (grecs_sockaddr_new,grecs_sockaddr_free)
(grecs_str_to_sockaddr): New protos.
(grecs_cidr): New struct.
(grecs_str_to_cidr,grecs_sockaddr_to_cidr)
(grecs_sockadd_cidr_match): New protos.
* src/tree.c [GRECS_SOCKADDR_LIST] (grecs_sockaddr_hints): New global.
[!GRECS_SOCKADDR_LIST] (string_to_sockaddr): Retain for backward
compatibility.
[GRECS_SOCKADDR_LIST] (grecs_string_convert): Use grecs_str_to_sockaddr.
(grecs_prop_tab): Add entry for grecs_type_cidr
Diffstat (limited to 'src')
-rw-r--r-- | src/Make.am | 10 | ||||
-rw-r--r-- | src/cidr.c | 214 | ||||
-rw-r--r-- | src/format.c | 2 | ||||
-rw-r--r-- | src/grecs-lex.l | 4 | ||||
-rw-r--r-- | src/grecs.hin | 48 | ||||
-rw-r--r-- | src/ipstr.c | 88 | ||||
-rw-r--r-- | src/sockaddr.c | 280 | ||||
-rw-r--r-- | src/tree.c | 30 |
8 files changed, 662 insertions, 14 deletions
diff --git a/src/Make.am b/src/Make.am index 7ded902..fe94d42 100644 --- a/src/Make.am +++ b/src/Make.am @@ -41,10 +41,12 @@ endif GRECS_SRC = \ asprintf.c\ + cidr.c\ diag.c\ format.c\ grecs-gram.y\ grecs-lex.l\ + ipstr.c\ join.c\ lineacc.c\ list.c\ @@ -67,6 +69,10 @@ GRECS_SRC = \ $(GRECS_PARSER_GIT)\ $(GRECS_PARSER_META1) +if GRECS_COND_SOCKADDR_LIST + GRECS_SRC += sockaddr.c +endif + noinst_HEADERS = grecs-locus.h EXTRA_DIST=\ @@ -80,10 +86,6 @@ EXTRA_DIST=\ BUILT_SOURCES=grecs.h -grecs.h: $(abs_srcdir)/grecs.hin - $(AM_V_GEN)sed 's/@''GRECS_TREE_API''@/@GRECS_TREE_API@/g' \ - $(abs_srcdir)/grecs.hin > grecs.h - AM_CPPFLAGS = \ -I$(srcdir)\ -I$(top_srcdir)/@GRECS_SUBDIR@\ diff --git a/src/cidr.c b/src/cidr.c new file mode 100644 index 0000000..d21cc44 --- /dev/null +++ b/src/cidr.c @@ -0,0 +1,214 @@ +/* grecs - Gray's Extensible Configuration System + Copyright (C) 2007-2014 Sergey Poznyakoff + + Grecs 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 3 of the License, or (at your + option) any later version. + + Grecs is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Grecs. If not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <sys/types.h> +#include <sys/un.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include "grecs.h" + +static void +uint32_to_bytes (unsigned char *bytes, uint32_t u) +{ + int i; + + for (i = 0; i < 4; i++) { + bytes[i] = u & 0xff; + u >>= 8; + } +} + +int +grecs_inaddr_to_bytes(int af, void *buf, unsigned char *bytes) +{ + uint32_t u; + + switch (af) { + case AF_INET: + memcpy(&u, buf, sizeof u); + uint32_to_bytes(bytes, u); + return 4; + + case AF_INET6: + memcpy(bytes, buf, 16); + return 16; + } + return 0; +} + +int +grecs_sockaddr_to_bytes(unsigned char *bytes, struct sockaddr const *sa) +{ + switch (sa->sa_family) { + case AF_INET: + uint32_to_bytes(bytes, + ((struct sockaddr_in*)sa)->sin_addr.s_addr); + return 4; + + case AF_INET6: + memcpy(bytes, &((struct sockaddr_in6*)sa)->sin6_addr, 16); + return 16; + } + return 0; +} + +int +grecs_sockaddr_to_cidr(struct grecs_cidr *cidr, const struct sockaddr *sa) +{ + unsigned char address[GRECS_INADDR_BYTES]; + int len; + int i; + + len = grecs_sockaddr_to_bytes(address, sa); + if (len == 0) + return -1; + cidr->family = sa->sa_family; + cidr->len = len; + memcpy(cidr->address, address, sizeof(cidr->address)); + for (i = 0; i < GRECS_INADDR_BYTES; i++) + cidr->netmask[i] = 0xff; + return 0; +} + +static void +masklen_to_netmask(unsigned char *buf, size_t len, size_t masklen) +{ + int i, cnt; + + cnt = masklen / 8; + for (i = 0; i < cnt; i++) + buf[i] = 0xff; + if (i == GRECS_INADDR_BYTES) + return; + cnt = 8 - masklen % 8; + buf[i++] = (0xff >> cnt) << cnt; + for (; i < GRECS_INADDR_BYTES; i++) + buf[i] = 0; +} + +int +grecs_str_to_cidr(struct grecs_cidr *pcidr, const char *str, + grecs_locus_t const *locus) +{ + int rc; + char ipbuf[41]; + struct grecs_cidr cidr; + char *p; + size_t len; + union { + struct in_addr in; + struct in6_addr in6; + } inaddr; + + p = strchr(str, '/'); + if (p) + len = p - str; + else + len = strlen(str); + + if (len > sizeof(ipbuf)) { + grecs_error(locus, 0, _("network length too big: %s"), str); + return -1; + } + + memcpy(ipbuf, str, len); + ipbuf[len] = 0; + + if (grecs_str_is_ipv4(ipbuf)) + cidr.family = AF_INET; + else if (grecs_str_is_ipv6(ipbuf)) + cidr.family = AF_INET6; + else { + grecs_error(locus, 0, _("unrecognized network family: %s"), + str); + return -1; + } + + rc = inet_pton(cidr.family, ipbuf, &inaddr); + if (rc == -1) { + grecs_error(locus, 0, _("unrecognized network family: %s"), + str); + return -1; + } else if (rc != 1) { + grecs_error(locus, 0, _("invalid network: %s"), + str); + return -1; + } + + cidr.len = grecs_inaddr_to_bytes(cidr.family, &inaddr, cidr.address); + if (cidr.len == 0) { + grecs_error(locus, 0, _("unrecognized network family: %s"), + str); + return -1; + } + + if (p) { + char *end; + unsigned long masklen; + + p++; + + masklen = strtoul(p, &end, 10); + if (*end == 0) + masklen_to_netmask(cidr.netmask, cidr.len, masklen); + else if ((cidr.family == AF_INET && grecs_str_is_ipv4(p)) + || (cidr.family == AF_INET6 + && grecs_str_is_ipv6(ipbuf))) { + rc = inet_pton(cidr.family, p, &inaddr); + if (rc != 1) { + grecs_error(locus, 0, _("invalid network mask: %s"), + str); + return -1; + } + grecs_inaddr_to_bytes(cidr.family, &inaddr, + cidr.netmask); + } else { + grecs_error(locus, 0, _("invalid network mask: %s"), + str); + return -1; + } + } else + masklen_to_netmask(cidr.netmask, cidr.len, cidr.len * 8); + + memcpy(pcidr, &cidr, sizeof(*pcidr)); + return 0; +} + +int +grecs_cidr_match(struct grecs_cidr *a, struct grecs_cidr *b) +{ + int i; + + if (a->family != b->family) + return 1; + for (i = 0; i < a->len; i++) { + if (a->address[i] != (b->address[i] & a->netmask[i])) + return 1; + } + return 0; +} + +int +grecs_sockadd_cidr_match(struct sockaddr *sa, struct grecs_cidr *cidr) +{ + struct grecs_cidr t; + if (grecs_sockaddr_to_cidr(&t, sa)) + return 1; + return grecs_cidr_match(cidr, &t); +} diff --git a/src/format.c b/src/format.c index c554cc9..d010729 100644 --- a/src/format.c +++ b/src/format.c @@ -61,7 +61,7 @@ grecs_data_type_string(enum grecs_data_type type) return "hostname"; case grecs_type_sockaddr: - return "sock-addr"; + return "sockaddr"; case grecs_type_section: return "section"; diff --git a/src/grecs-lex.l b/src/grecs-lex.l index cbc8f8b..329c725 100644 --- a/src/grecs-lex.l +++ b/src/grecs-lex.l @@ -6,7 +6,7 @@ } %{ /* grecs - Gray's Extensible Configuration System - Copyright (C) 2007-2012 Sergey Poznyakoff + Copyright (C) 2007-2014 Sergey Poznyakoff Grecs is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the @@ -106,7 +106,7 @@ P [1-9][0-9]* /* Identifiers */ <INITIAL>{ID} return ident(); /* Strings */ -[a-zA-Z0-9_\.\*/:@-]([a-zA-Z0-9_\./:@-][a-zA-Z0-9_\.\*/:@-]*)? { +[a-zA-Z0-9_\.\*/:@\[\]-]([a-zA-Z0-9_\./:@\[\]-][a-zA-Z0-9_\.\*/:@\[\]-]*)? { grecs_line_begin(); grecs_line_add(yytext, yyleng); yylval.string = grecs_line_finish(); diff --git a/src/grecs.hin b/src/grecs.hin index 8bf0826..677a237 100644 --- a/src/grecs.hin +++ b/src/grecs.hin @@ -41,6 +41,7 @@ #define GRECS_VERSION_MINOR 0 #define GRECS_TREE_API @GRECS_TREE_API@ +#define GRECS_SOCKADDR_LIST @GRECS_SOCKADDR_LIST@ struct grecs_version_info { const char *package; @@ -191,6 +192,9 @@ struct grecs_keyword { }; struct grecs_sockaddr { +#if GRECS_SOCKADDR_LIST + struct grecs_sockaddr *next; +#endif int len; struct sockaddr *sa; }; @@ -524,4 +528,48 @@ void grecs_tree_sort(struct grecs_node *node, struct grecs_node *grecs_tree_first_node(struct grecs_node *tree); struct grecs_node *grecs_next_node(struct grecs_node *node); +int grecs_str_is_ipv4(const char *addr); +int grecs_str_is_num(const char *s); +int grecs_str_is_ipv6(const char *addr); +int grecs_str_is_num(const char *s); +int grecs_str_is_ipaddr(const char *addr); + +#if GRECS_SOCKADDR_LIST + +#define GRECS_AH_PASSIVE 0x01 +#define GRECS_HINT_SERVICE 0x02 +#define GRECS_HINT_PORT 0x04 + +struct grecs_sockaddr_hints { + int flags; + char *service; + unsigned short port; +}; + +extern struct grecs_sockaddr_hints *grecs_sockaddr_hints; + +struct grecs_sockaddr *grecs_sockaddr_new(size_t s); +void grecs_sockaddr_free(struct grecs_sockaddr *p); + +int grecs_str_to_sockaddr(struct grecs_sockaddr **sap, + const char *arg, struct grecs_sockaddr_hints *gh, + grecs_locus_t const *locus); +#endif + +#define GRECS_INADDR_BYTES 16 + +struct grecs_cidr +{ + int family; + int len; + unsigned char address[GRECS_INADDR_BYTES]; + unsigned char netmask[GRECS_INADDR_BYTES]; +}; + +int grecs_str_to_cidr(struct grecs_cidr *pcidr, const char *str, + grecs_locus_t const *locus); + +int grecs_sockaddr_to_cidr(struct grecs_cidr *cidr, const struct sockaddr *sa); +int grecs_sockadd_cidr_match(struct sockaddr *sa, struct grecs_cidr *cidr); + #endif diff --git a/src/ipstr.c b/src/ipstr.c new file mode 100644 index 0000000..5676301 --- /dev/null +++ b/src/ipstr.c @@ -0,0 +1,88 @@ +/* grecs - Gray's Extensible Configuration System + Copyright (C) 2007-2014 Sergey Poznyakoff + + Grecs 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 3 of the License, or (at your + option) any later version. + + Grecs is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Grecs. If not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <string.h> +#include <ctype.h> +#include "grecs.h" + +int +grecs_str_is_ipv4(const char *addr) +{ + int dot_count = 0; + int digit_count = 0; + + for (; *addr; addr++) { + if (!isascii(*addr)) + return 0; + if (*addr == '.') { + if (++dot_count > 3) + break; + digit_count = 0; + } else if (!(isdigit(*addr) && ++digit_count <= 3)) + return 0; + } + + return (dot_count == 3); +} + +int +grecs_str_is_ipv6(const char *addr) +{ + int col_count = 0; /* Number of colons */ + int dcol = 0; /* Did we encounter a double-colon? */ + int dig_count = 0; /* Number of digits in the last group */ + + for (; *addr; addr++) { + if (!isascii(*addr)) + return 0; + else if (isxdigit(*addr)) { + if (++dig_count > 4) + return 0; + } else if (*addr == ':') { + if (col_count && dig_count == 0 && ++dcol > 1) + return 0; + if (++col_count > 7) + return 0; + dig_count = 0; + } else + return 0; + } + + return (col_count == 7 || dcol); +} + +int +grecs_str_is_num(const char *s) +{ + for (; *s; ++s) + if (!isdigit(*s)) + return 0; + return 1; +} + +int +grecs_str_is_ipaddr(const char *addr) +{ + if (strchr (addr, '.')) + return grecs_str_is_ipv4(addr); + else if (strchr (addr, ':')) + return grecs_str_is_ipv6(addr); + return 0; +} + diff --git a/src/sockaddr.c b/src/sockaddr.c new file mode 100644 index 0000000..28f99b1 --- /dev/null +++ b/src/sockaddr.c @@ -0,0 +1,280 @@ +/* grecs - Gray's Extensible Configuration System + Copyright (C) 2007-2014 Sergey Poznyakoff + + Grecs 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 3 of the License, or (at your + option) any later version. + + Grecs is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Grecs. If not, see <http://www.gnu.org/licenses/>. */ + +/* Network-specific functions */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <string.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <errno.h> +#include "grecs.h" + +struct grecs_sockaddr * +grecs_sockaddr_new(size_t s) +{ + struct grecs_sockaddr *sp = grecs_malloc(sizeof(*sp)); + sp->next = NULL; + sp->sa = grecs_zalloc(s); + sp->len = s; + return sp; +} + +void +grecs_sockaddr_free(struct grecs_sockaddr *p) +{ + while (p) { + struct grecs_sockaddr *next = p->next; + free(p->sa); + free(p); + p = next; + } +} + +static int +parse_unix(struct grecs_sockaddr **ret, const char *arg, const char *addrstr, + struct grecs_sockaddr_hints *gh, grecs_locus_t const *locus) +{ + struct sockaddr_un *s_un; + size_t slen = strlen(addrstr); + struct grecs_sockaddr *sp; + + if (slen >= sizeof s_un->sun_path) { + grecs_error(locus, 0, _("socket path name too long: %s"), arg); + return -1; + } + + sp = grecs_sockaddr_new(sizeof(s_un[0])); + s_un = (struct sockaddr_un *) sp->sa; + s_un->sun_family = AF_UNIX; + strcpy(s_un->sun_path, addrstr); + + *ret = sp; + return 0; +} + +static int +parse_inet(struct grecs_sockaddr **ret, + int family, const char *arg, const char *addrstr, + struct grecs_sockaddr_hints *gh, grecs_locus_t const *locus) +{ + int rc; + struct addrinfo hints; + struct addrinfo *res, *ap; + const char *node = NULL; + char *nodebuf = NULL; + const char *service = NULL; + struct grecs_sockaddr *head = NULL, *tail = NULL; + char portbuf[64]; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + + if ((family == AF_INET6 || family == AF_UNSPEC) + && addrstr[0] == '[') { + char *p = strchr(addrstr + 1, ']'); + if (p && p > addrstr + 1) { + size_t len; + + addrstr++; + len = p - addrstr; + nodebuf = grecs_malloc(len + 1); + memcpy(nodebuf, addrstr, len); + nodebuf[len] = 0; + node = nodebuf; + service = p + 1; + family = AF_INET6; + } else + service = strchr(addrstr, ':'); + } else + service = strrchr(addrstr, ':'); + + if (service && *service) { + if (*service != ':') { + grecs_error(locus, 0, + _("%s: garbage near %s"), arg, service); + return -1; + } + service++; + } + + if (!node) { + if (service) { + size_t len = service - addrstr - 1; + + if (len == 0) + node = NULL; + else { + nodebuf = grecs_malloc(len + 1); + memcpy(nodebuf, addrstr, len); + nodebuf[len] = 0; + node = nodebuf; + } + } else { + if (grecs_str_is_ipaddr(addrstr)) + node = addrstr; + else if (grecs_str_is_num(addrstr)) { + service = addrstr; + hints.ai_flags |= AI_NUMERICSERV; + } + } + } + + if (!service || !*service) { + if (!node && addrstr[0]) + node = addrstr; + if (gh->flags & GRECS_HINT_SERVICE) { + service = gh->service; + } else if (gh->flags & GRECS_HINT_PORT) { + snprintf(portbuf, sizeof portbuf, "%hu", gh->port); + service = portbuf; + hints.ai_flags |= AI_NUMERICSERV; + } else { + grecs_error(locus, 0, + _("service not specified: %s"), arg); + return -1; + } + } + + if (!node) { + if (gh->flags & GRECS_AH_PASSIVE) + hints.ai_flags |= AI_PASSIVE; + } + + rc = getaddrinfo(node, service, &hints, &res); + free(nodebuf); + switch (rc) { + case 0: + break; + case EAI_SYSTEM: + grecs_error(locus, 0, + _("%s: cannot parse address: %s"), + arg, strerror(errno)); + break; + case EAI_BADFLAGS: + case EAI_SOCKTYPE: + grecs_error(locus, 0, + "%s:%d: internal error converting %s", + __FILE__,__LINE__,arg); + break; + case EAI_MEMORY: + grecs_alloc_die(); + default: + grecs_error(locus, 0, + "%s: %s", arg, gai_strerror(rc)); + return -1; + } + + for (ap = res; ap; ap = ap->ai_next) { + if (family == AF_UNSPEC || ap->ai_addr->sa_family == family) { + struct grecs_sockaddr *sp = + grecs_sockaddr_new(ap->ai_addrlen); + memcpy(sp->sa, ap->ai_addr, ap->ai_addrlen); + sp->len = ap->ai_addrlen; + if (!head) + head = sp; + else + tail->next = sp; + tail = sp; + } + } + freeaddrinfo(res); + *ret = head; + return 0; +} + +static int +parse_inet4(struct grecs_sockaddr **ret, const char *arg, const char *addrstr, + struct grecs_sockaddr_hints *gh, grecs_locus_t const *locus) +{ + return parse_inet(ret, AF_INET, arg, addrstr, gh, locus); +} + +static int +parse_inet6(struct grecs_sockaddr **ret, const char *arg, const char *addrstr, + struct grecs_sockaddr_hints *gh, grecs_locus_t const *locus) +{ + return parse_inet(ret, AF_INET6, arg, addrstr, gh, locus); +} + +struct schemetab { + const char *scheme; + size_t len; + int (*parser)(struct grecs_sockaddr **ret, + const char *arg, const char *addr, + struct grecs_sockaddr_hints *gh, + grecs_locus_t const *locus); +}; + +struct schemetab schemetab[] = { + { "inet", 4, parse_inet4 }, + { "inet4", 5, parse_inet4 }, + { "inet6", 5, parse_inet6 }, + { "unix", 4, parse_unix }, + { NULL } +}; + +int +grecs_str_to_sockaddr(struct grecs_sockaddr **sap, + const char *arg, struct grecs_sockaddr_hints *gh, + grecs_locus_t const *locus) +{ + char *p; + struct grecs_sockaddr_hints ghints; + + if (!gh) { + memset(&ghints, 0, sizeof(ghints)); + if (grecs_default_port) { + ghints.flags = GRECS_HINT_PORT; + ghints.port = ntohs(grecs_default_port); + } + gh = &ghints; + } + + p = strchr(arg, ':'); + if (p && p > arg && p[1] == '/' && p[2] == '/') { + size_t len = p - arg; + struct schemetab *sp; + + for (sp = schemetab; sp->scheme; sp++) + if (len == sp->len && + memcmp(arg, sp->scheme, len) == 0) + return sp->parser(sap, arg, p + 3, gh, locus); + grecs_error(locus, 0, + _("unknown or unsupported scheme: %s"), arg); + return -1; + } + + if (arg[0] == '/') + return parse_unix(sap, arg, arg, gh, locus); + else if (strlen(arg) > 5 && memcmp(arg, "unix:", 5) == 0) { + if (arg[5] != '/') + grecs_error(locus, 0, + _("%s: UNIX socket must be an absolute file name"), + arg); + return parse_unix(sap, arg, arg + 5, gh, locus); + } + + return parse_inet(sap, AF_UNSPEC, arg, arg, gh, locus); +} @@ -1,5 +1,5 @@ /* grecs - Gray's Extensible Configuration System - Copyright (C) 2007-2012 Sergey Poznyakoff + Copyright (C) 2007-2014 Sergey Poznyakoff Grecs is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the @@ -30,6 +30,8 @@ #include <string.h> #include "grecs.h" +struct grecs_sockaddr_hints *grecs_sockaddr_hints; + void grecs_value_free_content(struct grecs_value *val) { @@ -347,6 +349,7 @@ string_to_host(struct in_addr *in, const char *string, return 0; } +#if !GRECS_SOCKADDR_LIST static int string_to_sockaddr(struct grecs_sockaddr *sp, const char *string, grecs_locus_t const *locus) @@ -425,7 +428,7 @@ string_to_sockaddr(struct grecs_sockaddr *sp, const char *string, } return 0; } - +#endif /* The TYPE_* defines come from gnulib's intprops.h */ @@ -615,14 +618,17 @@ grecs_string_convert(void *target, enum grecs_data_type type, break; case grecs_type_sockaddr: +#if GRECS_SOCKADDR_LIST + return grecs_str_to_sockaddr((struct grecs_sockaddr **)target, + string, grecs_sockaddr_hints, + locus); +#else return string_to_sockaddr((struct grecs_sockaddr *)target, string, locus); +#endif - /* FIXME: */ case grecs_type_cidr: - grecs_error(locus, 0, - _("INTERNAL ERROR at %s:%d"), __FILE__, __LINE__); - abort(); + return grecs_str_to_cidr((struct grecs_cidr *)target, string, locus); case grecs_type_section: grecs_error(locus, 0, @@ -663,6 +669,16 @@ DECL_NUMCMP(time_t) __DECL_NUMCMP(in_addr, struct in_addr) __DECL_NUMCMP(grecs_sockaddr, struct grecs_sockaddr) +static int +cidr_cmp(const void *elt1, const void *elt2) +{ + struct grecs_cidr const *cp1 = elt1, *cp2 = elt2; + return !(cp1->family == cp2->family + && cp1->len == cp2->len + && memcmp(cp1->address, cp2->address, cp1->len) == 0 + && memcmp(cp1->netmask, cp2->netmask, cp1->len) == 0); +} + struct grecs_prop grecs_prop_tab[] = { { 0, NULL }, /* grecs_type_void */ { sizeof(char*), string_cmp }, /* grecs_type_string */ @@ -680,7 +696,7 @@ struct grecs_prop grecs_prop_tab[] = { { sizeof(time_t), NUMCMP(time_t) }, /* grecs_type_time */ { sizeof(int), NUMCMP(int) }, /* grecs_type_bool */ { sizeof(struct in_addr), NUMCMP(in_addr) }, /* grecs_type_ipv4 */ - { 0, NULL }, /* FIXME: grecs_type_cidr */ + { sizeof(struct grecs_cidr), cidr_cmp }, /* grecs_type_cidr */ { sizeof(struct in_addr), NUMCMP(in_addr) }, /* grecs_type_host */ { sizeof(struct grecs_sockaddr), NUMCMP(grecs_sockaddr) }, /* grecs_type_sockaddr */ |