/** * Break the netmaks string and convert them into network_pair elements in * the local_networks array. IP's are in network order. */ void parse_netmasks(const char *str) { char **masks = g_strsplit(str, ";", 0); char *p; guint32 mask_div; int i; free_networks(); if (!masks) return; for (i = 0; masks[i]; i++) /* just count */ ; number_local_networks = i; if (i == 0) { g_strfreev(masks); return; } local_networks = g_malloc(i * sizeof *local_networks); for (i = 0; masks[i]; i++) { /* Network is of the form ip/mask or ip/bits */ if ((p = strchr(masks[i], '/')) && *p) { *p++ = '\0'; if (strchr(p, '.')) { /* get the network address from the user */ if (!string_to_ip_strict(p, &local_networks[i].mask, NULL)) g_warning("parse_netmasks(): Invalid netmask: \"%s\"", p); } else { int error; mask_div = parse_uint32(p, NULL, 10, &error); mask_div = MIN(32, mask_div); if (error) g_warning("parse_netmasks(): " "Invalid CIDR prefixlen: \"%s\"", p); else local_networks[i].mask = (guint32) -1 << (32 - mask_div); } } else { /* Assume single-host */ local_networks[i].mask = -1; /* 255.255.255.255 */ } /* get the network address from the user */ if (!string_to_ip_strict(masks[i], &local_networks[i].net, NULL)) g_warning("parse_netmasks(): Invalid netmask: \"%s\"", masks[i]); } g_strfreev(masks); }
/** * Parses IPv4 and IPv6 addresses. The latter requires IPv6 support to be * enabled. * * For convenience, if the first character is '[' then the address is parsed * as an IPv6 one and the trailing ']' is both expected and swallowed. This * allows the routine to be used when parsing "ipv4:port" or "[ipv6]:port" * strings. * * @param s The string to parse. * @param endptr This will point to the first character after the parsed * address. * @param addr_ptr If not NULL, it is set to the parsed host address or * ``zero_host_addr'' on failure. * @return Returns TRUE on success; otherwise FALSE. */ bool string_to_host_addr(const char *s, const char **endptr, host_addr_t *addr_ptr) { uint32 ip; bool brackets = FALSE; g_assert(s); if ('[' == *s) { brackets = TRUE; s++; } if (!brackets && string_to_ip_strict(s, &ip, endptr)) { if (addr_ptr) { *addr_ptr = host_addr_get_ipv4(ip); } return TRUE; } else { uint8 ipv6[16]; const char *end; bool ok; ok = parse_ipv6_addr(s, ipv6, &end); if (ok) { if (addr_ptr) { *addr_ptr = host_addr_peek_ipv6(ipv6); } if (brackets) { if (']' == *end) { end++; } else { ok = FALSE; /* Trailing ']' required if we had '[' */ } } } if (endptr != NULL) *endptr = end; if (ok) return TRUE; /* FALL THROUGH */ } if (addr_ptr) *addr_ptr = zero_host_addr; return FALSE; }
/** * Loads the whitelist into memory. */ static void G_COLD whitelist_retrieve(void) { char line[1024]; FILE *f; filestat_t st; unsigned linenum = 0; file_path_t fp[1]; whitelist_generation++; file_path_set(fp, settings_config_dir(), whitelist_file); f = file_config_open_read_norename("Host Whitelist", fp, N_ITEMS(fp)); if (!f) return; if (fstat(fileno(f), &st)) { g_warning("%s(): fstat() failed: %m", G_STRFUNC); fclose(f); return; } while (fgets(line, sizeof line, f)) { pslist_t *sl_addr, *sl; const char *endptr, *start; host_addr_t addr; uint16 port; uint8 bits; bool item_ok; bool use_tls; char *hname; linenum++; if (!file_line_chomp_tail(line, sizeof line, NULL)) { g_warning("%s(): line %u too long, aborting", G_STRFUNC, linenum); break; } if (file_line_is_skipable(line)) continue; sl_addr = NULL; addr = zero_host_addr; endptr = NULL; hname = NULL; endptr = is_strprefix(line, "tls:"); if (endptr) { use_tls = TRUE; start = endptr; } else { use_tls = FALSE; start = line; } port = 0; if (string_to_host_addr_port(start, &endptr, &addr, &port)) { sl_addr = name_to_host_addr(host_addr_to_string(addr), settings_dns_net()); } else if (string_to_host_or_addr(start, &endptr, &addr)) { uchar c = *endptr; switch (c) { case '\0': case ':': case '/': break; default: if (!is_ascii_space(c)) endptr = NULL; } if (!endptr) { g_warning("%s(): line %d: " "expected a hostname or IP address \"%s\"", G_STRFUNC, linenum, line); continue; } /* Terminate the string for name_to_host_addr() */ hname = h_strndup(start, endptr - start); } else { g_warning("%s(): line %d: expected hostname or IP address \"%s\"", G_STRFUNC, linenum, line); continue; } g_assert(sl_addr != NULL || hname != NULL); g_assert(NULL != endptr); bits = 0; item_ok = TRUE; /* * When an explicit address is given (no hostname) and with no * port, one can suffix the address with bits to indicate a CIDR * range of whitelisted addresses. */ if (0 == port) { /* Ignore trailing items separated by a space */ while ('\0' != *endptr && !is_ascii_space(*endptr)) { uchar c = *endptr++; if (':' == c) { int error; uint32 v; if (0 != port) { g_warning("%s(): line %d: multiple colons after host", G_STRFUNC, linenum); item_ok = FALSE; break; } v = parse_uint32(endptr, &endptr, 10, &error); port = (error || v > 0xffff) ? 0 : v; if (0 == port) { g_warning("%s(): line %d: " "invalid port value after host", G_STRFUNC, linenum); item_ok = FALSE; break; } } else if ('/' == c) { const char *ep; uint32 mask; if (0 != bits) { g_warning("%s(): line %d: " "multiple slashes after host", G_STRFUNC, linenum); item_ok = FALSE; break; } if (string_to_ip_strict(endptr, &mask, &ep)) { if (!host_addr_is_ipv4(addr)) { g_warning("%s(): line %d: " "IPv4 netmask after non-IPv4 address", G_STRFUNC, linenum); item_ok = FALSE; break; } endptr = ep; if (0 == (bits = netmask_to_cidr(mask))) { g_warning("%s(): line %d: " "IPv4 netmask after non-IPv4 address", G_STRFUNC, linenum); item_ok = FALSE; break; } } else { int error; uint32 v; v = parse_uint32(endptr, &endptr, 10, &error); if ( error || 0 == v || (v > 32 && host_addr_is_ipv4(addr)) || (v > 128 && host_addr_is_ipv6(addr)) ) { g_warning("%s(): line %d: " "invalid numeric netmask after host", G_STRFUNC, linenum); item_ok = FALSE; break; } bits = v; } } else { g_warning("%s(): line %d: " "unexpected character after host", G_STRFUNC, linenum); item_ok = FALSE; break; } } } if (item_ok) { struct whitelist *item; if (hname) { item = whitelist_hostname_create(use_tls, hname, port); whitelist_dns_resolve(item, FALSE); } else { PSLIST_FOREACH(sl_addr, sl) { host_addr_t *aptr = sl->data; g_assert(aptr != NULL); item = whitelist_addr_create(use_tls, *aptr, port, bits); whitelist_add(item); } } } else {
/** * Parse an IPv4 Geo IP line and record the range in the database. */ static void gip_parse_ipv4(const char *line, int linenum) { const char *end; uint16 code; int c; struct range_context ctx; /* * Each line looks like: * * 15.0.0.0 - 15.130.191.255 fr * * So we just have to parse the two IP addresses, then compute * all the ranges they cover in order to insert them into * the IP database. */ end = strchr(line, '-'); if (end == NULL) { g_warning("%s, line %d: no IP address separator in \"%s\"", gip_source[GIP_IPV4].file, linenum, line); return; } if (!string_to_ip_strict(line, &ctx.ip1, NULL)) { g_warning("%s, line %d: invalid first IP in \"%s\"", gip_source[GIP_IPV4].file, linenum, line); return; } /* * Skip spaces until the second IP. */ end++; /* Go past the minus, parsing the second IP */ while ((c = *end)) { if (!is_ascii_space(c)) break; end++; } if (!string_to_ip_strict(end, &ctx.ip2, &end)) { g_warning("%s, line %d: invalid second IP in \"%s\"", gip_source[GIP_IPV4].file, linenum, line); return; } /* * Make sure the IP addresses are ordered correctly */ if (ctx.ip1 > ctx.ip2) { g_warning("%s, line %d: invalid IP order in \"%s\"", gip_source[GIP_IPV4].file, linenum, line); return; } /* * Skip spaces until the country code. */ while ((c = *end)) { if (!is_ascii_space(c)) break; end++; } if (c == '\0') { g_warning("%s, line %d: missing country code in \"%s\"", gip_source[GIP_IPV4].file, linenum, line); return; } code = iso3166_encode_cc(end); if (ISO3166_INVALID == code) { g_warning("%s, line %d: bad country code in \"%s\"", gip_source[GIP_IPV4].file, linenum, line); return; } /* code must not be zero and the LSB must be zero due to using it as * as key for ipranges */ ctx.country = (code + 1) << 1; ctx.line = line; ctx.linenum = linenum; /* * Now compute the CIDR ranges between the ip1 and ip2 addresses * and insert each range into the database, linking it to the * country code. */ ip_range_split(ctx.ip1, ctx.ip2, gip_add_cidr, &ctx); }