subnets 1.0.0pre

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.
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
+ }