1 /* $NetBSD: geoip.c,v 1.1.1.6 2015/07/08 15:38:01 christos Exp $ */
4 * Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
11 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
12 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
13 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
14 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
15 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16 * PERFORMANCE OF THIS SOFTWARE.
27 #include <isc/string.h>
30 #include <dns/geoip.h>
32 #include <isc/thread.h>
35 #include <netinet/in.h>
38 #define _WINSOCKAPI_ /* Prevent inclusion of winsock.h in windows.h */
46 #include <GeoIPCity.h>
49 * This structure preserves state from the previous GeoIP lookup,
50 * so that successive lookups for the same data from the same IP
51 * address will not require repeated calls into the GeoIP library
52 * to look up data in the database. This should improve performance
55 * For lookups in the City and Region databases, we preserve pointers
56 * to the GeoIPRecord and GeoIPregion structures; these will need to be
57 * freed by GeoIPRecord_delete() and GeoIPRegion_delete().
59 * for lookups in ISP, AS, Org and Domain we prserve a pointer to
60 * the returned name; these must be freed by free().
62 * For lookups in Country we preserve a pointer to the text of
63 * the country code, name, etc (we use a different pointer for this
64 * than for the names returned by Org, ISP, etc, because those need
65 * to be freed but country lookups do not).
67 * For lookups in Netspeed we preserve the returned ID.
69 * XXX: Currently this mechanism is only used for IPv4 lookups; the
70 * family and addr6 fields are to be used IPv6 is added.
72 typedef struct geoip_state
{
85 #ifdef ISC_PLATFORM_USETHREADS
86 static isc_mutex_t key_mutex
;
87 static isc_boolean_t state_key_initialized
= ISC_FALSE
;
88 static isc_thread_key_t state_key
;
89 static isc_once_t mutex_once
= ISC_ONCE_INIT
;
90 static isc_mem_t
*state_mctx
= NULL
;
93 key_mutex_init(void) {
94 RUNTIME_CHECK(isc_mutex_init(&key_mutex
) == ISC_R_SUCCESS
);
98 free_state(void *arg
) {
99 geoip_state_t
*state
= arg
;
100 if (state
!= NULL
&& state
->record
!= NULL
)
101 GeoIPRecord_delete(state
->record
);
103 isc_mem_putanddetach(&state
->mctx
,
104 state
, sizeof(geoip_state_t
));
105 isc_thread_key_setspecific(state_key
, NULL
);
109 state_key_init(void) {
112 result
= isc_once_do(&mutex_once
, key_mutex_init
);
113 if (result
!= ISC_R_SUCCESS
)
116 if (!state_key_initialized
) {
118 if (!state_key_initialized
) {
121 if (state_mctx
== NULL
)
122 result
= isc_mem_create2(0, 0, &state_mctx
, 0);
123 if (result
!= ISC_R_SUCCESS
)
125 isc_mem_setname(state_mctx
, "geoip_state", NULL
);
126 isc_mem_setdestroycheck(state_mctx
, ISC_FALSE
);
128 ret
= isc_thread_key_create(&state_key
, free_state
);
130 state_key_initialized
= ISC_TRUE
;
132 result
= ISC_R_FAILURE
;
141 geoip_state_t prev_state
;
145 clean_state(geoip_state_t
*state
) {
149 if (state
->record
!= NULL
) {
150 GeoIPRecord_delete(state
->record
);
151 state
->record
= NULL
;
153 if (state
->region
!= NULL
) {
154 GeoIPRegion_delete(state
->region
);
155 state
->region
= NULL
;
157 if (state
->name
!= NULL
) {
167 set_state(unsigned int family
, isc_uint32_t ipnum
, const geoipv6_t
*ipnum6
,
168 dns_geoip_subtype_t subtype
, GeoIPRecord
*record
,
169 GeoIPRegion
*region
, char *name
, const char *text
, int id
)
171 geoip_state_t
*state
= NULL
;
173 #ifdef ISC_PLATFORM_USETHREADS
176 result
= state_key_init();
177 if (result
!= ISC_R_SUCCESS
)
180 state
= (geoip_state_t
*) isc_thread_key_getspecific(state_key
);
182 state
= (geoip_state_t
*) isc_mem_get(state_mctx
,
183 sizeof(geoip_state_t
));
185 return (ISC_R_NOMEMORY
);
186 memset(state
, 0, sizeof(*state
));
188 result
= isc_thread_key_setspecific(state_key
, state
);
189 if (result
!= ISC_R_SUCCESS
) {
190 isc_mem_put(state_mctx
, state
, sizeof(geoip_state_t
));
194 isc_mem_attach(state_mctx
, &state
->mctx
);
202 if (family
== AF_INET
)
203 state
->ipnum
= ipnum
;
205 state
->ipnum6
= *ipnum6
;
207 state
->family
= family
;
208 state
->subtype
= subtype
;
209 state
->record
= record
;
210 state
->region
= region
;
215 return (ISC_R_SUCCESS
);
218 static geoip_state_t
*
219 get_state_for(unsigned int family
, isc_uint32_t ipnum
,
220 const geoipv6_t
*ipnum6
)
222 geoip_state_t
*state
;
224 #ifdef ISC_PLATFORM_USETHREADS
227 result
= state_key_init();
228 if (result
!= ISC_R_SUCCESS
)
231 state
= (geoip_state_t
*) isc_thread_key_getspecific(state_key
);
238 if (state
->family
== family
&&
239 ((state
->family
== AF_INET
&& state
->ipnum
== ipnum
) ||
240 (state
->family
== AF_INET6
&& ipnum6
!= NULL
&&
241 memcmp(state
->ipnum6
.s6_addr
, ipnum6
->s6_addr
, 16) == 0)))
248 * Country lookups are performed if the previous lookup was from a
249 * different IP address than the current, or was for a search of a
253 country_lookup(GeoIP
*db
, dns_geoip_subtype_t subtype
,
255 isc_uint32_t ipnum
, const geoipv6_t
*ipnum6
)
257 geoip_state_t
*prev_state
= NULL
;
258 const char *text
= NULL
;
262 #ifndef HAVE_GEOIP_V6
263 /* no IPv6 support? give up now */
264 if (family
== AF_INET6
)
268 prev_state
= get_state_for(family
, ipnum
, ipnum6
);
269 if (prev_state
!= NULL
&& prev_state
->subtype
== subtype
)
270 text
= prev_state
->text
;
274 case dns_geoip_country_code
:
275 if (family
== AF_INET
)
276 text
= GeoIP_country_code_by_ipnum(db
, ipnum
);
279 text
= GeoIP_country_code_by_ipnum_v6(db
,
283 case dns_geoip_country_code3
:
284 if (family
== AF_INET
)
285 text
= GeoIP_country_code3_by_ipnum(db
, ipnum
);
288 text
= GeoIP_country_code3_by_ipnum_v6(db
,
292 case dns_geoip_country_name
:
293 if (family
== AF_INET
)
294 text
= GeoIP_country_name_by_ipnum(db
, ipnum
);
297 text
= GeoIP_country_name_by_ipnum_v6(db
,
305 set_state(family
, ipnum
, ipnum6
, subtype
,
306 NULL
, NULL
, NULL
, text
, 0);
313 city_string(GeoIPRecord
*record
, dns_geoip_subtype_t subtype
, int *maxlen
) {
317 REQUIRE(record
!= NULL
);
318 REQUIRE(maxlen
!= NULL
);
320 /* Set '*maxlen' to the maximum length of this subtype, if any */
322 case dns_geoip_city_countrycode
:
323 case dns_geoip_city_region
:
324 case dns_geoip_city_continentcode
:
328 case dns_geoip_city_countrycode3
:
333 /* No fixed length; just use strcasecmp() for comparison */
338 case dns_geoip_city_countrycode
:
339 return (record
->country_code
);
340 case dns_geoip_city_countrycode3
:
341 return (record
->country_code3
);
342 case dns_geoip_city_countryname
:
343 return (record
->country_name
);
344 case dns_geoip_city_region
:
345 return (record
->region
);
346 case dns_geoip_city_regionname
:
347 s
= GeoIP_region_name_by_code(record
->country_code
,
349 DE_CONST(s
, deconst
);
351 case dns_geoip_city_name
:
352 return (record
->city
);
353 case dns_geoip_city_postalcode
:
354 return (record
->postal_code
);
355 case dns_geoip_city_continentcode
:
356 return (record
->continent_code
);
357 case dns_geoip_city_timezonecode
:
358 s
= GeoIP_time_zone_by_country_and_region(record
->country_code
,
360 DE_CONST(s
, deconst
);
368 is_city(dns_geoip_subtype_t subtype
) {
370 case dns_geoip_city_countrycode
:
371 case dns_geoip_city_countrycode3
:
372 case dns_geoip_city_countryname
:
373 case dns_geoip_city_region
:
374 case dns_geoip_city_regionname
:
375 case dns_geoip_city_name
:
376 case dns_geoip_city_postalcode
:
377 case dns_geoip_city_continentcode
:
378 case dns_geoip_city_timezonecode
:
379 case dns_geoip_city_metrocode
:
380 case dns_geoip_city_areacode
:
388 * GeoIPRecord lookups are performed if the previous lookup was
389 * from a different IP address than the current, or was for a search
390 * outside the City database.
393 city_lookup(GeoIP
*db
, dns_geoip_subtype_t subtype
,
394 unsigned int family
, isc_uint32_t ipnum
, const geoipv6_t
*ipnum6
)
396 GeoIPRecord
*record
= NULL
;
397 geoip_state_t
*prev_state
= NULL
;
401 #ifndef HAVE_GEOIP_V6
402 /* no IPv6 support? give up now */
403 if (family
== AF_INET6
)
407 prev_state
= get_state_for(family
, ipnum
, ipnum6
);
408 if (prev_state
!= NULL
&& is_city(prev_state
->subtype
))
409 record
= prev_state
->record
;
411 if (record
== NULL
) {
412 if (family
== AF_INET
)
413 record
= GeoIP_record_by_ipnum(db
, ipnum
);
416 record
= GeoIP_record_by_ipnum_v6(db
, *ipnum6
);
421 set_state(family
, ipnum
, ipnum6
, subtype
,
422 record
, NULL
, NULL
, NULL
, 0);
429 region_string(GeoIPRegion
*region
, dns_geoip_subtype_t subtype
, int *maxlen
) {
433 REQUIRE(region
!= NULL
);
434 REQUIRE(maxlen
!= NULL
);
437 case dns_geoip_region_countrycode
:
439 return (region
->country_code
);
440 case dns_geoip_region_code
:
442 return (region
->region
);
443 case dns_geoip_region_name
:
445 s
= GeoIP_region_name_by_code(region
->country_code
,
447 DE_CONST(s
, deconst
);
455 is_region(dns_geoip_subtype_t subtype
) {
457 case dns_geoip_region_countrycode
:
458 case dns_geoip_region_code
:
466 * GeoIPRegion lookups are performed if the previous lookup was
467 * from a different IP address than the current, or was for a search
468 * outside the Region database.
471 region_lookup(GeoIP
*db
, dns_geoip_subtype_t subtype
, isc_uint32_t ipnum
) {
472 GeoIPRegion
*region
= NULL
;
473 geoip_state_t
*prev_state
= NULL
;
477 prev_state
= get_state_for(AF_INET
, ipnum
, NULL
);
478 if (prev_state
!= NULL
&& is_region(prev_state
->subtype
))
479 region
= prev_state
->region
;
481 if (region
== NULL
) {
482 region
= GeoIP_region_by_ipnum(db
, ipnum
);
486 set_state(AF_INET
, ipnum
, NULL
,
487 subtype
, NULL
, region
, NULL
, NULL
, 0);
494 * ISP, Organization, AS Number and Domain lookups are performed if
495 * the previous lookup was from a different IP address than the current,
496 * or was for a search of a different subtype.
499 name_lookup(GeoIP
*db
, dns_geoip_subtype_t subtype
, isc_uint32_t ipnum
) {
501 geoip_state_t
*prev_state
= NULL
;
505 prev_state
= get_state_for(AF_INET
, ipnum
, NULL
);
506 if (prev_state
!= NULL
&& prev_state
->subtype
== subtype
)
507 name
= prev_state
->name
;
510 name
= GeoIP_name_by_ipnum(db
, ipnum
);
514 set_state(AF_INET
, ipnum
, NULL
,
515 subtype
, NULL
, NULL
, name
, NULL
, 0);
522 * Netspeed lookups are performed if the previous lookup was from a
523 * different IP address than the current, or was for a search of a
527 netspeed_lookup(GeoIP
*db
, dns_geoip_subtype_t subtype
, isc_uint32_t ipnum
) {
528 geoip_state_t
*prev_state
= NULL
;
529 isc_boolean_t found
= ISC_FALSE
;
534 prev_state
= get_state_for(AF_INET
, ipnum
, NULL
);
535 if (prev_state
!= NULL
&& prev_state
->subtype
== subtype
) {
541 id
= GeoIP_id_by_ipnum(db
, ipnum
);
542 set_state(AF_INET
, ipnum
, NULL
,
543 subtype
, NULL
, NULL
, NULL
, NULL
, id
);
548 #endif /* HAVE_GEOIP */
550 #define DB46(addr, geoip, name) \
551 ((addr->family == AF_INET) ? (geoip->name##_v4) : (geoip->name##_v6))
555 * Find the best database to answer a generic subtype
557 static dns_geoip_subtype_t
558 fix_subtype(const isc_netaddr_t
*reqaddr
, const dns_geoip_databases_t
*geoip
,
559 dns_geoip_subtype_t subtype
)
561 dns_geoip_subtype_t ret
= subtype
;
564 case dns_geoip_countrycode
:
565 if (DB46(reqaddr
, geoip
, city
) != NULL
)
566 ret
= dns_geoip_city_countrycode
;
567 else if (reqaddr
->family
== AF_INET
&& geoip
->region
!= NULL
)
568 ret
= dns_geoip_region_countrycode
;
569 else if (DB46(reqaddr
, geoip
, country
) != NULL
)
570 ret
= dns_geoip_country_code
;
572 case dns_geoip_countrycode3
:
573 if (DB46(reqaddr
, geoip
, city
) != NULL
)
574 ret
= dns_geoip_city_countrycode3
;
575 else if (DB46(reqaddr
, geoip
, country
) != NULL
)
576 ret
= dns_geoip_country_code3
;
578 case dns_geoip_countryname
:
579 if (DB46(reqaddr
, geoip
, city
) != NULL
)
580 ret
= dns_geoip_city_countryname
;
581 else if (DB46(reqaddr
, geoip
, country
) != NULL
)
582 ret
= dns_geoip_country_name
;
584 case dns_geoip_region
:
585 if (DB46(reqaddr
, geoip
, city
) != NULL
)
586 ret
= dns_geoip_city_region
;
587 else if (reqaddr
->family
== AF_INET
&& geoip
->region
!= NULL
)
588 ret
= dns_geoip_region_code
;
590 case dns_geoip_regionname
:
591 if (DB46(reqaddr
, geoip
, city
) != NULL
)
592 ret
= dns_geoip_city_regionname
;
593 else if (reqaddr
->family
== AF_INET
&& geoip
->region
!= NULL
)
594 ret
= dns_geoip_region_name
;
602 #endif /* HAVE_GEOIP */
605 dns_geoip_match(const isc_netaddr_t
*reqaddr
,
606 const dns_geoip_databases_t
*geoip
,
607 const dns_geoip_elem_t
*elt
)
619 dns_geoip_subtype_t subtype
;
620 isc_uint32_t ipnum
= 0;
621 int maxlen
= 0, id
, family
;
625 const geoipv6_t
*ipnum6
= NULL
;
627 const void *ipnum6
= NULL
;
630 INSIST(geoip
!= NULL
);
632 family
= reqaddr
->family
;
635 ipnum
= ntohl(reqaddr
->type
.in
.s_addr
);
639 ipnum6
= &reqaddr
->type
.in6
;
648 subtype
= fix_subtype(reqaddr
, geoip
, elt
->subtype
);
651 case dns_geoip_country_code
:
655 case dns_geoip_country_code3
:
659 case dns_geoip_country_name
:
662 db
= DB46(reqaddr
, geoip
, country
);
666 INSIST(elt
->as_string
!= NULL
);
668 cs
= country_lookup(db
, subtype
, family
, ipnum
, ipnum6
);
669 if (cs
!= NULL
&& strncasecmp(elt
->as_string
, cs
, maxlen
) == 0)
673 case dns_geoip_city_countrycode
:
674 case dns_geoip_city_countrycode3
:
675 case dns_geoip_city_countryname
:
676 case dns_geoip_city_region
:
677 case dns_geoip_city_regionname
:
678 case dns_geoip_city_name
:
679 case dns_geoip_city_postalcode
:
680 case dns_geoip_city_continentcode
:
681 case dns_geoip_city_timezonecode
:
682 INSIST(elt
->as_string
!= NULL
);
684 db
= DB46(reqaddr
, geoip
, city
);
688 record
= city_lookup(db
, subtype
, family
, ipnum
, ipnum6
);
692 s
= city_string(record
, subtype
, &maxlen
);
694 if (s
!= NULL
&& strncasecmp(elt
->as_string
, s
, maxlen
) == 0)
698 case dns_geoip_city_metrocode
:
699 db
= DB46(reqaddr
, geoip
, city
);
703 record
= city_lookup(db
, subtype
, family
, ipnum
, ipnum6
);
707 if (elt
->as_int
== record
->metro_code
)
711 case dns_geoip_city_areacode
:
712 db
= DB46(reqaddr
, geoip
, city
);
716 record
= city_lookup(db
, subtype
, family
, ipnum
, ipnum6
);
720 if (elt
->as_int
== record
->area_code
)
724 case dns_geoip_region_countrycode
:
725 case dns_geoip_region_code
:
726 case dns_geoip_region_name
:
727 case dns_geoip_region
:
728 if (geoip
->region
== NULL
)
731 INSIST(elt
->as_string
!= NULL
);
733 /* Region DB is not supported for IPv6 */
734 if (family
== AF_INET6
)
737 region
= region_lookup(geoip
->region
, subtype
, ipnum
);
741 s
= region_string(region
, subtype
, &maxlen
);
743 if (s
!= NULL
&& strncasecmp(elt
->as_string
, s
, maxlen
) == 0)
747 case dns_geoip_isp_name
:
751 case dns_geoip_org_name
:
755 case dns_geoip_as_asnum
:
759 case dns_geoip_domain_name
:
766 INSIST(elt
->as_string
!= NULL
);
767 /* ISP, Org, AS, and Domain are not supported for IPv6 */
768 if (family
== AF_INET6
)
771 s
= name_lookup(db
, subtype
, ipnum
);
774 if (strcasecmp(elt
->as_string
, s
) == 0)
776 if (subtype
!= dns_geoip_as_asnum
)
779 * Just check if the ASNNNN value matches.
781 l
= strlen(elt
->as_string
);
782 if (l
> 0U && strchr(elt
->as_string
, ' ') == NULL
&&
783 strncasecmp(elt
->as_string
, s
, l
) == 0 &&
789 case dns_geoip_netspeed_id
:
790 INSIST(geoip
->netspeed
!= NULL
);
792 /* Netspeed DB is not supported for IPv6 */
793 if (family
== AF_INET6
)
796 id
= netspeed_lookup(geoip
->netspeed
, subtype
, ipnum
);
797 if (id
== elt
->as_int
)
801 case dns_geoip_countrycode
:
802 case dns_geoip_countrycode3
:
803 case dns_geoip_countryname
:
804 case dns_geoip_regionname
:
806 * If these were not remapped by fix_subtype(),
807 * the database was unavailable. Always return false.
820 dns_geoip_shutdown(void) {
823 #ifdef ISC_PLATFORM_USETHREADS
824 if (state_mctx
!= NULL
)
825 isc_mem_detach(&state_mctx
);