geoipdb 0.1.4 → 0.3.1
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.
- data/LICENSE.txt +1 -1
- data/README.markdown +7 -7
- data/Rakefile +6 -5
- data/VERSION +1 -1
- data/ext/geoipdb/Makefile +187 -0
- data/ext/{build.sh → geoipdb/build.sh} +0 -0
- data/ext/geoipdb/extconf.rb +5 -0
- data/ext/geoipdb/geoipdb.c +112 -0
- data/ext/{ipdb.c → geoipdb/ipdb.c} +134 -45
- data/ext/{ipdb.h → geoipdb/ipdb.h} +29 -9
- data/geoipdb.gemspec +20 -16
- data/lib/geoipdb.rb +17 -0
- data/sample_data/ip_ranges.csv +11 -11
- data/sample_data/ip_ranges_corrupt.csv +11 -11
- data/spec/geoipdb_spec.rb +34 -14
- data/spec/spec_helper.rb +2 -1
- data/test.rb +9 -0
- metadata +29 -26
- data/ext/extconf.rb +0 -5
- data/ext/geoipdb.c +0 -109
- data/ext/test.c +0 -42
data/LICENSE.txt
CHANGED
data/README.markdown
CHANGED
@@ -2,17 +2,17 @@
|
|
2
2
|
|
3
3
|
Fast (>3 Mio queries/sec!!!) GeoIpDb implementation for Ruby using C-Extensions.
|
4
4
|
|
5
|
-
* Returns a GeoLocation
|
5
|
+
* Returns a GeoLocation and additional information for a given IP.
|
6
6
|
* Reads Data from CSV-Files and uses internal binary caching.
|
7
7
|
|
8
8
|
## Usage
|
9
9
|
|
10
|
-
db =
|
11
|
-
|
12
|
-
|
10
|
+
db = IpDb.init "city_codes.csv", "ip_city.txt", "ip_city.cache"
|
11
|
+
ip_info = db.information_for_ip("178.0.0.1")
|
12
|
+
ip_info.inspect
|
13
|
+
=> #<IpInformation:0x101385c78 @city_name="eschborn", @city_code="ax5", @lng=8.55, @country_iso_code="de", @lat=50.133333, @is_mobile=true>
|
13
14
|
|
14
15
|
== Copyright
|
15
16
|
|
16
|
-
Copyright (c) 2010
|
17
|
-
further details.
|
18
|
-
|
17
|
+
Copyright (c) 2010 madvertise GmbH. See LICENSE.txt for
|
18
|
+
further details.
|
data/Rakefile
CHANGED
@@ -13,14 +13,15 @@ require 'jeweler'
|
|
13
13
|
Jeweler::Tasks.new do |gem|
|
14
14
|
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
15
15
|
gem.name = "geoipdb"
|
16
|
-
gem.homepage = "http://github.com/
|
16
|
+
gem.homepage = "http://github.com/madvertise/geoipdb"
|
17
17
|
gem.license = "MIT"
|
18
18
|
gem.summary = %Q{Fast (>3 Mio queries/sec!!!) GeoIpDb implementation for Ruby using C-Extensions.}
|
19
|
-
gem.description = %Q{Returns a GeoLocation
|
19
|
+
gem.description = %Q{Returns a GeoLocation and additional information for given IP. Reads Data from CSV-Files and uses internal binary caching.}
|
20
20
|
gem.email = "eugeniusmartinus@googlemail.com"
|
21
|
-
gem.authors = ["Eugen Martin"]
|
22
|
-
gem.extensions = ['ext/extconf.rb']
|
23
|
-
gem.
|
21
|
+
gem.authors = ["Eugen Martin", "Martin Karlsch"]
|
22
|
+
gem.extensions = ['ext/geoipdb/extconf.rb']
|
23
|
+
gem.require_paths = ["lib","ext"]
|
24
|
+
|
24
25
|
end
|
25
26
|
Jeweler::RubygemsDotOrgTasks.new
|
26
27
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1
|
1
|
+
0.3.1
|
@@ -0,0 +1,187 @@
|
|
1
|
+
|
2
|
+
SHELL = /bin/sh
|
3
|
+
|
4
|
+
#### Start of system configuration section. ####
|
5
|
+
|
6
|
+
srcdir = .
|
7
|
+
topdir = /Users/eugen/.rvm/rubies/ruby-1.9.2-p180/include/ruby-1.9.1
|
8
|
+
hdrdir = /Users/eugen/.rvm/rubies/ruby-1.9.2-p180/include/ruby-1.9.1
|
9
|
+
arch_hdrdir = /Users/eugen/.rvm/rubies/ruby-1.9.2-p180/include/ruby-1.9.1/$(arch)
|
10
|
+
VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
|
11
|
+
prefix = $(DESTDIR)/Users/eugen/.rvm/rubies/ruby-1.9.2-p180
|
12
|
+
rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
|
13
|
+
exec_prefix = $(prefix)
|
14
|
+
vendorhdrdir = $(rubyhdrdir)/vendor_ruby
|
15
|
+
sitehdrdir = $(rubyhdrdir)/site_ruby
|
16
|
+
rubyhdrdir = $(includedir)/$(RUBY_BASE_NAME)-$(ruby_version)
|
17
|
+
vendordir = $(rubylibprefix)/vendor_ruby
|
18
|
+
sitedir = $(rubylibprefix)/site_ruby
|
19
|
+
ridir = $(datarootdir)/$(RI_BASE_NAME)
|
20
|
+
mandir = $(datarootdir)/man
|
21
|
+
localedir = $(datarootdir)/locale
|
22
|
+
libdir = $(exec_prefix)/lib
|
23
|
+
psdir = $(docdir)
|
24
|
+
pdfdir = $(docdir)
|
25
|
+
dvidir = $(docdir)
|
26
|
+
htmldir = $(docdir)
|
27
|
+
infodir = $(datarootdir)/info
|
28
|
+
docdir = $(datarootdir)/doc/$(PACKAGE)
|
29
|
+
oldincludedir = $(DESTDIR)/usr/include
|
30
|
+
includedir = $(prefix)/include
|
31
|
+
localstatedir = $(prefix)/var
|
32
|
+
sharedstatedir = $(prefix)/com
|
33
|
+
sysconfdir = $(prefix)/etc
|
34
|
+
datadir = $(datarootdir)
|
35
|
+
datarootdir = $(prefix)/share
|
36
|
+
libexecdir = $(exec_prefix)/libexec
|
37
|
+
sbindir = $(exec_prefix)/sbin
|
38
|
+
bindir = $(exec_prefix)/bin
|
39
|
+
rubylibdir = $(rubylibprefix)/$(ruby_version)
|
40
|
+
archdir = $(rubylibdir)/$(arch)
|
41
|
+
sitelibdir = $(sitedir)/$(ruby_version)
|
42
|
+
sitearchdir = $(sitelibdir)/$(sitearch)
|
43
|
+
vendorlibdir = $(vendordir)/$(ruby_version)
|
44
|
+
vendorarchdir = $(vendorlibdir)/$(sitearch)
|
45
|
+
|
46
|
+
CC = gcc
|
47
|
+
CXX = g++
|
48
|
+
LIBRUBY = $(LIBRUBY_SO)
|
49
|
+
LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
|
50
|
+
LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME)
|
51
|
+
LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static
|
52
|
+
OUTFLAG = -o
|
53
|
+
COUTFLAG = -o
|
54
|
+
|
55
|
+
RUBY_EXTCONF_H =
|
56
|
+
cflags = $(optflags) $(debugflags) $(warnflags)
|
57
|
+
optflags = -O3
|
58
|
+
debugflags = -ggdb
|
59
|
+
warnflags = -Wextra -Wno-unused-parameter -Wno-parentheses -Wpointer-arith -Wwrite-strings -Wno-missing-field-initializers -Wshorten-64-to-32 -Wno-long-long
|
60
|
+
CFLAGS = -fno-common $(cflags) -fno-common -pipe
|
61
|
+
INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
|
62
|
+
DEFS =
|
63
|
+
CPPFLAGS = -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE $(DEFS) $(cppflags)
|
64
|
+
CXXFLAGS = $(CFLAGS) $(cxxflags)
|
65
|
+
ldflags = -L. -L/usr/local/lib
|
66
|
+
dldflags = -Wl,-undefined,dynamic_lookup -Wl,-multiply_defined,suppress -Wl,-flat_namespace
|
67
|
+
ARCH_FLAG =
|
68
|
+
DLDFLAGS = $(ldflags) $(dldflags)
|
69
|
+
LDSHARED = $(CC) -dynamic -bundle
|
70
|
+
LDSHAREDXX = $(CXX) -dynamic -bundle
|
71
|
+
AR = ar
|
72
|
+
EXEEXT =
|
73
|
+
|
74
|
+
RUBY_BASE_NAME = ruby
|
75
|
+
RUBY_INSTALL_NAME = ruby
|
76
|
+
RUBY_SO_NAME = ruby.1.9.1
|
77
|
+
arch = x86_64-darwin10.7.0
|
78
|
+
sitearch = $(arch)
|
79
|
+
ruby_version = 1.9.1
|
80
|
+
ruby = /Users/eugen/.rvm/rubies/ruby-1.9.2-p180/bin/ruby
|
81
|
+
RUBY = $(ruby)
|
82
|
+
RM = rm -f
|
83
|
+
RM_RF = $(RUBY) -run -e rm -- -rf
|
84
|
+
RMDIRS = $(RUBY) -run -e rmdir -- -p
|
85
|
+
MAKEDIRS = mkdir -p
|
86
|
+
INSTALL = /usr/bin/install -c
|
87
|
+
INSTALL_PROG = $(INSTALL) -m 0755
|
88
|
+
INSTALL_DATA = $(INSTALL) -m 644
|
89
|
+
COPY = cp
|
90
|
+
|
91
|
+
#### End of system configuration section. ####
|
92
|
+
|
93
|
+
preload =
|
94
|
+
|
95
|
+
libpath = . $(libdir)
|
96
|
+
LIBPATH = -L. -L$(libdir)
|
97
|
+
DEFFILE =
|
98
|
+
|
99
|
+
CLEANFILES = mkmf.log
|
100
|
+
DISTCLEANFILES =
|
101
|
+
DISTCLEANDIRS =
|
102
|
+
|
103
|
+
extout =
|
104
|
+
extout_prefix =
|
105
|
+
target_prefix = /geoipdb
|
106
|
+
LOCAL_LIBS =
|
107
|
+
LIBS = $(LIBRUBYARG_SHARED) -lpthread -ldl -lobjc
|
108
|
+
SRCS = geoipdb.c ipdb.c
|
109
|
+
OBJS = geoipdb.o ipdb.o
|
110
|
+
TARGET = geoipdb
|
111
|
+
DLLIB = $(TARGET).bundle
|
112
|
+
EXTSTATIC =
|
113
|
+
STATIC_LIB =
|
114
|
+
|
115
|
+
BINDIR = $(bindir)
|
116
|
+
RUBYCOMMONDIR = $(sitedir)$(target_prefix)
|
117
|
+
RUBYLIBDIR = $(sitelibdir)$(target_prefix)
|
118
|
+
RUBYARCHDIR = $(sitearchdir)$(target_prefix)
|
119
|
+
HDRDIR = $(rubyhdrdir)/ruby$(target_prefix)
|
120
|
+
ARCHHDRDIR = $(rubyhdrdir)/$(arch)/ruby$(target_prefix)
|
121
|
+
|
122
|
+
TARGET_SO = $(DLLIB)
|
123
|
+
CLEANLIBS = $(TARGET).bundle
|
124
|
+
CLEANOBJS = *.o *.bak
|
125
|
+
|
126
|
+
all: $(DLLIB)
|
127
|
+
static: $(STATIC_LIB)
|
128
|
+
.PHONY: all install static install-so install-rb
|
129
|
+
.PHONY: clean clean-so clean-rb
|
130
|
+
|
131
|
+
clean-rb-default::
|
132
|
+
clean-rb::
|
133
|
+
clean-so::
|
134
|
+
clean: clean-so clean-rb-default clean-rb
|
135
|
+
@-$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES)
|
136
|
+
|
137
|
+
distclean-rb-default::
|
138
|
+
distclean-rb::
|
139
|
+
distclean-so::
|
140
|
+
distclean: clean distclean-so distclean-rb-default distclean-rb
|
141
|
+
@-$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
|
142
|
+
@-$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
|
143
|
+
@-$(RMDIRS) $(DISTCLEANDIRS)
|
144
|
+
|
145
|
+
realclean: distclean
|
146
|
+
install: install-so install-rb
|
147
|
+
|
148
|
+
install-so: $(RUBYARCHDIR)
|
149
|
+
install-so: $(RUBYARCHDIR)/$(DLLIB)
|
150
|
+
$(RUBYARCHDIR)/$(DLLIB): $(DLLIB)
|
151
|
+
@-$(MAKEDIRS) $(@D)
|
152
|
+
$(INSTALL_PROG) $(DLLIB) $(@D)
|
153
|
+
install-rb: pre-install-rb install-rb-default
|
154
|
+
install-rb-default: pre-install-rb-default
|
155
|
+
pre-install-rb: Makefile
|
156
|
+
pre-install-rb-default: Makefile
|
157
|
+
$(RUBYARCHDIR):
|
158
|
+
$(MAKEDIRS) $@
|
159
|
+
|
160
|
+
site-install: site-install-so site-install-rb
|
161
|
+
site-install-so: install-so
|
162
|
+
site-install-rb: install-rb
|
163
|
+
|
164
|
+
.SUFFIXES: .c .m .cc .cxx .cpp .C .o
|
165
|
+
|
166
|
+
.cc.o:
|
167
|
+
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
|
168
|
+
|
169
|
+
.cxx.o:
|
170
|
+
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
|
171
|
+
|
172
|
+
.cpp.o:
|
173
|
+
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
|
174
|
+
|
175
|
+
.C.o:
|
176
|
+
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
|
177
|
+
|
178
|
+
.c.o:
|
179
|
+
$(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $<
|
180
|
+
|
181
|
+
$(DLLIB): $(OBJS) Makefile
|
182
|
+
@-$(RM) $(@)
|
183
|
+
$(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
|
184
|
+
|
185
|
+
|
186
|
+
|
187
|
+
$(OBJS): $(hdrdir)/ruby.h $(hdrdir)/ruby/defines.h $(arch_hdrdir)/ruby/config.h
|
File without changes
|
@@ -0,0 +1,112 @@
|
|
1
|
+
#include "ipdb.h"
|
2
|
+
|
3
|
+
#include "ruby.h"
|
4
|
+
/**
|
5
|
+
Ruby Wrapper
|
6
|
+
*/
|
7
|
+
|
8
|
+
typedef struct geipdb {
|
9
|
+
IPDB *db;
|
10
|
+
} geoipdb;
|
11
|
+
|
12
|
+
static VALUE cIpDb;
|
13
|
+
|
14
|
+
// free the memory used by the db, called by the Ruby-GC
|
15
|
+
void geoipdb_free(geoipdb *gi) {
|
16
|
+
printf("Freeing memory for the GeoIpDb.. \n");
|
17
|
+
if(gi == NULL)
|
18
|
+
return;
|
19
|
+
if (gi->db != NULL){
|
20
|
+
if(gi->db->cities != NULL ){
|
21
|
+
printf("..freeing cities \n");
|
22
|
+
free(gi->db->cities);
|
23
|
+
gi->db->cities = NULL;
|
24
|
+
}
|
25
|
+
if(gi->db->ranges != NULL ){
|
26
|
+
printf("..freeing ranges \n");
|
27
|
+
free(gi->db->ranges);
|
28
|
+
gi->db->ranges = NULL;
|
29
|
+
}
|
30
|
+
if(gi->db != NULL){
|
31
|
+
printf("..freeing ipdb \n");
|
32
|
+
free(gi->db);
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
|
38
|
+
VALUE ipdb_init(VALUE self, VALUE cities_file_name, VALUE ranges_file_name, VALUE cache_file_name) {
|
39
|
+
geoipdb *gi;
|
40
|
+
|
41
|
+
Check_Type(cities_file_name, T_STRING);
|
42
|
+
Check_Type(ranges_file_name, T_STRING);
|
43
|
+
Check_Type(cache_file_name, T_STRING);
|
44
|
+
|
45
|
+
char *cities_csv_file = RSTRING_PTR(cities_file_name);
|
46
|
+
char *ranges_csv_file = RSTRING_PTR(ranges_file_name);
|
47
|
+
char *cache_file = RSTRING_PTR(cache_file_name);
|
48
|
+
|
49
|
+
gi = ALLOC(geoipdb);
|
50
|
+
|
51
|
+
gi->db= init_db(cities_csv_file, ranges_csv_file, cache_file);
|
52
|
+
|
53
|
+
|
54
|
+
if(gi->db == NULL)
|
55
|
+
{
|
56
|
+
if(DEBUG)
|
57
|
+
printf("Could not init DB!\n");
|
58
|
+
/*
|
59
|
+
TODO: Add geoipdb_free in this case.. though not important for production...
|
60
|
+
*/
|
61
|
+
return Qnil;
|
62
|
+
}else{
|
63
|
+
if(DEBUG)
|
64
|
+
printf("\nDB Init completed!\n");
|
65
|
+
return(Data_Wrap_Struct(cIpDb, 0, geoipdb_free, gi));
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
|
70
|
+
VALUE build_ip_information_object(IpRange *range, City *city, char* isp) {
|
71
|
+
VALUE CIpInformation;
|
72
|
+
|
73
|
+
CIpInformation = rb_const_get(rb_cObject, rb_intern("IpInformation"));
|
74
|
+
|
75
|
+
VALUE ip_information = rb_funcall(CIpInformation, rb_intern("new"), 0);
|
76
|
+
rb_ivar_set(ip_information, rb_intern("@country_iso_code"), rb_str_new2(city->country_iso2) );
|
77
|
+
rb_ivar_set(ip_information, rb_intern("@city_name"), rb_str_new2(city->name) );
|
78
|
+
rb_ivar_set(ip_information, rb_intern("@city_code"), INT2FIX(city->city_code) );
|
79
|
+
rb_ivar_set(ip_information, rb_intern("@lng"), rb_float_new(city->lng) );
|
80
|
+
rb_ivar_set(ip_information, rb_intern("@lat"), rb_float_new(city->lat) );
|
81
|
+
rb_ivar_set(ip_information, rb_intern("@is_mobile"), range->is_mobile == 1 ? Qtrue : Qfalse );
|
82
|
+
rb_ivar_set(ip_information, rb_intern("@isp_name"), isp == NULL ? Qnil : ID2SYM( rb_intern(isp) ) );
|
83
|
+
|
84
|
+
return ip_information;
|
85
|
+
}
|
86
|
+
|
87
|
+
VALUE ipdb_information_for_ip(VALUE self, VALUE ip_string){
|
88
|
+
char *ip = RSTRING_PTR(ip_string);
|
89
|
+
geoipdb *gi;
|
90
|
+
|
91
|
+
Data_Get_Struct(self, geoipdb, gi);
|
92
|
+
|
93
|
+
IpRange* ip_range = find_range_for_ip(gi->db, ip);
|
94
|
+
|
95
|
+
if(!ip_range)
|
96
|
+
return Qnil;
|
97
|
+
|
98
|
+
City * city = find_city_for_ip_range(gi->db, ip_range);
|
99
|
+
if(!city)
|
100
|
+
return Qnil;
|
101
|
+
|
102
|
+
char* isp = find_isp_for_ip_range(gi->db, ip_range);
|
103
|
+
|
104
|
+
return build_ip_information_object(ip_range, city, isp);
|
105
|
+
}
|
106
|
+
|
107
|
+
void Init_geoipdb(void)
|
108
|
+
{
|
109
|
+
cIpDb = rb_define_class( "GeoIpDb", rb_cObject);
|
110
|
+
rb_define_singleton_method( cIpDb, "init", ipdb_init, 3);
|
111
|
+
rb_define_method( cIpDb, "information_for_ip", ipdb_information_for_ip, 1);
|
112
|
+
}
|
@@ -6,9 +6,6 @@
|
|
6
6
|
#include <stdlib.h>
|
7
7
|
#include <sys/time.h>
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
9
|
const char country_iso2_codes[253][3] = { "--","ap","eu","ad","ae","af","ag","ai","al","am","an",
|
13
10
|
"ao","aq","ar","as","at","au","aw","az","ba","bb",
|
14
11
|
"bd","be","bf","bg","bh","bi","bj","bm","bn","bo",
|
@@ -144,7 +141,26 @@ ip_to_int(const char *addr){
|
|
144
141
|
ipnum <<= 8;
|
145
142
|
return ipnum + octet;
|
146
143
|
}
|
147
|
-
|
144
|
+
|
145
|
+
|
146
|
+
unsigned char con_type_to_int(char* con_type) {
|
147
|
+
// possible values
|
148
|
+
// ?
|
149
|
+
// dialup
|
150
|
+
// broadband
|
151
|
+
// cable
|
152
|
+
// xdsl
|
153
|
+
// mobile
|
154
|
+
// t1
|
155
|
+
// t3
|
156
|
+
// oc3
|
157
|
+
// oc12
|
158
|
+
// satellite
|
159
|
+
// wireless
|
160
|
+
if(strlen(con_type) > 0 && (con_type[0] == 'm'))
|
161
|
+
return 1;
|
162
|
+
return 0;
|
163
|
+
}
|
148
164
|
|
149
165
|
// Function to compare
|
150
166
|
// - either two ip-ranges: i.e.: a(from...to) <=> b(from...to)
|
@@ -250,13 +266,10 @@ city_index_by_code(IPDB * db, uint16 city_code){
|
|
250
266
|
}
|
251
267
|
|
252
268
|
|
253
|
-
|
254
|
-
|
255
|
-
IpRange
|
269
|
+
IpRange* find_range_for_ip(IPDB *db, char *ip) {
|
270
|
+
IpRange* search;
|
271
|
+
IpRange* result;
|
256
272
|
search = (IpRange *)malloc(sizeof(IpRange));
|
257
|
-
void * res;
|
258
|
-
|
259
|
-
// print_stats(db);
|
260
273
|
|
261
274
|
if(db == NULL)
|
262
275
|
{
|
@@ -275,24 +288,29 @@ city_by_ip(IPDB *db, char *ip){
|
|
275
288
|
search->city_index = 0;
|
276
289
|
if(DEBUG)
|
277
290
|
printf("Searching for: ip=%s, ipnum=%lu \n", ip, search->from);
|
278
|
-
|
279
|
-
res = bsearch(search, db->ranges, db->ranges_count, sizeof(IpRange), compare_ranges);
|
291
|
+
result = (IpRange*)bsearch(search, db->ranges, db->ranges_count, sizeof(IpRange), compare_ranges);
|
280
292
|
if(search != NULL)
|
281
293
|
free(search);
|
282
294
|
|
283
|
-
if(
|
295
|
+
if(result == NULL)
|
284
296
|
{
|
285
297
|
if(DEBUG)
|
286
|
-
|
298
|
+
printf("ERROR: Could not find the IP: %s! THIS SHOULD NOT HAPPEN!\n", ip);
|
287
299
|
return NULL;
|
288
|
-
}else{
|
289
|
-
|
290
|
-
if(DEBUG){
|
300
|
+
} else {
|
301
|
+
if(DEBUG) {
|
291
302
|
printf("Found Range: \t");
|
292
303
|
print_range(result);
|
293
|
-
}
|
304
|
+
}
|
305
|
+
return (IpRange*)result;
|
294
306
|
}
|
295
|
-
|
307
|
+
}
|
308
|
+
|
309
|
+
City * find_city_for_ip_range(IPDB * db, IpRange* range)
|
310
|
+
{
|
311
|
+
if(!db || !range)
|
312
|
+
return NULL;
|
313
|
+
|
296
314
|
if(db->cities_count == 0)
|
297
315
|
{
|
298
316
|
if(DEBUG)
|
@@ -300,21 +318,55 @@ city_by_ip(IPDB *db, char *ip){
|
|
300
318
|
return NULL;
|
301
319
|
}
|
302
320
|
|
303
|
-
|
304
|
-
if(result->city_index >0 && result->city_index < db->cities_count)
|
321
|
+
if( range->city_index <= 0 || range->city_index >= db->cities_count )
|
305
322
|
{
|
306
|
-
// address the city directly via the array-idx
|
307
|
-
return &(db->cities[result->city_index]);
|
308
|
-
} else {
|
309
323
|
if(DEBUG)
|
310
|
-
printf("ERROR: Could not find city with index: %i - THIS SHOULD NOT HAPPEN!\n",
|
311
|
-
|
324
|
+
printf("ERROR: Could not find city with index: %i - THIS SHOULD NOT HAPPEN!\n", range->city_index);
|
325
|
+
}
|
326
|
+
|
327
|
+
return &(db->cities[range->city_index]);
|
328
|
+
}
|
329
|
+
|
330
|
+
char* find_isp_for_ip_range(IPDB * db, IpRange* range)
|
331
|
+
{
|
332
|
+
if( range == NULL || range->isp_index < 0 || range->isp_index > MAX_ISPS_COUNT){
|
333
|
+
printf("Could not find isp for isp_index=%i", range->isp_index);
|
334
|
+
return NULL;
|
335
|
+
}
|
336
|
+
return db->isps[range->isp_index];
|
337
|
+
}
|
338
|
+
|
339
|
+
int16
|
340
|
+
isp_index_by_name(IPDB * db, char* isp_name){
|
341
|
+
if(isp_name == NULL || isp_name == "")
|
342
|
+
return -1;
|
343
|
+
if( db->isps_count > 0){
|
344
|
+
int16 i = 0;
|
345
|
+
for( i = 0; i < db->isps_count; i++)
|
346
|
+
{
|
347
|
+
if( strcmp(db->isps[i], isp_name)==0)
|
348
|
+
{
|
349
|
+
return i;
|
350
|
+
}
|
351
|
+
}
|
352
|
+
}
|
353
|
+
// TODO: malloc more space if needed
|
354
|
+
// add new isp
|
355
|
+
if(db->isps_count < MAX_ISPS_COUNT){
|
356
|
+
int16 new_index = db->isps_count;
|
357
|
+
strncpy(db->isps[new_index], isp_name, MAX_ISP_NAME_LENGTH);
|
358
|
+
db->isps_count++;
|
359
|
+
return new_index;
|
360
|
+
}else{
|
361
|
+
printf("ERROR: MAX_ISPS_COUNT = %i limit reached - this should not happen!", MAX_ISPS_COUNT);
|
362
|
+
return -1;
|
312
363
|
}
|
313
364
|
}
|
314
365
|
|
315
366
|
|
367
|
+
|
316
368
|
// read ip-ranges from csv file, of format:
|
317
|
-
// from_ip|to_ip|city_code
|
369
|
+
// from_ip|to_ip|contype|city_code
|
318
370
|
void
|
319
371
|
read_ranges_csv(IPDB * db){
|
320
372
|
struct timeval tim;
|
@@ -332,39 +384,56 @@ read_ranges_csv(IPDB * db){
|
|
332
384
|
return;
|
333
385
|
}
|
334
386
|
char line[256];
|
335
|
-
char* from
|
336
|
-
char* to
|
337
|
-
char* city_code
|
338
|
-
int invalid_cities_count = 0;
|
387
|
+
char* from;
|
388
|
+
char* to;
|
389
|
+
char* city_code;
|
339
390
|
int city_index;
|
391
|
+
|
392
|
+
char* con_type;
|
393
|
+
char* isp_name;
|
394
|
+
uint16 isp_index;
|
395
|
+
|
396
|
+
int invalid_cities_count = 0;
|
340
397
|
|
341
398
|
IpRange* entry;
|
342
399
|
db->ranges_count = 0;
|
343
|
-
while (fgets(line,sizeof(line),f) && db->ranges_count < db->max_ranges_count){
|
400
|
+
while (fgets(line, sizeof(line) ,f) && db->ranges_count < db->max_ranges_count){
|
401
|
+
from = NULL;
|
402
|
+
to = NULL;
|
403
|
+
city_code = NULL;
|
404
|
+
city_index = 0;
|
405
|
+
|
406
|
+
con_type = NULL;
|
407
|
+
isp_name = NULL;
|
408
|
+
int16 isp_index = -1;
|
409
|
+
|
344
410
|
if(DEBUG && db->ranges_count % 1000000 == 0)
|
345
411
|
printf("Worked lines: %i\n", db->ranges_count);
|
346
412
|
|
347
413
|
from = strtok(line, RANGES_DELIM);
|
348
414
|
to = strtok(NULL, RANGES_DELIM);
|
415
|
+
con_type = strtok(NULL, RANGES_DELIM);
|
349
416
|
city_code = strtok(NULL, RANGES_DELIM);
|
417
|
+
isp_name = strtok(NULL, RANGES_DELIM);
|
418
|
+
|
350
419
|
city_index = city_index_by_code(db, atoi(city_code));
|
420
|
+
isp_index = isp_index_by_name(db, isp_name);
|
421
|
+
|
351
422
|
if(city_index < 0)
|
352
|
-
{
|
423
|
+
{
|
353
424
|
if(DEBUG)
|
354
425
|
printf("Could not find city for code: %i", atoi(city_code));
|
355
426
|
invalid_cities_count ++;
|
356
427
|
continue;
|
357
428
|
}else{
|
358
429
|
entry = &(db->ranges[db->ranges_count]);
|
359
|
-
|
360
430
|
entry->from = ip_to_int(from);
|
361
|
-
entry->to = ip_to_int(to);
|
362
|
-
|
363
|
-
|
431
|
+
entry->to = ip_to_int(to);
|
432
|
+
entry->is_mobile = con_type_to_int(con_type);
|
364
433
|
entry->city_index = city_index;
|
365
|
-
|
366
|
-
|
367
|
-
//
|
434
|
+
entry->isp_index = isp_index;
|
435
|
+
|
436
|
+
// printf("from: %u,to: %u, city_code:%s, city_index: %i\n",entry->from,entry->to,city_code, entry->city_index);
|
368
437
|
// printf("working record nr: %li\n", db->ranges_count);
|
369
438
|
db->ranges_count++;
|
370
439
|
}
|
@@ -488,13 +557,20 @@ write_cache_file(IPDB * db){
|
|
488
557
|
printf("FieldLength: %li\n",sizeof(db->ranges[0].from));
|
489
558
|
}
|
490
559
|
//write the header: i.e.: numbers of records
|
491
|
-
fwrite(&(db->cities_count), sizeof(db->cities_count),1,f);
|
560
|
+
fwrite(&(db->cities_count), sizeof(db->cities_count),1,f);
|
561
|
+
fwrite(&(db->isps_count), sizeof(db->isps_count),1,f);
|
492
562
|
fwrite(&(db->ranges_count), sizeof(db->ranges_count),1,f);
|
493
563
|
|
494
564
|
if(DEBUG)
|
495
565
|
printf("Writing Contents with %i cities, a %li bytes each, should = %li \n", db->cities_count, sizeof(City), db->cities_count * sizeof(City));
|
496
566
|
//write the actual data: all the ranges-array-buffer:
|
497
567
|
objects_written = fwrite(db->cities, sizeof(City), db->cities_count, f);
|
568
|
+
|
569
|
+
if(DEBUG)
|
570
|
+
printf("Writing Contents with %i isps, a %li bytes each, should = %li \n", db->isps_count, MAX_ISP_NAME_LENGTH, db->isps_count * MAX_ISP_NAME_LENGTH);
|
571
|
+
//write the actual data: all the ranges-array-buffer:
|
572
|
+
objects_written += fwrite(db->isps, MAX_ISP_NAME_LENGTH, db->isps_count, f);
|
573
|
+
|
498
574
|
if(DEBUG)
|
499
575
|
printf("Writing Contents with %i ranges, a %li bytes each, should = %li \n", db->ranges_count, sizeof(IpRange), db->ranges_count * sizeof(IpRange));
|
500
576
|
//write the actual data: all the ranges-array-buffer:
|
@@ -518,22 +594,28 @@ read_cache_file(IPDB * db){
|
|
518
594
|
return 0;
|
519
595
|
}
|
520
596
|
int cities_header_read = fread(&(db->cities_count), sizeof(db->cities_count),1,f);
|
597
|
+
int isps_header_read = fread(&(db->isps_count), sizeof(db->isps_count),1,f);
|
521
598
|
int ranges_header_read = fread(&(db->ranges_count), sizeof(db->ranges_count),1,f);
|
522
599
|
|
523
600
|
|
524
|
-
if(cities_header_read == 0 || ranges_header_read == 0 || db->cities_count == 0 || db->ranges_count ==0
|
601
|
+
if(cities_header_read == 0 || isps_header_read == 0 || ranges_header_read == 0 || db->cities_count == 0 || db->isps_count ==0 || db->ranges_count ==0)
|
525
602
|
{
|
526
603
|
printf("Could not read Cities-Header from Cache-File: %s", db->cache_file_name);
|
527
604
|
return 0;
|
528
605
|
}
|
529
606
|
if(DEBUG)
|
530
|
-
printf("Reading DB-Header from Cache-File: %s, with %i cities and %i ranges\n",db->cache_file_name, db->cities_count, db->ranges_count);
|
607
|
+
printf("Reading DB-Header from Cache-File: %s, with %i cities, %iisps and %i ranges\n",db->cache_file_name, db->cities_count, db->isps_count, db->ranges_count);
|
531
608
|
|
532
609
|
int objects_read = 0;
|
533
610
|
if(DEBUG)
|
534
611
|
printf("Allocating: %lu for cities-array \n", sizeof(City)*(db->cities_count));
|
535
612
|
db->cities = malloc(sizeof(City) * db->cities_count);
|
536
613
|
objects_read += fread(db->cities, sizeof(City),db->cities_count,f);
|
614
|
+
|
615
|
+
if(DEBUG)
|
616
|
+
printf("Reading in the isps into preallocated buffer of size: ", sizeof(db->isps));
|
617
|
+
objects_read += fread(db->isps, MAX_ISP_NAME_LENGTH, db->isps_count,f);
|
618
|
+
|
537
619
|
if(DEBUG)
|
538
620
|
printf("Allocating: %lu for ranges-array \n", sizeof(IpRange)*(db->ranges_count));
|
539
621
|
db->ranges = malloc(sizeof(IpRange) * db->ranges_count);
|
@@ -554,8 +636,9 @@ benchmark_search(IPDB * db,int count){
|
|
554
636
|
int i;
|
555
637
|
City * city;
|
556
638
|
|
557
|
-
for(i=0;i<count; i++){
|
558
|
-
|
639
|
+
for(i=0;i<count; i++){
|
640
|
+
IpRange* range = find_range_for_ip(db,"278.50.47.0");
|
641
|
+
City* city = find_city_for_ip_range(db,range);
|
559
642
|
}
|
560
643
|
double delta = get_time(&tim)-t1;
|
561
644
|
|
@@ -563,6 +646,8 @@ benchmark_search(IPDB * db,int count){
|
|
563
646
|
}
|
564
647
|
|
565
648
|
IPDB * init_db(char * cities_csv_file, char * ranges_csv_file, char * cache_file_name){
|
649
|
+
if(DEBUG)
|
650
|
+
printf("Initializing db");
|
566
651
|
IPDB *db;
|
567
652
|
db = (IPDB*)malloc(sizeof(IPDB));
|
568
653
|
if (db == NULL) //no memory left
|
@@ -575,6 +660,10 @@ IPDB * init_db(char * cities_csv_file, char * ranges_csv_file, char * cache_file
|
|
575
660
|
db->max_cities_count = MAX_CITIES_COUNT;
|
576
661
|
db->ranges_csv_file = ranges_csv_file;
|
577
662
|
db->max_ranges_count = MAX_RANGES_COUNT;
|
663
|
+
|
664
|
+
// db->isps = NULL;
|
665
|
+
// db->isps = malloc(MAX_ISP_NAME_LENGTH * MAX_ISPS_COUNT);
|
666
|
+
db->isps_count = 0;
|
578
667
|
|
579
668
|
|
580
669
|
if(USE_CACHE && read_cache_file(db) > 0){
|
@@ -11,21 +11,31 @@ typedef unsigned short uint16;
|
|
11
11
|
typedef int int32;
|
12
12
|
#endif
|
13
13
|
|
14
|
+
#ifdef RSTRING_PTR
|
15
|
+
#else
|
16
|
+
# define RSTRING_LEN(x) (RSTRING(x)->len)
|
17
|
+
# define RSTRING_PTR(x) (RSTRING(x)->ptr)
|
18
|
+
#endif
|
19
|
+
|
20
|
+
|
21
|
+
#define RANGES_DELIM ",\n"
|
22
|
+
#define CITIES_DELIM ",\n"
|
14
23
|
|
15
|
-
#define RANGES_DELIM "|"
|
16
|
-
#define CITIES_DELIM ","
|
17
24
|
#define MAX_CITIES_COUNT 100000 //Usually we have about 50 000 Cities
|
18
|
-
#define MAX_RANGES_COUNT 10000000 //Usually we have about 6 Mio IP-Ranges
|
25
|
+
#define MAX_RANGES_COUNT 10000000 //Usually we have about 6 Mio IP-Ranges
|
19
26
|
|
27
|
+
#define MAX_ISPS_COUNT 100000
|
28
|
+
#define MAX_ISP_NAME_LENGTH 100
|
20
29
|
|
21
30
|
#define USE_CACHE 1
|
22
31
|
#define DEBUG 0
|
23
32
|
|
24
33
|
typedef struct{
|
25
34
|
unsigned long from;
|
26
|
-
unsigned long to;
|
27
|
-
|
35
|
+
unsigned long to;
|
36
|
+
unsigned char is_mobile;
|
28
37
|
uint16 city_index; //index of the city in the cities-array
|
38
|
+
int16 isp_index; //index of the isp in the isps-array
|
29
39
|
} IpRange;
|
30
40
|
|
31
41
|
typedef struct{
|
@@ -47,11 +57,14 @@ typedef struct{
|
|
47
57
|
unsigned int cities_count;
|
48
58
|
unsigned int max_cities_count;
|
49
59
|
|
50
|
-
|
51
60
|
char *cache_file_name; // a binary file to store the whole db.....
|
52
61
|
IpRange * ranges;
|
53
62
|
City * cities;
|
54
63
|
|
64
|
+
char isps[MAX_ISPS_COUNT][MAX_ISP_NAME_LENGTH]; // a fixed size array of strings should be enough here...do not expect the isps to grow dramatically..
|
65
|
+
uint16 isps_count;
|
66
|
+
|
67
|
+
|
55
68
|
} IPDB;
|
56
69
|
|
57
70
|
|
@@ -59,11 +72,18 @@ typedef struct{
|
|
59
72
|
IPDB *
|
60
73
|
init_db(char * cities_csv_file, char * ranges_csv_file, char * cache_file_name);
|
61
74
|
|
62
|
-
City *
|
63
|
-
city_by_ip(IPDB *db, char *ip);
|
64
75
|
|
65
76
|
void
|
66
77
|
print_city(const City * e);
|
67
78
|
|
68
79
|
void
|
69
|
-
benchmark_search(IPDB * db,int count);
|
80
|
+
benchmark_search(IPDB * db,int count);
|
81
|
+
|
82
|
+
IpRange*
|
83
|
+
find_range_for_ip(IPDB *db, char *ip);
|
84
|
+
|
85
|
+
City*
|
86
|
+
find_city_for_ip_range(IPDB * db, IpRange* range);
|
87
|
+
|
88
|
+
char*
|
89
|
+
find_isp_for_ip_range(IPDB * db, IpRange* range);
|
data/geoipdb.gemspec
CHANGED
@@ -5,14 +5,14 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{geoipdb}
|
8
|
-
s.version = "0.1
|
8
|
+
s.version = "0.3.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = ["Eugen Martin"]
|
12
|
-
s.date = %q{2011-
|
13
|
-
s.description = %q{Returns a GeoLocation
|
11
|
+
s.authors = ["Eugen Martin", "Martin Karlsch"]
|
12
|
+
s.date = %q{2011-06-14}
|
13
|
+
s.description = %q{Returns a GeoLocation and additional information for given IP. Reads Data from CSV-Files and uses internal binary caching.}
|
14
14
|
s.email = %q{eugeniusmartinus@googlemail.com}
|
15
|
-
s.extensions = ["ext/extconf.rb"]
|
15
|
+
s.extensions = ["ext/geoipdb/extconf.rb"]
|
16
16
|
s.extra_rdoc_files = [
|
17
17
|
"LICENSE.txt",
|
18
18
|
"README.markdown"
|
@@ -25,21 +25,26 @@ Gem::Specification.new do |s|
|
|
25
25
|
"README.markdown",
|
26
26
|
"Rakefile",
|
27
27
|
"VERSION",
|
28
|
-
"ext/
|
29
|
-
"ext/
|
30
|
-
"ext/geoipdb.
|
31
|
-
"ext/
|
32
|
-
"ext/ipdb.
|
33
|
-
"ext/
|
28
|
+
"ext/geoipdb/Makefile",
|
29
|
+
"ext/geoipdb/build.sh",
|
30
|
+
"ext/geoipdb/extconf.rb",
|
31
|
+
"ext/geoipdb/geoipdb.c",
|
32
|
+
"ext/geoipdb/ipdb.c",
|
33
|
+
"ext/geoipdb/ipdb.h",
|
34
34
|
"geoipdb.gemspec",
|
35
35
|
"lib/geoipdb.rb",
|
36
|
+
"sample_data/cities.csv",
|
37
|
+
"sample_data/citiess_corrupt.csv",
|
38
|
+
"sample_data/ip_ranges.csv",
|
39
|
+
"sample_data/ip_ranges_corrupt.csv",
|
36
40
|
"spec/geoipdb_spec.rb",
|
37
|
-
"spec/spec_helper.rb"
|
41
|
+
"spec/spec_helper.rb",
|
42
|
+
"test.rb"
|
38
43
|
]
|
39
|
-
s.homepage = %q{http://github.com/
|
44
|
+
s.homepage = %q{http://github.com/madvertise/geoipdb}
|
40
45
|
s.licenses = ["MIT"]
|
41
|
-
s.require_paths = ["ext"]
|
42
|
-
s.rubygems_version = %q{1.3
|
46
|
+
s.require_paths = ["lib", "ext"]
|
47
|
+
s.rubygems_version = %q{1.5.3}
|
43
48
|
s.summary = %q{Fast (>3 Mio queries/sec!!!) GeoIpDb implementation for Ruby using C-Extensions.}
|
44
49
|
s.test_files = [
|
45
50
|
"spec/geoipdb_spec.rb",
|
@@ -47,7 +52,6 @@ Gem::Specification.new do |s|
|
|
47
52
|
]
|
48
53
|
|
49
54
|
if s.respond_to? :specification_version then
|
50
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
51
55
|
s.specification_version = 3
|
52
56
|
|
53
57
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
data/lib/geoipdb.rb
CHANGED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
|
3
|
+
require 'geoipdb/geoipdb'
|
4
|
+
|
5
|
+
class IpInformation
|
6
|
+
attr_accessor :country_iso_code
|
7
|
+
attr_accessor :city_code
|
8
|
+
attr_accessor :city_name
|
9
|
+
attr_accessor :lat
|
10
|
+
attr_accessor :lng
|
11
|
+
attr_accessor :is_mobile
|
12
|
+
attr_accessor :isp_name
|
13
|
+
|
14
|
+
def mobile?
|
15
|
+
@is_mobile
|
16
|
+
end
|
17
|
+
end
|
data/sample_data/ip_ranges.csv
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
start_ip
|
2
|
-
0.0.0.0
|
3
|
-
0.0.1.0
|
4
|
-
1.0.0.0
|
5
|
-
1.0.1.0
|
6
|
-
1.1.1.0
|
7
|
-
1.1.2.0
|
8
|
-
1.2.3.0
|
9
|
-
1.2.4.0
|
10
|
-
1.4.0.0
|
11
|
-
1.4.1.0
|
1
|
+
start_ip,end_ip,field 4,field 13,
|
2
|
+
0.0.0.0,0.0.0.255,mobile,0
|
3
|
+
0.0.1.0,0.255.255.255,mobile,1,asdf,
|
4
|
+
1.0.0.0,1.0.0.255,xdsl,2,vodafone
|
5
|
+
1.0.1.0,1.1.0.255,wireless,3,
|
6
|
+
1.1.1.0,1.1.1.255,mobile,4,1vodafone2vodafone3vodafone4vodafone5vodafone1vodafone2vodafone3vodafone4vodafone5vodafone1vodafone2vodafone3vodafone4vodafone5vodafone1vodafone2vodafone3vodafone4vodafone5vodafone
|
7
|
+
1.1.2.0,1.2.2.255,mobile,5,
|
8
|
+
1.2.3.0,1.2.3.255,mobile,5,
|
9
|
+
1.2.4.0,1.3.255.255,mobile,2,
|
10
|
+
1.4.0.0,1.4.0.255,mobile,5,
|
11
|
+
1.4.1.0,1.8.255.255,mobile,3,
|
@@ -1,20 +1,20 @@
|
|
1
1
|
|
2
2
|
|
3
3
|
|
4
|
-
start_ip
|
5
|
-
0.0.0.0
|
4
|
+
start_ip,end_ip,field 13,
|
5
|
+
0.0.0.0,0.0.0.255,0,
|
6
6
|
|
7
|
-
0.0.1.0
|
7
|
+
0.0.1.0,0.255.255.255,1,
|
8
8
|
|
9
|
-
1.0.0.0
|
9
|
+
1.0.0.0,1.0.0.255,2,
|
10
10
|
|
11
|
-
1.0.1.0
|
11
|
+
1.0.1.0,1.1.0.255,3,
|
12
12
|
|
13
13
|
asfdasdf asdf asfasdfasdf§$%&/
|
14
14
|
|
15
|
-
1,
|
16
|
-
1.1.2.0
|
17
|
-
1.2.3.0
|
18
|
-
1.2.4.0
|
19
|
-
1.4.0.0
|
20
|
-
1.4.1.0
|
15
|
+
1, , , 7 ,.1.1.0,1.1.1.255,4,
|
16
|
+
1.1.2.0,1.2.2.255,5,
|
17
|
+
1.2.3.0,1.2.3.255,5,
|
18
|
+
1.2.4.0,1.3.255.255,2,
|
19
|
+
1.4.0.0,1.4.0.255,5,
|
20
|
+
1.4.1.0,1.8.255.255,3,
|
data/spec/geoipdb_spec.rb
CHANGED
@@ -1,37 +1,57 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__)+'/spec_helper')
|
2
2
|
|
3
3
|
|
4
|
-
describe
|
4
|
+
describe GeoIpDb do
|
5
|
+
|
6
|
+
CACHE_FILE = 'sample_data/ipdb.cache'
|
7
|
+
|
5
8
|
def init_db
|
6
|
-
@db = GeoIpDb.init './sample_data/cities.csv', './sample_data/ip_ranges.csv',
|
7
|
-
end
|
9
|
+
@db = GeoIpDb.init './sample_data/cities.csv', './sample_data/ip_ranges.csv', CACHE_FILE
|
10
|
+
end
|
8
11
|
|
9
|
-
|
10
|
-
@
|
12
|
+
it "should not throw an exception fault if data is corrupt" do
|
13
|
+
@db = GeoIpDb.init './sample_data/cities_corrupt.csv', './sample_data/ip_ranges_corrupt.csv', CACHE_FILE
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should not init a db object if data files are missing" do
|
17
|
+
GeoIpDb.init( './sample_data/bla.csv', './sample_data/blubb.csv', CACHE_FILE ).should be_nil
|
11
18
|
end
|
12
19
|
|
13
20
|
it "should init correctly with sample data and create the cache-file" do
|
14
21
|
init_db
|
15
22
|
@db.should_not be_nil
|
16
|
-
File.exist?(
|
23
|
+
File.exist?(CACHE_FILE).should be_true
|
17
24
|
end
|
18
25
|
|
19
26
|
it "sould find the sample cities correcty" do
|
20
27
|
init_db
|
21
28
|
#afg,no region,kabul,-1,3,34.5167,69.1833
|
22
|
-
@db.
|
29
|
+
info = @db.information_for_ip "1.1.0.254"
|
30
|
+
info.city_code.should == 3
|
31
|
+
info.city_name.should == 'kabul'
|
32
|
+
info.country_iso_code.should == 'af'
|
33
|
+
info.lat.should == 34.5167
|
34
|
+
info.lng.should == 69.1833
|
35
|
+
info.should_not be_mobile
|
23
36
|
end
|
24
37
|
|
25
|
-
it
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
38
|
+
it 'should return correct is_mobile information' do
|
39
|
+
init_db
|
40
|
+
@db.information_for_ip("1.0.0.1").should_not be_mobile
|
41
|
+
@db.information_for_ip("1.1.1.1").should be_mobile
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should return correct isp_name in ip_information' do
|
45
|
+
init_db
|
46
|
+
@db.information_for_ip("1.0.0.1").isp_name.should == :vodafone
|
47
|
+
@db.information_for_ip("1.1.1.1").isp_name.should == "1vodafone2vodafone3vodafone4vodafone5vodafone1vodafone2vodafone3vodafone4vodafone5vodafone1vodafone2vodafone3vodafone4vodafone5vodafone1vodafone2vodafone3vodafone4vodafone5vodafone"[0..99].to_sym
|
48
|
+
@db.information_for_ip("1.2.1.1").isp_name.should == nil
|
31
49
|
end
|
32
50
|
|
51
|
+
it "should write and read the cachefile correctly"
|
33
52
|
|
34
53
|
after :each do
|
35
|
-
File.unlink
|
54
|
+
File.unlink CACHE_FILE if File.exist? CACHE_FILE
|
36
55
|
end
|
56
|
+
|
37
57
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'ext'))
|
2
2
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
|
3
4
|
require 'rspec'
|
4
|
-
require 'geoipdb'
|
5
|
+
require 'lib/geoipdb'
|
5
6
|
|
6
7
|
# Requires supporting files with custom matchers and macros, etc,
|
7
8
|
# in ./support/ and its subdirectories.
|
data/test.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'ext'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
require 'lib/geoipdb'
|
5
|
+
|
6
|
+
|
7
|
+
db = GeoIpDb.init 'ext/data/cities.csv', 'ext/data/ip_ranges.csv', 'ext/data/ipdb.cache'
|
8
|
+
|
9
|
+
puts db.information_for_ip("165.193.245.54").inspect
|
metadata
CHANGED
@@ -1,25 +1,26 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: geoipdb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 17
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
+
- 3
|
8
9
|
- 1
|
9
|
-
|
10
|
-
version: 0.1.4
|
10
|
+
version: 0.3.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Eugen Martin
|
14
|
+
- Martin Karlsch
|
14
15
|
autorequire:
|
15
16
|
bindir: bin
|
16
17
|
cert_chain: []
|
17
18
|
|
18
|
-
date: 2011-
|
19
|
+
date: 2011-06-14 00:00:00 +02:00
|
19
20
|
default_executable:
|
20
21
|
dependencies:
|
21
22
|
- !ruby/object:Gem::Dependency
|
22
|
-
|
23
|
+
name: rspec
|
23
24
|
version_requirements: &id001 !ruby/object:Gem::Requirement
|
24
25
|
none: false
|
25
26
|
requirements:
|
@@ -31,11 +32,11 @@ dependencies:
|
|
31
32
|
- 1
|
32
33
|
- 0
|
33
34
|
version: 2.1.0
|
34
|
-
requirement: *id001
|
35
35
|
prerelease: false
|
36
|
-
name: rspec
|
37
|
-
- !ruby/object:Gem::Dependency
|
38
36
|
type: :development
|
37
|
+
requirement: *id001
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: bundler
|
39
40
|
version_requirements: &id002 !ruby/object:Gem::Requirement
|
40
41
|
none: false
|
41
42
|
requirements:
|
@@ -47,11 +48,11 @@ dependencies:
|
|
47
48
|
- 0
|
48
49
|
- 0
|
49
50
|
version: 1.0.0
|
50
|
-
requirement: *id002
|
51
51
|
prerelease: false
|
52
|
-
name: bundler
|
53
|
-
- !ruby/object:Gem::Dependency
|
54
52
|
type: :development
|
53
|
+
requirement: *id002
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: jeweler
|
55
56
|
version_requirements: &id003 !ruby/object:Gem::Requirement
|
56
57
|
none: false
|
57
58
|
requirements:
|
@@ -63,11 +64,11 @@ dependencies:
|
|
63
64
|
- 5
|
64
65
|
- 1
|
65
66
|
version: 1.5.1
|
66
|
-
requirement: *id003
|
67
67
|
prerelease: false
|
68
|
-
name: jeweler
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
68
|
type: :development
|
69
|
+
requirement: *id003
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: rcov
|
71
72
|
version_requirements: &id004 !ruby/object:Gem::Requirement
|
72
73
|
none: false
|
73
74
|
requirements:
|
@@ -77,15 +78,15 @@ dependencies:
|
|
77
78
|
segments:
|
78
79
|
- 0
|
79
80
|
version: "0"
|
80
|
-
requirement: *id004
|
81
81
|
prerelease: false
|
82
|
-
|
83
|
-
|
82
|
+
type: :development
|
83
|
+
requirement: *id004
|
84
|
+
description: Returns a GeoLocation and additional information for given IP. Reads Data from CSV-Files and uses internal binary caching.
|
84
85
|
email: eugeniusmartinus@googlemail.com
|
85
86
|
executables: []
|
86
87
|
|
87
88
|
extensions:
|
88
|
-
- ext/extconf.rb
|
89
|
+
- ext/geoipdb/extconf.rb
|
89
90
|
extra_rdoc_files:
|
90
91
|
- LICENSE.txt
|
91
92
|
- README.markdown
|
@@ -97,12 +98,12 @@ files:
|
|
97
98
|
- README.markdown
|
98
99
|
- Rakefile
|
99
100
|
- VERSION
|
100
|
-
- ext/
|
101
|
-
- ext/
|
102
|
-
- ext/geoipdb.
|
103
|
-
- ext/
|
104
|
-
- ext/ipdb.
|
105
|
-
- ext/
|
101
|
+
- ext/geoipdb/Makefile
|
102
|
+
- ext/geoipdb/build.sh
|
103
|
+
- ext/geoipdb/extconf.rb
|
104
|
+
- ext/geoipdb/geoipdb.c
|
105
|
+
- ext/geoipdb/ipdb.c
|
106
|
+
- ext/geoipdb/ipdb.h
|
106
107
|
- geoipdb.gemspec
|
107
108
|
- lib/geoipdb.rb
|
108
109
|
- sample_data/cities.csv
|
@@ -111,14 +112,16 @@ files:
|
|
111
112
|
- sample_data/ip_ranges_corrupt.csv
|
112
113
|
- spec/geoipdb_spec.rb
|
113
114
|
- spec/spec_helper.rb
|
115
|
+
- test.rb
|
114
116
|
has_rdoc: true
|
115
|
-
homepage: http://github.com/
|
117
|
+
homepage: http://github.com/madvertise/geoipdb
|
116
118
|
licenses:
|
117
119
|
- MIT
|
118
120
|
post_install_message:
|
119
121
|
rdoc_options: []
|
120
122
|
|
121
123
|
require_paths:
|
124
|
+
- lib
|
122
125
|
- ext
|
123
126
|
required_ruby_version: !ruby/object:Gem::Requirement
|
124
127
|
none: false
|
@@ -141,7 +144,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
141
144
|
requirements: []
|
142
145
|
|
143
146
|
rubyforge_project:
|
144
|
-
rubygems_version: 1.
|
147
|
+
rubygems_version: 1.5.3
|
145
148
|
signing_key:
|
146
149
|
specification_version: 3
|
147
150
|
summary: Fast (>3 Mio queries/sec!!!) GeoIpDb implementation for Ruby using C-Extensions.
|
data/ext/extconf.rb
DELETED
data/ext/geoipdb.c
DELETED
@@ -1,109 +0,0 @@
|
|
1
|
-
#include "ipdb.h"
|
2
|
-
|
3
|
-
#include "ruby.h"
|
4
|
-
|
5
|
-
/**
|
6
|
-
Ruby Wrapper
|
7
|
-
*/
|
8
|
-
|
9
|
-
typedef struct geipdb {
|
10
|
-
IPDB *db;
|
11
|
-
} geoipdb;
|
12
|
-
|
13
|
-
static VALUE cGeoIpDb;
|
14
|
-
|
15
|
-
VALUE method_hello_world(VALUE self);
|
16
|
-
|
17
|
-
// free the memory used by the db, called by the Ruby-GC
|
18
|
-
void geoipdb_free(geoipdb *gi) {
|
19
|
-
printf("Freeing memory for the GeoIpDb.. \n");
|
20
|
-
if(gi == NULL)
|
21
|
-
return;
|
22
|
-
if (gi->db != NULL){
|
23
|
-
if(gi->db->cities != NULL ){
|
24
|
-
printf("..freeing cities \n");
|
25
|
-
free(gi->db->cities);
|
26
|
-
gi->db->cities = NULL;
|
27
|
-
}
|
28
|
-
if(gi->db->ranges != NULL ){
|
29
|
-
printf("..freeing ranges \n");
|
30
|
-
free(gi->db->ranges);
|
31
|
-
gi->db->ranges = NULL;
|
32
|
-
}
|
33
|
-
if(gi->db != NULL){
|
34
|
-
printf("..freeing ipdb \n");
|
35
|
-
free(gi->db);
|
36
|
-
}
|
37
|
-
}
|
38
|
-
}
|
39
|
-
|
40
|
-
|
41
|
-
VALUE ipdb_init(VALUE self, VALUE cities_file_name, VALUE ranges_file_name, VALUE cache_file_name) {
|
42
|
-
geoipdb *gi;
|
43
|
-
|
44
|
-
Check_Type(cities_file_name, T_STRING);
|
45
|
-
Check_Type(ranges_file_name, T_STRING);
|
46
|
-
Check_Type(cache_file_name, T_STRING);
|
47
|
-
|
48
|
-
char *cities_csv_file = RSTRING(cities_file_name)->ptr;
|
49
|
-
char *ranges_csv_file = RSTRING(ranges_file_name)->ptr;
|
50
|
-
char *cache_file = RSTRING(cache_file_name)->ptr;
|
51
|
-
|
52
|
-
gi = malloc(sizeof(geoipdb));
|
53
|
-
|
54
|
-
gi->db= init_db(cities_csv_file, ranges_csv_file, cache_file);
|
55
|
-
|
56
|
-
if(gi->db == NULL)
|
57
|
-
{
|
58
|
-
if(DEBUG)
|
59
|
-
printf("Could not init DB!\n");
|
60
|
-
/*
|
61
|
-
TODO: Add geoipdb_free in this case.. though not important for production...
|
62
|
-
*/
|
63
|
-
return Qnil;
|
64
|
-
}else{
|
65
|
-
if(DEBUG)
|
66
|
-
printf("\nDB Init completed!\n");
|
67
|
-
return(Data_Wrap_Struct(cGeoIpDb, 0, geoipdb_free, gi));
|
68
|
-
}
|
69
|
-
}
|
70
|
-
|
71
|
-
|
72
|
-
VALUE city_to_hash(City * city){
|
73
|
-
VALUE hash = rb_hash_new();
|
74
|
-
// return a ruby-hash with the needed data
|
75
|
-
rb_hash_aset(hash, rb_str_new2("name"), rb_str_new2(city->name));
|
76
|
-
rb_hash_aset(hash, rb_str_new2("country"), rb_str_new2(city->country_iso2));
|
77
|
-
rb_hash_aset(hash, rb_str_new2("lat"), rb_float_new(city->lat));
|
78
|
-
rb_hash_aset(hash, rb_str_new2("lng"), rb_float_new(city->lng));
|
79
|
-
rb_hash_aset(hash, rb_str_new2("city_code"), INT2FIX(city->city_code));
|
80
|
-
return hash;
|
81
|
-
}
|
82
|
-
|
83
|
-
VALUE ipdb_city_by_ip(VALUE self, VALUE ip_string){
|
84
|
-
char *ip = RSTRING(ip_string)->ptr;
|
85
|
-
geoipdb *gi;
|
86
|
-
|
87
|
-
Data_Get_Struct(self, geoipdb, gi);
|
88
|
-
City * city = city_by_ip(gi->db, ip);
|
89
|
-
if(city == NULL)
|
90
|
-
{
|
91
|
-
// printf("Could not find city for ip: %s\n", ip);
|
92
|
-
return Qnil;
|
93
|
-
}else{
|
94
|
-
// printf("Found city for ip: %s\n", ip);
|
95
|
-
// print_city(city);
|
96
|
-
return city_to_hash(city);
|
97
|
-
// create a hash to return to ruby:
|
98
|
-
}
|
99
|
-
}
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
void Init_geoipdb(void)
|
104
|
-
{
|
105
|
-
//Erstelle die Klasse cGeoIpDb als Subklasse von Object
|
106
|
-
cGeoIpDb = rb_define_class( "GeoIpDb", rb_cObject);
|
107
|
-
rb_define_singleton_method( cGeoIpDb, "init", ipdb_init, 3);
|
108
|
-
rb_define_method( cGeoIpDb, "city_by_ip", ipdb_city_by_ip, 1);
|
109
|
-
}
|
data/ext/test.c
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
#include "ipdb.h"
|
2
|
-
|
3
|
-
#include <search.h>
|
4
|
-
#include <stdio.h>
|
5
|
-
#include <string.h>
|
6
|
-
#include <stdlib.h>
|
7
|
-
#include <sys/time.h>
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
int main() {
|
12
|
-
IPDB * db = init_db("data/cities.csv", "data/ip_ranges.csv", "data/ipdb.cache");
|
13
|
-
// IPDB * db = init_db(NULL, NULL, NULL);
|
14
|
-
|
15
|
-
|
16
|
-
// City * city;
|
17
|
-
//
|
18
|
-
// // city = city_by_ip(db, "1.16.0.0");
|
19
|
-
// int i;
|
20
|
-
int count = 10000000;
|
21
|
-
|
22
|
-
benchmark_search(db, count);
|
23
|
-
|
24
|
-
// for(i = 0; i < count; ++i)
|
25
|
-
// {
|
26
|
-
// if(i % 100000 == 0)
|
27
|
-
// printf("Working: i= %i \n", i);
|
28
|
-
//
|
29
|
-
// city = city_by_ip(db, "91.44.93.35");
|
30
|
-
// }
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
//
|
37
|
-
int * response;
|
38
|
-
scanf("%i",response);
|
39
|
-
|
40
|
-
return 0;
|
41
|
-
|
42
|
-
}
|