refactor: move TH structs and definitions to model (#894)

This commit moves the TH structs and definitions to model. We don't want
oohelperd to depend on web_connectivity@v0.4.

Part of https://github.com/ooni/probe/issues/2240
This commit is contained in:
Simone Basso 2022-08-28 20:20:12 +02:00 committed by GitHub
parent 110a11828b
commit bb6563f363
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 161 additions and 157 deletions

View File

@ -11,7 +11,6 @@ import (
"net/url" "net/url"
"github.com/apex/log" "github.com/apex/log"
"github.com/ooni/probe-cli/v3/internal/engine/experiment/webconnectivity"
"github.com/ooni/probe-cli/v3/internal/model" "github.com/ooni/probe-cli/v3/internal/model"
"github.com/ooni/probe-cli/v3/internal/netxlite" "github.com/ooni/probe-cli/v3/internal/netxlite"
"github.com/ooni/probe-cli/v3/internal/runtimex" "github.com/ooni/probe-cli/v3/internal/runtimex"
@ -20,10 +19,10 @@ import (
type ( type (
// CtrlResponse is the type of response returned by the test helper. // CtrlResponse is the type of response returned by the test helper.
CtrlResponse = webconnectivity.ControlResponse CtrlResponse = model.THResponse
// ctrlRequest is the type of the request sent to the test helper. // ctrlRequest is the type of the request sent to the test helper.
ctrlRequest = webconnectivity.ControlRequest ctrlRequest = model.THRequest
) )
// The following errors may be returned by this implementation. // The following errors may be returned by this implementation.

View File

@ -9,7 +9,6 @@ import (
"sync" "sync"
"time" "time"
"github.com/ooni/probe-cli/v3/internal/engine/experiment/webconnectivity"
"github.com/ooni/probe-cli/v3/internal/model" "github.com/ooni/probe-cli/v3/internal/model"
"github.com/ooni/probe-cli/v3/internal/netxlite" "github.com/ooni/probe-cli/v3/internal/netxlite"
"github.com/ooni/probe-cli/v3/internal/tracex" "github.com/ooni/probe-cli/v3/internal/tracex"
@ -20,7 +19,7 @@ var newfailure = tracex.NewFailure
// ctrlDNSResult is the result of the DNS check performed by // ctrlDNSResult is the result of the DNS check performed by
// the Web Connectivity test helper. // the Web Connectivity test helper.
type ctrlDNSResult = webconnectivity.ControlDNSResult type ctrlDNSResult = model.THDNSResult
// dnsConfig configures the DNS check. // dnsConfig configures the DNS check.
type dnsConfig struct { type dnsConfig struct {
@ -70,7 +69,7 @@ func dnsMapFailure(failure *string) *string {
case netxlite.FailureDNSNXDOMAINError: case netxlite.FailureDNSNXDOMAINError:
// We have a name for this string because dnsanalysis.go is // We have a name for this string because dnsanalysis.go is
// already checking for this specific error string. // already checking for this specific error string.
s := webconnectivity.DNSNameError s := model.THDNSNameError
return &s return &s
case netxlite.FailureDNSNoAnswer: case netxlite.FailureDNSNoAnswer:
// In this case the legacy TH would produce an empty // In this case the legacy TH would produce an empty

View File

@ -6,7 +6,6 @@ import (
"testing" "testing"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/ooni/probe-cli/v3/internal/engine/experiment/webconnectivity"
"github.com/ooni/probe-cli/v3/internal/model" "github.com/ooni/probe-cli/v3/internal/model"
"github.com/ooni/probe-cli/v3/internal/model/mocks" "github.com/ooni/probe-cli/v3/internal/model/mocks"
"github.com/ooni/probe-cli/v3/internal/netxlite" "github.com/ooni/probe-cli/v3/internal/netxlite"
@ -28,7 +27,7 @@ func Test_dnsMapFailure(t *testing.T) {
}, { }, {
name: "nxdomain", name: "nxdomain",
failure: stringPointerForString(netxlite.FailureDNSNXDOMAINError), failure: stringPointerForString(netxlite.FailureDNSNXDOMAINError),
want: stringPointerForString(webconnectivity.DNSNameError), want: stringPointerForString(model.THDNSNameError),
}, { }, {
name: "no answer", name: "no answer",
failure: stringPointerForString(netxlite.FailureDNSNoAnswer), failure: stringPointerForString(netxlite.FailureDNSNoAnswer),
@ -79,7 +78,7 @@ func TestDNSDo(t *testing.T) {
}, },
} }
}, },
Out: make(chan webconnectivity.ControlDNSResult, 1), Out: make(chan model.THDNSResult, 1),
Wg: &sync.WaitGroup{}, Wg: &sync.WaitGroup{},
} }
config.Wg.Add(1) config.Wg.Add(1)

View File

@ -23,7 +23,7 @@ import (
// ctrlHTTPResponse is the result of the HTTP check performed by // ctrlHTTPResponse is the result of the HTTP check performed by
// the Web Connectivity test helper. // the Web Connectivity test helper.
type ctrlHTTPResponse = webconnectivity.ControlHTTPRequestResult type ctrlHTTPResponse = model.THHTTPRequestResult
// httpConfig configures the HTTP check. // httpConfig configures the HTTP check.
type httpConfig struct { type httpConfig struct {

View File

@ -10,34 +10,34 @@ import (
"sort" "sort"
"strings" "strings"
"github.com/ooni/probe-cli/v3/internal/engine/experiment/webconnectivity"
"github.com/ooni/probe-cli/v3/internal/geoipx" "github.com/ooni/probe-cli/v3/internal/geoipx"
"github.com/ooni/probe-cli/v3/internal/model"
"github.com/ooni/probe-cli/v3/internal/netxlite" "github.com/ooni/probe-cli/v3/internal/netxlite"
) )
// newIPInfo creates an IP to IPInfo mapping from addresses resolved // newIPInfo creates an IP to IPInfo mapping from addresses resolved
// by the probe (inside [creq]) or the TH (inside [addrs]). // by the probe (inside [creq]) or the TH (inside [addrs]).
func newIPInfo(creq *ctrlRequest, addrs []string) map[string]*webconnectivity.ControlIPInfo { func newIPInfo(creq *ctrlRequest, addrs []string) map[string]*model.THIPInfo {
discoveredby := make(map[string]int64) discoveredby := make(map[string]int64)
for _, epnt := range creq.TCPConnect { for _, epnt := range creq.TCPConnect {
addr, _, err := net.SplitHostPort(epnt) addr, _, err := net.SplitHostPort(epnt)
if err != nil || net.ParseIP(addr) == nil { if err != nil || net.ParseIP(addr) == nil {
continue continue
} }
discoveredby[addr] |= webconnectivity.ControlIPInfoFlagResolvedByProbe discoveredby[addr] |= model.THIPInfoFlagResolvedByProbe
} }
for _, addr := range addrs { for _, addr := range addrs {
if net.ParseIP(addr) != nil { if net.ParseIP(addr) != nil {
discoveredby[addr] |= webconnectivity.ControlIPInfoFlagResolvedByTH discoveredby[addr] |= model.THIPInfoFlagResolvedByTH
} }
} }
ipinfo := make(map[string]*webconnectivity.ControlIPInfo) ipinfo := make(map[string]*model.THIPInfo)
for addr, flags := range discoveredby { for addr, flags := range discoveredby {
if netxlite.IsBogon(addr) { // note: we already excluded non-IP addrs above if netxlite.IsBogon(addr) { // note: we already excluded non-IP addrs above
flags |= webconnectivity.ControlIPInfoFlagIsBogon flags |= model.THIPInfoFlagIsBogon
} }
asn, _, _ := geoipx.LookupASN(addr) // AS0 on failure asn, _, _ := geoipx.LookupASN(addr) // AS0 on failure
ipinfo[addr] = &webconnectivity.ControlIPInfo{ ipinfo[addr] = &model.THIPInfo{
ASN: int64(asn), ASN: int64(asn),
Flags: flags, Flags: flags,
} }
@ -70,7 +70,7 @@ type endpointInfo struct {
// whether an IP address is valid for a domain; // whether an IP address is valid for a domain;
// //
// 4. otherwise, we don't generate any endpoint to measure. // 4. otherwise, we don't generate any endpoint to measure.
func ipInfoToEndpoints(URL *url.URL, ipinfo map[string]*webconnectivity.ControlIPInfo) []endpointInfo { func ipInfoToEndpoints(URL *url.URL, ipinfo map[string]*model.THIPInfo) []endpointInfo {
var ports []string var ports []string
if port := URL.Port(); port != "" { if port := URL.Port(); port != "" {
ports = []string{port} // as documented ports = []string{port} // as documented
@ -81,7 +81,7 @@ func ipInfoToEndpoints(URL *url.URL, ipinfo map[string]*webconnectivity.ControlI
} }
out := []endpointInfo{} out := []endpointInfo{}
for addr, info := range ipinfo { for addr, info := range ipinfo {
if (info.Flags & webconnectivity.ControlIPInfoFlagIsBogon) != 0 { if (info.Flags & model.THIPInfoFlagIsBogon) != 0 {
continue // as documented continue // as documented
} }
for _, port := range ports { for _, port := range ports {

View File

@ -5,7 +5,7 @@ import (
"testing" "testing"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/ooni/probe-cli/v3/internal/engine/experiment/webconnectivity" "github.com/ooni/probe-cli/v3/internal/model"
) )
func Test_newIPInfo(t *testing.T) { func Test_newIPInfo(t *testing.T) {
@ -16,22 +16,22 @@ func Test_newIPInfo(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
args args args args
want map[string]*webconnectivity.ControlIPInfo want map[string]*model.THIPInfo
}{{ }{{
name: "with empty input", name: "with empty input",
args: args{ args: args{
creq: &webconnectivity.ControlRequest{ creq: &model.THRequest{
HTTPRequest: "", HTTPRequest: "",
HTTPRequestHeaders: map[string][]string{}, HTTPRequestHeaders: map[string][]string{},
TCPConnect: []string{}, TCPConnect: []string{},
}, },
addrs: []string{}, addrs: []string{},
}, },
want: map[string]*webconnectivity.ControlIPInfo{}, want: map[string]*model.THIPInfo{},
}, { }, {
name: "typical case with also bogons", name: "typical case with also bogons",
args: args{ args: args{
creq: &webconnectivity.ControlRequest{ creq: &model.THRequest{
HTTPRequest: "", HTTPRequest: "",
HTTPRequestHeaders: map[string][]string{}, HTTPRequestHeaders: map[string][]string{},
TCPConnect: []string{ TCPConnect: []string{
@ -44,24 +44,24 @@ func Test_newIPInfo(t *testing.T) {
"8.8.4.4", "8.8.4.4",
}, },
}, },
want: map[string]*webconnectivity.ControlIPInfo{ want: map[string]*model.THIPInfo{
"10.0.0.1": { "10.0.0.1": {
ASN: 0, ASN: 0,
Flags: webconnectivity.ControlIPInfoFlagIsBogon | webconnectivity.ControlIPInfoFlagResolvedByProbe, Flags: model.THIPInfoFlagIsBogon | model.THIPInfoFlagResolvedByProbe,
}, },
"8.8.8.8": { "8.8.8.8": {
ASN: 15169, ASN: 15169,
Flags: webconnectivity.ControlIPInfoFlagResolvedByProbe | webconnectivity.ControlIPInfoFlagResolvedByTH, Flags: model.THIPInfoFlagResolvedByProbe | model.THIPInfoFlagResolvedByTH,
}, },
"8.8.4.4": { "8.8.4.4": {
ASN: 15169, ASN: 15169,
Flags: webconnectivity.ControlIPInfoFlagResolvedByTH, Flags: model.THIPInfoFlagResolvedByTH,
}, },
}, },
}, { }, {
name: "with invalid endpoint", name: "with invalid endpoint",
args: args{ args: args{
creq: &webconnectivity.ControlRequest{ creq: &model.THRequest{
HTTPRequest: "", HTTPRequest: "",
HTTPRequestHeaders: map[string][]string{}, HTTPRequestHeaders: map[string][]string{},
TCPConnect: []string{ TCPConnect: []string{
@ -70,11 +70,11 @@ func Test_newIPInfo(t *testing.T) {
}, },
addrs: []string{}, addrs: []string{},
}, },
want: map[string]*webconnectivity.ControlIPInfo{}, want: map[string]*model.THIPInfo{},
}, { }, {
name: "with invalid IP addr", name: "with invalid IP addr",
args: args{ args: args{
creq: &webconnectivity.ControlRequest{ creq: &model.THRequest{
HTTPRequest: "", HTTPRequest: "",
HTTPRequestHeaders: map[string][]string{}, HTTPRequestHeaders: map[string][]string{},
TCPConnect: []string{ TCPConnect: []string{
@ -83,7 +83,7 @@ func Test_newIPInfo(t *testing.T) {
}, },
addrs: []string{}, addrs: []string{},
}, },
want: map[string]*webconnectivity.ControlIPInfo{}, want: map[string]*model.THIPInfo{},
}} }}
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
@ -98,7 +98,7 @@ func Test_newIPInfo(t *testing.T) {
func Test_ipInfoToEndpoints(t *testing.T) { func Test_ipInfoToEndpoints(t *testing.T) {
type args struct { type args struct {
URL *url.URL URL *url.URL
ipinfo map[string]*webconnectivity.ControlIPInfo ipinfo map[string]*model.THIPInfo
} }
tests := []struct { tests := []struct {
name string name string
@ -115,7 +115,7 @@ func Test_ipInfoToEndpoints(t *testing.T) {
name: "with empty map and empty URL", name: "with empty map and empty URL",
args: args{ args: args{
URL: &url.URL{}, URL: &url.URL{},
ipinfo: map[string]*webconnectivity.ControlIPInfo{}, ipinfo: map[string]*model.THIPInfo{},
}, },
want: []endpointInfo{}, want: []endpointInfo{},
}, { }, {
@ -124,18 +124,18 @@ func Test_ipInfoToEndpoints(t *testing.T) {
URL: &url.URL{ URL: &url.URL{
Scheme: "http", Scheme: "http",
}, },
ipinfo: map[string]*webconnectivity.ControlIPInfo{ ipinfo: map[string]*model.THIPInfo{
"10.0.0.1": { "10.0.0.1": {
ASN: 0, ASN: 0,
Flags: webconnectivity.ControlIPInfoFlagIsBogon | webconnectivity.ControlIPInfoFlagResolvedByProbe, Flags: model.THIPInfoFlagIsBogon | model.THIPInfoFlagResolvedByProbe,
}, },
"8.8.8.8": { "8.8.8.8": {
ASN: 15169, ASN: 15169,
Flags: webconnectivity.ControlIPInfoFlagResolvedByProbe | webconnectivity.ControlIPInfoFlagResolvedByTH, Flags: model.THIPInfoFlagResolvedByProbe | model.THIPInfoFlagResolvedByTH,
}, },
"8.8.4.4": { "8.8.4.4": {
ASN: 15169, ASN: 15169,
Flags: webconnectivity.ControlIPInfoFlagResolvedByTH, Flags: model.THIPInfoFlagResolvedByTH,
}, },
}, },
}, },
@ -162,18 +162,18 @@ func Test_ipInfoToEndpoints(t *testing.T) {
URL: &url.URL{ URL: &url.URL{
Host: "dns.google:5432", Host: "dns.google:5432",
}, },
ipinfo: map[string]*webconnectivity.ControlIPInfo{ ipinfo: map[string]*model.THIPInfo{
"10.0.0.1": { "10.0.0.1": {
ASN: 0, ASN: 0,
Flags: webconnectivity.ControlIPInfoFlagIsBogon | webconnectivity.ControlIPInfoFlagResolvedByProbe, Flags: model.THIPInfoFlagIsBogon | model.THIPInfoFlagResolvedByProbe,
}, },
"8.8.8.8": { "8.8.8.8": {
ASN: 15169, ASN: 15169,
Flags: webconnectivity.ControlIPInfoFlagResolvedByProbe | webconnectivity.ControlIPInfoFlagResolvedByTH, Flags: model.THIPInfoFlagResolvedByProbe | model.THIPInfoFlagResolvedByTH,
}, },
"8.8.4.4": { "8.8.4.4": {
ASN: 15169, ASN: 15169,
Flags: webconnectivity.ControlIPInfoFlagResolvedByTH, Flags: model.THIPInfoFlagResolvedByTH,
}, },
}, },
}, },
@ -190,18 +190,18 @@ func Test_ipInfoToEndpoints(t *testing.T) {
name: "with addresses and some bogons, no port, and unknown scheme", name: "with addresses and some bogons, no port, and unknown scheme",
args: args{ args: args{
URL: &url.URL{}, URL: &url.URL{},
ipinfo: map[string]*webconnectivity.ControlIPInfo{ ipinfo: map[string]*model.THIPInfo{
"10.0.0.1": { "10.0.0.1": {
ASN: 0, ASN: 0,
Flags: webconnectivity.ControlIPInfoFlagIsBogon | webconnectivity.ControlIPInfoFlagResolvedByProbe, Flags: model.THIPInfoFlagIsBogon | model.THIPInfoFlagResolvedByProbe,
}, },
"8.8.8.8": { "8.8.8.8": {
ASN: 15169, ASN: 15169,
Flags: webconnectivity.ControlIPInfoFlagResolvedByProbe | webconnectivity.ControlIPInfoFlagResolvedByTH, Flags: model.THIPInfoFlagResolvedByProbe | model.THIPInfoFlagResolvedByTH,
}, },
"8.8.4.4": { "8.8.4.4": {
ASN: 15169, ASN: 15169,
Flags: webconnectivity.ControlIPInfoFlagResolvedByTH, Flags: model.THIPInfoFlagResolvedByTH,
}, },
}, },
}, },
@ -212,18 +212,18 @@ func Test_ipInfoToEndpoints(t *testing.T) {
URL: &url.URL{ URL: &url.URL{
Scheme: "https", Scheme: "https",
}, },
ipinfo: map[string]*webconnectivity.ControlIPInfo{ ipinfo: map[string]*model.THIPInfo{
"10.0.0.1": { "10.0.0.1": {
ASN: 0, ASN: 0,
Flags: webconnectivity.ControlIPInfoFlagIsBogon | webconnectivity.ControlIPInfoFlagResolvedByProbe, Flags: model.THIPInfoFlagIsBogon | model.THIPInfoFlagResolvedByProbe,
}, },
"8.8.8.8": { "8.8.8.8": {
ASN: 15169, ASN: 15169,
Flags: webconnectivity.ControlIPInfoFlagResolvedByProbe | webconnectivity.ControlIPInfoFlagResolvedByTH, Flags: model.THIPInfoFlagResolvedByProbe | model.THIPInfoFlagResolvedByTH,
}, },
"8.8.4.4": { "8.8.4.4": {
ASN: 15169, ASN: 15169,
Flags: webconnectivity.ControlIPInfoFlagResolvedByTH, Flags: model.THIPInfoFlagResolvedByTH,
}, },
}, },
}, },

View File

@ -10,15 +10,15 @@ import (
"net/url" "net/url"
"sync" "sync"
"github.com/ooni/probe-cli/v3/internal/engine/experiment/webconnectivity" "github.com/ooni/probe-cli/v3/internal/model"
) )
type ( type (
// ctrlRequest is the request sent to the test helper // ctrlRequest is the request sent to the test helper
ctrlRequest = webconnectivity.ControlRequest ctrlRequest = model.THRequest
// ctrlResponse is the response from the test helper // ctrlResponse is the response from the test helper
ctrlResponse = webconnectivity.ControlResponse ctrlResponse = model.THResponse
) )
// measure performs the measurement described by the request and // measure performs the measurement described by the request and
@ -48,11 +48,11 @@ func measure(ctx context.Context, config *handler, creq *ctrlRequest) (*ctrlResp
// start assembling the response // start assembling the response
cresp := &ctrlResponse{ cresp := &ctrlResponse{
TCPConnect: map[string]webconnectivity.ControlTCPConnectResult{}, TCPConnect: map[string]model.THTCPConnectResult{},
TLSHandshake: map[string]webconnectivity.ControlTLSHandshakeResult{}, TLSHandshake: map[string]model.THTLSHandshakeResult{},
HTTPRequest: webconnectivity.ControlHTTPRequestResult{}, HTTPRequest: model.THHTTPRequestResult{},
DNS: webconnectivity.ControlDNSResult{}, DNS: model.THDNSResult{},
IPInfo: map[string]*webconnectivity.ControlIPInfo{}, IPInfo: map[string]*model.THIPInfo{},
} }
select { select {
case cresp.DNS = <-dnsch: case cresp.DNS = <-dnsch:
@ -111,7 +111,7 @@ Loop:
if tcpconn.TLS != nil { if tcpconn.TLS != nil {
cresp.TLSHandshake[tcpconn.Endpoint] = *tcpconn.TLS cresp.TLSHandshake[tcpconn.Endpoint] = *tcpconn.TLS
if info := cresp.IPInfo[tcpconn.Address]; info != nil && tcpconn.TLS.Failure == nil { if info := cresp.IPInfo[tcpconn.Address]; info != nil && tcpconn.TLS.Failure == nil {
info.Flags |= webconnectivity.ControlIPInfoFlagValidForDomain info.Flags |= model.THIPInfoFlagValidForDomain
} }
} }
default: default:

View File

@ -10,17 +10,16 @@ import (
"sync" "sync"
"time" "time"
"github.com/ooni/probe-cli/v3/internal/engine/experiment/webconnectivity"
"github.com/ooni/probe-cli/v3/internal/measurexlite" "github.com/ooni/probe-cli/v3/internal/measurexlite"
"github.com/ooni/probe-cli/v3/internal/model" "github.com/ooni/probe-cli/v3/internal/model"
"github.com/ooni/probe-cli/v3/internal/netxlite" "github.com/ooni/probe-cli/v3/internal/netxlite"
) )
// ctrlTCPResult is the result of the TCP check performed by the test helper. // ctrlTCPResult is the result of the TCP check performed by the test helper.
type ctrlTCPResult = webconnectivity.ControlTCPConnectResult type ctrlTCPResult = model.THTCPConnectResult
// ctrlTLSResult is the result of the TLS check performed by the test helper. // ctrlTLSResult is the result of the TLS check performed by the test helper.
type ctrlTLSResult = webconnectivity.ControlTLSHandshakeResult type ctrlTLSResult = model.THTLSHandshakeResult
// tcpResultPair contains the endpoint and the corresponding result. // tcpResultPair contains the endpoint and the corresponding result.
type tcpResultPair struct { type tcpResultPair struct {
@ -73,7 +72,7 @@ func tcpDo(ctx context.Context, config *tcpConfig) {
out := &tcpResultPair{ out := &tcpResultPair{
Address: config.Address, Address: config.Address,
Endpoint: config.Endpoint, Endpoint: config.Endpoint,
TCP: webconnectivity.ControlTCPConnectResult{}, TCP: model.THTCPConnectResult{},
TLS: nil, // means: not measured TLS: nil, // means: not measured
} }
defer func() { defer func() {

View File

@ -9,90 +9,15 @@ import (
"github.com/ooni/probe-cli/v3/internal/netxlite" "github.com/ooni/probe-cli/v3/internal/netxlite"
) )
// TODO(bassosimone): these struct definitions should be moved outside the // Redirect to types defined inside the model package
// specific implementation of Web Connectivity v0.4. type (
ControlRequest = model.THRequest
// ControlRequest is the request that we send to the control ControlResponse = model.THResponse
type ControlRequest struct { ControlDNSResult = model.THDNSResult
HTTPRequest string `json:"http_request"` ControlHTTPRequestResult = model.THHTTPRequestResult
HTTPRequestHeaders map[string][]string `json:"http_request_headers"` ControlTCPConnectResult = model.THTCPConnectResult
TCPConnect []string `json:"tcp_connect"`
}
// ControlTCPConnectResult is the result of the TCP connect
// attempt performed by the control vantage point.
type ControlTCPConnectResult struct {
Status bool `json:"status"`
Failure *string `json:"failure"`
}
// ControlTLSHandshakeResult is the result of the TLS handshake
// attempt performed by the control vantage point.
type ControlTLSHandshakeResult struct {
ServerName string `json:"server_name"`
Status bool `json:"status"`
Failure *string `json:"failure"`
}
// ControlHTTPRequestResult is the result of the HTTP request
// performed by the control vantage point.
type ControlHTTPRequestResult struct {
BodyLength int64 `json:"body_length"`
Failure *string `json:"failure"`
Title string `json:"title"`
Headers map[string]string `json:"headers"`
StatusCode int64 `json:"status_code"`
}
// TODO(bassosimone): ASNs and FillASNs are private implementation details of v0.4
// that are actually ~annoying because we are mixing the data model with fields used
// by just the v0.4 client implementation. We should avoid repeating this mistake
// when implementing v0.5 of the client.
// ControlDNSResult is the result of the DNS lookup
// performed by the control vantage point.
type ControlDNSResult struct {
Failure *string `json:"failure"`
Addrs []string `json:"addrs"`
ASNs []int64 `json:"-"` // not visible from the JSON
}
// ControlIPInfo contains information about IP addresses resolved either
// by the probe or by the TH and processed by the TH.
type ControlIPInfo struct {
// ASN contains the address' AS number.
ASN int64 `json:"asn"`
// Flags contains flags describing this address.
Flags int64 `json:"flags"`
}
const (
// ControlIPInfoFlagResolvedByProbe indicates that the probe has
// resolved this IP address.
ControlIPInfoFlagResolvedByProbe = 1 << iota
// ControlIPInfoFlagResolvedByTH indicates that the test helper
// has resolved this IP address.
ControlIPInfoFlagResolvedByTH
// ControlIPInfoFlagIsBogon indicates that the address is a bogon
ControlIPInfoFlagIsBogon
// ControlIPInfoFlagValidForDomain indicates that an IP address
// is valid for the domain because it works with TLS
ControlIPInfoFlagValidForDomain
) )
// ControlResponse is the response from the control service.
type ControlResponse struct {
TCPConnect map[string]ControlTCPConnectResult `json:"tcp_connect"`
TLSHandshake map[string]ControlTLSHandshakeResult `json:"tls_handshake"`
HTTPRequest ControlHTTPRequestResult `json:"http_request"`
DNS ControlDNSResult `json:"dns"`
IPInfo map[string]*ControlIPInfo `json:"ip_info"`
}
// Control performs the control request and returns the response. // Control performs the control request and returns the response.
func Control( func Control(
ctx context.Context, sess model.ExperimentSession, ctx context.Context, sess model.ExperimentSession,
@ -110,16 +35,16 @@ func Control(
err = netxlite.NewTopLevelGenericErrWrapper(err) err = netxlite.NewTopLevelGenericErrWrapper(err)
} }
sess.Logger().Infof("control for %s... %+v", creq.HTTPRequest, model.ErrorToStringOrOK(err)) sess.Logger().Infof("control for %s... %+v", creq.HTTPRequest, model.ErrorToStringOrOK(err))
(&out.DNS).FillASNs(sess) fillASNs(&out.DNS)
return return
} }
// FillASNs fills the ASNs array of ControlDNSResult. For each Addr inside // fillASNs fills the ASNs array of ControlDNSResult. For each Addr inside
// of the ControlDNSResult structure, we obtain the corresponding ASN. // of the ControlDNSResult structure, we obtain the corresponding ASN.
// //
// This is very useful to know what ASNs were the IP addresses returned by // This is very useful to know what ASNs were the IP addresses returned by
// the control according to the probe's ASN database. // the control according to the probe's ASN database.
func (dns *ControlDNSResult) FillASNs(sess model.ExperimentSession) { func fillASNs(dns *ControlDNSResult) {
dns.ASNs = []int64{} dns.ASNs = []int64{}
for _, ip := range dns.Addrs { for _, ip := range dns.Addrs {
asn, _, _ := geoipx.LookupASN(ip) asn, _, _ := geoipx.LookupASN(ip)

View File

@ -1,26 +1,23 @@
package webconnectivity_test package webconnectivity
import ( import (
"testing" "testing"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/ooni/probe-cli/v3/internal/engine/experiment/webconnectivity"
"github.com/ooni/probe-cli/v3/internal/engine/mockable"
) )
func TestFillASNsEmpty(t *testing.T) { func TestFillASNsEmpty(t *testing.T) {
dns := new(webconnectivity.ControlDNSResult) dns := new(ControlDNSResult)
dns.FillASNs(new(mockable.Session)) fillASNs(dns)
if diff := cmp.Diff(dns.ASNs, []int64{}); diff != "" { if diff := cmp.Diff(dns.ASNs, []int64{}); diff != "" {
t.Fatal(diff) t.Fatal(diff)
} }
} }
func TestFillASNsSuccess(t *testing.T) { func TestFillASNsSuccess(t *testing.T) {
sess := newsession(t, false) dns := new(ControlDNSResult)
dns := new(webconnectivity.ControlDNSResult)
dns.Addrs = []string{"8.8.8.8", "1.1.1.1"} dns.Addrs = []string{"8.8.8.8", "1.1.1.1"}
dns.FillASNs(sess) fillASNs(dns)
if diff := cmp.Diff(dns.ASNs, []int64{15169, 13335}); diff != "" { if diff := cmp.Diff(dns.ASNs, []int64{15169, 13335}); diff != "" {
t.Fatal(diff) t.Fatal(diff)
} }

View File

@ -4,6 +4,7 @@ import (
"net" "net"
"net/url" "net/url"
"github.com/ooni/probe-cli/v3/internal/model"
"github.com/ooni/probe-cli/v3/internal/netxlite" "github.com/ooni/probe-cli/v3/internal/netxlite"
) )
@ -14,7 +15,7 @@ type DNSAnalysisResult struct {
} }
// DNSNameError is the error returned by the control on NXDOMAIN // DNSNameError is the error returned by the control on NXDOMAIN
const DNSNameError = "dns_name_error" const DNSNameError = model.THDNSNameError
var ( var (
// DNSConsistent indicates that the measurement and the // DNSConsistent indicates that the measurement and the

85
internal/model/th.go Normal file
View File

@ -0,0 +1,85 @@
package model
// THDNSNameError is the error returned by the control on NXDOMAIN
const THDNSNameError = "dns_name_error"
// THRequest is the request that we send to the control
type THRequest struct {
HTTPRequest string `json:"http_request"`
HTTPRequestHeaders map[string][]string `json:"http_request_headers"`
TCPConnect []string `json:"tcp_connect"`
}
// THTCPConnectResult is the result of the TCP connect
// attempt performed by the control vantage point.
type THTCPConnectResult struct {
Status bool `json:"status"`
Failure *string `json:"failure"`
}
// THTLSHandshakeResult is the result of the TLS handshake
// attempt performed by the control vantage point.
type THTLSHandshakeResult struct {
ServerName string `json:"server_name"`
Status bool `json:"status"`
Failure *string `json:"failure"`
}
// THHTTPRequestResult is the result of the HTTP request
// performed by the control vantage point.
type THHTTPRequestResult struct {
BodyLength int64 `json:"body_length"`
Failure *string `json:"failure"`
Title string `json:"title"`
Headers map[string]string `json:"headers"`
StatusCode int64 `json:"status_code"`
}
// TODO(bassosimone): ASNs is a private implementation detail of v0.4
// that is actually ~annoying because we are mixing the data model with fields used
// by just the v0.4 client implementation. We should avoid repeating this mistake
// when implementing v0.5 of the client and eventually remove ASNs.
// THDNSResult is the result of the DNS lookup
// performed by the control vantage point.
type THDNSResult struct {
Failure *string `json:"failure"`
Addrs []string `json:"addrs"`
ASNs []int64 `json:"-"` // not visible from the JSON
}
// THIPInfo contains information about IP addresses resolved either
// by the probe or by the TH and processed by the TH.
type THIPInfo struct {
// ASN contains the address' AS number.
ASN int64 `json:"asn"`
// Flags contains flags describing this address.
Flags int64 `json:"flags"`
}
const (
// THIPInfoFlagResolvedByProbe indicates that the probe has
// resolved this IP address.
THIPInfoFlagResolvedByProbe = 1 << iota
// THIPInfoFlagResolvedByTH indicates that the test helper
// has resolved this IP address.
THIPInfoFlagResolvedByTH
// THIPInfoFlagIsBogon indicates that the address is a bogon
THIPInfoFlagIsBogon
// THIPInfoFlagValidForDomain indicates that an IP address
// is valid for the domain because it works with TLS
THIPInfoFlagValidForDomain
)
// THResponse is the response from the control service.
type THResponse struct {
TCPConnect map[string]THTCPConnectResult `json:"tcp_connect"`
TLSHandshake map[string]THTLSHandshakeResult `json:"tls_handshake"`
HTTPRequest THHTTPRequestResult `json:"http_request"`
DNS THDNSResult `json:"dns"`
IPInfo map[string]*THIPInfo `json:"ip_info"`
}