Skip to content

Commit

Permalink
oonidatamodel: emit DNS data model
Browse files Browse the repository at this point in the history
This was not implemented up until now, but actually is better
to have it, because #2 can also ingest hostnames, and I really
would like to provide DNS info in such case.

While there add DNS info to both psiphon and telegram.
  • Loading branch information
bassosimone committed Jan 9, 2020
1 parent 4ec7991 commit 41e218c
Show file tree
Hide file tree
Showing 4 changed files with 316 additions and 60 deletions.
8 changes: 7 additions & 1 deletion experiment/psiphon/psiphon.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (

const (
testName = "psiphon"
testVersion = "0.3.0"
testVersion = "0.3.1"
)

// Config contains the experiment's configuration.
Expand Down Expand Up @@ -58,6 +58,9 @@ type TestKeys struct {
// interrupt the psiphon experiment.
MaxRuntime float64 `json:"max_runtime"`

// Queries contains the DNS queries.
Queries oonidatamodel.DNSQueriesList `json:"queries"`

// Requests contains HTTP measurements
Requests oonidatamodel.RequestList `json:"requests"`

Expand Down Expand Up @@ -126,6 +129,9 @@ func (r *runner) usetunnel(
URL: "https://www.google.com/humans.txt",
UserAgent: httpheader.RandomUserAgent(),
})
r.testkeys.Queries = append(
r.testkeys.Queries, oonidatamodel.NewDNSQueriesList(results.TestKeys)...,
)
r.testkeys.Requests = append(
r.testkeys.Requests, oonidatamodel.NewRequestList(results)...,
)
Expand Down
8 changes: 7 additions & 1 deletion experiment/telegram/telegram.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,16 @@ import (

const (
testName = "telegram"
testVersion = "0.0.3"
testVersion = "0.0.4"
)

// Config contains the experiment config.
type Config struct{}

// TestKeys contains telegram test keys.
type TestKeys struct {
Agent string `json:"agent"`
Queries oonidatamodel.DNSQueriesList `json:"queries"`
Requests oonidatamodel.RequestList `json:"requests"`
TCPConnect oonidatamodel.TCPConnectList `json:"tcp_connect"`
TelegramHTTPBlocking bool `json:"telegram_http_blocking"`
Expand Down Expand Up @@ -64,7 +66,11 @@ func (tk *TestKeys) processone(v *urlMeasurements) error {
if r == nil {
return errors.New("passed nil results")
}
tk.Agent = "redirect"
// update the requests and tcp-connect entries
tk.Queries = append(
tk.Queries, oonidatamodel.NewDNSQueriesList(r.TestKeys)...,
)
tk.Requests = append(
tk.Requests, oonidatamodel.NewRequestList(r)...,
)
Expand Down
78 changes: 78 additions & 0 deletions internal/oonidatamodel/oonidatamodel.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import (
"net"
"net/http"
"strconv"
"strings"
"unicode/utf8"

"github.com/ooni/netx/modelx"
"github.com/ooni/probe-engine/internal/oonitemplates"
)

Expand Down Expand Up @@ -210,3 +212,79 @@ func NewRequestList(httpresults *oonitemplates.HTTPDoResults) RequestList {
}
return out
}

// DNSAnswerEntry is the answer to a DNS query
type DNSAnswerEntry struct {
AnswerType string `json:"answer_type"`
Hostname string `json:"hostname,omitempty"`
IPv4 string `json:"ipv4,omitempty"`
IPv6 string `json:"ipv6,omitempty"`
TTL *uint32 `json:"ttl"`
}

// DNSQueryEntry is a DNS query with possibly an answer
type DNSQueryEntry struct {
Answers []DNSAnswerEntry `json:"answers"`
Engine string `json:"engine"`
Failure *string `json:"failure"`
Hostname string `json:"hostname"`
QueryType string `json:"query_type"`
ResolverHostname *string `json:"resolver_hostname"`
ResolverPort *string `json:"resolver_port"`
ResolverAddress string `json:"resolver_address"`
}

type (
// DNSQueriesList is a list of DNS queries
DNSQueriesList []DNSQueryEntry
dnsQueryType string
)

// NewDNSQueriesList returns a list of DNS queries.
func NewDNSQueriesList(results oonitemplates.Results) DNSQueriesList {
// TODO(bassosimone): add support for CNAME lookups.
var out DNSQueriesList
for _, resolve := range results.Resolves {
for _, qtype := range []dnsQueryType{"A", "AAAA"} {
entry := qtype.makequeryentry(resolve)
for _, addr := range resolve.Addresses {
if qtype.ipoftype(addr) {
entry.Answers = append(entry.Answers, qtype.makeanswerentry(addr))
}
}
out = append(out, entry)
}
}
return out
}

func (qtype dnsQueryType) ipoftype(addr string) bool {
switch qtype {
case "A":
return strings.Contains(addr, ":") == false
case "AAAA":
return strings.Contains(addr, ":") == true
}
return false
}

func (qtype dnsQueryType) makeanswerentry(addr string) DNSAnswerEntry {
answer := DNSAnswerEntry{AnswerType: string(qtype)}
switch qtype {
case "A":
answer.IPv4 = addr
case "AAAA":
answer.IPv6 = addr
}
return answer
}

func (qtype dnsQueryType) makequeryentry(resolve *modelx.ResolveDoneEvent) DNSQueryEntry {
return DNSQueryEntry{
Engine: resolve.TransportNetwork,
Failure: makeFailure(resolve.Error),
Hostname: resolve.Hostname,
QueryType: string(qtype),
ResolverAddress: resolve.TransportAddress,
}
}

0 comments on commit 41e218c

Please sign in to comment.