diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2014-12-26 01:22:26 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2014-12-26 01:22:26 +0200 |
commit | 75e73954ab1dc5fee42255a13457b2a092cd86e3 (patch) | |
tree | 94a65b9d10bd8c618707870b3be4f5043a660dd8 | |
parent | a03c7a67580ae57a93f6f2ea85485d612a00c46c (diff) | |
parent | add57c075c6f747a81c142ab48d59106de822664 (diff) | |
download | grecs-75e73954ab1dc5fee42255a13457b2a092cd86e3.tar.gz grecs-75e73954ab1dc5fee42255a13457b2a092cd86e3.tar.bz2 |
Merge branch 'master' into clparse
-rw-r--r-- | am/grecs.m4 | 12 | ||||
-rw-r--r-- | doc/grecs-syntax.texi | 25 | ||||
-rw-r--r-- | doc/grecs_config.5 | 27 | ||||
-rw-r--r-- | src/Make.am | 10 | ||||
-rw-r--r-- | src/cidr.c | 215 | ||||
-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/preproc.c | 63 | ||||
-rw-r--r-- | src/sockaddr.c | 280 | ||||
-rw-r--r-- | src/tree.c | 36 | ||||
-rw-r--r-- | tests/Makefile.am | 6 | ||||
-rw-r--r-- | tests/incl00.at | 39 | ||||
-rw-r--r-- | tests/incl01.at | 41 | ||||
-rw-r--r-- | tests/incl02.at | 51 | ||||
-rw-r--r-- | tests/incl03.at | 45 | ||||
-rw-r--r-- | tests/testsuite.at | 8 |
18 files changed, 957 insertions, 43 deletions
diff --git a/am/grecs.m4 b/am/grecs.m4 index 2787563..1c1a412 100644 --- a/am/grecs.m4 +++ b/am/grecs.m4 @@ -68,6 +68,8 @@ AC_DEFUN([_GRECS_SET_OPTIONS], # tree-api Use alternative signature of callback functions, # passing a pointer to grecs_node_t as an argument, # instead of pointers to the value and locus. +# sockaddr-list Sockaddr type keeps a singly-linked list of addresses +# returned by getaddrinfo. # # The pp-setup-file argument supplies the pathname of the preprocessor # setup file in the source tree. It is ignored if std-pp-setup option is @@ -175,6 +177,12 @@ AC_DEFUN([GRECS_SETUP],[ AM_CONDITIONAL([GRECS_COND_INSTALLHEADERS], _GRECS_IF_OPTION_SET([install-headers],[true],[false])) + AM_CONDITIONAL([GRECS_COND_SOCKADDR_LIST], + _GRECS_IF_OPTION_SET([sockaddr-list],[true],[false])) + AC_SUBST([GRECS_SOCKADDR_LIST]) + _GRECS_IF_OPTION_SET([sockaddr-list],[GRECS_SOCKADDR_LIST=1], + [GRECS_SOCKADDR_LIST=0]) + AC_SUBST([GRECS_TREE_API]) _GRECS_IF_OPTION_SET([tree-api],[GRECS_TREE_API=1],[GRECS_TREE_API=0]) @@ -187,7 +195,7 @@ AC_DEFUN([GRECS_SETUP],[ AC_SUBST([GRECS_CHANGELOG]) AC_SUBST([GRECS_DISTCK_AT]) AC_SUBST([GRECS_README]) - AC_SUBST([GRECS_INCLUDES],['-I$(top_srcdir)/]grecsdir[src]') + AC_SUBST([GRECS_INCLUDES],['-I$(top_srcdir)/]grecsdir[src] [-I$(top_builddir)/]grecsdir[src]') AC_SUBST([GRECS_HOST_PROJECT_INCLUDES]) AC_SUBST([GRECS_DISTDOC]) AC_SUBST([GRECS_INCLUDE_DIR],['$(pkgincludedir)']) @@ -215,6 +223,6 @@ AC_DEFUN([GRECS_SETUP],[ _GRECS_IF_OPTION_SET([syntax-doc],[GRECS_DISTDOC=doc/grecs-syntax.texi]) AC_CONFIG_FILES(grecsdir[src/Makefile]:grecsdir[src/Make-static.in]) ]) - AC_CONFIG_FILES(grecsdir[Makefile]) + AC_CONFIG_FILES(grecsdir[Makefile] grecsdir[src/grecs.h]:grecsdir[src/grecs.hin]) m4_popdef([grecsdir]) ]) diff --git a/doc/grecs-syntax.texi b/doc/grecs-syntax.texi index c10cab6..da59d03 100644 --- a/doc/grecs-syntax.texi +++ b/doc/grecs-syntax.texi @@ -1,5 +1,5 @@ @c This file is part of grecs - Gray's Extensible Configuration System -@c Copyright (C) 2007, 2009-2012 Sergey Poznyakoff +@c Copyright (C) 2007, 2009-2014 Sergey Poznyakoff @c @c Grecs is free software; you can redistribute it and/or modify @c it under the terms of the GNU General Public License as published by @@ -82,12 +82,23 @@ next physical newline character. @kwindex #include @item #include <@var{file}> @itemx #include @var{file} -Include the contents of the file @var{file}. If @var{file} is an -absolute file name, both forms are equivalent. Otherwise, the form -with angle brackets searches for the file in the @dfn{include -search path}, while the second one looks for it in the current working -directory first, and, if not found there, in the include search -path. +Include the contents of the file @var{file}. There are three possible +use cases. + +If @var{file} is an absolute file name, the named file is included. +An error message will be issued if it does not exist. + +If @var{file} contains wildcard characters (@samp{*}, @samp{[}, +@samp{]} or @samp{?}), it is interpreted as shell globbing pattern and +all files matching that pattern are included, in lexicographical +order. If no files match the pattern, the statement is silently +ignored. + +Otherwise, the form with angle brackets searches for file in the +@dfn{include search path}, while the second one looks for it in the +current working directory first, and, if not found there, in the +include search path. If the file is not found, an error message will +be issued. The default include search path is: diff --git a/doc/grecs_config.5 b/doc/grecs_config.5 index 80f7bcc..82633d5 100644 --- a/doc/grecs_config.5 +++ b/doc/grecs_config.5 @@ -1,5 +1,5 @@ .\" This file is part of grecs -*- nroff -*- -.\" Copyright (C) 2007, 2009-2012 Sergey Poznyakoff +.\" Copyright (C) 2007, 2009-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 @@ -14,7 +14,7 @@ .\" You should have received a copy of the GNU General Public License .\" along with Grecs. If not, see <http://www.gnu.org/licenses/>. .\" -.TH GRECS_CONFIG 3 "May 4, 2011" "GRECS" "Grecs User Reference" +.TH GRECS_CONFIG 3 "December 25, 2014" "GRECS" "Grecs User Reference" .SH NAME \fBGrecs\fR configuration file syntax .SH DESCRIPTION @@ -64,12 +64,23 @@ sign and end with the next physical newline character. .TP .BR "#include " "file" .PD -Include the contents of the file \fIfile\fR. If it is an -absolute file name, both forms are equivalent. Otherwise, the form -with angle brackets searches for the file in the \fIinclude -search path\fR, while the second one looks for it in the current working -directory first, and, if not found there, in the include search -path. +Include the contents of the file \fIfile\fR. There are three possible +use cases. + +If \fIfile\fR is an absolute file name, the named file is included. +An error message will be issued if it does not exist. + +If \fIfile\fR contains wildcard characters (\fB*\fR, \fB[\fR, +\fB]\fR or \fB?\fR), it is interpreted as shell globbing pattern and +all files matching that pattern are included, in lexicographical +order. If no files match the pattern, the statement is silently +ignored. + +Otherwise, the form with angle brackets searches for file in the +\fIinclude search path\fR, while the second one looks for it in the +current working directory first, and, if not found there, in the +include search path. If the file is not found, an error message will +be issued. .sp The default include search path is: .sp 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..0f75476 --- /dev/null +++ b/src/cidr.c @@ -0,0 +1,215 @@ +/* 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/socket.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 --- /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/preproc.c b/src/preproc.c index ce32a29..56dcc22 100644 --- a/src/preproc.c +++ b/src/preproc.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 @@ -28,6 +28,7 @@ #include <string.h> #include <errno.h> #include <signal.h> +#include <glob.h> #include <wordsplit.h> @@ -62,6 +63,9 @@ static size_t bufsize; static char *putback_buffer; static size_t putback_size; static size_t putback_max; +static glob_t include_glob; +static size_t include_pos; +static int include_once; static int push_source (const char *name, int once); static int pop_source (void); @@ -287,7 +291,9 @@ static int pp_list_find(struct grecs_list *list, struct file_data *dptr) { struct grecs_list_entry *ep; - + + if (!list) + return 0; for (ep = list->head; !dptr->found && ep; ep = ep->next) { const char *dir = ep->data; size_t size = strlen (dir) + 1 + dptr->namelen + 1; @@ -395,6 +401,13 @@ incl_compare(void const *data1, void const *data2) } static int +incl_copy(void *dst, void *src) +{ + memcpy(dst, src, sizeof(struct input_file_ident)); + return 0; +} + +static int source_lookup(struct stat *st) { struct input_file_ident key; @@ -405,7 +418,7 @@ source_lookup(struct stat *st) sizeof(struct input_file_ident), incl_hasher, incl_compare, - NULL, + incl_copy, NULL,/*FIXME: alloc*/ NULL); if (!incl_sources) @@ -505,6 +518,14 @@ pop_source() grecs_free(context_stack); context_stack = ctx; + if (include_pos < include_glob.gl_pathc) { + push_source(include_glob.gl_pathv[include_pos++], include_once); + return 0; + } else if (include_glob.gl_pathc) { + globfree(&include_glob); + include_pos = include_glob.gl_pathc = 0; + } + if (!context_stack) { if (grecs_grecs__flex_debug) fprintf(stderr, "End of input\n"); @@ -552,6 +573,16 @@ grecs_find_include_file(const char *name, int allow_cwd) } static int +isglob(const char *s) +{ + for (; *s; s++) { + if (strchr("*?[", *s)) + return 1; + } + return 0; +} + +static int parse_include(const char *text, int once) { struct wordsplit ws; @@ -579,17 +610,35 @@ parse_include(const char *text, int once) else allow_cwd = 1; - if (p[0] != '/') { - p = grecs_find_include_file(p, allow_cwd); + if (isglob(p)) { + switch (glob(p, 0, NULL, &include_glob)) { + case 0: + include_pos = 0; + include_once = once; + break; + case GLOB_NOSPACE: + grecs_alloc_die(); + case GLOB_NOMATCH: + break; + default: + grecs_error(&LOCUS, 0, _("read error")); + } + p = NULL; + } else if (p[0] != '/') { + char *q = p; + p = grecs_find_include_file(q, allow_cwd); if (!p) grecs_error(&LOCUS, 0, _("%s: No such file or directory"), - p); + q); } - } + } if (p) rc = push_source(p, once); + else if (include_pos < include_glob.gl_pathc) + rc = push_source(include_glob.gl_pathv[include_pos++], once); + grecs_free(tmp); wordsplit_free(&ws); return rc; 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); +} |