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