unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
geoip_csv.c (20707B)
1 /* 2 * IRC - Internet Relay Chat, src/modules/geoip_csv.c 3 * (C) 2021 The UnrealIRCd Team 4 * 5 * See file AUTHORS in IRC package for additional names of 6 * the programmers. 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 1, or (at your option) 11 * any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 */ 22 23 #include "unrealircd.h" 24 25 ModuleHeader MOD_HEADER 26 = { 27 "geoip_csv", 28 "5.0", 29 "GEOIP using csv data files", 30 "UnrealIRCd Team", 31 "unrealircd-6", 32 }; 33 34 struct geoip_csv_config_s { 35 char *v4_db_file; 36 char *v6_db_file; 37 char *countries_db_file; 38 /* for config reading only */ 39 int have_config; 40 int have_ipv4_database; 41 int have_ipv6_database; 42 int have_countries; 43 }; 44 45 struct geoip_csv_ip_range { 46 uint32_t addr; 47 uint32_t mask; 48 int geoid; 49 struct geoip_csv_ip_range *next; 50 }; 51 52 struct geoip_csv_ip6_range { 53 uint16_t addr[8]; 54 uint16_t mask[8]; 55 int geoid; 56 struct geoip_csv_ip6_range *next; 57 }; 58 59 struct geoip_csv_country { 60 char code[10]; 61 char name[100]; 62 char continent[25]; 63 int id; 64 struct geoip_csv_country *next; 65 }; 66 67 /* Variables */ 68 struct geoip_csv_config_s geoip_csv_config; 69 struct geoip_csv_ip_range *geoip_csv_ip_range_list[256]; // we are keeping a separate list for each possible first octet to speed up searching 70 struct geoip_csv_ip6_range *geoip_csv_ip6_range_list = NULL; // for ipv6 there would be too many separate lists so just use a single one 71 struct geoip_csv_country *geoip_csv_country_list = NULL; 72 73 /* Forward declarations */ 74 static void geoip_csv_free_ipv4(void); 75 static void geoip_csv_free_ipv6(void); 76 static void geoip_csv_free_ipv6(void); 77 static void geoip_csv_free_countries(void); 78 static void geoip_csv_free(void); 79 static int geoip_csv_read_ipv4(char *file); 80 static int geoip_csv_ip6_convert(char *ip, uint16_t out[8]); 81 static int geoip_csv_read_ipv6(char *file); 82 static int geoip_csv_read_countries(char *file); 83 static struct geoip_csv_country *geoip_csv_get_country(int id); 84 static int geoip_csv_get_v4_geoid(char *iip); 85 static int geoip_csv_get_v6_geoid(char *iip); 86 int geoip_csv_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); 87 int geoip_csv_configposttest(int *errs); 88 int geoip_csv_configrun(ConfigFile *cf, ConfigEntry *ce, int type); 89 void geoip_csv_free(void); 90 GeoIPResult *geoip_lookup_csv(char *ip); 91 92 int geoip_csv_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) 93 { 94 ConfigEntry *cep; 95 int errors = 0; 96 int i; 97 98 if (type != CONFIG_SET) 99 return 0; 100 101 if (!ce || !ce->name) 102 return 0; 103 104 if (strcmp(ce->name, "geoip-csv")) 105 return 0; 106 107 geoip_csv_config.have_config = 1; 108 109 for (cep = ce->items; cep; cep = cep->next) 110 { 111 if (!strcmp(cep->name, "ipv4-blocks-file")) 112 { 113 if (geoip_csv_config.have_ipv4_database) 114 { 115 config_error("%s:%i: duplicate item set::geoip-csv::%s", cep->file->filename, cep->line_number, cep->name); 116 continue; 117 } 118 if (!is_file_readable(cep->value, PERMDATADIR)) 119 { 120 config_error("%s:%i: set::geoip-csv::%s: cannot open file \"%s/%s\" for reading (%s)", cep->file->filename, cep->line_number, cep->name, PERMDATADIR, cep->value, strerror(errno)); 121 errors++; 122 continue; 123 } 124 geoip_csv_config.have_ipv4_database = 1; 125 continue; 126 } 127 if (!strcmp(cep->name, "ipv6-blocks-file")) 128 { 129 if (geoip_csv_config.have_ipv6_database) 130 { 131 config_error("%s:%i: duplicate item set::geoip-csv::%s", cep->file->filename, cep->line_number, cep->name); 132 continue; 133 } 134 if (!is_file_readable(cep->value, PERMDATADIR)) 135 { 136 config_error("%s:%i: set::geoip-csv::%s: cannot open file \"%s/%s\" for reading (%s)", cep->file->filename, cep->line_number, cep->name, PERMDATADIR, cep->value, strerror(errno)); 137 errors++; 138 continue; 139 } 140 geoip_csv_config.have_ipv6_database = 1; 141 continue; 142 } 143 if (!strcmp(cep->name, "countries-file")) 144 { 145 if (geoip_csv_config.have_countries) 146 { 147 config_error("%s:%i: duplicate item set::geoip-csv::%s", cep->file->filename, cep->line_number, cep->name); 148 continue; 149 } 150 if (!is_file_readable(cep->value, PERMDATADIR)) 151 { 152 config_error("%s:%i: set::geoip-csv::%s: cannot open file \"%s/%s\" for reading (%s)", cep->file->filename, cep->line_number, cep->name, PERMDATADIR, cep->value, strerror(errno)); 153 errors++; 154 continue; 155 } 156 geoip_csv_config.have_countries = 1; 157 continue; 158 } 159 config_warn("%s:%i: unknown item set::geoip-csv::%s", cep->file->filename, cep->line_number, cep->name); 160 } 161 162 *errs = errors; 163 return errors ? -1 : 1; 164 } 165 166 int geoip_csv_configposttest(int *errs) 167 { 168 int errors = 0; 169 if (geoip_csv_config.have_config) 170 { 171 if (!geoip_csv_config.have_countries) 172 { 173 config_error("[geoip_csv] no countries file specified! Remove set::geoip-csv to use defaults"); 174 errors++; 175 } 176 if (!geoip_csv_config.have_ipv4_database && !geoip_csv_config.have_ipv6_database) 177 { 178 config_error("[geoip_csv] no database files specified! Remove set::geoip-csv to use defaults"); 179 errors++; 180 } 181 } else 182 { 183 safe_strdup(geoip_csv_config.v4_db_file, "GeoLite2-Country-Blocks-IPv4.csv"); 184 safe_strdup(geoip_csv_config.v6_db_file, "GeoLite2-Country-Blocks-IPv6.csv"); 185 safe_strdup(geoip_csv_config.countries_db_file, "GeoLite2-Country-Locations-en.csv"); 186 187 if (is_file_readable(geoip_csv_config.v4_db_file, PERMDATADIR)) 188 { 189 geoip_csv_config.have_ipv4_database = 1; 190 } else 191 { 192 config_warn("[geoip_csv] cannot open IPv4 blocks file \"%s/%s\" for reading (%s)", PERMDATADIR, geoip_csv_config.v4_db_file, strerror(errno)); 193 safe_free(geoip_csv_config.v4_db_file); 194 } 195 if (is_file_readable(geoip_csv_config.v6_db_file, PERMDATADIR)) 196 { 197 geoip_csv_config.have_ipv6_database = 1; 198 } else 199 { 200 config_warn("[geoip_csv] cannot open IPv6 blocks file \"%s/%s\" for reading (%s)", PERMDATADIR, geoip_csv_config.v6_db_file, strerror(errno)); 201 safe_free(geoip_csv_config.v6_db_file); 202 } 203 if (!is_file_readable(geoip_csv_config.countries_db_file, PERMDATADIR)) 204 { 205 config_error("[geoip_csv] cannot open countries file \"%s/%s\" for reading (%s)", PERMDATADIR, geoip_csv_config.countries_db_file, strerror(errno)); 206 safe_free(geoip_csv_config.countries_db_file); 207 errors++; 208 } 209 if (!geoip_csv_config.have_ipv4_database && !geoip_csv_config.have_ipv6_database) 210 { 211 config_error("[geoip_csv] couldn't read any blocks file! Either put these in %s location " 212 "or specify another in set::geoip-csv config block", PERMDATADIR); 213 errors++; 214 } 215 } 216 217 *errs = errors; 218 return errors ? -1 : 1; 219 } 220 221 int geoip_csv_configrun(ConfigFile *cf, ConfigEntry *ce, int type) 222 { 223 ConfigEntry *cep; 224 225 if (type != CONFIG_SET) 226 return 0; 227 228 if (!ce || !ce->name) 229 return 0; 230 231 if (strcmp(ce->name, "geoip-csv")) 232 return 0; 233 234 for (cep = ce->items; cep; cep = cep->next) 235 { 236 if (!strcmp(cep->name, "ipv4-blocks-file") && geoip_csv_config.have_ipv4_database) 237 safe_strdup(geoip_csv_config.v4_db_file, cep->value); 238 if (!strcmp(cep->name, "ipv6-blocks-file") && geoip_csv_config.have_ipv6_database) 239 safe_strdup(geoip_csv_config.v6_db_file, cep->value); 240 if (!strcmp(cep->name, "countries-file")) 241 safe_strdup(geoip_csv_config.countries_db_file, cep->value); 242 } 243 return 1; 244 } 245 246 MOD_TEST() 247 { 248 MARK_AS_OFFICIAL_MODULE(modinfo); 249 if (!CallbackAddPVoid(modinfo->handle, CALLBACKTYPE_GEOIP_LOOKUP, TO_PVOIDFUNC(geoip_lookup_csv))) 250 { 251 unreal_log(ULOG_ERROR, "geoip_csv", "GEOIP_ADD_CALLBACK_FAILED", NULL, 252 "geoip_csv: Could not install GEOIP_LOOKUP callback. " 253 "Most likely another geoip module is already loaded. " 254 "You can only load one!"); 255 return MOD_FAILED; 256 } 257 258 geoip_csv_config.have_config = 0; 259 geoip_csv_config.have_ipv4_database = 0; 260 geoip_csv_config.have_ipv6_database = 0; 261 geoip_csv_config.have_countries = 0; 262 HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, geoip_csv_configtest); 263 HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, geoip_csv_configposttest); 264 return MOD_SUCCESS; 265 } 266 267 MOD_INIT() 268 { 269 MARK_AS_OFFICIAL_MODULE(modinfo); 270 geoip_csv_free(); 271 HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, geoip_csv_configrun); 272 return MOD_SUCCESS; 273 } 274 275 MOD_LOAD() 276 { 277 int found_good_file = 0; 278 279 if (geoip_csv_config.v4_db_file) 280 { 281 convert_to_absolute_path(&geoip_csv_config.v4_db_file, PERMDATADIR); 282 if (!geoip_csv_read_ipv4(geoip_csv_config.v4_db_file)) 283 { 284 found_good_file = 1; 285 } 286 } 287 if (geoip_csv_config.v6_db_file) 288 { 289 convert_to_absolute_path(&geoip_csv_config.v6_db_file, PERMDATADIR); 290 if (!geoip_csv_read_ipv6(geoip_csv_config.v6_db_file)) 291 { 292 found_good_file = 1; 293 } 294 } 295 if (!geoip_csv_config.countries_db_file) 296 { 297 unreal_log(ULOG_DEBUG, "geoip_csv", "GEOIP_NO_COUNTRIES", NULL, 298 "[BUG] No countries file specified"); 299 geoip_csv_free(); 300 return MOD_FAILED; 301 } 302 convert_to_absolute_path(&geoip_csv_config.countries_db_file, PERMDATADIR); 303 if (geoip_csv_read_countries(geoip_csv_config.countries_db_file)) 304 { 305 unreal_log(ULOG_ERROR, "geoip_csv", "GEOIP_CANNOT_OPEN_DB", NULL, 306 "could not open required countries file!"); 307 geoip_csv_free(); 308 return MOD_FAILED; 309 } 310 311 if (!found_good_file) 312 { 313 unreal_log(ULOG_ERROR, "geoip_csv", "GEOIP_CANNOT_OPEN_DB", NULL, 314 "could not open any database!"); 315 geoip_csv_free(); 316 return MOD_FAILED; 317 } 318 return MOD_SUCCESS; 319 } 320 321 MOD_UNLOAD() 322 { 323 geoip_csv_free(); 324 return MOD_SUCCESS; 325 } 326 327 static void geoip_csv_free_ipv4(void) 328 { 329 struct geoip_csv_ip_range *ptr, *oldptr; 330 int i; 331 for (i=0; i<256; i++) 332 { 333 ptr = geoip_csv_ip_range_list[i]; 334 geoip_csv_ip_range_list[i] = NULL; 335 while (ptr) 336 { 337 oldptr = ptr; 338 ptr = ptr->next; 339 safe_free(oldptr); 340 } 341 } 342 } 343 344 static void geoip_csv_free_ipv6(void) 345 { 346 struct geoip_csv_ip6_range *ptr, *oldptr; 347 ptr = geoip_csv_ip6_range_list; 348 geoip_csv_ip6_range_list = NULL; 349 while (ptr) 350 { 351 oldptr = ptr; 352 ptr = ptr->next; 353 safe_free(oldptr); 354 } 355 } 356 357 static void geoip_csv_free_countries(void) 358 { 359 struct geoip_csv_country *ptr, *oldptr; 360 ptr = geoip_csv_country_list; 361 geoip_csv_country_list = NULL; 362 while (ptr) 363 { 364 oldptr = ptr; 365 ptr = ptr->next; 366 safe_free(oldptr); 367 } 368 } 369 370 static void geoip_csv_free(void) 371 { 372 geoip_csv_free_ipv4(); 373 geoip_csv_free_ipv6(); 374 geoip_csv_free_countries(); 375 } 376 377 /* reading data from files */ 378 379 #define STR_HELPER(x) #x 380 #define STR(x) STR_HELPER(x) 381 #define BUFLEN 8191 382 383 static int geoip_csv_read_ipv4(char *file) 384 { 385 FILE *u; 386 char buf[BUFLEN+1]; 387 int cidr, geoid; 388 char ip[24]; 389 char netmask[24]; 390 uint32_t addr; 391 uint32_t mask; 392 struct geoip_csv_ip_range *curr[256]; 393 struct geoip_csv_ip_range *ptr; 394 memset(curr, 0, sizeof(curr)); 395 int i; 396 char *filename = NULL; 397 398 safe_strdup(filename, file); 399 convert_to_absolute_path(&filename, CONFDIR); 400 u = fopen(filename, "r"); 401 safe_free(filename); 402 if (!u) 403 { 404 config_warn("[geoip_csv] Cannot open IPv4 ranges list file"); 405 return 1; 406 } 407 408 if (!fgets(buf, BUFLEN, u)) 409 { 410 config_warn("[geoip_csv] IPv4 list file is empty"); 411 fclose(u); 412 return 1; 413 } 414 buf[BUFLEN] = '\0'; 415 while (fscanf(u, "%23[^/\n]/%d,%" STR(BUFLEN) "[^\n]\n", ip, &cidr, buf) == 3) 416 { 417 if (sscanf(buf, "%d,", &geoid) != 1) 418 { 419 /* missing geoid: can happen with valid files */ 420 continue; 421 } 422 423 if (cidr < 1 || cidr > 32) 424 { 425 config_warn("[geoip_csv] Invalid CIDR found! IP=%s CIDR=%d! Bad CSV file?", ip, cidr); 426 continue; 427 } 428 429 if (inet_pton(AF_INET, ip, &addr) < 1) 430 { 431 config_warn("[geoip_csv] Invalid IP found! \"%s\" Bad CSV file?", ip); 432 continue; 433 } 434 addr = htonl(addr); 435 436 mask = 0; 437 while (cidr) 438 { /* calculate netmask */ 439 mask >>= 1; 440 mask |= (1<<31); 441 cidr--; 442 } 443 444 i=0; 445 do 446 { /* multiple iterations in case CIDR is <8 and we have multiple first octets matching */ 447 uint8_t index = addr>>24; 448 if (!curr[index]) 449 { 450 geoip_csv_ip_range_list[index] = safe_alloc(sizeof(struct geoip_csv_ip_range)); 451 curr[index] = geoip_csv_ip_range_list[index]; 452 } else 453 { 454 curr[index]->next = safe_alloc(sizeof(struct geoip_csv_ip_range)); 455 curr[index] = curr[index]->next; 456 } 457 ptr = curr[index]; 458 ptr->next = NULL; 459 ptr->addr = addr; 460 ptr->mask = mask; 461 ptr->geoid = geoid; 462 i++; 463 index++; 464 } while (i<=((~mask)>>24)); 465 } 466 fclose(u); 467 return 0; 468 } 469 470 static int geoip_csv_ip6_convert(char *ip, uint16_t out[8]) 471 { /* convert text to binary form */ 472 uint16_t tmp[8]; 473 int i; 474 if (inet_pton(AF_INET6, ip, out) < 1) 475 return 0; 476 for (i=0; i<8; i++) 477 { 478 out[i] = htons(out[i]); 479 } 480 return 1; 481 } 482 483 #define IPV6_STRING_SIZE 40 484 485 static int geoip_csv_read_ipv6(char *file) 486 { 487 FILE *u; 488 char buf[BUFLEN+1]; 489 char *bptr, *optr; 490 int cidr, geoid; 491 char ip[IPV6_STRING_SIZE]; 492 uint16_t addr[8]; 493 uint16_t mask[8]; 494 struct geoip_csv_ip6_range *curr = NULL; 495 struct geoip_csv_ip6_range *ptr; 496 int error; 497 int length; 498 char *filename = NULL; 499 500 safe_strdup(filename, file); 501 convert_to_absolute_path(&filename, CONFDIR); 502 u = fopen(filename, "r"); 503 safe_free(filename); 504 if (!u) 505 { 506 config_warn("[geoip_csv] Cannot open IPv6 ranges list file"); 507 return 1; 508 } 509 if (!fgets(buf, BUFLEN, u)) 510 { 511 config_warn("[geoip_csv] IPv6 list file is empty"); 512 fclose(u); 513 return 1; 514 } 515 while (fgets(buf, BUFLEN, u)) 516 { 517 error = 0; 518 bptr = buf; 519 optr = ip; 520 length = 0; 521 while (*bptr != '/') 522 { 523 if (!*bptr) 524 { 525 error = 1; 526 break; 527 } 528 if (++length >= IPV6_STRING_SIZE) 529 { 530 ip[IPV6_STRING_SIZE-1] = '\0'; 531 config_warn("[geoip_csv] Too long IPv6 address found, starts with %s. Bad CSV file?", ip); 532 error = 1; 533 break; 534 } 535 *optr++ = *bptr++; 536 } 537 if (error) 538 continue; 539 *optr = '\0'; 540 bptr++; 541 if (!geoip_csv_ip6_convert(ip, addr)) 542 { 543 config_warn("[geoip_csv] Invalid IP found! \"%s\" Bad CSV file?", ip); 544 continue; 545 } 546 sscanf(bptr, "%d,%d,", &cidr, &geoid); 547 if (cidr < 1 || cidr > 128) 548 { 549 config_warn("[geoip_csv] Invalid CIDR found! CIDR=%d Bad CSV file?", cidr); 550 continue; 551 } 552 553 memset(mask, 0, 16); 554 555 int mask_bit = 0; 556 while (cidr) 557 { /* calculate netmask */ 558 mask[mask_bit/16] |= 1<<(15-(mask_bit%16)); 559 mask_bit++; 560 cidr--; 561 } 562 563 if (!curr) 564 { 565 geoip_csv_ip6_range_list = safe_alloc(sizeof(struct geoip_csv_ip6_range)); 566 curr = geoip_csv_ip6_range_list; 567 } else 568 { 569 curr->next = safe_alloc(sizeof(struct geoip_csv_ip6_range)); 570 curr = curr->next; 571 } 572 ptr = curr; 573 ptr->next = NULL; 574 memcpy(ptr->addr, addr, 16); 575 memcpy(ptr->mask, mask, 16); 576 ptr->geoid = geoid; 577 } 578 fclose(u); 579 return 0; 580 } 581 582 /* CSV fields; no STATE_GEONAME_ID because of using %d in fscanf */ 583 #define STATE_LOCALE_CODE 0 584 #define STATE_CONTINENT_CODE 1 585 #define STATE_CONTINENT_NAME 2 586 #define STATE_COUNTRY_ISO_CODE 3 587 #define STATE_COUNTRY_NAME 4 588 #define STATE_IS_IN_EU 5 589 590 #define MEMBER_SIZE(type,member) sizeof(((type *)0)->member) 591 592 static int geoip_csv_read_countries(char *file) 593 { 594 FILE *u; 595 char code[MEMBER_SIZE(struct geoip_csv_country, code)]; 596 char continent[MEMBER_SIZE(struct geoip_csv_country, continent)]; 597 char name[MEMBER_SIZE(struct geoip_csv_country, name)]; 598 char buf[BUFLEN+1]; 599 int state; 600 int id; 601 struct geoip_csv_country *curr = NULL; 602 char *filename = NULL; 603 604 safe_strdup(filename, file); 605 convert_to_absolute_path(&filename, CONFDIR); 606 u = fopen(filename, "r"); 607 safe_free(filename); 608 if (!u) 609 { 610 config_warn("[geoip_csv] Cannot open countries list file"); 611 return 1; 612 } 613 614 if (!fgets(buf, BUFLEN, u)) 615 { 616 config_warn("[geoip_csv] Countries list file is empty"); 617 fclose(u); 618 return 1; 619 } 620 while (fscanf(u, "%d,%" STR(BUFLEN) "[^\n]", &id, buf) == 2) 621 { /* getting country ID integer and all other data in string */ 622 char *ptr = buf; 623 char *codeptr = code; 624 char *contptr = continent; 625 char *nptr = name; 626 int quote_open = 0; 627 int length = 0; 628 state = STATE_LOCALE_CODE; 629 while (*ptr) 630 { 631 switch (state) 632 { 633 case STATE_CONTINENT_NAME: 634 if (*ptr == ',') 635 goto next_line; /* no continent? */ 636 if (length >= MEMBER_SIZE(struct geoip_csv_country, continent)) 637 { 638 *contptr = '\0'; 639 config_warn("[geoip_csv] Too long continent name found: `%s`. If you are sure your countries file is correct, please file a bug report.", continent); 640 goto next_line; 641 } 642 *contptr = *ptr; /* scan for continent name */ 643 contptr++; 644 length++; 645 break; 646 case STATE_COUNTRY_ISO_CODE: 647 if (*ptr == ',') /* country code is empty */ 648 goto next_line; /* -- that means only the continent is specified - we ignore it completely */ 649 if (length >= MEMBER_SIZE(struct geoip_csv_country, code)) 650 { 651 *codeptr = '\0'; 652 config_warn("[geoip_csv] Too long country code found: `%s`. If you are sure your countries file is correct, please file a bug report.", code); 653 goto next_line; 654 } 655 *codeptr = *ptr; // scan for country code (DE, PL, US etc) 656 codeptr++; 657 length++; 658 break; 659 case STATE_COUNTRY_NAME: 660 goto read_country_name; 661 default: 662 break; // ignore this field and wait for next one 663 } 664 ptr++; 665 if (*ptr == ',') 666 { 667 length = 0; 668 ptr++; 669 state++; 670 } 671 } 672 read_country_name: 673 *codeptr = '\0'; 674 *contptr = '\0'; 675 length = 0; 676 while (*ptr) 677 { 678 switch (*ptr) 679 { 680 case '"': 681 quote_open = !quote_open; 682 ptr++; 683 continue; 684 case ',': 685 if (!quote_open) 686 goto end_country_name; /* we reached the end of current CSV field */ 687 /* fall through */ 688 default: 689 *nptr++ = *ptr++; 690 if (length >= MEMBER_SIZE(struct geoip_csv_country, name)) 691 { 692 *nptr = '\0'; 693 config_warn("[geoip_csv] Too long country name found: `%s`. If you are sure your countries file is correct, please file a bug report.", name); 694 goto next_line; 695 } 696 break; // scan for country name 697 } 698 } 699 end_country_name: 700 *nptr = '\0'; 701 if (geoip_csv_country_list) 702 { 703 curr->next = safe_alloc(sizeof(struct geoip_csv_country)); 704 curr = curr->next; 705 } else 706 { 707 geoip_csv_country_list = safe_alloc(sizeof(struct geoip_csv_country)); 708 curr = geoip_csv_country_list; 709 } 710 curr->next = NULL; 711 strcpy(curr->code, code); 712 strcpy(curr->name, name); 713 strcpy(curr->continent, continent); 714 curr->id = id; 715 next_line: continue; 716 } 717 fclose(u); 718 return 0; 719 } 720 721 static struct geoip_csv_country *geoip_csv_get_country(int id) 722 { 723 struct geoip_csv_country *curr = geoip_csv_country_list; 724 if (!curr) 725 return NULL; 726 int found = 0; 727 for (;curr;curr = curr->next) 728 { 729 if (curr->id == id) 730 { 731 found = 1; 732 break; 733 } 734 } 735 if (found) 736 return curr; 737 return NULL; 738 } 739 740 static int geoip_csv_get_v4_geoid(char *iip) 741 { 742 uint32_t addr, tmp_addr; 743 struct geoip_csv_ip_range *curr; 744 int i; 745 int found = 0; 746 if (inet_pton(AF_INET, iip, &addr) < 1) 747 { 748 unreal_log(ULOG_WARNING, "geoip_csv", "UNSUPPORTED_IP", NULL, "Invalid or unsupported client IP $ip", log_data_string("ip", iip)); 749 return 0; 750 } 751 addr = htonl(addr); 752 curr = geoip_csv_ip_range_list[addr>>24]; 753 if (curr) 754 { 755 i = 0; 756 for (;curr;curr = curr->next) 757 { 758 tmp_addr = addr; 759 tmp_addr &= curr->mask; /* mask the address to filter out net prefix only */ 760 if (tmp_addr == curr->addr) 761 { /* ... and match it to the loaded data */ 762 found = 1; 763 break; 764 } 765 i++; 766 } 767 } 768 if (found) 769 return curr->geoid; 770 return 0; 771 } 772 773 static int geoip_csv_get_v6_geoid(char *iip) 774 { 775 uint16_t addr[8]; 776 struct geoip_csv_ip6_range *curr; 777 int i; 778 int found = 0; 779 780 if (!geoip_csv_ip6_convert(iip, addr)) 781 { 782 unreal_log(ULOG_WARNING, "geoip_csv", "UNSUPPORTED_IP", NULL, "Invalid or unsupported client IP $ip", log_data_string("ip", iip)); 783 return 0; 784 } 785 curr = geoip_csv_ip6_range_list; 786 if (curr) 787 { 788 for (;curr;curr = curr->next) 789 { 790 found = 1; 791 for (i=0; i<8; i++) 792 { 793 if (curr->addr[i] != (addr[i] & curr->mask[i])) 794 { /* compare net address to loaded data */ 795 found = 0; 796 break; 797 } 798 } 799 if(found) 800 break; 801 } 802 } 803 if (found) 804 return curr->geoid; 805 return 0; 806 } 807 808 GeoIPResult *geoip_lookup_csv(char *ip) 809 { 810 int geoid; 811 struct geoip_csv_country *country; 812 GeoIPResult *r; 813 814 if (!ip) 815 return NULL; 816 817 if (strchr(ip, ':')) 818 { 819 geoid = geoip_csv_get_v6_geoid(ip); 820 } else 821 { 822 geoid = geoip_csv_get_v4_geoid(ip); 823 } 824 825 if (geoid == 0) 826 return NULL; 827 828 country = geoip_csv_get_country(geoid); 829 830 if (!country) 831 return NULL; 832 833 r = safe_alloc(sizeof(GeoIPResult)); 834 safe_strdup(r->country_code, country->code); 835 safe_strdup(r->country_name, country->name); 836 return r; 837 } 838