UNPKG

14.9 kBJavaScriptView Raw
1/*!
2 * inet.js - inet pton/ntop for bcoin
3 * Copyright (c) 2018, Christopher Jeffrey (MIT License).
4 * https://github.com/bcoin-org/bcoin
5 *
6 * Parts of this software are based on c-ares:
7 * Copyright (c) 2007-2018, Daniel Stenberg (MIT License)
8 * https://github.com/c-ares/c-ares
9 * https://github.com/c-ares/c-ares/blob/master/inet_net_pton.c
10 * https://github.com/c-ares/c-ares/blob/master/inet_ntop.c
11 */
12
13/* eslint spaced-comment: "off" */
14
15'use strict';
16
17const assert = require('bsert');
18
19/*
20 * Constants
21 */
22
23const ENOENT = 1;
24const EMSGSIZE = 2;
25
26const POOL16 = Buffer.allocUnsafe(16);
27const BUFFER16 = Buffer.allocUnsafe(16);
28const UINT16 = new Uint16Array(16 / 2);
29
30const CHARSET = [
31 '0', '1', '2', '3', '4', '5', '6', '7',
32 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
33];
34
35const TABLE = new Int8Array([
36 -1, -1, -1, -1, -1, -1, -1, -1,
37 -1, -1, -1, -1, -1, -1, -1, -1,
38 -1, -1, -1, -1, -1, -1, -1, -1,
39 -1, -1, -1, -1, -1, -1, -1, -1,
40 -1, -1, -1, -1, -1, -1, -1, -1,
41 -1, -1, -1, -1, -1, -1, -1, -1,
42 0, 1, 2, 3, 4, 5, 6, 7,
43 8, 9, -1, -1, -1, -1, -1, -1,
44 -1, 10, 11, 12, 13, 14, 15, -1,
45 -1, -1, -1, -1, -1, -1, -1, -1,
46 -1, -1, -1, -1, -1, -1, -1, -1,
47 -1, -1, -1, -1, -1, -1, -1, -1,
48 -1, 10, 11, 12, 13, 14, 15, -1,
49 -1, -1, -1, -1, -1, -1, -1, -1,
50 -1, -1, -1, -1, -1, -1, -1, -1,
51 -1, -1, -1, -1, -1, -1, -1, -1
52]);
53
54/*
55 * Presentation to Network
56 */
57
58/**
59 * Convert IPv4 network string to network format.
60 * @param {String} src - IP String
61 * @param {Buffer} dst - buffer where to decode to
62 * @param {Number} off - from where to start.
63 * @returns {Number} - number of bits, either imputed classfully or specified
64 * with /CIDR, or -1 if some failure occurred (check errno). ENOENT means it
65 * was not an IPv4 network specification.
66 */
67
68function pton4(src, dst, off) {
69 if (dst == null)
70 dst = null;
71
72 if (off == null)
73 off = 0;
74
75 assert(typeof src === 'string');
76 assert(dst === null || Buffer.isBuffer(dst));
77 assert((off >>> 0) === off);
78
79 const start = off;
80
81 let i = 0;
82 let ch = -1;
83 let first = -1;
84
85 if (dst) {
86 if (off + 4 > dst.length)
87 return -ENOENT;
88 }
89
90 if (isHex(src, i)) {
91 i += 2;
92
93 let dirty = 0;
94 let word = 0;
95 let total = 0;
96
97 for (; i < src.length; i++) {
98 ch = byte(src, i);
99
100 const n = TABLE[ch];
101
102 if (n === -1)
103 break;
104
105 if (dirty === 0)
106 word = n;
107 else
108 word = (word << 4) | n;
109
110 total += 1;
111 dirty += 1;
112
113 if (total > 8)
114 return -ENOENT;
115
116 if (dirty === 2) {
117 if ((off + 1) - start > 4)
118 return -EMSGSIZE;
119
120 if (dst)
121 dst[off] = word;
122
123 if (first === -1)
124 first = word;
125
126 off += 1;
127 dirty = 0;
128 }
129
130 ch = -1;
131 }
132
133 if (dirty) {
134 if ((off + 1) - start > 4)
135 return -EMSGSIZE;
136
137 if (dst)
138 dst[off] = word << 4;
139
140 if (first === -1)
141 first = word << 4;
142
143 off += 1;
144 }
145 } else if (isDecimal(src, i)) {
146 let t = 0;
147
148 for (;;) {
149 let word = 0;
150 let total = 0;
151
152 for (; i < src.length; i++) {
153 ch = byte(src, i);
154
155 if (ch < 0x30 || ch > 0x39)
156 break;
157
158 word *= 10;
159 word += ch - 0x30;
160 total += 1;
161
162 if (total > 3 || word > 255)
163 return -ENOENT;
164
165 ch = -1;
166 }
167
168 t += total;
169
170 if (t > 12)
171 return -ENOENT;
172
173 if ((off + 1) - start > 4)
174 return -EMSGSIZE;
175
176 if (dst)
177 dst[off] = word;
178
179 if (first === -1)
180 first = word;
181
182 off += 1;
183
184 if (ch === -1 || ch === 0x2f /*/*/)
185 break;
186
187 if (ch !== 0x2e /*.*/)
188 return -ENOENT;
189
190 i += 1;
191
192 ch = byte(src, i);
193
194 if (ch < 0x30 || ch > 0x39)
195 return -ENOENT;
196 }
197 } else {
198 return -ENOENT;
199 }
200
201 let bits = -1;
202
203 if (isCIDR(src, i) && off > start) {
204 i += 1;
205 bits = 0;
206 ch = -1;
207
208 let total = 0;
209
210 for (; i < src.length; i++) {
211 ch = byte(src, i);
212
213 if (ch < 0x30 || ch > 0x39)
214 break;
215
216 bits *= 10;
217 bits += ch - 0x30;
218 total += 1;
219
220 if (total > 3 || bits > 32)
221 return -ENOENT;
222
223 ch = -1;
224 }
225 }
226
227 if (ch !== -1)
228 return -ENOENT;
229
230 if (off === start)
231 return -ENOENT;
232
233 if (bits === -1) {
234 assert(first !== -1);
235
236 if (first >= 240)
237 bits = 32;
238 else if (first >= 224)
239 bits = 8;
240 else if (first >= 192)
241 bits = 24;
242 else if (first >= 128)
243 bits = 16;
244 else
245 bits = 8;
246
247 if (bits < (off - start) * 8)
248 bits = (off - start) * 8;
249
250 if (bits === 8 && first === 224)
251 bits = 4;
252 }
253
254 assert(bits <= 32);
255
256 const left = 4 - (off - start);
257 assert(left >= 0 && left <= 4);
258
259 if (dst) {
260 assert(off + left <= dst.length);
261 dst.fill(0x00, off, off + left);
262 }
263
264 off += left;
265
266 return bits;
267}
268
269/**
270 * Convert IPv6 network string to network format.
271 * @param {String} src - IP String
272 * @param {Buffer} dst - buffer where to decode to
273 * @param {Number} off - from where to start.
274 * @returns {Number} - number of bits, either imputed classfully or specified
275 * with /CIDR, or -1 if some failure occurred (check errno). ENOENT means it
276 * was not an IPv6 network specification.
277 */
278
279function pton6(src, dst, off) {
280 if (dst == null)
281 dst = null;
282
283 if (off == null)
284 off = 0;
285
286 assert(typeof src === 'string');
287 assert(dst === null || Buffer.isBuffer(dst));
288 assert((off >>> 0) === off);
289
290 const tmp = POOL16;
291
292 let i = 0;
293 let ptr = 0;
294 let end = 16;
295 let col = -1;
296 let cur = 0;
297 let digit = false;
298 let word = 0;
299 let digits = 0;
300 let bits = -1;
301 let inet4 = false;
302
303 if (dst) {
304 if (off + 16 > dst.length)
305 return -EMSGSIZE;
306 }
307
308 if (isColon(src, i)) {
309 if (!isColon(src, i + 1))
310 return -ENOENT;
311 i += 1;
312 }
313
314 tmp.fill(0x00, 0, 16);
315 cur = i;
316
317 for (; i < src.length; i++) {
318 const ch = byte(src, i);
319 const n = TABLE[ch];
320
321 if (n !== -1) {
322 word <<= 4;
323 word |= n;
324
325 digits += 1;
326
327 if (digits > 4)
328 return -ENOENT;
329
330 digit = true;
331
332 continue;
333 }
334
335 if (ch === 0x3a /*:*/) {
336 cur = i + 1;
337
338 if (!digit) {
339 if (col !== -1)
340 return -ENOENT;
341 col = ptr;
342 continue;
343 }
344
345 if (i === src.length)
346 return -ENOENT;
347
348 if (ptr + 2 > end)
349 return -ENOENT;
350
351 tmp[ptr++] = (word >>> 8) & 0xff;
352 tmp[ptr++] = word & 0xff;
353
354 digit = false;
355 digits = 0;
356 word = 0;
357
358 continue;
359 }
360
361 if (ch === 0x2e /*.*/ && ptr + 4 <= end) {
362 const b = getV4(src, cur, tmp, ptr);
363
364 if (b !== -1) {
365 if (b !== 0)
366 bits = b;
367 ptr += 4;
368 digit = false;
369 inet4 = true;
370 break;
371 }
372 }
373
374 if (ch === 0x2f /*/*/) {
375 const b = getBits(src, i + 1);
376 if (b !== -1) {
377 bits = b;
378 break;
379 }
380 }
381
382 return -ENOENT;
383 }
384
385 if (digit) {
386 if (ptr + 2 > end)
387 return -ENOENT;
388
389 tmp[ptr++] = (word >>> 8) & 0xff;
390 tmp[ptr++] = word & 0xff;
391 }
392
393 if (bits === -1)
394 bits = 128;
395
396 assert(bits <= 128);
397
398 let words = (bits + 15) / 16 | 0;
399
400 if (words < 2)
401 words = 2;
402
403 if (inet4)
404 words = 8;
405
406 end = 2 * words;
407
408 if (col !== -1) {
409 const n = ptr - col;
410
411 let i;
412
413 if (ptr === end)
414 return -ENOENT;
415
416 for (i = 1; i <= n; i++) {
417 tmp[end - i] = tmp[col + n - i];
418 tmp[col + n - i] = 0;
419 }
420
421 ptr = end;
422 }
423
424 if (ptr !== end)
425 return -ENOENT;
426
427 const bytes = (bits + 7) / 8 | 0;
428 const left = 16 - bytes;
429
430 assert(bytes >= 0 && bytes <= 16);
431
432 if (dst) {
433 assert(off + bytes + left <= dst.length);
434 off += tmp.copy(dst, off, 0, bytes);
435 dst.fill(0x00, off, off + left);
436 off += left;
437 } else {
438 off += bytes;
439 off += left;
440 }
441
442 return bits;
443}
444
445function pton(af, src, dst, off) {
446 if (dst == null)
447 dst = null;
448
449 if (off == null)
450 off = 0;
451
452 assert((af >>> 0) === af);
453 assert(typeof src === 'string');
454 assert(dst === null || Buffer.isBuffer(dst));
455 assert((off >>> 0) === off);
456
457 if (af === 4)
458 return pton4(src, dst, off);
459
460 if (af === 6)
461 return pton6(src, dst, off);
462
463 return -ENOENT;
464}
465
466/*
467 * Network to Presentation
468 */
469
470function ntop4(src, off) {
471 if (off == null)
472 off = 0;
473
474 assert(Buffer.isBuffer(src));
475 assert((off >>> 0) === off);
476
477 if (off + 4 > src.length)
478 return '';
479
480 let str = '';
481 str += dec(src[off + 0]);
482 str += '.';
483 str += dec(src[off + 1]);
484 str += '.';
485 str += dec(src[off + 2]);
486 str += '.';
487 str += dec(src[off + 3]);
488
489 return str;
490}
491
492function ntop6(src, off) {
493 if (off == null)
494 off = 0;
495
496 assert(Buffer.isBuffer(src));
497 assert((off >>> 0) === off);
498
499 if (off + 16 > src.length)
500 return '';
501
502 let bestBase = -1;
503 let bestLen = 0;
504 let curBase = -1;
505 let curLen = 0;
506 let str = '';
507
508 const words = UINT16;
509
510 for (let i = 0; i < 16; i++)
511 words[i] = 0;
512
513 for (let i = 0; i < 16; i++)
514 words[i >>> 1] |= src[off + i] << ((1 - (i & 1)) << 3);
515
516 for (let i = 0; i < (16 / 2); i++) {
517 if (words[i] === 0) {
518 if (curBase === -1) {
519 curBase = i;
520 curLen = 1;
521 } else {
522 curLen += 1;
523 }
524 } else {
525 if (curBase !== -1) {
526 if (bestBase === -1 || curLen > bestLen) {
527 bestBase = curBase;
528 bestLen = curLen;
529 }
530 curBase = -1;
531 }
532 }
533 }
534
535 if (curBase !== -1) {
536 if (bestBase === -1 || curLen > bestLen) {
537 bestBase = curBase;
538 bestLen = curLen;
539 }
540 }
541
542 if (bestBase !== -1 && bestLen < 2)
543 bestBase = -1;
544
545 for (let i = 0; i < (16 / 2); i++) {
546 // Are we inside the best run of 0x00's?
547 if (bestBase !== -1 && i >= bestBase && i < bestBase + bestLen) {
548 if (i === bestBase)
549 str += ':';
550 continue;
551 }
552
553 // Are we following an initial run of 0x00s or any real hex?
554 if (i !== 0)
555 str += ':';
556
557 // Is this address an encapsulated IPv4?
558 if (i === 6
559 && bestBase === 0
560 && (bestLen === 6
561 || (bestLen === 7 && words[7] !== 0x0001)
562 || (bestLen === 5 && words[5] === 0xffff))) {
563 const s = ntop4(src, off + 12);
564
565 if (!s)
566 return '';
567
568 str += s;
569
570 break;
571 }
572
573 str += hex(words[i]);
574 }
575
576 // Was it a trailing run of 0x00's?
577 if (bestBase !== -1 && bestBase + bestLen === 16 / 2)
578 str += ':';
579
580 return str;
581}
582
583function ntop(af, src, off) {
584 if (off == null)
585 off = 0;
586
587 assert((af >>> 0) === af);
588 assert(Buffer.isBuffer(src));
589 assert((off >>> 0) === off);
590
591 if (af === 4)
592 return ntop4(src, off);
593
594 if (af === 6)
595 return ntop6(src, off);
596
597 return '';
598}
599
600/*
601 * Public Helpers
602 */
603
604function family(str) {
605 if (pton4(str, null, 0) >= 0)
606 return 4;
607
608 if (pton6(str, null, 0) >= 0)
609 return 6;
610
611 return 0;
612}
613
614/**
615 * IPv4 Mapped - RFC 2765
616 * @param {Buffer} raw
617 * @param {Number} off
618 * @returns {Boolean}
619 */
620
621function mapped(raw, off) {
622 if (off == null)
623 off = 0;
624
625 assert(Buffer.isBuffer(raw));
626 assert((off >>> 0) === off);
627
628 if (off + 12 > raw.length)
629 return false;
630
631 return raw[off++] === 0x00
632 && raw[off++] === 0x00
633 && raw[off++] === 0x00
634 && raw[off++] === 0x00
635 && raw[off++] === 0x00
636 && raw[off++] === 0x00
637 && raw[off++] === 0x00
638 && raw[off++] === 0x00
639 && raw[off++] === 0x00
640 && raw[off++] === 0x00
641 && raw[off++] === 0xff
642 && raw[off++] === 0xff;
643}
644
645function onion(raw, off) {
646 if (off == null)
647 off = 0;
648
649 assert(Buffer.isBuffer(raw));
650 assert((off >>> 0) === off);
651
652 if (off + 6 > raw.length)
653 return false;
654
655 return raw[off++] === 0xfd
656 && raw[off++] === 0x87
657 && raw[off++] === 0xd8
658 && raw[off++] === 0x7e
659 && raw[off++] === 0xeb
660 && raw[off++] === 0x43;
661}
662
663function normalize(str) {
664 const raw = BUFFER16;
665
666 if (pton4(str, raw, 0) >= 0)
667 return ntop4(raw, 0);
668
669 if (pton6(str, raw, 0) >= 0)
670 return ntop6(raw, 0);
671
672 return '';
673}
674
675/*
676 * Helpers
677 */
678
679function byte(str, i) {
680 const ch = str.charCodeAt(i);
681
682 if (ch & 0xff80)
683 return 0x00;
684
685 return ch;
686}
687
688function dec(ch, i) {
689 return ch.toString(10);
690}
691
692function hex(w) {
693 let str = '';
694
695 for (let i = 3; i >= 0; i--) {
696 const n = (w >>> (i * 4)) & 0x0f;
697
698 if (n === 0 && str.length === 0)
699 continue;
700
701 str += CHARSET[n];
702 }
703
704 if (str.length === 0)
705 str += CHARSET[0];
706
707 return str;
708}
709
710function isDecimal(str, i) {
711 if (i + 1 > str.length)
712 return false;
713
714 const ch = byte(str, i);
715
716 return ch >= 0x30 && ch <= 0x39;
717}
718
719function isHex(str, i) {
720 if (i + 3 > str.length)
721 return false;
722
723 const a = byte(str, i);
724
725 if (a !== 0x30 /*0*/)
726 return false;
727
728 const b = byte(str, i + 1);
729
730 if (b !== 0x58 /*X*/ && b !== 0x78 /*x*/)
731 return false;
732
733 const c = byte(str, i + 2);
734
735 if (TABLE[c] === -1)
736 return false;
737
738 return true;
739}
740
741function isCIDR(str, i) {
742 if (i + 2 > str.length)
743 return false;
744
745 const a = byte(str, i);
746
747 if (a !== 0x2f /*/*/)
748 return false;
749
750 const b = byte(str, i + 1);
751
752 if (b < 0x30 || b > 0x39)
753 return false;
754
755 return true;
756}
757
758function isColon(str, i) {
759 if (i + 1 > str.length)
760 return false;
761
762 return byte(str, i) === 0x3a /*:*/;
763}
764
765function getBits(src, i) {
766 let word = 0;
767 let total = 0;
768
769 for (; i < src.length; i++) {
770 const ch = byte(src, i);
771
772 if (ch < 0x30 || ch > 0x39)
773 return -1;
774
775 if (total > 0 && word === 0)
776 return -1;
777
778 word *= 10;
779 word += ch - 0x30;
780 total += 1;
781
782 if (total > 3 || word > 128)
783 return -1;
784 }
785
786 if (total === 0)
787 return -1;
788
789 return word;
790}
791
792function getV4(src, i, dst, off) {
793 const start = off;
794
795 let word = 0;
796 let total = 0;
797
798 for (; i < src.length; i++) {
799 const ch = byte(src, i);
800
801 if (ch >= 0x30 && ch <= 0x39) {
802 if (total > 0 && word === 0)
803 return -1;
804
805 word *= 10;
806 word += ch - 0x30;
807 total += 1;
808
809 if (total > 3 || word > 255)
810 return -1;
811
812 continue;
813 }
814
815 if (ch === 0x2e /*.*/ || ch === 0x2f /*/*/) {
816 if (off - start > 3)
817 return -1;
818
819 if (dst) {
820 if (off + 1 > dst.length)
821 return -1;
822 dst[off] = word;
823 }
824
825 off += 1;
826
827 if (ch === 0x2f)
828 return getBits(src, i + 1);
829
830 word = 0;
831 total = 0;
832
833 continue;
834 }
835
836 return -1;
837 }
838
839 if (total === 0)
840 return -1;
841
842 if (off - start > 3)
843 return -1;
844
845 if (dst) {
846 if (off + 1 > dst.length)
847 return -1;
848 dst[off] = word;
849 }
850
851 off += 1;
852
853 return 0;
854}
855
856/*
857 * Expose
858 */
859
860exports.pton4 = pton4;
861exports.pton6 = pton6;
862exports.pton = pton;
863
864exports.ntop4 = ntop4;
865exports.ntop6 = ntop6;
866exports.ntop = ntop;
867
868exports.family = family;
869exports.mapped = mapped;
870exports.onion = onion;
871exports.normalize = normalize;