fix(webconnectivity@v0.5): fetch HTTP only using system-resolver addrs (#935)
While there, change the emoji logger to emit whitespace on info logs. This makes warnings stand out even more. Closes https://github.com/ooni/probe/issues/2258
This commit is contained in:
parent
a72a9284f1
commit
3b24b1196d
|
@ -23,11 +23,11 @@ type EndpointMeasurementsStarter interface {
|
||||||
// nonblocking read fails. Hence, you must create a [sema] channel with buffer equal
|
// nonblocking read fails. Hence, you must create a [sema] channel with buffer equal
|
||||||
// to N and N elements inside it to allow N flows to perform HTTP measurements. Passing
|
// to N and N elements inside it to allow N flows to perform HTTP measurements. Passing
|
||||||
// a nil [sema] causes no flow to attempt HTTP measurements.
|
// a nil [sema] causes no flow to attempt HTTP measurements.
|
||||||
startCleartextFlowsWithSema(ctx context.Context, sema <-chan any, addresses []string)
|
startCleartextFlowsWithSema(ctx context.Context, sema <-chan any, addresses []DNSEntry)
|
||||||
|
|
||||||
// startSecureFlowsWithSema starts a TCP+TLS measurement flow for each IP addr. See
|
// startSecureFlowsWithSema starts a TCP+TLS measurement flow for each IP addr. See
|
||||||
// the docs of startCleartextFlowsWithSema for more info on the [sema] arg.
|
// the docs of startCleartextFlowsWithSema for more info on the [sema] arg.
|
||||||
startSecureFlowsWithSema(ctx context.Context, sema <-chan any, addresses []string)
|
startSecureFlowsWithSema(ctx context.Context, sema <-chan any, addresses []DNSEntry)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Control issues a Control request and saves the results
|
// Control issues a Control request and saves the results
|
||||||
|
@ -157,12 +157,22 @@ func (c *Control) maybeStartExtraMeasurements(ctx context.Context, thAddrs []str
|
||||||
}
|
}
|
||||||
|
|
||||||
// obtain the TH-only addresses
|
// obtain the TH-only addresses
|
||||||
var thOnly []string
|
var thOnlyAddrs []string
|
||||||
for addr, flags := range mapping {
|
for addr, flags := range mapping {
|
||||||
if (flags & inProbe) != 0 {
|
if (flags & inProbe) != 0 {
|
||||||
continue // discovered by the probe => already tested
|
continue // discovered by the probe => already tested
|
||||||
}
|
}
|
||||||
thOnly = append(thOnly, addr)
|
thOnlyAddrs = append(thOnlyAddrs, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Logger.Infof("measuring additional addrs from TH: %+v", thOnlyAddrs)
|
||||||
|
|
||||||
|
var thOnly []DNSEntry
|
||||||
|
for _, addr := range thOnlyAddrs {
|
||||||
|
thOnly = append(thOnly, DNSEntry{
|
||||||
|
Addr: addr,
|
||||||
|
Flags: 0, // neither system, nor udp, nor doh
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start extra measurements for TH-only addresses. Because we already
|
// Start extra measurements for TH-only addresses. Because we already
|
||||||
|
|
|
@ -2,6 +2,26 @@ package webconnectivity
|
||||||
|
|
||||||
import "sync"
|
import "sync"
|
||||||
|
|
||||||
|
// DNSEntry is an entry in the DNS cache.
|
||||||
|
type DNSEntry struct {
|
||||||
|
// Addr is the cached address
|
||||||
|
Addr string
|
||||||
|
|
||||||
|
// Flags contains flags
|
||||||
|
Flags int64
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DNSAddrFlagSystemResolver means we discovered this addr using the system resolver.
|
||||||
|
DNSAddrFlagSystemResolver = 1 << iota
|
||||||
|
|
||||||
|
// DNSAddrFlagUDP means we discovered this addr using the UDP resolver.
|
||||||
|
DNSAddrFlagUDP
|
||||||
|
|
||||||
|
// DNSAddrFlagHTTPS means we discovered this addr using the DNS-over-HTTPS resolver.
|
||||||
|
DNSAddrFlagHTTPS
|
||||||
|
)
|
||||||
|
|
||||||
// DNSCache wraps a model.Resolver to provide DNS caching.
|
// DNSCache wraps a model.Resolver to provide DNS caching.
|
||||||
//
|
//
|
||||||
// The zero value is invalid; please, use NewDNSCache to construct.
|
// The zero value is invalid; please, use NewDNSCache to construct.
|
||||||
|
@ -10,11 +30,11 @@ type DNSCache struct {
|
||||||
mu *sync.Mutex
|
mu *sync.Mutex
|
||||||
|
|
||||||
// values contains already resolved values.
|
// values contains already resolved values.
|
||||||
values map[string][]string
|
values map[string][]DNSEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get gets values from the cache
|
// Get gets values from the cache
|
||||||
func (c *DNSCache) Get(domain string) ([]string, bool) {
|
func (c *DNSCache) Get(domain string) ([]DNSEntry, bool) {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
values, found := c.values[domain]
|
values, found := c.values[domain]
|
||||||
c.mu.Unlock()
|
c.mu.Unlock()
|
||||||
|
@ -22,7 +42,7 @@ func (c *DNSCache) Get(domain string) ([]string, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set inserts into the cache
|
// Set inserts into the cache
|
||||||
func (c *DNSCache) Set(domain string, values []string) {
|
func (c *DNSCache) Set(domain string, values []DNSEntry) {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
c.values[domain] = values
|
c.values[domain] = values
|
||||||
c.mu.Unlock()
|
c.mu.Unlock()
|
||||||
|
@ -32,6 +52,6 @@ func (c *DNSCache) Set(domain string, values []string) {
|
||||||
func NewDNSCache() *DNSCache {
|
func NewDNSCache() *DNSCache {
|
||||||
return &DNSCache{
|
return &DNSCache{
|
||||||
mu: &sync.Mutex{},
|
mu: &sync.Mutex{},
|
||||||
values: map[string][]string{},
|
values: map[string][]DNSEntry{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,7 @@ func (t *DNSResolvers) Start(ctx context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// run performs a DNS lookup and returns the looked up addrs
|
// run performs a DNS lookup and returns the looked up addrs
|
||||||
func (t *DNSResolvers) run(parentCtx context.Context) []string {
|
func (t *DNSResolvers) run(parentCtx context.Context) []DNSEntry {
|
||||||
// create output channels for the lookup
|
// create output channels for the lookup
|
||||||
systemOut := make(chan []string)
|
systemOut := make(chan []string)
|
||||||
udpOut := make(chan []string)
|
udpOut := make(chan []string)
|
||||||
|
@ -117,39 +117,42 @@ func (t *DNSResolvers) run(parentCtx context.Context) []string {
|
||||||
})
|
})
|
||||||
|
|
||||||
// merge the resolved IP addresses
|
// merge the resolved IP addresses
|
||||||
merged := map[string]bool{}
|
merged := map[string]*DNSEntry{}
|
||||||
for _, addr := range systemAddrs {
|
for _, addr := range systemAddrs {
|
||||||
merged[addr] = true
|
if _, found := merged[addr]; !found {
|
||||||
|
merged[addr] = &DNSEntry{}
|
||||||
|
}
|
||||||
|
merged[addr].Addr = addr
|
||||||
|
merged[addr].Flags |= DNSAddrFlagSystemResolver
|
||||||
}
|
}
|
||||||
for _, addr := range udpAddrs {
|
for _, addr := range udpAddrs {
|
||||||
merged[addr] = true
|
if _, found := merged[addr]; !found {
|
||||||
|
merged[addr] = &DNSEntry{}
|
||||||
|
}
|
||||||
|
merged[addr].Addr = addr
|
||||||
|
merged[addr].Flags |= DNSAddrFlagUDP
|
||||||
}
|
}
|
||||||
for _, addr := range httpsAddrs {
|
for _, addr := range httpsAddrs {
|
||||||
merged[addr] = true
|
if _, found := merged[addr]; !found {
|
||||||
}
|
merged[addr] = &DNSEntry{}
|
||||||
|
|
||||||
// rearrange addresses to have IPv4 first
|
|
||||||
sorted := []string{}
|
|
||||||
for addr := range merged {
|
|
||||||
if v6, err := netxlite.IsIPv6(addr); err == nil && !v6 {
|
|
||||||
sorted = append(sorted, addr)
|
|
||||||
}
|
}
|
||||||
|
merged[addr].Addr = addr
|
||||||
|
merged[addr].Flags |= DNSAddrFlagHTTPS
|
||||||
}
|
}
|
||||||
for addr := range merged {
|
// implementation note: we don't remove bogons because accessing
|
||||||
if v6, err := netxlite.IsIPv6(addr); err == nil && v6 {
|
// them can lead us to discover block pages
|
||||||
sorted = append(sorted, addr)
|
var entries []DNSEntry
|
||||||
}
|
for _, entry := range merged {
|
||||||
|
entries = append(entries, *entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(bassosimone): remove bogons
|
return entries
|
||||||
|
|
||||||
return sorted
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run runs this task in the current goroutine.
|
// Run runs this task in the current goroutine.
|
||||||
func (t *DNSResolvers) Run(parentCtx context.Context) {
|
func (t *DNSResolvers) Run(parentCtx context.Context) {
|
||||||
var (
|
var (
|
||||||
addresses []string
|
addresses []DNSEntry
|
||||||
found bool
|
found bool
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -162,9 +165,11 @@ func (t *DNSResolvers) Run(parentCtx context.Context) {
|
||||||
|
|
||||||
// insert the addresses we just looked us into the cache
|
// insert the addresses we just looked us into the cache
|
||||||
t.DNSCache.Set(t.Domain, addresses)
|
t.DNSCache.Set(t.Domain, addresses)
|
||||||
}
|
|
||||||
|
|
||||||
log.Infof("using resolved addrs: %+v", addresses)
|
log.Infof("using resolved addrs: %+v", addresses)
|
||||||
|
} else {
|
||||||
|
log.Infof("using previously-cached addrs: %+v", addresses)
|
||||||
|
}
|
||||||
|
|
||||||
// fan out a number of child async tasks to use the IP addrs
|
// fan out a number of child async tasks to use the IP addrs
|
||||||
t.startCleartextFlows(parentCtx, addresses)
|
t.startCleartextFlows(parentCtx, addresses)
|
||||||
|
@ -409,14 +414,14 @@ func (t *DNSResolvers) dohSplitQueries(
|
||||||
}
|
}
|
||||||
|
|
||||||
// startCleartextFlows starts a TCP measurement flow for each IP addr.
|
// startCleartextFlows starts a TCP measurement flow for each IP addr.
|
||||||
func (t *DNSResolvers) startCleartextFlows(ctx context.Context, addresses []string) {
|
func (t *DNSResolvers) startCleartextFlows(ctx context.Context, addresses []DNSEntry) {
|
||||||
sema := make(chan any, 1)
|
sema := make(chan any, 1)
|
||||||
sema <- true // allow a single flow to fetch the HTTP body
|
sema <- true // allow a single flow to fetch the HTTP body
|
||||||
t.startCleartextFlowsWithSema(ctx, sema, addresses)
|
t.startCleartextFlowsWithSema(ctx, sema, addresses)
|
||||||
}
|
}
|
||||||
|
|
||||||
// startCleartextFlowsWithSema implements EndpointMeasurementsStarter.
|
// startCleartextFlowsWithSema implements EndpointMeasurementsStarter.
|
||||||
func (t *DNSResolvers) startCleartextFlowsWithSema(ctx context.Context, sema <-chan any, addresses []string) {
|
func (t *DNSResolvers) startCleartextFlowsWithSema(ctx context.Context, sema <-chan any, addresses []DNSEntry) {
|
||||||
if t.URL.Scheme != "http" {
|
if t.URL.Scheme != "http" {
|
||||||
// Do not bother with measuring HTTP when the user
|
// Do not bother with measuring HTTP when the user
|
||||||
// has asked us to measure an HTTPS URL.
|
// has asked us to measure an HTTPS URL.
|
||||||
|
@ -427,12 +432,16 @@ func (t *DNSResolvers) startCleartextFlowsWithSema(ctx context.Context, sema <-c
|
||||||
port = urlPort
|
port = urlPort
|
||||||
}
|
}
|
||||||
for _, addr := range addresses {
|
for _, addr := range addresses {
|
||||||
|
maybeNilSema := sema
|
||||||
|
if (addr.Flags & DNSAddrFlagSystemResolver) == 0 {
|
||||||
|
maybeNilSema = nil // see https://github.com/ooni/probe/issues/2258
|
||||||
|
}
|
||||||
task := &CleartextFlow{
|
task := &CleartextFlow{
|
||||||
Address: net.JoinHostPort(addr, port),
|
Address: net.JoinHostPort(addr.Addr, port),
|
||||||
DNSCache: t.DNSCache,
|
DNSCache: t.DNSCache,
|
||||||
IDGenerator: t.IDGenerator,
|
IDGenerator: t.IDGenerator,
|
||||||
Logger: t.Logger,
|
Logger: t.Logger,
|
||||||
Sema: sema,
|
Sema: maybeNilSema,
|
||||||
TestKeys: t.TestKeys,
|
TestKeys: t.TestKeys,
|
||||||
ZeroTime: t.ZeroTime,
|
ZeroTime: t.ZeroTime,
|
||||||
WaitGroup: t.WaitGroup,
|
WaitGroup: t.WaitGroup,
|
||||||
|
@ -449,7 +458,7 @@ func (t *DNSResolvers) startCleartextFlowsWithSema(ctx context.Context, sema <-c
|
||||||
}
|
}
|
||||||
|
|
||||||
// startSecureFlows starts a TCP+TLS measurement flow for each IP addr.
|
// startSecureFlows starts a TCP+TLS measurement flow for each IP addr.
|
||||||
func (t *DNSResolvers) startSecureFlows(ctx context.Context, addresses []string) {
|
func (t *DNSResolvers) startSecureFlows(ctx context.Context, addresses []DNSEntry) {
|
||||||
sema := make(chan any, 1)
|
sema := make(chan any, 1)
|
||||||
if t.URL.Scheme == "https" {
|
if t.URL.Scheme == "https" {
|
||||||
// Allows just a single worker to fetch the response body but do that
|
// Allows just a single worker to fetch the response body but do that
|
||||||
|
@ -461,7 +470,7 @@ func (t *DNSResolvers) startSecureFlows(ctx context.Context, addresses []string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// startSecureFlowsWithSema implements EndpointMeasurementsStarter.
|
// startSecureFlowsWithSema implements EndpointMeasurementsStarter.
|
||||||
func (t *DNSResolvers) startSecureFlowsWithSema(ctx context.Context, sema <-chan any, addresses []string) {
|
func (t *DNSResolvers) startSecureFlowsWithSema(ctx context.Context, sema <-chan any, addresses []DNSEntry) {
|
||||||
port := "443"
|
port := "443"
|
||||||
if urlPort := t.URL.Port(); urlPort != "" {
|
if urlPort := t.URL.Port(); urlPort != "" {
|
||||||
if t.URL.Scheme != "https" {
|
if t.URL.Scheme != "https" {
|
||||||
|
@ -472,12 +481,16 @@ func (t *DNSResolvers) startSecureFlowsWithSema(ctx context.Context, sema <-chan
|
||||||
port = urlPort
|
port = urlPort
|
||||||
}
|
}
|
||||||
for _, addr := range addresses {
|
for _, addr := range addresses {
|
||||||
|
maybeNilSema := sema
|
||||||
|
if (addr.Flags & DNSAddrFlagSystemResolver) == 0 {
|
||||||
|
maybeNilSema = nil // see https://github.com/ooni/probe/issues/2258
|
||||||
|
}
|
||||||
task := &SecureFlow{
|
task := &SecureFlow{
|
||||||
Address: net.JoinHostPort(addr, port),
|
Address: net.JoinHostPort(addr.Addr, port),
|
||||||
DNSCache: t.DNSCache,
|
DNSCache: t.DNSCache,
|
||||||
IDGenerator: t.IDGenerator,
|
IDGenerator: t.IDGenerator,
|
||||||
Logger: t.Logger,
|
Logger: t.Logger,
|
||||||
Sema: sema,
|
Sema: maybeNilSema,
|
||||||
TestKeys: t.TestKeys,
|
TestKeys: t.TestKeys,
|
||||||
ZeroTime: t.ZeroTime,
|
ZeroTime: t.ZeroTime,
|
||||||
WaitGroup: t.WaitGroup,
|
WaitGroup: t.WaitGroup,
|
||||||
|
@ -496,10 +509,16 @@ func (t *DNSResolvers) startSecureFlowsWithSema(ctx context.Context, sema <-chan
|
||||||
}
|
}
|
||||||
|
|
||||||
// maybeStartControlFlow starts the control flow iff .Session and .THAddr are set.
|
// maybeStartControlFlow starts the control flow iff .Session and .THAddr are set.
|
||||||
func (t *DNSResolvers) maybeStartControlFlow(ctx context.Context, addresses []string) {
|
func (t *DNSResolvers) maybeStartControlFlow(ctx context.Context, addresses []DNSEntry) {
|
||||||
|
// note: for subsequent requests we don't set .Session and .THAddr hence
|
||||||
|
// we are not going to query the test helper more than once
|
||||||
if t.Session != nil && t.THAddr != "" {
|
if t.Session != nil && t.THAddr != "" {
|
||||||
|
var addrs []string
|
||||||
|
for _, addr := range addresses {
|
||||||
|
addrs = append(addrs, addr.Addr)
|
||||||
|
}
|
||||||
ctrl := &Control{
|
ctrl := &Control{
|
||||||
Addresses: addresses,
|
Addresses: addrs,
|
||||||
ExtraMeasurementsStarter: t, // allows starting follow-up measurement flows
|
ExtraMeasurementsStarter: t, // allows starting follow-up measurement flows
|
||||||
Logger: t.Logger,
|
Logger: t.Logger,
|
||||||
TestKeys: t.TestKeys,
|
TestKeys: t.TestKeys,
|
||||||
|
|
|
@ -36,7 +36,7 @@ func (m *Measurer) ExperimentName() string {
|
||||||
|
|
||||||
// ExperimentVersion implements model.ExperimentMeasurer.
|
// ExperimentVersion implements model.ExperimentMeasurer.
|
||||||
func (m *Measurer) ExperimentVersion() string {
|
func (m *Measurer) ExperimentVersion() string {
|
||||||
return "0.5.3"
|
return "0.5.4"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run implements model.ExperimentMeasurer.
|
// Run implements model.ExperimentMeasurer.
|
||||||
|
|
|
@ -45,7 +45,7 @@ func (h *Handler) HandleLog(e *log.Entry) (err error) {
|
||||||
case log.DebugLevel:
|
case log.DebugLevel:
|
||||||
level = "🧐"
|
level = "🧐"
|
||||||
case log.InfoLevel:
|
case log.InfoLevel:
|
||||||
level = "🗒️"
|
level = " "
|
||||||
case log.WarnLevel:
|
case log.WarnLevel:
|
||||||
level = "🔥"
|
level = "🔥"
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -74,7 +74,7 @@ func TestLogHandlerHandleLog(t *testing.T) {
|
||||||
Name: "info level with emoji",
|
Name: "info level with emoji",
|
||||||
Emoji: true,
|
Emoji: true,
|
||||||
Level: log.InfoLevel,
|
Level: log.InfoLevel,
|
||||||
ExpectSeverity: "🗒️",
|
ExpectSeverity: " ",
|
||||||
}, {
|
}, {
|
||||||
Name: "warn level with emoji",
|
Name: "warn level with emoji",
|
||||||
Emoji: true,
|
Emoji: true,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user