geoip 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. data/lib/geoip.rb +73 -33
  2. metadata +3 -3
data/lib/geoip.rb CHANGED
@@ -164,8 +164,7 @@ class GeoIP
164
164
  "AS","AS","OC","AS","AF","OC","AS","AS","SA","OC","AS","AF","EU","AF",
165
165
  "OC","NA","SA","AS","EU","SA","SA","SA","SA","AS","OC","OC","OC","AS",
166
166
  "AF","EU","AF","AF","AF","AF"]
167
-
168
- private
167
+ public
169
168
  # Edition enumeration:
170
169
  (GEOIP_COUNTRY_EDITION,
171
170
  GEOIP_CITY_EDITION_REV1,
@@ -178,6 +177,7 @@ class GeoIP
178
177
  GEOIP_ASNUM_EDITION,
179
178
  GEOIP_NETSPEED_EDITION,
180
179
  ) = *1..10
180
+ private
181
181
 
182
182
  COUNTRY_BEGIN = 16776960
183
183
  STATE_BEGIN_REV0 = 16700000
@@ -195,6 +195,7 @@ class GeoIP
195
195
  SEGMENT_RECORD_LENGTH = 3
196
196
 
197
197
  public
198
+ attr_reader :databaseType
198
199
  # Open the GeoIP database and determine the file format version
199
200
  #
200
201
  # +filename+ is a String holding the path to the GeoIP.dat file
@@ -300,51 +301,57 @@ class GeoIP
300
301
  # * The latitude
301
302
  # * The longitude
302
303
  # * 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
304
+ private
309
305
 
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);
306
+ def read_city(pos, hostname = '', ip = '')
317
307
  @file.seek(pos + (2*@record_length-1) * @databaseSegments[0])
318
- record = @file.read(FULL_RECORD_LENGTH)
308
+ return nil if((record = @file.read(FULL_RECORD_LENGTH)).nil?)
319
309
 
320
310
  # The country code is the first byte:
321
311
  code = record[0]
322
312
  record = record[1..-1]
313
+ @iter_pos += 1 unless @iter_pos.nil?
323
314
 
315
+ spl = record.split("\x00", 4)
324
316
  # Get the region:
325
- region = record.sub(/\x00.*/, "")
326
- record.sub!(/[^\x00]*\x00/, '')
317
+ region = spl[0]
318
+ @iter_pos += (region.size + 1) unless @iter_pos.nil?
327
319
 
328
320
  # Get the city:
329
- city = record.sub(/\x00.*/, "")
330
- record.sub!(/[^\x00]*\x00/, '')
321
+ city = spl[1]
322
+ @iter_pos += (city.size + 1) unless @iter_pos.nil?
331
323
 
332
324
  # Get the postal code:
333
- postal_code = record.sub(/\x00.*/, "")
334
- record.sub!(/[^\x00]*\x00/, '')
325
+ postal_code = spl[2]
326
+ @iter_pos += (postal_code.size + 1) unless @iter_pos.nil?
335
327
 
328
+ record = spl[3]
336
329
  # 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
330
+ if(record && record[0,3]) then
331
+ latitude = le_to_ui(record[0,3].unpack('C*')) / 10000.0 - 180
332
+ record = record[3..-1]
333
+ @iter_pos += 3 unless @iter_pos.nil?
334
+ else
335
+ latitude = ''
336
+ end
337
+ if(record && record[0,3]) then
338
+ longitude = le_to_ui(record[0,3].unpack('C*')) / 10000.0 - 180
339
+ record = record[3..-1]
340
+ @iter_pos += 3 unless @iter_pos.nil?
341
+ else
342
+ longitude = ''
343
+ end
339
344
 
340
- record = record[6..-1]
341
345
  us_area_codes = []
