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,7 +82,88 @@ 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 )
111+ local family = options .family
112+ local path = options .path
113+ local host = options .host
114+ if not path and not http_util .is_ip (host ) then
115+ local dns_resolver = options .dns_resolver
116+ if dns_resolver then
117+ local deadline = timeout and monotime ()+ timeout
118+ local hostv4 , hostv6
119+ if family == nil or family == cs .AF_UNSPEC or family == cs .AF_INET6 then
120+ -- Query for AAAA record
121+ local packet = ca .fileresult (dns_resolver :query (host , cqueues_dns_record .AAAA , nil , timeout ))
122+ if packet then
123+ -- If IPv6 explicitly requested then filter down to only AAAA records
124+ local type = (family == cs .AF_INET6 ) and cqueues_dns_record .AAAA or nil
125+ for rec in each_matching_record (packet , host , type ) do
126+ local t = rec :type ()
127+ if t == cqueues_dns_record .AAAA then
128+ hostv6 = rec :addr ()
129+ break
130+ elseif t == cqueues_dns_record .A then
131+ hostv4 = rec :addr ()
132+ break
133+ end
134+ end
135+ end
136+ end
137+ if (hostv4 == nil and hostv6 == nil ) and (family == nil or family == cs .AF_UNSPEC or family == cs .AF_INET ) then
138+ -- Query for A record
139+ local packet = ca .fileresult (dns_resolver :query (host , cqueues_dns_record .A , nil , deadline and deadline - monotime ()))
140+ if packet then
141+ -- If IPv4 explicitly requested then filter down to only A records
142+ -- Skip AAAA if we already have hostv6
143+ local type = (family == cs .AF_INET or hostv6 ) and cqueues_dns_record .A or nil
144+ for rec in each_matching_record (packet , host , type ) do
145+ local t = rec :type ()
146+ if t == cqueues_dns_record .A then
147+ hostv4 = rec :addr ()
148+ break
149+ elseif t == cqueues_dns_record .AAAA then
150+ hostv6 = rec :addr ()
151+ break
152+ end
153+ end
154+ end
155+ end
156+ if hostv6 then
157+ host = hostv6
158+ elseif hostv4 then
159+ host = hostv4
160+ else
161+ return nil , " The name does not resolve for the supplied parameters"
162+ end
163+ timeout = deadline and deadline - monotime ()
164+ end
165+ end
166+
84167 local bind = options .bind
85168 if bind ~= nil then
86169 assert (type (bind ) == " string" )
@@ -99,11 +182,12 @@ local function connect(options, timeout)
99182 port = bind_port ;
100183 }
101184 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