| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent fffc300 commit 1e01bdc
4 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -62,6 +62,7 @@ const { | |||
| 62 | 62 | UV_ECANCELED, | |
| 63 | 63 | UV_ETIMEDOUT, | |
| 64 | 64 | } = internalBinding('uv'); | |
| 65 | + const { convertIpv6StringToBuffer } = internalBinding('cares_wrap'); | ||
| 65 | 66 | ||
| 66 | 67 | const { Buffer } = require('buffer'); | |
| 67 | 68 | const { ShutdownWrap } = internalBinding('stream_wrap'); | |
@@ -2118,19 +2119,51 @@ Server.prototype.listen = function(...args) { | |||
| 2118 | 2119 | throw new ERR_INVALID_ARG_VALUE('options', options); | |
| 2119 | 2120 | }; | |
| 2120 | 2121 | ||
| 2122 | + function isIpv6LinkLocal(ip) { | ||
| 2123 | + if (!isIPv6(ip)) { return false; } | ||
| 2124 | + | ||
| 2125 | + const ipv6Buffer = convertIpv6StringToBuffer(ip); | ||
| 2126 | + const firstByte = ipv6Buffer[0]; // The first 8 bits | ||
| 2127 | + const secondByte = ipv6Buffer[1]; // The next 8 bits | ||
| 2128 | + | ||
| 2129 | + // The link-local prefix is `1111111010`, which in hexadecimal is `fe80` | ||
| 2130 | + // First 8 bits (firstByte) should be `11111110` (0xfe) | ||
| 2131 | + // The next 2 bits of the second byte should be `10` (0x80) | ||
| 2132 | + | ||
| 2133 | + const isFirstByteCorrect = (firstByte === 0xfe); // 0b11111110 == 0xfe | ||
| 2134 | + const isSecondByteCorrect = (secondByte & 0xc0) === 0x80; // 0b10xxxxxx == 0x80 | ||
| 2135 | + | ||
| 2136 | + return isFirstByteCorrect && isSecondByteCorrect; | ||
| 2137 | + } | ||
| 2138 | + | ||
| 2139 | + function filterOnlyValidAddress(addresses) { | ||
| 2140 | + // Return the first non IPV6 link-local address if present | ||
| 2141 | + for (const address of addresses) { | ||
| 2142 | + if (!isIpv6LinkLocal(address.address)) { | ||
| 2143 | + return address; | ||
| 2144 | + } | ||
| 2145 | + } | ||
| 2146 | + | ||
| 2147 | + // Otherwise return the first address | ||
| 2148 | + return addresses[0]; | ||
| 2149 | + } | ||
| 2150 | + | ||
| 2121 | 2151 | function lookupAndListen(self, port, address, backlog, | |
| 2122 | 2152 | exclusive, flags) { | |
| 2123 | 2153 | if (dns === undefined) dns = require('dns'); | |
| 2124 | 2154 | const listeningId = self._listeningId; | |
| 2125 | - dns.lookup(address, function doListen(err, ip, addressType) { | ||
| 2155 | + | ||
| 2156 | + dns.lookup(address, { all: true }, (err, addresses) => { | ||
| 2126 | 2157 | if (listeningId !== self._listeningId) { | |
| 2127 | 2158 | return; | |
| 2128 | 2159 | } | |
| 2129 | 2160 | if (err) { | |
| 2130 | 2161 | self.emit('error', err); | |
| 2131 | 2162 | } else { | |
| 2132 | - addressType = ip ? addressType : 4; | ||
| 2133 | - listenInCluster(self, ip, port, addressType, | ||
| 2163 | + const validAddress = filterOnlyValidAddress(addresses); | ||
| 2164 | + const family = validAddress?.family || 4; | ||
| 2165 | + | ||
| 2166 | + listenInCluster(self, validAddress.address, port, family, | ||
| 2134 | 2167 | backlog, undefined, exclusive, flags); | |
| 2135 | 2168 | } | |
| 2136 | 2169 | }); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1566,6 +1566,24 @@ void CanonicalizeIP(const FunctionCallbackInfo<Value>& args) { | |||
| 1566 | 1566 | args.GetReturnValue().Set(val); | |
| 1567 | 1567 | } | |
| 1568 | 1568 | ||
| 1569 | + void ConvertIpv6StringToBuffer(const FunctionCallbackInfo<Value>& args) { | ||
| 1570 | + Isolate* isolate = args.GetIsolate(); | ||
| 1571 | + node::Utf8Value ip(isolate, args[0]); | ||
| 1572 | + unsigned char dst[16]; // IPv6 addresses are 128 bits (16 bytes) | ||
| 1573 | + | ||
| 1574 | + if (uv_inet_pton(AF_INET6, *ip, dst) != 0) { | ||
| 1575 | + isolate->ThrowException(v8::Exception::Error( | ||
| 1576 | + String::NewFromUtf8(isolate, "Invalid IPv6 address").ToLocalChecked())); | ||
| 1577 | + return; | ||
| 1578 | + } | ||
| 1579 | + | ||
| 1580 | + Local<Object> buffer = | ||
| 1581 | + node::Buffer::Copy( | ||
| 1582 | + isolate, reinterpret_cast<const char*>(dst), sizeof(dst)) | ||
| 1583 | + .ToLocalChecked(); | ||
| 1584 | + args.GetReturnValue().Set(buffer); | ||
| 1585 | + } | ||
| 1586 | + | ||
| 1569 | 1587 | void GetAddrInfo(const FunctionCallbackInfo<Value>& args) { | |
| 1570 | 1588 | Environment* env = Environment::GetCurrent(args); | |
| 1571 | 1589 | ||
@@ -1902,6 +1920,8 @@ void Initialize(Local<Object> target, | |||
| 1902 | 1920 | SetMethod(context, target, "getaddrinfo", GetAddrInfo); | |
| 1903 | 1921 | SetMethod(context, target, "getnameinfo", GetNameInfo); | |
| 1904 | 1922 | SetMethodNoSideEffect(context, target, "canonicalizeIP", CanonicalizeIP); | |
| 1923 | + SetMethodNoSideEffect( | ||
| 1924 | + context, target, "convertIpv6StringToBuffer", ConvertIpv6StringToBuffer); | ||
| 1905 | 1925 | ||
| 1906 | 1926 | SetMethod(context, target, "strerror", StrError); | |
| 1907 | 1927 | ||
@@ -1995,6 +2015,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) { | |||
| 1995 | 2015 | registry->Register(GetAddrInfo); | |
| 1996 | 2016 | registry->Register(GetNameInfo); | |
| 1997 | 2017 | registry->Register(CanonicalizeIP); | |
| 2018 | + registry->Register(ConvertIpv6StringToBuffer); | ||
| 1998 | 2019 | registry->Register(StrError); | |
| 1999 | 2020 | registry->Register(ChannelWrap::New); | |
| 2000 | 2021 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,5 +3,5 @@ | |||
| 3 | 3 | const common = require('../common'); | |
| 4 | 4 | const net = require('net'); | |
| 5 | 5 | // Process should exit because it does not create a real TCP server. | |
| 6 | - // Paas localhost to ensure create TCP handle asynchronously because It causes DNS resolution. | ||
| 6 | + // Pass localhost to ensure create TCP handle asynchronously because it causes DNS resolution. | ||
| 7 | 7 | net.createServer().listen(0, 'localhost', common.mustNotCall()).close(); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,131 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + const common = require('../common'); | ||
| 3 | + const assert = require('assert'); | ||
| 4 | + const net = require('net'); | ||
| 5 | + const dns = require('dns'); | ||
| 6 | + const { mock } = require('node:test'); | ||
| 7 | + | ||
| 8 | + if (!common.hasIPv6) { | ||
| 9 | + common.printSkipMessage('IPv6 support is required for this test'); | ||
| 10 | + return; | ||
| 11 | + } | ||
| 12 | + | ||
| 13 | + // Test on IPv6 Server, dns.lookup throws an error | ||
| 14 | + { | ||
| 15 | + mock.method(dns, 'lookup', (hostname, options, callback) => { | ||
| 16 | + callback(new Error('Mocked error')); | ||
| 17 | + }); | ||
| 18 | + const host = 'ipv6_link_local'; | ||
| 19 | + | ||
| 20 | + const server = net.createServer(); | ||
| 21 | + | ||
| 22 | + server.on('error', common.mustCall((e) => { | ||
| 23 | + assert.strictEqual(e.message, 'Mocked error'); | ||
| 24 | + })); | ||
| 25 | + | ||
| 26 | + server.listen(common.PORT + 2, host); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + // Test on IPv6 Server, server.listen throws an error | ||
| 31 | + { | ||
| 32 | + mock.method(dns, 'lookup', (hostname, options, callback) => { | ||
| 33 | + if (hostname === 'ipv6_link_local') { | ||
| 34 | + callback(null, [{ address: 'fe80::1', family: 6 }]); | ||
| 35 | + } else { | ||
| 36 | + dns.lookup.wrappedMethod(hostname, options, callback); | ||
| 37 | + } | ||
| 38 | + }); | ||
| 39 | + const host = 'ipv6_link_local'; | ||
| 40 | + | ||
| 41 | + const server = net.createServer(); | ||
| 42 | + | ||
| 43 | + server.on('error', common.mustCall((e) => { | ||
| 44 | + assert.strictEqual(e.address, 'fe80::1'); | ||
| 45 | + assert.strictEqual(e.syscall, 'listen'); | ||
| 46 | + })); | ||
| 47 | + | ||
| 48 | + server.listen(common.PORT + 2, host); | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + // Test on IPv6 Server, picks 127.0.0.1 between that and a bunch of link-local addresses | ||
| 52 | + { | ||
| 53 | + | ||
| 54 | + mock.method(dns, 'lookup', (hostname, options, callback) => { | ||
| 55 | + if (hostname === 'ipv6_link_local_with_many_entries') { | ||
| 56 | + callback(null, [ | ||
| 57 | + { address: 'fe80::1', family: 6 }, | ||
| 58 | + { address: 'fe80::abcd:1234', family: 6 }, | ||
| 59 | + { address: 'fe80::1ff:fe23:4567:890a', family: 6 }, | ||
| 60 | + { address: 'fe80::200:5aee:feaa:20a2', family: 6 }, | ||
| 61 | + { address: 'fe80::f2de:f1ff:fe2b:3c4b', family: 6 }, | ||
| 62 | + { address: 'fe81::1', family: 6 }, | ||
| 63 | + { address: 'fe82::abcd:1234', family: 6 }, | ||
| 64 | + { address: 'fe83::1ff:fe23:4567:890a', family: 6 }, | ||
| 65 | + { address: 'fe84::200:5aee:feaa:20a2', family: 6 }, | ||
| 66 | + { address: 'fe85::f2de:f1ff:fe2b:3c4b', family: 6 }, | ||
| 67 | + { address: 'fe86::1', family: 6 }, | ||
| 68 | + { address: 'fe87::abcd:1234', family: 6 }, | ||
| 69 | + { address: 'fe88::1ff:fe23:4567:890a', family: 6 }, | ||
| 70 | + { address: 'fe89::200:5aee:feaa:20a2', family: 6 }, | ||
| 71 | + { address: 'fe8a::f2de:f1ff:fe2b:3c4b', family: 6 }, | ||
| 72 | + { address: 'fe8b::1', family: 6 }, | ||
| 73 | + { address: 'fe8c::abcd:1234', family: 6 }, | ||
| 74 | + { address: 'fe8d::1ff:fe23:4567:890a', family: 6 }, | ||
| 75 | + { address: 'fe8e::200:5aee:feaa:20a2', family: 6 }, | ||
| 76 | + { address: 'fe8f::f2de:f1ff:fe2b:3c4b', family: 6 }, | ||
| 77 | + { address: 'fea0::1', family: 6 }, | ||
| 78 | + { address: 'febf::abcd:1234', family: 6 }, | ||
| 79 | + { address: 'febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff', family: 6 }, | ||
| 80 | + { address: '127.0.0.1', family: 4 }, | ||
| 81 | + ]); | ||
| 82 | + } else { | ||
| 83 | + dns.lookup.wrappedMethod(hostname, options, callback); | ||
| 84 | + } | ||
| 85 | + }); | ||
| 86 | + | ||
| 87 | + const host = 'ipv6_link_local_with_many_entries'; | ||
| 88 | + | ||
| 89 | + const server = net.createServer(); | ||
| 90 | + | ||
| 91 | + server.on('error', common.mustNotCall()); | ||
| 92 | + | ||
| 93 | + server.listen(common.PORT + 3, host, common.mustCall(() => { | ||
| 94 | + const address = server.address(); | ||
| 95 | + assert.strictEqual(address.address, '127.0.0.1'); | ||
| 96 | + assert.strictEqual(address.port, common.PORT + 3); | ||
| 97 | + assert.strictEqual(address.family, 'IPv4'); | ||
| 98 | + server.close(); | ||
| 99 | + })); | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + | ||
| 103 | + // Test on IPv6 Server, picks ::1 because the other address is a link-local address | ||
| 104 | + { | ||
| 105 | + | ||
| 106 | + const host = 'ipv6_link_local_with_double_entry'; | ||
| 107 | + const validIpv6Address = '::1'; | ||
| 108 | + | ||
| 109 | + mock.method(dns, 'lookup', (hostname, options, callback) => { | ||
| 110 | + if (hostname === 'ipv6_link_local_with_double_entry') { | ||
| 111 | + callback(null, [ | ||
| 112 | + { address: 'fe80::1', family: 6 }, | ||
| 113 | + { address: validIpv6Address, family: 6 }, | ||
| 114 | + ]); | ||
| 115 | + } else { | ||
| 116 | + dns.lookup.wrappedMethod(hostname, options, callback); | ||
| 117 | + } | ||
| 118 | + }); | ||
| 119 | + | ||
| 120 | + const server = net.createServer(); | ||
| 121 | + | ||
| 122 | + server.on('error', common.mustNotCall()); | ||
| 123 | + | ||
| 124 | + server.listen(common.PORT + 4, host, common.mustCall(() => { | ||
| 125 | + const address = server.address(); | ||
| 126 | + assert.strictEqual(address.address, validIpv6Address); | ||
| 127 | + assert.strictEqual(address.port, common.PORT + 4); | ||
| 128 | + assert.strictEqual(address.family, 'IPv6'); | ||
| 129 | + server.close(); | ||
| 130 | + })); | ||
| 131 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments