"Fossies" - the Fresh Open Source Software Archive

Member "bind-9.11.23/lib/dns/geoip2.c" (7 Sep 2020, 14733 Bytes) of package /linux/misc/dns/bind9/9.11.23/bind-9.11.23.tar.gz:


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 latest 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 <config.h>
   15 
   16 #include <inttypes.h>
   17 #include <stdbool.h>
   18 #include <stdlib.h>
   19 
   20 /*
   21  * This file is only built and linked if GeoIP2 has been configured.
   22  */
   23 #include <maxminddb.h>
   24 
   25 #include <isc/mem.h>
   26 #include <isc/once.h>
   27 #include <isc/platform.h>
   28 #include <isc/sockaddr.h>
   29 #include <isc/string.h>
   30 #include <isc/thread.h>
   31 #include <isc/util.h>
   32 
   33 #include <dns/acl.h>
   34 #include <dns/geoip.h>
   35 
   36 #include <math.h>
   37 #ifndef WIN32
   38 #include <netinet/in.h>
   39 #else
   40 #ifndef _WINSOCKAPI_
   41 #define _WINSOCKAPI_   /* Prevent inclusion of winsock.h in windows.h */
   42 #endif
   43 #include <winsock2.h>
   44 #endif  /* WIN32 */
   45 #include <dns/log.h>
   46 
   47 /*
   48  * This structure preserves state from the previous GeoIP lookup,
   49  * so that successive lookups for the same data from the same IP
   50  * address will not require repeated database lookups.
   51  * This should improve performance somewhat.
   52  *
   53  * For all lookups we preserve pointers to the MMDB_lookup_result_s
   54  * and MMDB_entry_s structures, a pointer to the database from which
   55  * the lookup was answered, and a copy of the request address.
   56  *
   57  * If the next geoip ACL lookup is for the same database and from the
   58  * same address, we can reuse the MMDB entry without repeating the lookup.
   59  * This is for the case when a single query has to process multiple
   60  * geoip ACLs: for example, when there are multiple views with
   61  * match-clients statements that search for different countries.
   62  *
   63  * (XXX: Currently the persistent state is stored in thread specific
   64  * memory, but it could more simply be stored in the client object.
   65  * Also multiple entries could be stored in case the ACLs require
   66  * searching in more than one GeoIP database.)
   67  */
   68 
   69 typedef struct geoip_state {
   70     isc_mem_t *mctx;
   71     uint16_t subtype;
   72     const MMDB_s *db;
   73     isc_netaddr_t addr;
   74     MMDB_lookup_result_s mmresult;
   75     MMDB_entry_s entry;
   76 } geoip_state_t;
   77 
   78 #ifdef ISC_PLATFORM_USETHREADS
   79 static isc_mutex_t key_mutex;
   80 static bool state_key_initialized = false;
   81 static isc_thread_key_t state_key;
   82 static isc_once_t mutex_once = ISC_ONCE_INIT;
   83 #else
   84 static geoip_state_t *saved_state = NULL;
   85 #endif
   86 static isc_mem_t *state_mctx = NULL;
   87 
   88 #ifdef ISC_PLATFORM_USETHREADS
   89 static void
   90 key_mutex_init(void) {
   91     RUNTIME_CHECK(isc_mutex_init(&key_mutex) == ISC_R_SUCCESS);
   92 }
   93 
   94 static void
   95 free_state(void *arg) {
   96     geoip_state_t *state = arg;
   97     if (state != NULL) {
   98         isc_mem_putanddetach(&state->mctx,
   99                      state, sizeof(geoip_state_t));
  100     }
  101     isc_thread_key_setspecific(state_key, NULL);
  102 }
  103 
  104 static isc_result_t
  105 state_key_init(void) {
  106     isc_result_t result;
  107 
  108     result = isc_once_do(&mutex_once, key_mutex_init);
  109     if (result != ISC_R_SUCCESS) {
  110         return (result);
  111     }
  112 
  113     if (!state_key_initialized) {
  114         LOCK(&key_mutex);
  115         if (!state_key_initialized) {
  116             int ret;
  117 
  118             if (state_mctx == NULL) {
  119                 result = isc_mem_create(0, 0, &state_mctx);
  120             }
  121             if (result != ISC_R_SUCCESS) {
  122                 goto unlock;
  123             }
  124             isc_mem_setname(state_mctx, "geoip_state", NULL);
  125             isc_mem_setdestroycheck(state_mctx, false);
  126 
  127             ret = isc_thread_key_create(&state_key, free_state);
  128             if (ret == 0) {
  129                 state_key_initialized = true;
  130             } else {
  131                 result = ISC_R_FAILURE;
  132             }
  133         }
  134  unlock:
  135         UNLOCK(&key_mutex);
  136     }
  137 
  138     return (result);
  139 }
  140 #else
  141 static isc_result_t
  142 state_key_init(void) {
  143     isc_result_t result = ISC_R_SUCCESS;
  144 
  145     if (state_mctx == NULL) {
  146         result = isc_mem_create(0, 0, &state_mctx);
  147         isc_mem_setname(state_mctx, "geoip_state", NULL);
  148         isc_mem_setdestroycheck(state_mctx, false);
  149     }
  150 
  151     return (result);
  152 }
  153 #endif
  154 
  155 static isc_result_t
  156 set_state(const MMDB_s *db, const isc_netaddr_t *addr,
  157       MMDB_lookup_result_s mmresult, MMDB_entry_s entry,
  158       geoip_state_t **statep)
  159 {
  160     geoip_state_t *state = NULL;
  161     isc_result_t result;
  162 
  163     result = state_key_init();
  164     if (result != ISC_R_SUCCESS) {
  165         return (result);
  166     }
  167 
  168 #ifdef ISC_PLATFORM_USETHREADS
  169     state = (geoip_state_t *) isc_thread_key_getspecific(state_key);
  170 #else
  171     state = saved_state;
  172 #endif
  173     if (state == NULL) {
  174         state = (geoip_state_t *) isc_mem_get(state_mctx,
  175                               sizeof(geoip_state_t));
  176         if (state == NULL) {
  177             return (ISC_R_NOMEMORY);
  178         }
  179         memset(state, 0, sizeof(*state));
  180 
  181 #ifdef ISC_PLATFORM_USETHREADS
  182         result = isc_thread_key_setspecific(state_key, state);
  183         if (result != ISC_R_SUCCESS) {
  184             isc_mem_put(state_mctx, state, sizeof(geoip_state_t));
  185             return (result);
  186         }
  187 #else
  188         saved_state = state;
  189 #endif
  190 
  191         isc_mem_attach(state_mctx, &state->mctx);
  192     }
  193 
  194     state->db = db;
  195     state->addr = *addr;
  196     state->mmresult = mmresult;
  197     state->entry = entry;
  198 
  199     if (statep != NULL) {
  200         *statep = state;
  201     }
  202 
  203     return (ISC_R_SUCCESS);
  204 }
  205 
  206 static geoip_state_t *
  207 get_entry_for(MMDB_s * const db, const isc_netaddr_t *addr) {
  208     isc_result_t result;
  209     isc_sockaddr_t sa;
  210     geoip_state_t *state;
  211     MMDB_lookup_result_s match;
  212     int err;
  213 
  214     result = state_key_init();
  215     if (result != ISC_R_SUCCESS) {
  216         return (NULL);
  217     }
  218 
  219 #ifdef ISC_PLATFORM_USETHREADS
  220     state = (geoip_state_t *) isc_thread_key_getspecific(state_key);
  221 #else
  222     state = saved_state;
  223 #endif
  224     if (state != NULL) {
  225         if (db == state->db && isc_netaddr_equal(addr, &state->addr)) {
  226             return (state);
  227         }
  228     }
  229 
  230     isc_sockaddr_fromnetaddr(&sa, addr, 0);
  231     match = MMDB_lookup_sockaddr(db, &sa.type.sa, &err);
  232     if (err != MMDB_SUCCESS || !match.found_entry) {
  233         return (NULL);
  234     }
  235 
  236     result = set_state(db, addr, match, match.entry, &state);
  237     if (result != ISC_R_SUCCESS) {
  238         return (NULL);
  239     }
  240 
  241     return (state);
  242 }
  243 
  244 static dns_geoip_subtype_t
  245 fix_subtype(const dns_geoip_databases_t *geoip, dns_geoip_subtype_t subtype) {
  246     dns_geoip_subtype_t ret = subtype;
  247 
  248     switch (subtype) {
  249     case dns_geoip_countrycode:
  250         if (geoip->city != NULL) {
  251             ret = dns_geoip_city_countrycode;
  252         } else if (geoip->country != NULL) {
  253             ret = dns_geoip_country_code;
  254         }
  255         break;
  256     case dns_geoip_countryname:
  257         if (geoip->city != NULL) {
  258             ret = dns_geoip_city_countryname;
  259         } else if (geoip->country != NULL) {
  260             ret = dns_geoip_country_name;
  261         }
  262         break;
  263     case dns_geoip_continentcode:
  264         if (geoip->city != NULL) {
  265             ret = dns_geoip_city_continentcode;
  266         } else if (geoip->country != NULL) {
  267             ret = dns_geoip_country_continentcode;
  268         }
  269         break;
  270     case dns_geoip_continent:
  271         if (geoip->city != NULL) {
  272             ret = dns_geoip_city_continent;
  273         } else if (geoip->country != NULL) {
  274             ret = dns_geoip_country_continent;
  275         }
  276         break;
  277     case dns_geoip_region:
  278         if (geoip->city != NULL) {
  279             ret = dns_geoip_city_region;
  280         }
  281         break;
  282     case dns_geoip_regionname:
  283         if (geoip->city != NULL) {
  284             ret = dns_geoip_city_regionname;
  285         }
  286     default:
  287         break;
  288     }
  289 
  290     return (ret);
  291 }
  292 
  293 static MMDB_s *
  294 geoip2_database(const dns_geoip_databases_t *geoip,
  295         dns_geoip_subtype_t subtype)
  296 {
  297     switch (subtype) {
  298     case dns_geoip_country_code:
  299     case dns_geoip_country_name:
  300     case dns_geoip_country_continentcode:
  301     case dns_geoip_country_continent:
  302         return (geoip->country);
  303 
  304     case dns_geoip_city_countrycode:
  305     case dns_geoip_city_countryname:
  306     case dns_geoip_city_continentcode:
  307     case dns_geoip_city_continent:
  308     case dns_geoip_city_region:
  309     case dns_geoip_city_regionname:
  310     case dns_geoip_city_name:
  311     case dns_geoip_city_postalcode:
  312     case dns_geoip_city_timezonecode:
  313     case dns_geoip_city_metrocode:
  314     case dns_geoip_city_areacode:
  315         return (geoip->city);
  316 
  317     case dns_geoip_isp_name:
  318         return (geoip->isp);
  319 
  320     case dns_geoip_as_asnum:
  321     case dns_geoip_org_name:
  322         return (geoip->as);
  323 
  324     case dns_geoip_domain_name:
  325         return (geoip->domain);
  326 
  327     default:
  328         /*
  329          * All other subtypes are unavailable in GeoIP2.
  330          */
  331         return (NULL);
  332     }
  333 }
  334 
  335 static bool
  336 match_string(MMDB_entry_data_s *value, const char *str) {
  337     REQUIRE(str != NULL);
  338 
  339     if (value == NULL || !value->has_data ||
  340         value->type != MMDB_DATA_TYPE_UTF8_STRING ||
  341         value->utf8_string == NULL)
  342     {
  343         return (false);
  344     }
  345 
  346     return (strncasecmp(value->utf8_string, str, value->data_size) == 0);
  347 }
  348 
  349 static bool
  350 match_int(MMDB_entry_data_s *value, const uint32_t ui32) {
  351     if (value == NULL || !value->has_data ||
  352         (value->type != MMDB_DATA_TYPE_UINT32 &&
  353          value->type != MMDB_DATA_TYPE_UINT16))
  354     {
  355         return (false);
  356     }
  357 
  358     return (value->uint32 == ui32);
  359 }
  360 
  361 bool
  362 dns_geoip_match(const isc_netaddr_t *reqaddr, uint8_t *scope,
  363         const dns_geoip_databases_t *geoip,
  364         const dns_geoip_elem_t *elt)
  365 {
  366     MMDB_s *db = NULL;
  367     MMDB_entry_data_s value;
  368     geoip_state_t *state = NULL;
  369     dns_geoip_subtype_t subtype;
  370     const char *s = NULL;
  371     int ret;
  372 
  373     REQUIRE(reqaddr != NULL);
  374     REQUIRE(elt != NULL);
  375     REQUIRE(geoip != NULL);
  376 
  377     subtype = fix_subtype(geoip, elt->subtype);
  378     db = geoip2_database(geoip, subtype);
  379     if (db == NULL) {
  380         return (false);
  381     }
  382 
  383     state = get_entry_for(db, reqaddr);
  384     if (state == NULL) {
  385         return (false);
  386     }
  387 
  388     switch (subtype) {
  389     case dns_geoip_country_code:
  390     case dns_geoip_city_countrycode:
  391         ret = MMDB_get_value(&state->entry, &value,
  392                      "country", "iso_code", (char *)0);
  393         if (ret == MMDB_SUCCESS) {
  394             if (scope != NULL) {
  395                 *scope = state->mmresult.netmask;
  396 #define isc_netaddr_pf(x) (x)->family
  397                 if (isc_netaddr_pf(reqaddr) == AF_INET) {
  398                     *scope -= 96;
  399                 }
  400             }
  401             return (match_string(&value, elt->as_string));
  402         }
  403         break;
  404 
  405     case dns_geoip_country_name:
  406     case dns_geoip_city_countryname:
  407         ret = MMDB_get_value(&state->entry, &value,
  408                      "country", "names", "en", (char *)0);
  409         if (ret == MMDB_SUCCESS) {
  410             if (scope != NULL) {
  411                 *scope = state->mmresult.netmask;
  412                 if (isc_netaddr_pf(reqaddr) == AF_INET) {
  413                     *scope -= 96;
  414                 }
  415             }
  416             return (match_string(&value, elt->as_string));
  417         }
  418         break;
  419 
  420     case dns_geoip_country_continentcode:
  421     case dns_geoip_city_continentcode:
  422         ret = MMDB_get_value(&state->entry, &value,
  423                      "continent", "code", (char *)0);
  424         if (ret == MMDB_SUCCESS) {
  425             if (scope != NULL) {
  426                 *scope = state->mmresult.netmask;
  427                 if (isc_netaddr_pf(reqaddr) == AF_INET) {
  428                     *scope -= 96;
  429                 }
  430             }
  431             return (match_string(&value, elt->as_string));
  432         }
  433         break;
  434 
  435     case dns_geoip_country_continent:
  436     case dns_geoip_city_continent:
  437         ret = MMDB_get_value(&state->entry, &value,
  438                      "continent", "names", "en", (char *)0);
  439         if (ret == MMDB_SUCCESS) {
  440             if (scope != NULL) {
  441                 *scope = state->mmresult.netmask;
  442                 if (isc_netaddr_pf(reqaddr) == AF_INET) {
  443                     *scope -= 96;
  444                 }
  445             }
  446             return (match_string(&value, elt->as_string));
  447         }
  448         break;
  449 
  450     case dns_geoip_region:
  451     case dns_geoip_city_region:
  452         ret = MMDB_get_value(&state->entry, &value,
  453                      "subdivisions", "0", "iso_code", (char *)0);
  454         if (ret == MMDB_SUCCESS) {
  455             if (scope != NULL) {
  456                 *scope = state->mmresult.netmask;
  457                 if (isc_netaddr_pf(reqaddr) == AF_INET) {
  458                     *scope -= 96;
  459                 }
  460             }
  461             return (match_string(&value, elt->as_string));
  462         }
  463         break;
  464 
  465     case dns_geoip_regionname:
  466     case dns_geoip_city_regionname:
  467         ret = MMDB_get_value(&state->entry, &value,
  468                      "subdivisions", "0", "names", "en", (char *)0);
  469         if (ret == MMDB_SUCCESS) {
  470             if (scope != NULL) {
  471                 *scope = state->mmresult.netmask;
  472                 if (isc_netaddr_pf(reqaddr) == AF_INET) {
  473                     *scope -= 96;
  474                 }
  475             }
  476             return (match_string(&value, elt->as_string));
  477         }
  478         break;
  479 
  480     case dns_geoip_city_name:
  481         ret = MMDB_get_value(&state->entry, &value,
  482                      "city", "names", "en", (char *)0);
  483         if (ret == MMDB_SUCCESS) {
  484             if (scope != NULL) {
  485                 *scope = state->mmresult.netmask;
  486                 if (isc_netaddr_pf(reqaddr) == AF_INET) {
  487                     *scope -= 96;
  488                 }
  489             }
  490             return (match_string(&value, elt->as_string));
  491         }
  492         break;
  493 
  494     case dns_geoip_city_postalcode:
  495         ret = MMDB_get_value(&state->entry, &value,
  496                      "postal", "code", (char *)0);
  497         if (ret == MMDB_SUCCESS) {
  498             if (scope != NULL) {
  499                 *scope = state->mmresult.netmask;
  500                 if (isc_netaddr_pf(reqaddr) == AF_INET) {
  501                     *scope -= 96;
  502                 }
  503             }
  504             return (match_string(&value, elt->as_string));
  505         }
  506         break;
  507 
  508     case dns_geoip_city_timezonecode:
  509         ret = MMDB_get_value(&state->entry, &value,
  510                      "location", "time_zone", (char *)0);
  511         if (ret == MMDB_SUCCESS) {
  512             if (scope != NULL) {
  513                 *scope = state->mmresult.netmask;
  514                 if (isc_netaddr_pf(reqaddr) == AF_INET) {
  515                     *scope -= 96;
  516                 }
  517             }
  518             return (match_string(&value, elt->as_string));
  519         }
  520         break;
  521 
  522 
  523     case dns_geoip_city_metrocode:
  524         ret = MMDB_get_value(&state->entry, &value,
  525                      "location", "metro_code", (char *)0);
  526         if (ret == MMDB_SUCCESS) {
  527             if (scope != NULL) {
  528                 *scope = state->mmresult.netmask;
  529                 if (isc_netaddr_pf(reqaddr) == AF_INET) {
  530                     *scope -= 96;
  531                 }
  532             }
  533             return (match_string(&value, elt->as_string));
  534         }
  535         break;
  536 
  537     case dns_geoip_isp_name:
  538         ret = MMDB_get_value(&state->entry, &value, "isp", (char *)0);
  539         if (ret == MMDB_SUCCESS) {
  540             if (scope != NULL) {
  541                 *scope = state->mmresult.netmask;
  542                 if (isc_netaddr_pf(reqaddr) == AF_INET) {
  543                     *scope -= 96;
  544                 }
  545             }
  546             return (match_string(&value, elt->as_string));
  547         }
  548         break;
  549 
  550     case dns_geoip_as_asnum:
  551         INSIST(elt->as_string != NULL);
  552 
  553         ret = MMDB_get_value(&state->entry, &value,
  554                      "autonomous_system_number", (char *)0);
  555         if (ret == MMDB_SUCCESS) {
  556             if (scope != NULL) {
  557                 *scope = state->mmresult.netmask;
  558                 if (isc_netaddr_pf(reqaddr) == AF_INET) {
  559                     *scope -= 96;
  560                 }
  561             }
  562 
  563             s = elt->as_string;
  564             if (strncasecmp(s, "AS", 2) == 0) {
  565                 s += 2;
  566             }
  567 
  568             return (match_int(&value, strtol(s, NULL, 10)));
  569         }
  570         break;
  571 
  572     case dns_geoip_org_name:
  573         ret = MMDB_get_value(&state->entry, &value,
  574                      "autonomous_system_organization", (char *)0);
  575         if (ret == MMDB_SUCCESS) {
  576             if (scope != NULL) {
  577                 *scope = state->mmresult.netmask;
  578                 if (isc_netaddr_pf(reqaddr) == AF_INET) {
  579                     *scope -= 96;
  580                 }
  581             }
  582             return (match_string(&value, elt->as_string));
  583         }
  584         break;
  585 
  586     case dns_geoip_domain_name:
  587         ret = MMDB_get_value(&state->entry, &value, "domain", (char *)0);
  588         if (ret == MMDB_SUCCESS) {
  589             if (scope != NULL) {
  590                 *scope = state->mmresult.netmask;
  591                 if (isc_netaddr_pf(reqaddr) == AF_INET) {
  592                     *scope -= 96;
  593                 }
  594             }
  595             return (match_string(&value, elt->as_string));
  596         }
  597         break;
  598 
  599     default:
  600         /*
  601          * For any other subtype, we assume the database was
  602          * unavailable and return false.
  603          */
  604         return (false);
  605     }
  606 
  607     /*
  608      * No database matched: return false.
  609      */
  610     return (false);
  611 }
  612 
  613 void
  614 dns_geoip_shutdown(void) {
  615 #ifndef ISC_PLATFORM_USETHREADS
  616     if (saved_state != NULL) {
  617         isc_mem_putanddetach(&saved_state->mctx,
  618                      saved_state, sizeof(geoip_state_t));
  619     }
  620 #endif
  621     if (state_mctx != NULL) {
  622         isc_mem_detach(&state_mctx);
  623     }
  624 }