#include #include #include #include #include #include #include #include #include #include "str.h" #include "strbuf.h" #include "strbuf_helpers.h" #define NELS(a) (sizeof (a) / sizeof *(a)) #define _DEBUGF(F,...) fprintf(stderr, "DEBUG: " F "\n", ##__VA_ARGS__) #define _WARNF(F,...) fprintf(stderr, "WARN: " F "\n", ##__VA_ARGS__) #define _WHYF(F,...) fprintf(stderr, "ERROR: " F "\n", ##__VA_ARGS__) #define _WHYF_perror(F,...) fprintf(stderr, "ERROR: " F ": %s [errno=%d]\n", ##__VA_ARGS__, strerror(errno), errno) #define DEBUGF(F,...) _DEBUGF("%s:%u " F, __FILE__, __LINE__, ##__VA_ARGS__) #define WARNF(F,...) _WARNF("%s:%u " F, __FILE__, __LINE__, ##__VA_ARGS__) #define WHYF(F,...) _WHYF("%s:%u " F, __FILE__, __LINE__, ##__VA_ARGS__) #define WHYF_perror(F,...) _WHYF_perror("%s:%u " F, __FILE__, __LINE__, ##__VA_ARGS__) #include "config.h" void *emalloc(size_t len) { char *new = malloc(len + 1); if (!new) { WHYF_perror("malloc(%lu)", (long)len); return NULL; } return new; } char *strn_emalloc(const char *str, size_t len) { char *new = emalloc(len + 1); if (new) { strncpy(new, str, len); new[len] = '\0'; } return new; } char *str_emalloc(const char *str) { return strn_emalloc(str, strlen(str)); } static const char *cf_find_keyend(const char *const key, const char *const fullkeyend) { const char *s = key; if (s < fullkeyend && (isalpha(*s) || *s == '_')) ++s; while (s < fullkeyend && (isalnum(*s) || *s == '_')) ++s; if (s == key || (s < fullkeyend && *s != '.')) return NULL; return s; } static int cf_om_make_child(struct cf_om_node **const parentp, const char *const fullkey, const char *const key, const char *const keyend) { size_t keylen = keyend - key; //DEBUGF("%s key=%s", __FUNCTION__, alloca_toprint(-1, key, keylen)); int i = 0; struct cf_om_node *child; if ((*parentp)->nodc) { // Binary search for matching child. int m = 0; int n = (*parentp)->nodc - 1; int c; do { i = (m + n) / 2; child = (*parentp)->nodv[i]; c = strncmp(key, child->key, keylen); if (c == 0 && child->key[keylen]) c = -1; //DEBUGF(" m=%d n=%d i=%d child->key=%s c=%d", m, n, i, alloca_str_toprint(child->key), c); if (c == 0) { //DEBUGF(" found i=%d", i); return i; } if (c > 0) m = ++i; else n = i - 1; } while (m <= n); } // At this point, i is the index where a new child should be inserted. assert(i >= 0); assert(i <= (*parentp)->nodc); child = emalloc(sizeof *child); if (child == NULL) return -1; memset(child, 0, sizeof *child); ++(*parentp)->nodc; if ((*parentp)->nodc > NELS((*parentp)->nodv)) *parentp = realloc(*parentp, sizeof(**parentp) + sizeof((*parentp)->nodv[0]) * ((*parentp)->nodc - NELS((*parentp)->nodv))); int j; for (j = (*parentp)->nodc - 1; j > i; --j) (*parentp)->nodv[j] = (*parentp)->nodv[j-1]; (*parentp)->nodv[i] = child; if (!(child->fullkey = strn_emalloc(fullkey, keyend - fullkey))) { free(child); return -1; } child->key = child->fullkey + (key - fullkey); //DEBUGF(" insert i=%d", i); return i; } void cf_free_node(struct cf_om_node *node) { while (node->nodc) cf_free_node(node->nodv[--node->nodc]); if (node->fullkey) { free((char *)node->fullkey); node->fullkey = node->key = NULL; } if (node->text) { free((char *)node->text); node->text = NULL; } free(node); } struct cf_om_node *cf_parse_to_om(const char *source, const char *buf, size_t len) { struct cf_om_node *root = emalloc(sizeof(struct cf_om_node)); if (root == NULL) return NULL; memset(root, 0, sizeof *root); const char *end = buf + len; const char *line = buf; const char *nextline; unsigned lineno = 1; for (lineno = 1; line < end; line = nextline, ++lineno) { const char *lend = line; while (lend < end && *lend != '\n') ++lend; nextline = lend + 1; if (lend > line && lend[-1] == '\r') --lend; //DEBUGF("lineno=%u %s", lineno, alloca_toprint(-1, line, lend - line)); if (line[0] == '#') continue; // skip comment lines const char *p; for (p = line; p < lend && isspace(*p); ++p) ; if (p == lend) continue; // skip empty and blank lines for (p = line; p < lend && *p != '='; ++p) ; if (p == line || p == lend) { WARNF("%s:%u: malformed configuration line -- ignored", source, lineno); continue; } struct cf_om_node **nodep = &root; const char *fullkey = line; const char *fullkeyend = p; const char *key = fullkey; const char *keyend = NULL; int nodi = -1; while (key <= fullkeyend && (keyend = cf_find_keyend(key, fullkeyend)) && (nodi = cf_om_make_child(nodep, fullkey, key, keyend)) != -1) { key = keyend + 1; nodep = &(*nodep)->nodv[nodi]; } if (keyend == NULL) { WARNF("%s:%u: malformed configuration option %s -- ignored", source, lineno, alloca_toprint(-1, fullkey, fullkeyend - fullkey) ); continue; } if (nodi == -1) goto error; // out of memory struct cf_om_node *node = *nodep; if (node->text) { WARNF("%s:%u: duplicate configuration option %s -- ignored (original is at %s:%u)", source, lineno, alloca_toprint(-1, fullkey, fullkeyend - fullkey), node->source, node->line_number ); continue; } ++p; if (!(node->text = strn_emalloc(p, lend - p))) break; // out of memory node->source = source; node->line_number = lineno; } return root; error: cf_free_node(root); return NULL; } void cf_dump_node(const struct cf_om_node *node, int indent) { if (node == NULL) DEBUGF("%*sNULL", indent * 3, ""); else { DEBUGF("%*s%s:%u fullkey=%s key=%s text=%s", indent * 3, "", node->source ? node->source : "NULL", node->line_number, alloca_str_toprint(node->fullkey), alloca_str_toprint(node->key), alloca_str_toprint(node->text) ); int i; for (i = 0; i < node->nodc; ++i) cf_dump_node(node->nodv[i], indent + 1); } } static int cf_get_child(const struct cf_om_node *parent, const char *key) { int i; for (i = 0; i < parent->nodc; ++i) if (strcmp(parent->nodv[i]->key, key) == 0) return i; return -1; } void cf_warn_nodev(const char *file, unsigned line, const struct cf_om_node *node, const char *key, const char *fmt, va_list ap) { strbuf b = strbuf_alloca(1024); if (node) { if (node->source && node->line_number) strbuf_sprintf(b, "%s:%u: ", node->source, node->line_number); strbuf_puts(b, "configuration option \""); strbuf_puts(b, node->fullkey); if (key && key[0]) { strbuf_putc(b, '.'); strbuf_puts(b, key); } strbuf_puts(b, "\" "); } strbuf_vsprintf(b, fmt, ap); _WARNF("%s:%u %s", file, line, strbuf_str(b)); } void cf_warn_childrenv(const char *file, unsigned line, const struct cf_om_node *parent, const char *fmt, va_list ap) { int i; for (i = 0; i < parent->nodc; ++i) { cf_warn_nodev(file, line, parent->nodv[i], NULL, fmt, ap); cf_warn_childrenv(file, line, parent->nodv[i], fmt, ap); } } void cf_warn_node(const char *file, unsigned line, const struct cf_om_node *node, const char *key, const char *fmt, ...) { va_list ap; va_start(ap, fmt); cf_warn_nodev(file, line, node, key, fmt, ap); va_end(ap); } void cf_warn_children(const char *file, unsigned line, const struct cf_om_node *node, const char *fmt, ...) { va_list ap; va_start(ap, fmt); cf_warn_childrenv(file, line, node, fmt, ap); va_end(ap); } void cf_warn_duplicate_node(const struct cf_om_node *parent, const char *key) { cf_warn_node(__FILE__, __LINE__, parent, key, "is duplicate"); } void cf_warn_missing_node(const struct cf_om_node *parent, const char *key) { cf_warn_node(__FILE__, __LINE__, parent, key, "is missing"); } strbuf strbuf_cf_flags(strbuf sb, int flags) { if (flags == CFERROR) return strbuf_puts(sb, "CFERROR"); size_t n = strbuf_len(sb); static struct { int flag; const char *name; } flagdefs[] = { { CFEMPTY, "CFEMPTY" }, { CFSTRINGOVERFLOW, "CFSTRINGOVERFLOW" }, { CFARRAYOVERFLOW, "CFARRAYOVERFLOW" }, { CFINCOMPLETE, "CFINCOMPLETE" }, { CFINVALID, "CFINVALID" }, { CFUNSUPPORTED, "CFUNSUPPORTED" }, }; int i; for (i = 0; i < NELS(flagdefs); ++i) { if (flags & flagdefs[i].flag) { if (strbuf_len(sb) != n) strbuf_putc(sb, ' '); strbuf_puts(sb, flagdefs[i].name); flags &= ~flagdefs[i].flag; } } for (i = 0; i < NELS(flagdefs); ++i) { if (flags & CFSUB(flagdefs[i].flag)) { if (strbuf_len(sb) != n) strbuf_putc(sb, ' '); strbuf_puts(sb, "CFSUB("); strbuf_puts(sb, flagdefs[i].name); strbuf_putc(sb, ')'); flags &= ~CFSUB(flagdefs[i].flag); } } if (flags) { if (strbuf_len(sb) != n) strbuf_putc(sb, ' '); strbuf_sprintf(sb, "%#x", flags); } if (strbuf_len(sb) == n) strbuf_puts(sb, "CFOK"); return sb; } static strbuf strbuf_cf_flag_reason(strbuf sb, int flags) { if (flags == CFERROR) return strbuf_puts(sb, "unrecoverable error"); size_t n = strbuf_len(sb); static struct { int flag; const char *reason; } flagdefs[] = { { CFEMPTY, "empty" }, { CFSTRINGOVERFLOW, "string overflow" }, { CFARRAYOVERFLOW, "array overflow" }, { CFINCOMPLETE, "incomplete" }, { CFINVALID, "invalid" }, { CFUNSUPPORTED, "not supported" }, { CFSUB(CFEMPTY), "contains empty element" }, { CFSUB(CFSTRINGOVERFLOW), "contains string overflow" }, { CFSUB(CFARRAYOVERFLOW), "contains array overflow" }, { CFSUB(CFINCOMPLETE), "contains incomplete element" }, { CFSUB(CFINVALID), "contains invalid element" }, { CFSUB(CFUNSUPPORTED), "contains unsupported element" }, }; int i; for (i = 0; i < NELS(flagdefs); ++i) { if (flags & flagdefs[i].flag) { if (strbuf_len(sb) != n) strbuf_puts(sb, ", "); strbuf_puts(sb, flagdefs[i].reason); flags &= ~flagdefs[i].flag; } } if (strbuf_len(sb) == n) strbuf_puts(sb, "no reason"); return sb; } void cf_warn_node_value(const struct cf_om_node *node, int reason) { strbuf b = strbuf_alloca(180); strbuf_cf_flag_reason(b, reason); cf_warn_node(__FILE__, __LINE__, node, NULL, "value %s %s", alloca_str_toprint(node->text), strbuf_str(b)); } void cf_warn_no_array(const struct cf_om_node *node, int reason) { strbuf b = strbuf_alloca(180); strbuf_cf_flag_reason(b, reason); cf_warn_node(__FILE__, __LINE__, node, NULL, "array discarded -- %s", strbuf_str(b)); } void cf_warn_unsupported_node(const struct cf_om_node *node) { cf_warn_node(__FILE__, __LINE__, node, NULL, "not supported"); } void cf_warn_unsupported_children(const struct cf_om_node *parent) { int i; for (i = 0; i < parent->nodc; ++i) { if (parent->nodv[i]->text) cf_warn_unsupported_node(parent->nodv[i]); cf_warn_unsupported_children(parent->nodv[i]); } } void cf_warn_list_overflow(const struct cf_om_node *node) { cf_warn_node(__FILE__, __LINE__, node, NULL, "list overflow"); cf_warn_children(__FILE__, __LINE__, node, "list overflow"); } void cf_warn_spurious_children(const struct cf_om_node *parent) { cf_warn_children(__FILE__, __LINE__, parent, "spurious"); } void cf_warn_array_label(const struct cf_om_node *node, int reason) { strbuf b = strbuf_alloca(180); strbuf_cf_flag_reason(b, reason); cf_warn_node(__FILE__, __LINE__, node, NULL, "array label %s -- %s", alloca_str_toprint(node->key), strbuf_str(b)); } void cf_warn_array_value(const struct cf_om_node *node, int reason) { strbuf b = strbuf_alloca(180); strbuf_cf_flag_reason(b, reason); if (node->text) cf_warn_node(__FILE__, __LINE__, node, NULL, "array value %s -- %s", alloca_str_toprint(node->text), strbuf_str(b)); else cf_warn_node(__FILE__, __LINE__, node, NULL, "array element -- %s", strbuf_str(b)); } int cf_opt_boolean(int *booleanp, const char *text) { if (!strcasecmp(text, "true") || !strcasecmp(text, "yes") || !strcasecmp(text, "on") || !strcasecmp(text, "1")) { *booleanp = 1; return CFOK; } else if (!strcasecmp(text, "false") || !strcasecmp(text, "no") || !strcasecmp(text, "off") || !strcasecmp(text, "0")) { *booleanp = 0; return CFOK; } return CFINVALID; } int cf_opt_absolute_path(char *str, size_t len, const char *text) { if (text[0] != '/') return CFINVALID; if (strlen(text) >= len) return CFSTRINGOVERFLOW; strncpy(str, text, len); assert(str[len - 1] == '\0'); return CFOK; } debugflags_t debugFlagMask(const char *flagname) { if (!strcasecmp(flagname,"all")) return ~0; else if (!strcasecmp(flagname,"interfaces")) return 1 << 0; else if (!strcasecmp(flagname,"rx")) return 1 << 1; else if (!strcasecmp(flagname,"tx")) return 1 << 2; else if (!strcasecmp(flagname,"verbose")) return 1 << 3; else if (!strcasecmp(flagname,"verbio")) return 1 << 4; else if (!strcasecmp(flagname,"peers")) return 1 << 5; else if (!strcasecmp(flagname,"dnaresponses")) return 1 << 6; else if (!strcasecmp(flagname,"dnahelper")) return 1 << 7; else if (!strcasecmp(flagname,"vomp")) return 1 << 8; else if (!strcasecmp(flagname,"packetformats")) return 1 << 9; else if (!strcasecmp(flagname,"packetconstruction")) return 1 << 10; else if (!strcasecmp(flagname,"gateway")) return 1 << 11; else if (!strcasecmp(flagname,"keyring")) return 1 << 12; else if (!strcasecmp(flagname,"sockio")) return 1 << 13; else if (!strcasecmp(flagname,"frames")) return 1 << 14; else if (!strcasecmp(flagname,"abbreviations")) return 1 << 15; else if (!strcasecmp(flagname,"routing")) return 1 << 16; else if (!strcasecmp(flagname,"security")) return 1 << 17; else if (!strcasecmp(flagname,"rhizome")) return 1 << 18; else if (!strcasecmp(flagname,"rhizometx")) return 1 << 19; else if (!strcasecmp(flagname,"rhizomerx")) return 1 << 20; else if (!strcasecmp(flagname,"rhizomeads")) return 1 << 21; else if (!strcasecmp(flagname,"monitorroutes")) return 1 << 22; else if (!strcasecmp(flagname,"queues")) return 1 << 23; else if (!strcasecmp(flagname,"broadcasts")) return 1 << 24; else if (!strcasecmp(flagname,"manifests")) return 1 << 25; else if (!strcasecmp(flagname,"mdprequests")) return 1 << 26; else if (!strcasecmp(flagname,"timing")) return 1 << 27; return 0; } int cf_opt_debugflags(debugflags_t *flagsp, const struct cf_om_node *node) { //DEBUGF("%s", __FUNCTION__); //cf_dump_node(node, 1); debugflags_t setmask = 0; debugflags_t clearmask = 0; int setall = 0; int clearall = 0; int result = CFEMPTY; int i; for (i = 0; i < node->nodc; ++i) { const struct cf_om_node *child = node->nodv[i]; cf_warn_unsupported_children(child); debugflags_t mask = debugFlagMask(child->key); int flag = -1; if (!mask) cf_warn_unsupported_node(child); else if (child->text) { int ret = cf_opt_boolean(&flag, child->text); switch (ret) { case CFERROR: return CFERROR; case CFOK: result &= ~CFEMPTY; if (mask == ~0) { if (flag) setall = 1; else clearall = 1; } else { if (flag) setmask |= mask; else clearmask |= mask; } break; default: cf_warn_node_value(child, ret); result |= ret; break; } } } if (setall) *flagsp = ~0; else if (clearall) *flagsp = 0; *flagsp &= ~clearmask; *flagsp |= setmask; return result; } int cf_opt_protocol(char *str, size_t len, const char *text) { if (!str_is_uri_scheme(text)) return CFINVALID; if (strlen(text) >= len) return CFSTRINGOVERFLOW; strncpy(str, text, len); assert(str[len - 1] == '\0'); return CFOK; } int cf_opt_rhizome_peer(struct config_rhizomepeer *rpeer, const struct cf_om_node *node) { if (!node->text) return cf_opt_config_rhizomepeer(rpeer, node); cf_warn_spurious_children(node); const char *protocol; size_t protolen; const char *auth; if (str_is_uri(node->text)) { const char *hier; if (!( str_uri_scheme(node->text, &protocol, &protolen) && str_uri_hierarchical(node->text, &hier, NULL) && str_uri_hierarchical_authority(hier, &auth, NULL)) ) return CFINVALID; } else { auth = node->text; protocol = "http"; protolen = strlen(protocol); } const char *host; size_t hostlen; unsigned short port = 4110; if (!str_uri_authority_hostname(auth, &host, &hostlen)) return CFINVALID; str_uri_authority_port(auth, &port); if (protolen >= sizeof rpeer->protocol) return CFSTRINGOVERFLOW; if (hostlen >= sizeof rpeer->host) return CFSTRINGOVERFLOW; strncpy(rpeer->protocol, protocol, protolen)[protolen] = '\0'; strncpy(rpeer->host, host, hostlen)[hostlen] = '\0'; rpeer->port = port; return CFOK; } int cf_opt_str(char *str, size_t len, const char *text) { if (strlen(text) >= len) return CFSTRINGOVERFLOW; strncpy(str, text, len); assert(str[len - 1] == '\0'); return CFOK; } int cf_opt_str_nonempty(char *str, size_t len, const char *text) { if (!text[0]) return CFINVALID; return cf_opt_str(str, len, text); } int cf_opt_int(int *intp, const char *text) { const char *end = text; long value = strtol(text, (char**)&end, 10); if (end == text || *end) return CFINVALID; *intp = value; return CFOK; } int cf_opt_uint32_nonzero(uint32_t *intp, const char *text) { const char *end = text; long value = strtoul(text, (char**)&end, 10); if (end == text || *end || value < 1 || value > 0xffffffffL) return CFINVALID; *intp = value; return CFOK; } int cf_opt_uint64_scaled(uint64_t *intp, const char *text) { uint64_t result; const char *end; if (!str_to_uint64_scaled(text, 10, &result, &end) || *end) return CFINVALID; *intp = result; return CFOK; } int cf_opt_ushort_nonzero(unsigned short *ushortp, const char *text) { uint32_t ui; if (cf_opt_uint32_nonzero(&ui, text) != CFOK || ui > 0xffff) return CFINVALID; *ushortp = ui; return CFOK; } int cmp_short(const short *a, const short *b) { return *a < *b ? -1 : *a > *b ? 1 : 0; } int cmp_ushort(const unsigned short *a, const unsigned short *b) { return *a < *b ? -1 : *a > *b ? 1 : 0; } int cmp_sid(const sid_t *a, const sid_t *b) { return memcmp(a->binary, b->binary, sizeof a->binary); } int vld_argv(const struct cf_om_node *parent, struct config_argv *array, int result) { unsigned short last_key = 0; int i; if (array->ac) { unsigned short last_key = array->av[0].key; for (i = 1; i < array->ac; ++i) { unsigned short key = array->av[i].key; if (last_key > key) { cf_warn_node(__FILE__, __LINE__, parent, NULL, "array is not sorted"); return CFERROR; } last_key = key; } } for (i = 0; i < array->ac; ++i) { unsigned short key = array->av[i].key; assert(key >= 1); assert(key >= last_key); if (last_key == key) { char labelkey[12]; sprintf(labelkey, "%u", last_key); cf_warn_duplicate_node(parent, labelkey); result |= CFINVALID; } while (++last_key < key && last_key <= sizeof(array->av)) { char labelkey[12]; sprintf(labelkey, "%u", last_key); cf_warn_missing_node(parent, labelkey); result |= CFINCOMPLETE; } last_key = key; } return result; } int cf_opt_in_addr(struct in_addr *addrp, const char *text) { struct in_addr addr; if (!inet_aton(text, &addr)) return CFINVALID; *addrp = addr; return CFOK; } int cf_opt_port(unsigned short *portp, const char *text) { unsigned short port = 0; const char *p; for (p = text; isdigit(*p); ++p) { unsigned oport = port; port = port * 10 + *p - '0'; if (port / 10 != oport) break; } if (*p || port == 0) return CFINVALID; *portp = port; return CFOK; } int cf_opt_sid(sid_t *sidp, const char *text) { sid_t sid; if (!str_is_subscriber_id(text)) return CFINVALID; size_t n = fromhex(sidp->binary, text, SID_SIZE); assert(n == SID_SIZE); return CFOK; } int cf_opt_rhizome_bk(rhizome_bk_t *bkp, const char *text) { rhizome_bk_t sid; if (!rhizome_str_is_bundle_key(text)) return CFINVALID; size_t n = fromhex(bkp->binary, text, RHIZOME_BUNDLE_KEY_BYTES); assert(n == RHIZOME_BUNDLE_KEY_BYTES); return CFOK; } int cf_opt_interface_type(short *typep, const char *text) { if (strcasecmp(text, "ethernet") == 0) { *typep = OVERLAY_INTERFACE_ETHERNET; return CFOK; } if (strcasecmp(text, "wifi") == 0) { *typep = OVERLAY_INTERFACE_WIFI; return CFOK; } if (strcasecmp(text, "catear") == 0) { *typep = OVERLAY_INTERFACE_PACKETRADIO; return CFOK; } if (strcasecmp(text, "other") == 0) { *typep = OVERLAY_INTERFACE_UNKNOWN; return CFOK; } return CFINVALID; } int cf_opt_pattern_list(struct pattern_list *listp, const char *text) { struct pattern_list list; memset(&list, 0, sizeof list); const char *word = NULL; const char *p; for (p = text; ; ++p) { if (!*p || isspace(*p) || *p == ',') { if (word) { size_t len = p - word; if (list.patc >= NELS(list.patv) || len >= sizeof(list.patv[list.patc])) return CFARRAYOVERFLOW; strncpy(list.patv[list.patc++], word, len)[len] = '\0'; word = NULL; } if (!*p) break; } else if (!word) word = p; } assert(word == NULL); *listp = list; return CFOK; } /* Config parse function. Implements the original form of the 'interfaces' config option. Parses a * single text string of the form: * * ( "+" | "-" ) [ interfacename ] [ "=" type ] [ ":" port [ ":" speed ] ] * * where: * * "+" means include the interface * "-" means exclude the interface * * The original implementation applied include/exclude matching in the order that the list was * given, but the new implementation applies all exclusions before apply inclusions. This should * not be a problem, as there were no known uses that depended on testing an inclusion before an * exclusion. * * An empty 'interfacename' matches all interfaces. So a "+" by itself includes all interfaces, * and a '-' by itself excludes all interfaces. These two rules are applied after all other * interface inclusions/exclusions are tested, otherwise "-" would overrule all other interfaces. * * The optional 'type' tells DNA how to handle the interface in terms of bandwidth:distance * relationship for calculating tick times etc. * * The optional 'port' is the port number to bind all interfaces, instead of the default. * * The optional 'speed' is the nominal bits/second bandwidth of the interface, instead of the * default. It is expressed as a positive integer with an optional scaling suffix, eg, "150k", * "1K", "900M". * * @author Andrew Bettison */ int cf_opt_network_interface(struct config_network_interface *nifp, const char *text) { //DEBUGF("%s text=%s", __FUNCTION__, alloca_str_toprint(text)); struct config_network_interface nif; cf_dfl_config_network_interface(&nif); if (text[0] != '+' && text[0] != '-') return CFINVALID; // "Sign must be + or -" nif.exclude = (text[0] == '-'); const char *const endtext = text + strlen(text); const char *name = text + 1; const char *p = strpbrk(name, "=:"); if (!p) p = endtext; size_t len = p - name; int star = (len == 0 || (name[0] != '>' && name[len - 1] != '*')) ? 1 : 0; if (len + star >= sizeof(nif.match.patv[0])) return CFSTRINGOVERFLOW; strncpy(nif.match.patv[0], name, len)[len + star] = '\0'; if (star) nif.match.patv[0][len] = '*'; nif.match.patc = 1; if (*p == '=') { const char *const type = p + 1; p = strchr(type, ':'); if (!p) p = endtext; len = p - type; if (len) { char buf[len + 1]; strncpy(buf, type, len)[len] = '\0'; int result = cf_opt_interface_type(&nif.type, buf); switch (result) { case CFERROR: return CFERROR; case CFOK: break; default: return result; // "Invalid interface type" } } } if (*p == ':') { const char *const port = p + 1; p = strchr(port, ':'); if (!p) p = endtext; len = p - port; if (len) { char buf[len + 1]; strncpy(buf, port, len)[len] = '\0'; int result = cf_opt_port(&nif.port, buf); switch (result) { case CFERROR: return CFERROR; case CFOK: break; default: return result; // "Invalid interface port number" } } } if (*p == ':') { const char *const speed = p + 1; p = endtext; len = p - speed; if (len) { char buf[len + 1]; strncpy(buf, speed, len)[len] = '\0'; int result = cf_opt_uint64_scaled(&nif.speed, buf); switch (result) { case CFERROR: return CFERROR; case CFOK: break; default: return result; // "Invalid interface speed" } if (nif.speed < 1) return CFINVALID; // "Interfaces must be capable of at least 1 bit per second" } } if (*p) return CFINVALID; // "Extra junk at end of interface specification" *nifp = nif; return CFOK; } /* Config parse function. Implements the original form of the 'interfaces' config option. Parses a * comma-separated list of interface rules (see cf_opt_network_interface() for the format of each * rule), then parses the regular config array-of-struct style interface option settings so that * both forms are supported. * * @author Andrew Bettison */ int cf_opt_interface_list(struct config_interface_list *listp, const struct cf_om_node *node) { int result = cf_opt_config_interface_list(listp, node); if (result == CFERROR) return CFERROR; if (node->text) { const char *p; const char *arg = NULL; unsigned n = listp->ac; for (p = node->text; n < NELS(listp->av); ++p) { if (*p == '\0' || *p == ',' || isspace(*p)) { if (arg) { int len = p - arg; if (len > 80) { result |= CFSTRINGOVERFLOW; goto bye; } char buf[len + 1]; strncpy(buf, arg, len)[len] = '\0'; int ret = cf_opt_network_interface(&listp->av[n].value, buf); switch (ret) { case CFERROR: return CFERROR; case CFOK: len = snprintf(listp->av[n].key, sizeof listp->av[n].key - 1, "%u", n); listp->av[n].key[len] = '\0'; ++n; break; default: cf_warn_node(__FILE__, __LINE__, node, NULL, "invalid interface rule %s", alloca_str_toprint(buf)); \ result |= CFSUB(ret); break; } arg = NULL; } if (!*p) break; } else if (!arg) arg = p; } if (*p) { result |= CFARRAYOVERFLOW; goto bye; } assert(n <= NELS(listp->av)); listp->ac = n; } bye: if (listp->ac == 0) result |= CFEMPTY; else result &= ~CFEMPTY; return result; } // Generate array element comparison functions. #define STRUCT(__name, __validator...) #define NODE(__type, __element, __default, __parser, __flags, __comment) #define ATOM(__type, __element, __default, __parser, __flags, __comment) #define STRING(__size, __element, __default, __parser, __flags, __comment) #define SUB_STRUCT(__name, __element, __flags) #define NODE_STRUCT(__name, __element, __parser, __flags) #define END_STRUCT #define ARRAY(__name, __validator...) \ static int __cmp_config_##__name(const struct config_##__name##__element *a, const struct config_##__name##__element *b) { \ __compare_func__config_##__name##__t *cmp = (NULL #define KEY_ATOM(__type, __eltparser, __cmpfunc...) \ ,##__cmpfunc); \ return cmp ? (*cmp)(&a->key, &b->key) : 0; #define KEY_STRING(__strsize, __eltparser, __cmpfunc...) \ ,##__cmpfunc); \ return cmp ? (*cmp)(a->key, b->key) : 0; #define VALUE_ATOM(__type, __eltparser) #define VALUE_STRING(__strsize, __eltparser) #define VALUE_NODE(__type, __eltparser) #define VALUE_SUB_STRUCT(__structname) #define VALUE_NODE_STRUCT(__structname, __eltparser) #define END_ARRAY(__size) \ } #include "config_schema.h" #undef STRUCT #undef NODE #undef ATOM #undef STRING #undef SUB_STRUCT #undef NODE_STRUCT #undef END_STRUCT #undef ARRAY #undef KEY_ATOM #undef KEY_STRING #undef VALUE_ATOM #undef VALUE_STRING #undef VALUE_NODE #undef VALUE_SUB_STRUCT #undef VALUE_NODE_STRUCT #undef END_ARRAY // Schema item flags. #define __MANDATORY (1<<0) #define __TEXT (1<<1) #define __CHILDREN (1<<2) // Schema flag symbols, to be used in the '__flags' macro arguments. #define MANDATORY |__MANDATORY #define USES_TEXT |__TEXT #define USES_CHILDREN |__CHILDREN // Generate parsing functions, cf_opt_config_SECTION() #define STRUCT(__name, __validator...) \ int cf_opt_config_##__name(struct config_##__name *strct, const struct cf_om_node *node) { \ int (*validator)(const struct cf_om_node *, struct config_##__name *, int) = (NULL, ##__validator); \ int result = CFEMPTY; \ char used[node->nodc]; \ memset(used, 0, node->nodc * sizeof used[0]); #define __ITEM(__element, __flags, __parseexpr) \ { \ int i = cf_get_child(node, #__element); \ const struct cf_om_node *child = (i != -1) ? node->nodv[i] : NULL; \ int ret = CFEMPTY; \ if (child) { \ used[i] |= (__flags); \ ret = (__parseexpr); \ if (ret == CFERROR) \ return CFERROR; \ } \ result |= ret & CF__SUBFLAGS; \ ret &= CF__FLAGS; \ if (!(ret & CFEMPTY)) \ result &= ~CFEMPTY; \ else if ((__flags) & __MANDATORY) { \ cf_warn_missing_node(node, #__element); \ result |= CFINCOMPLETE; \ } \ if (ret & ~CFEMPTY) { \ assert(child != NULL); \ if (child->text) \ cf_warn_node_value(child, ret); \ result |= CFSUB(ret); \ } \ } #define NODE(__type, __element, __default, __parser, __flags, __comment) \ __ITEM(__element, 0 __flags, __parser(&strct->__element, child)) #define ATOM(__type, __element, __default, __parser, __flags, __comment) \ __ITEM(__element, ((0 __flags)|__TEXT)&~__CHILDREN, child->text ? __parser(&strct->__element, child->text) : CFEMPTY) #define STRING(__size, __element, __default, __parser, __flags, __comment) \ __ITEM(__element, ((0 __flags)|__TEXT)&~__CHILDREN, child->text ? __parser(strct->__element, (__size) + 1, child->text) : CFEMPTY) #define SUB_STRUCT(__name, __element, __flags) \ __ITEM(__element, (0 __flags)|__CHILDREN, cf_opt_config_##__name(&strct->__element, child)) #define NODE_STRUCT(__name, __element, __parser, __flags) \ __ITEM(__element, (0 __flags)|__TEXT|__CHILDREN, __parser(&strct->__element, child)) #define END_STRUCT \ { \ int i; \ for (i = 0; i < node->nodc; ++i) { \ if (node->nodv[i]->text && !(used[i] & __TEXT)) { \ cf_warn_unsupported_node(node->nodv[i]); \ result |= CFSUB(CFUNSUPPORTED); \ } \ if (node->nodv[i]->nodc && !(used[i] & __CHILDREN)) { \ cf_warn_unsupported_children(node->nodv[i]); \ result |= CFSUB(CFUNSUPPORTED); \ } \ } \ } \ if (validator) \ result = (*validator)(node, strct, result); \ return result; \ } #define ARRAY(__name, __validator...) \ int cf_opt_config_##__name(struct config_##__name *array, const struct cf_om_node *node) { \ __compare_func__config_##__name##__t *cmp = NULL; \ int (*eltcmp)(const struct config_##__name##__element *, const struct config_##__name##__element *) = __cmp_config_##__name; \ int (*validator)(const struct cf_om_node *, struct config_##__name *, int) = (NULL, ##__validator); \ int result = CFOK; \ int i, n; \ for (n = 0, i = 0; i < node->nodc && n < NELS(array->av); ++i) { \ const struct cf_om_node *child = node->nodv[i]; \ int ret = CFEMPTY; #define __ARRAY_KEY(__parseexpr) \ ret = (__parseexpr); \ if (ret == CFERROR) \ return CFERROR; \ result |= ret & CF__SUBFLAGS; \ ret &= CF__FLAGS; \ result |= CFSUB(ret); \ if (ret != CFOK) \ cf_warn_array_label(child, ret); #define __ARRAY_VALUE(__parseexpr) \ if (ret == CFOK) { \ ret = (__parseexpr); \ if (ret == CFERROR) \ return CFERROR; \ result |= ret & CF__SUBFLAGS; \ ret &= CF__FLAGS; \ result |= CFSUB(ret); \ if (ret == CFOK) \ ++n; \ else \ cf_warn_array_value(child, ret); \ } #define END_ARRAY(__size) \ } \ if (i < node->nodc) { \ assert(n == NELS(array->av)); \ result |= CFARRAYOVERFLOW; \ for (; i < node->nodc; ++i) \ cf_warn_list_overflow(node->nodv[i]); \ } \ array->ac = n; \ if (cmp) \ qsort(array->av, array->ac, sizeof array->av[0], (int (*)(const void *, const void *)) eltcmp); \ if (validator) \ result = (*validator)(node, array, result); \ if (result & ~CFEMPTY) { \ cf_warn_no_array(node, result); \ array->ac = 0; \ } \ if (array->ac == 0) \ result |= CFEMPTY; \ return result; \ } #define KEY_ATOM(__type, __eltparser, __cmpfunc...) \ __ARRAY_KEY(__eltparser(&array->av[n].key, child->key)) \ cmp = (NULL, ##__cmpfunc); #define KEY_STRING(__strsize, __eltparser, __cmpfunc...) \ __ARRAY_KEY(__eltparser(array->av[n].key, sizeof array->av[n].key, child->key)) \ cmp = (NULL, ##__cmpfunc); #define VALUE_ATOM(__type, __eltparser) \ __ARRAY_VALUE(child->text ? __eltparser(&array->av[n].value, child->text) : CFEMPTY) #define VALUE_STRING(__strsize, __eltparser) \ __ARRAY_VALUE(child->text ? __eltparser(array->av[n].value, sizeof array->av[n].value, child->text) : CFEMPTY) #define VALUE_NODE(__type, __eltparser) \ __ARRAY_VALUE(__eltparser(&array->av[n].value, child)) #define VALUE_SUB_STRUCT(__structname) \ __ARRAY_VALUE(cf_opt_config_##__structname(&array->av[n].value, child)) #define VALUE_NODE_STRUCT(__structname, __eltparser) \ __ARRAY_VALUE(__eltparser(&array->av[n].value, child)) #include "config_schema.h" #undef STRUCT #undef NODE #undef ATOM #undef STRING #undef SUB_STRUCT #undef NODE_STRUCT #undef END_STRUCT #undef ARRAY #undef KEY_ATOM #undef KEY_STRING #undef VALUE_ATOM #undef VALUE_STRING #undef VALUE_NODE #undef VALUE_SUB_STRUCT #undef VALUE_NODE_STRUCT #undef END_ARRAY int main(int argc, char **argv) { int i; for (i = 1; i < argc; ++i) { int fd = open(argv[i], O_RDONLY); if (fd == -1) { perror("open"); exit(1); } struct stat st; fstat(fd, &st); char *buf = malloc(st.st_size); if (!buf) { perror("malloc"); exit(1); } if (read(fd, buf, st.st_size) != st.st_size) { perror("read"); exit(1); } struct cf_om_node *root = cf_parse_to_om(argv[i], buf, st.st_size); close(fd); //cf_dump_node(root, 0); struct config_main config; memset(&config, 0, sizeof config); cf_dfl_config_main(&config); int result = cf_opt_config_main(&config, root); cf_free_node(root); free(buf); DEBUGF("result = %s", strbuf_str(strbuf_cf_flags(strbuf_alloca(128), result))); DEBUGF("config.log.file = %s", alloca_str_toprint(config.log.file)); DEBUGF("config.log.show_pid = %d", config.log.show_pid); DEBUGF("config.log.show_time = %d", config.log.show_time); DEBUGF("config.server.chdir = %s", alloca_str_toprint(config.server.chdir)); DEBUGF("config.debug = %llx", (unsigned long long) config.debug); DEBUGF("config.directory.service = %s", alloca_tohex(config.directory.service.binary, SID_SIZE)); DEBUGF("config.rhizome.api.addfile.allow_host = %s", inet_ntoa(config.rhizome.api.addfile.allow_host)); int j; for (j = 0; j < config.mdp.iftype.ac; ++j) { DEBUGF("config.mdp.iftype.%u", config.mdp.iftype.av[j].key); DEBUGF(" .tick_ms = %u", config.mdp.iftype.av[j].value.tick_ms); } for (j = 0; j < config.dna.helper.argv.ac; ++j) { DEBUGF("config.dna.helper.argv.%u=%s", config.dna.helper.argv.av[j].key, config.dna.helper.argv.av[j].value); } for (j = 0; j < config.rhizome.direct.peer.ac; ++j) { DEBUGF("config.rhizome.direct.peer.%s", config.rhizome.direct.peer.av[j].key); DEBUGF(" .protocol = %s", alloca_str_toprint(config.rhizome.direct.peer.av[j].value.protocol)); DEBUGF(" .host = %s", alloca_str_toprint(config.rhizome.direct.peer.av[j].value.host)); DEBUGF(" .port = %u", config.rhizome.direct.peer.av[j].value.port); } for (j = 0; j < config.interfaces.ac; ++j) { DEBUGF("config.interfaces.%s", config.interfaces.av[j].key); DEBUGF(" .exclude = %d", config.interfaces.av[j].value.exclude); DEBUGF(" .match = ["); int k; for (k = 0; k < config.interfaces.av[j].value.match.patc; ++k) DEBUGF(" %s", alloca_str_toprint(config.interfaces.av[j].value.match.patv[k])); DEBUGF(" ]"); DEBUGF(" .type = %d", config.interfaces.av[j].value.type); DEBUGF(" .port = %u", config.interfaces.av[j].value.port); DEBUGF(" .speed = %llu", (unsigned long long) config.interfaces.av[j].value.speed); } for (j = 0; j < config.hosts.ac; ++j) { char sidhex[SID_STRLEN + 1]; tohex(sidhex, config.hosts.av[j].key.binary, SID_SIZE); DEBUGF("config.hosts.%s", sidhex); DEBUGF(" .interface = %s", alloca_str_toprint(config.hosts.av[j].value.interface)); DEBUGF(" .address = %s", inet_ntoa(config.hosts.av[j].value.address)); DEBUGF(" .port = %u", config.hosts.av[j].value.port); } } exit(0); }