1 /*
2 OpenLieroX
3
4 reader for IpToCountry database
5
6 code under LGPL
7 by Albert Zeyer and Dark Charlie
8 */
9
10
11 #include "GeoIPDatabase.h"
12 #include "FindFile.h"
13
14 //
15 // Defines
16 //
17 typedef enum {
18 GEOIP_COUNTRY_EDITION = 1,
19 GEOIP_REGION_EDITION_REV0 = 7,
20 GEOIP_CITY_EDITION_REV0 = 6,
21 GEOIP_ORG_EDITION = 5,
22 GEOIP_ISP_EDITION = 4,
23 GEOIP_CITY_EDITION_REV1 = 2,
24 GEOIP_REGION_EDITION_REV1 = 3,
25 GEOIP_PROXY_EDITION = 8,
26 GEOIP_ASNUM_EDITION = 9,
27 GEOIP_NETSPEED_EDITION = 10,
28 GEOIP_DOMAIN_EDITION = 11,
29 GEOIP_COUNTRY_EDITION_V6 = 12,
30 } GeoIPDBTypes;
31
32 #define SEGMENT_RECORD_LENGTH 3
33 #define STANDARD_RECORD_LENGTH 3
34 #define ORG_RECORD_LENGTH 4
35 #define MAX_RECORD_LENGTH 4
36 #define NUM_DB_TYPES 20
37 #define FULL_RECORD_LENGTH 50
38
39 #define COUNTRY_BEGIN 16776960
40 #define STATE_BEGIN_REV0 16700000
41 #define STATE_BEGIN_REV1 16000000
42 #define STRUCTURE_INFO_MAX_SIZE 20
43 #define DATABASE_INFO_MAX_SIZE 100
44 #define MAX_ORG_RECORD_LENGTH 300
45 #define US_OFFSET 1
46 #define CANADA_OFFSET 677
47 #define WORLD_OFFSET 1353
48 #define FIPS_RANGE 360
49
50
51 //
52 // Country codes and names
53 //
54
55 const char GeoIP_country_code[253][3] = { "--","AP","EU","AD","AE","AF","AG","AI","AL","AM","AN",
56 "AO","AQ","AR","AS","AT","AU","AW","AZ","BA","BB",
57 "BD","BE","BF","BG","BH","BI","BJ","BM","BN","BO",
58 "BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD",
59 "CF","CG","CH","CI","CK","CL","CM","CN","CO","CR",
60 "CU","CV","CX","CY","CZ","DE","DJ","DK","DM","DO",
61 "DZ","EC","EE","EG","EH","ER","ES","ET","FI","FJ",
62 "FK","FM","FO","FR","FX","GA","GB","GD","GE","GF",
63 "GH","GI","GL","GM","GN","GP","GQ","GR","GS","GT",
64 "GU","GW","GY","HK","HM","HN","HR","HT","HU","ID",
65 "IE","IL","IN","IO","IQ","IR","IS","IT","JM","JO",
66 "JP","KE","KG","KH","KI","KM","KN","KP","KR","KW",
67 "KY","KZ","LA","LB","LC","LI","LK","LR","LS","LT",
68 "LU","LV","LY","MA","MC","MD","MG","MH","MK","ML",
69 "MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV",
70 "MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI",
71 "NL","NO","NP","NR","NU","NZ","OM","PA","PE","PF",
72 "PG","PH","PK","PL","PM","PN","PR","PS","PT","PW",
73 "PY","QA","RE","RO","RU","RW","SA","SB","SC","SD",
74 "SE","SG","SH","SI","SJ","SK","SL","SM","SN","SO",
75 "SR","ST","SV","SY","SZ","TC","TD","TF","TG","TH",
76 "TJ","TK","TM","TN","TO","TL","TR","TT","TV","TW",
77 "TZ","UA","UG","UM","US","UY","UZ","VA","VC","VE",
78 "VG","VI","VN","VU","WF","WS","YE","YT","RS","ZA",
79 "ZM","ME","ZW","A1","A2","O1","AX","GG","IM","JE",
80 "BL","MF"};
81
82 static const unsigned GeoIP_country_count = (unsigned)(sizeof(GeoIP_country_code)/sizeof(GeoIP_country_code[0]));
83
84 const char GeoIP_country_code3[253][4] = { "--","AP","EU","AND","ARE","AFG","ATG","AIA","ALB","ARM","ANT",
85 "AGO","AQ","ARG","ASM","AUT","AUS","ABW","AZE","BIH","BRB",
86 "BGD","BEL","BFA","BGR","BHR","BDI","BEN","BMU","BRN","BOL",
87 "BRA","BHS","BTN","BV","BWA","BLR","BLZ","CAN","CC","COD",
88 "CAF","COG","CHE","CIV","COK","CHL","CMR","CHN","COL","CRI",
89 "CUB","CPV","CX","CYP","CZE","DEU","DJI","DNK","DMA","DOM",
90 "DZA","ECU","EST","EGY","ESH","ERI","ESP","ETH","FIN","FJI",
91 "FLK","FSM","FRO","FRA","FX","GAB","GBR","GRD","GEO","GUF",
92 "GHA","GIB","GRL","GMB","GIN","GLP","GNQ","GRC","GS","GTM",
93 "GUM","GNB","GUY","HKG","HM","HND","HRV","HTI","HUN","IDN",
94 "IRL","ISR","IND","IO","IRQ","IRN","ISL","ITA","JAM","JOR",
95 "JPN","KEN","KGZ","KHM","KIR","COM","KNA","PRK","KOR","KWT",
96 "CYM","KAZ","LAO","LBN","LCA","LIE","LKA","LBR","LSO","LTU",
97 "LUX","LVA","LBY","MAR","MCO","MDA","MDG","MHL","MKD","MLI",
98 "MMR","MNG","MAC","MNP","MTQ","MRT","MSR","MLT","MUS","MDV",
99 "MWI","MEX","MYS","MOZ","NAM","NCL","NER","NFK","NGA","NIC",
100 "NLD","NOR","NPL","NRU","NIU","NZL","OMN","PAN","PER","PYF",
101 "PNG","PHL","PAK","POL","SPM","PCN","PRI","PSE","PRT","PLW",
102 "PRY","QAT","REU","ROU","RUS","RWA","SAU","SLB","SYC","SDN",
103 "SWE","SGP","SHN","SVN","SJM","SVK","SLE","SMR","SEN","SOM",
104 "SUR","STP","SLV","SYR","SWZ","TCA","TCD","TF","TGO","THA",
105 "TJK","TKL","TKM","TUN","TON","TLS","TUR","TTO","TUV","TWN",
106 "TZA","UKR","UGA","UM","USA","URY","UZB","VAT","VCT","VEN",
107 "VGB","VIR","VNM","VUT","WLF","WSM","YEM","YT","SRB","ZAF",
108 "ZMB","MNE","ZWE","A1","A2","O1","ALA","GGY","IMN","JEY",
109 "BLM","MAF"};
110
111 const char * GeoIP_country_name[253] = {"N/A","Asia/Pacific Region","Europe","Andorra","United Arab Emirates","Afghanistan","Antigua and Barbuda","Anguilla","Albania","Armenia","Netherlands Antilles",
112 "Angola","Antarctica","Argentina","American Samoa","Austria","Australia","Aruba","Azerbaijan","Bosnia and Herzegovina","Barbados",
113 "Bangladesh","Belgium","Burkina Faso","Bulgaria","Bahrain","Burundi","Benin","Bermuda","Brunei Darussalam","Bolivia",
114 "Brazil","Bahamas","Bhutan","Bouvet Island","Botswana","Belarus","Belize","Canada","Cocos (Keeling) Islands","Congo, The Democratic Republic of the",
115 "Central African Republic","Congo","Switzerland","Cote D'Ivoire","Cook Islands","Chile","Cameroon","China","Colombia","Costa Rica",
116 "Cuba","Cape Verde","Christmas Island","Cyprus","Czech Republic","Germany","Djibouti","Denmark","Dominica","Dominican Republic",
117 "Algeria","Ecuador","Estonia","Egypt","Western Sahara","Eritrea","Spain","Ethiopia","Finland","Fiji",
118 "Falkland Islands (Malvinas)","Micronesia, Federated States of","Faroe Islands","France","France, Metropolitan","Gabon","United Kingdom","Grenada","Georgia","French Guiana",
119 "Ghana","Gibraltar","Greenland","Gambia","Guinea","Guadeloupe","Equatorial Guinea","Greece","South Georgia and the South Sandwich Islands","Guatemala",
120 "Guam","Guinea-Bissau","Guyana","Hong Kong","Heard Island and McDonald Islands","Honduras","Croatia","Haiti","Hungary","Indonesia",
121 "Ireland","Israel","India","British Indian Ocean Territory","Iraq","Iran, Islamic Republic of","Iceland","Italy","Jamaica","Jordan",
122 "Japan","Kenya","Kyrgyzstan","Cambodia","Kiribati","Comoros","Saint Kitts and Nevis","Korea, Democratic People's Republic of","Korea, Republic of","Kuwait",
123 "Cayman Islands","Kazakhstan","Lao People's Democratic Republic","Lebanon","Saint Lucia","Liechtenstein","Sri Lanka","Liberia","Lesotho","Lithuania",
124 "Luxembourg","Latvia","Libyan Arab Jamahiriya","Morocco","Monaco","Moldova, Republic of","Madagascar","Marshall Islands","Macedonia","Mali",
125 "Myanmar","Mongolia","Macau","Northern Mariana Islands","Martinique","Mauritania","Montserrat","Malta","Mauritius","Maldives",
126 "Malawi","Mexico","Malaysia","Mozambique","Namibia","New Caledonia","Niger","Norfolk Island","Nigeria","Nicaragua",
127 "Netherlands","Norway","Nepal","Nauru","Niue","New Zealand","Oman","Panama","Peru","French Polynesia",
128 "Papua New Guinea","Philippines","Pakistan","Poland","Saint Pierre and Miquelon","Pitcairn Islands","Puerto Rico","Palestinian Territory","Portugal","Palau",
129 "Paraguay","Qatar","Reunion","Romania","Russian Federation","Rwanda","Saudi Arabia","Solomon Islands","Seychelles","Sudan",
130 "Sweden","Singapore","Saint Helena","Slovenia","Svalbard and Jan Mayen","Slovakia","Sierra Leone","San Marino","Senegal","Somalia","Suriname",
131 "Sao Tome and Principe","El Salvador","Syrian Arab Republic","Swaziland","Turks and Caicos Islands","Chad","French Southern Territories","Togo","Thailand",
132 "Tajikistan","Tokelau","Turkmenistan","Tunisia","Tonga","Timor-Leste","Turkey","Trinidad and Tobago","Tuvalu","Taiwan",
133 "Tanzania, United Republic of","Ukraine","Uganda","United States Minor Outlying Islands","United States","Uruguay","Uzbekistan","Holy See (Vatican City State)","Saint Vincent and the Grenadines","Venezuela",
134 "Virgin Islands, British","Virgin Islands, U.S.","Vietnam","Vanuatu","Wallis and Futuna","Samoa","Yemen","Mayotte","Serbia","South Africa",
135 "Zambia","Montenegro","Zimbabwe","Anonymous Proxy","Satellite Provider","Other","Aland Islands","Guernsey","Isle of Man","Jersey",
136 "Saint Barthelemy","Saint Martin"};
137
138 /* Possible continent codes are AF, AS, EU, NA, OC, SA for Africa, Asia, Europe, North America, Oceania
139 and South America. */
140
141 const char GeoIP_country_continent[253][3] = {"--","AS","EU","EU","AS","AS","SA","SA","EU","AS","SA",
142 "AF","AN","SA","OC","EU","OC","SA","AS","EU","SA",
143 "AS","EU","AF","EU","AS","AF","AF","SA","AS","SA",
144 "SA","SA","AS","AF","AF","EU","SA","NA","AS","AF",
145 "AF","AF","EU","AF","OC","SA","AF","AS","SA","SA",
146 "SA","AF","AS","AS","EU","EU","AF","EU","SA","SA",
147 "AF","SA","EU","AF","AF","AF","EU","AF","EU","OC",
148 "SA","OC","EU","EU","EU","AF","EU","SA","AS","SA",
149 "AF","EU","SA","AF","AF","SA","AF","EU","SA","SA",
150 "OC","AF","SA","AS","AF","SA","EU","SA","EU","AS",
151 "EU","AS","AS","AS","AS","AS","EU","EU","SA","AS",
152 "AS","AF","AS","AS","OC","AF","SA","AS","AS","AS",
153 "SA","AS","AS","AS","SA","EU","AS","AF","AF","EU",
154 "EU","EU","AF","AF","EU","EU","AF","OC","EU","AF",
155 "AS","AS","AS","OC","SA","AF","SA","EU","AF","AS",
156 "AF","NA","AS","AF","AF","OC","AF","OC","AF","SA",
157 "EU","EU","AS","OC","OC","OC","AS","SA","SA","OC",
158 "OC","AS","AS","EU","SA","OC","SA","AS","EU","OC",
159 "SA","AS","AF","EU","AS","AF","AS","OC","AF","AF",
160 "EU","AS","AF","EU","EU","EU","AF","EU","AF","AF",
161 "SA","AF","SA","AS","AF","SA","AF","AF","AF","AS",
162 "AS","OC","AS","AF","OC","AS","AS","SA","OC","AS",
163 "AF","EU","AF","OC","NA","SA","AS","EU","SA","SA",
164 "SA","SA","AS","OC","OC","OC","AS","AF","EU","AF",
165 "AF","EU","AF","--","--","--","EU","EU","EU","EU",
166 "SA","SA"};
167
168 //
169 // GeoRecord operations
170 //
171
operator =(const GeoRecord & oth)172 GeoRecord& GeoRecord::operator= (const GeoRecord& oth)
173 {
174 if (this != &oth) {
175 continentCode = oth.continentCode;
176 continent = oth.continent;
177 countryCode = oth.countryCode;
178 countryCode3 = oth.countryCode3;
179 countryName = oth.countryName;
180 hasCityLevel = oth.hasCityLevel;
181 region = oth.region;
182 city = oth.city;
183 postalCode = oth.postalCode;
184 latitude = oth.latitude;
185 longitude = oth.longitude;
186 dmaCode = oth.dmaCode;
187 areaCode = oth.areaCode;
188 }
189
190 return *this;
191 }
192
193 //
194 // GeoIP Database class methods
195 //
196
197
198 /////////////////
199 // Destructor
~GeoIPDatabase()200 GeoIPDatabase::~GeoIPDatabase()
201 {
202 if (m_file) {
203 fclose(m_file);
204 }
205 if (m_dbSegments) {
206 delete[] m_dbSegments;
207 }
208
209 m_file = NULL;
210 }
211
212 ////////////////
213 // Loads the database, returns false on failure
load(const std::string & filename)214 bool GeoIPDatabase::load(const std::string& filename)
215 {
216 m_file = OpenGameFile(filename, "rb");
217 if (!m_file)
218 return false;
219
220 m_fileName = filename;
221 if (!setupSegments()) {
222 fclose(m_file);
223 return false;
224 }
225
226 return true;
227 }
228
229 /////////////////
230 // Reads database segments (private)
231 // Requires the database file to be open
232 // Returns true on success, false otherwise
setupSegments()233 bool GeoIPDatabase::setupSegments()
234 {
235 // Cleanup
236 if (m_dbSegments)
237 delete[] m_dbSegments;
238 m_dbSegments = NULL;
239
240 // Default to GeoIP Country Edition
241 m_dbType = GEOIP_COUNTRY_EDITION;
242 m_recordLength = STANDARD_RECORD_LENGTH;
243 fseek(m_file, -3l, SEEK_END);
244 for (int i = 0; i < STRUCTURE_INFO_MAX_SIZE; i++) {
245 unsigned char delim[3]; // Record delimiter
246 if (fread(delim, 1, 3, m_file) != 3)
247 errors << "Error reading from GeoIP database" << endl;
248 if (delim[0] == 255 && delim[1] == 255 && delim[2] == 255) {
249 if (fread(&m_dbType, 1, 1, m_file) != 1)
250 errors << "Error reading from GeoIP database" << endl;
251
252 // Backwards compatibility with databases from April 2003 and earlier
253 if (m_dbType >= 106)
254 m_dbType -= 105;
255
256 if (m_dbType == GEOIP_REGION_EDITION_REV0) {
257 // Region Edition, pre June 2003
258 m_dbSegments = new unsigned int[1];
259 m_dbSegments[0] = STATE_BEGIN_REV0;
260 } else if (m_dbType == GEOIP_REGION_EDITION_REV1) {
261 // Region Edition, post June 2003
262 m_dbSegments = new unsigned int[1];
263 m_dbSegments[0] = STATE_BEGIN_REV1;
264 } else if (m_dbType == GEOIP_CITY_EDITION_REV0 ||
265 m_dbType == GEOIP_CITY_EDITION_REV1 ||
266 m_dbType == GEOIP_ORG_EDITION ||
267 m_dbType == GEOIP_ISP_EDITION ||
268 m_dbType == GEOIP_ASNUM_EDITION) {
269
270 // City/Org Editions have two segments, read offset of second segment
271 m_dbSegments = new unsigned int[1];
272 m_dbSegments[0] = 0;
273
274 unsigned char buf[SEGMENT_RECORD_LENGTH];
275 if (fread(buf, SEGMENT_RECORD_LENGTH, 1, m_file) != 1)
276 errors << "Error reading from GeoIP database" << endl;
277 for (int j = 0; j < SEGMENT_RECORD_LENGTH; j++)
278 m_dbSegments[0] += (buf[j] << (j * 8));
279
280 if (m_dbType == GEOIP_ORG_EDITION || m_dbType == GEOIP_ISP_EDITION)
281 m_recordLength = ORG_RECORD_LENGTH;
282 }
283 break;
284 } else {
285 fseek(m_file, -4l, SEEK_CUR);
286 }
287 }
288
289 if (m_dbType == GEOIP_COUNTRY_EDITION ||
290 m_dbType == GEOIP_PROXY_EDITION ||
291 m_dbType == GEOIP_NETSPEED_EDITION ||
292 m_dbType == GEOIP_COUNTRY_EDITION_V6 ) {
293 m_dbSegments = new unsigned int[1];
294 m_dbSegments[0] = COUNTRY_BEGIN;
295 }
296
297 return true;
298 }
299
300 ////////////////
301 // Seek a record in the database, returns record index or 0 on failure
seekRecord(unsigned long ipnum) const302 unsigned int GeoIPDatabase::seekRecord(unsigned long ipnum) const
303 {
304 if (!m_file)
305 return 0;
306
307 unsigned int x;
308 unsigned char stack_buffer[2 * MAX_RECORD_LENGTH];
309 const unsigned char *buf = stack_buffer;
310 unsigned int offset = 0;
311
312 const unsigned char * p;
313
314 for (int depth = 31; depth >= 0; depth--) {
315 // Read from disk
316 fseek(m_file, (long)m_recordLength * 2 * offset, SEEK_SET);
317 if (fread(stack_buffer, m_recordLength, 2, m_file) != 2)
318 errors << "Error reading from GeoIP database" << endl;
319
320 if (ipnum & (1 << depth)) {
321 // Take the right-hand branch
322 if (m_recordLength == 3) {
323 // Most common case is completely unrolled and uses constants
324 x = (buf[3*1 + 0] << (0*8))
325 + (buf[3*1 + 1] << (1*8))
326 + (buf[3*1 + 2] << (2*8));
327
328 } else {
329 // General case
330 int j = m_recordLength;
331 p = &buf[2*j];
332 x = 0;
333 do {
334 x <<= 8;
335 x += *(--p);
336 } while (--j);
337 }
338
339 } else {
340 // Take the left-hand branch
341 if (m_recordLength == 3) {
342 // Most common case is completely unrolled and uses constants
343 x = (buf[3*0 + 0] << (0*8))
344 + (buf[3*0 + 1] << (1*8))
345 + (buf[3*0 + 2] << (2*8));
346 } else {
347 // General case
348 int j = m_recordLength;
349 p = &buf[1*j];
350 x = 0;
351 do {
352 x <<= 8;
353 x += *(--p);
354 } while (--j);
355 }
356 }
357
358 if (x >= m_dbSegments[0])
359 return x;
360
361 offset = x;
362 }
363
364 // Shouldn't reach here
365 errors << "Error Traversing Database for ipnum = %lu - Perhaps database is corrupt?" << endl;
366 return 0;
367 }
368
369 //////////////////
370 // Extracts a record information from a city-level database
extractRecordCity(unsigned int seekRecord) const371 GeoRecord GeoIPDatabase::extractRecordCity(unsigned int seekRecord) const
372 {
373 GeoRecord record;
374
375 int record_pointer;
376 unsigned char *record_buf = NULL;
377 unsigned char *begin_record_buf = NULL;
378 double latitude = 0, longitude = 0;
379 int metroarea_combo = 0;
380 int bytes_read = 0;
381 if (seekRecord == m_dbSegments[0])
382 return record;
383
384 record_pointer = seekRecord + (2 * m_recordLength - 1) * m_dbSegments[0];
385
386 // Seek to the record
387 fseek(m_file, record_pointer, SEEK_SET);
388 begin_record_buf = record_buf = new unsigned char[FULL_RECORD_LENGTH];
389 bytes_read = fread(record_buf, sizeof(char), FULL_RECORD_LENGTH, m_file);
390 if (bytes_read == 0) {
391 // Eof or other error
392 delete[] begin_record_buf;
393 return record;
394 }
395
396 // Get country
397 record.continentCode = GeoIP_country_continent[record_buf[0]];
398 record.countryCode = GeoIP_country_code[record_buf[0]];
399 record.countryCode3 = GeoIP_country_code3[record_buf[0]];
400 record.countryName = GeoIP_country_name[record_buf[0]];
401 record_buf++;
402
403 // Get region
404 record.region = (char *)record_buf;
405 record_buf += record.region.size() + 1;
406 record.region = ISO88591ToUtf8(record.region);
407
408 // Get city
409 record.city = (char *)record_buf;
410 record_buf += record.city.size() + 1;
411 record.city = ISO88591ToUtf8(record.city);
412
413
414 // Get postal code
415 record.postalCode = (char *)record_buf;
416 record_buf += record.postalCode.size() + 1;
417
418 // Get latitude
419 for (int j = 0; j < 3; ++j)
420 latitude += (record_buf[j] << (j * 8));
421 record.latitude = latitude/10000 - 180;
422 record_buf += 3;
423
424 // Get longitude
425 for (int j = 0; j < 3; ++j)
426 longitude += (record_buf[j] << (j * 8));
427 record.longitude = longitude/10000 - 180;
428
429 // Get area code and metro code for post April 2002 databases and for US locations
430 if (GEOIP_CITY_EDITION_REV1 == m_dbType) {
431 if (record.countryCode == "US") {
432 record_buf += 3;
433 for (int j = 0; j < 3; ++j)
434 metroarea_combo += (record_buf[j] << (j * 8));
435 record.metroCode = metroarea_combo/1000;
436 record.areaCode = metroarea_combo % 1000;
437 }
438 }
439
440 delete[] begin_record_buf;
441
442 record.hasCityLevel = true;
443 fillContinent(record);
444
445 return record;
446 }
447
448 ///////////////////
449 // Converts continent codes to full continent name
fillContinent(GeoRecord & res) const450 void GeoIPDatabase::fillContinent(GeoRecord& res) const
451 {
452 // Fill in continent based on continent shortcut
453 if (res.continentCode == "EU")
454 res.continent = "Europe";
455 else if (res.continentCode == "NA")
456 res.continent = "North America";
457 else if (res.continentCode == "SA")
458 res.continent = "South America";
459 else if (res.continentCode == "AS")
460 res.continent = "Asia";
461 else if (res.continentCode == "OC")
462 res.continent = "Australia and Oceania";
463 else if (res.continentCode == "AN")
464 res.continent = "Antarctica";
465 else if (res.continentCode == "AF")
466 res.continent = "Africa";
467 else
468 res.continent = "Unknown";
469 }
470
471 ///////////////
472 // Reads a records from a country-only database
extractRecordCtry(unsigned int seekRecord) const473 GeoRecord GeoIPDatabase::extractRecordCtry(unsigned int seekRecord) const
474 {
475 GeoRecord res;
476
477 int ctry = seekRecord - COUNTRY_BEGIN;
478
479 // seekRecord contains coutry ID in this case
480 // But to be sure we check for the range
481 if (ctry >= (int)GeoIP_country_count || ctry < 0)
482 return res;
483
484 // Fill in the info
485 res.continentCode = GeoIP_country_continent[ctry];
486 res.countryCode = GeoIP_country_code[ctry];
487 res.countryCode3 = GeoIP_country_code3[ctry];
488 res.countryName = GeoIP_country_name[ctry];
489 res.hasCityLevel = false;
490
491 fillContinent(res);
492
493 return res;
494 }
495
496 //////////////////
497 // Converts a string IP to MaxMind's representation
convertIp(const std::string & strAddr) const498 unsigned long GeoIPDatabase::convertIp(const std::string& strAddr) const
499 {
500 unsigned int octet;
501 unsigned long ipnum;
502 int i = 3;
503
504 octet = ipnum = 0;
505 for (std::string::const_iterator it = strAddr.begin(); it != strAddr.end(); it++) {
506 if (*it == '.') {
507 if (octet > 255)
508 return 0;
509 ipnum <<= 8;
510 ipnum += octet;
511 i--;
512 octet = 0;
513 } else if (*it == ':') {
514 break;
515 } else {
516 int t = octet;
517 octet <<= 3;
518 octet += t;
519 octet += t;
520 int c = (int)(unsigned char)(*it);
521 c -= '0';
522 if (c > 9 || c < 0)
523 return 0;
524 octet += c;
525 }
526 }
527 if ((octet > 255) || (i != 0))
528 return 0;
529 ipnum <<= 8;
530 return ipnum + octet;
531 }
532
533 /////////////////
534 // Performs a search for the given IP, returns a record with information about the IP
lookup(const std::string & ip) const535 GeoRecord GeoIPDatabase::lookup(const std::string& ip) const
536 {
537 GeoRecord res;
538
539 if (!m_file)
540 return res;
541
542 // IP check
543 unsigned long l_ip = convertIp(ip);
544 if (!l_ip) {
545 res.continentCode = "UN";
546 res.countryCode = "HCK";
547 res.countryCode3 = "HCK";
548 res.countryName = "Hackerland";
549 res.region = "Pirate area";
550 res.city = "No City";
551 return res;
552 }
553
554 // Find the record
555 int record = seekRecord(l_ip);
556
557 // Get information
558 if (m_dbType == GEOIP_CITY_EDITION_REV0 || m_dbType == GEOIP_CITY_EDITION_REV1)
559 return extractRecordCity(record);
560 else if (m_dbType == GEOIP_COUNTRY_EDITION)
561 return extractRecordCtry(record);
562 else {
563 errors << "The Geo IP database has an unsupported format" << endl;
564 return res;
565 }
566 }
567