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