IPprefix 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -0
- data/IPprefix.gemspec +16 -0
- data/README +3 -0
- data/Rakefile +24 -0
- data/ext/IPprefix.c +559 -0
- data/ext/extconf.rb +5 -0
- metadata +75 -0
data/Gemfile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
gemspec
|
data/IPprefix.gemspec
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "IPprefix"
|
3
|
+
s.version = "0.0.1"
|
4
|
+
s.platform = Gem::Platform::RUBY
|
5
|
+
s.authors = ["Nathan Ward"]
|
6
|
+
s.email = ["nward@braintrust.co.nz"]
|
7
|
+
s.homepage = "http://github.com/nward/IPprefix"
|
8
|
+
s.summary = "Native, fast IP prefix handling code"
|
9
|
+
s.description = "Includes the IPprefix class, a fast class for handling IP prefixes."
|
10
|
+
|
11
|
+
s.required_rubygems_version = ">= 1.3.6"
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
|
15
|
+
s.extensions = "ext/extconf.rb"
|
16
|
+
end
|
data/README
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
gemspec = eval(File.read(Dir["*.gemspec"].first))
|
2
|
+
|
3
|
+
desc "Validate the gemspec"
|
4
|
+
task :gemspec do
|
5
|
+
gemspec.validate
|
6
|
+
end
|
7
|
+
|
8
|
+
desc "Build gem locally"
|
9
|
+
task :build => :gemspec do
|
10
|
+
system "gem build #{gemspec.name}.gemspec"
|
11
|
+
FileUtils.mkdir_p "pkg"
|
12
|
+
FileUtils.mv "#{gemspec.name}-#{gemspec.version}.gem", "pkg"
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Install gem locally"
|
16
|
+
task :install => :build do
|
17
|
+
system "gem install pkg/#{gemspec.name}-#{gemspec.version}"
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "Clean automatically generated files"
|
21
|
+
task :clean do
|
22
|
+
FileUtils.rm_rf "pkg"
|
23
|
+
end
|
24
|
+
|
data/ext/IPprefix.c
ADDED
@@ -0,0 +1,559 @@
|
|
1
|
+
/* 1531, Fri 29 Aug 08 (NZST)
|
2
|
+
|
3
|
+
IPprefix.c: IPprefix class, useful stuff for IP addresses and prefixes
|
4
|
+
|
5
|
+
Copyright (C) 2007-2009, Nevil Brownlee, U Auckland | CAIDA | WAND */
|
6
|
+
|
7
|
+
#include <stdint.h>
|
8
|
+
#include <arpa/inet.h>
|
9
|
+
#include <ctype.h>
|
10
|
+
#include "ruby.h"
|
11
|
+
|
12
|
+
#define IP4_ADDR_LEN 4
|
13
|
+
#define IP6_ADDR_LEN 16
|
14
|
+
|
15
|
+
#define IP4_LAST_BIT 31
|
16
|
+
#define IP6_LAST_BIT 127
|
17
|
+
|
18
|
+
static uint8_t b_mask[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
|
19
|
+
static uint8_t p_mask[8] = { 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE };
|
20
|
+
|
21
|
+
VALUE ipPref; /* Global so we can use Check_Type( ,ipPref) */
|
22
|
+
|
23
|
+
/*
|
24
|
+
* Document-class: IPprefix
|
25
|
+
*
|
26
|
+
* IPprefix is a class encapsulating an IPv4 or IPv6 address with a prefix length.
|
27
|
+
*/
|
28
|
+
|
29
|
+
/*
|
30
|
+
* Document-attr: addr
|
31
|
+
*
|
32
|
+
* Address as a 4 or 16 octet string
|
33
|
+
*/
|
34
|
+
|
35
|
+
/*
|
36
|
+
* Document-attr: version
|
37
|
+
*
|
38
|
+
* IP version (4 or 6)
|
39
|
+
*/
|
40
|
+
|
41
|
+
/*
|
42
|
+
* Document-attr: length
|
43
|
+
*
|
44
|
+
* Prefix length in bits
|
45
|
+
*/
|
46
|
+
|
47
|
+
/*
|
48
|
+
* call-seq:
|
49
|
+
* initialize(version,addr) -> newPrefix
|
50
|
+
* initialize(version,addr,length) -> newPrefix
|
51
|
+
*
|
52
|
+
* Returns an IPprefix conatining an IP address, e.g. 192.168.0.3
|
53
|
+
* version = 4 for IPv4, 6 for IPv6
|
54
|
+
* addr = 4- or 16-byte string containing actual address
|
55
|
+
* length = Integer containing the prefix length (optional)
|
56
|
+
*/
|
57
|
+
static VALUE pr_init(int argc, VALUE *argv, VALUE self) {
|
58
|
+
VALUE pa[3];
|
59
|
+
rb_scan_args(argc, argv, "12", &pa[0], &pa[1], &pa[2]);
|
60
|
+
if (argc < 1 || argc > 3) rb_raise(rb_eArgError,
|
61
|
+
"IPprefix.new called with < 1 or > 3 arguments!");
|
62
|
+
|
63
|
+
int version = NUM2INT(pa[0]);
|
64
|
+
if (version != 4 && version != 6) rb_raise(rb_eArgError,
|
65
|
+
"IPprefix.new: version must be 4 or 6!");
|
66
|
+
rb_iv_set(self, "@version", pa[0]);
|
67
|
+
int slen = version == 4 ? IP4_ADDR_LEN : IP6_ADDR_LEN;
|
68
|
+
|
69
|
+
if (argc == 1) { /* Create null prefix (root key for new ptrie) */
|
70
|
+
char null[IP6_ADDR_LEN] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
|
71
|
+
rb_iv_set(self, "@addr", rb_str_new(null, slen));
|
72
|
+
rb_iv_set(self, "@length", INT2FIX(0));
|
73
|
+
}
|
74
|
+
else {
|
75
|
+
uint8_t *ap = (uint8_t *)RSTRING_PTR(pa[1]);
|
76
|
+
int alen = RSTRING_LEN(pa[1]);
|
77
|
+
if (version == 4 && alen > 4) rb_raise(rb_eArgError,
|
78
|
+
"IPprefix.new: string length >4 for an IPv4 address!");
|
79
|
+
else if (version == 6 && alen > 16) rb_raise(rb_eArgError,
|
80
|
+
"IPprefix.new: string length >a16for an IPv6 address!");
|
81
|
+
if (alen == slen) rb_iv_set(self, "@addr", pa[1]);
|
82
|
+
else {
|
83
|
+
char as[IP6_ADDR_LEN] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
|
84
|
+
memcpy(as, ap, alen);
|
85
|
+
rb_iv_set(self, "@addr", rb_str_new(as, slen));
|
86
|
+
}
|
87
|
+
|
88
|
+
if (argc == 3) rb_iv_set(self, "@length", pa[2]);
|
89
|
+
else rb_iv_set(self, "@length", Qnil);
|
90
|
+
}
|
91
|
+
return self;
|
92
|
+
}
|
93
|
+
|
94
|
+
|
95
|
+
/*
|
96
|
+
* Returns the IP version as a Fixnum.
|
97
|
+
*/
|
98
|
+
static VALUE pr_version(VALUE self) {
|
99
|
+
return rb_iv_get(self, "@version");
|
100
|
+
}
|
101
|
+
|
102
|
+
/*
|
103
|
+
* Returns the address as a 4 or 16 byte string.
|
104
|
+
*/
|
105
|
+
static VALUE pr_addr(VALUE self) {
|
106
|
+
return rb_iv_get(self, "@addr");
|
107
|
+
}
|
108
|
+
|
109
|
+
/*
|
110
|
+
* Returns the prefix length as a Fixnum, or nil if no length is set.
|
111
|
+
*/
|
112
|
+
static VALUE pr_length(VALUE self) { /* ||k|| (may be nil) */
|
113
|
+
return rb_iv_get(self, "@length");
|
114
|
+
}
|
115
|
+
|
116
|
+
/*
|
117
|
+
* call-seq:
|
118
|
+
* length=(newLength) -> newLength
|
119
|
+
*
|
120
|
+
* Sets the prefix length.
|
121
|
+
*/
|
122
|
+
static VALUE pr_set_length(VALUE self, VALUE length) {
|
123
|
+
int w = NUM2INT(length);
|
124
|
+
int ver = FIX2INT(rb_iv_get(self, "@version"));
|
125
|
+
if (w < 0) rb_raise(rb_eArgError,
|
126
|
+
"IPprefix.length= length must be >= 0!");
|
127
|
+
if (ver == 4 && w > IP4_ADDR_LEN*8) rb_raise(rb_eArgError,
|
128
|
+
"IPprefix.length= IPv4 length must be <= %d!", IP4_ADDR_LEN*8);
|
129
|
+
else if (ver == 6 && w > IP6_ADDR_LEN*8) rb_raise(rb_eArgError,
|
130
|
+
"IPprefix.length= IPv6 length must be <= %d!", IP6_ADDR_LEN*8);
|
131
|
+
|
132
|
+
rb_iv_set(self, "@length", length);
|
133
|
+
return length;
|
134
|
+
}
|
135
|
+
|
136
|
+
/*
|
137
|
+
* call-seq:
|
138
|
+
* ==(otherPrefix) -> (true,false)
|
139
|
+
*
|
140
|
+
* Tests to see if two IPprefix objects have the same version and addr. Does not test length.
|
141
|
+
*/
|
142
|
+
static VALUE pr_addr_equal(VALUE self, VALUE sa) {
|
143
|
+
int vs = FIX2INT(rb_iv_get(self, "@version"));
|
144
|
+
if (vs != FIX2INT(rb_iv_get(sa, "@version"))) rb_raise(rb_eArgError,
|
145
|
+
"IPprefix.equal: Versions must be the same (4 or 6)");
|
146
|
+
int nb = vs == 4 ? IP4_ADDR_LEN : IP6_ADDR_LEN;
|
147
|
+
VALUE vsa = rb_iv_get(self, "@addr");
|
148
|
+
VALUE vaa = rb_iv_get(sa, "@addr");
|
149
|
+
uint8_t *sp = (uint8_t *)RSTRING_PTR(vsa);
|
150
|
+
uint8_t *ap = (uint8_t *)RSTRING_PTR(vaa);
|
151
|
+
return memcmp(sp, ap, nb) == 0 ? Qtrue : Qfalse;
|
152
|
+
}
|
153
|
+
|
154
|
+
/*
|
155
|
+
* call-seq:
|
156
|
+
* <=>(otherPrefix) -> (-1,0,1)
|
157
|
+
*
|
158
|
+
* Compares self.addr otherprefix.addr and returns -1, 0 or 1. Useful for sorting prefixes.
|
159
|
+
*/
|
160
|
+
static VALUE pr_addr_compare(VALUE self, VALUE sa) {
|
161
|
+
int vs = FIX2INT(rb_iv_get(self, "@version"));
|
162
|
+
if (vs != FIX2INT(rb_iv_get(sa, "@version"))) rb_raise(rb_eArgError,
|
163
|
+
"IPprefix.compare: Versions must be the same (4 or 6)");
|
164
|
+
int nb = vs == 4 ? IP4_ADDR_LEN : IP6_ADDR_LEN;
|
165
|
+
VALUE s_v = rb_iv_get(self, "@addr");
|
166
|
+
VALUE a_v = rb_iv_get(sa, "@addr");
|
167
|
+
uint8_t *sp = (uint8_t *)RSTRING_PTR(s_v);
|
168
|
+
uint8_t *ap = (uint8_t *)RSTRING_PTR(a_v);
|
169
|
+
int r = memcmp(sp, ap, nb);
|
170
|
+
if (r != 0) return INT2FIX(r < 0 ? -1 : +1);
|
171
|
+
else return INT2FIX(0);
|
172
|
+
}
|
173
|
+
|
174
|
+
/*
|
175
|
+
* Returns length-1
|
176
|
+
*/
|
177
|
+
static VALUE pr_width(VALUE self) { /* |k| = length-1 */
|
178
|
+
VALUE w = rb_iv_get(self, "@length");
|
179
|
+
if (w == Qnil) rb_raise(rb_eArgError,
|
180
|
+
"IPprefix.width: length nil, can't get width");
|
181
|
+
return INT2FIX(FIX2INT(w)-1);
|
182
|
+
}
|
183
|
+
|
184
|
+
/*
|
185
|
+
* call-seq:
|
186
|
+
* prefix?(otherPrefix) -> (true,false)
|
187
|
+
*
|
188
|
+
* Returns true if OtherIPprefix is a subnet of IPprefix, i.e. their first IPprefix.length bits are the same.
|
189
|
+
*/
|
190
|
+
static VALUE pr_preceq(VALUE self, VALUE v_arg) { /* precedes or equals */
|
191
|
+
int vs = FIX2INT(rb_iv_get(self, "@version"));
|
192
|
+
if (vs != FIX2INT(rb_iv_get(v_arg, "@version"))) rb_raise(rb_eArgError,
|
193
|
+
"IPprefix.first_bit_different: Versions must be same version (4 or 6)");
|
194
|
+
int nb = vs == 4 ? IP4_ADDR_LEN : IP6_ADDR_LEN;
|
195
|
+
VALUE s_v = rb_iv_get(self, "@length");
|
196
|
+
VALUE a_v = rb_iv_get(v_arg, "@length");
|
197
|
+
if (s_v == Qnil || a_v == Qnil) rb_raise(rb_eArgError,
|
198
|
+
"IPprefix.first_bit_different: either or both lengthis nil");
|
199
|
+
int s_len = FIX2INT(s_v);
|
200
|
+
if (s_len > FIX2INT(a_v)) return Qfalse; /* Widths not <= */
|
201
|
+
|
202
|
+
uint8_t *sp = (uint8_t *)RSTRING_PTR(rb_iv_get(self, "@addr"));
|
203
|
+
uint8_t *ap = (uint8_t *)RSTRING_PTR(rb_iv_get(v_arg, "@addr"));
|
204
|
+
int j, r;
|
205
|
+
uint8_t xor;
|
206
|
+
for (j = 0; j != nb; ++j)
|
207
|
+
if (ap[j] != sp[j]) break;
|
208
|
+
r = j*8;
|
209
|
+
if (r >= s_len) return Qtrue; /* They differ at or after min_len */
|
210
|
+
xor = ap[j] ^ sp[j];
|
211
|
+
while ((xor & 0x80) == 0) {
|
212
|
+
r += 1; xor <<= 1;
|
213
|
+
}
|
214
|
+
return r >= s_len ? Qtrue : Qfalse; /* first_bit_different > width */
|
215
|
+
}
|
216
|
+
|
217
|
+
/*
|
218
|
+
* call-seq:
|
219
|
+
* equals?(otherPrefix) -> (true,false)
|
220
|
+
*
|
221
|
+
* Returns true if self's addr, version, and length match otherPrefix.
|
222
|
+
*/
|
223
|
+
static VALUE pr_equal(VALUE self, VALUE v_arg) {
|
224
|
+
int v = FIX2INT(rb_iv_get(self, "@version"));
|
225
|
+
if (v != FIX2INT(rb_iv_get(v_arg, "@version"))) rb_raise(rb_eArgError,
|
226
|
+
"IPprefix.equal: Versions must be same version (4 or 6)");
|
227
|
+
VALUE s_v = rb_iv_get(self, "@length");
|
228
|
+
VALUE a_v = rb_iv_get(v_arg, "@length");
|
229
|
+
if (s_v == Qnil || a_v == Qnil) rb_raise(rb_eArgError,
|
230
|
+
"IPprefix.equal: either or both length nil");
|
231
|
+
int s_len = FIX2INT(s_v);
|
232
|
+
if (s_len != FIX2INT(a_v)) return Qfalse;
|
233
|
+
int nb = v == 4 ? IP4_ADDR_LEN : IP6_ADDR_LEN;
|
234
|
+
uint8_t *sp = (uint8_t *)RSTRING_PTR(rb_iv_get(self, "@addr"));
|
235
|
+
uint8_t *ap = (uint8_t *)RSTRING_PTR(rb_iv_get(v_arg, "@addr"));
|
236
|
+
int j;
|
237
|
+
for (j = 0; j != nb; ++j)
|
238
|
+
if (ap[j] != sp[j]) break;
|
239
|
+
if (j == nb) return Qtrue; /* All bits same */
|
240
|
+
return ((ap[j] ^ sp[j]) & p_mask[s_len%8]) == 0 ? Qtrue : Qfalse;
|
241
|
+
}
|
242
|
+
|
243
|
+
/*
|
244
|
+
* call-seq:
|
245
|
+
* first_bit_different(otherPrefix) -> Integer
|
246
|
+
*
|
247
|
+
* Returns an Integer, the (0-origin) bit position where the two IPprefixes are different.
|
248
|
+
*/
|
249
|
+
static VALUE pr_first_bit_differ(VALUE self, VALUE v_arg) {
|
250
|
+
int v = FIX2INT(rb_iv_get(self, "@version"));
|
251
|
+
if (v != FIX2INT(rb_iv_get(v_arg, "@version"))) rb_raise(rb_eArgError,
|
252
|
+
"IPprefix.first_bit_different: Versions must be same version (4 or 6)");
|
253
|
+
int nb = v == 4 ? IP4_ADDR_LEN : IP6_ADDR_LEN;
|
254
|
+
VALUE s_v = rb_iv_get(self, "@length");
|
255
|
+
VALUE a_v = rb_iv_get(v_arg, "@length");
|
256
|
+
if (s_v == Qnil || a_v == Qnil) rb_raise(rb_eArgError,
|
257
|
+
"IPprefix.first_bit_different: either or both length is nil");
|
258
|
+
int s_len = FIX2INT(s_v), a_len = FIX2INT(a_v);
|
259
|
+
uint8_t *sp = (uint8_t *)RSTRING_PTR(rb_iv_get(self, "@addr"));
|
260
|
+
uint8_t *ap = (uint8_t *)RSTRING_PTR(rb_iv_get(v_arg, "@addr"));
|
261
|
+
int min_len = s_len < a_len ? s_len : a_len;
|
262
|
+
int j, r;
|
263
|
+
uint8_t xor;
|
264
|
+
for (j = 0; j != nb; ++j)
|
265
|
+
if (ap[j] != sp[j]) break;
|
266
|
+
if (j*8 >= min_len) /* They differ at or after min_len */
|
267
|
+
return INT2NUM(min_len);
|
268
|
+
xor = ap[j] ^ sp[j];
|
269
|
+
r = j*8;
|
270
|
+
while ((xor & 0x80) == 0) {
|
271
|
+
r += 1; xor <<= 1;
|
272
|
+
}
|
273
|
+
return INT2FIX(r >= min_len ? min_len : r);
|
274
|
+
}
|
275
|
+
|
276
|
+
/*
|
277
|
+
* call-seq:
|
278
|
+
* bit_set?(bit) -> (true,false)
|
279
|
+
*
|
280
|
+
* Returns true if a certain bit (specified as an Integer offset) is set.
|
281
|
+
*/
|
282
|
+
static VALUE pr_bit_set(VALUE self, VALUE v_arg) {
|
283
|
+
VALUE v = FIX2INT(rb_iv_get(self, "@version"));
|
284
|
+
int last_bit = v == 4 ? IP4_LAST_BIT : IP6_LAST_BIT;
|
285
|
+
int n = NUM2INT(v_arg); /* 0-org */
|
286
|
+
if (n < 0) /* Special case: <root> node has bit_index -1 */
|
287
|
+
/* Always returns true, so <root> stays at top of the tree */
|
288
|
+
return Qtrue;
|
289
|
+
else if (n > last_bit) /* Past last byte of key (string), always false */
|
290
|
+
return Qfalse;
|
291
|
+
else {
|
292
|
+
uint8_t *ap = (uint8_t *)RSTRING_PTR(rb_iv_get(self, "@addr"));
|
293
|
+
return(ap[n/8] & b_mask[n%8]) != 0 ? Qtrue : Qfalse;
|
294
|
+
}
|
295
|
+
}
|
296
|
+
|
297
|
+
/*
|
298
|
+
* Returns the complement of an IPprefix, i.e. one having the same length, but all bits the ones-complement of those in IPprefix.
|
299
|
+
*/
|
300
|
+
static VALUE pr_complement(VALUE self) {
|
301
|
+
VALUE ver = rb_iv_get(self, "@version");
|
302
|
+
int nb = FIX2INT(ver) == 4 ? IP4_ADDR_LEN : IP6_ADDR_LEN;
|
303
|
+
uint8_t *sp = (uint8_t *)RSTRING_PTR(rb_iv_get(self, "@addr"));
|
304
|
+
VALUE len = rb_iv_get(self, "@length");
|
305
|
+
uint8_t a[IP6_ADDR_LEN]; int j;
|
306
|
+
for (j = 0; j != nb; ++j) a[j] = ~sp[j];
|
307
|
+
VALUE rk_argv[] = { ver, rb_str_new((char *)a, nb), len };
|
308
|
+
return rb_class_new_instance(3, rk_argv, ipPref);
|
309
|
+
}
|
310
|
+
|
311
|
+
static char *strmov(char *d, char *s) {
|
312
|
+
while (*s != '\0') *d++ = *s++;
|
313
|
+
return d;
|
314
|
+
}
|
315
|
+
|
316
|
+
static char v6a[60];
|
317
|
+
static char *v6addr_to_s(uint8_t *in6a)
|
318
|
+
{ /* Returns pointer to next byte in v6a
|
319
|
+
Code from NeTraMet's nmc_pars.c */
|
320
|
+
char buf[10]; /* RFC 2373: IPv6 Address Architecture */
|
321
|
+
char *d = v6a;
|
322
|
+
char *a = (char *)in6a;
|
323
|
+
int j, k, st,len, stx,lenx;
|
324
|
+
uint32_t v, a2[8];
|
325
|
+
|
326
|
+
stx = st = len = lenx = 0;
|
327
|
+
for (k = j = 0; j != 16; j += 2) {
|
328
|
+
v = ntohs(*(uint16_t *)&a[j]);
|
329
|
+
a2[k++] = v; /* Build array of two-byte pairs */
|
330
|
+
if (v == 0) ++len;
|
331
|
+
else {
|
332
|
+
if (len > lenx) { /* Find longest run of zero pairs */
|
333
|
+
stx = st; lenx = len;
|
334
|
+
}
|
335
|
+
st = k; len = 0;
|
336
|
+
}
|
337
|
+
}
|
338
|
+
|
339
|
+
if (len > lenx) {
|
340
|
+
stx = st; lenx = len;
|
341
|
+
}
|
342
|
+
if (lenx != 0 && stx == 0) { /* Longest run at left */
|
343
|
+
d = strmov(d, ":"); j = lenx;
|
344
|
+
}
|
345
|
+
else {
|
346
|
+
sprintf(buf, "%x", a2[0]);
|
347
|
+
d = strmov(d,buf); j = 1;
|
348
|
+
}
|
349
|
+
for (; j < 8; ) {
|
350
|
+
if (lenx != 0 && j == stx) {
|
351
|
+
d = strmov(d,":"); j += lenx;
|
352
|
+
}
|
353
|
+
else {
|
354
|
+
sprintf(buf, ":%x", a2[j]);
|
355
|
+
d = strmov(d, buf); ++j;
|
356
|
+
}
|
357
|
+
}
|
358
|
+
if (j == stx+lenx) d = strmov(d, ":"); /* Longest run at right */
|
359
|
+
*d = '\0';
|
360
|
+
return d;
|
361
|
+
}
|
362
|
+
|
363
|
+
/*
|
364
|
+
* Returns a String with a human readable prefix and length in CIDR notation.
|
365
|
+
*/
|
366
|
+
static VALUE pr_to_s(VALUE self) {
|
367
|
+
int ver = FIX2INT(rb_iv_get(self, "@version"));
|
368
|
+
VALUE addr = rb_iv_get(self, "@addr");
|
369
|
+
uint8_t *ap = (uint8_t *)RSTRING_PTR(addr);
|
370
|
+
VALUE length = rb_iv_get(self, "@length");
|
371
|
+
int w = length == Qnil ? -1 : FIX2INT(length);
|
372
|
+
|
373
|
+
if (ver == 4) {
|
374
|
+
if (w < 0) sprintf(v6a, "%u.%u.%u.%u",
|
375
|
+
ap[0],ap[1],ap[2],ap[3]);
|
376
|
+
else sprintf(v6a, "%u.%u.%u.%u/%u",
|
377
|
+
ap[0],ap[1],ap[2],ap[3], w);
|
378
|
+
}
|
379
|
+
else {
|
380
|
+
char *v6e = v6addr_to_s(ap);
|
381
|
+
if (w >= 0) sprintf(v6e, "/%u", w);
|
382
|
+
}
|
383
|
+
return rb_str_new2(v6a);
|
384
|
+
}
|
385
|
+
|
386
|
+
static uint16_t get_nbr(char **str, int *rem, int *base) {
|
387
|
+
char *s = *str;
|
388
|
+
int len = *rem, b = *base, j,k, c, n;
|
389
|
+
|
390
|
+
for (j = 0; j != len; ++j) {
|
391
|
+
c = s[j];
|
392
|
+
if (c == '.') {
|
393
|
+
if (!b) b = 10; break;
|
394
|
+
if (b == 16) rb_raise(rb_eArgError,
|
395
|
+
"IPprefix.from_s: can't have . in IPv6 address!");
|
396
|
+
}
|
397
|
+
if (c == ':') {
|
398
|
+
if (!b) b = 16; break;
|
399
|
+
if (b == 10) rb_raise(rb_eArgError,
|
400
|
+
"IPprefix.from_s: can't have : in IPv4 address!");
|
401
|
+
}
|
402
|
+
if (c == '/') break;
|
403
|
+
if (!isdigit(c) && isxdigit(c)) {
|
404
|
+
if (!b) { b = 16; }
|
405
|
+
}
|
406
|
+
if (b == 10 && !isdigit(c)) rb_raise(rb_eArgError,
|
407
|
+
"IPprefix.from_s: non-decimal digit in IPv4 address!");
|
408
|
+
else if (!isxdigit(c)) rb_raise(rb_eArgError,
|
409
|
+
"IPprefix.from_s: non-hex digit in IPv6 address!");
|
410
|
+
if (!isxdigit(c)) {
|
411
|
+
rb_raise(rb_eArgError,
|
412
|
+
"IPprefix.from_s: non-(hex) digit found!");
|
413
|
+
}
|
414
|
+
}
|
415
|
+
|
416
|
+
for (n = k = 0; k != j; ++k) {
|
417
|
+
c = s[k];
|
418
|
+
if (c >= '0' && c <= '9') n = n*b + (c-'0');
|
419
|
+
else if (c >= 'a' && c <= 'f') n = n*b + (10 + c-'a');
|
420
|
+
else n = n*b + (10 + c-'A');
|
421
|
+
}
|
422
|
+
|
423
|
+
*str = &s[j]; *rem = len-j; *base = b;
|
424
|
+
return n;
|
425
|
+
}
|
426
|
+
|
427
|
+
/*
|
428
|
+
* call-seq:
|
429
|
+
* from_s(string) -> IPprefix
|
430
|
+
*
|
431
|
+
* Parses str to find version and address, e.g.
|
432
|
+
* p = IPprefix::from_s('192.168.1.1')
|
433
|
+
* p = IPprefix.from_s('fe80::20d:60ff:fe38:18b/64')
|
434
|
+
*/
|
435
|
+
static VALUE pr_from_s(VALUE self, VALUE v_str) {
|
436
|
+
VALUE pa[3] = { Qnil, Qnil, Qnil };
|
437
|
+
char *str = RSTRING_PTR(v_str), *sp;
|
438
|
+
int len = RSTRING_LEN(v_str);
|
439
|
+
int base, n, x, dcx, havedcx, y, argc;
|
440
|
+
char a[16], *a2p; uint16_t a2[8];
|
441
|
+
|
442
|
+
memset(a, 0, sizeof(a));
|
443
|
+
sp = str;
|
444
|
+
havedcx = dcx = 0;
|
445
|
+
if (sp[0] == ':') {
|
446
|
+
if (sp[1] == ':') {
|
447
|
+
base = 16; havedcx = 1; dcx = -1;
|
448
|
+
sp += 2; len -= 2;
|
449
|
+
}
|
450
|
+
else rb_raise(rb_eArgError,
|
451
|
+
"IPprefix.from_s: can't start an IPv6 address with : !");
|
452
|
+
}
|
453
|
+
else {
|
454
|
+
base = 0; havedcx = 0;
|
455
|
+
}
|
456
|
+
|
457
|
+
n = get_nbr(&sp, &len, &base);
|
458
|
+
|
459
|
+
if (base == 10) { /* IPv4 prefix */
|
460
|
+
for (x = 0; x != 4; ++x) {
|
461
|
+
if (n > 255) rb_raise(rb_eArgError,
|
462
|
+
"IPprefix.from_s: integer > 255 in IPv4 address!");
|
463
|
+
a[x] = n;
|
464
|
+
if (len == 0) break;
|
465
|
+
sp += 1; len -= 1;
|
466
|
+
n = get_nbr(&sp, &len, &base);
|
467
|
+
if (len == 0 || *sp == '/') {
|
468
|
+
a[x+1] = n; break;
|
469
|
+
}
|
470
|
+
}
|
471
|
+
pa[0] = INT2FIX(4);
|
472
|
+
pa[1] = rb_str_new(a, 4);
|
473
|
+
if (len == 0) argc = 2;
|
474
|
+
else if (*sp == '/') {
|
475
|
+
argc = 3;
|
476
|
+
sp += 1; len -= 1;
|
477
|
+
n = get_nbr(&sp, &len, &base);
|
478
|
+
if (n > 32) rb_raise(rb_eArgError,
|
479
|
+
"IPprefix.from_s: IPv4 prefix length > 32!");
|
480
|
+
pa[2] = INT2FIX(n);
|
481
|
+
}
|
482
|
+
else rb_raise(rb_eArgError,
|
483
|
+
"IPprefix.from_s: more than 4 integers in IPv4 address!");
|
484
|
+
}
|
485
|
+
else if (base == 16) { /* IPv6 prefix */
|
486
|
+
memset(a2, 0, sizeof(a2));
|
487
|
+
for (x = 0; x != 8; ++x) {
|
488
|
+
if (n > 0xFFFF) rb_raise(rb_eArgError,
|
489
|
+
"IPprefix.from_s: integer > 0xFFFF in IPv6 address!");
|
490
|
+
a2[x] = ntohs((uint16_t)n); /* Nathan, 13 Aug 09 */
|
491
|
+
if (len == 0 || *sp == '/') {
|
492
|
+
/* x += 1; a2[x] = ntohs((uint16_t)n); Nathan, 13 Aug 09 */
|
493
|
+
break;
|
494
|
+
}
|
495
|
+
sp += 1; len -= 1; /* Skip the delimiter */
|
496
|
+
n = get_nbr(&sp, &len, &base);
|
497
|
+
if (sp[1] == ':') {
|
498
|
+
if (havedcx) rb_raise(rb_eArgError,
|
499
|
+
"IPprefix.from_s: can only have one :: in an IPv6 address!");
|
500
|
+
dcx = x+1; havedcx = 1;
|
501
|
+
sp += 1; len -= 1;
|
502
|
+
}
|
503
|
+
}
|
504
|
+
a2p = (char *)a2;
|
505
|
+
if (!havedcx) memcpy(a, a2p, 16);
|
506
|
+
else {
|
507
|
+
if (dcx >= 0) memcpy(a, a2p, (dcx+1)*2);
|
508
|
+
y = (x-dcx)*2;
|
509
|
+
memcpy(a + (16-y), a2p + (dcx+1)*2, y);
|
510
|
+
}
|
511
|
+
pa[0] = INT2FIX(6);
|
512
|
+
pa[1] = rb_str_new(a, 16);
|
513
|
+
if (len == 0) argc = 2;
|
514
|
+
else if (*sp == '/') {
|
515
|
+
argc = 3;
|
516
|
+
sp += 1; len -= 1; base = 10;
|
517
|
+
n = get_nbr(&sp, &len, &base);
|
518
|
+
if (n > 128) rb_raise(rb_eArgError,
|
519
|
+
"IPprefix.from_s: IPv6 prefix length > 128!");
|
520
|
+
pa[2] = INT2FIX(n);
|
521
|
+
}
|
522
|
+
else rb_raise(rb_eArgError,
|
523
|
+
"IPprefix.from_s: more than 8 hex numbers in IPv6 address!");
|
524
|
+
}
|
525
|
+
else rb_raise(rb_eArgError,
|
526
|
+
"IPprefix.from_s: can't tell whether address is IPv4 or IPv6!");
|
527
|
+
|
528
|
+
VALUE vp = rb_class_new_instance(argc, pa, ipPref);
|
529
|
+
return vp;
|
530
|
+
}
|
531
|
+
|
532
|
+
void Init_IPprefix() {
|
533
|
+
ipPref = rb_define_class("IPprefix", rb_cObject);
|
534
|
+
/* from_s is a class function */
|
535
|
+
rb_define_module_function(ipPref, "from_s", pr_from_s, 1);
|
536
|
+
|
537
|
+
rb_define_method(ipPref, "initialize", pr_init, -1);
|
538
|
+
rb_define_method(ipPref, "version", pr_version, 0);
|
539
|
+
rb_define_method(ipPref, "addr", pr_addr, 0);
|
540
|
+
rb_define_method(ipPref, "length", pr_length, 0); /* ||k|| */
|
541
|
+
rb_define_method(ipPref, "length=", pr_set_length, 1);
|
542
|
+
rb_define_method(ipPref, "==", pr_addr_equal, 1); /* Only compare addrs */
|
543
|
+
rb_define_method(ipPref, "<=>", pr_addr_compare, 1);
|
544
|
+
rb_define_method(ipPref, "to_s", pr_to_s, 0);
|
545
|
+
|
546
|
+
/* Following methods check that length is non-nil (for IP prefix testing) */
|
547
|
+
rb_define_method(ipPref, "width", pr_width, 0); /* |k| = length-1 */
|
548
|
+
rb_define_method(ipPref, "prefix?", pr_preceq, 1);
|
549
|
+
rb_define_method(ipPref, "equal", pr_equal, 1);
|
550
|
+
rb_define_method(ipPref, "bit_set?", pr_bit_set, 1);
|
551
|
+
rb_define_method(ipPref, "first_bit_different", pr_first_bit_differ, 1);
|
552
|
+
rb_define_method(ipPref, "complement", pr_complement, 0);
|
553
|
+
|
554
|
+
/* Define the attributes */
|
555
|
+
rb_define_attr(ipPref, "addr", 1,0);
|
556
|
+
rb_define_attr(ipPref, "version", 1,0);
|
557
|
+
rb_define_attr(ipPref, "length", 1,1);
|
558
|
+
|
559
|
+
}
|
data/ext/extconf.rb
ADDED
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: IPprefix
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Nathan Ward
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-02-12 00:00:00 +13:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: Includes the IPprefix class, a fast class for handling IP prefixes.
|
23
|
+
email:
|
24
|
+
- nward@braintrust.co.nz
|
25
|
+
executables: []
|
26
|
+
|
27
|
+
extensions:
|
28
|
+
- ext/extconf.rb
|
29
|
+
extra_rdoc_files: []
|
30
|
+
|
31
|
+
files:
|
32
|
+
- Gemfile
|
33
|
+
- IPprefix.gemspec
|
34
|
+
- README
|
35
|
+
- Rakefile
|
36
|
+
- ext/IPprefix.c
|
37
|
+
- ext/extconf.rb
|
38
|
+
has_rdoc: true
|
39
|
+
homepage: http://github.com/nward/IPprefix
|
40
|
+
licenses: []
|
41
|
+
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
none: false
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
hash: 3
|
53
|
+
segments:
|
54
|
+
- 0
|
55
|
+
version: "0"
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 23
|
62
|
+
segments:
|
63
|
+
- 1
|
64
|
+
- 3
|
65
|
+
- 6
|
66
|
+
version: 1.3.6
|
67
|
+
requirements: []
|
68
|
+
|
69
|
+
rubyforge_project:
|
70
|
+
rubygems_version: 1.3.7
|
71
|
+
signing_key:
|
72
|
+
specification_version: 3
|
73
|
+
summary: Native, fast IP prefix handling code
|
74
|
+
test_files: []
|
75
|
+
|