Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: anomaly with android_dns_cache_no_data and inconsistent dns #1211

Merged
merged 8 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions internal/experiment/webconnectivity/dnsanalysis.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,14 @@ func DNSAnalysis(URL *url.URL, measurement DNSLookupResult,
control ControlResponse) (out DNSAnalysisResult) {
// 0. start assuming it's not consistent
out.DNSConsistency = &DNSInconsistent

// 1. flip to consistent if we're targeting an IP address because the
// control will actually return dns_name_error in this case.
if net.ParseIP(URL.Hostname()) != nil {
out.DNSConsistency = &DNSConsistent
return
}

// 2. flip to consistent if the failures are compatible
if measurement.Failure != nil && control.DNS.Failure != nil {
switch *control.DNS.Failure {
Expand All @@ -57,6 +59,7 @@ func DNSAnalysis(URL *url.URL, measurement DNSLookupResult,
}
return
}

// 3. flip to consistent if measurement and control returned IP addresses
// that belong to the same Autonomous System(s).
//
Expand Down Expand Up @@ -85,6 +88,7 @@ func DNSAnalysis(URL *url.URL, measurement DNSLookupResult,
return
}
}

// 4. when ASN lookup failed (unlikely), check whether
// there is overlap in the returned IP addresses
ipmap := make(map[string]int)
Expand All @@ -101,6 +105,7 @@ func DNSAnalysis(URL *url.URL, measurement DNSLookupResult,
return
}
}

