• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const common = require('../common');
4const { parseDNSPacket, writeDNSPacket } = require('../common/dns');
5
6const assert = require('assert');
7const dgram = require('dgram');
8const { Resolver } = require('dns');
9const { createConnection, createServer, setDefaultAutoSelectFamily } = require('net');
10
11// Test that the default for happy eyeballs algorithm is properly respected.
12
13let autoSelectFamilyAttemptTimeout = common.platformTimeout(250);
14if (common.isWindows) {
15  // Some of the windows machines in the CI need more time to establish connection
16  autoSelectFamilyAttemptTimeout = common.platformTimeout(1500);
17}
18
19function _lookup(resolver, hostname, options, cb) {
20  resolver.resolve(hostname, 'ANY', (err, replies) => {
21    assert.notStrictEqual(options.family, 4);
22
23    if (err) {
24      return cb(err);
25    }
26
27    const hosts = replies
28      .map((r) => ({ address: r.address, family: r.type === 'AAAA' ? 6 : 4 }))
29      .sort((a, b) => b.family - a.family);
30
31    if (options.all === true) {
32      return cb(null, hosts);
33    }
34
35    return cb(null, hosts[0].address, hosts[0].family);
36  });
37}
38
39function createDnsServer(ipv6Addr, ipv4Addr, cb) {
40  // Create a DNS server which replies with a AAAA and a A record for the same host
41  const socket = dgram.createSocket('udp4');
42
43  socket.on('message', common.mustCall((msg, { address, port }) => {
44    const parsed = parseDNSPacket(msg);
45    const domain = parsed.questions[0].domain;
46    assert.strictEqual(domain, 'example.org');
47
48    socket.send(writeDNSPacket({
49      id: parsed.id,
50      questions: parsed.questions,
51      answers: [
52        { type: 'AAAA', address: ipv6Addr, ttl: 123, domain: 'example.org' },
53        { type: 'A', address: ipv4Addr, ttl: 123, domain: 'example.org' },
54      ]
55    }), port, address);
56  }));
57
58  socket.bind(0, () => {
59    const resolver = new Resolver();
60    resolver.setServers([`127.0.0.1:${socket.address().port}`]);
61
62    cb({ dnsServer: socket, lookup: _lookup.bind(null, resolver) });
63  });
64}
65
66// Test that IPV4 is reached by default if IPV6 is not reachable and the default is enabled
67{
68  createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) {
69    const ipv4Server = createServer((socket) => {
70      socket.on('data', common.mustCall(() => {
71        socket.write('response-ipv4');
72        socket.end();
73      }));
74    });
75
76    ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
77      setDefaultAutoSelectFamily(true);
78
79      const connection = createConnection({
80        host: 'example.org',
81        port: ipv4Server.address().port,
82        lookup,
83        autoSelectFamilyAttemptTimeout,
84      });
85
86      let response = '';
87      connection.setEncoding('utf-8');
88
89      connection.on('data', (chunk) => {
90        response += chunk;
91      });
92
93      connection.on('end', common.mustCall(() => {
94        assert.strictEqual(response, 'response-ipv4');
95        ipv4Server.close();
96        dnsServer.close();
97      }));
98
99      connection.write('request');
100    }));
101  }));
102}
103
104// Test that IPV4 is not reached by default if IPV6 is not reachable and the default is disabled
105{
106  createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) {
107    const ipv4Server = createServer((socket) => {
108      socket.on('data', common.mustCall(() => {
109        socket.write('response-ipv4');
110        socket.end();
111      }));
112    });
113
114    ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
115      setDefaultAutoSelectFamily(false);
116
117      const port = ipv4Server.address().port;
118
119      const connection = createConnection({
120        host: 'example.org',
121        port,
122        lookup,
123      });
124
125      connection.on('ready', common.mustNotCall());
126      connection.on('error', common.mustCall((error) => {
127        if (common.hasIPv6) {
128          assert.strictEqual(error.code, 'ECONNREFUSED');
129          assert.strictEqual(error.message, `connect ECONNREFUSED ::1:${port}`);
130        } else if (error.code === 'EAFNOSUPPORT') {
131          assert.strictEqual(error.message, `connect EAFNOSUPPORT ::1:${port} - Local (undefined:undefined)`);
132        } else if (common.isIBMi) {
133          // IBMi returns EUNATCH (ERRNO 42) when IPv6 is disabled
134          // keep this errno assertion until EUNATCH is recognized by libuv
135          assert.strictEqual(error.errno, -42);
136        } else {
137          assert.strictEqual(error.code, 'EADDRNOTAVAIL');
138          assert.strictEqual(error.message, `connect EADDRNOTAVAIL ::1:${port} - Local (:::0)`);
139        }
140
141        ipv4Server.close();
142        dnsServer.close();
143      }));
144    }));
145  }));
146}
147