2022-08-26 16:42:48 +02:00
|
|
|
package webconnectivity
|
|
|
|
|
|
|
|
//
|
|
|
|
// DNSResolvers
|
|
|
|
//
|
|
|
|
// Generated by `boilerplate' using the multi-resolver template.
|
|
|
|
//
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"math/rand"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/apex/log"
|
|
|
|
"github.com/ooni/probe-cli/v3/internal/atomicx"
|
|
|
|
"github.com/ooni/probe-cli/v3/internal/measurexlite"
|
|
|
|
"github.com/ooni/probe-cli/v3/internal/model"
|
|
|
|
"github.com/ooni/probe-cli/v3/internal/netxlite"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Resolves the URL's domain using several resolvers.
|
|
|
|
//
|
|
|
|
// The zero value of this structure IS NOT valid and you MUST initialize
|
|
|
|
// all the fields marked as MANDATORY before using this structure.
|
|
|
|
type DNSResolvers struct {
|
|
|
|
// DNSCache is the MANDATORY DNS cache.
|
|
|
|
DNSCache *DNSCache
|
|
|
|
|
|
|
|
// Domain is the MANDATORY domain to resolve.
|
|
|
|
Domain string
|
|
|
|
|
|
|
|
// IDGenerator is the MANDATORY atomic int64 to generate task IDs.
|
|
|
|
IDGenerator *atomicx.Int64
|
|
|
|
|
|
|
|
// Logger is the MANDATORY logger to use.
|
|
|
|
Logger model.Logger
|
|
|
|
|
2022-09-15 08:46:53 +02:00
|
|
|
// NumRedirects it the MANDATORY counter of the number of redirects.
|
|
|
|
NumRedirects *NumRedirects
|
|
|
|
|
2022-08-26 16:42:48 +02:00
|
|
|
// TestKeys is MANDATORY and contains the TestKeys.
|
|
|
|
TestKeys *TestKeys
|
|
|
|
|
|
|
|
// URL is the MANDATORY URL we're measuring.
|
|
|
|
URL *url.URL
|
|
|
|
|
|
|
|
// ZeroTime is the MANDATORY zero time of the measurement.
|
|
|
|
ZeroTime time.Time
|
|
|
|
|
|
|
|
// WaitGroup is the MANDATORY wait group this task belongs to.
|
|
|
|
WaitGroup *sync.WaitGroup
|
|
|
|
|
|
|
|
// CookieJar contains the OPTIONAL cookie jar, used for redirects.
|
|
|
|
CookieJar http.CookieJar
|
|
|
|
|
|
|
|
// Referer contains the OPTIONAL referer, used for redirects.
|
|
|
|
Referer string
|
|
|
|
|
|
|
|
// Session is the OPTIONAL session. If the session is set, we will use
|
|
|
|
// it to start the task that issues the control request. This request must
|
|
|
|
// only be sent during the first iteration. It would be pointless to
|
|
|
|
// issue such a request for subsequent redirects, because the TH will
|
|
|
|
// always follow the redirect chain caused by the provided URL.
|
|
|
|
Session model.ExperimentSession
|
|
|
|
|
|
|
|
// THAddr is the OPTIONAL test helper address.
|
|
|
|
THAddr string
|
|
|
|
|
|
|
|
// UDPAddress is the OPTIONAL address of the UDP resolver to use. If this
|
|
|
|
// field is not set we use a default one (e.g., `8.8.8.8:53`).
|
|
|
|
UDPAddress string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start starts this task in a background goroutine.
|
|
|
|
func (t *DNSResolvers) Start(ctx context.Context) {
|
|
|
|
t.WaitGroup.Add(1)
|
|
|
|
go func() {
|
|
|
|
defer t.WaitGroup.Done() // synchronize with the parent
|
|
|
|
t.Run(ctx)
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
// run performs a DNS lookup and returns the looked up addrs
|
2022-09-05 13:33:59 +02:00
|
|
|
func (t *DNSResolvers) run(parentCtx context.Context) []DNSEntry {
|
2022-08-26 16:42:48 +02:00
|
|
|
// create output channels for the lookup
|
|
|
|
systemOut := make(chan []string)
|
|
|
|
udpOut := make(chan []string)
|
|
|
|
httpsOut := make(chan []string)
|
|
|
|
whoamiSystemV4Out := make(chan []DNSWhoamiInfoEntry)
|
|
|
|
whoamiUDPv4Out := make(chan []DNSWhoamiInfoEntry)
|
|
|
|
|
|
|
|
// TODO(bassosimone): add opportunistic support for detecting
|
|
|
|
// whether DNS queries are answered regardless of dest addr by
|
|
|
|
// sending a few queries to root DNS servers
|
|
|
|
|
|
|
|
udpAddress := t.udpAddress()
|
|
|
|
|
|
|
|
// start asynchronous lookups
|
|
|
|
go t.lookupHostSystem(parentCtx, systemOut)
|
|
|
|
go t.lookupHostUDP(parentCtx, udpAddress, udpOut)
|
|
|
|
go t.lookupHostDNSOverHTTPS(parentCtx, httpsOut)
|
|
|
|
go t.whoamiSystemV4(parentCtx, whoamiSystemV4Out)
|
|
|
|
go t.whoamiUDPv4(parentCtx, udpAddress, whoamiUDPv4Out)
|
|
|
|
|
|
|
|
// collect resulting IP addresses (which may be nil/empty lists)
|
|
|
|
systemAddrs := <-systemOut
|
|
|
|
udpAddrs := <-udpOut
|
|
|
|
httpsAddrs := <-httpsOut
|
|
|
|
|
|
|
|
// collect whoami results (which also may be nil/empty)
|
|
|
|
whoamiSystemV4 := <-whoamiSystemV4Out
|
|
|
|
whoamiUDPv4 := <-whoamiUDPv4Out
|
|
|
|
t.TestKeys.WithDNSWhoami(func(di *DNSWhoamiInfo) {
|
|
|
|
di.SystemV4 = whoamiSystemV4
|
|
|
|
di.UDPv4[udpAddress] = whoamiUDPv4
|
|
|
|
})
|
|
|
|
|
|
|
|
// merge the resolved IP addresses
|
2022-09-05 13:33:59 +02:00
|
|
|
merged := map[string]*DNSEntry{}
|
2022-08-26 16:42:48 +02:00
|
|
|
for _, addr := range systemAddrs {
|
2022-09-05 13:33:59 +02:00
|
|
|
if _, found := merged[addr]; !found {
|
|
|
|
merged[addr] = &DNSEntry{}
|
|
|
|
}
|
|
|
|
merged[addr].Addr = addr
|
|
|
|
merged[addr].Flags |= DNSAddrFlagSystemResolver
|
2022-08-26 16:42:48 +02:00
|
|
|
}
|
|
|
|
for _, addr := range udpAddrs {
|
2022-09-05 13:33:59 +02:00
|
|
|
if _, found := merged[addr]; !found {
|
|
|
|
merged[addr] = &DNSEntry{}
|
|
|
|
}
|
|
|
|
merged[addr].Addr = addr
|
|
|
|
merged[addr].Flags |= DNSAddrFlagUDP
|
2022-08-26 16:42:48 +02:00
|
|
|
}
|
|
|
|
for _, addr := range httpsAddrs {
|
2022-09-05 13:33:59 +02:00
|
|
|
if _, found := merged[addr]; !found {
|
|
|
|
merged[addr] = &DNSEntry{}
|
2022-08-26 16:42:48 +02:00
|
|
|
}
|
2022-09-05 13:33:59 +02:00
|
|
|
merged[addr].Addr = addr
|
|
|
|
merged[addr].Flags |= DNSAddrFlagHTTPS
|
2022-08-26 16:42:48 +02:00
|
|
|
}
|
2022-09-05 13:33:59 +02:00
|
|
|
// implementation note: we don't remove bogons because accessing
|
|
|
|
// them can lead us to discover block pages
|
|
|
|
var entries []DNSEntry
|
|
|
|
for _, entry := range merged {
|
|
|
|
entries = append(entries, *entry)
|
2022-08-26 16:42:48 +02:00
|
|
|
}
|
|
|
|
|
2022-09-05 13:33:59 +02:00
|
|
|
return entries
|
2022-08-26 16:42:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Run runs this task in the current goroutine.
|
|
|
|
func (t *DNSResolvers) Run(parentCtx context.Context) {
|
|
|
|
var (
|
2022-09-05 13:33:59 +02:00
|
|
|
addresses []DNSEntry
|
2022-08-26 16:42:48 +02:00
|
|
|
found bool
|
|
|
|
)
|
|
|
|
|
|
|
|
// attempt to use the dns cache
|
|
|
|
addresses, found = t.DNSCache.Get(t.Domain)
|
|
|
|
|
|
|
|
if !found {
|
|
|
|
// fall back to performing a real dns lookup
|
|
|
|
addresses = t.run(parentCtx)
|
|
|
|
|
|
|
|
// insert the addresses we just looked us into the cache
|
|
|
|
t.DNSCache.Set(t.Domain, addresses)
|
|
|
|
|
2022-09-05 13:33:59 +02:00
|
|
|
log.Infof("using resolved addrs: %+v", addresses)
|
|
|
|
} else {
|
|
|
|
log.Infof("using previously-cached addrs: %+v", addresses)
|
|
|
|
}
|
2022-08-26 16:42:48 +02:00
|
|
|
|
2022-09-11 22:12:48 +02:00
|
|
|
// create priority selector
|
2022-09-12 07:33:34 +02:00
|
|
|
ps := newPrioritySelector(parentCtx, t.ZeroTime, t.TestKeys, t.Logger, addresses)
|
2022-09-11 22:12:48 +02:00
|
|
|
|
2022-08-26 16:42:48 +02:00
|
|
|
// fan out a number of child async tasks to use the IP addrs
|
2022-09-11 22:12:48 +02:00
|
|
|
t.startCleartextFlows(parentCtx, ps, addresses)
|
|
|
|
t.startSecureFlows(parentCtx, ps, addresses)
|
|
|
|
t.maybeStartControlFlow(parentCtx, ps, addresses)
|
2022-08-26 16:42:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// whoamiSystemV4 performs a DNS whoami lookup for the system resolver. This function must
|
|
|
|
// always emit an ouput on the [out] channel to synchronize with the caller func.
|
|
|
|
func (t *DNSResolvers) whoamiSystemV4(parentCtx context.Context, out chan<- []DNSWhoamiInfoEntry) {
|
|
|
|
value, _ := DNSWhoamiSingleton.SystemV4(parentCtx)
|
|
|
|
t.Logger.Infof("DNS whoami for system resolver: %+v", value)
|
|
|
|
out <- value
|
|
|
|
}
|
|
|
|
|
|
|
|
// whoamiUDPv4 performs a DNS whoami lookup for the given UDP resolver. This function must
|
|
|
|
// always emit an ouput on the [out] channel to synchronize with the caller func.
|
|
|
|
func (t *DNSResolvers) whoamiUDPv4(parentCtx context.Context, udpAddress string, out chan<- []DNSWhoamiInfoEntry) {
|
|
|
|
value, _ := DNSWhoamiSingleton.UDPv4(parentCtx, udpAddress)
|
|
|
|
t.Logger.Infof("DNS whoami for %s/udp resolver: %+v", udpAddress, value)
|
|
|
|
out <- value
|
|
|
|
}
|
|
|
|
|
|
|
|
// lookupHostSystem performs a DNS lookup using the system resolver. This function must
|
|
|
|
// always emit an ouput on the [out] channel to synchronize with the caller func.
|
|
|
|
func (t *DNSResolvers) lookupHostSystem(parentCtx context.Context, out chan<- []string) {
|
|
|
|
// create context with attached a timeout
|
|
|
|
const timeout = 4 * time.Second
|
|
|
|
lookupCtx, lookpCancel := context.WithTimeout(parentCtx, timeout)
|
|
|
|
defer lookpCancel()
|
|
|
|
|
|
|
|
// create trace's index
|
|
|
|
index := t.IDGenerator.Add(1)
|
|
|
|
|
|
|
|
// create trace
|
|
|
|
trace := measurexlite.NewTrace(index, t.ZeroTime)
|
|
|
|
|
|
|
|
// start the operation logger
|
|
|
|
ol := measurexlite.NewOperationLogger(
|
|
|
|
t.Logger, "[#%d] lookup %s using system", index, t.Domain,
|
|
|
|
)
|
|
|
|
|
|
|
|
// runs the lookup
|
|
|
|
reso := trace.NewStdlibResolver(t.Logger)
|
|
|
|
addrs, err := reso.LookupHost(lookupCtx, t.Domain)
|
|
|
|
t.TestKeys.AppendQueries(trace.DNSLookupsFromRoundTrip()...)
|
|
|
|
ol.Stop(err)
|
|
|
|
out <- addrs
|
|
|
|
}
|
|
|
|
|
|
|
|
// lookupHostUDP performs a DNS lookup using an UDP resolver. This function must always
|
|
|
|
// emit an ouput on the [out] channel to synchronize with the caller func.
|
|
|
|
func (t *DNSResolvers) lookupHostUDP(parentCtx context.Context, udpAddress string, out chan<- []string) {
|
|
|
|
// create context with attached a timeout
|
|
|
|
const timeout = 4 * time.Second
|
|
|
|
lookupCtx, lookpCancel := context.WithTimeout(parentCtx, timeout)
|
|
|
|
defer lookpCancel()
|
|
|
|
|
|
|
|
// create trace's index
|
|
|
|
index := t.IDGenerator.Add(1)
|
|
|
|
|
|
|
|
// create trace
|
|
|
|
trace := measurexlite.NewTrace(index, t.ZeroTime)
|
|
|
|
|
|
|
|
// start the operation logger
|
|
|
|
ol := measurexlite.NewOperationLogger(
|
|
|
|
t.Logger, "[#%d] lookup %s using %s", index, t.Domain, udpAddress,
|
|
|
|
)
|
|
|
|
|
|
|
|
// runs the lookup
|
|
|
|
dialer := netxlite.NewDialerWithoutResolver(t.Logger)
|
|
|
|
reso := trace.NewParallelUDPResolver(t.Logger, dialer, udpAddress)
|
|
|
|
addrs, err := reso.LookupHost(lookupCtx, t.Domain)
|
|
|
|
|
|
|
|
// saves the results making sure we split Do53 queries from other queries
|
|
|
|
do53, other := t.do53SplitQueries(trace.DNSLookupsFromRoundTrip())
|
|
|
|
t.TestKeys.AppendQueries(do53...)
|
|
|
|
t.TestKeys.WithTestKeysDo53(func(tkd *TestKeysDo53) {
|
|
|
|
tkd.Queries = append(tkd.Queries, other...)
|
|
|
|
tkd.NetworkEvents = append(tkd.NetworkEvents, trace.NetworkEvents()...)
|
|
|
|
})
|
|
|
|
|
|
|
|
ol.Stop(err)
|
|
|
|
out <- addrs
|
2022-08-26 18:32:36 +02:00
|
|
|
|
|
|
|
// wait for late DNS replies
|
|
|
|
t.WaitGroup.Add(1)
|
|
|
|
go t.waitForLateReplies(parentCtx, trace)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Waits for late DNS replies.
|
|
|
|
func (t *DNSResolvers) waitForLateReplies(parentCtx context.Context, trace *measurexlite.Trace) {
|
|
|
|
defer t.WaitGroup.Done()
|
|
|
|
const lateTimeout = 500 * time.Millisecond
|
|
|
|
events := trace.DelayedDNSResponseWithTimeout(parentCtx, lateTimeout)
|
|
|
|
t.TestKeys.AppendDNSLateReplies(events...)
|
2022-08-26 16:42:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Divides queries generated by Do53 in Do53-proper queries and other queries.
|
|
|
|
func (t *DNSResolvers) do53SplitQueries(
|
|
|
|
input []*model.ArchivalDNSLookupResult) (do53, other []*model.ArchivalDNSLookupResult) {
|
|
|
|
for _, query := range input {
|
|
|
|
switch query.Engine {
|
|
|
|
case "udp", "tcp":
|
|
|
|
do53 = append(do53, query)
|
|
|
|
default:
|
|
|
|
other = append(other, query)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(bassosimone): maybe cycle through a bunch of well known addresses
|
|
|
|
|
|
|
|
// Returns the UDP resolver we should be using by default.
|
|
|
|
func (t *DNSResolvers) udpAddress() string {
|
|
|
|
if t.UDPAddress != "" {
|
|
|
|
return t.UDPAddress
|
|
|
|
}
|
|
|
|
return "8.8.4.4:53"
|
|
|
|
}
|
|
|
|
|
|
|
|
// OpportunisticDNSOverHTTPS allows to perform opportunistic DNS-over-HTTPS
|
|
|
|
// measurements as part of Web Connectivity.
|
|
|
|
type OpportunisticDNSOverHTTPS struct {
|
|
|
|
// interval is the next interval after which to measure.
|
|
|
|
interval time.Duration
|
|
|
|
|
|
|
|
// mu provides mutual exclusion
|
|
|
|
mu *sync.Mutex
|
|
|
|
|
|
|
|
// rnd is the random number generator to use.
|
|
|
|
rnd *rand.Rand
|
|
|
|
|
|
|
|
// t is when we last run an opportunistic measurement.
|
|
|
|
t time.Time
|
|
|
|
|
|
|
|
// urls contains the urls of known DoH services.
|
|
|
|
urls []string
|
|
|
|
}
|
|
|
|
|
|
|
|
// MaybeNextURL returns the next URL to measure, if any. Our aim is to perform
|
|
|
|
// periodic, opportunistic DoH measurements as part of Web Connectivity.
|
|
|
|
func (o *OpportunisticDNSOverHTTPS) MaybeNextURL() (string, bool) {
|
|
|
|
now := time.Now()
|
|
|
|
o.mu.Lock()
|
|
|
|
defer o.mu.Unlock()
|
|
|
|
if o.t.IsZero() || now.Sub(o.t) > o.interval {
|
|
|
|
o.rnd.Shuffle(len(o.urls), func(i, j int) {
|
|
|
|
o.urls[i], o.urls[j] = o.urls[j], o.urls[i]
|
|
|
|
})
|
|
|
|
o.t = now
|
|
|
|
o.interval = time.Duration(20+o.rnd.Uint32()%20) * time.Second
|
|
|
|
return o.urls[0], true
|
|
|
|
}
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(bassosimone): consider whether factoring out this code
|
|
|
|
// and storing the state on disk instead of using memory
|
|
|
|
|
|
|
|
// TODO(bassosimone): consider unifying somehow this code and
|
|
|
|
// the systemresolver code (or maybe just the list of resolvers)
|
|
|
|
|
|
|
|
// OpportunisticDNSOverHTTPSSingleton is the singleton used to keep
|
|
|
|
// track of the opportunistic DNS-over-HTTPS measurements state.
|
|
|
|
var OpportunisticDNSOverHTTPSSingleton = &OpportunisticDNSOverHTTPS{
|
|
|
|
interval: 0,
|
|
|
|
mu: &sync.Mutex{},
|
|
|
|
rnd: rand.New(rand.NewSource(time.Now().UnixNano())),
|
|
|
|
t: time.Time{},
|
|
|
|
urls: []string{
|
|
|
|
"https://mozilla.cloudflare-dns.com/dns-query",
|
|
|
|
"https://dns.nextdns.io/dns-query",
|
|
|
|
"https://dns.google/dns-query",
|
|
|
|
"https://dns.quad9.net/dns-query",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// lookupHostDNSOverHTTPS performs a DNS lookup using a DoH resolver. This function must
|
|
|
|
// always emit an ouput on the [out] channel to synchronize with the caller func.
|
|
|
|
func (t *DNSResolvers) lookupHostDNSOverHTTPS(parentCtx context.Context, out chan<- []string) {
|
|
|
|
// obtain an opportunistic DoH URL
|
|
|
|
URL, good := OpportunisticDNSOverHTTPSSingleton.MaybeNextURL()
|
|
|
|
if !good {
|
|
|
|
// no need to perform opportunistic DoH at this time but we still
|
|
|
|
// need to fake out a lookup to please our caller
|
|
|
|
out <- []string{}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// create context with attached a timeout
|
|
|
|
const timeout = 4 * time.Second
|
|
|
|
lookupCtx, lookpCancel := context.WithTimeout(parentCtx, timeout)
|
|
|
|
defer lookpCancel()
|
|
|
|
|
|
|
|
// create trace's index
|
|
|
|
index := t.IDGenerator.Add(1)
|
|
|
|
|
|
|
|
// create trace
|
|
|
|
trace := measurexlite.NewTrace(index, t.ZeroTime)
|
|
|
|
|
|
|
|
// start the operation logger
|
|
|
|
ol := measurexlite.NewOperationLogger(
|
|
|
|
t.Logger, "[#%d] lookup %s using %s", index, t.Domain, URL,
|
|
|
|
)
|
|
|
|
|
|
|
|
// runs the lookup
|
|
|
|
reso := trace.NewParallelDNSOverHTTPSResolver(t.Logger, URL)
|
|
|
|
addrs, err := reso.LookupHost(lookupCtx, t.Domain)
|
|
|
|
reso.CloseIdleConnections()
|
|
|
|
|
|
|
|
// save results making sure we properly split DoH queries from other queries
|
|
|
|
doh, other := t.dohSplitQueries(trace.DNSLookupsFromRoundTrip())
|
|
|
|
t.TestKeys.AppendQueries(doh...)
|
|
|
|
t.TestKeys.WithTestKeysDoH(func(tkdh *TestKeysDoH) {
|
|
|
|
tkdh.Queries = append(tkdh.Queries, other...)
|
|
|
|
tkdh.NetworkEvents = append(tkdh.NetworkEvents, trace.NetworkEvents()...)
|
|
|
|
tkdh.TCPConnect = append(tkdh.TCPConnect, trace.TCPConnects()...)
|
|
|
|
tkdh.TLSHandshakes = append(tkdh.TLSHandshakes, trace.TLSHandshakes()...)
|
|
|
|
})
|
|
|
|
|
|
|
|
ol.Stop(err)
|
|
|
|
out <- addrs
|
|
|
|
}
|
|
|
|
|
|
|
|
// Divides queries generated by DoH in DoH-proper queries and other queries.
|
|
|
|
func (t *DNSResolvers) dohSplitQueries(
|
|
|
|
input []*model.ArchivalDNSLookupResult) (doh, other []*model.ArchivalDNSLookupResult) {
|
|
|
|
for _, query := range input {
|
|
|
|
switch query.Engine {
|
|
|
|
case "doh":
|
|
|
|
doh = append(doh, query)
|
|
|
|
default:
|
|
|
|
other = append(other, query)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// startCleartextFlows starts a TCP measurement flow for each IP addr.
|
2022-09-11 22:12:48 +02:00
|
|
|
func (t *DNSResolvers) startCleartextFlows(
|
|
|
|
ctx context.Context,
|
|
|
|
ps *prioritySelector,
|
|
|
|
addresses []DNSEntry,
|
|
|
|
) {
|
2022-08-26 16:42:48 +02:00
|
|
|
if t.URL.Scheme != "http" {
|
|
|
|
// Do not bother with measuring HTTP when the user
|
|
|
|
// has asked us to measure an HTTPS URL.
|
|
|
|
return
|
|
|
|
}
|
|
|
|
port := "80"
|
|
|
|
if urlPort := t.URL.Port(); urlPort != "" {
|
|
|
|
port = urlPort
|
|
|
|
}
|
|
|
|
for _, addr := range addresses {
|
|
|
|
task := &CleartextFlow{
|
2022-09-05 13:33:59 +02:00
|
|
|
Address: net.JoinHostPort(addr.Addr, port),
|
2022-08-26 16:42:48 +02:00
|
|
|
DNSCache: t.DNSCache,
|
|
|
|
IDGenerator: t.IDGenerator,
|
|
|
|
Logger: t.Logger,
|
2022-09-15 08:46:53 +02:00
|
|
|
NumRedirects: t.NumRedirects,
|
2022-08-26 16:42:48 +02:00
|
|
|
TestKeys: t.TestKeys,
|
|
|
|
ZeroTime: t.ZeroTime,
|
|
|
|
WaitGroup: t.WaitGroup,
|
|
|
|
CookieJar: t.CookieJar,
|
|
|
|
FollowRedirects: t.URL.Scheme == "http",
|
|
|
|
HostHeader: t.URL.Host,
|
2022-09-11 22:12:48 +02:00
|
|
|
PrioSelector: ps,
|
2022-08-26 16:42:48 +02:00
|
|
|
Referer: t.Referer,
|
|
|
|
UDPAddress: t.UDPAddress,
|
|
|
|
URLPath: t.URL.Path,
|
|
|
|
URLRawQuery: t.URL.RawQuery,
|
|
|
|
}
|
|
|
|
task.Start(ctx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// startSecureFlows starts a TCP+TLS measurement flow for each IP addr.
|
2022-09-11 22:12:48 +02:00
|
|
|
func (t *DNSResolvers) startSecureFlows(
|
|
|
|
ctx context.Context,
|
|
|
|
ps *prioritySelector,
|
|
|
|
addresses []DNSEntry,
|
|
|
|
) {
|
|
|
|
if t.URL.Scheme != "https" {
|
|
|
|
// When the scheme is not HTTPS we fetch using HTTP
|
|
|
|
ps = nil
|
2022-08-26 16:42:48 +02:00
|
|
|
}
|
|
|
|
port := "443"
|
|
|
|
if urlPort := t.URL.Port(); urlPort != "" {
|
|
|
|
if t.URL.Scheme != "https" {
|
|
|
|
// If the URL is like http://example.com:8080/, we don't know
|
|
|
|
// which would be the correct port where to use HTTPS.
|
|
|
|
return
|
|
|
|
}
|
|
|
|
port = urlPort
|
|
|
|
}
|
|
|
|
for _, addr := range addresses {
|
|
|
|
task := &SecureFlow{
|
2022-09-05 13:33:59 +02:00
|
|
|
Address: net.JoinHostPort(addr.Addr, port),
|
2022-08-26 16:42:48 +02:00
|
|
|
DNSCache: t.DNSCache,
|
|
|
|
IDGenerator: t.IDGenerator,
|
|
|
|
Logger: t.Logger,
|
2022-09-15 08:46:53 +02:00
|
|
|
NumRedirects: t.NumRedirects,
|
2022-08-26 16:42:48 +02:00
|
|
|
TestKeys: t.TestKeys,
|
|
|
|
ZeroTime: t.ZeroTime,
|
|
|
|
WaitGroup: t.WaitGroup,
|
|
|
|
ALPN: []string{"h2", "http/1.1"},
|
|
|
|
CookieJar: t.CookieJar,
|
|
|
|
FollowRedirects: t.URL.Scheme == "https",
|
|
|
|
SNI: t.URL.Hostname(),
|
|
|
|
HostHeader: t.URL.Host,
|
2022-09-11 22:12:48 +02:00
|
|
|
PrioSelector: ps,
|
2022-08-26 16:42:48 +02:00
|
|
|
Referer: t.Referer,
|
|
|
|
UDPAddress: t.UDPAddress,
|
|
|
|
URLPath: t.URL.Path,
|
|
|
|
URLRawQuery: t.URL.RawQuery,
|
|
|
|
}
|
|
|
|
task.Start(ctx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// maybeStartControlFlow starts the control flow iff .Session and .THAddr are set.
|
2022-09-11 22:12:48 +02:00
|
|
|
func (t *DNSResolvers) maybeStartControlFlow(
|
|
|
|
ctx context.Context,
|
|
|
|
ps *prioritySelector,
|
|
|
|
addresses []DNSEntry,
|
|
|
|
) {
|
2022-09-05 13:33:59 +02:00
|
|
|
// note: for subsequent requests we don't set .Session and .THAddr hence
|
|
|
|
// we are not going to query the test helper more than once
|
2022-08-26 16:42:48 +02:00
|
|
|
if t.Session != nil && t.THAddr != "" {
|
2022-09-05 13:33:59 +02:00
|
|
|
var addrs []string
|
|
|
|
for _, addr := range addresses {
|
|
|
|
addrs = append(addrs, addr.Addr)
|
|
|
|
}
|
2022-08-26 16:42:48 +02:00
|
|
|
ctrl := &Control{
|
2022-09-05 13:33:59 +02:00
|
|
|
Addresses: addrs,
|
2022-08-26 16:42:48 +02:00
|
|
|
ExtraMeasurementsStarter: t, // allows starting follow-up measurement flows
|
|
|
|
Logger: t.Logger,
|
2022-09-11 22:12:48 +02:00
|
|
|
PrioSelector: ps,
|
2022-08-26 16:42:48 +02:00
|
|
|
TestKeys: t.TestKeys,
|
|
|
|
Session: t.Session,
|
|
|
|
THAddr: t.THAddr,
|
|
|
|
URL: t.URL,
|
|
|
|
WaitGroup: t.WaitGroup,
|
|
|
|
}
|
|
|
|
ctrl.Start(ctx)
|
|
|
|
}
|
|
|
|
}
|