geoip 0.1.0 → 0.2.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 +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: []