"Fossies" - the Fresh Open Source Software Archive

Member "bind-9.16.7/lib/dns/geoip2.c" (4 Sep 2020, 9751 Bytes) of package /linux/misc/dns/bind9/9.16.7/bind-9.16.7.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "geoip2.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 9.11.22_vs_9.11.23.

    1 /*
    2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
    3  *
    4  * This Source Code Form is subject to the terms of the Mozilla Public
    5  * License, v. 2.0. If a copy of the MPL was not distributed with this
    6  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
    7  *
    8  * See the COPYRIGHT file distributed with this work for additional
    9  * information regarding copyright ownership.
   10  */
   11 
   12 /*! \file */
   13 
   14 #include <inttypes.h>
   15 #include <stdbool.h>
   16 #include <stdlib.h>
   17 
   18 /*
   19  * This file is only built and linked if GeoIP2 has been configured.
   20  */
   21 #include <math.h>
   22 #include <maxminddb.h>
   23 
   24 #include <isc/mem.h>
   25 #include <isc/once.h>
   26 #include <isc/sockaddr.h>
   27 #include <isc/string.h>
   28 #include <isc/thread.h>
   29 #include <isc/util.h>
   30 
   31 #include <dns/acl.h>
   32 #include <dns/geoip.h>
   33 #ifndef WIN32
   34 #include <netinet/in.h>
   35 #else /* ifndef WIN32 */
   36 #ifndef _WINSOCKAPI_
   37 #define _WINSOCKAPI_ /* Prevent inclusion of winsock.h in windows.h */
   38 #endif           /* ifndef _WINSOCKAPI_ */
   39 #include <winsock2.h>
   40 #endif /* WIN32 */
   41 #include <dns/log.h>
   42 
   43 /*
   44  * This structure preserves state from the previous GeoIP lookup,
   45  * so that successive lookups for the same data from the same IP
   46  * address will not require repeated database lookups.
   47  * This should improve performance somewhat.
   48  *
   49  * For all lookups we preserve pointers to the MMDB_lookup_result_s
   50  * and MMDB_entry_s structures, a pointer to the database from which
   51  * the lookup was answered, and a copy of the request address.
   52  *
   53  * If the next geoip ACL lookup is for the same database and from the
   54  * same address, we can reuse the MMDB entry without repeating the lookup.
   55  * This is for the case when a single query has to process multiple
   56  * geoip ACLs: for example, when there are multiple views with
   57  * match-clients statements that search for different countries.
   58  *
   59  * (XXX: Currently the persistent state is stored in thread specific
   60  * memory, but it could more simply be stored in the client object.
   61  * Also multiple entries could be stored in case the ACLs require
   62  * searching in more than one GeoIP database.)
   63  */
   64 
   65 typedef struct geoip_state {
   66     uint16_t subtype;
   67     const MMDB_s *db;
   68     isc_netaddr_t addr;
   69     MMDB_lookup_result_s mmresult;
   70     MMDB_entry_s entry;
   71 } geoip_state_t;
   72 
   73 ISC_THREAD_LOCAL geoip_state_t geoip_state = { 0 };
   74 
   75 static void
   76 set_state(const MMDB_s *db, const isc_netaddr_t *addr,
   77       MMDB_lookup_result_s mmresult, MMDB_entry_s entry) {
   78     geoip_state.db = db;
   79     geoip_state.addr = *addr;
   80     geoip_state.mmresult = mmresult;
   81     geoip_state.entry = entry;
   82 }
   83 
   84 static geoip_state_t *
   85 get_entry_for(MMDB_s *const db, const isc_netaddr_t *addr) {
   86     isc_sockaddr_t sa;
   87     MMDB_lookup_result_s match;
   88     int err;
   89 
   90     if (db == geoip_state.db && isc_netaddr_equal(addr, &geoip_state.addr))
   91     {
   92         return (&geoip_state);
   93     }
   94 
   95     isc_sockaddr_fromnetaddr(&sa, addr, 0);
   96     match = MMDB_lookup_sockaddr(db, &sa.type.sa, &err);
   97     if (err != MMDB_SUCCESS || !match.found_entry) {
   98         return (NULL);
   99     }
  100 
  101     set_state(db, addr, match, match.entry);
  102 
  103     return (&geoip_state);
  104 }
  105 
  106 static dns_geoip_subtype_t
  107 fix_subtype(const dns_geoip_databases_t *geoip, dns_geoip_subtype_t subtype) {
  108     dns_geoip_subtype_t ret = subtype;
  109 
  110     switch (subtype) {
  111     case dns_geoip_countrycode:
  112         if (geoip->city != NULL) {
  113             ret = dns_geoip_city_countrycode;
  114         } else if (geoip->country != NULL) {
  115             ret = dns_geoip_country_code;
  116         }
  117         break;
  118     case dns_geoip_countryname:
  119         if (geoip->city != NULL) {
  120             ret = dns_geoip_city_countryname;
  121         } else if (geoip->country != NULL) {
  122             ret = dns_geoip_country_name;
  123         }
  124         break;
  125     case dns_geoip_continentcode:
  126         if (geoip->city != NULL) {
  127             ret = dns_geoip_city_continentcode;
  128         } else if (geoip->country != NULL) {
  129             ret = dns_geoip_country_continentcode;
  130         }
  131         break;
  132     case dns_geoip_continent:
  133         if (geoip->city != NULL) {
  134             ret = dns_geoip_city_continent;
  135         } else if (geoip->country != NULL) {
  136             ret = dns_geoip_country_continent;
  137         }
  138         break;
  139     case dns_geoip_region:
  140         if (geoip->city != NULL) {
  141             ret = dns_geoip_city_region;
  142         }
  143         break;
  144     case dns_geoip_regionname:
  145         if (geoip->city != NULL) {
  146             ret = dns_geoip_city_regionname;
  147         }
  148     default:
  149         break;
  150     }
  151 
  152     return (ret);
  153 }
  154 
  155 static MMDB_s *
  156 geoip2_database(const dns_geoip_databases_t *geoip,
  157         dns_geoip_subtype_t subtype) {
  158     switch (subtype) {
  159     case dns_geoip_country_code:
  160     case dns_geoip_country_name:
  161     case dns_geoip_country_continentcode:
  162     case dns_geoip_country_continent:
  163         return (geoip->country);
  164 
  165     case dns_geoip_city_countrycode:
  166     case dns_geoip_city_countryname:
  167     case dns_geoip_city_continentcode:
  168     case dns_geoip_city_continent:
  169     case dns_geoip_city_region:
  170     case dns_geoip_city_regionname:
  171     case dns_geoip_city_name:
  172     case dns_geoip_city_postalcode:
  173     case dns_geoip_city_timezonecode:
  174     case dns_geoip_city_metrocode:
  175     case dns_geoip_city_areacode:
  176         return (geoip->city);
  177 
  178     case dns_geoip_isp_name:
  179         return (geoip->isp);
  180 
  181     case dns_geoip_as_asnum:
  182     case dns_geoip_org_name:
  183         return (geoip->as);
  184 
  185     case dns_geoip_domain_name:
  186         return (geoip->domain);
  187 
  188     default:
  189         /*
  190          * All other subtypes are unavailable in GeoIP2.
  191          */
  192         return (NULL);
  193     }
  194 }
  195 
  196 static bool
  197 match_string(MMDB_entry_data_s *value, const char *str) {
  198     REQUIRE(str != NULL);
  199 
  200     if (value == NULL || !value->has_data ||
  201         value->type != MMDB_DATA_TYPE_UTF8_STRING ||
  202         value->utf8_string == NULL)
  203     {
  204         return (false);
  205     }
  206 
  207     return (strncasecmp(value->utf8_string, str, value->data_size) == 0);
  208 }
  209 
  210 static bool
  211 match_int(MMDB_entry_data_s *value, const uint32_t ui32) {
  212     if (value == NULL || !value->has_data ||
  213         (value->type != MMDB_DATA_TYPE_UINT32 &&
  214          value->type != MMDB_DATA_TYPE_UINT16))
  215     {
  216         return (false);
  217     }
  218 
  219     return (value->uint32 == ui32);
  220 }
  221 
  222 bool
  223 dns_geoip_match(const isc_netaddr_t *reqaddr,
  224         const dns_geoip_databases_t *geoip,
  225         const dns_geoip_elem_t *elt) {
  226     MMDB_s *db = NULL;
  227     MMDB_entry_data_s value;
  228     geoip_state_t *state = NULL;
  229     dns_geoip_subtype_t subtype;
  230     const char *s = NULL;
  231     int ret;
  232 
  233     REQUIRE(reqaddr != NULL);
  234     REQUIRE(elt != NULL);
  235     REQUIRE(geoip != NULL);
  236 
  237     subtype = fix_subtype(geoip, elt->subtype);
  238     db = geoip2_database(geoip, subtype);
  239     if (db == NULL) {
  240         return (false);
  241     }
  242 
  243     state = get_entry_for(db, reqaddr);
  244     if (state == NULL) {
  245         return (false);
  246     }
  247 
  248     switch (subtype) {
  249     case dns_geoip_country_code:
  250     case dns_geoip_city_countrycode:
  251         ret = MMDB_get_value(&state->entry, &value, "country",
  252                      "iso_code", (char *)0);
  253         if (ret == MMDB_SUCCESS) {
  254             return (match_string(&value, elt->as_string));
  255         }
  256         break;
  257 
  258     case dns_geoip_country_name:
  259     case dns_geoip_city_countryname:
  260         ret = MMDB_get_value(&state->entry, &value, "country", "names",
  261                      "en", (char *)0);
  262         if (ret == MMDB_SUCCESS) {
  263             return (match_string(&value, elt->as_string));
  264         }
  265         break;
  266 
  267     case dns_geoip_country_continentcode:
  268     case dns_geoip_city_continentcode:
  269         ret = MMDB_get_value(&state->entry, &value, "continent", "code",
  270                      (char *)0);
  271         if (ret == MMDB_SUCCESS) {
  272             return (match_string(&value, elt->as_string));
  273         }
  274         break;
  275 
  276     case dns_geoip_country_continent:
  277     case dns_geoip_city_continent:
  278         ret = MMDB_get_value(&state->entry, &value, "continent",
  279                      "names", "en", (char *)0);
  280         if (ret == MMDB_SUCCESS) {
  281             return (match_string(&value, elt->as_string));
  282         }
  283         break;
  284 
  285     case dns_geoip_region:
  286     case dns_geoip_city_region:
  287         ret = MMDB_get_value(&state->entry, &value, "subdivisions", "0",
  288                      "iso_code", (char *)0);
  289         if (ret == MMDB_SUCCESS) {
  290             return (match_string(&value, elt->as_string));
  291         }
  292         break;
  293 
  294     case dns_geoip_regionname:
  295     case dns_geoip_city_regionname:
  296         ret = MMDB_get_value(&state->entry, &value, "subdivisions", "0",
  297                      "names", "en", (char *)0);
  298         if (ret == MMDB_SUCCESS) {
  299             return (match_string(&value, elt->as_string));
  300         }
  301         break;
  302 
  303     case dns_geoip_city_name:
  304         ret = MMDB_get_value(&state->entry, &value, "city", "names",
  305                      "en", (char *)0);
  306         if (ret == MMDB_SUCCESS) {
  307             return (match_string(&value, elt->as_string));
  308         }
  309         break;
  310 
  311     case dns_geoip_city_postalcode:
  312         ret = MMDB_get_value(&state->entry, &value, "postal", "code",
  313                      (char *)0);
  314         if (ret == MMDB_SUCCESS) {
  315             return (match_string(&value, elt->as_string));
  316         }
  317         break;
  318 
  319     case dns_geoip_city_timezonecode:
  320         ret = MMDB_get_value(&state->entry, &value, "location",
  321                      "time_zone", (char *)0);
  322         if (ret == MMDB_SUCCESS) {
  323             return (match_string(&value, elt->as_string));
  324         }
  325         break;
  326 
  327     case dns_geoip_city_metrocode:
  328         ret = MMDB_get_value(&state->entry, &value, "location",
  329                      "metro_code", (char *)0);
  330         if (ret == MMDB_SUCCESS) {
  331             return (match_string(&value, elt->as_string));
  332         }
  333         break;
  334 
  335     case dns_geoip_isp_name:
  336         ret = MMDB_get_value(&state->entry, &value, "isp", (char *)0);
  337         if (ret == MMDB_SUCCESS) {
  338             return (match_string(&value, elt->as_string));
  339         }
  340         break;
  341 
  342     case dns_geoip_as_asnum:
  343         INSIST(elt->as_string != NULL);
  344 
  345         ret = MMDB_get_value(&state->entry, &value,
  346                      "autonomous_system_number", (char *)0);
  347         if (ret == MMDB_SUCCESS) {
  348             int i;
  349             s = elt->as_string;
  350             if (strncasecmp(s, "AS", 2) == 0) {
  351                 s += 2;
  352             }
  353             i = strtol(s, NULL, 10);
  354             return (match_int(&value, i));
  355         }
  356         break;
  357 
  358     case dns_geoip_org_name:
  359         ret = MMDB_get_value(&state->entry, &value,
  360                      "autonomous_system_organization",
  361                      (char *)0);
  362         if (ret == MMDB_SUCCESS) {
  363             return (match_string(&value, elt->as_string));
  364         }
  365         break;
  366 
  367     case dns_geoip_domain_name:
  368         ret = MMDB_get_value(&state->entry, &value, "domain",
  369                      (char *)0);
  370         if (ret == MMDB_SUCCESS) {
  371             return (match_string(&value, elt->as_string));
  372         }
  373         break;
  374 
  375     default:
  376         /*
  377          * For any other subtype, we assume the database was
  378          * unavailable and return false.
  379          */
  380         return (false);
  381     }
  382 
  383     /*
  384      * No database matched: return false.
  385      */
  386     return (false);
  387 }