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