summaryrefslogtreecommitdiffabout
path: root/src
authorSergey Poznyakoff <gray@gnu.org>2014-09-25 05:42:57 (GMT)
committer Sergey Poznyakoff <gray@gnu.org>2014-09-25 05:53:52 (GMT)
commitc711cf9ecbbe0df7f7f27cec789a75c0b4e39040 (patch) (side-by-side diff)
tree6ca6ae15720c1da84f30e430ef39964867061b24 /src
parent82658ac36ae527e7d1cd949fb379ef26caad31cb (diff)
downloadgrecs-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') (more/less context) (ignore whitespace changes)
-rw-r--r--src/Make.am10
-rw-r--r--src/cidr.c214
-rw-r--r--src/format.c2
-rw-r--r--src/grecs-lex.l4
-rw-r--r--src/grecs.hin48
-rw-r--r--src/ipstr.c88
-rw-r--r--src/sockaddr.c280
-rw-r--r--src/tree.c36
8 files changed, 665 insertions, 17 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
--- a/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;
};
@@ -523,5 +527,49 @@ 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
--- a/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
--- a/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);
+}
diff --git a/src/tree.c b/src/tree.c
index a0f8736..c0c1920 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -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);
-
- /* FIXME: */
+#endif
+
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,9 +669,19 @@ 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 */
+ { 0, NULL }, /* grecs_type_void */
+ { sizeof(char*), string_cmp }, /* grecs_type_string */
{ sizeof(short), NUMCMP(short) }, /* grecs_type_short */
{ sizeof(unsigned short), NUMCMP(short) }, /* grecs_type_ushort */
{ sizeof(int), NUMCMP(int) }, /* grecs_type_int */
@@ -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 */

Return to:

Send suggestions and report system problems to the System administrator.