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
@@ -0,0 +1,402 @@
|
|
1
|
+
#include <ctype.h> /* isdigit, isxdigit */
|
2
|
+
#include <stdarg.h>
|
3
|
+
#include <stddef.h>
|
4
|
+
#include <stdint.h>
|
5
|
+
#include <stdio.h>
|
6
|
+
#include <stdlib.h>
|
7
|
+
#include <string.h>
|
8
|
+
|
9
|
+
#include <unistd.h>
|
10
|
+
|
11
|
+
#include "ipaddr.h"
|
12
|
+
|
13
|
+
ip4_t
|
14
|
+
mk_mask4(int prefixlen) {
|
15
|
+
if (prefixlen==0) return 0;
|
16
|
+
|
17
|
+
int shift = 32 - prefixlen;
|
18
|
+
return (~((ip4_t) 0) >> shift) << shift;
|
19
|
+
}
|
20
|
+
|
21
|
+
ip6_t
|
22
|
+
mk_mask6(int prefixlen) {
|
23
|
+
ip6_t mask;
|
24
|
+
uint16_t ones = ~((uint16_t) 0);
|
25
|
+
|
26
|
+
int pivot = prefixlen / 16;
|
27
|
+
int shift = 16 - (prefixlen % 16);
|
28
|
+
|
29
|
+
for (int i=0; i<pivot && i<8; i++) {
|
30
|
+
mask.x[i] = ones;
|
31
|
+
}
|
32
|
+
if (pivot<8) {
|
33
|
+
mask.x[pivot] = (ones >> shift) << shift;
|
34
|
+
}
|
35
|
+
for (int i = pivot+1; i<8; i++) {
|
36
|
+
mask.x[i] = 0;
|
37
|
+
}
|
38
|
+
|
39
|
+
return mask;
|
40
|
+
}
|
41
|
+
|
42
|
+
int
|
43
|
+
net4_include_p(net4_t net, ip4_t a) {
|
44
|
+
return ((net.address & net.mask) == (a & net.mask));
|
45
|
+
}
|
46
|
+
|
47
|
+
int
|
48
|
+
net6_include_p(net6_t net, ip6_t a) {
|
49
|
+
for (int i=0; i<8; i++) {
|
50
|
+
if ((net.address.x[i] & net.mask.x[i]) != (a.x[i] & net.mask.x[i])) return 0;
|
51
|
+
}
|
52
|
+
return !0;
|
53
|
+
}
|
54
|
+
|
55
|
+
int
|
56
|
+
net4_include_net4_p(net4_t net, net4_t other) {
|
57
|
+
return net.prefixlen <= other.prefixlen && net4_include_p(net, other.address);
|
58
|
+
}
|
59
|
+
|
60
|
+
int
|
61
|
+
net6_include_net6_p(net6_t net, net6_t other) {
|
62
|
+
return net.prefixlen <= other.prefixlen && net6_include_p(net, other.address);
|
63
|
+
}
|
64
|
+
|
65
|
+
net4_t
|
66
|
+
net4_network(net4_t net) {
|
67
|
+
net.address = net.address & net.mask;
|
68
|
+
return net;
|
69
|
+
}
|
70
|
+
|
71
|
+
int
|
72
|
+
ip4_snprint(ip4_t ip, char *str, size_t size) {
|
73
|
+
return snprintf(str, size, "%u.%u.%u.%u",
|
74
|
+
(ip >> 24) & 0xff,
|
75
|
+
(ip >> 16) & 0xff,
|
76
|
+
(ip >> 8) & 0xff,
|
77
|
+
(ip >> 0) & 0xff);
|
78
|
+
}
|
79
|
+
|
80
|
+
int
|
81
|
+
net4_snprint(net4_t net, char *str, size_t size) {
|
82
|
+
return snprintf(str, size, "%u.%u.%u.%u/%d",
|
83
|
+
(net.address >> 24) & 0xff,
|
84
|
+
(net.address >> 16) & 0xff,
|
85
|
+
(net.address >> 8) & 0xff,
|
86
|
+
(net.address >> 0) & 0xff,
|
87
|
+
net.prefixlen);
|
88
|
+
}
|
89
|
+
|
90
|
+
int
|
91
|
+
ip6_snprint(ip6_t ip, char *str, size_t size) {
|
92
|
+
/* find first longest string of zeroes */
|
93
|
+
int longzerostart = -1;
|
94
|
+
int longzeroend = -1;
|
95
|
+
|
96
|
+
int currzerostart = -1;
|
97
|
+
int currzeroend = -1;
|
98
|
+
|
99
|
+
for (int i = 0; i < 8; i++) {
|
100
|
+
if (0 == ip.x[i]) {
|
101
|
+
currzeroend = i+1;
|
102
|
+
if (currzerostart < 0) {
|
103
|
+
currzerostart = i;
|
104
|
+
}
|
105
|
+
if ((currzeroend - currzerostart) > (longzeroend - longzerostart)) {
|
106
|
+
longzerostart = currzerostart;
|
107
|
+
longzeroend = currzeroend;
|
108
|
+
}
|
109
|
+
} else {
|
110
|
+
if (currzerostart >= 0) {
|
111
|
+
currzerostart = -1;
|
112
|
+
}
|
113
|
+
}
|
114
|
+
}
|
115
|
+
|
116
|
+
if (longzeroend - longzerostart <= 1) {
|
117
|
+
return snprintf(str, size, "%x:%x:%x:%x:%x:%x:%x:%x",
|
118
|
+
ip.x[0],
|
119
|
+
ip.x[1],
|
120
|
+
ip.x[2],
|
121
|
+
ip.x[3],
|
122
|
+
ip.x[4],
|
123
|
+
ip.x[5],
|
124
|
+
ip.x[6],
|
125
|
+
ip.x[7]);
|
126
|
+
} else {
|
127
|
+
int total = 0;
|
128
|
+
int n;
|
129
|
+
|
130
|
+
/* print first before zeros */
|
131
|
+
for (int i = 0; i < 1 && i < longzerostart; i++) {
|
132
|
+
n = snprintf(str+total, size-total, "%x", ip.x[i]);
|
133
|
+
if (n < 0) { return n; }
|
134
|
+
total += n;
|
135
|
+
}
|
136
|
+
|
137
|
+
/* print rest before zeros with leading colon */
|
138
|
+
for (int i = 1; i < longzerostart; i++) {
|
139
|
+
n = snprintf(str+total, size-total, ":");
|
140
|
+
if (n < 0) { return n; }
|
141
|
+
total += n;
|
142
|
+
n = snprintf(str+total, size-total, "%x", ip.x[i]);
|
143
|
+
if (n < 0) { return n; }
|
144
|
+
total += n;
|
145
|
+
}
|
146
|
+
|
147
|
+
/* print double colon */
|
148
|
+
n = snprintf(str+total, size-total, "::");
|
149
|
+
if (n < 0) { return n; }
|
150
|
+
total += n;
|
151
|
+
|
152
|
+
/* print first after zeros */
|
153
|
+
for (int i = longzeroend; i < 8 && i < longzeroend+1; i++) {
|
154
|
+
n = snprintf(str+total, size-total, "%x", ip.x[i]);
|
155
|
+
if (n < 0) { return n; }
|
156
|
+
total += n;
|
157
|
+
}
|
158
|
+
|
159
|
+
/* print rest after zeros with leading colon */
|
160
|
+
for (int i = longzeroend + 1; i < 8; i++) {
|
161
|
+
n = snprintf(str+total, size-total, ":");
|
162
|
+
if (n < 0) { return n; }
|
163
|
+
total += n;
|
164
|
+
|
165
|
+
n = snprintf(str+total, size-total, "%x", ip.x[i]);
|
166
|
+
if (n < 0) { return n; }
|
167
|
+
total += n;
|
168
|
+
}
|
169
|
+
|
170
|
+
return total;
|
171
|
+
}
|
172
|
+
}
|
173
|
+
|
174
|
+
int
|
175
|
+
net6_snprint(net6_t net, char *str, size_t size) {
|
176
|
+
int n0, n1;
|
177
|
+
n0 = ip6_snprint(net.address, str, size);
|
178
|
+
if (n0 < 0) {
|
179
|
+
return n0;
|
180
|
+
}
|
181
|
+
|
182
|
+
n1 = snprintf(str+n0, size-n0, "/%d", net.prefixlen);
|
183
|
+
if (n1 < 0) {
|
184
|
+
return n1;
|
185
|
+
}
|
186
|
+
|
187
|
+
return n0 + n1;
|
188
|
+
}
|
189
|
+
|
190
|
+
int
|
191
|
+
hexvalue(unsigned int c) {
|
192
|
+
if (c-'0'<10) return c-'0';
|
193
|
+
if (c-'a'<6) return c-'a'+10;
|
194
|
+
if (c-'A'<6) return c-'A'+10;
|
195
|
+
return -1;
|
196
|
+
}
|
197
|
+
|
198
|
+
/*
|
199
|
+
* read_ip4 is adapted from musl-libc inet_pton
|
200
|
+
* https://git.musl-libc.org/cgit/musl/tree/src/network/inet_pton.c?id=fc13acc3dcb5b1f215c007f583a63551f6a71363
|
201
|
+
*
|
202
|
+
* Copyright © 2005-2014 Rich Felker, et al.
|
203
|
+
*
|
204
|
+
* Permission is hereby granted, free of charge, to any person
|
205
|
+
* obtaining a copy of this software and associated documentation
|
206
|
+
* files (the "Software"), to deal in the Software without
|
207
|
+
* restriction, including without limitation the rights to use, copy,
|
208
|
+
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
209
|
+
* of the Software, and to permit persons to whom the Software is
|
210
|
+
* furnished to do so, subject to the following conditions:
|
211
|
+
*
|
212
|
+
* The above copyright notice and this permission notice shall be
|
213
|
+
* included in all copies or substantial portions of the Software.
|
214
|
+
*
|
215
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
216
|
+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
217
|
+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
218
|
+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
219
|
+
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
220
|
+
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
221
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
222
|
+
* DEALINGS IN THE SOFTWARE.
|
223
|
+
*
|
224
|
+
*/
|
225
|
+
/**
|
226
|
+
* Read an Ip4 address from s.
|
227
|
+
*
|
228
|
+
* @return the number of characters consumed
|
229
|
+
*/
|
230
|
+
size_t
|
231
|
+
read_ip4(const char *s, ip4_t *a) {
|
232
|
+
size_t pos = 0;
|
233
|
+
|
234
|
+
*a = 0;
|
235
|
+
|
236
|
+
for (int i = 0; i < 4; i++) {
|
237
|
+
int v, j;
|
238
|
+
for (v=j=0; j < 3 && isdigit(s[pos+j]); j++) {
|
239
|
+
v = 10*v + s[pos+j]-'0';
|
240
|
+
}
|
241
|
+
if (j==0 || (j>1 && s[pos]=='0') || v>255) return 0;
|
242
|
+
*a |= (v & 0xff) << (3-i)*8;
|
243
|
+
pos += j;
|
244
|
+
if (i == 3) return pos;
|
245
|
+
if (s[pos++] != '.') return 0;
|
246
|
+
}
|
247
|
+
|
248
|
+
return 0;
|
249
|
+
}
|
250
|
+
|
251
|
+
size_t
|
252
|
+
read_hextet(const char *s, uint16_t *v) {
|
253
|
+
int i;
|
254
|
+
for (i=0; i<4 && isxdigit(s[i]); i++) {
|
255
|
+
*v = 0x10*(*v) + hexvalue(s[i]);
|
256
|
+
}
|
257
|
+
if (i>1 && s[0]=='0') return 0;
|
258
|
+
return i;
|
259
|
+
}
|
260
|
+
|
261
|
+
size_t
|
262
|
+
read_ip6(const char *s, ip6_t *a) {
|
263
|
+
uint16_t hextets[8] = { 0, 0, 0, 0, 0, 0, 0, 0, };
|
264
|
+
size_t n;
|
265
|
+
int i = 0;
|
266
|
+
size_t pos = 0;
|
267
|
+
int brk = 8;
|
268
|
+
|
269
|
+
/*
|
270
|
+
* read optional leading hextet followed by zero or more
|
271
|
+
* colon-hextet pairs, zero or one lone colon (the second of a
|
272
|
+
* double-colon), zero or more colon-hextet pairs.
|
273
|
+
*/
|
274
|
+
|
275
|
+
n = read_hextet(s+pos, &hextets[i]);
|
276
|
+
if (n) {
|
277
|
+
//printf("read hextet %d: %x\n", i, hextets[i]);
|
278
|
+
pos+=n;
|
279
|
+
i++;
|
280
|
+
}
|
281
|
+
|
282
|
+
for (; i < 8;) {
|
283
|
+
if (s[pos] != ':') break;
|
284
|
+
pos++;
|
285
|
+
|
286
|
+
if (brk==8 && s[pos] == ':') {
|
287
|
+
//printf("see break at pos=%d, brk=%d\n", pos, i);
|
288
|
+
pos++;
|
289
|
+
brk = i;
|
290
|
+
if (!isxdigit(s[pos])) break;
|
291
|
+
}
|
292
|
+
else if (i==0) { /* can't lead with single-colon */
|
293
|
+
return 0;
|
294
|
+
}
|
295
|
+
|
296
|
+
ip4_t ip4;
|
297
|
+
if ((i==6 || (brk<8 && i<4)) && (n = read_ip4(s+pos, &ip4))) {
|
298
|
+
pos += n;
|
299
|
+
hextets[i] = (ip4 >> 16) & 0xffff;
|
300
|
+
hextets[i+1] = (ip4 >> 0) & 0xffff;
|
301
|
+
i+=2;
|
302
|
+
break;
|
303
|
+
}
|
304
|
+
|
305
|
+
n = read_hextet(s+pos, &hextets[i]);
|
306
|
+
if (n) {
|
307
|
+
//printf("read hextet %d: %x\n", i, hextets[i]);
|
308
|
+
pos+=n;
|
309
|
+
i++;
|
310
|
+
}
|
311
|
+
else {
|
312
|
+
//printf("error 'cause read %c\n", s[pos]);
|
313
|
+
return 0;
|
314
|
+
}
|
315
|
+
}
|
316
|
+
|
317
|
+
if (brk==8 && i<8) return 0;
|
318
|
+
if (brk<8 && i==8) return 0;
|
319
|
+
|
320
|
+
for (int k = 0; k < 8; k++) {
|
321
|
+
//printf("brk=%d i=%d hextets[%d]=%x\n", brk, i, k, hextets[k]);
|
322
|
+
}
|
323
|
+
|
324
|
+
/* TODO move down hextets after the break */
|
325
|
+
//printf("before\n");
|
326
|
+
for (int j = 0; j < brk; j++) {
|
327
|
+
a->x[j] = hextets[j];
|
328
|
+
}
|
329
|
+
//printf("brk\n");
|
330
|
+
for (int j = 0; j < (8-i); j++) {
|
331
|
+
a->x[j+brk] = 0;
|
332
|
+
}
|
333
|
+
//printf("after\n");
|
334
|
+
for (int j = 0; j < (i-brk); j++) {
|
335
|
+
//printf(" (from hextets[%d])\n", j+brk);
|
336
|
+
a->x[j+(8-i)+brk] = hextets[j+brk];
|
337
|
+
}
|
338
|
+
|
339
|
+
return pos;
|
340
|
+
}
|
341
|
+
|
342
|
+
size_t
|
343
|
+
read_net4(const char *s, net4_t *net) {
|
344
|
+
int i, v=0;
|
345
|
+
size_t pos = read_ip4(s, &net->address);
|
346
|
+
if (!pos) return 0;
|
347
|
+
|
348
|
+
if (!(s[pos++] == '/')) return 0;
|
349
|
+
for (i = 0; i < 2 && isdigit(s[pos+i]); i++) {
|
350
|
+
v = v*10 + s[pos+i]-'0';
|
351
|
+
}
|
352
|
+
if (i==0 || (i>1 && s[pos]=='0') || v>32) return 0;
|
353
|
+
net->prefixlen = v;
|
354
|
+
net->mask = mk_mask4(net->prefixlen);
|
355
|
+
return pos+i;
|
356
|
+
}
|
357
|
+
|
358
|
+
size_t
|
359
|
+
read_ip4_strict(const char *s, ip4_t *ip) {
|
360
|
+
size_t n = read_ip4(s, ip);
|
361
|
+
if (!n || s[n] != 0) return 0;
|
362
|
+
return n;
|
363
|
+
}
|
364
|
+
|
365
|
+
size_t
|
366
|
+
read_net4_strict(const char *s, net4_t *net) {
|
367
|
+
size_t n = read_net4(s, net);
|
368
|
+
if (!n || s[n] != 0) return 0;
|
369
|
+
return n;
|
370
|
+
}
|
371
|
+
|
372
|
+
size_t
|
373
|
+
read_ip6_strict(const char *s, ip6_t *ip) {
|
374
|
+
size_t n = read_ip6(s, ip);
|
375
|
+
if (!n || s[n] != 0) return 0;
|
376
|
+
return n;
|
377
|
+
}
|
378
|
+
|
379
|
+
size_t
|
380
|
+
read_net6_strict(const char *s, net6_t *net) {
|
381
|
+
size_t n = read_net6(s, net);
|
382
|
+
if (!n || s[n] != 0) return 0;
|
383
|
+
return n;
|
384
|
+
}
|
385
|
+
|
386
|
+
size_t
|
387
|
+
read_net6(const char *s, net6_t *net) {
|
388
|
+
size_t pos;
|
389
|
+
|
390
|
+
pos = read_ip6(s, &net->address);
|
391
|
+
if (!pos) return 0;
|
392
|
+
|
393
|
+
int i, v = 0;
|
394
|
+
if (s[pos++] != '/') return 0;
|
395
|
+
for (i = 0; i < 3 && isdigit(s[pos+i]); i++) {
|
396
|
+
v = v*10 + s[pos+i]-'0';
|
397
|
+
}
|
398
|
+
if (i==0 || (i>1 && s[pos]=='0') || v>128) return 0;
|
399
|
+
net->prefixlen = v;
|
400
|
+
net->mask = mk_mask6(net->prefixlen);
|
401
|
+
return pos+i;
|
402
|
+
}
|
@@ -0,0 +1,105 @@
|
|
1
|
+
#ifndef __IPADDR_H__
|
2
|
+
#define __IPADDR_H__
|
3
|
+
|
4
|
+
#include <ctype.h> /* isdigit, isxdigit */
|
5
|
+
#include <stdarg.h>
|
6
|
+
#include <stddef.h>
|
7
|
+
#include <stdint.h>
|
8
|
+
#include <stdio.h>
|
9
|
+
#include <stdlib.h>
|
10
|
+
#include <string.h>
|
11
|
+
|
12
|
+
#include <unistd.h>
|
13
|
+
|
14
|
+
typedef uint32_t ip4_t;
|
15
|
+
|
16
|
+
typedef struct {
|
17
|
+
int prefixlen;
|
18
|
+
ip4_t address;
|
19
|
+
ip4_t mask;
|
20
|
+
} net4_t;
|
21
|
+
|
22
|
+
typedef struct {
|
23
|
+
uint16_t x[8];
|
24
|
+
} ip6_t;
|
25
|
+
|
26
|
+
typedef struct {
|
27
|
+
int prefixlen;
|
28
|
+
ip6_t address;
|
29
|
+
ip6_t mask;
|
30
|
+
} net6_t;
|
31
|
+
|
32
|
+
/**
|
33
|
+
* Make an IPv4 mask of the given prefixlen in the range [0,32].
|
34
|
+
*/
|
35
|
+
ip4_t mk_mask4(int prefixlen);
|
36
|
+
|
37
|
+
/**
|
38
|
+
* Make an IPv6 mask of the given prefixlen in the range [0,128].
|
39
|
+
*/
|
40
|
+
ip6_t mk_mask6(int prefixlen);
|
41
|
+
|
42
|
+
/**
|
43
|
+
* Test if this network includes the given ip.
|
44
|
+
*/
|
45
|
+
int net4_include_p(net4_t, ip4_t);
|
46
|
+
int net6_include_p(net6_t, ip6_t);
|
47
|
+
|
48
|
+
/**
|
49
|
+
* Test if this network includes the given network, which must have a
|
50
|
+
* prefexlen greater than or equal to that of this network and a
|
51
|
+
* network address that is included within this network.
|
52
|
+
*/
|
53
|
+
int net4_include_net4_p(net4_t, net4_t);
|
54
|
+
int net6_include_net6_p(net6_t, net6_t);
|
55
|
+
|
56
|
+
/**
|
57
|
+
* Zero-out the host portion of this network by applying the netmask,
|
58
|
+
* and return that network.
|
59
|
+
*/
|
60
|
+
net4_t net4_network(net4_t);
|
61
|
+
net6_t net6_network(net6_t);
|
62
|
+
|
63
|
+
/**
|
64
|
+
* Write a string representation of this network to the given string,
|
65
|
+
* according to the rules of snprintf().
|
66
|
+
*/
|
67
|
+
int net4_snprint(net4_t, char *, size_t);
|
68
|
+
int net6_snprint(net6_t, char *, size_t);
|
69
|
+
|
70
|
+
/**
|
71
|
+
* Write a string representation of this ip to the given string,
|
72
|
+
* according to the rules of snprintf().
|
73
|
+
*/
|
74
|
+
int ip4_snprint(ip4_t, char *, size_t);
|
75
|
+
int ip6_snprint(ip6_t, char *, size_t);
|
76
|
+
|
77
|
+
/**
|
78
|
+
* Read an IP from the string, returning the number of bytes read, or
|
79
|
+
* zero on parse error.
|
80
|
+
*/
|
81
|
+
size_t read_ip4(const char *, ip4_t *);
|
82
|
+
size_t read_ip6(const char *, ip6_t *);
|
83
|
+
|
84
|
+
/**
|
85
|
+
* Read a network from the string, returning the number of bytes read,
|
86
|
+
* or zero on parse error.
|
87
|
+
*/
|
88
|
+
size_t read_net4(const char *, net4_t *);
|
89
|
+
size_t read_net6(const char *, net6_t *);
|
90
|
+
|
91
|
+
/**
|
92
|
+
* Like read_ip*, but it is an error if the IP is not followed by a
|
93
|
+
* null byte.
|
94
|
+
*/
|
95
|
+
size_t read_ip4_strict(const char *, ip4_t *);
|
96
|
+
size_t read_ip6_strict(const char *, ip6_t *);
|
97
|
+
|
98
|
+
/**
|
99
|
+
* Like read_net*, but it is an error if the network is not followed
|
100
|
+
* by a null byte.
|
101
|
+
*/
|
102
|
+
size_t read_net4_strict(const char *, net4_t *);
|
103
|
+
size_t read_net6_strict(const char *, net6_t *);
|
104
|
+
|
105
|
+
#endif /* __IPADDR_H__ */
|