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