1 /*
2 ** Copyright (C) 2004-2020 by Carnegie Mellon University.
3 **
4 ** @OPENSOURCE_LICENSE_START@
5 ** See license information in ../../LICENSE.txt
6 ** @OPENSOURCE_LICENSE_END@
7 */
8 
9 /*
10 **  skcountry.c
11 **
12 **    Katherine Prevost
13 **    December 6th, 2004
14 **
15 **    Country code lookups using the prefixmap data structure.
16 */
17 
18 #include <silk/silk.h>
19 
20 RCSIDENT("$SiLK: skcountry.c ef14e54179be 2020-04-14 21:57:45Z mthomas $");
21 
22 #include <silk/rwrec.h>
23 #include <silk/skcountry.h>
24 #include <silk/skipaddr.h>
25 #include <silk/skprefixmap.h>
26 
27 
28 /* TYPEDEFS AND MACROS */
29 
30 #define MIN_COUNTRY_CODE  (((uint32_t)' ' << 8) | (uint32_t)' ')
31 
32 #define MAX_COUNTRY_CODE  (((uint32_t)'~' << 8) | (uint32_t)'~')
33 
34 #define WRITE_INVALID_CC_STRING(out, out_len)   \
35     (void)snprintf((out), (out_len), "??")
36 
37 
38 /* LOCAL VARIABLES */
39 
40 /* the prefixmap used to look up country codes */
41 static skPrefixMap_t *ccmap = NULL;
42 
43 
44 /* FUNCTION DEFINITIONS */
45 
46 
47 sk_countrycode_t
skCountryGetMaxCode(void)48 skCountryGetMaxCode(
49     void)
50 {
51     return SK_COUNTRYCODE_INVALID;
52 }
53 
54 
55 sk_countrycode_t
skCountryNameToCode(const char * name)56 skCountryNameToCode(
57     const char         *name)
58 {
59     sk_countrycode_t code;
60 
61     /* a valid code contains two ascii characters, an alpha and an
62      * alpha-numeric, or the string "--" */
63     if ('\0' == name[2]
64         && ((isalpha((int)name[0]) && isalnum((int)name[1])
65              && isascii((int)name[0]) && isascii((int)name[1]))
66             || ('-' == name[0] && '-' == name[1])))
67     {
68         code = (sk_countrycode_t)((tolower(name[0]) << 8) | tolower(name[1]));
69         assert(code >= MIN_COUNTRY_CODE && code <= MAX_COUNTRY_CODE);
70         return code;
71     }
72 
73     return SK_COUNTRYCODE_INVALID;
74 }
75 
76 
77 char *
skCountryCodeToName(sk_countrycode_t code,char * name,size_t name_len)78 skCountryCodeToName(
79     sk_countrycode_t    code,
80     char               *name,
81     size_t              name_len)
82 {
83     if (!name || name_len < 2) {
84         if (name && name_len == 1) {
85             name[0] = '\0';
86             return name;
87         }
88         return NULL;
89     }
90 
91     if (code < MIN_COUNTRY_CODE || code > MAX_COUNTRY_CODE) {
92         WRITE_INVALID_CC_STRING(name, name_len);
93         return name;
94     }
95 
96     switch (name_len) {
97       case 0:
98       case 1:
99         skAbortBadCase(name_len);
100       case 2:
101         name[0] = (code >> 8);
102         name[1] = '\0';
103         break;
104       default:
105         name[0] = (code >> 8);
106         name[1] = (code & 0xFF);
107         name[2] = '\0';
108         break;
109     }
110     return name;
111 }
112 
113 
114 const skPrefixMap_t *
skCountryGetPrefixMap(void)115 skCountryGetPrefixMap(
116     void)
117 {
118     return ccmap;
119 }
120 
121 
122 int
skCountryIsV6(void)123 skCountryIsV6(
124     void)
125 {
126     if (!ccmap) {
127         return -1;
128     }
129     return (skPrefixMapGetContentType(ccmap) == SKPREFIXMAP_CONT_ADDR_V6);
130 }
131 
132 
133 sk_countrycode_t
skCountryLookupCode(const skipaddr_t * ipaddr)134 skCountryLookupCode(
135     const skipaddr_t   *ipaddr)
136 {
137     uint32_t code;
138 
139     if (!ccmap) {
140         return SK_COUNTRYCODE_INVALID;
141     }
142 
143     code = skPrefixMapFindValue(ccmap, ipaddr);
144     if (code == SKPREFIXMAP_NOT_FOUND) {
145         return SK_COUNTRYCODE_INVALID;
146     }
147     return (sk_countrycode_t)code;
148 }
149 
150 
151 char *
skCountryLookupName(const skipaddr_t * ipaddr,char * name,size_t name_len)152 skCountryLookupName(
153     const skipaddr_t   *ipaddr,
154     char               *name,
155     size_t              name_len)
156 {
157     uint32_t code;
158 
159     if (!ccmap) {
160         goto ERROR;
161     }
162 
163     code = skPrefixMapFindValue(ccmap, ipaddr);
164     if (code > MAX_COUNTRY_CODE) {
165         goto ERROR;
166     }
167 
168     return skCountryCodeToName((sk_countrycode_t)code, name, name_len);
169 
170   ERROR:
171     if (!name || name_len == 0) {
172         return NULL;
173     }
174     WRITE_INVALID_CC_STRING(name, name_len);
175     return name;
176 }
177 
178 
179 sk_countrycode_t
skCountryLookupCodeAndRange(const skipaddr_t * ipaddr,skipaddr_t * start_range,skipaddr_t * end_range)180 skCountryLookupCodeAndRange(
181     const skipaddr_t   *ipaddr,
182     skipaddr_t         *start_range,
183     skipaddr_t         *end_range)
184 {
185     uint32_t code;
186 
187     if (!ccmap) {
188         return SK_COUNTRYCODE_INVALID;
189     }
190 
191     code = skPrefixMapFindRange(ccmap, ipaddr, start_range, end_range);
192     if (code == SKPREFIXMAP_NOT_FOUND) {
193         return SK_COUNTRYCODE_INVALID;
194     }
195     return (sk_countrycode_t)code;
196 }
197 
198 
199 int
skCountrySetup(const char * map_name,sk_msg_fn_t errfn)200 skCountrySetup(
201     const char         *map_name,
202     sk_msg_fn_t         errfn)
203 {
204     char filename[PATH_MAX];
205     skPrefixMapErr_t map_error;
206     const char *errmsg;
207     int check_pwd = 1;
208     int found = 0;
209 
210     if (ccmap) {
211         return 0;
212     }
213 
214     if (!map_name) {
215         map_name = getenv(SK_COUNTRY_MAP_ENVAR);
216         if (!map_name || !map_name[0]) {
217             map_name = SK_COUNTRY_DEFAULT_MAP;
218             /* don't check pwd if we use the default map name */
219             check_pwd = 0;
220         }
221     }
222 
223     /* if name explicitly given, see if the file exists.  this will
224      * support relative paths that skFindFile() does not. */
225     if (check_pwd) {
226         if (skFileExists(map_name)) {
227             strncpy(filename, map_name, sizeof(filename));
228             filename[sizeof(filename)-1] = '\0';
229             found = 1;
230         }
231     }
232 
233     /* Locate the data file */
234     if (!found
235         && (NULL == skFindFile(map_name, filename, sizeof(filename), 1)))
236     {
237         if (errfn) {
238             errfn("Could not locate Country Code data file '%s'",
239                   map_name);
240         }
241         return -1;
242     }
243 
244     /* Read in the data file */
245     map_error = skPrefixMapLoad(&ccmap, filename);
246     switch (map_error) {
247       case SKPREFIXMAP_OK:
248         if (SKPREFIXMAP_CONT_PROTO_PORT == skPrefixMapGetContentType(ccmap)) {
249             skPrefixMapDelete(ccmap);
250             ccmap = NULL;
251             errmsg = "Map contains protocol/port pairs";
252             break;
253         }
254         return 0;
255       case SKPREFIXMAP_ERR_ARGS:
256         errmsg = "Invalid arguments";
257         break;
258       case SKPREFIXMAP_ERR_MEMORY:
259         errmsg = "Out of memory";
260         break;
261       case SKPREFIXMAP_ERR_IO:
262         errmsg = "I/O error";
263         break;
264       case SKPREFIXMAP_ERR_HEADER:
265         errmsg = "Unexpected file type, version, or compression";
266         break;
267       case SKPREFIXMAP_ERR_NO_IPV6:
268         errmsg = "Cannot read IPv6 file";
269         break;
270       default:
271         errmsg = "Unknown error";
272         break;
273     }
274 
275     if (errfn) {
276         errfn("Failed to load Country Code data file '%s': %s",
277               filename, errmsg);
278     }
279     return -1;
280 }
281 
282 
283 void
skCountryTeardown(void)284 skCountryTeardown(
285     void)
286 {
287     if (ccmap) {
288         skPrefixMapDelete(ccmap);
289         ccmap = NULL;
290     }
291 }
292 
293 
294 
295 /* **************************************************************** */
296 /* Country Code "Plug-In" Support */
297 /* **************************************************************** */
298 
299 
300 /* TYPEDEFS AND DEFINES */
301 
302 #define CCFILTER_TEXT_WIDTH 3
303 
304 #define CCFILTER_SCC  1
305 #define CCFILTER_DCC  2
306 
307 /* Plugin protocol version */
308 #define PLUGIN_API_VERSION_MAJOR 1
309 #define PLUGIN_API_VERSION_MINOR 0
310 
311 
312 /* LOCAL VARIABLES */
313 
314 /* fields for rwcut, rwuniq, etc */
315 static struct plugin_fields_st {
316     const char *name;
317     const char *alias;
318     uint32_t    val;
319     const char *description;
320 } plugin_fields[] = {
321     {"scc", "18", CCFILTER_SCC, "Country code of source address"},
322     {"dcc", "19", CCFILTER_DCC, "Country code of destination address"},
323     {NULL,  NULL, UINT32_MAX, NULL}         /* sentinel */
324 };
325 
326 
327 /* PRIVATE FUNCTION PROTOTYPES */
328 
329 static skplugin_err_t ccInit(void UNUSED(*x));
330 static skplugin_err_t ccCleanup(void UNUSED(*x));
331 static skplugin_err_t
332 recToText(
333     const rwRec        *rwrec,
334     char               *dest,
335     size_t              width,
336     void               *cbdata,
337     void              **extra);
338 static skplugin_err_t
339 recToBin(
340     const rwRec        *rec,
341     uint8_t            *dest,
342     void               *cbdata,
343     void              **extra);
344 static skplugin_err_t
345 binToText(
346     const uint8_t      *bin,
347     char               *dest,
348     size_t              width,
349     void               *cbdata);
350 
351 
352 /* FUNCTION DEFINITIONS */
353 
354 /* the registration function called by skplugin.c */
355 skplugin_err_t
skCountryAddFields(uint16_t major_version,uint16_t minor_version,void UNUSED (* pi_data))356 skCountryAddFields(
357     uint16_t            major_version,
358     uint16_t            minor_version,
359     void        UNUSED(*pi_data))
360 {
361     int i;
362     skplugin_field_t *field;
363     skplugin_err_t rv = SKPLUGIN_OK;
364     skplugin_callbacks_t regdata;
365 
366     /* Check API version */
367     rv = skpinSimpleCheckVersion(major_version, minor_version,
368                                  PLUGIN_API_VERSION_MAJOR,
369                                  PLUGIN_API_VERSION_MINOR,
370                                  skAppPrintErr);
371     if (rv != SKPLUGIN_OK) {
372         return rv;
373     }
374 
375     /* register the fields to use for rwcut, rwuniq, rwsort */
376     memset(&regdata, 0, sizeof(regdata));
377     regdata.init         = ccInit;
378     regdata.cleanup      = ccCleanup;
379     regdata.column_width = CCFILTER_TEXT_WIDTH;
380     regdata.bin_bytes    = sizeof(sk_countrycode_t);
381     regdata.rec_to_text  = recToText;
382     regdata.rec_to_bin   = recToBin;
383     regdata.bin_to_text  = binToText;
384 
385     for (i = 0; plugin_fields[i].name; ++i) {
386         rv = skpinRegField(&field, plugin_fields[i].name,
387                            plugin_fields[i].description,
388                            &regdata, (void*)&plugin_fields[i].val);
389         if (SKPLUGIN_OK != rv) {
390             return rv;
391         }
392         rv = skpinAddFieldAlias(field, plugin_fields[i].alias);
393         if (SKPLUGIN_OK != rv) {
394             return rv;
395         }
396     }
397 
398     return rv;
399 }
400 
401 
402 /*
403  *  status = ccInit(data);
404  *
405  *    The initialization code for this plugin.  This is called by the
406  *    plugin initialization code after option parsing and before data
407  *    processing.
408  */
409 static skplugin_err_t
ccInit(void UNUSED (* x))410 ccInit(
411     void        UNUSED(*x))
412 {
413     /* Read in the data file */
414     if (skCountrySetup(NULL, &skAppPrintErr)) {
415         return SKPLUGIN_ERR;
416     }
417 
418     return SKPLUGIN_OK;
419 }
420 
421 
422 /*
423  *   status = ccCleanup(data);
424  *
425  *     Called by plugin interface code to tear down this plugin.
426  */
427 static skplugin_err_t
ccCleanup(void UNUSED (* x))428 ccCleanup(
429     void        UNUSED(*x))
430 {
431     skCountryTeardown();
432     return SKPLUGIN_OK;
433 }
434 
435 
436 /*
437  *  status = recToText(rwrec, text_val, text_len, &index, NULL);
438  *
439  *    Given the SiLK Flow record 'rwrec', lookup the Country Code
440  *    specified by '*index', and write a textual representation of
441  *    that value into 'text_val', a buffer of 'text_len' characters.
442  */
443 static skplugin_err_t
recToText(const rwRec * rwrec,char * text_value,size_t text_size,void * idx,void UNUSED (** extra))444 recToText(
445     const rwRec            *rwrec,
446     char                   *text_value,
447     size_t                  text_size,
448     void                   *idx,
449     void           UNUSED(**extra))
450 {
451     skipaddr_t ipaddr;
452 
453     switch (*((unsigned int*)(idx))) {
454       case CCFILTER_SCC:
455         rwRecMemGetSIP(rwrec, &ipaddr);
456         break;
457       case CCFILTER_DCC:
458         rwRecMemGetDIP(rwrec, &ipaddr);
459         break;
460       default:
461         return SKPLUGIN_ERR_FATAL;
462     }
463 
464     skCountryLookupName(&ipaddr, text_value, text_size);
465     return SKPLUGIN_OK;
466 }
467 
468 
469 /*
470  *  status = recToBin(rwrec, bin_val, &index, NULL);
471  *
472  *    Given the SiLK Flow record 'rwrec', lookup the Country Code
473  *    specified by '*index', and write a binary representation of
474  *    that value into 'bin_val'.
475  */
476 static skplugin_err_t
recToBin(const rwRec * rwrec,uint8_t * bin_value,void * idx,void UNUSED (** extra))477 recToBin(
478     const rwRec            *rwrec,
479     uint8_t                *bin_value,
480     void                   *idx,
481     void           UNUSED(**extra))
482 {
483     skipaddr_t ipaddr;
484     sk_countrycode_t cc;
485 
486     switch (*((unsigned int*)(idx))) {
487       case CCFILTER_SCC:
488         rwRecMemGetSIP(rwrec, &ipaddr);
489         break;
490       case CCFILTER_DCC:
491         rwRecMemGetDIP(rwrec, &ipaddr);
492         break;
493       default:
494         return SKPLUGIN_ERR_FATAL;
495     }
496 
497     cc = htons(skCountryLookupCode(&ipaddr));
498     memcpy(bin_value, &cc, sizeof(sk_countrycode_t));
499 
500     return SKPLUGIN_OK;
501 }
502 
503 
504 /*
505  *  status = binToText(bin_val, text_val, text_len, &index);
506  *
507  *    Given the buffer 'bin_val' which was filled by calling
508  *    recToBin(), write a textual representation of that value into
509  *    'text_val', a buffer of 'text_len' characters.
510  */
511 static skplugin_err_t
binToText(const uint8_t * bin_value,char * text_value,size_t text_size,void UNUSED (* idx))512 binToText(
513     const uint8_t          *bin_value,
514     char                   *text_value,
515     size_t                  text_size,
516     void            UNUSED(*idx))
517 {
518     sk_countrycode_t cc;
519 
520     memcpy(&cc, bin_value, sizeof(sk_countrycode_t));
521 
522     skCountryCodeToName(ntohs(cc), text_value, text_size);
523     return SKPLUGIN_OK;
524 }
525 
526 
527 /*
528 ** Local Variables:
529 ** mode:c
530 ** indent-tabs-mode:nil
531 ** c-basic-offset:4
532 ** End:
533 */
534