1 /*
2 * Copyright (c) 2008-2020, OARC, Inc.
3 * Copyright (c) 2007-2008, Internet Systems Consortium, Inc.
4 * Copyright (c) 2003-2007, The Measurement Factory, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * 3. Neither the name of the copyright holder nor the names of its
20 * contributors may be used to endorse or promote products derived
21 * from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 */
36
37 #include "config.h"
38
39 #include "country_index.h"
40 #include "xmalloc.h"
41 #include "hashtbl.h"
42 #include "syslog_debug.h"
43 #include "geoip.h"
44 #if defined(HAVE_LIBGEOIP) && defined(HAVE_GEOIP_H)
45 #define HAVE_GEOIP 1
46 #include <GeoIP.h>
47 #endif
48 #if defined(HAVE_LIBMAXMINDDB) && defined(HAVE_MAXMINDDB_H)
49 #define HAVE_MAXMINDDB 1
50 #include <maxminddb.h>
51 #endif
52
53 #ifdef HAVE_MAXMINDDB
54 #include "compat.h"
55 #endif
56
57 #ifdef HAVE_MAXMINDDB
58 #include <errno.h>
59 #endif
60 #include <string.h>
61 #include <strings.h>
62 #include <stdlib.h>
63
64 extern int debug_flag;
65 extern char* geoip_v4_dat;
66 extern int geoip_v4_options;
67 extern char* geoip_v6_dat;
68 extern int geoip_v6_options;
69 extern enum geoip_backend country_indexer_backend;
70 extern char* maxminddb_country;
71 static hashfunc country_hashfunc;
72 static hashkeycmp country_cmpfunc;
73
74 #define MAX_ARRAY_SZ 65536
75 static hashtbl* theHash = NULL;
76 static int next_idx = 0;
77 #ifdef HAVE_GEOIP
78 static GeoIP* geoip = NULL;
79 static GeoIP* geoip6 = NULL;
80 #endif
81 #ifdef HAVE_MAXMINDDB
82 static MMDB_s mmdb;
83 static char _mmcountry[32];
84 static int have_mmdb = 0;
85 #endif
86 static char ipstr[81];
87 static char* unknown = "??";
88 static char* unknown_v4 = "?4";
89 static char* unknown_v6 = "?6";
90
91 typedef struct
92 {
93 char* country;
94 int index;
95 } countryobj;
96
97 const char*
country_get_from_message(dns_message * m)98 country_get_from_message(dns_message* m)
99 {
100 transport_message* tm = m->tm;
101 const char* cc = unknown;
102
103 if (country_indexer_backend == geoip_backend_libgeoip) {
104 if (!inXaddr_ntop(&tm->src_ip_addr, ipstr, sizeof(ipstr) - 1)) {
105 dfprint(0, "country_index: Error converting IP address");
106 return (unknown);
107 }
108 }
109
110 switch (tm->ip_version) {
111 case 4:
112 switch (country_indexer_backend) {
113 case geoip_backend_libgeoip:
114 #ifdef HAVE_GEOIP
115 if (geoip) {
116 cc = GeoIP_country_code_by_addr(geoip, ipstr);
117 if (cc == NULL) {
118 cc = unknown_v4;
119 }
120 }
121 #endif
122 break;
123 case geoip_backend_libmaxminddb:
124 #ifdef HAVE_MAXMINDDB
125 if (have_mmdb) {
126 struct sockaddr_in s;
127 int ret;
128 MMDB_lookup_result_s r;
129
130 s.sin_family = AF_INET;
131 s.sin_addr = tm->src_ip_addr._.in4;
132
133 r = MMDB_lookup_sockaddr(&mmdb, (struct sockaddr*)&s, &ret);
134 if (ret == MMDB_SUCCESS && r.found_entry) {
135 MMDB_entry_data_s entry_data;
136
137 if (MMDB_get_value(&r.entry, &entry_data, "country", "iso_code", 0) == MMDB_SUCCESS
138 && entry_data.type == MMDB_DATA_TYPE_UTF8_STRING) {
139 size_t len = entry_data.data_size > (sizeof(_mmcountry) - 1) ? (sizeof(_mmcountry) - 1) : entry_data.data_size;
140 memcpy(_mmcountry, entry_data.utf8_string, len);
141 _mmcountry[len] = 0;
142 cc = _mmcountry;
143 break;
144 }
145 }
146 }
147 cc = unknown_v4;
148 #endif
149 break;
150 default:
151 break;
152 }
153 break;
154
155 case 6:
156 switch (country_indexer_backend) {
157 case geoip_backend_libgeoip:
158 #ifdef HAVE_GEOIP
159 if (geoip6) {
160 cc = GeoIP_country_code_by_addr_v6(geoip6, ipstr);
161 if (cc == NULL) {
162 cc = unknown_v6;
163 }
164 }
165 #endif
166 break;
167 case geoip_backend_libmaxminddb:
168 #ifdef HAVE_MAXMINDDB
169 if (have_mmdb) {
170 struct sockaddr_in6 s;
171 int ret;
172 MMDB_lookup_result_s r;
173
174 s.sin6_family = AF_INET;
175 s.sin6_addr = tm->src_ip_addr.in6;
176
177 r = MMDB_lookup_sockaddr(&mmdb, (struct sockaddr*)&s, &ret);
178 if (ret == MMDB_SUCCESS && r.found_entry) {
179 MMDB_entry_data_s entry_data;
180
181 if (MMDB_get_value(&r.entry, &entry_data, "country", "iso_code", 0) == MMDB_SUCCESS
182 && entry_data.type == MMDB_DATA_TYPE_UTF8_STRING) {
183 size_t len = entry_data.data_size > (sizeof(_mmcountry) - 1) ? (sizeof(_mmcountry) - 1) : entry_data.data_size;
184 memcpy(_mmcountry, entry_data.utf8_string, len);
185 _mmcountry[len] = 0;
186 cc = _mmcountry;
187 break;
188 }
189 }
190 }
191 cc = unknown_v6;
192 #endif
193 break;
194 default:
195 break;
196 }
197 break;
198
199 default:
200 break;
201 }
202
203 dfprintf(1, "country_index: country code: %s", cc);
204 return cc;
205 }
206
country_indexer(const dns_message * m)207 int country_indexer(const dns_message* m)
208 {
209 const char* country;
210 countryobj* obj;
211 if (m->malformed)
212 return -1;
213 country = country_get_from_message((dns_message*)m);
214 if (NULL == theHash) {
215 theHash = hash_create(MAX_ARRAY_SZ, country_hashfunc, country_cmpfunc, 1, afree, afree);
216 if (NULL == theHash)
217 return -1;
218 }
219 if ((obj = hash_find(country, theHash)))
220 return obj->index;
221 obj = acalloc(1, sizeof(*obj));
222 if (NULL == obj)
223 return -1;
224 obj->country = astrdup(country);
225 if (NULL == obj->country) {
226 afree(obj);
227 return -1;
228 }
229 obj->index = next_idx;
230 if (0 != hash_add(obj->country, obj, theHash)) {
231 afree(obj->country);
232 afree(obj);
233 return -1;
234 }
235 next_idx++;
236 return obj->index;
237 }
238
country_iterator(const char ** label)239 int country_iterator(const char** label)
240 {
241 countryobj* obj;
242 static char label_buf[MAX_QNAME_SZ];
243 if (0 == next_idx)
244 return -1;
245 if (NULL == label) {
246 /* initialize and tell caller how big the array is */
247 hash_iter_init(theHash);
248 return next_idx;
249 }
250 if ((obj = hash_iterate(theHash)) == NULL)
251 return -1;
252 snprintf(label_buf, sizeof(label_buf), "%s", obj->country);
253 *label = label_buf;
254 return obj->index;
255 }
256
country_reset()257 void country_reset()
258 {
259 theHash = NULL;
260 next_idx = 0;
261 }
262
263 static unsigned int
country_hashfunc(const void * key)264 country_hashfunc(const void* key)
265 {
266 return hashendian(key, strlen(key), 0);
267 }
268
269 static int
country_cmpfunc(const void * a,const void * b)270 country_cmpfunc(const void* a, const void* b)
271 {
272 return strcasecmp(a, b);
273 }
274
country_init(void)275 void country_init(void)
276 {
277 switch (country_indexer_backend) {
278 case geoip_backend_libgeoip:
279 #ifdef HAVE_GEOIP
280 if (geoip_v4_dat) {
281 geoip = GeoIP_open(geoip_v4_dat, geoip_v4_options);
282 if (geoip == NULL) {
283 dsyslog(LOG_ERR, "country_index: Error opening IPv4 Country DB. Make sure libgeoip's GeoIP.dat file is available");
284 exit(1);
285 }
286 }
287 if (geoip_v6_dat) {
288 geoip6 = GeoIP_open(geoip_v6_dat, geoip_v6_options);
289 if (geoip6 == NULL) {
290 dsyslog(LOG_ERR, "country_index: Error opening IPv6 Country DB. Make sure libgeoip's GeoIPv6.dat file is available");
291 exit(1);
292 }
293 }
294 memset(ipstr, 0, sizeof(ipstr));
295 if (geoip || geoip6) {
296 dsyslog(LOG_INFO, "country_index: Sucessfully initialized GeoIP");
297 } else {
298 dsyslog(LOG_INFO, "country_index: No database loaded for GeoIP");
299 }
300 #endif
301 break;
302 case geoip_backend_libmaxminddb:
303 #ifdef HAVE_MAXMINDDB
304 if (maxminddb_country) {
305 int ret;
306 char errbuf[512];
307
308 ret = MMDB_open(maxminddb_country, 0, &mmdb);
309 if (ret == MMDB_IO_ERROR) {
310 dsyslogf(LOG_ERR, "country_index: Error opening MaxMind Country, IO error: %s", dsc_strerror(errno, errbuf, sizeof(errbuf)));
311 exit(1);
312 } else if (ret != MMDB_SUCCESS) {
313 dsyslogf(LOG_ERR, "country_index: Error opening MaxMind Country: %s", MMDB_strerror(ret));
314 exit(1);
315 }
316 dsyslog(LOG_INFO, "country_index: Sucessfully initialized MaxMind Country");
317 have_mmdb = 1;
318 } else {
319 dsyslog(LOG_INFO, "country_index: No database loaded for MaxMind Country");
320 }
321 #endif
322 break;
323 default:
324 break;
325 }
326 }
327