346
+ if(record && record[0,3]) then
342
347
  if (@databaseType == GEOIP_CITY_EDITION_REV1 &&
343
348
  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;
349
+ dmaarea_combo = le_to_ui(record[0,3].unpack('C*'))
350
+ dma_code = dmaarea_combo / 1000;
351
+ area_code = dmaarea_combo % 1000;
347
352
  us_area_codes = [ dma_code, area_code ]
353
+ @iter_pos += 3 unless @iter_pos.nil?
354
+ end
348
355
  end
349
356
 
350
357
  [ hostname, # Requested hostname
@@ -359,7 +366,39 @@ class GeoIP
359
366
  latitude,
360
367
  longitude,
361
368
  ] + us_area_codes
369
+ end
370
+ public
371
+ def city(hostname)
372
+ ip = hostname
373
+ if ip.kind_of?(String) && ip !~ /^[0-9.]*$/
374
+ # Lookup IP address, we were given a name
375
+ ip = IPSocket.getaddress(hostname)
376
+ end
377
+
378
+ # Convert numeric IP address to an integer
379
+ ipnum = iptonum(ip)
380
+ if (@databaseType != GEOIP_CITY_EDITION_REV0 &&
381
+ @databaseType != GEOIP_CITY_EDITION_REV1)
382
+ throw "Invalid GeoIP database type, can't look up City by IP"
383
+ end
384
+ pos = seek_record(ipnum);
385
+ read_city(pos, hostname, ip)
386
+ end
362
387
 
388
+ def each
389
+ if (@databaseType != GEOIP_CITY_EDITION_REV0 &&
390
+ @databaseType != GEOIP_CITY_EDITION_REV1)
391
+ throw "Invalid GeoIP database type, can't iterate thru non-City database"
392
+ end
393
+
394
+ @iter_pos = @databaseSegments[0] + 1
395
+ num = 0
396
+ until((rec = read_city(@iter_pos)).nil?)
397
+ yield(rec)
398
+ print "#{num}: #{@iter_pos}\n" if((num += 1) % 1000 == 0)
399
+ end
400
+ @iter_pos = nil
401
+ self
363
402
  end
364
403
 
365
404
  private
@@ -375,21 +414,21 @@ class GeoIP
375
414
  # Binary search in the file.
376
415
  # Records are pairs of little-endian integers, each of @record_length.
377
416
  offset = 0
378
- mask = 2147483648
417
+ mask = 0x80000000
379
418
  31.downto(0) { |depth|
380
419
  @file.seek(@record_length * 2 * offset);
381
- buf = @file.read(@record_length*2);
420
+ buf = @file.read(@record_length * 2);
382
421
  buf.slice!(0...@record_length) if ((ipnum & mask) != 0)
383
422
  offset = le_to_ui(buf[0...@record_length].unpack("C*"))
384
423
  return offset if (offset >= @databaseSegments[0])
385
- mask = mask/2
424
+ mask >>= 1
386
425
  }
387
426
  end
388
427
 
389
428
  # Convert a big-endian array of numeric bytes to unsigned int
390
429
  def be_to_ui(s)
391
430
  s.inject(0) { |m, o|
392
- (m<<8)+o.to_i
431
+ (m << 8) + o.to_i
393
432
  }
394
433
  end
395
434
 
@@ -402,7 +441,8 @@ end
402
441
  if $0 == __FILE__
403
442
  g = GeoIP.new('/usr/share/GeoIP/GeoIP.dat');
404
443
 
444
+ req = ([GeoIP::GEOIP_CITY_EDITION_REV1, GeoIP::GEOIP_CITY_EDITION_REV0].include?(g.databaseType)) ? :city : :country
405
445
  ARGV.each { |a|
406
- p g.country(a)
446
+ p g.send(req, a)
407
447
  }
408
448
  end
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.8.4
2
+ rubygems_version: 0.8.10
3
3
  specification_version: 1
4
4
  name: geoip
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.2.0
7
- date: 2005-04-27
6
+ version: 0.3.0
7
+ date: 2006-09-25
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
10
  www.maxmind.com only contains country information. This library supports that