subnets 1.0.0pre

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9ca7677ed446bf92038930c57e31d26878b05fe6dc7c6fa4cf5f731ec58a5340
4
+ data.tar.gz: 3d82804a72f2181c5036d69aad617c927bac2eb64ee4fe496d822ca6a006a043
5
+ SHA512:
6
+ metadata.gz: 1f152961d4118eac6bb2bfafb1f65bdc94ee971b6ff0215542790a446da7e3c3e09d35278dcbfbd8e918f5defad442dbf38d3cb067f39521a3e9fa67ae86d853
7
+ data.tar.gz: 2655d78b2dbf09510a28cdabaea00c572c5038889b7724c34fa96e2f1bb72319831c468b57caf39055ddae8d106ff91a61e7695d9dd3762d6fbe92c7f7ac6f4b
data/COPYING ADDED
@@ -0,0 +1,30 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2018 Raise Marketplace, Inc. https://www.raise.com/
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ ------------------------------------------------------------
24
+
25
+ Portions of this software are derived from third-party works licensed
26
+ under terms compatible with the above MIT license:
27
+
28
+ read_ipv4 function is Copyright © 2005-2014 Rich Felker, et al. and
29
+ licensed under the MIT license (see ext/subnets/ipaddr.c). The
30
+ function is a modified version of part of inet_pton from musl libc.
data/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # Subnets
2
+
3
+ Subnets is a C-extension for Ruby with IPv4 and IPv6 address and
4
+ network classes. The motivating goal is to provide a fast test whether
5
+ a given set of subnets includes a given IP.
6
+
7
+ ```ruby
8
+ Subnets.include?(subnets, '192.168.1.1')
9
+ ```
10
+
11
+ [Rack::Request#ip](https://github.com/rack/rack/blob/2.0.4/lib/rack/request.rb#L419-L421)
12
+ and
13
+ [ActionDispatch::Request#remote_ip](https://github.com/rails/rails/blob/v5.2.0/actionpack/lib/action_dispatch/middleware/remote_ip.rb#L176-L179)
14
+ perform such a test. `ActionDispatch`, which uses `ipaddr` by default,
15
+ explicitly calls out that the technique is too slow to run on every
16
+ request. Rack uses a regular expression of IPv4 and IPv6 private
17
+ address spaces which, while comparably fast, is not easily extended to
18
+ support arbitrary subnets.
19
+
20
+ (See also [this answer explaining request.ip
21
+ vs. request.remote_ip](https://stackoverflow.com/a/43014286/454156))
22
+
23
+ A [benchmark](test/private_networks_benchmark.rb) tests if a random IP
24
+ (75% private IPs) is within any of the private IPv4 ranges. Lower is
25
+ better. Plotted on logscale. (Ruby 2.5.0p0, 2.5 GHz Intel Core i7).
26
+
27
+ ```
28
+ $ bundle exec rake benchmark TEST=test/private_networks_benchmark
29
+
30
+ ipaddr : 46.25us/ip ██████████████████████████████████▋
31
+ ipaddress : 63.88us/ip ██████████████████████████████████████▏
32
+ netaddr : 31.03us/ip ██████████████████████████████▎
33
+ *subnets : 4.19us/ip ████████▏
34
+ rack (regexp) : 5.25us/ip ██████████▋
35
+ ' ' ' ' '
36
+ 2 5 10 20 50
37
+ ```
38
+
39
+ ## Usage
40
+
41
+ ```ruby
42
+ require 'subnets'
43
+
44
+ subnets = %w(127.0.0.1/32 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
45
+ ::1/128 fc00::/7).map(&Subnets.method(:parse))
46
+
47
+ Subnets.include?(subnets, '192.168.1.1') #=> true
48
+ Subnets.include?(subnets, '203.0.113.12') #=> false
49
+ ```
50
+
51
+ ## Similar Gems
52
+
53
+ There are several IP gems, all of which are implemented in pure-Ruby
54
+ and not performance oriented.
55
+
56
+ - [ipaddr](https://github.com/ruby/ipaddr): (Ruby stdlib) A set of
57
+ methods to manipulate an IP address. Both IPv4 and IPv6 are
58
+ supported.
59
+ - [ipaddress](https://github.com/ipaddress-gem/ipaddress): A Ruby
60
+ library designed to make the use of IPv4 and IPv6 addresses simple,
61
+ powerful and enjoyable. It provides a complete set of methods to
62
+ handle IP addresses for any need, from simple scripting to full
63
+ network design.
64
+ - [netaddr](https://github.com/dspinhirne/netaddr-rb): A Ruby library
65
+ for performing calculations on IPv4 and IPv6 subnets. There is also
66
+ limited support for EUI addresses.
67
+
68
+ ## Production Ready?
69
+
70
+ This has not been used in production.
71
+
72
+ ## Safe?
73
+
74
+ The IPv4 and IPv6 parsers are written in C. In addition to the unit
75
+ test suite, the parsers have had minimal (8+ hours) of fuzzing with
76
+ [American fuzzy lop](http://lcamtuf.coredump.cx/afl/). There is low
77
+ confidence in the parsers.
78
+
79
+ ## Correct?
80
+
81
+ The unit test suite tests parsing of a variety of valid and invalid
82
+ IPv4 and IPv6 networks.
83
+
84
+ ## Fast?
85
+
86
+ Yes, for checking if an array of subnets includes a given IP at least.
data/ext/subnets/ext.c ADDED
@@ -0,0 +1,1007 @@
1
+ #include "ruby.h"
2
+
3
+ #include <stdio.h>
4
+
5
+ #include "ipaddr.h"
6
+
7
+ VALUE Subnets = Qnil;
8
+ VALUE IP = Qnil;
9
+ VALUE IP4 = Qnil;
10
+ VALUE IP6 = Qnil;
11
+ VALUE Net = Qnil;
12
+ VALUE Net4 = Qnil;
13
+ VALUE Net6 = Qnil;
14
+
15
+ VALUE rb_intern_hash = Qnil;
16
+ VALUE rb_intern_xor = Qnil;
17
+
18
+ #define hash(o) rb_funcall(o, rb_intern_hash, 0)
19
+ #define xor(a,b) rb_funcall(a, rb_intern_xor, 1, b)
20
+
21
+ #define assert_kind_of(obj, kind) do { \
22
+ if (!rb_obj_is_kind_of(obj, kind)) { \
23
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected " #kind ")", rb_obj_classname(obj)); \
24
+ } \
25
+ } while (0)
26
+
27
+ /**
28
+ * ParseError indicates the input string could not be parsed as the
29
+ * requested type.
30
+ */
31
+ VALUE ParseError = Qnil;
32
+
33
+ VALUE
34
+ raise_parse_error(const char *type, const char *data) {
35
+ /* 49 is longest possible ip6 cidr */
36
+ /* ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255/128 */
37
+ if (strlen(data) > 49) {
38
+ rb_raise(ParseError, "failed to parse as %s: '%.45s...'", type, data);
39
+ } else {
40
+ rb_raise(ParseError, "failed to parse as %s: '%s'", type, data);
41
+ }
42
+ }
43
+
44
+ VALUE
45
+ ip4_new(VALUE class, ip4_t src) {
46
+ ip4_t *ip;
47
+ VALUE v = Data_Make_Struct(class, ip4_t, 0, free, ip);
48
+ *ip = src;
49
+ rb_obj_call_init(v, 0, 0);
50
+ return v;
51
+ }
52
+
53
+ VALUE
54
+ ip6_new(VALUE class, ip6_t src) {
55
+ ip6_t *ip;
56
+ VALUE v = Data_Make_Struct(class, ip6_t, 0, free, ip);
57
+ *ip = src;
58
+ rb_obj_call_init(v, 0, 0);
59
+ return v;
60
+ }
61
+
62
+ VALUE
63
+ net4_new(VALUE class, net4_t src) {
64
+ net4_t *net;
65
+ VALUE rbnet = Data_Make_Struct(class, net4_t, 0, free, net);
66
+ *net = src;
67
+ rb_obj_call_init(rbnet, 0, 0);
68
+ return rbnet;
69
+ }
70
+
71
+ VALUE
72
+ net6_new(VALUE class, net6_t src) {
73
+ net6_t *net;
74
+ VALUE rbnet = Data_Make_Struct(class, net6_t, 0, free, net);
75
+ *net = src;
76
+ rb_obj_call_init(rbnet, 0, 0);
77
+ return rbnet;
78
+ }
79
+
80
+ VALUE
81
+ method_ip4_new(VALUE class, VALUE address) {
82
+ return ip4_new(class, RB_NUM2UINT(address));
83
+ }
84
+
85
+ VALUE
86
+ method_net4_new(VALUE class, VALUE address, VALUE prefixlen) {
87
+ net4_t net;
88
+ net.address = RB_NUM2UINT(address);
89
+ net.prefixlen = NUM2INT(prefixlen);
90
+ if (!(net.prefixlen >= 0 && net.prefixlen <= 32)) {
91
+ rb_raise(rb_eArgError, "prefixlen must be in range [0,32], was %d", net.prefixlen);
92
+ }
93
+ net.mask = mk_mask4(net.prefixlen);
94
+ return net4_new(class, net);
95
+ }
96
+
97
+ VALUE
98
+ method_net6_new(VALUE class, VALUE hextets, VALUE prefixlen) {
99
+ net6_t net;
100
+
101
+ if (RARRAY_LEN(hextets) != 8) {
102
+ rb_raise(rb_eArgError, "hextets must be size=8, was %ld", RARRAY_LEN(hextets));
103
+ }
104
+
105
+ for (ssize_t i = 0; i < RARRAY_LEN(hextets); i++) {
106
+ net.address.x[i] = NUM2INT(RARRAY_AREF(hextets, i)) & 0xffff;
107
+ }
108
+
109
+ net.prefixlen = NUM2INT(prefixlen);
110
+ if (!(net.prefixlen >= 0 && net.prefixlen <= 128)) {
111
+ rb_raise(rb_eArgError, "prefixlen must be in range [0,128], was %d", net.prefixlen);
112
+ }
113
+
114
+ net.mask = mk_mask6(net.prefixlen);
115
+
116
+ return net6_new(class, net);
117
+ }
118
+
119
+ /**
120
+ * Parse +s+ as an IPv4 network in CIDR notation.
121
+ *
122
+ * @param [String] s
123
+ * @return {Net4}
124
+ * @raise {Subnets::ParseError}
125
+ */
126
+ VALUE
127
+ method_net4_parse(VALUE class, VALUE s) {
128
+ const char *buf = StringValueCStr(s);
129
+
130
+ net4_t net;
131
+ if (!read_net4_strict(buf, &net)) {
132
+ raise_parse_error("net4", buf);
133
+ }
134
+ return net4_new(class, net);
135
+ }
136
+
137
+ /**
138
+ * Parse +s+ as an IPv6 network in CIDR notation. Handles the
139
+ * following formats:
140
+ *
141
+ * - x:x:x:x:x:x:x:x consisting of eight hexadecimal numbers of one to
142
+ * four digits (16 bits) separated by colons
143
+ *
144
+ * - x:x:x::x:x:x as above, but a single double-colon replaces two or
145
+ * more repeated zeros
146
+ *
147
+ * - x:x:x:x:x:x:d.d.d.d consisting of a colon-separated hexadecimal
148
+ * portion as above defining up to six hextets, followed by
149
+ * dot-separated decimal numbers 0-255 in typical IPv4 format.
150
+ *
151
+ * @param [String] s
152
+ * @return {Net6}
153
+ * @raise {Subnets::ParseError}
154
+ */
155
+ VALUE
156
+ method_net6_parse(VALUE class, VALUE s) {
157
+ const char *buf = StringValueCStr(s);
158
+
159
+ net6_t net;
160
+ if (!read_net6_strict(buf, &net)) {
161
+ raise_parse_error("net6", buf);
162
+ }
163
+ return net6_new(class, net);
164
+ }
165
+
166
+ /**
167
+ * @overload random(rng=Random.new)
168
+ * @param rng [#rand] (optional) a random number generator
169
+ * @return [IP4] a random IP4 address
170
+ */
171
+ VALUE
172
+ method_ip4_random(int argc, VALUE *argv, VALUE class) {
173
+ ip4_t ip;
174
+ VALUE rng;
175
+
176
+ rb_scan_args(argc, argv, "01", &rng);
177
+ if (Qnil == rng) {
178
+ rng = rb_funcall(rb_cRandom, rb_intern("new"), 0);
179
+ }
180
+
181
+ VALUE rand = rb_intern("rand");
182
+ ip = FIX2INT(rb_funcall(rng, rand, 1, INT2FIX(0xffff+1)));
183
+ ip |= FIX2INT(rb_funcall(rng, rand, 1, INT2FIX(0xffff+1))) << 16;
184
+
185
+ return ip4_new(class, ip);
186
+ }
187
+
188
+ /**
189
+ * @overload random(rng=Random.new)
190
+ * @param rng [#rand] (optional) a random number generator
191
+ * @return [Net4] a random Net4 address
192
+ */
193
+ VALUE
194
+ method_net4_random(int argc, VALUE *argv, VALUE class) {
195
+ net4_t net;
196
+ VALUE rng;
197
+
198
+ rb_scan_args(argc, argv, "01", &rng);
199
+ if (Qnil == rng) {
200
+ rng = rb_funcall(rb_cRandom, rb_intern("new"), 0);
201
+ }
202
+
203
+ VALUE rand = rb_intern("rand");
204
+ net.address = FIX2INT(rb_funcall(rng, rand, 1, INT2FIX(0xffff+1)));
205
+ net.address |= FIX2INT(rb_funcall(rng, rand, 1, INT2FIX(0xffff+1))) << 16;
206
+ net.prefixlen = FIX2INT(rb_funcall(rng, rb_intern("rand"), 1, INT2FIX(32+1)));
207
+ net.mask = mk_mask4(net.prefixlen);
208
+
209
+ return net4_new(class, net);
210
+ }
211
+
212
+ void
213
+ ip6_fill_random(ip6_t *ip, VALUE rng, VALUE opts) {
214
+ if (Qnil == rng) {
215
+ rng = rb_funcall(rb_cRandom, rb_intern("new"), 0);
216
+ }
217
+
218
+ VALUE rand = rb_intern("rand");
219
+ int pre, zeros;
220
+
221
+ if (Qnil != opts && RTEST(rb_hash_aref(opts, ID2SYM(rb_intern("zeros"))))) {
222
+ pre = FIX2INT(rb_funcall(rng, rand, 1, INT2FIX(8+1)));
223
+ zeros = FIX2INT(rb_funcall(rng, rand, 1, INT2FIX(8+1 - pre)));
224
+ } else {
225
+ pre = 8;
226
+ zeros = 0;
227
+ }
228
+
229
+ for (int i=0; i<pre; i++) {
230
+ ip->x[i] = FIX2INT(rb_funcall(rng, rand, 1, INT2FIX(0xffff+1)));
231
+ }
232
+ for (int i=pre; i<pre+zeros; i++) {
233
+ ip->x[i] = 0;
234
+ }
235
+ for (int i=pre+zeros; i<8; i++) {
236
+ ip->x[i] = FIX2INT(rb_funcall(rng, rand, 1, INT2FIX(0xffff+1)));
237
+ }
238
+ }
239
+
240
+ /**
241
+ * @overload random(rand=Random.new, opts={})
242
+ * @param rand [#rand] (optional) a random number generator
243
+ * @param opts [Hash]
244
+ * @option opts [Boolean] :zeros include a random string of zeros at a random position rather than simply randomizing the entire address
245
+ * @return [IP6] a random IP6 address
246
+ */
247
+ VALUE
248
+ method_ip6_random(int argc, VALUE *argv, VALUE class) {
249
+ ip6_t ip;
250
+ VALUE rng;
251
+ VALUE opts;
252
+
253
+ rb_scan_args(argc, argv, "01:", &rng, &opts);
254
+ ip6_fill_random(&ip, rng, opts);
255
+ return ip6_new(class, ip);
256
+ }
257
+
258
+ /**
259
+ * @overload random(rand=Random.new, opts={})
260
+ * @param rand [#rand] (optional) a random number generator
261
+ * @param opts [Hash]
262
+ * @option opts [Boolean] :zeros include a random string of zeros at a random position rather than simply randomizing the entire address
263
+ * @return [Net6] a random Net6
264
+ */
265
+ VALUE
266
+ method_net6_random(int argc, VALUE *argv, VALUE class) {
267
+ net6_t net;
268
+ VALUE rng;
269
+ VALUE opts;
270
+
271
+ rb_scan_args(argc, argv, "01:", &rng, &opts);
272
+ ip6_fill_random(&net.address, rng, opts);
273
+ net.prefixlen = FIX2INT(rb_funcall(rng, rb_intern("rand"), 1, INT2FIX(128+1)));
274
+ net.mask = mk_mask6(net.prefixlen);
275
+
276
+ return net6_new(class, net);
277
+ }
278
+
279
+ VALUE
280
+ method_ip4_not(VALUE self) {
281
+ ip4_t *ip;
282
+ Data_Get_Struct(self, ip4_t, ip);
283
+ return ip4_new(IP4, ~ *ip);
284
+ }
285
+
286
+ VALUE
287
+ method_ip4_bor(VALUE self, VALUE other) {
288
+ ip4_t *a, *b;
289
+
290
+ assert_kind_of(other, IP4);
291
+
292
+ Data_Get_Struct(self, ip4_t, a);
293
+ Data_Get_Struct(other, ip4_t, b);
294
+ return ip4_new(IP4, *a | *b);
295
+ }
296
+
297
+ VALUE
298
+ method_ip4_xor(VALUE self, VALUE other) {
299
+ ip4_t *a, *b;
300
+
301
+ assert_kind_of(other, IP4);
302
+
303
+ Data_Get_Struct(self, ip4_t, a);
304
+ Data_Get_Struct(other, ip4_t, b);
305
+ return ip4_new(IP4, *a ^ *b);
306
+ }
307
+
308
+ VALUE
309
+ method_ip4_band(VALUE self, VALUE other) {
310
+ ip4_t *a, *b;
311
+
312
+ assert_kind_of(other, IP4);
313
+
314
+ Data_Get_Struct(self, ip4_t, a);
315
+ Data_Get_Struct(other, ip4_t, b);
316
+ return ip4_new(IP4, *a & *b);
317
+ }
318
+
319
+ VALUE
320
+ method_ip6_not(VALUE self) {
321
+ ip6_t *ip, not;
322
+ Data_Get_Struct(self, ip6_t, ip);
323
+ for (int i=0; i<8; i++) {
324
+ not.x[i] = ~(ip->x[i]);
325
+ }
326
+ return ip6_new(IP6, not);
327
+ }
328
+
329
+ VALUE
330
+ method_ip6_bor(VALUE self, VALUE other) {
331
+ ip6_t *a, *b, bor;
332
+
333
+ assert_kind_of(other, IP6);
334
+
335
+ Data_Get_Struct(self, ip6_t, a);
336
+ Data_Get_Struct(other, ip6_t, b);
337
+ for (int i=0; i<8; i++) {
338
+ bor.x[i] = a->x[i] | b->x[i];
339
+ }
340
+ return ip6_new(IP6, bor);
341
+ }
342
+
343
+ VALUE
344
+ method_ip6_xor(VALUE self, VALUE other) {
345
+ ip6_t *a, *b, xor;
346
+
347
+ assert_kind_of(other, IP6);
348
+
349
+ Data_Get_Struct(self, ip6_t, a);
350
+ Data_Get_Struct(other, ip6_t, b);
351
+ for (int i=0; i<8; i++) {
352
+ xor.x[i] = a->x[i] ^ b->x[i];
353
+ }
354
+ return ip6_new(IP6, xor);
355
+ }
356
+
357
+ VALUE
358
+ method_ip6_band(VALUE self, VALUE other) {
359
+ ip6_t *a, *b, band;
360
+
361
+ assert_kind_of(other, IP6);
362
+
363
+ Data_Get_Struct(self, ip6_t, a);
364
+ Data_Get_Struct(other, ip6_t, b);
365
+ for (int i=0; i<8; i++) {
366
+ band.x[i] = a->x[i] & b->x[i];
367
+ }
368
+ return ip6_new(IP6, band);
369
+ }
370
+
371
+ /**
372
+ * The prefix length of this network, or number of leading ones in the
373
+ * netmask.
374
+ *
375
+ * @return [Fixnum]
376
+ */
377
+ VALUE
378
+ method_net4_prefixlen(VALUE self) {
379
+ net4_t *net;
380
+ Data_Get_Struct(self, net4_t, net);
381
+ return INT2FIX(net->prefixlen);
382
+ }
383
+
384
+ /**
385
+ * (see Subnets::Net4#prefixlen)
386
+ */
387
+ VALUE
388
+ method_net6_prefixlen(VALUE self) {
389
+ net6_t *net;
390
+ Data_Get_Struct(self, net6_t, net);
391
+ return INT2FIX(net->prefixlen);
392
+ }
393
+
394
+ /**
395
+ * Test if this network includes +v+.
396
+ *
397
+ * A String must parse as an IP4 or Net4. An IP4 must be included
398
+ * within the range defined by this network. A Net4 must both have a
399
+ * prefixlen greater than or equal to that of this network, and have
400
+ * an address included within the range defined by this network.
401
+ *
402
+ * @param [String, IP, Net] v
403
+ */
404
+ VALUE
405
+ method_net4_include_p(VALUE self, VALUE v) {
406
+ net4_t *net;
407
+ Data_Get_Struct(self, net4_t, net);
408
+
409
+ if (CLASS_OF(v) == IP4) {
410
+ ip4_t *ip;
411
+ Data_Get_Struct(v, ip4_t, ip);
412
+ return net4_include_p(*net, *ip) ? Qtrue : Qfalse;
413
+ } else if (CLASS_OF(v) == Net4) {
414
+ net4_t *other;
415
+ Data_Get_Struct(v, net4_t, other);
416
+ return net4_include_net4_p(*net, *other) ? Qtrue : Qfalse;
417
+ } else if (CLASS_OF(v) == IP6 || CLASS_OF(v) == Net6) {
418
+ return Qfalse;
419
+ } else {
420
+ v = StringValue(v);
421
+
422
+ {
423
+ net4_t other;
424
+ if (read_net4_strict(RSTRING_PTR(v), &other)) {
425
+ return net4_include_net4_p(*net, other) ? Qtrue : Qfalse;
426
+ }
427
+ }
428
+ {
429
+ ip4_t ip;
430
+ if (read_ip4_strict(RSTRING_PTR(v), &ip)) {
431
+ return net4_include_p(*net, ip) ? Qtrue : Qfalse;
432
+ }
433
+ }
434
+
435
+ return Qfalse;
436
+ }
437
+ }
438
+
439
+ /**
440
+ * Test if this network includes +v+.
441
+ *
442
+ * A String must parse as an IP6 or Net6. An IP6 must be included
443
+ * within the range defined by this network. A Net6 must both have a
444
+ * prefixlen greater than or equal to that of this network, and have
445
+ * an address included within the range defined by this network.
446
+ *
447
+ * @param [String, IP, Net] v
448
+ */
449
+ VALUE
450
+ method_net6_include_p(VALUE self, VALUE v) {
451
+ net6_t *net;
452
+ Data_Get_Struct(self, net6_t, net);
453
+
454
+ if (CLASS_OF(v) == IP6) {
455
+ ip6_t *ip;
456
+ Data_Get_Struct(v, ip6_t, ip);
457
+ return net6_include_p(*net, *ip) ? Qtrue : Qfalse;
458
+ } else if (CLASS_OF(v) == Net6) {
459
+ net6_t *other;
460
+ Data_Get_Struct(v, net6_t, other);
461
+ return net6_include_net6_p(*net, *other) ? Qtrue : Qfalse;
462
+ } else if (CLASS_OF(v) == IP4 || CLASS_OF(v) == Net4) {
463
+ return Qfalse;
464
+ } else if (rb_obj_is_kind_of(v, rb_cString)) {
465
+ const char *buf = StringValueCStr(v);
466
+
467
+ {
468
+ net6_t other;
469
+ if (read_net6_strict(buf, &other)) {
470
+ return net6_include_net6_p(*net, other) ? Qtrue : Qfalse;
471
+ }
472
+ }
473
+ {
474
+ ip6_t ip;
475
+ if (read_ip6_strict(buf, &ip)) {
476
+ return net6_include_p(*net, ip) ? Qtrue : Qfalse;
477
+ }
478
+ }
479
+ }
480
+
481
+ return Qfalse;
482
+ }
483
+
484
+ /**
485
+ * Return a String in dotted-decimal notation.
486
+ *
487
+ * @return [String]
488
+ */
489
+ VALUE
490
+ method_ip4_to_s(VALUE self) {
491
+ ip4_t *ip;
492
+ Data_Get_Struct(self, ip4_t, ip);
493
+
494
+ char buf[16];
495
+
496
+ ip4_snprint(*ip, buf, 16);
497
+ return rb_str_new2(buf);
498
+ }
499
+
500
+ /**
501
+ * Return a String of colon-separated hexadecimal parts with multiple
502
+ * zeros compresses with a double-colon.
503
+ *
504
+ * @return [String]
505
+ */
506
+ VALUE
507
+ method_ip6_to_s(VALUE self) {
508
+ ip6_t *ip;
509
+ Data_Get_Struct(self, ip6_t, ip);
510
+
511
+ char buf[64];
512
+
513
+ ip6_snprint(*ip, buf, 64);
514
+ return rb_str_new2(buf);
515
+ }
516
+
517
+ /**
518
+ * Return a String in CIDR notation.
519
+ *
520
+ * @return [String]
521
+ */
522
+ VALUE
523
+ method_net4_to_s(VALUE self) {
524
+ net4_t *net;
525
+ Data_Get_Struct(self, net4_t, net);
526
+
527
+ char buf[32];
528
+
529
+ net4_snprint(*net, buf, 32);
530
+ return rb_str_new2(buf);
531
+ }
532
+
533
+ /**
534
+ * Return a String in CIDR notation.
535
+ *
536
+ * @return [String]
537
+ */
538
+ VALUE
539
+ method_net6_to_s(VALUE self) {
540
+ net6_t *net;
541
+ Data_Get_Struct(self, net6_t, net);
542
+
543
+ char buf[64];
544
+
545
+ net6_snprint(*net, buf, 64);
546
+ return rb_str_new2(buf);
547
+ }
548
+
549
+ /**
550
+ * @return [Boolean]
551
+ */
552
+ VALUE
553
+ method_ip4_eql_p(VALUE self, VALUE other) {
554
+ if (CLASS_OF(other) != CLASS_OF(self)) {
555
+ return Qfalse;
556
+ }
557
+
558
+ ip4_t *a, *b;
559
+ Data_Get_Struct(self, ip4_t, a);
560
+ Data_Get_Struct(other, ip4_t, b);
561
+
562
+ if (a != b) {
563
+ return Qfalse;
564
+ }
565
+ return Qtrue;
566
+ }
567
+
568
+ /**
569
+ * @return [Boolean]
570
+ */
571
+ VALUE
572
+ method_net4_eql_p(VALUE self, VALUE other) {
573
+ if (CLASS_OF(other) != CLASS_OF(self)) {
574
+ return Qfalse;
575
+ }
576
+
577
+ net4_t *a, *b;
578
+ Data_Get_Struct(self, net4_t, a);
579
+ Data_Get_Struct(other, net4_t, b);
580
+
581
+ if (a->prefixlen != b->prefixlen) {
582
+ return Qfalse;
583
+ }
584
+ if (a->address != b->address) {
585
+ return Qfalse;
586
+ }
587
+ return Qtrue;
588
+ }
589
+
590
+ /**
591
+ * @return [Boolean]
592
+ */
593
+ VALUE
594
+ method_ip6_eql_p(VALUE self, VALUE other) {
595
+ if (CLASS_OF(other) != CLASS_OF(self)) {
596
+ return Qfalse;
597
+ }
598
+
599
+ ip6_t *a, *b;
600
+ Data_Get_Struct(self, ip6_t, a);
601
+ Data_Get_Struct(other, ip6_t, b);
602
+
603
+ for (int i=0; i<8; i++) {
604
+ if (a->x[i] != b->x[i]) return Qfalse;
605
+ }
606
+ return Qtrue;
607
+ }
608
+
609
+ /**
610
+ * @return [Boolean]
611
+ */
612
+ VALUE
613
+ method_net6_eql_p(VALUE self, VALUE other) {
614
+ if (CLASS_OF(other) != CLASS_OF(self)) {
615
+ return Qfalse;
616
+ }
617
+
618
+ net6_t *a, *b;
619
+ Data_Get_Struct(self, net6_t, a);
620
+ Data_Get_Struct(other, net6_t, b);
621
+
622
+ if (a->prefixlen != b->prefixlen) {
623
+ return Qfalse;
624
+ }
625
+
626
+ for (int i=0; i<8; i++) {
627
+ if (a->address.x[i] != b->address.x[i]) return Qfalse;
628
+ }
629
+ return Qtrue;
630
+ }
631
+
632
+ /**
633
+ * @return [Integer]
634
+ */
635
+ VALUE
636
+ method_ip4_hash(VALUE self) {
637
+ ip4_t *ip;
638
+ Data_Get_Struct(self, ip4_t, ip);
639
+ return hash(UINT2NUM(ip));
640
+ }
641
+
642
+ /**
643
+ * @return [Integer]
644
+ */
645
+ VALUE
646
+ method_net4_hash(VALUE self) {
647
+ net4_t *net;
648
+ Data_Get_Struct(self, net4_t, net);
649
+ return xor(hash(INT2FIX(net->prefixlen)), hash(UINT2NUM(net->address)));
650
+ }
651
+
652
+ /**
653
+ * @return [Integer]
654
+ */
655
+ VALUE
656
+ method_ip6_hash(VALUE self) {
657
+ ip6_t *ip;
658
+ Data_Get_Struct(self, ip6_t, ip);
659
+
660
+ VALUE ret = hash(INT2FIX(ip->x[0]));
661
+ for (int i=1; i<8; i++) {
662
+ ret = xor(ret, hash(INT2FIX(ip->x[i])));
663
+ }
664
+ return ret;
665
+ }
666
+
667
+ /**
668
+ * @return [Integer]
669
+ */
670
+ VALUE
671
+ method_net6_hash(VALUE self) {
672
+ net6_t *net;
673
+ Data_Get_Struct(self, net6_t, net);
674
+
675
+ VALUE ret = hash(INT2FIX(net->prefixlen));
676
+ for (int i=0; i<8; i++) {
677
+ ret = xor(ret, hash(INT2FIX(net->address.x[i])));
678
+ }
679
+ return ret;
680
+ }
681
+
682
+ VALUE
683
+ method_net4_network(VALUE self) {
684
+ net4_t *addr;
685
+ Data_Get_Struct(self, net4_t, addr);
686
+
687
+ return net4_new(Net4, net4_network(*addr));
688
+ }
689
+
690
+ VALUE
691
+ method_net4_address(VALUE self) {
692
+ net4_t *net;
693
+ Data_Get_Struct(self, net4_t, net);
694
+ return ip4_new(IP4, net->address);
695
+ }
696
+
697
+ VALUE
698
+ method_net6_address(VALUE self) {
699
+ net6_t *net;
700
+ Data_Get_Struct(self, net6_t, net);
701
+ return ip6_new(IP6, net->address);
702
+ }
703
+
704
+ VALUE
705
+ method_net4_mask(VALUE self) {
706
+ net4_t *net;
707
+ Data_Get_Struct(self, net4_t, net);
708
+ return ip4_new(IP4, net->mask);
709
+ }
710
+
711
+ VALUE
712
+ method_net6_mask(VALUE self) {
713
+ net6_t *net;
714
+ Data_Get_Struct(self, net6_t, net);
715
+ return ip6_new(IP6, net->mask);
716
+ }
717
+
718
+ /**
719
+ * @return [Array<Fixnum>] 16-bit hextets, most significant first
720
+ */
721
+ VALUE
722
+ method_ip6_hextets(VALUE self) {
723
+ ip6_t *ip;
724
+ Data_Get_Struct(self, ip6_t, ip);
725
+
726
+ VALUE hextets = rb_ary_new();
727
+ for (int i=0; i<8; i++) {
728
+ rb_ary_push(hextets, INT2FIX(ip->x[i]));
729
+ }
730
+ return hextets;
731
+ }
732
+
733
+ /**
734
+ * (see Subnets::IP6#hextets)
735
+ */
736
+ VALUE
737
+ method_net6_hextets(VALUE self) {
738
+ net6_t *net;
739
+ Data_Get_Struct(self, net6_t, net);
740
+
741
+ VALUE hextets = rb_ary_new();
742
+ for (int i=0; i<8; i++) {
743
+ rb_ary_push(hextets, INT2FIX(net->address.x[i]));
744
+ }
745
+ return hextets;
746
+ }
747
+
748
+ VALUE
749
+ method_net4_summarize(VALUE class, VALUE nets) {
750
+ net4_t result;
751
+
752
+ result.address = mk_mask4(32);
753
+ result.prefixlen = 32;
754
+ result.mask = mk_mask4(32);
755
+
756
+ for (ssize_t i = 0; i < RARRAY_LEN(nets); i++) {
757
+ VALUE rbnet = RARRAY_AREF(nets, i);
758
+
759
+ assert_kind_of(rbnet, Net4);
760
+
761
+ net4_t *net;
762
+ Data_Get_Struct(rbnet, net4_t, net);
763
+
764
+ result.address &= (net->address & net->mask);
765
+
766
+ while (!net4_include_net4_p(result, *net)) {
767
+ result.prefixlen -= 1;
768
+ result.mask = mk_mask4(result.prefixlen);
769
+ result.address &= result.mask;
770
+ }
771
+ }
772
+
773
+ return net4_new(class, result);
774
+ }
775
+
776
+ /**
777
+ * Try parsing +str+ as Net4, Net6, IP4, IP6.
778
+ *
779
+ * @return [Net4, Net6, IP4, IP6]
780
+ * @raise {ParseError}
781
+ */
782
+ VALUE
783
+ method_subnets_parse(VALUE mod, VALUE str) {
784
+ str = StringValue(str);
785
+ const char *s = StringValueCStr(str);
786
+ {
787
+ net4_t net;
788
+ if (read_net4_strict(s, &net)) return net4_new(Net4, net);
789
+ }
790
+ {
791
+ net6_t net;
792
+ if (read_net6_strict(s, &net)) return net6_new(Net6, net);
793
+ }
794
+ {
795
+ ip4_t ip;
796
+ if (read_ip4_strict(s, &ip)) return ip4_new(IP4, ip);
797
+ }
798
+ {
799
+ ip6_t ip;
800
+ if (read_ip6_strict(s, &ip)) return ip6_new(IP6, ip);
801
+ }
802
+
803
+ raise_parse_error("{v4,v6}{net,ip}", s);
804
+ return Qnil;
805
+ }
806
+
807
+ /**
808
+ * Test if any element in +nets+ includes +v+. For array elements
809
+ * +obj+ that are not Net4 or Net6, calls +obj#===(v)+ to test for
810
+ * inclusion.
811
+ *
812
+ * @see Subnets::IP4#include?
813
+ * @see Subnets::IP6#include?
814
+ * @param [Array<Net,Object>] nets
815
+ * @param [String, IP, Net] v
816
+ */
817
+ VALUE
818
+ method_subnets_include_p(VALUE self, VALUE nets, VALUE v) {
819
+ int is_net4 = 0, is_net6 = 0, is_ip4 = 0, is_ip6 = 0;
820
+ net4_t net4;
821
+ net6_t net6;
822
+ ip4_t ip4;
823
+ ip6_t ip6;
824
+
825
+ if (CLASS_OF(v) == IP4) {
826
+ ip4_t *_ip4;
827
+ is_ip4 = !0;
828
+ Data_Get_Struct(v, ip4_t, _ip4);
829
+ ip4 = *_ip4;
830
+ } else if (CLASS_OF(v) == IP6) {
831
+ ip6_t *_ip6;
832
+ is_ip6 = !0;
833
+ Data_Get_Struct(v, ip6_t, _ip6);
834
+ ip6 = *_ip6;
835
+ } else if (CLASS_OF(v) == Net4) {
836
+ net4_t *_net4;
837
+ is_net4 = !0;
838
+ Data_Get_Struct(v, net4_t, _net4);
839
+ net4 = *_net4;
840
+ } else if (CLASS_OF(v) == Net6) {
841
+ net6_t *_net6;
842
+ is_net6 = !0;
843
+ Data_Get_Struct(v, net6_t, _net6);
844
+ net6 = *_net6;
845
+ } else {
846
+ const char *buf = StringValueCStr(v);
847
+
848
+ if (read_net4_strict(buf, &net4)) is_net4 = !0;
849
+ else if (read_net6_strict(buf, &net6)) is_net6 = !0;
850
+ else if (read_ip4_strict(buf, &ip4)) is_ip4 = !0;
851
+ else if (read_ip6_strict(buf, &ip6)) is_ip6 = !0;
852
+ }
853
+
854
+ for (ssize_t i = 0; i < RARRAY_LEN(nets); i++) {
855
+ VALUE rbnet = RARRAY_AREF(nets, i);
856
+
857
+ if (CLASS_OF(rbnet) == Net4) {
858
+ if (is_net4) {
859
+ net4_t *net;
860
+ Data_Get_Struct(rbnet, net4_t, net);
861
+ if (net4_include_net4_p(*net, net4)) {
862
+ return Qtrue;
863
+ }
864
+ } else if (is_ip4) {
865
+ net4_t *net;
866
+ Data_Get_Struct(rbnet, net4_t, net);
867
+ if (net4_include_p(*net, ip4)) {
868
+ return Qtrue;
869
+ }
870
+ }
871
+ }
872
+
873
+ else if (CLASS_OF(rbnet) == Net6) {
874
+ if (is_net6) {
875
+ net6_t *net;
876
+ Data_Get_Struct(rbnet, net6_t, net);
877
+ if (net6_include_net6_p(*net, net6)) {
878
+ return Qtrue;
879
+ }
880
+ } else if (is_ip6) {
881
+ net6_t *net;
882
+ Data_Get_Struct(rbnet, net6_t, net);
883
+ if (net6_include_p(*net, ip6)) {
884
+ return Qtrue;
885
+ }
886
+ }
887
+ }
888
+
889
+ else {
890
+ VALUE ret = rb_funcall(rbnet, rb_intern("==="), 1, v);
891
+ if (RTEST(ret)) return Qtrue;
892
+ }
893
+ }
894
+
895
+ return Qfalse;
896
+ }
897
+
898
+ VALUE
899
+ method_ip_inspect(VALUE ip) {
900
+ VALUE fmt = rb_str_new_cstr("#<%s %s>");
901
+ VALUE args = rb_ary_new_from_args(2,
902
+ rb_funcall(ip, rb_intern("class"), 0),
903
+ rb_funcall(ip, rb_intern("to_s"), 0));
904
+ return rb_funcall(fmt, rb_intern("%"), 1, args);
905
+ }
906
+
907
+ VALUE
908
+ method_net_inspect(VALUE net) {
909
+ VALUE fmt = rb_str_new_cstr("#<%s address=%s prefixlen=%d mask=%s>");
910
+ VALUE args = rb_ary_new_from_args(4,
911
+ rb_funcall(net, rb_intern("class"), 0),
912
+ rb_funcall(net, rb_intern("address"), 0),
913
+ rb_funcall(net, rb_intern("prefixlen"), 0),
914
+ rb_funcall(net, rb_intern("mask"), 0));
915
+ return rb_funcall(fmt, rb_intern("%"), 1, args);
916
+ }
917
+
918
+ /**
919
+ *
920
+ */
921
+ void Init_Subnets() {
922
+ rb_intern_hash = rb_intern("hash");
923
+ rb_intern_xor = rb_intern("^");
924
+
925
+ // Subnets
926
+ Subnets = rb_define_module("Subnets");
927
+ rb_define_singleton_method(Subnets, "parse", method_subnets_parse, 1);
928
+ rb_define_singleton_method(Subnets, "include?", method_subnets_include_p, 2);
929
+
930
+ // Subnets::ParseError
931
+ ParseError = rb_define_class_under(Subnets, "ParseError", rb_eArgError);
932
+
933
+ // Subnets::IP
934
+ IP = rb_define_class_under(Subnets, "IP", rb_cObject);
935
+ rb_define_method(IP, "inspect", method_ip_inspect, 0);
936
+
937
+ // Subnets::IP4
938
+ IP4 = rb_define_class_under(Subnets, "IP4", IP);
939
+ rb_define_singleton_method(IP4, "random", method_ip4_random, -1);
940
+ rb_define_singleton_method(IP4, "new", method_ip4_new, 1);
941
+ rb_define_method(IP4, "==", method_ip4_eql_p, 1);
942
+ rb_define_alias(IP4, "eql?", "==");
943
+ rb_define_method(IP4, "hash", method_ip4_hash, 0);
944
+ rb_define_method(IP4, "to_s", method_ip4_to_s, 0);
945
+
946
+ rb_define_method(IP4, "~", method_ip4_not, 0);
947
+ rb_define_method(IP4, "|", method_ip4_bor, 1);
948
+ rb_define_method(IP4, "^", method_ip4_xor, 1);
949
+ rb_define_method(IP4, "&", method_ip4_band, 1);
950
+
951
+ // Subnets::IP6
952
+ IP6 = rb_define_class_under(Subnets, "IP6", IP);
953
+ rb_define_singleton_method(IP6, "random", method_ip6_random, -1);
954
+ rb_define_method(IP6, "==", method_ip6_eql_p, 1);
955
+ rb_define_alias(IP6, "eql?", "==");
956
+ rb_define_method(IP6, "hash", method_ip6_hash, 0);
957
+ rb_define_method(IP6, "to_s", method_ip6_to_s, 0);
958
+ rb_define_method(IP6, "hextets", method_ip6_hextets, 0);
959
+
960
+ rb_define_method(IP6, "~", method_ip6_not, 0);
961
+ rb_define_method(IP6, "|", method_ip6_bor, 1);
962
+ rb_define_method(IP6, "^", method_ip6_xor, 1);
963
+ rb_define_method(IP6, "&", method_ip6_band, 1);
964
+
965
+ // Subnets::Net
966
+ Net = rb_define_class_under(Subnets, "Net", rb_cObject);
967
+ rb_define_method(Net, "inspect", method_net_inspect, 0);
968
+
969
+ // Subnets::Net4
970
+ Net4 = rb_define_class_under(Subnets, "Net4", Net);
971
+ rb_define_singleton_method(Net4, "parse", method_net4_parse, 1);
972
+ rb_define_singleton_method(Net4, "random", method_net4_random, -1);
973
+ rb_define_singleton_method(Net4, "new", method_net4_new, 2);
974
+ rb_define_singleton_method(Net4, "summarize", method_net4_summarize, 1);
975
+ rb_define_method(Net4, "==", method_net4_eql_p, 1);
976
+ rb_define_alias(Net4, "eql?", "==");
977
+ rb_define_method(Net4, "hash", method_net4_hash, 0);
978
+ rb_define_method(Net4, "to_s", method_net4_to_s, 0);
979
+ rb_define_method(Net4, "prefixlen", method_net4_prefixlen, 0);
980
+ rb_define_method(Net4, "include?", method_net4_include_p, 1);
981
+ rb_define_alias(Net4, "===", "include?");
982
+
983
+ rb_define_method(Net4, "address", method_net4_address, 0);
984
+ rb_define_method(Net4, "mask", method_net4_mask, 0);
985
+
986
+ // Subnets::Net6
987
+ Net6 = rb_define_class_under(Subnets, "Net6", Net);
988
+ rb_define_singleton_method(Net6, "parse", method_net6_parse, 1);
989
+ rb_define_singleton_method(Net6, "random", method_net6_random, -1);
990
+ rb_define_singleton_method(Net6, "new", method_net6_new, 2);
991
+ rb_define_method(Net6, "==", method_net6_eql_p, 1);
992
+ rb_define_alias(Net6, "eql?", "==");
993
+ rb_define_method(Net6, "hash", method_net6_hash, 0);
994
+ rb_define_method(Net6, "to_s", method_net6_to_s, 0);
995
+ rb_define_method(Net6, "prefixlen", method_net6_prefixlen, 0);
996
+ rb_define_method(Net6, "include?", method_net6_include_p, 1);
997
+ rb_define_method(Net6, "hextets", method_net6_hextets, 0);
998
+ rb_define_alias(Net6, "===", "include?");
999
+
1000
+ rb_define_method(Net6, "address", method_net6_address, 0);
1001
+ rb_define_method(Net6, "mask", method_net6_mask, 0);
1002
+ }
1003
+
1004
+ void Init_subnets() {
1005
+ // this is so YARD picks up the (case sensitive) Subnets docstring
1006
+ Init_Subnets();
1007
+ }