11cb0ef41Sopenharmony_ci'use strict'; 21cb0ef41Sopenharmony_ci 31cb0ef41Sopenharmony_ciconst common = require('../common'); 41cb0ef41Sopenharmony_ciconst { parseDNSPacket, writeDNSPacket } = require('../common/dns'); 51cb0ef41Sopenharmony_ci 61cb0ef41Sopenharmony_ciconst assert = require('assert'); 71cb0ef41Sopenharmony_ciconst dgram = require('dgram'); 81cb0ef41Sopenharmony_ciconst { Resolver } = require('dns'); 91cb0ef41Sopenharmony_ciconst { createConnection, createServer } = require('net'); 101cb0ef41Sopenharmony_ci 111cb0ef41Sopenharmony_ci// Test that happy eyeballs algorithm is properly implemented. 121cb0ef41Sopenharmony_ci 131cb0ef41Sopenharmony_ci// Purposely not using setDefaultAutoSelectFamilyAttemptTimeout here to test the 141cb0ef41Sopenharmony_ci// parameter is correctly used in options. 151cb0ef41Sopenharmony_ci// 161cb0ef41Sopenharmony_ci// Some of the windows machines in the CI need more time to establish connection 171cb0ef41Sopenharmony_ciconst autoSelectFamilyAttemptTimeout = common.platformTimeout(common.isWindows ? 1500 : 250); 181cb0ef41Sopenharmony_ci 191cb0ef41Sopenharmony_cifunction _lookup(resolver, hostname, options, cb) { 201cb0ef41Sopenharmony_ci resolver.resolve(hostname, 'ANY', (err, replies) => { 211cb0ef41Sopenharmony_ci assert.notStrictEqual(options.family, 4); 221cb0ef41Sopenharmony_ci 231cb0ef41Sopenharmony_ci if (err) { 241cb0ef41Sopenharmony_ci return cb(err); 251cb0ef41Sopenharmony_ci } 261cb0ef41Sopenharmony_ci 271cb0ef41Sopenharmony_ci const hosts = replies 281cb0ef41Sopenharmony_ci .map((r) => ({ address: r.address, family: r.type === 'AAAA' ? 6 : 4 })) 291cb0ef41Sopenharmony_ci .sort((a, b) => b.family - a.family); 301cb0ef41Sopenharmony_ci 311cb0ef41Sopenharmony_ci if (options.all === true) { 321cb0ef41Sopenharmony_ci return cb(null, hosts); 331cb0ef41Sopenharmony_ci } 341cb0ef41Sopenharmony_ci 351cb0ef41Sopenharmony_ci return cb(null, hosts[0].address, hosts[0].family); 361cb0ef41Sopenharmony_ci }); 371cb0ef41Sopenharmony_ci} 381cb0ef41Sopenharmony_ci 391cb0ef41Sopenharmony_cifunction createDnsServer(ipv6Addrs, ipv4Addrs, cb) { 401cb0ef41Sopenharmony_ci if (!Array.isArray(ipv6Addrs)) { 411cb0ef41Sopenharmony_ci ipv6Addrs = [ipv6Addrs]; 421cb0ef41Sopenharmony_ci } 431cb0ef41Sopenharmony_ci 441cb0ef41Sopenharmony_ci if (!Array.isArray(ipv4Addrs)) { 451cb0ef41Sopenharmony_ci ipv4Addrs = [ipv4Addrs]; 461cb0ef41Sopenharmony_ci } 471cb0ef41Sopenharmony_ci 481cb0ef41Sopenharmony_ci // Create a DNS server which replies with a AAAA and a A record for the same host 491cb0ef41Sopenharmony_ci const socket = dgram.createSocket('udp4'); 501cb0ef41Sopenharmony_ci 511cb0ef41Sopenharmony_ci socket.on('message', common.mustCall((msg, { address, port }) => { 521cb0ef41Sopenharmony_ci const parsed = parseDNSPacket(msg); 531cb0ef41Sopenharmony_ci const domain = parsed.questions[0].domain; 541cb0ef41Sopenharmony_ci assert.strictEqual(domain, 'example.org'); 551cb0ef41Sopenharmony_ci 561cb0ef41Sopenharmony_ci socket.send(writeDNSPacket({ 571cb0ef41Sopenharmony_ci id: parsed.id, 581cb0ef41Sopenharmony_ci questions: parsed.questions, 591cb0ef41Sopenharmony_ci answers: [ 601cb0ef41Sopenharmony_ci ...ipv6Addrs.map((address) => ({ type: 'AAAA', address, ttl: 123, domain: 'example.org' })), 611cb0ef41Sopenharmony_ci ...ipv4Addrs.map((address) => ({ type: 'A', address, ttl: 123, domain: 'example.org' })), 621cb0ef41Sopenharmony_ci ] 631cb0ef41Sopenharmony_ci }), port, address); 641cb0ef41Sopenharmony_ci })); 651cb0ef41Sopenharmony_ci 661cb0ef41Sopenharmony_ci socket.bind(0, () => { 671cb0ef41Sopenharmony_ci const resolver = new Resolver(); 681cb0ef41Sopenharmony_ci resolver.setServers([`127.0.0.1:${socket.address().port}`]); 691cb0ef41Sopenharmony_ci 701cb0ef41Sopenharmony_ci cb({ dnsServer: socket, lookup: _lookup.bind(null, resolver) }); 711cb0ef41Sopenharmony_ci }); 721cb0ef41Sopenharmony_ci} 731cb0ef41Sopenharmony_ci 741cb0ef41Sopenharmony_ci// Test that IPV4 is reached if IPV6 is not reachable 751cb0ef41Sopenharmony_ci{ 761cb0ef41Sopenharmony_ci createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) { 771cb0ef41Sopenharmony_ci const ipv4Server = createServer((socket) => { 781cb0ef41Sopenharmony_ci socket.on('data', common.mustCall(() => { 791cb0ef41Sopenharmony_ci socket.write('response-ipv4'); 801cb0ef41Sopenharmony_ci socket.end(); 811cb0ef41Sopenharmony_ci })); 821cb0ef41Sopenharmony_ci }); 831cb0ef41Sopenharmony_ci 841cb0ef41Sopenharmony_ci ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => { 851cb0ef41Sopenharmony_ci const port = ipv4Server.address().port; 861cb0ef41Sopenharmony_ci 871cb0ef41Sopenharmony_ci const connection = createConnection({ 881cb0ef41Sopenharmony_ci host: 'example.org', 891cb0ef41Sopenharmony_ci port: port, 901cb0ef41Sopenharmony_ci lookup, 911cb0ef41Sopenharmony_ci autoSelectFamily: true, 921cb0ef41Sopenharmony_ci autoSelectFamilyAttemptTimeout, 931cb0ef41Sopenharmony_ci }); 941cb0ef41Sopenharmony_ci 951cb0ef41Sopenharmony_ci let response = ''; 961cb0ef41Sopenharmony_ci connection.setEncoding('utf-8'); 971cb0ef41Sopenharmony_ci 981cb0ef41Sopenharmony_ci connection.on('ready', common.mustCall(() => { 991cb0ef41Sopenharmony_ci assert.deepStrictEqual(connection.autoSelectFamilyAttemptedAddresses, [`::1:${port}`, `127.0.0.1:${port}`]); 1001cb0ef41Sopenharmony_ci })); 1011cb0ef41Sopenharmony_ci 1021cb0ef41Sopenharmony_ci connection.on('data', (chunk) => { 1031cb0ef41Sopenharmony_ci response += chunk; 1041cb0ef41Sopenharmony_ci }); 1051cb0ef41Sopenharmony_ci 1061cb0ef41Sopenharmony_ci connection.on('end', common.mustCall(() => { 1071cb0ef41Sopenharmony_ci assert.strictEqual(response, 'response-ipv4'); 1081cb0ef41Sopenharmony_ci ipv4Server.close(); 1091cb0ef41Sopenharmony_ci dnsServer.close(); 1101cb0ef41Sopenharmony_ci })); 1111cb0ef41Sopenharmony_ci 1121cb0ef41Sopenharmony_ci connection.write('request'); 1131cb0ef41Sopenharmony_ci })); 1141cb0ef41Sopenharmony_ci })); 1151cb0ef41Sopenharmony_ci} 1161cb0ef41Sopenharmony_ci 1171cb0ef41Sopenharmony_ci// Test that only the last successful connection is established. 1181cb0ef41Sopenharmony_ci{ 1191cb0ef41Sopenharmony_ci createDnsServer( 1201cb0ef41Sopenharmony_ci ['2606:4700::6810:85e5', '2606:4700::6810:84e5', '::1'], 1211cb0ef41Sopenharmony_ci ['104.20.22.46', '104.20.23.46', '127.0.0.1'], 1221cb0ef41Sopenharmony_ci common.mustCall(function({ dnsServer, lookup }) { 1231cb0ef41Sopenharmony_ci const ipv4Server = createServer((socket) => { 1241cb0ef41Sopenharmony_ci socket.on('data', common.mustCall(() => { 1251cb0ef41Sopenharmony_ci socket.write('response-ipv4'); 1261cb0ef41Sopenharmony_ci socket.end(); 1271cb0ef41Sopenharmony_ci })); 1281cb0ef41Sopenharmony_ci }); 1291cb0ef41Sopenharmony_ci 1301cb0ef41Sopenharmony_ci ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => { 1311cb0ef41Sopenharmony_ci const port = ipv4Server.address().port; 1321cb0ef41Sopenharmony_ci 1331cb0ef41Sopenharmony_ci const connection = createConnection({ 1341cb0ef41Sopenharmony_ci host: 'example.org', 1351cb0ef41Sopenharmony_ci port: port, 1361cb0ef41Sopenharmony_ci lookup, 1371cb0ef41Sopenharmony_ci autoSelectFamily: true, 1381cb0ef41Sopenharmony_ci autoSelectFamilyAttemptTimeout, 1391cb0ef41Sopenharmony_ci }); 1401cb0ef41Sopenharmony_ci 1411cb0ef41Sopenharmony_ci let response = ''; 1421cb0ef41Sopenharmony_ci connection.setEncoding('utf-8'); 1431cb0ef41Sopenharmony_ci 1441cb0ef41Sopenharmony_ci connection.on('ready', common.mustCall(() => { 1451cb0ef41Sopenharmony_ci assert.deepStrictEqual( 1461cb0ef41Sopenharmony_ci connection.autoSelectFamilyAttemptedAddresses, 1471cb0ef41Sopenharmony_ci [ 1481cb0ef41Sopenharmony_ci `2606:4700::6810:85e5:${port}`, 1491cb0ef41Sopenharmony_ci `104.20.22.46:${port}`, 1501cb0ef41Sopenharmony_ci `2606:4700::6810:84e5:${port}`, 1511cb0ef41Sopenharmony_ci `104.20.23.46:${port}`, 1521cb0ef41Sopenharmony_ci `::1:${port}`, 1531cb0ef41Sopenharmony_ci `127.0.0.1:${port}`, 1541cb0ef41Sopenharmony_ci ] 1551cb0ef41Sopenharmony_ci ); 1561cb0ef41Sopenharmony_ci })); 1571cb0ef41Sopenharmony_ci 1581cb0ef41Sopenharmony_ci connection.on('data', (chunk) => { 1591cb0ef41Sopenharmony_ci response += chunk; 1601cb0ef41Sopenharmony_ci }); 1611cb0ef41Sopenharmony_ci 1621cb0ef41Sopenharmony_ci connection.on('end', common.mustCall(() => { 1631cb0ef41Sopenharmony_ci assert.strictEqual(response, 'response-ipv4'); 1641cb0ef41Sopenharmony_ci ipv4Server.close(); 1651cb0ef41Sopenharmony_ci dnsServer.close(); 1661cb0ef41Sopenharmony_ci })); 1671cb0ef41Sopenharmony_ci 1681cb0ef41Sopenharmony_ci connection.write('request'); 1691cb0ef41Sopenharmony_ci })); 1701cb0ef41Sopenharmony_ci }) 1711cb0ef41Sopenharmony_ci ); 1721cb0ef41Sopenharmony_ci} 1731cb0ef41Sopenharmony_ci 1741cb0ef41Sopenharmony_ci// Test that IPV4 is NOT reached if IPV6 is reachable 1751cb0ef41Sopenharmony_ciif (common.hasIPv6) { 1761cb0ef41Sopenharmony_ci createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) { 1771cb0ef41Sopenharmony_ci const ipv4Server = createServer((socket) => { 1781cb0ef41Sopenharmony_ci socket.on('data', common.mustNotCall(() => { 1791cb0ef41Sopenharmony_ci socket.write('response-ipv4'); 1801cb0ef41Sopenharmony_ci socket.end(); 1811cb0ef41Sopenharmony_ci })); 1821cb0ef41Sopenharmony_ci }); 1831cb0ef41Sopenharmony_ci 1841cb0ef41Sopenharmony_ci const ipv6Server = createServer((socket) => { 1851cb0ef41Sopenharmony_ci socket.on('data', common.mustCall(() => { 1861cb0ef41Sopenharmony_ci socket.write('response-ipv6'); 1871cb0ef41Sopenharmony_ci socket.end(); 1881cb0ef41Sopenharmony_ci })); 1891cb0ef41Sopenharmony_ci }); 1901cb0ef41Sopenharmony_ci 1911cb0ef41Sopenharmony_ci ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => { 1921cb0ef41Sopenharmony_ci const port = ipv4Server.address().port; 1931cb0ef41Sopenharmony_ci 1941cb0ef41Sopenharmony_ci ipv6Server.listen(port, '::1', common.mustCall(() => { 1951cb0ef41Sopenharmony_ci const connection = createConnection({ 1961cb0ef41Sopenharmony_ci host: 'example.org', 1971cb0ef41Sopenharmony_ci port, 1981cb0ef41Sopenharmony_ci lookup, 1991cb0ef41Sopenharmony_ci autoSelectFamily: true, 2001cb0ef41Sopenharmony_ci autoSelectFamilyAttemptTimeout, 2011cb0ef41Sopenharmony_ci }); 2021cb0ef41Sopenharmony_ci 2031cb0ef41Sopenharmony_ci let response = ''; 2041cb0ef41Sopenharmony_ci connection.setEncoding('utf-8'); 2051cb0ef41Sopenharmony_ci 2061cb0ef41Sopenharmony_ci connection.on('ready', common.mustCall(() => { 2071cb0ef41Sopenharmony_ci assert.deepStrictEqual(connection.autoSelectFamilyAttemptedAddresses, [`::1:${port}`]); 2081cb0ef41Sopenharmony_ci })); 2091cb0ef41Sopenharmony_ci 2101cb0ef41Sopenharmony_ci connection.on('data', (chunk) => { 2111cb0ef41Sopenharmony_ci response += chunk; 2121cb0ef41Sopenharmony_ci }); 2131cb0ef41Sopenharmony_ci 2141cb0ef41Sopenharmony_ci connection.on('end', common.mustCall(() => { 2151cb0ef41Sopenharmony_ci assert.strictEqual(response, 'response-ipv6'); 2161cb0ef41Sopenharmony_ci ipv4Server.close(); 2171cb0ef41Sopenharmony_ci ipv6Server.close(); 2181cb0ef41Sopenharmony_ci dnsServer.close(); 2191cb0ef41Sopenharmony_ci })); 2201cb0ef41Sopenharmony_ci 2211cb0ef41Sopenharmony_ci connection.write('request'); 2221cb0ef41Sopenharmony_ci })); 2231cb0ef41Sopenharmony_ci })); 2241cb0ef41Sopenharmony_ci })); 2251cb0ef41Sopenharmony_ci} 2261cb0ef41Sopenharmony_ci 2271cb0ef41Sopenharmony_ci// Test that when all errors are returned when no connections succeeded 2281cb0ef41Sopenharmony_ci{ 2291cb0ef41Sopenharmony_ci createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) { 2301cb0ef41Sopenharmony_ci const connection = createConnection({ 2311cb0ef41Sopenharmony_ci host: 'example.org', 2321cb0ef41Sopenharmony_ci port: 10, 2331cb0ef41Sopenharmony_ci lookup, 2341cb0ef41Sopenharmony_ci autoSelectFamily: true, 2351cb0ef41Sopenharmony_ci autoSelectFamilyAttemptTimeout, 2361cb0ef41Sopenharmony_ci }); 2371cb0ef41Sopenharmony_ci 2381cb0ef41Sopenharmony_ci connection.on('ready', common.mustNotCall()); 2391cb0ef41Sopenharmony_ci connection.on('error', common.mustCall((error) => { 2401cb0ef41Sopenharmony_ci assert.deepStrictEqual(connection.autoSelectFamilyAttemptedAddresses, ['::1:10', '127.0.0.1:10']); 2411cb0ef41Sopenharmony_ci assert.strictEqual(error.constructor.name, 'AggregateError'); 2421cb0ef41Sopenharmony_ci assert.strictEqual(error.errors.length, 2); 2431cb0ef41Sopenharmony_ci 2441cb0ef41Sopenharmony_ci const errors = error.errors.map((e) => e.message); 2451cb0ef41Sopenharmony_ci assert.ok(errors.includes('connect ECONNREFUSED 127.0.0.1:10')); 2461cb0ef41Sopenharmony_ci 2471cb0ef41Sopenharmony_ci if (common.hasIPv6) { 2481cb0ef41Sopenharmony_ci assert.ok(errors.includes('connect ECONNREFUSED ::1:10')); 2491cb0ef41Sopenharmony_ci } 2501cb0ef41Sopenharmony_ci 2511cb0ef41Sopenharmony_ci dnsServer.close(); 2521cb0ef41Sopenharmony_ci })); 2531cb0ef41Sopenharmony_ci })); 2541cb0ef41Sopenharmony_ci} 2551cb0ef41Sopenharmony_ci 2561cb0ef41Sopenharmony_ci// Test that the option can be disabled 2571cb0ef41Sopenharmony_ci{ 2581cb0ef41Sopenharmony_ci createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) { 2591cb0ef41Sopenharmony_ci const ipv4Server = createServer((socket) => { 2601cb0ef41Sopenharmony_ci socket.on('data', common.mustCall(() => { 2611cb0ef41Sopenharmony_ci socket.write('response-ipv4'); 2621cb0ef41Sopenharmony_ci socket.end(); 2631cb0ef41Sopenharmony_ci })); 2641cb0ef41Sopenharmony_ci }); 2651cb0ef41Sopenharmony_ci 2661cb0ef41Sopenharmony_ci ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => { 2671cb0ef41Sopenharmony_ci const port = ipv4Server.address().port; 2681cb0ef41Sopenharmony_ci 2691cb0ef41Sopenharmony_ci const connection = createConnection({ 2701cb0ef41Sopenharmony_ci host: 'example.org', 2711cb0ef41Sopenharmony_ci port, 2721cb0ef41Sopenharmony_ci lookup, 2731cb0ef41Sopenharmony_ci autoSelectFamily: false, 2741cb0ef41Sopenharmony_ci }); 2751cb0ef41Sopenharmony_ci 2761cb0ef41Sopenharmony_ci connection.on('ready', common.mustNotCall()); 2771cb0ef41Sopenharmony_ci connection.on('error', common.mustCall((error) => { 2781cb0ef41Sopenharmony_ci assert.strictEqual(connection.autoSelectFamilyAttemptedAddresses, undefined); 2791cb0ef41Sopenharmony_ci 2801cb0ef41Sopenharmony_ci if (common.hasIPv6) { 2811cb0ef41Sopenharmony_ci assert.strictEqual(error.code, 'ECONNREFUSED'); 2821cb0ef41Sopenharmony_ci assert.strictEqual(error.message, `connect ECONNREFUSED ::1:${port}`); 2831cb0ef41Sopenharmony_ci } else if (error.code === 'EAFNOSUPPORT') { 2841cb0ef41Sopenharmony_ci assert.strictEqual(error.message, `connect EAFNOSUPPORT ::1:${port} - Local (undefined:undefined)`); 2851cb0ef41Sopenharmony_ci } else if (common.isIBMi) { 2861cb0ef41Sopenharmony_ci // IBMi returns EUNATCH (ERRNO 42) when IPv6 is disabled 2871cb0ef41Sopenharmony_ci // keep this errno assertion until EUNATCH is recognized by libuv 2881cb0ef41Sopenharmony_ci assert.strictEqual(error.errno, -42); 2891cb0ef41Sopenharmony_ci } else { 2901cb0ef41Sopenharmony_ci assert.strictEqual(error.code, 'EADDRNOTAVAIL'); 2911cb0ef41Sopenharmony_ci assert.strictEqual(error.message, `connect EADDRNOTAVAIL ::1:${port} - Local (:::0)`); 2921cb0ef41Sopenharmony_ci } 2931cb0ef41Sopenharmony_ci 2941cb0ef41Sopenharmony_ci ipv4Server.close(); 2951cb0ef41Sopenharmony_ci dnsServer.close(); 2961cb0ef41Sopenharmony_ci })); 2971cb0ef41Sopenharmony_ci })); 2981cb0ef41Sopenharmony_ci })); 2991cb0ef41Sopenharmony_ci} 300