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