1
2/*
3 * GeoIPCity.c
4 *
5 * Copyright (C) 2006 MaxMind LLC
6 *
7 * This library is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Lesser General Public License as published by the
9 * Free Software Foundation; either version 2.1 of the License, or (at your
10 * option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
15 * License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22#include "GeoIP.h"
23#include "GeoIP_internal.h"
24#include "GeoIPCity.h"
25#if !defined(_WIN32)
26#include <unistd.h>
27#include <netdb.h>
28#include <netinet/in.h>	/* For ntohl */
29#else
30#include <windows.h>
31#include <winsock.h>
32#endif
33#include <sys/types.h>	/* For uint32_t */
34#ifdef HAVE_STDINT_H
35#include <stdint.h>	/* For uint32_t */
36#endif
37
38#ifndef HAVE_PREAD
39#define pread(fd, buf, count, offset) \
40        ( \
41        lseek(fd, offset, SEEK_SET) == offset ? \
42                read(fd, buf, count) : \
43                -1 \
44                )
45#endif /* HAVE_PREAD */
46
47
48static
49const int       FULL_RECORD_LENGTH = 50;
50
51
52static
53GeoIPRecord    *
54_extract_record(GeoIP * gi, unsigned int seek_record, int *next_record_ptr)
55{
56  int             record_pointer;
57  unsigned char  *record_buf = NULL;
58  unsigned char  *begin_record_buf = NULL;
59  GeoIPRecord    *record;
60  int             str_length = 0;
61  int             j;
62  double          latitude = 0, longitude = 0;
63  int             metroarea_combo = 0;
64  int             bytes_read = 0;
65  if (seek_record == gi->databaseSegments[0])
66    return NULL;
67
68  record = malloc(sizeof(GeoIPRecord));
69  memset(record, 0, sizeof(GeoIPRecord));
70  record->charset = gi->charset;
71
72    record_pointer = seek_record + (2 * gi->record_length - 1) * gi->databaseSegments[0];
73
74    if (gi->cache == NULL) {
75      begin_record_buf = record_buf = malloc(sizeof(char) * FULL_RECORD_LENGTH);
76        bytes_read = pread(fileno(gi->GeoIPDatabase), record_buf, FULL_RECORD_LENGTH, record_pointer);
77      if (bytes_read == 0) {
78        /* eof or other error */
79	free(begin_record_buf);
80        free(record);
81	return NULL;
82      }
83    }
84    else {
85      record_buf = gi->cache + (long) record_pointer;
86    }
87
88  /* get country */
89  record->continent_code = (char *) GeoIP_country_continent[record_buf[0]];
90  record->country_code = (char *) GeoIP_country_code[record_buf[0]];
91  record->country_code3 = (char *) GeoIP_country_code3[record_buf[0]];
92  record->country_name = (char *) GeoIP_country_name_by_id(gi, record_buf[0]);
93  record_buf++;
94
95  /* get region */
96  while (record_buf[str_length] != '\0')
97    str_length++;
98  if (str_length > 0) {
99    record->region = malloc(str_length + 1);
100    strncpy(record->region, (char *) record_buf, str_length + 1);
101  }
102  record_buf += str_length + 1;
103  str_length = 0;
104
105  /* get city */
106  while (record_buf[str_length] != '\0')
107    str_length++;
108  if (str_length > 0) {
109    if (gi->charset == GEOIP_CHARSET_UTF8) {
110      record->city = _GeoIP_iso_8859_1__utf8((const char *) record_buf);
111    }
112    else {
113      record->city = malloc(str_length + 1);
114      strncpy(record->city, (const char *) record_buf, str_length + 1);
115    }
116  }
117  record_buf += (str_length + 1);
118  str_length = 0;
119
120  /* get postal code */
121  while (record_buf[str_length] != '\0')
122    str_length++;
123  if (str_length > 0) {
124    record->postal_code = malloc(str_length + 1);
125    strncpy(record->postal_code, (char *) record_buf, str_length + 1);
126  }
127  record_buf += (str_length + 1);
128
129  /* get latitude */
130  for (j = 0; j < 3; ++j)
131    latitude += (record_buf[j] << (j * 8));
132  record->latitude = latitude / 10000 - 180;
133  record_buf += 3;
134
135  /* get longitude */
136  for (j = 0; j < 3; ++j)
137    longitude += (record_buf[j] << (j * 8));
138  record->longitude = longitude / 10000 - 180;
139
140  /*
141   * get area code and metro code for post April 2002 databases and for US
142   * locations
143   */
144  if (GEOIP_CITY_EDITION_REV1 == gi->databaseType) {
145    if (!strcmp(record->country_code, "US")) {
146      record_buf += 3;
147      for (j = 0; j < 3; ++j)
148	metroarea_combo += (record_buf[j] << (j * 8));
149      record->metro_code = metroarea_combo / 1000;
150      record->area_code = metroarea_combo % 1000;
151    }
152  }
153
154  if (gi->cache == NULL)
155    free(begin_record_buf);
156
157  /* Used for GeoIP_next_record */
158  if (next_record_ptr != NULL)
159    *next_record_ptr = seek_record + record_buf - begin_record_buf + 3;
160
161  return record;
162}
163
164static
165GeoIPRecord    *
166_get_record_gl(GeoIP * gi, unsigned long ipnum, GeoIPLookup * gl)
167{
168  unsigned int    seek_record;
169  GeoIPRecord * r;
170  if (gi->databaseType != GEOIP_CITY_EDITION_REV0
171      && gi->databaseType != GEOIP_CITY_EDITION_REV1) {
172    printf("Invalid database type %s, expected %s\n", GeoIPDBDescription[(int) gi->databaseType], GeoIPDBDescription[GEOIP_CITY_EDITION_REV1]);
173    return NULL;
174  }
175
176  seek_record = _GeoIP_seek_record_gl(gi, ipnum, gl);
177  r = _extract_record(gi, seek_record, NULL);
178  if ( r )
179    r->netmask = gl->netmask;
180  return r;
181}
182
183static
184GeoIPRecord    *
185_get_record(GeoIP * gi, unsigned long ipnum)
186{
187  GeoIPLookup gl;
188  return _get_record_gl(gi, ipnum, &gl);
189}
190
191static
192GeoIPRecord    *
193_get_record_v6_gl(GeoIP * gi, geoipv6_t ipnum, GeoIPLookup * gl)
194{
195  GeoIPRecord * r;
196  unsigned int    seek_record;
197  if (gi->databaseType != GEOIP_CITY_EDITION_REV0_V6 &&
198      gi->databaseType != GEOIP_CITY_EDITION_REV1_V6) {
199    printf("Invalid database type %s, expected %s\n", GeoIPDBDescription[(int) gi->databaseType], GeoIPDBDescription[GEOIP_CITY_EDITION_REV1_V6]);
200    return NULL;
201  }
202
203  seek_record = _GeoIP_seek_record_v6_gl(gi, ipnum, gl);
204  r = _extract_record(gi, seek_record, NULL);
205  if ( r )
206    r->netmask = gl->netmask;
207  return r;
208}
209
210static
211GeoIPRecord    *
212_get_record_v6(GeoIP * gi, geoipv6_t ipnum)
213{
214  GeoIPLookup gl;
215  return _get_record_v6_gl(gi, ipnum, &gl);
216}
217
218GeoIPRecord    *
219GeoIP_record_by_ipnum(GeoIP * gi, unsigned long ipnum)
220{
221  return _get_record(gi, ipnum);
222}
223
224GeoIPRecord    *
225GeoIP_record_by_ipnum_v6(GeoIP * gi, geoipv6_t ipnum)
226{
227  return _get_record_v6(gi, ipnum);
228}
229
230GeoIPRecord    *
231GeoIP_record_by_addr(GeoIP * gi, const char *addr)
232{
233  unsigned long   ipnum;
234  GeoIPLookup gl;
235  if (addr == NULL) {
236    return 0;
237  }
238  ipnum = GeoIP_addr_to_num(addr);
239  return _get_record_gl(gi, ipnum, &gl);
240}
241
242GeoIPRecord    *
243GeoIP_record_by_addr_v6(GeoIP * gi, const char *addr)
244{
245  geoipv6_t       ipnum;
246  if (addr == NULL) {
247    return 0;
248  }
249  ipnum = _GeoIP_addr_to_num_v6(addr);
250  return _get_record_v6(gi, ipnum);
251}
252
253GeoIPRecord    *
254GeoIP_record_by_name(GeoIP * gi, const char *name)
255{
256  unsigned long   ipnum;
257  if (name == NULL) {
258    return 0;
259  }
260  ipnum = _GeoIP_lookupaddress(name);
261  return _get_record(gi, ipnum);
262}
263
264GeoIPRecord    *
265GeoIP_record_by_name_v6(GeoIP * gi, const char *name)
266{
267  geoipv6_t       ipnum;
268  if (name == NULL) {
269    return 0;
270  }
271  ipnum = _GeoIP_lookupaddress_v6(name);
272  return _get_record_v6(gi, ipnum);
273}
274
275int
276GeoIP_record_id_by_addr(GeoIP * gi, const char *addr)
277{
278  unsigned long   ipnum;
279  if (gi->databaseType != GEOIP_CITY_EDITION_REV0 &&
280      gi->databaseType != GEOIP_CITY_EDITION_REV1) {
281    printf("Invalid database type %s, expected %s\n", GeoIPDBDescription[(int) gi->databaseType], GeoIPDBDescription[GEOIP_CITY_EDITION_REV1]);
282    return 0;
283  }
284  if (addr == NULL) {
285    return 0;
286  }
287  ipnum = GeoIP_addr_to_num(addr);
288  return _GeoIP_seek_record(gi, ipnum);
289}
290
291int
292GeoIP_record_id_by_addr_v6(GeoIP * gi, const char *addr)
293{
294  geoipv6_t       ipnum;
295  if (gi->databaseType != GEOIP_CITY_EDITION_REV0_V6 &&
296      gi->databaseType != GEOIP_CITY_EDITION_REV1_V6) {
297    printf("Invalid database type %s, expected %s\n", GeoIPDBDescription[(int) gi->databaseType], GeoIPDBDescription[GEOIP_CITY_EDITION_REV1]);
298    return 0;
299  }
300  if (addr == NULL) {
301    return 0;
302  }
303  ipnum = _GeoIP_addr_to_num_v6(addr);
304  return _GeoIP_seek_record_v6(gi, ipnum);
305}
306
307int
308GeoIP_init_record_iter(GeoIP * gi)
309{
310  return gi->databaseSegments[0] + 1;
311}
312
313int
314GeoIP_next_record(GeoIP * gi, GeoIPRecord ** gir, int *record_iter)
315{
316  if (gi->cache != NULL) {
317    printf("GeoIP_next_record not supported in memory cache mode\n");
318    return 1;
319  }
320  *gir = _extract_record(gi, *record_iter, record_iter);
321  return 0;
322}
323
324void
325GeoIPRecord_delete(GeoIPRecord * gir)
326{
327  free(gir->region);
328  free(gir->city);
329  free(gir->postal_code);
330  free(gir);
331}
332