1+ local monotime = require " cqueues" .monotime
12local ca = require " cqueues.auxlib"
23local cs = require " cqueues.socket"
4+ local cqueues_dns_record = require " cqueues.dns.record"
35local http_tls = require " http.tls"
46local http_util = require " http.util"
57local connection_common = require " http.connection_common"
@@ -80,6 +82,31 @@ local function negotiate(s, options, timeout)
8082 end
8183end
8284
85+ -- `type` parameter is what sort of records you want to find could be "A" or
86+ -- "AAAA" or `nil` if you want to filter yourself e.g. to implement
87+ -- https://www.ietf.org/archive/id/draft-vavrusa-dnsop-aaaa-for-free-00.txt
88+ local function each_matching_record (pkt , name , type )
89+ -- First need to do CNAME chasing
90+ local params = {
91+ section = " answer" ;
92+ class = cqueues_dns_record .IN ;
93+ type = cqueues_dns_record .CNAME ;
94+ name = name .. " ." ;
95+ }
96+ for _ = 1 , 8 do -- avoid cname loops
97+ -- Ignores any CNAME record past the first (which should never occur anyway)
98+ local func , state , first = pkt :grep (params )
99+ local record = func (state , first )
100+ if record == nil then
101+ -- Not found
102+ break
103+ end
104+ params .name = record :host ()
105+ end
106+ params .type = type
107+ return pkt :grep (params )
108+ end
109+
83110local function connect (options , timeout )
84111 local bind = options .bind
85112 if bind ~= nil then
@@ -99,11 +126,68 @@ local function connect(options, timeout)
99126 port = bind_port ;
100127 }
101128 end
129+
130+ local family = options .family
131+ local path = options .path
132+ local host = options .host
133+ if not path and not http_util .is_ip (host ) then
134+ local dns_resolver = options .dns_resolver
135+ if dns_resolver then
136+ local deadline = timeout and monotime ()+ timeout
137+ local hostv4 , hostv6
138+ if family == nil or family == cs .AF_UNSPEC or family == cs .AF_INET6 then
139+ -- Query for AAAA record
140+ local packet = ca .fileresult (dns_resolver :query (host , cqueues_dns_record .AAAA , nil , timeout ))
141+ if packet then
142+ -- If IPv6 explicitly requested then filter down to only AAAA records
143+ local type = (family == cs .AF_INET6 ) and cqueues_dns_record .AAAA or nil
144+ for rec in each_matching_record (packet , host , type ) do
145+ local t = rec :type ()
146+ if t == cqueues_dns_record .AAAA then
147+ hostv6 = rec :addr ()
148+ break
149+ elseif t == cqueues_dns_record .A then
150+ hostv4 = rec :addr ()
151+ break
152+ end
153+ end
154+ end
155+ end
156+ if (hostv4 == nil and hostv6 == nil ) and (family == nil or family == cs .AF_UNSPEC or family == cs .AF_INET ) then
157+ -- Query for A record
158+ local packet = ca .fileresult (dns_resolver :query (host , cqueues_dns_record .A , nil , deadline and deadline - monotime ()))
159+ if packet then
160+ -- If IPv4 explicitly requested then filter down to only A records
161+ -- Skip AAAA if we already have hostv6
162+ local type = (family == cs .AF_INET or hostv6 ) and cqueues_dns_record .A or nil
163+ for rec in each_matching_record (packet , host , type ) do
164+ local t = rec :type ()
165+ if t == cqueues_dns_record .A then
166+ hostv4 = rec :addr ()
167+ break
168+ elseif t == cqueues_dns_record .AAAA then
169+ hostv6 = rec :addr ()
170+ break
171+ end
172+ end
173+ end
174+ end
175+ if hostv6 then
176+ host = hostv6
177+ elseif hostv4 then
178+ host = hostv4
179+ else
180+ return nil , " The name does not resolve for the supplied parameters"
181+ end
182+ timeout = deadline and deadline - monotime ()
183+ end
184+ end
185+
102186 local s , err , errno = ca .fileresult (cs .connect {
103- family = options . family ;
104- host = options . host ;
187+ family = family ;
188+ host = host ;
105189 port = options .port ;
106- path = options . path ;
190+ path = path ;
107191 bind = bind ;
108192 sendname = false ;
109193 v6only = options .v6only ;
0 commit comments