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, setDefaultAutoSelectFamilyAttemptTimeout } = require('net'); 10 11// Test that happy eyeballs algorithm is properly implemented when a A record is returned first. 12 13// Some of the windows machines in the CI need more time to establish connection 14setDefaultAutoSelectFamilyAttemptTimeout(common.platformTimeout(common.isWindows ? 1500 : 250)); 15 16function _lookup(resolver, hostname, options, cb) { 17 resolver.resolve(hostname, 'ANY', (err, replies) => { 18 assert.notStrictEqual(options.family, 4); 19 20 if (err) { 21 return cb(err); 22 } 23 24 const hosts = replies 25 .map((r) => ({ address: r.address, family: r.type === 'AAAA' ? 6 : 4 })); 26 27 if (options.all === true) { 28 return cb(null, hosts); 29 } 30 31 return cb(null, hosts[0].address, hosts[0].family); 32 }); 33} 34 35function createDnsServer(ipv6Addr, ipv4Addr, cb) { 36 // Create a DNS server which replies with a AAAA and a A record for the same host 37 const socket = dgram.createSocket('udp4'); 38 39 socket.on('message', common.mustCall((msg, { address, port }) => { 40 const parsed = parseDNSPacket(msg); 41 const domain = parsed.questions[0].domain; 42 assert.strictEqual(domain, 'example.org'); 43 44 socket.send(writeDNSPacket({ 45 id: parsed.id, 46 questions: parsed.questions, 47 answers: [ 48 { type: 'A', address: ipv4Addr, ttl: 123, domain: 'example.org' }, 49 { type: 'AAAA', address: ipv6Addr, ttl: 123, domain: 'example.org' }, 50 ] 51 }), port, address); 52 })); 53 54 socket.bind(0, () => { 55 const resolver = new Resolver(); 56 resolver.setServers([`127.0.0.1:${socket.address().port}`]); 57 58 cb({ dnsServer: socket, lookup: _lookup.bind(null, resolver) }); 59 }); 60} 61 62// Test that IPV6 is NOT reached if IPV4 is sorted first 63if (common.hasIPv6) { 64 createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) { 65 const ipv4Server = createServer((socket) => { 66 socket.on('data', common.mustCall(() => { 67 socket.write('response-ipv4'); 68 socket.end(); 69 })); 70 }); 71 72 const ipv6Server = createServer((socket) => { 73 socket.on('data', common.mustNotCall(() => { 74 socket.write('response-ipv6'); 75 socket.end(); 76 })); 77 }); 78 79 ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => { 80 const port = ipv4Server.address().port; 81 82 ipv6Server.listen(port, '::1', common.mustCall(() => { 83 const connection = createConnection({ 84 host: 'example.org', 85 port, 86 lookup, 87 autoSelectFamily: true, 88 }); 89 90 let response = ''; 91 connection.setEncoding('utf-8'); 92 93 connection.on('data', (chunk) => { 94 response += chunk; 95 }); 96 97 connection.on('end', common.mustCall(() => { 98 assert.strictEqual(response, 'response-ipv4'); 99 ipv4Server.close(); 100 ipv6Server.close(); 101 dnsServer.close(); 102 })); 103 104 connection.write('request'); 105 })); 106 })); 107 })); 108} 109