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(®data, 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 ®data, (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