nlist_t* nets_make_list(vscf_data_t* nets_cfg, dclists_t* dclists, const char* map_name) { nlist_t* nl = nlist_new(map_name, false); if(nets_cfg) { dmn_assert(vscf_is_hash(nets_cfg)); if(nets_parse(nets_cfg, dclists, map_name, nl)) { nlist_destroy(nl); nl = NULL; } } if(nl) { // This masks out the 5x v4-like spaces that we *never* // lookup directly. These "NN_UNDEF" dclists will // never be seen by runtime lookups. The only // reason these exist is so that supernets and // adjacent networks get proper masks. Otherwise // lookups in these nearby spaces might return // oversized edns-client-subnet masks and cause // the cache to affect lookup of these spaces... nlist_append(nl, start_v4mapped, 96, NN_UNDEF); nlist_append(nl, start_siit, 96, NN_UNDEF); nlist_append(nl, start_wkp, 96, NN_UNDEF); nlist_append(nl, start_6to4, 16, NN_UNDEF); nlist_append(nl, start_teredo, 32, NN_UNDEF); nlist_finish(nl); } return nl; }
F_NONNULL static void geoip2_list_xlate_recurse(geoip2_t* db, nlist_t* nl, struct in6_addr ip, unsigned depth, const uint32_t node_count, const uint32_t node_num) { dmn_assert(db); dmn_assert(nl); dmn_assert(depth < 129U); if(!depth) { log_err("plugin_geoip: map '%s': GeoIP2 database '%s': Error while traversing tree nodes: depth too low", db->map_name, db->pathname); siglongjmp(db->jbuf, 1); } // skip v4-like spaces other than canonical compat area if( (depth == 32 && ( !memcmp(ip.s6_addr, start_v4mapped, 12U) || !memcmp(ip.s6_addr, start_siit, 12U) || !memcmp(ip.s6_addr, start_wkp, 12U) )) || (depth == 96U && !memcmp(ip.s6_addr, start_teredo, 4U)) || (depth == 112U && !memcmp(ip.s6_addr, start_6to4, 2U)) ) return; MMDB_search_node_s node; int read_rv = MMDB_read_node(&db->mmdb, node_num, &node); if(read_rv != MMDB_SUCCESS) { log_err("plugin_geoip: map '%s': GeoIP2 database '%s': Error while traversing tree nodes: %s", db->map_name, db->pathname, MMDB_strerror(read_rv)); siglongjmp(db->jbuf, 1); } const uint32_t zero_node_num = node.left_record; const uint32_t one_node_num = node.right_record; const unsigned new_depth = depth - 1U; const unsigned mask = 128U - new_depth; if(zero_node_num >= node_count) nlist_append(nl, ip.s6_addr, mask, geoip2_get_dclist_cached(db, zero_node_num - node_count)); else geoip2_list_xlate_recurse(db, nl, ip, new_depth, node_count, zero_node_num); SETBIT_v6(ip.s6_addr, mask - 1U); if(one_node_num >= node_count) nlist_append(nl, ip.s6_addr, mask, geoip2_get_dclist_cached(db, one_node_num - node_count)); else geoip2_list_xlate_recurse(db, nl, ip, new_depth, node_count, one_node_num); }
// arguably, with at least some of the v4-like spaces we could simply translate and hope to de-dupe, // if we upgraded nlist_normalize1 to de-dupe matching dclists instead of failing them F_NONNULL static bool nets_parse(vscf_data_t* nets_cfg, dclists_t* dclists, const char* map_name, nlist_t* nl) { bool rv = false; const unsigned input_nnets = vscf_hash_get_len(nets_cfg); for(unsigned i = 0; i < input_nnets; i++) { // convert 192.0.2.0/24 -> anysin_t w/ mask in port field unsigned net_str_len = 0; const char* net_str_cfg = vscf_hash_get_key_byindex(nets_cfg, i, &net_str_len); char net_str[net_str_len + 1]; memcpy(net_str, net_str_cfg, net_str_len + 1); char* mask_str = strchr(net_str, '/'); if(!mask_str) { log_err("plugin_geoip: map '%s': nets entry '%s' does not parse as addr/mask", map_name, net_str); rv = true; break; } *mask_str++ = '\0'; dmn_anysin_t tempsin; int addr_err = gdnsd_anysin_getaddrinfo(net_str, mask_str, &tempsin); if(addr_err) { log_err("plugin_geoip: map '%s': nets entry '%s/%s' does not parse as addr/mask: %s", map_name, net_str, mask_str, gai_strerror(addr_err)); rv = true; break; } unsigned mask; uint8_t ipv6[16]; // now store the anysin data into net_t if(tempsin.sa.sa_family == AF_INET6) { mask = ntohs(tempsin.sin6.sin6_port); if(mask > 128) { log_err("plugin_geoip: map '%s': nets entry '%s/%s': illegal IPv6 mask (>128)", map_name, net_str, mask_str); rv = true; break; } memcpy(ipv6, tempsin.sin6.sin6_addr.s6_addr, 16); if(check_v4_issues(ipv6, mask)) { log_err("plugin_geoip: map '%s': 'nets' entry '%s/%s' covers illegal IPv4-like space, see the documentation for more info", map_name, net_str, mask_str); rv = true; break; } } else { dmn_assert(tempsin.sa.sa_family == AF_INET); mask = ntohs(tempsin.sin.sin_port) + 96U; if(mask > 128) { log_err("plugin_geoip: map '%s': nets entry '%s/%s': illegal IPv4 mask (>32)", map_name, net_str, mask_str); rv = true; break; } memset(ipv6, 0, 16); memcpy(&ipv6[12], &tempsin.sin.sin_addr.s_addr, 4); } // get dclist integer from rhs vscf_data_t* dc_cfg = vscf_hash_get_data_byindex(nets_cfg, i); const uint32_t dclist = dclists_find_or_add_vscf(dclists, dc_cfg, map_name, false); dmn_assert(dclist <= DCLIST_MAX); // auto not allowed here nlist_append(nl, ipv6, mask, dclist); } return rv; }