PageRenderTime 64ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/bin/src/artica-send2/geoip.pas

https://github.com/rsd/artica-1.5
Pascal | 571 lines | 486 code | 40 blank | 45 comment | 53 complexity | 8b6a3a087040706a9df3468cc437cacd MD5 | raw file
  1. {
  2. * Copyright (C) 2005 MaxMind LLC All Rights Reserved.
  3. *
  4. * This library is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public
  6. * License as published by the Free Software Foundation; either
  7. * version 2 of the License, or (at your option) any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public
  15. * License along with this library; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  17. * ChangeLog
  18. * 2003-04-09 Translation of C# class to Pascal provided by W. Tracz
  19. * 2005-07-20 Added support for GeoIP Region, City, ISP and Organization (Yvan Schwab/esoftys)
  20. }
  21. { Thanks to W. Tracz/Yvan Schwab for contributing this class }
  22. unit GeoIP;
  23. {$MODE DELPHI}
  24. //{$mode objfpc}{$H+}
  25. {$LONGSTRINGS ON}
  26. interface
  27. uses Classes, SysUtils,Libc;
  28. type
  29. TGeoIPResult = (
  30. GEOIP_SUCCESS = 0,
  31. GEOIP_NODATA = 1,
  32. GEOIP_ERROR_IPADDR = 2,
  33. GEOIP_ERROR_DBTYPE = 3,
  34. GEOIP_ERROR_IO = 4
  35. );
  36. TGeoIPDBTypes = (
  37. GEOIP_COUNTRY_EDITION = 1,
  38. GEOIP_REGION_EDITION_REV0 = 7,
  39. GEOIP_CITY_EDITION_REV0 = 6,
  40. GEOIP_ORG_EDITION = 5,
  41. GEOIP_ISP_EDITION = 4,
  42. GEOIP_CITY_EDITION_REV1 = 2,
  43. GEOIP_REGION_EDITION_REV1 = 3,
  44. GEOIP_PROXY_EDITION = 8,
  45. GEOIP_ASNUM_EDITION = 9
  46. );
  47. TGeoIPCountry = record
  48. CountryCode: string;
  49. CountryName: string;
  50. end;
  51. type TGeoIPRegion = record
  52. CountryCode: string;
  53. Region: string;
  54. end;
  55. type TGeoIPCity = record
  56. CountryCode: string;
  57. CountryName: string;
  58. Region: string;
  59. City: string;
  60. PostalCode: string;
  61. Latitude: Double;
  62. Longitude: Double;
  63. DmaCode: Integer;
  64. AreaCode: Integer;
  65. end;
  66. type TGeoIPOrg = record
  67. Name: string;
  68. end;
  69. type TGeoIP = class
  70. private
  71. FInputFile: TFileStream;
  72. FDatabaseType: TGeoIPDBTypes;
  73. FDatabaseSegments: array of Cardinal;
  74. FDatabaseInfo: string;
  75. FRecordLength: Cardinal;
  76. function _GetCity(IPNum: Cardinal; var GeoIPCity: TGeoIPCity): TGeoIPResult;
  77. function _GetCountry(IPNum: Cardinal; var GeoIPCountry: TGeoIPCountry): TGeoIPResult;
  78. function _GetOrg(IPNum: Cardinal; var GeoIPOrg: TGeoIPOrg): TGeoIPResult;
  79. function _GetRegion(IPNum: Cardinal; var GeoIPRegion: TGeoIPRegion): TGeoIPResult;
  80. function AddrToNum(const IPAddr: string): Cardinal;
  81. procedure InitDBFile;
  82. function SeekRecord(IPNum: Cardinal): Cardinal;
  83. public
  84. constructor Create(const FileName: string);
  85. destructor Destroy; override;
  86. function GetCity(const IPAddr: string; var GeoIPCity: TGeoIPCity): TGeoIPResult;
  87. function GetCountry(const IPAddr: string; var GeoIPCountry: TGeoIPCountry): TGeoIPResult;
  88. function GetDatabaseInfo: string;
  89. function GetOrg(const IPAddr: string; var GeoIPOrg: TGeoIPOrg): TGeoIPResult;
  90. function GetRegion(const IPAddr: string; var GeoIPRegion: TGeoIPRegion): TGeoIPResult;
  91. end;
  92. const
  93. CountryCodes:array [0..250] of string = ('--','AP','EU','AD','AE','AF','AG','AI','AL','AM','AN','AO','AQ','AR','AS','AT','AU','AW','AZ','BA','BB','BD','BE','BF','BG','BH','BI','BJ','BM','BN','BO','BR','BS','BT','BV','BW','BY','BZ','CA','CC','CD','CF','CG','CH','CI','CK','CL','CM','CN','CO','CR','CU','CV','CX','CY','CZ','DE','DJ','DK','DM','DO','DZ','EC','EE','EG','EH','ER','ES','ET','FI','FJ','FK','FM','FO','FR','FX','GA','GB','GD','GE','GF','GH','GI','GL','GM','GN','GP','GQ','GR','GS','GT','GU','GW',
  94. 'GY','HK','HM','HN','HR','HT','HU','ID','IE','IL','IN','IO','IQ','IR','IS','IT','JM','JO','JP','KE','KG','KH','KI','KM','KN','KP','KR','KW','KY','KZ','LA','LB','LC','LI','LK','LR','LS','LT','LU','LV','LY','MA','MC','MD','MG','MH','MK','ML','MM','MN','MO','MP','MQ','MR','MS','MT','MU','MV','MW','MX','MY','MZ','NA','NC','NE','NF','NG','NI','NL','NO','NP','NR','NU','NZ','OM','PA','PE','PF','PG','PH','PK','PL','PM','PN','PR','PS','PT','PW','PY','QA','RE','RO','RU',
  95. 'RW','SA','SB','SC','SD','SE','SG','SH','SI','SJ','SK','SL','SM','SN','SO','SR','ST','SV','SY','SZ','TC','TD','TF','TG','TH','TJ','TK','TM','TN','TO','TL','TR','TT','TV','TW','TZ','UA','UG','UM','US','UY','UZ','VA','VC','VE','VG','VI','VN','VU','WF','WS','YE','YT','RS','ZA','ZM','ME','ZW','A1','A2','O1','AX','GG','IM','JE');
  96. CountryNames:array [0..250] of string = ('N/A','Asia/Pacific Region','Europe','Andorra','United Arab Emirates','Afghanistan','Antigua and Barbuda','Anguilla','Albania','Armenia','Netherlands Antilles','Angola','Antarctica','Argentina','American Samoa','Austria','Australia','Aruba','Azerbaijan','Bosnia and Herzegovina','Barbados','Bangladesh','Belgium','Burkina Faso','Bulgaria','Bahrain','Burundi','Benin','Bermuda','Brunei Darussalam','Bolivia','Brazil','Bahamas','Bhutan','Bouvet Island','Botswana',
  97. 'Belarus','Belize','Canada','Cocos (Keeling) Islands','Congo, The Democratic Republic of the','Central African Republic','Congo','Switzerland','Cote D''Ivoire','Cook Islands','Chile','Cameroon','China','Colombia','Costa Rica','Cuba','Cape Verde','Christmas Island','Cyprus','Czech Republic','Germany','Djibouti','Denmark','Dominica','Dominican Republic','Algeria','Ecuador','Estonia','Egypt','Western Sahara','Eritrea','Spain','Ethiopia','Finland','Fiji',
  98. 'Falkland Islands (Malvinas)','Micronesia, Federated States of','Faroe Islands','France','France, Metropolitan','Gabon','United Kingdom','Grenada','Georgia','French Guiana','Ghana','Gibraltar','Greenland','Gambia','Guinea','Guadeloupe','Equatorial Guinea','Greece','South Georgia and the South Sandwich Islands','Guatemala','Guam','Guinea-Bissau','Guyana','Hong Kong','Heard Island and McDonald Islands','Honduras','Croatia','Haiti','Hungary','Indonesia','Ireland',
  99. 'Israel','India','British Indian Ocean Territory','Iraq','Iran, Islamic Republic of','Iceland','Italy','Jamaica','Jordan','Japan','Kenya','Kyrgyzstan','Cambodia','Kiribati','Comoros','Saint Kitts and Nevis','Korea, Democratic People''s Republic of','Korea, Republic of','Kuwait','Cayman Islands','Kazakstan','Lao People''s Democratic Republic','Lebanon','Saint Lucia','Liechtenstein','Sri Lanka','Liberia','Lesotho','Lithuania','Luxembourg','Latvia',
  100. 'Libyan Arab Jamahiriya','Morocco','Monaco','Moldova, Republic of','Madagascar','Marshall Islands','Macedonia, the Former Yugoslav Republic of','Mali','Myanmar','Mongolia','Macao','Northern Mariana Islands','Martinique','Mauritania','Montserrat','Malta','Mauritius','Maldives','Malawi','Mexico','Malaysia','Mozambique','Namibia','New Caledonia','Niger','Norfolk Island','Nigeria','Nicaragua','Netherlands','Norway','Nepal','Nauru','Niue','New Zealand','Oman',
  101. 'Panama','Peru','French Polynesia','Papua New Guinea','Philippines','Pakistan','Poland','Saint Pierre and Miquelon','Pitcairn','Puerto Rico','Palestinian Territory, Occupied','Portugal','Palau','Paraguay','Qatar','Reunion','Romania','Russian Federation','Rwanda','Saudi Arabia','Solomon Islands','Seychelles','Sudan','Sweden','Singapore','Saint Helena','Slovenia','Svalbard and Jan Mayen','Slovakia','Sierra Leone','San Marino','Senegal','Somalia','Suriname',
  102. 'Sao Tome and Principe','El Salvador','Syrian Arab Republic','Swaziland','Turks and Caicos Islands','Chad','French Southern Territories','Togo','Thailand','Tajikistan','Tokelau','Turkmenistan','Tunisia','Tonga','Timor-Leste','Turkey','Trinidad and Tobago','Tuvalu','Taiwan','Tanzania, United Republic of','Ukraine','Uganda','United States Minor Outlying Islands','United States','Uruguay','Uzbekistan','Holy See (Vatican City State)',
  103. 'Saint Vincent and the Grenadines','Venezuela','Virgin Islands, British','Virgin Islands, U.S.','Vietnam','Vanuatu','Wallis and Futuna','Samoa','Yemen','Mayotte','Serbia','South Africa','Zambia','Montenegro','Zimbabwe','Anonymous Proxy','Satellite Provider','Other','Aland Islands','Guernsey','Isle of Man','Jersey');
  104. implementation
  105. const
  106. COUNTRY_BEGIN = 16776960;
  107. STATE_BEGIN_REV0 = 16700000;
  108. STATE_BEGIN_REV1 = 16000000;
  109. STRUCTURE_INFO_MAX_SIZE = 20;
  110. DATABASE_INFO_MAX_SIZE = 100;
  111. SEGMENT_RECORD_LENGTH = 3;
  112. STANDARD_RECORD_LENGTH = 3;
  113. ORG_RECORD_LENGTH = 4;
  114. MAX_RECORD_LENGTH = 4;
  115. MAX_ORG_RECORD_LENGTH = 300;
  116. FULL_RECORD_LENGTH = 50;
  117. US_OFFSET = 1;
  118. CANADA_OFFSET = 677;
  119. WORLD_OFFSET = 1353;
  120. FIPS_RANGE = 360;
  121. { TGeoIP }
  122. constructor TGeoIP.Create(const FileName: string);
  123. begin
  124. inherited Create;
  125. FInputFile := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);
  126. InitDBFile;
  127. end;
  128. destructor TGeoIP.Destroy;
  129. begin
  130. if Assigned(FInputFile) then
  131. FInputFile.Free;
  132. inherited Destroy;
  133. end;
  134. function TGeoIP._GetCity(IPNum: Cardinal; var GeoIPCity: TGeoIPCity): TGeoIPResult;
  135. var
  136. SeekCity: Cardinal;
  137. RecordPointer: Cardinal;
  138. StrLen: Cardinal;
  139. buf: array[0..FULL_RECORD_LENGTH-1] of Byte;
  140. p: PChar;
  141. i: Integer;
  142. DmaAreaCombo: Integer;
  143. begin
  144. if (FDatabaseType <> GEOIP_CITY_EDITION_REV0) and (FDatabaseType <> GEOIP_CITY_EDITION_REV1) then
  145. begin
  146. Result := GEOIP_ERROR_DBTYPE;
  147. Exit;
  148. end;
  149. SeekCity := SeekRecord(IPNum);
  150. if SeekCity = FDatabaseSegments[0] then
  151. begin
  152. Result := GEOIP_NODATA;
  153. Exit;
  154. end;
  155. RecordPointer := SeekCity + (2 * FRecordLength - 1) * FDatabaseSegments[0];
  156. FInputFile.Seek(RecordPointer, soFromBeginning);
  157. FInputFile.Read(buf, FULL_RECORD_LENGTH);
  158. // get country
  159. GeoIPCity.CountryCode := CountryCodes[buf[0]];
  160. GeoIPCity.CountryName := CountryNames[buf[0]];
  161. // get region
  162. p := @buf[1];
  163. StrLen := 0;
  164. while (p[StrLen] <> #0) do
  165. Inc(StrLen);
  166. GeoIPCity.Region := Copy(p, 0, StrLen);
  167. // get city
  168. Inc(p, StrLen + 1);
  169. StrLen := 0;
  170. while (p[StrLen] <> #0) do
  171. Inc(StrLen);
  172. GeoIPCity.City := Copy(p, 0, StrLen);
  173. // get postal code
  174. Inc(p, StrLen + 1);
  175. StrLen := 0;
  176. while (p[StrLen] <> #0) do
  177. Inc(StrLen);
  178. GeoIPCity.PostalCode := Copy(p, 0, StrLen);
  179. // get latitude
  180. Inc(p, StrLen + 1);
  181. GeoIPCity.Latitude := 0.0;
  182. for i:=0 to 2 do
  183. begin
  184. GeoIPCity.Latitude := GeoIPCity.Latitude + (Integer(p[i]) shl (i*8));
  185. end;
  186. GeoIPCity.Latitude := GeoIPCity.Latitude/10000 - 180;
  187. // get longitude
  188. Inc(p, 3);
  189. GeoIPCity.Longitude := 0.0;
  190. for i:=0 to 2 do
  191. begin
  192. GeoIPCity.Longitude := GeoIPCity.Longitude + (Integer(p[i]) shl (i*8));
  193. end;
  194. GeoIPCity.Longitude := GeoIPCity.Longitude/10000 - 180;
  195. // get area code and dma code for post April 2002 databases and for US locations
  196. GeoIPCity.DmaCode := 0;
  197. GeoIPCity.AreaCode := 0;
  198. if FDatabaseType = GEOIP_CITY_EDITION_REV1 then
  199. begin
  200. if GeoIPCity.CountryCode = 'US' then
  201. begin
  202. Inc(p, 3);
  203. DmaAreaCombo := 0;
  204. for i:=0 to 2 do
  205. begin
  206. DmaAreaCombo := DmaAreaCombo + (Integer(p[i]) shl (i*8));
  207. end;
  208. GeoIPCity.DmaCode := DmaAreaCombo div 1000;
  209. GeoIPCity.AreaCode := DmaAreaCombo mod 1000;
  210. end;
  211. end;
  212. Result := GEOIP_SUCCESS;
  213. end;
  214. function TGeoIP._GetCountry(IPNum: Cardinal; var GeoIPCountry: TGeoIPCountry): TGeoIPResult;
  215. var
  216. ret: Cardinal;
  217. begin
  218. if (FDatabaseType <> GEOIP_COUNTRY_EDITION) and (FDatabaseType <> GEOIP_PROXY_EDITION) then
  219. begin
  220. Result := GEOIP_ERROR_DBTYPE;
  221. Exit;
  222. end;
  223. ret := SeekRecord(IPNum) - COUNTRY_BEGIN;
  224. if ret > 0 then
  225. begin
  226. GeoIPCountry.CountryCode := CountryCodes[ret];
  227. GeoIPCountry.CountryName := CountryNames[ret];
  228. Result := GEOIP_SUCCESS;
  229. end
  230. else
  231. begin
  232. Result := GEOIP_NODATA;
  233. end;
  234. end;
  235. function TGeoIP._GetOrg(IPNum: Cardinal; var GeoIPOrg: TGeoIPOrg): TGeoIPResult;
  236. var
  237. SeekOrg: Cardinal;
  238. RecordPointer: Cardinal;
  239. StrLen: Cardinal;
  240. buf: array[0..MAX_ORG_RECORD_LENGTH-1] of Byte;
  241. p: PChar;
  242. begin
  243. if (FDatabaseType <> GEOIP_ORG_EDITION) and (FDatabaseType <> GEOIP_ISP_EDITION) and (FDatabaseType <> GEOIP_ASNUM_EDITION) then
  244. begin
  245. Result := GEOIP_ERROR_DBTYPE;
  246. Exit;
  247. end;
  248. SeekOrg := SeekRecord(IPNum);
  249. if SeekOrg = FDatabaseSegments[0] then
  250. begin
  251. Result := GEOIP_NODATA;
  252. Exit;
  253. end;
  254. RecordPointer := SeekOrg + (2 * FRecordLength - 1) * FDatabaseSegments[0];
  255. FInputFile.Seek(RecordPointer, soFromBeginning);
  256. FInputFile.Read(buf, FULL_RECORD_LENGTH);
  257. p := @buf[0];
  258. StrLen := 0;
  259. while (p[StrLen] <> #0) do
  260. Inc(StrLen);
  261. GeoIPOrg.Name := Copy(p, 0, StrLen);
  262. Result := GEOIP_SUCCESS;
  263. end;
  264. function TGeoIP._GetRegion(IPNum: Cardinal; var GeoIPRegion: TGeoIPRegion): TGeoIPResult;
  265. var
  266. SeekRegion: Cardinal;
  267. begin
  268. if (FDatabaseType <> GEOIP_REGION_EDITION_REV0) and (FDatabaseType <> GEOIP_REGION_EDITION_REV1) then
  269. begin
  270. Result := GEOIP_ERROR_DBTYPE;
  271. Exit;
  272. end;
  273. SeekRegion := SeekRecord(IPNum);
  274. if FDatabaseType = GEOIP_REGION_EDITION_REV0 then
  275. begin
  276. // Region Edition, pre June 2003
  277. Dec(SeekRegion, STATE_BEGIN_REV0);
  278. if SeekRegion >= 1000 then
  279. begin
  280. GeoIPRegion.CountryCode := 'US';
  281. GeoIPRegion.Region := Chr((SeekRegion - 1000) div 26 + 65) + Chr((SeekRegion - 1000) mod 26 + 65);
  282. end
  283. else
  284. begin
  285. GeoIPRegion.CountryCode := CountryCodes[SeekRegion];
  286. GeoIPRegion.Region := '';
  287. end;
  288. end
  289. else if FDatabaseType = GEOIP_REGION_EDITION_REV1 then
  290. begin
  291. // Region Edition, post June 2003
  292. Dec(SeekRegion, STATE_BEGIN_REV1);
  293. if SeekRegion < US_OFFSET then
  294. begin
  295. // Unknown
  296. GeoIPRegion.CountryCode := '';
  297. GeoIPRegion.Region := '';
  298. end
  299. else if SeekRegion < CANADA_OFFSET then
  300. begin
  301. // USA State
  302. GeoIPRegion.CountryCode := 'US';
  303. GeoIPRegion.Region := Chr((SeekRegion - US_OFFSET) div 26 + 65) + Chr((SeekRegion - US_OFFSET) mod 26 + 65);
  304. end
  305. else if SeekRegion < WORLD_OFFSET then
  306. begin
  307. // Canada Province
  308. GeoIPRegion.CountryCode := 'CA';
  309. GeoIPRegion.Region := Chr((SeekRegion - CANADA_OFFSET) div 26 + 65) + Chr((SeekRegion - CANADA_OFFSET) mod 26 + 65);
  310. end
  311. else
  312. begin
  313. // Not US or Canada
  314. GeoIPRegion.CountryCode := CountryCodes[(SeekRegion - WORLD_OFFSET) div FIPS_RANGE];
  315. GeoIPRegion.Region := '';
  316. end;
  317. end;
  318. Result := GEOIP_SUCCESS;
  319. end;
  320. function TGeoIP.AddrToNum(const IPAddr: string): Cardinal;
  321. var
  322. netlong: LongInt;
  323. begin
  324. netlong := inet_addr(PChar(IPAddr));
  325. if netlong <> INADDR_NONE then
  326. Result := ntohl(netlong)
  327. else
  328. Result := 0;
  329. end;
  330. function TGeoIP.GetCity(const IPAddr: string; var GeoIPCity: TGeoIPCity): TGeoIPResult;
  331. var
  332. IPNum: Cardinal;
  333. begin
  334. IPNum := AddrToNum(IPAddr);
  335. if IPNum = 0 then
  336. begin
  337. Result := GEOIP_ERROR_IPADDR;
  338. Exit;
  339. end;
  340. Result := _GetCity(IPNum, GeoIPCity);
  341. end;
  342. function TGeoIP.GetCountry(const IPAddr: string; var GeoIPCountry: TGeoIPCountry): TGeoIPResult;
  343. var
  344. IPNum: Cardinal;
  345. begin
  346. IPNum := AddrToNum(IPAddr);
  347. if IPNum = 0 then
  348. begin
  349. Result := GEOIP_ERROR_IPADDR;
  350. Exit;
  351. end;
  352. Result := _GetCountry(IPNum, GeoIPCountry);
  353. end;
  354. function TGeoIP.GetDatabaseInfo: string;
  355. var
  356. i: Integer;
  357. delim: array[0..2] of Byte;
  358. HasStructureInfo: Boolean;
  359. begin
  360. FDatabaseInfo := '';
  361. HasStructureInfo := False;
  362. FInputFile.Seek(-3, soFromEnd);
  363. for i:=0 to STRUCTURE_INFO_MAX_SIZE-1 do
  364. begin
  365. FInputFile.Read(delim, 3);
  366. if (delim[0] = 255) and (delim[1] = 255) and (delim[2] = 255) then
  367. begin
  368. HasStructureInfo := True;
  369. Break;
  370. end;
  371. FInputFile.Seek(-4, soFromCurrent);
  372. end;
  373. if HasStructureInfo then
  374. FInputFile.Seek(-3, soFromCurrent)
  375. else
  376. // no structure info, must be pre Sep 2002 database, go back to end
  377. FInputFile.Seek(-3, soFromEnd);
  378. for i:=0 to DATABASE_INFO_MAX_SIZE-1 do
  379. begin
  380. FInputFile.Read(delim, 3);
  381. if (delim[0] = 0) and (delim[1] = 0) and (delim[2] = 0) then
  382. begin
  383. SetLength(FDatabaseInfo, i);
  384. FInputFile.Read(PChar(FDatabaseInfo)^, i);
  385. Break;
  386. end;
  387. FInputFile.Seek(-4, soFromCurrent);
  388. end;
  389. Result := FDatabaseInfo;
  390. end;
  391. function TGeoIP.GetOrg(const IPAddr: string; var GeoIPOrg: TGeoIPOrg): TGeoIPResult;
  392. var
  393. IPNum: Cardinal;
  394. begin
  395. IPNum := AddrToNum(IPAddr);
  396. if IPNum = 0 then
  397. begin
  398. Result := GEOIP_ERROR_IPADDR;
  399. Exit;
  400. end;
  401. Result := _GetOrg(IPNum, GeoIPOrg);
  402. end;
  403. function TGeoIP.GetRegion(const IPAddr: string; var GeoIPRegion: TGeoIPRegion): TGeoIPResult;
  404. var
  405. IPNum: Cardinal;
  406. begin
  407. IPNum := AddrToNum(IPAddr);
  408. if IPNum = 0 then
  409. begin
  410. Result := GEOIP_ERROR_IPADDR;
  411. Exit;
  412. end;
  413. Result := _GetRegion(IPNum, GeoIPRegion);
  414. end;
  415. procedure TGeoIP.InitDBFile;
  416. var
  417. i,j: Integer;
  418. delim: array[0..2] of Byte;
  419. buf: array[0..SEGMENT_RECORD_LENGTH-1] of Byte;
  420. begin
  421. // default to GeoIP Country Edition
  422. FDatabaseType := GEOIP_COUNTRY_EDITION;
  423. FRecordLength := STANDARD_RECORD_LENGTH;
  424. FInputFile.Seek(-3, soFromEnd);
  425. for i:=0 to STRUCTURE_INFO_MAX_SIZE-1 do
  426. begin
  427. FInputFile.Read(delim, 3);
  428. if (delim[0] = 255) and (delim[1] = 255) and (delim[2] = 255) then
  429. begin
  430. FInputFile.Read(FDatabaseType, 1);
  431. if Byte(FDatabaseType) >= 106 then
  432. begin
  433. // Backward compatibility with databases from April 2003 and earlier
  434. Dec(FDatabaseType, 105);
  435. end;
  436. if FDatabaseType = GEOIP_REGION_EDITION_REV0 then
  437. begin
  438. // Region Edition, pre June 2003
  439. SetLength(FDatabaseSegments, 1);
  440. FDatabaseSegments[0] := STATE_BEGIN_REV0;
  441. end
  442. else if FDatabaseType = GEOIP_REGION_EDITION_REV1 then
  443. begin
  444. // Region Edition, post June 2003
  445. SetLength(FDatabaseSegments, 1);
  446. FDatabaseSegments[0] := STATE_BEGIN_REV1;
  447. end
  448. else if (FDatabaseType = GEOIP_CITY_EDITION_REV0) or
  449. (FDatabaseType = GEOIP_CITY_EDITION_REV1) or
  450. (FDatabaseType = GEOIP_ORG_EDITION) or
  451. (FDatabaseType = GEOIP_ISP_EDITION) or
  452. (FDatabaseType = GEOIP_ASNUM_EDITION) then
  453. begin
  454. // City/Org Editions have two segments, read offset of second segment
  455. SetLength(FDatabaseSegments, 1);
  456. FDatabaseSegments[0] := 0;
  457. FInputFile.Read(buf, SEGMENT_RECORD_LENGTH);
  458. for j:=0 to SEGMENT_RECORD_LENGTH-1 do
  459. begin
  460. Inc(FDatabaseSegments[0], Integer(buf[j]) shl (j*8));
  461. end;
  462. if (FDatabaseType = GEOIP_ORG_EDITION) or
  463. (FDatabaseType = GEOIP_ISP_EDITION) then
  464. FRecordLength := ORG_RECORD_LENGTH;
  465. end;
  466. Break;
  467. end
  468. else
  469. begin
  470. FInputFile.Seek(-4, soFromCurrent);
  471. end;
  472. end;
  473. if (FDatabaseType = GEOIP_COUNTRY_EDITION) or
  474. (FDatabaseType = GEOIP_PROXY_EDITION) then
  475. begin
  476. SetLength(FDatabaseSegments, 1);
  477. FDatabaseSegments[0] := COUNTRY_BEGIN;
  478. end;
  479. end;
  480. function TGeoIP.SeekRecord(IPNum: Cardinal): Cardinal;
  481. var
  482. depth: Cardinal;
  483. offset: Cardinal;
  484. i,j: Cardinal;
  485. x: array[0..1] of Cardinal;
  486. y: Cardinal;
  487. buf: array[0..2*MAX_RECORD_LENGTH-1] of Byte;
  488. begin
  489. offset := 0;
  490. for depth:=31 downto 0 do
  491. begin
  492. FInputFile.Seek(2 * FRecordLength * offset, soFromBeginning);
  493. FInputFile.Read(buf, 2 * FRecordLength);
  494. for i:=0 to 1 do
  495. begin
  496. x[i] := 0;
  497. for j:=0 to FRecordLength-1 do
  498. begin
  499. y := buf[i*FRecordLength+j];
  500. x[i] := x[i] + (y shl (j*8));
  501. end;
  502. end;
  503. if (IPNum and (1 shl depth)) <> 0 then
  504. begin
  505. if x[1] >= FDatabaseSegments[0] then
  506. begin
  507. Result := x[1];
  508. Exit;
  509. end
  510. else
  511. begin
  512. Offset := x[1];
  513. end;
  514. end
  515. else
  516. begin
  517. if x[0] >= FDatabaseSegments[0] then
  518. begin
  519. Result := x[0];
  520. Exit;
  521. end
  522. else
  523. begin
  524. Offset := x[0];
  525. end;
  526. end;
  527. end;
  528. Result := 0;
  529. end;
  530. end.