// 5. conclude that measurement and control are inconsistent
return
}
30 changes: 26 additions & 4 deletions internal/experiment/webconnectivity/summary.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ func Summarize(tk *TestKeys) (out Summary) {
defer func() {
out.Blocking = DetermineBlocking(out)
}()

var (
accessible = true
inaccessible = false
Expand All @@ -108,6 +109,7 @@ func Summarize(tk *TestKeys) (out Summary) {
httpFailure = "http-failure"
tcpIP = "tcp_ip"
)

// If the measurement was for an HTTPS website and the HTTP experiment
// succeeded, then either there is a compromised CA in our pool (which is
// certifi-go), or there is transparent proxying, or we are actually
Expand All @@ -119,11 +121,13 @@ func Summarize(tk *TestKeys) (out Summary) {
out.Status |= StatusSuccessSecure
return
}

// If we couldn't contact the control, we cannot do much more here.
if tk.ControlFailure != nil {
out.Status |= StatusAnomalyControlUnreachable
return
}

// If DNS failed with NXDOMAIN and the control DNS is consistent, then it
// means this website does not exist anymore. We need to include the weird
// cache failure on Android into this analysis because that failure means
Expand All @@ -142,15 +146,33 @@ func Summarize(tk *TestKeys) (out Summary) {
out.Status |= StatusSuccessNXDOMAIN | StatusExperimentDNS
return
}
// Otherwise, if DNS failed with NXDOMAIN, it's DNS based blocking.
// TODO(bassosimone): do we wanna include other errors here? Like timeout?
if tk.DNSExperimentFailure != nil &&
*tk.DNSExperimentFailure == netxlite.FailureDNSNXDOMAINError {

// Web Connectivity's analysis algorithm up until v0.4.2 gave priority to checking for http-diff
// over saying that there's "dns" blocking when the DNS is inconsistent.
//
// In v0.4.3, we want to address https://github.com/ooni/probe/issues/2499 while still
// trying to preserve the original spirit of the v0.4.2 analysis algorithm.
//
// To this end, we _only_ flag failure if the following happens:
bassosimone marked this conversation as resolved.
Show resolved Hide resolved
//
// 1. the DNS is inconsistent; and
//
// 2. the probe's DNS lookup has failed with dns_nxdomain_error or android_dns_cache_no_data.
//
// By using this algorithm, we narrow the scope and impact of this change but we are, at
// the same time, able to catch cases such as the one mentioned by the issue above.
//
// A more aggressive approach would flag as "dns" blocking any inconsistent result but
// that would depart quite a lot from the behavior of v0.4.2.
if tk.DNSConsistency != nil && *tk.DNSConsistency == DNSInconsistent &&
tk.DNSExperimentFailure != nil && (*tk.DNSExperimentFailure == netxlite.FailureDNSNXDOMAINError ||
*tk.DNSExperimentFailure == netxlite.FailureAndroidDNSCacheNoData) {
out.Accessible = &inaccessible
out.BlockingReason = &dns
out.Status |= StatusAnomalyDNS | StatusExperimentDNS
return
}

// If we tried to connect more than once and never succedeed and we were
// able to measure DNS consistency, then we can conclude something.
if tk.TCPConnectAttempts > 0 && tk.TCPConnectSuccesses <= 0 && tk.DNSConsistency != nil {
Expand Down
2 changes: 1 addition & 1 deletion internal/experiment/webconnectivity/webconnectivity.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (

const (
testName = "web_connectivity"
testVersion = "0.4.2"
testVersion = "0.4.3"
)

// Config contains the experiment config.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func TestNewExperimentMeasurer(t *testing.T) {
if measurer.ExperimentName() != "web_connectivity" {
t.Fatal("unexpected name")
}
if measurer.ExperimentVersion() != "0.4.2" {
if measurer.ExperimentVersion() != "0.4.3" {
t.Fatal("unexpected version")
}
}
Expand Down
7 changes: 4 additions & 3 deletions internal/experiment/webconnectivityqa/dnsblocking.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
func dnsBlockingAndroidDNSCacheNoData() *TestCase {
return &TestCase{
Name: "dnsBlockingAndroidDNSCacheNoData",
Flags: TestCaseFlagNoV04, // see https://github.com/ooni/probe-cli/pull/1211
Flags: 0,
Input: "https://www.example.com/",
Configure: func(env *netemx.QAEnv) {
// make sure the env knows we want to emulate our getaddrinfo wrapper behavior
Expand All @@ -23,8 +23,9 @@ func dnsBlockingAndroidDNSCacheNoData() *TestCase {
ExpectTestKeys: &testKeys{
DNSExperimentFailure: "android_dns_cache_no_data",
DNSConsistency: "inconsistent",
XDNSFlags: 2, // AnalysisDNSUnexpectedFailure
XBlockingFlags: 33, // analysisFlagDNSBlocking | analysisFlagSuccess
XStatus: 2080, // StatusExperimentDNS | StatusAnomalyDNS
XDNSFlags: 2, // AnalysisDNSUnexpectedFailure
XBlockingFlags: 33, // analysisFlagDNSBlocking | analysisFlagSuccess
Accessible: false,
Blocking: "dns",
},
Expand Down
8 changes: 4 additions & 4 deletions internal/experiment/webconnectivityqa/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func TestRunTestCase(t *testing.T) {
return "web_connectivity"
},
MockExperimentVersion: func() string {
return "0.4.2"
return "0.4.3"
},
MockRun: func(ctx context.Context, args *model.ExperimentArgs) error {
args.Measurement.TestKeys = &testKeys{}
Expand Down Expand Up @@ -107,7 +107,7 @@ func TestRunTestCase(t *testing.T) {
return "web_connectivity"
},
MockExperimentVersion: func() string {
return "0.4.2"
return "0.4.3"
},
MockRun: func(ctx context.Context, args *model.ExperimentArgs) error {
args.Measurement.TestKeys = &testKeys{
Expand Down Expand Up @@ -139,7 +139,7 @@ func TestRunTestCase(t *testing.T) {
return "web_connectivity"
},
MockExperimentVersion: func() string {
return "0.4.2"
return "0.4.3"
},
MockRun: func(ctx context.Context, args *model.ExperimentArgs) error {
args.Measurement.TestKeys = &testKeys{
Expand Down Expand Up @@ -173,7 +173,7 @@ func TestRunTestCase(t *testing.T) {
return "web_connectivity"
},
MockExperimentVersion: func() string {
return "0.4.2"
return "0.4.3"
},
MockRun: func(ctx context.Context, args *model.ExperimentArgs) error {
args.Measurement.TestKeys = &testKeys{
Expand Down
2 changes: 1 addition & 1 deletion internal/experiment/webconnectivityqa/testkeys.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func compareTestKeys(expected, got *testKeys) error {
}

switch got.XExperimentVersion {
case "0.4.2":
case "0.4.3":
// ignore the fields that are specific to LTE
options = append(options, cmpopts.IgnoreFields(testKeys{}, "XDNSFlags", "XBlockingFlags", "XNullNullFlags"))

Expand Down