geoip 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/geoip.rb +326 -0
  2. metadata +44 -0
data/lib/geoip.rb ADDED
@@ -0,0 +1,326 @@
1
+ #
2
+ # Native Ruby reader for the GeoIP database
3
+ # Lookup the country where IP address is allocated
4
+ #
5
+ #= COPYRIGHT
6
+ # This version Copyright (C) 2005 Clifford Heath
7
+ # Derived from the C version, Copyright (C) 2003 MaxMind LLC
8
+ #
9
+ # This library is free software; you can redistribute it and/or
10
+ # modify it under the terms of the GNU General Public
11
+ # License as published by the Free Software Foundation; either
12
+ # version 2.1 of the License, or (at your option) any later version.
13
+ #
14
+ # This library is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17
+ # General Public License for more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public
20
+ # License along with this library; if not, write to the Free Software
21
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
+ #
23
+ #= SYNOPSIS
24
+ #
25
+ # require 'geoip'
26
+ # p GeoIP.new('/usr/share/GeoIP/GeoIP.dat').country("www.netscape.sk")
27
+ #
28
+ #= DESCRIPTION
29
+ #
30
+ # GeoIP searches a GeoIP database for a given host or IP address, and
31
+ # returns information about the country where the IP address is allocated.
32
+ #
33
+ #= PREREQUISITES
34
+ #
35
+ # You need at least the free GeoIP.dat, for which the last known download
36
+ # location is <http://www.maxmind.com/download/geoip/database/GeoIP.dat.gz>
37
+ # This API requires the file to be decompressed for searching. Other versions
38
+ # of this database are available for purchase which contain more detailed
39
+ # information, but this information is not returned by this implementation.
40
+ # See www.maxmind.com for more information.
41
+ #
42
+ #=end
43
+
44
+ require 'socket'
45
+
46
+ class GeoIP
47
+ private
48
+ CountryCode = [
49
+ "--","AP","EU","AD","AE","AF","AG","AI","AL","AM","AN","AO","AQ","AR",
50
+ "AS","AT","AU","AW","AZ","BA","BB","BD","BE","BF","BG","BH","BI","BJ",
51
+ "BM","BN","BO","BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD","CF",
52
+ "CG","CH","CI","CK","CL","CM","CN","CO","CR","CU","CV","CX","CY","CZ",
53
+ "DE","DJ","DK","DM","DO","DZ","EC","EE","EG","EH","ER","ES","ET","FI",
54
+ "FJ","FK","FM","FO","FR","FX","GA","GB","GD","GE","GF","GH","GI","GL",
55
+ "GM","GN","GP","GQ","GR","GS","GT","GU","GW","GY","HK","HM","HN","HR",
56
+ "HT","HU","ID","IE","IL","IN","IO","IQ","IR","IS","IT","JM","JO","JP",
57
+ "KE","KG","KH","KI","KM","KN","KP","KR","KW","KY","KZ","LA","LB","LC",
58
+ "LI","LK","LR","LS","LT","LU","LV","LY","MA","MC","MD","MG","MH","MK",
59
+ "ML","MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV","MW","MX","MY",
60
+ "MZ","NA","NC","NE","NF","NG","NI","NL","NO","NP","NR","NU","NZ","OM",
61
+ "PA","PE","PF","PG","PH","PK","PL","PM","PN","PR","PS","PT","PW","PY",
62
+ "QA","RE","RO","RU","RW","SA","SB","SC","SD","SE","SG","SH","SI","SJ",
63
+ "SK","SL","SM","SN","SO","SR","ST","SV","SY","SZ","TC","TD","TF","TG",
64
+ "TH","TJ","TK","TM","TN","TO","TP","TR","TT","TV","TW","TZ","UA","UG",
65
+ "UM","US","UY","UZ","VA","VC","VE","VG","VI","VN","VU","WF","WS","YE",
66
+ "YT","YU","ZA","ZM","ZR","ZW","A1","A2","O1" ]
67
+
68
+ CountryCode3 = [
69
+ "--","AP","EU","AND","ARE","AFG","ATG","AIA","ALB","ARM","ANT","AGO",
70
+ "AQ","ARG","ASM","AUT","AUS","ABW","AZE","BIH","BRB","BGD","BEL","BFA",
71
+ "BGR","BHR","BDI","BEN","BMU","BRN","BOL","BRA","BHS","BTN","BV","BWA",
72
+ "BLR","BLZ","CAN","CC","COD","CAF","COG","CHE","CIV","COK","CHL","CMR",
73
+ "CHN","COL","CRI","CUB","CPV","CX","CYP","CZE","DEU","DJI","DNK","DMA",
74
+ "DOM","DZA","ECU","EST","EGY","ESH","ERI","ESP","ETH","FIN","FJI","FLK",
75
+ "FSM","FRO","FRA","FX","GAB","GBR","GRD","GEO","GUF","GHA","GIB","GRL",
76
+ "GMB","GIN","GLP","GNQ","GRC","GS","GTM","GUM","GNB","GUY","HKG","HM",
77
+ "HND","HRV","HTI","HUN","IDN","IRL","ISR","IND","IO","IRQ","IRN","ISL",
78
+ "ITA","JAM","JOR","JPN","KEN","KGZ","KHM","KIR","COM","KNA","PRK","KOR",
79
+ "KWT","CYM","KAZ","LAO","LBN","LCA","LIE","LKA","LBR","LSO","LTU","LUX",
80
+ "LVA","LBY","MAR","MCO","MDA","MDG","MHL","MKD","MLI","MMR","MNG","MAC",
81
+ "MNP","MTQ","MRT","MSR","MLT","MUS","MDV","MWI","MEX","MYS","MOZ","NAM",
82
+ "NCL","NER","NFK","NGA","NIC","NLD","NOR","NPL","NRU","NIU","NZL","OMN",
83
+ "PAN","PER","PYF","PNG","PHL","PAK","POL","SPM","PCN","PRI","PSE","PRT",
84
+ "PLW","PRY","QAT","REU","ROU","RUS","RWA","SAU","SLB","SYC","SDN","SWE",
85
+ "SGP","SHN","SVN","SJM","SVK","SLE","SMR","SEN","SOM","SUR","STP","SLV",
86
+ "SYR","SWZ","TCA","TCD","TF","TGO","THA","TJK","TKL","TLS","TKM","TUN",
87
+ "TON","TUR","TTO","TUV","TWN","TZA","UKR","UGA","UM","USA","URY","UZB",
88
+ "VAT","VCT","VEN","VGB","VIR","VNM","VUT","WLF","WSM","YEM","YT","YUG",
89
+ "ZAF","ZMB","ZR","ZWE","A1","A2","O1"]
90
+
91
+ CountryName = [
92
+ "N/A","Asia/Pacific Region","Europe","Andorra","United Arab Emirates",
93
+ "Afghanistan","Antigua and Barbuda","Anguilla","Albania","Armenia",
94
+ "Netherlands Antilles","Angola","Antarctica","Argentina",
95
+ "American Samoa","Austria","Australia","Aruba","Azerbaijan",
96
+ "Bosnia and Herzegovina","Barbados","Bangladesh","Belgium",
97
+ "Burkina Faso","Bulgaria","Bahrain","Burundi","Benin","Bermuda",
98
+ "Brunei Darussalam","Bolivia","Brazil","Bahamas","Bhutan",
99
+ "Bouvet Island","Botswana","Belarus","Belize","Canada",
100
+ "Cocos (Keeling) Islands","Congo, The Democratic Republic of the",
101
+ "Central African Republic","Congo","Switzerland","Cote D'Ivoire",
102
+ "Cook Islands","Chile","Cameroon","China","Colombia","Costa Rica",
103
+ "Cuba","Cape Verde","Christmas Island","Cyprus","Czech Republic",
104
+ "Germany","Djibouti","Denmark","Dominica","Dominican Republic",
105
+ "Algeria","Ecuador","Estonia","Egypt","Western Sahara","Eritrea",
106
+ "Spain","Ethiopia","Finland","Fiji","Falkland Islands (Malvinas)",
107
+ "Micronesia, Federated States of","Faroe Islands","France","France,
108
+ Metropolitan","Gabon","United Kingdom","Grenada","Georgia",
109
+ "French Guiana","Ghana","Gibraltar","Greenland","Gambia","Guinea",
110
+ "Guadeloupe","Equatorial Guinea","Greece",
111
+ "South Georgia and the South Sandwich Islands","Guatemala","Guam",
112
+ "Guinea-Bissau","Guyana","Hong Kong",
113
+ "Heard Island and McDonald Islands","Honduras","Croatia","Haiti",
114
+ "Hungary","Indonesia","Ireland","Israel","India",
115
+ "British Indian Ocean Territory","Iraq","Iran, Islamic Republic of",
116
+ "Iceland","Italy","Jamaica","Jordan","Japan","Kenya","Kyrgyzstan",
117
+ "Cambodia","Kiribati","Comoros","Saint Kitts and Nevis",
118
+ "Korea, Democratic People's Republic of","Korea, Republic of","Kuwait",
119
+ "Cayman Islands","Kazakhstan","Lao People's Democratic Republic",
120
+ "Lebanon","Saint Lucia","Liechtenstein","Sri Lanka","Liberia","Lesotho",
121
+ "Lithuania","Luxembourg","Latvia","Libyan Arab Jamahiriya","Morocco",
122
+ "Monaco","Moldova, Republic of","Madagascar","Marshall Islands",
123
+ "Macedonia","Mali","Myanmar","Mongolia","Macau",
124
+ "Northern Mariana Islands","Martinique","Mauritania","Montserrat",
125
+ "Malta","Mauritius","Maldives","Malawi","Mexico","Malaysia",
126
+ "Mozambique","Namibia","New Caledonia","Niger","Norfolk Island",
127
+ "Nigeria","Nicaragua","Netherlands","Norway","Nepal","Nauru","Niue",
128
+ "New Zealand","Oman","Panama","Peru","French Polynesia",
129
+ "Papua New Guinea","Philippines","Pakistan","Poland",
130
+ "Saint Pierre and Miquelon","Pitcairn Islands","Puerto Rico",
131
+ "Palestinian Territory, Occupied","Portugal","Palau","Paraguay","Qatar",
132
+ "Reunion","Romania","Russian Federation","Rwanda","Saudi Arabia",
133
+ "Solomon Islands","Seychelles","Sudan","Sweden","Singapore",
134
+ "Saint Helena","Slovenia","Svalbard and Jan Mayen","Slovakia",
135
+ "Sierra Leone","San Marino","Senegal","Somalia","Suriname",
136
+ "Sao Tome and Principe","El Salvador","Syrian Arab Republic",
137
+ "Swaziland","Turks and Caicos Islands","Chad",
138
+ "French Southern Territories","Togo","Thailand","Tajikistan","Tokelau",
139
+ "Turkmenistan","Tunisia","Tonga","East Timor","Turkey",
140
+ "Trinidad and Tobago","Tuvalu","Taiwan","Tanzania, United Republic of",
141
+ "Ukraine","Uganda","United States Minor Outlying Islands",
142
+ "United States","Uruguay","Uzbekistan","Holy See (Vatican City State)",
143
+ "Saint Vincent and the Grenadines","Venezuela","Virgin Islands,
144
+ British","Virgin Islands, U.S.","Vietnam","Vanuatu","Wallis and Futuna",
145
+ "Samoa","Yemen","Mayotte","Yugoslavia","South Africa","Zambia","Zaire",
146
+ "Zimbabwe","Anonymous Proxy","Satellite Provider","Other"]
147
+
148
+ CountryContinent = [
149
+ "--","AS","EU","EU","AS","AS","SA","SA","EU","AS","SA","AF","AN","SA",
150
+ "OC","EU","OC","SA","AS","EU","SA","AS","EU","AF","EU","AS","AF","AF",
151
+ "SA","AS","SA","SA","SA","AS","AF","AF","EU","SA","NA","AS","AF","AF",
152
+ "AF","EU","AF","OC","SA","AF","AS","SA","SA","SA","AF","AS","AS","EU",
153
+ "EU","AF","EU","SA","SA","AF","SA","EU","AF","AF","AF","EU","AF","EU",
154
+ "OC","SA","OC","EU","EU","EU","AF","EU","SA","AS","SA","AF","EU","SA",
155
+ "AF","AF","SA","AF","EU","SA","SA","OC","AF","SA","AS","AF","SA","EU",
156
+ "SA","EU","AS","EU","AS","AS","AS","AS","AS","EU","EU","SA","AS","AS",
157
+ "AF","AS","AS","OC","AF","SA","AS","AS","AS","SA","AS","AS","AS","SA",
158
+ "EU","AS","AF","AF","EU","EU","EU","AF","AF","EU","EU","AF","OC","EU",
159
+ "AF","AS","AS","AS","OC","SA","AF","SA","EU","AF","AS","AF","NA","AS",
160
+ "AF","AF","OC","AF","OC","AF","SA","EU","EU","AS","OC","OC","OC","AS",
161
+ "SA","SA","OC","OC","AS","AS","EU","SA","OC","SA","AS","EU","OC","SA",
162
+ "AS","AF","EU","AS","AF","AS","OC","AF","AF","EU","AS","AF","EU","EU",
163
+ "EU","AF","EU","AF","AF","SA","AF","SA","AS","AF","SA","AF","AF","AF",
164
+ "AS","AS","OC","AS","AF","OC","AS","AS","SA","OC","AS","AF","EU","AF",
165
+ "OC","NA","SA","AS","EU","SA","SA","SA","SA","AS","OC","OC","OC","AS",
166
+ "AF","EU","AF","AF","AF","AF"]
167
+
168
+ private
169
+ # Edition enumeration:
170
+ (GEOIP_COUNTRY_EDITION,
171
+ GEOIP_CITY_EDITION_REV1,
172
+ GEOIP_REGION_EDITION_REV1,
173
+ GEOIP_ISP_EDITION,
174
+ GEOIP_ORG_EDITION,
175
+ GEOIP_CITY_EDITION_REV0,
176
+ GEOIP_REGION_EDITION_REV0,
177
+ GEOIP_PROXY_EDITION,
178
+ GEOIP_ASNUM_EDITION,
179
+ GEOIP_NETSPEED_EDITION,
180
+ ) = *1..10
181
+
182
+ COUNTRY_BEGIN = 16776960
183
+ STATE_BEGIN_REV0 = 16700000
184
+ STATE_BEGIN_REV1 = 16000000
185
+ STRUCTURE_INFO_MAX_SIZE = 20
186
+ DATABASE_INFO_MAX_SIZE = 100
187
+ MAX_ORG_RECORD_LENGTH = 300
188
+ US_OFFSET = 1
189
+ CANADA_OFFSET = 677
190
+ WORLD_OFFSET = 1353
191
+ FIPS_RANGE = 360
192
+
193
+ STANDARD_RECORD_LENGTH = 3
194
+ SEGMENT_RECORD_LENGTH = 3
195
+
196
+ public
197
+ # Open the GeoIP database and determine the file format version
198
+ #
199
+ # +filename+ is a String holding the path to the GeoIP.dat file
200
+ # +options+ is an integer holding caching flags (unimplemented)
201
+ def initialize(filename, flags = 0)
202
+ @flags = flags
203
+ @databaseType = GEOIP_COUNTRY_EDITION
204
+ @record_length = STANDARD_RECORD_LENGTH
205
+ @file = File.open(filename, 'rb')
206
+ @file.seek(-31, IO::SEEK_END)
207
+ 1.upto(STRUCTURE_INFO_MAX_SIZE) {
208
+ if @file.read(3) == "\xFF\xFF\xFF"
209
+ @databaseType = @file.getc
210
+ @databaseType -= 105 if @databaseType >= 106
211
+
212
+ puts "Old database file type #{@databaseType}, untested"
213
+
214
+ if (@databaseType == GEOIP_REGION_EDITION_REV0)
215
+ # Region Edition, pre June 2003
216
+ @databaseSegments = [ STATE_BEGIN_REV0 ]
217
+ elsif (@databaseType == GEOIP_REGION_EDITION_REV1)
218
+ # Region Edition, post June 2003
219
+ @databaseSegments = [ STATE_BEGIN_REV1 ]
220
+ elsif (@databaseType == GEOIP_CITY_EDITION_REV0 ||
221
+ @databaseType == GEOIP_CITY_EDITION_REV1 ||
222
+ @databaseType == GEOIP_ORG_EDITION ||
223
+ @databaseType == GEOIP_ISP_EDITION ||
224
+ @databaseType == GEOIP_ASNUM_EDITION)
225
+ # City/Org Editions have two segments, read offset of second segment
226
+ @databaseSegments = [ 0 ]
227
+ sr = @file.read(3).unpack("C*")
228
+ @databaseSegments[0] += le_to_ui(sr)
229
+
230
+ if (@databaseType == GEOIP_ORG_EDITION ||
231
+ @databaseType == GEOIP_ISP_EDITION)
232
+ @record_length = 4
233
+ end
234
+ end
235
+
236
+ else
237
+ @file.seek(-41, IO::SEEK_CUR)
238
+ end
239
+ }
240
+ if (@databaseType == GEOIP_COUNTRY_EDITION ||
241
+ @databaseType == GEOIP_PROXY_EDITION ||
242
+ @databaseType == GEOIP_NETSPEED_EDITION)
243
+ @databaseSegments = [ COUNTRY_BEGIN ]
244
+ end
245
+ end
246
+
247
+ # Search the GeoIP database for the specified host, returning country info
248
+ #
249
+ # +hostname+ is a String holding the host's DNS name or numeric IP address
250
+ # Return an array of seven elements:
251
+ # * The host or IP address string as requested
252
+ # * The IP address string after looking up the host
253
+ # * The GeoIP country-ID as an integer
254
+ # * The ISO3166-1 two-character country code
255
+ # * The ISO3166-2 three-character country code
256
+ # * The ISO3166 English-language name of the country
257
+ # * The two-character continent code
258
+ #
259
+ def country(hostname)
260
+ ip = hostname
261
+ if ip.kind_of?(String) && ip !~ /^[0-9.]*$/
262
+ # Lookup IP address, we were given a name
263
+ ip = IPSocket.getaddress(hostname)
264
+ end
265
+
266
+ # Convert numeric IP address to an integer
267
+ ipnum = iptonum(ip)
268
+ if (@databaseType != GEOIP_COUNTRY_EDITION &&
269
+ @databaseType != GEOIP_PROXY_EDITION &&
270
+ @databaseType != GEOIP_NETSPEED_EDITION)
271
+ throw "Invalid GeoIP database type, can't look up Country by IP"
272
+ end
273
+ code = seek_record(ipnum) - COUNTRY_BEGIN;
274
+ [ hostname, # Requested hostname
275
+ ip, # Ip address as dotted quad
276
+ code, # GeoIP's country code
277
+ CountryCode[code], # ISO3166-1 code
278
+ CountryCode3[code], # ISO3166-2 code
279
+ CountryName[code], # Country name, per IS03166
280
+ CountryContinent[code] ] # Continent code.
281
+ end
282
+
283
+ private
284
+ def iptonum(ip) # Convert numeric IP address to integer
285
+ if ip.kind_of?(String) &&
286
+ ip =~ /^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$/
287
+ ip = be_to_ui(Regexp.last_match().to_a.slice(1..4))
288
+ end
289
+ ip
290
+ end
291
+
292
+ def seek_record(ipnum)
293
+ # Binary search in the file.
294
+ # Records are pairs of little-endian integers, each of @record_length.
295
+ offset = 0
296
+ mask = 2147483648
297
+ 31.downto(0) { |depth|
298
+ @file.seek(@record_length * 2 * offset);
299
+ buf = @file.read(@record_length*2);
300
+ buf.slice!(0...@record_length) if ((ipnum & mask) != 0)
301
+ offset = le_to_ui(buf[0...@record_length].unpack("C*"))
302
+ return offset if (offset >= @databaseSegments[0])
303
+ mask = mask/2
304
+ }
305
+ end
306
+
307
+ # Convert a big-endian array of numeric bytes to unsigned int
308
+ def be_to_ui(s)
309
+ s.inject(0) { |m, o|
310
+ (m<<8)+o.to_i
311
+ }
312
+ end
313
+
314
+ # Same for little-endian
315
+ def le_to_ui(s)
316
+ be_to_ui(s.reverse)
317
+ end
318
+ end
319
+
320
+ if $0 == __FILE__
321
+ g = GeoIP.new('/usr/share/GeoIP/GeoIP.dat');
322
+
323
+ ARGV.each { |a|
324
+ p g.country(a)
325
+ }
326
+ end
metadata ADDED
@@ -0,0 +1,44 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.4
3
+ specification_version: 1
4
+ name: geoip
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.1.0
7
+ date: 2005-03-11
8
+ summary: "GeoIP looks up a GeoIP database to provide geographical data for an IP address
9
+ or Internet hostname. The free version of the GeoIP database available from
10
+ www.maxmind.com only contains country information, and so far that's all that
11
+ this library supports. The data is much more reliable than using the country
12
+ codes at the end of the hosts' domain names."
13
+ require_paths:
14
+ - lib
15
+ email: cjh@polyplex.org
16
+ homepage: http://polyplex.org/cjh
17
+ rubyforge_project:
18
+ description: "GeoIP is a pure-Ruby replacement for some of the functionality in the wrapped C
19
+ library by Sean Chittenden. Given an IP address or hostname, it looks up the
20
+ country where that IP address is allocated."
21
+ autorequire: geoip
22
+ default_executable:
23
+ bindir: bin
24
+ has_rdoc: true
25
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
26
+ requirements:
27
+ -
28
+ - ">"
29
+ - !ruby/object:Gem::Version
30
+ version: 0.0.0
31
+ version:
32
+ platform: ruby
33
+ authors:
34
+ - Clifford Heath
35
+ files:
36
+ - lib/geoip.rb
37
+ test_files: []
38
+ rdoc_options: []
39
+ extra_rdoc_files: []
40
+ executables: []
41
+ extensions: []
42
+ requirements:
43
+ - The free GeoIP database from www.maxmind.com
44
+ dependencies: []