#include #include #include #include #include #include #include #include #include #include #include #include "main.h" #include "uring.h" #include "config.h" #include "server.h" static void eat_whitespace_and_comments(char **pos) { while (true) { while (isspace(**pos)) (*pos)++; if (**pos == '#') { while (**pos != '\r' && **pos != '\n' && **pos != '\0') (*pos)++; continue; } return; } } static char * get_line(char **pos) { char *begin = *pos; char *end; while (isspace(*begin)) begin++; if (*begin == '\0') return NULL; end = begin; while (*end != '\n' && *end != '\0') end++; if (*end == '\0') *pos = end; else *pos = end + 1; while (isspace(*end)) { *end = '\0'; end--; } return begin; } static bool strtosockaddrs(const char *str, struct list_head *list) { char *tmp; uint16_t port; int r; struct sockaddr_in46 *addr; if (!str || *str == '\0' || !list) return false; list_init(list); if (*str == '[') { /* IPv6, [a:b:c...h]:p or [*]:p */ fprintf(stderr, "Attempting to parse an IPv6 addr\n"); str++; tmp = strchr(str, ']'); if (!tmp) goto out; *tmp = '\0'; addr = zmalloc(sizeof(*addr)); if (!addr) goto out; list_add(&addr->list, list); if (!strcmp(str, "*")) addr->in6.sin6_addr = in6addr_any; else if (inet_pton(AF_INET6, str, &addr->in6.sin6_addr) <= 0) goto out; tmp++; if (*tmp != ':') goto out; tmp++; if (strtou16_strict(tmp, &port) < 0) goto out; addr->in6.sin6_family = AF_INET6; addr->in6.sin6_port = htons(port); addr->addrlen = sizeof(addr->in6); } else if (*str == '*') { /* IPv4, *:p */ fprintf(stderr, "Attempting to parse an IPv4 wildcard\n"); str++; if (*str != ':') goto out; str++; if (strtou16_strict(str, &port) < 0) goto out; addr = zmalloc(sizeof(*addr)); if (!addr) goto out; list_add(&addr->list, list); addr->in4.sin_family = AF_INET; addr->in4.sin_addr.s_addr = INADDR_ANY; addr->in4.sin_port = htons(port); addr->addrlen = sizeof(addr->in4); } else if ((tmp = strchr(str, ':'))) { /* IPv4, a.b.c.d:p or IPv4/6 hostname:p */ fprintf(stderr, "Got an IPv4:port or hostname:port\n"); *tmp = '\0'; tmp++; if (strtou16_strict(tmp, &port) < 0) goto out; addr = zmalloc(sizeof(*addr)); if (!addr) goto out; if (inet_pton(AF_INET, str, &addr->in4.sin_addr) > 0) { fprintf(stderr, "...Got an IPv4:port (0x%p, list 0x%p)\n", addr, &addr->list); addr->in4.sin_family = AF_INET; addr->in4.sin_port = htons(port); addr->addrlen = sizeof(addr->in4); list_add(&addr->list, list); goto success; } else { free(addr); } struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_protocol = 0, .ai_flags = 0, }; struct addrinfo *results, *ai; /* FIXME: This is completely synchronous but getaddrinfo_a is not very ergonomic */ r = getaddrinfo(str, tmp, &hints, &results); if (r != 0) { fprintf(stderr, "gettaddrinfo(%s): %s\n", str, gai_strerror(r)); goto out; } fprintf(stderr, "...Got an hostname:port\n"); for (ai = results; ai; ai = ai->ai_next) { fprintf(stderr, "Got a result from getaddrinfo\n"); addr = zmalloc(sizeof(*addr)); if (!addr) { freeaddrinfo(results); goto out; } switch (ai->ai_family) { case AF_INET: { struct sockaddr_in *naddr = (struct sockaddr_in *)ai->ai_addr; addr->in4.sin_family = AF_INET; addr->in4.sin_addr = naddr->sin_addr; addr->in4.sin_port = naddr->sin_port; addr->addrlen = sizeof(addr->in4); list_add(&addr->list, list); break; } case AF_INET6: { struct sockaddr_in6 *naddr = (struct sockaddr_in6 *)ai->ai_addr; addr->in6.sin6_family = AF_INET6; addr->in6.sin6_addr = naddr->sin6_addr; addr->in6.sin6_port = naddr->sin6_port; addr->addrlen = sizeof(addr->in6); list_add(&addr->list, list); break; } default: fprintf(stderr, " Fam: Unknown (%i)\n", ai->ai_family); free(addr); continue; } } freeaddrinfo(results); } else if (strtou16_strict(tmp, &port) == 0) { /* Port */ fprintf(stderr, "Attempting to parse a port\n"); addr = zmalloc(sizeof(*addr)); if (!addr) goto out; addr->in6.sin6_family = AF_INET6; addr->in6.sin6_addr = in6addr_any; addr->in6.sin6_port = htons(port); addr->addrlen = sizeof(addr->in6); list_add(&addr->list, list); addr = zmalloc(sizeof(*addr)); if (!addr) goto out; addr->in4.sin_family = AF_INET; addr->in4.sin_addr.s_addr = INADDR_ANY; addr->in4.sin_port = htons(port); addr->addrlen = sizeof(addr->in4); list_add(&addr->list, list); } else { goto out; } success: if (list_empty(list)) { fprintf(stderr, "Success but empty list!?\n"); return false; } else { int i = 0; struct list_head *pos; list_for_each(pos, list) i++; fprintf(stderr, "Success, %i entries\n", i); } return true; out: if (!list_empty(list)) { struct sockaddr_in46 *tmpaddr; list_for_each_entry_safe(addr, tmpaddr, list, list) { list_del(&addr->list); free(addr); } } return false; } /* Returns true if theres data left to parse in buf */ bool config_parse_line(struct cfg *cfg, char **buf, struct cfg_key_value_map *kvmap, int *rkey, union cfg_value *rvalue) { char *line, *tmp, *key; int i; if (!cfg || !buf || !*buf || !kvmap || !rkey || !rvalue) die("%s: called with invalid parameters\n", __func__); eat_whitespace_and_comments(buf); line = get_line(buf); if (!line) return false; printf("%s: examining line: %s\n", __func__, line); tmp = line; while (isspace(*tmp)) tmp++; if (*tmp == '\0') goto out; key = tmp; while (*tmp != '\0' && !isspace(*tmp)) tmp++; if (*tmp == '\0') goto out; *tmp = '\0'; tmp++; while (isspace(*tmp)) tmp++; if (*tmp != '=') goto out; tmp++; while (isspace(*tmp)) tmp++; if (*tmp == '\0') goto out; for (i = 0; kvmap[i].key_name; i++) { if (strcmp(kvmap[i].key_name, key)) continue; switch (kvmap[i].value_type) { case CFG_VAL_TYPE_STRING: rvalue->str = tmp; break; case CFG_VAL_TYPE_UINT16: { uint16_t v; if (strtou16_strict(tmp, &v) < 0) goto out; rvalue->uint16 = v; break; } case CFG_VAL_TYPE_ADDRS: { if (!strtosockaddrs(tmp, &rvalue->addr_list)) goto out; if (list_empty(&rvalue->addr_list)) { fprintf(stderr, "CFG_VAL_TYPE_ADDRS with zero list!?\n"); goto out; } else { int i = 0; struct list_head *pos; list_for_each(pos, &rvalue->addr_list) i++; fprintf(stderr, "CFG_VAL_TYPE_ADDRS with list %i entries\n", i); } break; } case CFG_VAL_TYPE_INVALID: /* fall through */ default: goto out; } *rkey = kvmap[i].key_value; return true; } out: fprintf(stderr, "Invalid line\n"); *rkey = 0; return true; } bool config_parse_header(struct cfg *cfg, const char *title, char **buf) { char *line; if (!cfg || !title || !buf || !*buf) return false; eat_whitespace_and_comments(buf); line = get_line(buf); if (!line) { printf("Cfg: premature EOF\n"); return false; } else { char titlehdr[strlen(title) + 3]; sprintf(titlehdr, "[%s]", title); if (strcmp(line, titlehdr)) { printf("Invalid header: %s\n", line); return false; } } return true; }