geoip 0.1.0 → 0.2.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 +86 -4
  2. metadata +6 -6
data/lib/geoip.rb CHANGED
@@ -189,6 +189,7 @@ class GeoIP
189
189
  CANADA_OFFSET = 677
190
190
  WORLD_OFFSET = 1353
191
191
  FIPS_RANGE = 360
192
+ FULL_RECORD_LENGTH = 50
192
193
 
193
194
  STANDARD_RECORD_LENGTH = 3
194
195
  SEGMENT_RECORD_LENGTH = 3
@@ -203,13 +204,15 @@ class GeoIP
203
204
  @databaseType = GEOIP_COUNTRY_EDITION
204
205
  @record_length = STANDARD_RECORD_LENGTH
205
206
  @file = File.open(filename, 'rb')
206
- @file.seek(-31, IO::SEEK_END)
207
- 1.upto(STRUCTURE_INFO_MAX_SIZE) {
207
+ @file.seek(-3, IO::SEEK_END)
208
+ 0.upto(STRUCTURE_INFO_MAX_SIZE-1) { |i|
208
209
  if @file.read(3) == "\xFF\xFF\xFF"
209
210
  @databaseType = @file.getc
210
211
  @databaseType -= 105 if @databaseType >= 106
211
212
 
212
- puts "Old database file type #{@databaseType}, untested"
213
+ if (@databaseType != GEOIP_CITY_EDITION_REV0)
214
+ puts "Old database file type #{@databaseType}, untested"
215
+ end
213
216
 
214
217
  if (@databaseType == GEOIP_REGION_EDITION_REV0)
215
218
  # Region Edition, pre June 2003
@@ -232,9 +235,10 @@ class GeoIP
232
235
  @record_length = 4
233
236
  end
234
237
  end
238
+ break
235
239
 
236
240
  else
237
- @file.seek(-41, IO::SEEK_CUR)
241
+ @file.seek(-4, IO::SEEK_CUR)
238
242
  end
239
243
  }
240
244
  if (@databaseType == GEOIP_COUNTRY_EDITION ||
@@ -257,6 +261,11 @@ class GeoIP
257
261
  # * The two-character continent code
258
262
  #
259
263
  def country(hostname)
264
+ if (@databaseType == GEOIP_CITY_EDITION_REV0 ||
265
+ @databaseType == GEOIP_CITY_EDITION_REV1)
266
+ return city(hostname)
267
+ end
268
+
260
269
  ip = hostname
261
270
  if ip.kind_of?(String) && ip !~ /^[0-9.]*$/
262
271
  # Lookup IP address, we were given a name
@@ -280,6 +289,79 @@ class GeoIP
280
289
  CountryContinent[code] ] # Continent code.
281
290
  end
282
291
 
292
+ # Search the GeoIP database for the specified host, returning city info
293
+ #
294
+ # +hostname+ is a String holding the host's DNS name or numeric IP address
295
+ # Return an array of eleven or thirteen elements:
296
+ # * All elements from the country query
297
+ # * The region (state or territory) name
298
+ # * The city name
299
+ # * The postal code (zipcode)
300
+ # * The latitude
301
+ # * The longitude
302
+ # * The dma_code and area_code, if available (REV1 City database)
303
+ def city(hostname)
304
+ ip = hostname
305
+ if ip.kind_of?(String) && ip !~ /^[0-9.]*$/
306
+ # Lookup IP address, we were given a name
307
+ ip = IPSocket.getaddress(hostname)
308
+ end
309
+
310
+ # Convert numeric IP address to an integer
311
+ ipnum = iptonum(ip)
312
+ if (@databaseType != GEOIP_CITY_EDITION_REV0 &&
313
+ @databaseType != GEOIP_CITY_EDITION_REV1)
314
+ throw "Invalid GeoIP database type, can't look up City by IP"
315
+ end
316
+ pos = seek_record(ipnum);
317
+ @file.seek(pos + (2*@record_length-1) * @databaseSegments[0])
318
+ record = @file.read(FULL_RECORD_LENGTH)
319
+
320
+ # The country code is the first byte:
321
+ code = record[0]
322
+ record = record[1..-1]
323
+
324
+ # Get the region:
325
+ region = record.sub(/\x00.*/, "")
326
+ record.sub!(/[^\x00]*\x00/, '')
327
+
328
+ # Get the city:
329
+ city = record.sub(/\x00.*/, "")
330
+ record.sub!(/[^\x00]*\x00/, '')
331
+
332
+ # Get the postal code:
333
+ postal_code = record.sub(/\x00.*/, "")
334
+ record.sub!(/[^\x00]*\x00/, '')
335
+
336
+ # Get the latitude/longitude:
337
+ latitude = le_to_ui(record[0,3].unpack("C*"))/10000.0 - 180
338
+ longitude = le_to_ui(record[3,3].unpack("C*"))/10000.0 - 180
339
+
340
+ record = record[6..-1]
341
+ us_area_codes = []
342
+ if (@databaseType == GEOIP_CITY_EDITION_REV1 &&
343
+ CountryCode[code] == "US") # UNTESTED
344
+ dmaarea_combo = le_to_ui(record[0,3].unpack("C*"))
345
+ dma_code = dmaarea_combo/1000;
346
+ area_code = dmaarea_combo%1000;
347
+ us_area_codes = [ dma_code, area_code ]
348
+ end
349
+
350
+ [ hostname, # Requested hostname
351
+ ip, # Ip address as dotted quad
352
+ CountryCode[code], # ISO3166-1 code
353
+ CountryCode3[code], # ISO3166-2 code
354
+ CountryName[code], # Country name, per IS03166
355
+ CountryContinent[code], # Continent code.
356
+ region, # Region name
357
+ city, # City name
358
+ postal_code, # Postal code
359
+ latitude,
360
+ longitude,
361
+ ] + us_area_codes
362
+
363
+ end
364
+
283
365
  private
284
366
  def iptonum(ip) # Convert numeric IP address to integer
285
367
  if ip.kind_of?(String) &&
metadata CHANGED
@@ -3,13 +3,13 @@ rubygems_version: 0.8.4
3
3
  specification_version: 1
4
4
  name: geoip
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.1.0
7
- date: 2005-03-11
6
+ version: 0.2.0
7
+ date: 2005-04-27
8
8
  summary: "GeoIP looks up a GeoIP database to provide geographical data for an IP address
9
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."
10
+ www.maxmind.com only contains country information. This library supports that
11
+ and the GeoIPCity file (both revisions). The data is much more reliable than
12
+ using the country codes at the end of the hosts' domain names."
13
13
  require_paths:
14
14
  - lib
15
15
  email: cjh@polyplex.org
@@ -40,5 +40,5 @@ extra_rdoc_files: []
40
40
  executables: []
41
41
  extensions: []
42
42
  requirements:
43
- - The free GeoIP database from www.maxmind.com
43
+ - The free GeoIP database or a GeoIP city database from www.maxmind.com
44
44
  dependencies: []