1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * SPDX-License-Identifier: MPL-2.0
5  *
6  * This Source Code Form is subject to the terms of the Mozilla Public
7  * License, v. 2.0. If a copy of the MPL was not distributed with this
8  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9  *
10  * See the COPYRIGHT file distributed with this work for additional
11  * information regarding copyright ownership.
12  */
13 
14 /*! \file */
15 
16 #if defined(HAVE_GEOIP2)
17 #include <maxminddb.h>
18 #endif /* if defined(HAVE_GEOIP2) */
19 
20 #include <isc/print.h>
21 #include <isc/string.h>
22 #include <isc/util.h>
23 
24 #include <dns/geoip.h>
25 
26 #include <named/geoip.h>
27 #include <named/log.h>
28 
29 static dns_geoip_databases_t geoip_table;
30 
31 #if defined(HAVE_GEOIP2)
32 static MMDB_s geoip_country, geoip_city, geoip_as, geoip_isp, geoip_domain;
33 
34 static MMDB_s *
open_geoip2(const char * dir,const char * dbfile,MMDB_s * mmdb)35 open_geoip2(const char *dir, const char *dbfile, MMDB_s *mmdb) {
36 	char pathbuf[PATH_MAX];
37 	unsigned int n;
38 	int ret;
39 
40 	n = snprintf(pathbuf, sizeof(pathbuf), "%s/%s", dir, dbfile);
41 	if (n >= sizeof(pathbuf)) {
42 		isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
43 			      NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
44 			      "GeoIP2 database '%s/%s': path too long", dir,
45 			      dbfile);
46 		return (NULL);
47 	}
48 
49 	ret = MMDB_open(pathbuf, MMDB_MODE_MMAP, mmdb);
50 	if (ret == MMDB_SUCCESS) {
51 		isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
52 			      NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
53 			      "opened GeoIP2 database '%s'", pathbuf);
54 		return (mmdb);
55 	}
56 
57 	isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
58 		      NAMED_LOGMODULE_SERVER, ISC_LOG_DEBUG(1),
59 		      "unable to open GeoIP2 database '%s' (status %d)",
60 		      pathbuf, ret);
61 
62 	return (NULL);
63 }
64 #endif /* HAVE_GEOIP2 */
65 
66 void
named_geoip_init(void)67 named_geoip_init(void) {
68 #if defined(HAVE_GEOIP2)
69 	if (named_g_geoip == NULL) {
70 		named_g_geoip = &geoip_table;
71 	}
72 #else  /* if defined(HAVE_GEOIP2) */
73 	return;
74 #endif /* if defined(HAVE_GEOIP2) */
75 }
76 
77 void
named_geoip_load(char * dir)78 named_geoip_load(char *dir) {
79 #if defined(HAVE_GEOIP2)
80 	REQUIRE(dir != NULL);
81 
82 	isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
83 		      NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
84 		      "looking for GeoIP2 databases in '%s'", dir);
85 
86 	named_g_geoip->country = open_geoip2(dir, "GeoIP2-Country.mmdb",
87 					     &geoip_country);
88 	if (named_g_geoip->country == NULL) {
89 		named_g_geoip->country = open_geoip2(
90 			dir, "GeoLite2-Country.mmdb", &geoip_country);
91 	}
92 
93 	named_g_geoip->city = open_geoip2(dir, "GeoIP2-City.mmdb", &geoip_city);
94 	if (named_g_geoip->city == NULL) {
95 		named_g_geoip->city = open_geoip2(dir, "GeoLite2-City.mmdb",
96 						  &geoip_city);
97 	}
98 
99 	named_g_geoip->as = open_geoip2(dir, "GeoIP2-ASN.mmdb", &geoip_as);
100 	if (named_g_geoip->as == NULL) {
101 		named_g_geoip->as = open_geoip2(dir, "GeoLite2-ASN.mmdb",
102 						&geoip_as);
103 	}
104 
105 	named_g_geoip->isp = open_geoip2(dir, "GeoIP2-ISP.mmdb", &geoip_isp);
106 	named_g_geoip->domain = open_geoip2(dir, "GeoIP2-Domain.mmdb",
107 					    &geoip_domain);
108 #else  /* if defined(HAVE_GEOIP2) */
109 	UNUSED(dir);
110 
111 	return;
112 #endif /* if defined(HAVE_GEOIP2) */
113 }
114 
115 void
named_geoip_unload(void)116 named_geoip_unload(void) {
117 #ifdef HAVE_GEOIP2
118 	if (named_g_geoip->country != NULL) {
119 		MMDB_close(named_g_geoip->country);
120 		named_g_geoip->country = NULL;
121 	}
122 	if (named_g_geoip->city != NULL) {
123 		MMDB_close(named_g_geoip->city);
124 		named_g_geoip->city = NULL;
125 	}
126 	if (named_g_geoip->as != NULL) {
127 		MMDB_close(named_g_geoip->as);
128 		named_g_geoip->as = NULL;
129 	}
130 	if (named_g_geoip->isp != NULL) {
131 		MMDB_close(named_g_geoip->isp);
132 		named_g_geoip->isp = NULL;
133 	}
134 	if (named_g_geoip->domain != NULL) {
135 		MMDB_close(named_g_geoip->domain);
136 		named_g_geoip->domain = NULL;
137 	}
138 #endif /* ifdef HAVE_GEOIP2 */
139 }
140 
141 void
named_geoip_shutdown(void)142 named_geoip_shutdown(void) {
143 #ifdef HAVE_GEOIP2
144 	named_geoip_unload();
145 #endif /* HAVE_GEOIP2 */
146 }
147