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 +7 -0
- data/COPYING +30 -0
- data/README.md +86 -0
- data/ext/subnets/ext.c +1007 -0
- data/ext/subnets/extconf.rb +6 -0
- data/ext/subnets/ipaddr.c +402 -0
- data/ext/subnets/ipaddr.h +105 -0
- data/test/benchmark_helper.rb +59 -0
- data/test/eql_and_hash.rb +35 -0
- data/test/private_networks_benchmark.rb +53 -0
- data/test/rack_ip_benchmark.rb +45 -0
- data/test/rails_remote_ip_benchmark.rb +104 -0
- data/test/subnets/net4_test.rb +74 -0
- data/test/subnets/net6_test.rb +125 -0
- data/test/subnets_test.rb +35 -0
- data/test/test_helper.rb +30 -0
- data/test/well_known_subnets.rb +68 -0
- metadata +204 -0
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
|
+
}
|