diff options
Diffstat (limited to 'libGeoIP/GeoIPCity.c')
-rw-r--r-- | libGeoIP/GeoIPCity.c | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/libGeoIP/GeoIPCity.c b/libGeoIP/GeoIPCity.c new file mode 100644 index 0000000..543f829 --- /dev/null +++ b/libGeoIP/GeoIPCity.c @@ -0,0 +1,330 @@ + +/* + * GeoIPCity.c + * + * Copyright (C) 2016 MaxMind, Inc. + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "GeoIPCity.h" +#include "GeoIP.h" +#include "GeoIP_internal.h" +#if !defined(_WIN32) +#include <netdb.h> +#include <netinet/in.h> /* For ntohl */ +#include <unistd.h> +#else +#include <windows.h> +#include <winsock.h> + +#if defined(_MSC_VER) && \ + _MSC_VER >= 1400 // VS 2005+ deprecates fileno, lseek and read +#define fileno _fileno +#define read _read +#define lseek _lseek +#endif +#endif +#include <sys/types.h> /* For uint32_t */ +#ifdef HAVE_STDINT_H +#include <stdint.h> /* For uint32_t */ +#endif + +#if defined(_WIN32) && !defined(__MINGW32__) +#include "pread.h" +#endif + +#ifndef HAVE_PREAD +#define pread(fd, buf, count, offset) \ + (lseek(fd, offset, SEEK_SET) == offset ? read(fd, buf, count) : -1) +#endif /* HAVE_PREAD */ + +static const int FULL_RECORD_LENGTH = 50; + +static GeoIPRecord * +_extract_record(GeoIP *gi, unsigned int seek_record, int *next_record_ptr) { + int record_pointer; + unsigned char *record_buf = NULL; + unsigned char *begin_record_buf = NULL; + GeoIPRecord *record; + int str_length = 0; + int j; + double latitude = 0, longitude = 0; + int metroarea_combo = 0; + int bytes_read = 0; + if (seek_record == gi->databaseSegments[0]) { + return NULL; + } + + record = malloc(sizeof(GeoIPRecord)); + memset(record, 0, sizeof(GeoIPRecord)); + record->charset = gi->charset; + + record_pointer = + seek_record + (2 * gi->record_length - 1) * gi->databaseSegments[0]; + + if (gi->cache == NULL) { + begin_record_buf = record_buf = + malloc(sizeof(unsigned char) * FULL_RECORD_LENGTH); + bytes_read = pread(fileno(gi->GeoIPDatabase), + record_buf, + FULL_RECORD_LENGTH, + record_pointer); + if (bytes_read <= 0) { + /* eof or other error */ + free(begin_record_buf); + free(record); + return NULL; + } + } else { + if (gi->size <= record_pointer) { + /* record does not exist in the cache */ + free(record); + return NULL; + } + record_buf = gi->cache + (long)record_pointer; + } + + /* get country */ + record->continent_code = (char *)GeoIP_country_continent[record_buf[0]]; + record->country_code = (char *)GeoIP_country_code[record_buf[0]]; + record->country_code3 = (char *)GeoIP_country_code3[record_buf[0]]; + record->country_name = (char *)GeoIP_country_name_by_id(gi, record_buf[0]); + record_buf++; + + /* get region */ + while (record_buf[str_length] != '\0') { + str_length++; + } + if (str_length > 0) { + record->region = malloc(str_length + 1); + strncpy(record->region, (char *)record_buf, str_length + 1); + } + record_buf += str_length + 1; + str_length = 0; + + /* get city */ + while (record_buf[str_length] != '\0') { + str_length++; + } + if (str_length > 0) { + if (gi->charset == GEOIP_CHARSET_UTF8) { + record->city = _GeoIP_iso_8859_1__utf8((const char *)record_buf); + } else { + record->city = malloc(str_length + 1); + strncpy(record->city, (const char *)record_buf, str_length + 1); + } + } + record_buf += (str_length + 1); + str_length = 0; + + /* get postal code */ + while (record_buf[str_length] != '\0') { + str_length++; + } + if (str_length > 0) { + record->postal_code = malloc(str_length + 1); + strncpy(record->postal_code, (char *)record_buf, str_length + 1); + } + record_buf += (str_length + 1); + + /* get latitude */ + for (j = 0; j < 3; ++j) { + latitude += (record_buf[j] << (j * 8)); + } + record->latitude = (float)(latitude / 10000 - 180); + record_buf += 3; + + /* get longitude */ + for (j = 0; j < 3; ++j) { + longitude += (record_buf[j] << (j * 8)); + } + record->longitude = (float)(longitude / 10000 - 180); + + /* + * get area code and metro code for post April 2002 databases and for US + * locations + */ + if (gi->databaseType == GEOIP_CITY_EDITION_REV1 || + gi->databaseType == GEOIP_CITY_EDITION_REV1_V6) { + if (!strcmp(record->country_code, "US")) { + record_buf += 3; + for (j = 0; j < 3; ++j) { + metroarea_combo += (record_buf[j] << (j * 8)); + } + record->metro_code = metroarea_combo / 1000; + record->area_code = metroarea_combo % 1000; + } + } + + if (begin_record_buf != NULL) { + free(begin_record_buf); + } + + /* Used for GeoIP_next_record */ + if (next_record_ptr != NULL) { + *next_record_ptr = seek_record + record_buf - begin_record_buf + 3; + } + + return record; +} + +static GeoIPRecord * +_get_record_gl(GeoIP *gi, unsigned long ipnum, GeoIPLookup *gl) { + unsigned int seek_record; + GeoIPRecord *r; + if (gi->databaseType != GEOIP_CITY_EDITION_REV0 && + gi->databaseType != GEOIP_CITY_EDITION_REV1) { + printf("Invalid database type %s, expected %s\n", + GeoIPDBDescription[(int)gi->databaseType], + GeoIPDBDescription[GEOIP_CITY_EDITION_REV1]); + return NULL; + } + + seek_record = _GeoIP_seek_record_gl(gi, ipnum, gl); + r = _extract_record(gi, seek_record, NULL); + if (r) { + r->netmask = gl->netmask; + } + return r; +} + +static GeoIPRecord *_get_record(GeoIP *gi, unsigned long ipnum) { + GeoIPLookup gl; + return _get_record_gl(gi, ipnum, &gl); +} + +static GeoIPRecord * +_get_record_v6_gl(GeoIP *gi, geoipv6_t ipnum, GeoIPLookup *gl) { + GeoIPRecord *r; + unsigned int seek_record; + if (gi->databaseType != GEOIP_CITY_EDITION_REV0_V6 && + gi->databaseType != GEOIP_CITY_EDITION_REV1_V6) { + printf("Invalid database type %s, expected %s\n", + GeoIPDBDescription[(int)gi->databaseType], + GeoIPDBDescription[GEOIP_CITY_EDITION_REV1_V6]); + return NULL; + } + + seek_record = _GeoIP_seek_record_v6_gl(gi, ipnum, gl); + r = _extract_record(gi, seek_record, NULL); + if (r) { + r->netmask = gl->netmask; + } + return r; +} + +static GeoIPRecord *_get_record_v6(GeoIP *gi, geoipv6_t ipnum) { + GeoIPLookup gl; + return _get_record_v6_gl(gi, ipnum, &gl); +} + +GeoIPRecord *GeoIP_record_by_ipnum(GeoIP *gi, unsigned long ipnum) { + return _get_record(gi, ipnum); +} + +GeoIPRecord *GeoIP_record_by_ipnum_v6(GeoIP *gi, geoipv6_t ipnum) { + return _get_record_v6(gi, ipnum); +} + +GeoIPRecord *GeoIP_record_by_addr(GeoIP *gi, const char *addr) { + unsigned long ipnum; + GeoIPLookup gl; + if (addr == NULL) { + return 0; + } + ipnum = GeoIP_addr_to_num(addr); + return _get_record_gl(gi, ipnum, &gl); +} + +GeoIPRecord *GeoIP_record_by_addr_v6(GeoIP *gi, const char *addr) { + geoipv6_t ipnum; + if (addr == NULL) { + return 0; + } + ipnum = _GeoIP_addr_to_num_v6(addr); + return _get_record_v6(gi, ipnum); +} + +GeoIPRecord *GeoIP_record_by_name(GeoIP *gi, const char *name) { + unsigned long ipnum; + if (name == NULL) { + return 0; + } + ipnum = _GeoIP_lookupaddress(name); + return _get_record(gi, ipnum); +} + +GeoIPRecord *GeoIP_record_by_name_v6(GeoIP *gi, const char *name) { + geoipv6_t ipnum; + if (name == NULL) { + return 0; + } + ipnum = _GeoIP_lookupaddress_v6(name); + return _get_record_v6(gi, ipnum); +} + +int GeoIP_record_id_by_addr(GeoIP *gi, const char *addr) { + unsigned long ipnum; + if (gi->databaseType != GEOIP_CITY_EDITION_REV0 && + gi->databaseType != GEOIP_CITY_EDITION_REV1) { + printf("Invalid database type %s, expected %s\n", + GeoIPDBDescription[(int)gi->databaseType], + GeoIPDBDescription[GEOIP_CITY_EDITION_REV1]); + return 0; + } + if (addr == NULL) { + return 0; + } + ipnum = GeoIP_addr_to_num(addr); + return _GeoIP_seek_record(gi, ipnum); +} + +int GeoIP_record_id_by_addr_v6(GeoIP *gi, const char *addr) { + geoipv6_t ipnum; + if (gi->databaseType != GEOIP_CITY_EDITION_REV0_V6 && + gi->databaseType != GEOIP_CITY_EDITION_REV1_V6) { + printf("Invalid database type %s, expected %s\n", + GeoIPDBDescription[(int)gi->databaseType], + GeoIPDBDescription[GEOIP_CITY_EDITION_REV1]); + return 0; + } + if (addr == NULL) { + return 0; + } + ipnum = _GeoIP_addr_to_num_v6(addr); + return _GeoIP_seek_record_v6(gi, ipnum); +} + +int GeoIP_init_record_iter(GeoIP *gi) { return gi->databaseSegments[0] + 1; } + +int GeoIP_next_record(GeoIP *gi, GeoIPRecord **gir, int *record_iter) { + if (gi->cache != NULL) { + printf("GeoIP_next_record not supported in memory cache mode\n"); + return 1; + } + *gir = _extract_record(gi, *record_iter, record_iter); + return 0; +} + +void GeoIPRecord_delete(GeoIPRecord *gir) { + if (gir == NULL) { + return; + } + free(gir->region); + free(gir->city); + free(gir->postal_code); + free(gir); +} |