lh | 9ed821d | 2023-04-07 01:36:19 -0700 | [diff] [blame] | 1 | /* |
| 2 | * |
| 3 | * IP-address/hostname to country converter. |
| 4 | * |
| 5 | * Problem; you want to know where IP a.b.c.d is located. |
| 6 | * |
| 7 | * Use ares_gethostbyname ("d.c.b.a.zz.countries.nerd.dk") |
| 8 | * and get the CNAME (host->h_name). Result will be: |
| 9 | * CNAME = zz<CC>.countries.nerd.dk with address 127.0.x.y (ver 1) or |
| 10 | * CNAME = <a.b.c.d>.zz.countries.nerd.dk with address 127.0.x.y (ver 2) |
| 11 | * |
| 12 | * The 2 letter country code is in <CC> and the ISO-3166 country |
| 13 | * number is in x.y (number = x*256 + y). Version 2 of the protocol is missing |
| 14 | * the <CC> number. |
| 15 | * |
| 16 | * Ref: http://countries.nerd.dk/more.html |
| 17 | * |
| 18 | * Written by G. Vanem <gvanem@yahoo.no> 2006, 2007 |
| 19 | * |
| 20 | * NB! This program may not be big-endian aware. |
| 21 | * |
| 22 | * Permission to use, copy, modify, and distribute this |
| 23 | * software and its documentation for any purpose and without |
| 24 | * fee is hereby granted, provided that the above copyright |
| 25 | * notice appear in all copies and that both that copyright |
| 26 | * notice and this permission notice appear in supporting |
| 27 | * documentation, and that the name of M.I.T. not be used in |
| 28 | * advertising or publicity pertaining to distribution of the |
| 29 | * software without specific, written prior permission. |
| 30 | * M.I.T. makes no representations about the suitability of |
| 31 | * this software for any purpose. It is provided "as is" |
| 32 | * without express or implied warranty. |
| 33 | */ |
| 34 | |
| 35 | #include "ares_setup.h" |
| 36 | |
| 37 | #ifdef HAVE_STRINGS_H |
| 38 | #include <strings.h> |
| 39 | #endif |
| 40 | |
| 41 | #if defined(WIN32) && !defined(WATT32) |
| 42 | #include <winsock.h> |
| 43 | #else |
| 44 | #include <arpa/inet.h> |
| 45 | #include <netinet/in.h> |
| 46 | #include <netdb.h> |
| 47 | #endif |
| 48 | |
| 49 | #include "ares.h" |
| 50 | #include "ares_getopt.h" |
| 51 | #include "ares_nowarn.h" |
| 52 | |
| 53 | #ifndef HAVE_STRDUP |
| 54 | # include "ares_strdup.h" |
| 55 | # define strdup(ptr) ares_strdup(ptr) |
| 56 | #endif |
| 57 | |
| 58 | #ifndef HAVE_STRCASECMP |
| 59 | # include "ares_strcasecmp.h" |
| 60 | # define strcasecmp(p1,p2) ares_strcasecmp(p1,p2) |
| 61 | #endif |
| 62 | |
| 63 | #ifndef HAVE_STRNCASECMP |
| 64 | # include "ares_strcasecmp.h" |
| 65 | # define strncasecmp(p1,p2,n) ares_strncasecmp(p1,p2,n) |
| 66 | #endif |
| 67 | |
| 68 | #ifndef INADDR_NONE |
| 69 | #define INADDR_NONE 0xffffffff |
| 70 | #endif |
| 71 | |
| 72 | static const char *usage = "acountry [-vh?] {host|addr} ...\n"; |
| 73 | static const char nerd_fmt[] = "%u.%u.%u.%u.zz.countries.nerd.dk"; |
| 74 | static const char *nerd_ver1 = nerd_fmt + 14; /* .countries.nerd.dk */ |
| 75 | static const char *nerd_ver2 = nerd_fmt + 11; /* .zz.countries.nerd.dk */ |
| 76 | static int verbose = 0; |
| 77 | |
| 78 | #define TRACE(fmt) do { \ |
| 79 | if (verbose > 0) \ |
| 80 | printf fmt ; \ |
| 81 | } WHILE_FALSE |
| 82 | |
| 83 | static void wait_ares(ares_channel channel); |
| 84 | static void callback(void *arg, int status, int timeouts, struct hostent *host); |
| 85 | static void callback2(void *arg, int status, int timeouts, struct hostent *host); |
| 86 | static void find_country_from_cname(const char *cname, struct in_addr addr); |
| 87 | |
| 88 | static void Abort(const char *fmt, ...) |
| 89 | { |
| 90 | va_list args; |
| 91 | va_start(args, fmt); |
| 92 | vfprintf(stderr, fmt, args); |
| 93 | va_end(args); |
| 94 | exit(1); |
| 95 | } |
| 96 | |
| 97 | int main(int argc, char **argv) |
| 98 | { |
| 99 | ares_channel channel; |
| 100 | int ch, status; |
| 101 | |
| 102 | #if defined(WIN32) && !defined(WATT32) |
| 103 | WORD wVersionRequested = MAKEWORD(USE_WINSOCK,USE_WINSOCK); |
| 104 | WSADATA wsaData; |
| 105 | WSAStartup(wVersionRequested, &wsaData); |
| 106 | #endif |
| 107 | |
| 108 | status = ares_library_init(ARES_LIB_INIT_ALL); |
| 109 | if (status != ARES_SUCCESS) |
| 110 | { |
| 111 | fprintf(stderr, "ares_library_init: %s\n", ares_strerror(status)); |
| 112 | return 1; |
| 113 | } |
| 114 | |
| 115 | while ((ch = ares_getopt(argc, argv, "dvh?")) != -1) |
| 116 | switch (ch) |
| 117 | { |
| 118 | case 'd': |
| 119 | #ifdef WATT32 |
| 120 | dbug_init(); |
| 121 | #endif |
| 122 | break; |
| 123 | case 'v': |
| 124 | verbose++; |
| 125 | break; |
| 126 | case 'h': |
| 127 | case '?': |
| 128 | default: |
| 129 | Abort(usage); |
| 130 | } |
| 131 | |
| 132 | argc -= optind; |
| 133 | argv += optind; |
| 134 | if (argc < 1) |
| 135 | Abort(usage); |
| 136 | |
| 137 | status = ares_init(&channel); |
| 138 | if (status != ARES_SUCCESS) |
| 139 | { |
| 140 | fprintf(stderr, "ares_init: %s\n", ares_strerror(status)); |
| 141 | return 1; |
| 142 | } |
| 143 | |
| 144 | /* Initiate the queries, one per command-line argument. */ |
| 145 | for ( ; *argv; argv++) |
| 146 | { |
| 147 | struct in_addr addr; |
| 148 | char buf[100]; |
| 149 | |
| 150 | /* If this fails, assume '*argv' is a host-name that |
| 151 | * must be resolved first |
| 152 | */ |
| 153 | if (ares_inet_pton(AF_INET, *argv, &addr) != 1) |
| 154 | { |
| 155 | ares_gethostbyname(channel, *argv, AF_INET, callback2, &addr); |
| 156 | wait_ares(channel); |
| 157 | if (addr.s_addr == INADDR_NONE) |
| 158 | { |
| 159 | printf("Failed to lookup %s\n", *argv); |
| 160 | continue; |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | sprintf(buf, nerd_fmt, |
| 165 | (unsigned int)(addr.s_addr >> 24), |
| 166 | (unsigned int)((addr.s_addr >> 16) & 255), |
| 167 | (unsigned int)((addr.s_addr >> 8) & 255), |
| 168 | (unsigned int)(addr.s_addr & 255)); |
| 169 | TRACE(("Looking up %s...", buf)); |
| 170 | fflush(stdout); |
| 171 | ares_gethostbyname(channel, buf, AF_INET, callback, buf); |
| 172 | } |
| 173 | |
| 174 | wait_ares(channel); |
| 175 | ares_destroy(channel); |
| 176 | |
| 177 | ares_library_cleanup(); |
| 178 | |
| 179 | #if defined(WIN32) && !defined(WATT32) |
| 180 | WSACleanup(); |
| 181 | #endif |
| 182 | |
| 183 | return 0; |
| 184 | } |
| 185 | |
| 186 | /* |
| 187 | * Wait for the queries to complete. |
| 188 | */ |
| 189 | static void wait_ares(ares_channel channel) |
| 190 | { |
| 191 | for (;;) |
| 192 | { |
| 193 | struct timeval *tvp, tv; |
| 194 | fd_set read_fds, write_fds; |
| 195 | int nfds; |
| 196 | |
| 197 | FD_ZERO(&read_fds); |
| 198 | FD_ZERO(&write_fds); |
| 199 | nfds = ares_fds(channel, &read_fds, &write_fds); |
| 200 | if (nfds == 0) |
| 201 | break; |
| 202 | tvp = ares_timeout(channel, NULL, &tv); |
| 203 | nfds = select(nfds, &read_fds, &write_fds, NULL, tvp); |
| 204 | if (nfds < 0) |
| 205 | continue; |
| 206 | ares_process(channel, &read_fds, &write_fds); |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | /* |
| 211 | * This is the callback used when we have the IP-address of interest. |
| 212 | * Extract the CNAME and figure out the country-code from it. |
| 213 | */ |
| 214 | static void callback(void *arg, int status, int timeouts, struct hostent *host) |
| 215 | { |
| 216 | const char *name = (const char*)arg; |
| 217 | const char *cname; |
| 218 | char buf[20]; |
| 219 | |
| 220 | (void)timeouts; |
| 221 | |
| 222 | if (!host || status != ARES_SUCCESS) |
| 223 | { |
| 224 | printf("Failed to lookup %s: %s\n", name, ares_strerror(status)); |
| 225 | return; |
| 226 | } |
| 227 | |
| 228 | TRACE(("\nFound address %s, name %s\n", |
| 229 | ares_inet_ntop(AF_INET,(const char*)host->h_addr,buf,sizeof(buf)), |
| 230 | host->h_name)); |
| 231 | |
| 232 | cname = host->h_name; /* CNAME gets put here */ |
| 233 | if (!cname) |
| 234 | printf("Failed to get CNAME for %s\n", name); |
| 235 | else |
| 236 | find_country_from_cname(cname, *(struct in_addr*)host->h_addr); |
| 237 | } |
| 238 | |
| 239 | /* |
| 240 | * This is the callback used to obtain the IP-address of the host of interest. |
| 241 | */ |
| 242 | static void callback2(void *arg, int status, int timeouts, struct hostent *host) |
| 243 | { |
| 244 | struct in_addr *addr = (struct in_addr*) arg; |
| 245 | |
| 246 | (void)timeouts; |
| 247 | if (!host || status != ARES_SUCCESS) |
| 248 | memset(addr, INADDR_NONE, sizeof(*addr)); |
| 249 | else |
| 250 | memcpy(addr, host->h_addr, sizeof(*addr)); |
| 251 | } |
| 252 | |
| 253 | struct search_list { |
| 254 | int country_number; /* ISO-3166 country number */ |
| 255 | char short_name[3]; /* A2 short country code */ |
| 256 | const char *long_name; /* normal country name */ |
| 257 | }; |
| 258 | |
| 259 | static const struct search_list *list_lookup(int number, const struct search_list *list, int num) |
| 260 | { |
| 261 | while (num > 0 && list->long_name) |
| 262 | { |
| 263 | if (list->country_number == number) |
| 264 | return (list); |
| 265 | num--; |
| 266 | list++; |
| 267 | } |
| 268 | return (NULL); |
| 269 | } |
| 270 | |
| 271 | /* |
| 272 | * Ref: ftp://ftp.ripe.net/iso3166-countrycodes.txt |
| 273 | */ |
| 274 | static const struct search_list country_list[] = { |
| 275 | { 4, "af", "Afghanistan" }, |
| 276 | { 248, "ax", "Ã…land Island" }, |
| 277 | { 8, "al", "Albania" }, |
| 278 | { 12, "dz", "Algeria" }, |
| 279 | { 16, "as", "American Samoa" }, |
| 280 | { 20, "ad", "Andorra" }, |
| 281 | { 24, "ao", "Angola" }, |
| 282 | { 660, "ai", "Anguilla" }, |
| 283 | { 10, "aq", "Antarctica" }, |
| 284 | { 28, "ag", "Antigua & Barbuda" }, |
| 285 | { 32, "ar", "Argentina" }, |
| 286 | { 51, "am", "Armenia" }, |
| 287 | { 533, "aw", "Aruba" }, |
| 288 | { 36, "au", "Australia" }, |
| 289 | { 40, "at", "Austria" }, |
| 290 | { 31, "az", "Azerbaijan" }, |
| 291 | { 44, "bs", "Bahamas" }, |
| 292 | { 48, "bh", "Bahrain" }, |
| 293 | { 50, "bd", "Bangladesh" }, |
| 294 | { 52, "bb", "Barbados" }, |
| 295 | { 112, "by", "Belarus" }, |
| 296 | { 56, "be", "Belgium" }, |
| 297 | { 84, "bz", "Belize" }, |
| 298 | { 204, "bj", "Benin" }, |
| 299 | { 60, "bm", "Bermuda" }, |
| 300 | { 64, "bt", "Bhutan" }, |
| 301 | { 68, "bo", "Bolivia" }, |
| 302 | { 70, "ba", "Bosnia & Herzegowina" }, |
| 303 | { 72, "bw", "Botswana" }, |
| 304 | { 74, "bv", "Bouvet Island" }, |
| 305 | { 76, "br", "Brazil" }, |
| 306 | { 86, "io", "British Indian Ocean Territory" }, |
| 307 | { 96, "bn", "Brunei Darussalam" }, |
| 308 | { 100, "bg", "Bulgaria" }, |
| 309 | { 854, "bf", "Burkina Faso" }, |
| 310 | { 108, "bi", "Burundi" }, |
| 311 | { 116, "kh", "Cambodia" }, |
| 312 | { 120, "cm", "Cameroon" }, |
| 313 | { 124, "ca", "Canada" }, |
| 314 | { 132, "cv", "Cape Verde" }, |
| 315 | { 136, "ky", "Cayman Islands" }, |
| 316 | { 140, "cf", "Central African Republic" }, |
| 317 | { 148, "td", "Chad" }, |
| 318 | { 152, "cl", "Chile" }, |
| 319 | { 156, "cn", "China" }, |
| 320 | { 162, "cx", "Christmas Island" }, |
| 321 | { 166, "cc", "Cocos Islands" }, |
| 322 | { 170, "co", "Colombia" }, |
| 323 | { 174, "km", "Comoros" }, |
| 324 | { 178, "cg", "Congo" }, |
| 325 | { 180, "cd", "Congo" }, |
| 326 | { 184, "ck", "Cook Islands" }, |
| 327 | { 188, "cr", "Costa Rica" }, |
| 328 | { 384, "ci", "Cote d'Ivoire" }, |
| 329 | { 191, "hr", "Croatia" }, |
| 330 | { 192, "cu", "Cuba" }, |
| 331 | { 196, "cy", "Cyprus" }, |
| 332 | { 203, "cz", "Czech Republic" }, |
| 333 | { 208, "dk", "Denmark" }, |
| 334 | { 262, "dj", "Djibouti" }, |
| 335 | { 212, "dm", "Dominica" }, |
| 336 | { 214, "do", "Dominican Republic" }, |
| 337 | { 218, "ec", "Ecuador" }, |
| 338 | { 818, "eg", "Egypt" }, |
| 339 | { 222, "sv", "El Salvador" }, |
| 340 | { 226, "gq", "Equatorial Guinea" }, |
| 341 | { 232, "er", "Eritrea" }, |
| 342 | { 233, "ee", "Estonia" }, |
| 343 | { 231, "et", "Ethiopia" }, |
| 344 | { 238, "fk", "Falkland Islands" }, |
| 345 | { 234, "fo", "Faroe Islands" }, |
| 346 | { 242, "fj", "Fiji" }, |
| 347 | { 246, "fi", "Finland" }, |
| 348 | { 250, "fr", "France" }, |
| 349 | { 249, "fx", "France, Metropolitan" }, |
| 350 | { 254, "gf", "French Guiana" }, |
| 351 | { 258, "pf", "French Polynesia" }, |
| 352 | { 260, "tf", "French Southern Territories" }, |
| 353 | { 266, "ga", "Gabon" }, |
| 354 | { 270, "gm", "Gambia" }, |
| 355 | { 268, "ge", "Georgia" }, |
| 356 | { 276, "de", "Germany" }, |
| 357 | { 288, "gh", "Ghana" }, |
| 358 | { 292, "gi", "Gibraltar" }, |
| 359 | { 300, "gr", "Greece" }, |
| 360 | { 304, "gl", "Greenland" }, |
| 361 | { 308, "gd", "Grenada" }, |
| 362 | { 312, "gp", "Guadeloupe" }, |
| 363 | { 316, "gu", "Guam" }, |
| 364 | { 320, "gt", "Guatemala" }, |
| 365 | { 324, "gn", "Guinea" }, |
| 366 | { 624, "gw", "Guinea-Bissau" }, |
| 367 | { 328, "gy", "Guyana" }, |
| 368 | { 332, "ht", "Haiti" }, |
| 369 | { 334, "hm", "Heard & Mc Donald Islands" }, |
| 370 | { 336, "va", "Vatican City" }, |
| 371 | { 340, "hn", "Honduras" }, |
| 372 | { 344, "hk", "Hong kong" }, |
| 373 | { 348, "hu", "Hungary" }, |
| 374 | { 352, "is", "Iceland" }, |
| 375 | { 356, "in", "India" }, |
| 376 | { 360, "id", "Indonesia" }, |
| 377 | { 364, "ir", "Iran" }, |
| 378 | { 368, "iq", "Iraq" }, |
| 379 | { 372, "ie", "Ireland" }, |
| 380 | { 376, "il", "Israel" }, |
| 381 | { 380, "it", "Italy" }, |
| 382 | { 388, "jm", "Jamaica" }, |
| 383 | { 392, "jp", "Japan" }, |
| 384 | { 400, "jo", "Jordan" }, |
| 385 | { 398, "kz", "Kazakhstan" }, |
| 386 | { 404, "ke", "Kenya" }, |
| 387 | { 296, "ki", "Kiribati" }, |
| 388 | { 408, "kp", "Korea (north)" }, |
| 389 | { 410, "kr", "Korea (south)" }, |
| 390 | { 414, "kw", "Kuwait" }, |
| 391 | { 417, "kg", "Kyrgyzstan" }, |
| 392 | { 418, "la", "Laos" }, |
| 393 | { 428, "lv", "Latvia" }, |
| 394 | { 422, "lb", "Lebanon" }, |
| 395 | { 426, "ls", "Lesotho" }, |
| 396 | { 430, "lr", "Liberia" }, |
| 397 | { 434, "ly", "Libya" }, |
| 398 | { 438, "li", "Liechtenstein" }, |
| 399 | { 440, "lt", "Lithuania" }, |
| 400 | { 442, "lu", "Luxembourg" }, |
| 401 | { 446, "mo", "Macao" }, |
| 402 | { 807, "mk", "Macedonia" }, |
| 403 | { 450, "mg", "Madagascar" }, |
| 404 | { 454, "mw", "Malawi" }, |
| 405 | { 458, "my", "Malaysia" }, |
| 406 | { 462, "mv", "Maldives" }, |
| 407 | { 466, "ml", "Mali" }, |
| 408 | { 470, "mt", "Malta" }, |
| 409 | { 584, "mh", "Marshall Islands" }, |
| 410 | { 474, "mq", "Martinique" }, |
| 411 | { 478, "mr", "Mauritania" }, |
| 412 | { 480, "mu", "Mauritius" }, |
| 413 | { 175, "yt", "Mayotte" }, |
| 414 | { 484, "mx", "Mexico" }, |
| 415 | { 583, "fm", "Micronesia" }, |
| 416 | { 498, "md", "Moldova" }, |
| 417 | { 492, "mc", "Monaco" }, |
| 418 | { 496, "mn", "Mongolia" }, |
| 419 | { 500, "ms", "Montserrat" }, |
| 420 | { 504, "ma", "Morocco" }, |
| 421 | { 508, "mz", "Mozambique" }, |
| 422 | { 104, "mm", "Myanmar" }, |
| 423 | { 516, "na", "Namibia" }, |
| 424 | { 520, "nr", "Nauru" }, |
| 425 | { 524, "np", "Nepal" }, |
| 426 | { 528, "nl", "Netherlands" }, |
| 427 | { 530, "an", "Netherlands Antilles" }, |
| 428 | { 540, "nc", "New Caledonia" }, |
| 429 | { 554, "nz", "New Zealand" }, |
| 430 | { 558, "ni", "Nicaragua" }, |
| 431 | { 562, "ne", "Niger" }, |
| 432 | { 566, "ng", "Nigeria" }, |
| 433 | { 570, "nu", "Niue" }, |
| 434 | { 574, "nf", "Norfolk Island" }, |
| 435 | { 580, "mp", "Northern Mariana Islands" }, |
| 436 | { 578, "no", "Norway" }, |
| 437 | { 512, "om", "Oman" }, |
| 438 | { 586, "pk", "Pakistan" }, |
| 439 | { 585, "pw", "Palau" }, |
| 440 | { 275, "ps", "Palestinian Territory" }, |
| 441 | { 591, "pa", "Panama" }, |
| 442 | { 598, "pg", "Papua New Guinea" }, |
| 443 | { 600, "py", "Paraguay" }, |
| 444 | { 604, "pe", "Peru" }, |
| 445 | { 608, "ph", "Philippines" }, |
| 446 | { 612, "pn", "Pitcairn" }, |
| 447 | { 616, "pl", "Poland" }, |
| 448 | { 620, "pt", "Portugal" }, |
| 449 | { 630, "pr", "Puerto Rico" }, |
| 450 | { 634, "qa", "Qatar" }, |
| 451 | { 638, "re", "Reunion" }, |
| 452 | { 642, "ro", "Romania" }, |
| 453 | { 643, "ru", "Russia" }, |
| 454 | { 646, "rw", "Rwanda" }, |
| 455 | { 659, "kn", "Saint Kitts & Nevis" }, |
| 456 | { 662, "lc", "Saint Lucia" }, |
| 457 | { 670, "vc", "Saint Vincent" }, |
| 458 | { 882, "ws", "Samoa" }, |
| 459 | { 674, "sm", "San Marino" }, |
| 460 | { 678, "st", "Sao Tome & Principe" }, |
| 461 | { 682, "sa", "Saudi Arabia" }, |
| 462 | { 686, "sn", "Senegal" }, |
| 463 | { 891, "cs", "Serbia and Montenegro" }, |
| 464 | { 690, "sc", "Seychelles" }, |
| 465 | { 694, "sl", "Sierra Leone" }, |
| 466 | { 702, "sg", "Singapore" }, |
| 467 | { 703, "sk", "Slovakia" }, |
| 468 | { 705, "si", "Slovenia" }, |
| 469 | { 90, "sb", "Solomon Islands" }, |
| 470 | { 706, "so", "Somalia" }, |
| 471 | { 710, "za", "South Africa" }, |
| 472 | { 239, "gs", "South Georgia" }, |
| 473 | { 724, "es", "Spain" }, |
| 474 | { 144, "lk", "Sri Lanka" }, |
| 475 | { 654, "sh", "St. Helena" }, |
| 476 | { 666, "pm", "St. Pierre & Miquelon" }, |
| 477 | { 736, "sd", "Sudan" }, |
| 478 | { 740, "sr", "Suriname" }, |
| 479 | { 744, "sj", "Svalbard & Jan Mayen Islands" }, |
| 480 | { 748, "sz", "Swaziland" }, |
| 481 | { 752, "se", "Sweden" }, |
| 482 | { 756, "ch", "Switzerland" }, |
| 483 | { 760, "sy", "Syrian Arab Republic" }, |
| 484 | { 626, "tl", "Timor-Leste" }, |
| 485 | { 158, "tw", "Taiwan" }, |
| 486 | { 762, "tj", "Tajikistan" }, |
| 487 | { 834, "tz", "Tanzania" }, |
| 488 | { 764, "th", "Thailand" }, |
| 489 | { 768, "tg", "Togo" }, |
| 490 | { 772, "tk", "Tokelau" }, |
| 491 | { 776, "to", "Tonga" }, |
| 492 | { 780, "tt", "Trinidad & Tobago" }, |
| 493 | { 788, "tn", "Tunisia" }, |
| 494 | { 792, "tr", "Turkey" }, |
| 495 | { 795, "tm", "Turkmenistan" }, |
| 496 | { 796, "tc", "Turks & Caicos Islands" }, |
| 497 | { 798, "tv", "Tuvalu" }, |
| 498 | { 800, "ug", "Uganda" }, |
| 499 | { 804, "ua", "Ukraine" }, |
| 500 | { 784, "ae", "United Arab Emirates" }, |
| 501 | { 826, "gb", "United Kingdom" }, |
| 502 | { 840, "us", "United States" }, |
| 503 | { 581, "um", "United States Minor Outlying Islands" }, |
| 504 | { 858, "uy", "Uruguay" }, |
| 505 | { 860, "uz", "Uzbekistan" }, |
| 506 | { 548, "vu", "Vanuatu" }, |
| 507 | { 862, "ve", "Venezuela" }, |
| 508 | { 704, "vn", "Vietnam" }, |
| 509 | { 92, "vg", "Virgin Islands (British)" }, |
| 510 | { 850, "vi", "Virgin Islands (US)" }, |
| 511 | { 876, "wf", "Wallis & Futuna Islands" }, |
| 512 | { 732, "eh", "Western Sahara" }, |
| 513 | { 887, "ye", "Yemen" }, |
| 514 | { 894, "zm", "Zambia" }, |
| 515 | { 716, "zw", "Zimbabwe" } |
| 516 | }; |
| 517 | |
| 518 | /* |
| 519 | * Check if start of 'str' is simply an IPv4 address. |
| 520 | */ |
| 521 | #define BYTE_OK(x) ((x) >= 0 && (x) <= 255) |
| 522 | |
| 523 | static int is_addr(char *str, char **end) |
| 524 | { |
| 525 | int a0, a1, a2, a3, num, rc = 0, length = 0; |
| 526 | |
| 527 | num = sscanf(str,"%3d.%3d.%3d.%3d%n",&a0,&a1,&a2,&a3,&length); |
| 528 | if( (num == 4) && |
| 529 | BYTE_OK(a0) && BYTE_OK(a1) && BYTE_OK(a2) && BYTE_OK(a3) && |
| 530 | length >= (3+4)) |
| 531 | { |
| 532 | rc = 1; |
| 533 | *end = str + length; |
| 534 | } |
| 535 | return rc; |
| 536 | } |
| 537 | |
| 538 | /* |
| 539 | * Find the country-code and name from the CNAME. E.g.: |
| 540 | * version 1: CNAME = zzno.countries.nerd.dk with address 127.0.2.66 |
| 541 | * yields ccode_A" = "no" and cnumber 578 (2.66). |
| 542 | * version 2: CNAME = <a.b.c.d>.zz.countries.nerd.dk with address 127.0.2.66 |
| 543 | * yields cnumber 578 (2.66). ccode_A is ""; |
| 544 | */ |
| 545 | static void find_country_from_cname(const char *cname, struct in_addr addr) |
| 546 | { |
| 547 | const struct search_list *country; |
| 548 | char ccode_A2[3], *ccopy, *dot_4; |
| 549 | int cnumber, z0, z1, ver_1, ver_2; |
| 550 | unsigned long ip; |
| 551 | |
| 552 | ip = ntohl(addr.s_addr); |
| 553 | z0 = TOLOWER(cname[0]); |
| 554 | z1 = TOLOWER(cname[1]); |
| 555 | ccopy = strdup(cname); |
| 556 | dot_4 = NULL; |
| 557 | |
| 558 | ver_1 = (z0 == 'z' && z1 == 'z' && !strcasecmp(cname+4,nerd_ver1)); |
| 559 | ver_2 = (is_addr(ccopy,&dot_4) && !strcasecmp(dot_4,nerd_ver2)); |
| 560 | |
| 561 | if (ver_1) |
| 562 | { |
| 563 | const char *dot = strchr(cname, '.'); |
| 564 | if (dot != cname+4) |
| 565 | { |
| 566 | printf("Unexpected CNAME %s (ver_1)\n", cname); |
| 567 | free(ccopy); |
| 568 | return; |
| 569 | } |
| 570 | } |
| 571 | else if (ver_2) |
| 572 | { |
| 573 | z0 = TOLOWER(dot_4[1]); |
| 574 | z1 = TOLOWER(dot_4[2]); |
| 575 | if (z0 != 'z' && z1 != 'z') |
| 576 | { |
| 577 | printf("Unexpected CNAME %s (ver_2)\n", cname); |
| 578 | free(ccopy); |
| 579 | return; |
| 580 | } |
| 581 | } |
| 582 | else |
| 583 | { |
| 584 | printf("Unexpected CNAME %s (ver?)\n", cname); |
| 585 | free(ccopy); |
| 586 | return; |
| 587 | } |
| 588 | |
| 589 | if (ver_1) |
| 590 | { |
| 591 | ccode_A2[0] = (char)TOLOWER(cname[2]); |
| 592 | ccode_A2[1] = (char)TOLOWER(cname[3]); |
| 593 | ccode_A2[2] = '\0'; |
| 594 | } |
| 595 | else |
| 596 | ccode_A2[0] = '\0'; |
| 597 | |
| 598 | cnumber = ip & 0xFFFF; |
| 599 | |
| 600 | TRACE(("Found country-code `%s', number %d\n", |
| 601 | ver_1 ? ccode_A2 : "<n/a>", cnumber)); |
| 602 | |
| 603 | country = list_lookup(cnumber, country_list, |
| 604 | sizeof(country_list) / sizeof(country_list[0])); |
| 605 | if (!country) |
| 606 | printf("Name for country-number %d not found.\n", cnumber); |
| 607 | else |
| 608 | { |
| 609 | if (ver_1) |
| 610 | { |
| 611 | if ((country->short_name[0] != ccode_A2[0]) || |
| 612 | (country->short_name[1] != ccode_A2[1]) || |
| 613 | (country->short_name[2] != ccode_A2[2])) |
| 614 | printf("short-name mismatch; %s vs %s\n", |
| 615 | country->short_name, ccode_A2); |
| 616 | } |
| 617 | printf("%s (%s), number %d.\n", |
| 618 | country->long_name, country->short_name, cnumber); |
| 619 | } |
| 620 | free(ccopy); |
| 621 | } |