fix(measurex): use same keys of the OONI data format (#572)
This change should simplify the pipeline's job. Reference issue: https://github.com/ooni/probe/issues/1817. I previously dismissed this possibility, but now it seems clear it is simpler to have a very tabular data format internally and to convert such a format to OONI's data format when serializing. The OONI data format is what the pipeline expects, but processing is easier with a more linear/tabular format.
This commit is contained in:
parent
6f90d29bfa
commit
aa27bbe33f
|
@ -29,7 +29,7 @@ type Config struct{}
|
||||||
|
|
||||||
// TestKeys contains the experiment's test keys.
|
// TestKeys contains the experiment's test keys.
|
||||||
type TestKeys struct {
|
type TestKeys struct {
|
||||||
*measurex.URLMeasurement
|
*measurex.ArchivalURLMeasurement
|
||||||
}
|
}
|
||||||
|
|
||||||
// Measurer performs the measurement.
|
// Measurer performs the measurement.
|
||||||
|
@ -142,7 +142,9 @@ func (mx *Measurer) runAsync(ctx context.Context, sess model.ExperimentSession,
|
||||||
},
|
},
|
||||||
Input: model.MeasurementTarget(m.URL),
|
Input: model.MeasurementTarget(m.URL),
|
||||||
MeasurementRuntime: m.TotalRuntime.Seconds(),
|
MeasurementRuntime: m.TotalRuntime.Seconds(),
|
||||||
TestKeys: &TestKeys{URLMeasurement: m},
|
TestKeys: &TestKeys{
|
||||||
|
ArchivalURLMeasurement: measurex.NewArchivalURLMeasurement(m),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,7 +165,7 @@ type measurerMeasureURLHelper struct {
|
||||||
func (mth *measurerMeasureURLHelper) LookupExtraHTTPEndpoints(
|
func (mth *measurerMeasureURLHelper) LookupExtraHTTPEndpoints(
|
||||||
ctx context.Context, URL *url.URL, headers http.Header,
|
ctx context.Context, URL *url.URL, headers http.Header,
|
||||||
curEndpoints ...*measurex.HTTPEndpoint) (
|
curEndpoints ...*measurex.HTTPEndpoint) (
|
||||||
[]*measurex.HTTPEndpoint, interface{}, error) {
|
[]*measurex.HTTPEndpoint, *measurex.THMeasurement, error) {
|
||||||
cc := &THClientCall{
|
cc := &THClientCall{
|
||||||
Endpoints: measurex.HTTPEndpointsToEndpoints(curEndpoints),
|
Endpoints: measurex.HTTPEndpointsToEndpoints(curEndpoints),
|
||||||
HTTPClient: mth.Clnt,
|
HTTPClient: mth.Clnt,
|
||||||
|
|
|
@ -41,14 +41,7 @@ type THClientRequest struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// THServerResponse is the response from the test helper.
|
// THServerResponse is the response from the test helper.
|
||||||
type THServerResponse struct {
|
type THServerResponse = measurex.THMeasurement
|
||||||
// DNS contains all the DNS related measurements.
|
|
||||||
DNS []*measurex.DNSMeasurement `json:"dns"`
|
|
||||||
|
|
||||||
// Endpoints contains a measurement for each endpoint
|
|
||||||
// that was discovered by the probe or the TH.
|
|
||||||
Endpoints []*measurex.HTTPEndpointMeasurement `json:"endpoints"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// thMaxAcceptableBodySize is the maximum acceptable body size by TH code.
|
// thMaxAcceptableBodySize is the maximum acceptable body size by TH code.
|
||||||
const thMaxAcceptableBodySize = 1 << 20
|
const thMaxAcceptableBodySize = 1 << 20
|
||||||
|
@ -294,9 +287,9 @@ func (h *THHandler) simplifyMeasurement(in *measurex.Measurement) (out *measurex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *THHandler) simplifyHandshake(
|
func (h *THHandler) simplifyHandshake(
|
||||||
in []*measurex.TLSHandshakeEvent) (out []*measurex.TLSHandshakeEvent) {
|
in []*measurex.QUICTLSHandshakeEvent) (out []*measurex.QUICTLSHandshakeEvent) {
|
||||||
for _, ev := range in {
|
for _, ev := range in {
|
||||||
out = append(out, &measurex.TLSHandshakeEvent{
|
out = append(out, &measurex.QUICTLSHandshakeEvent{
|
||||||
CipherSuite: ev.CipherSuite,
|
CipherSuite: ev.CipherSuite,
|
||||||
Failure: ev.Failure,
|
Failure: ev.Failure,
|
||||||
NegotiatedProto: ev.NegotiatedProto,
|
NegotiatedProto: ev.NegotiatedProto,
|
||||||
|
@ -319,32 +312,24 @@ func (h *THHandler) simplifyHTTPRoundTrip(
|
||||||
in []*measurex.HTTPRoundTripEvent) (out []*measurex.HTTPRoundTripEvent) {
|
in []*measurex.HTTPRoundTripEvent) (out []*measurex.HTTPRoundTripEvent) {
|
||||||
for _, ev := range in {
|
for _, ev := range in {
|
||||||
out = append(out, &measurex.HTTPRoundTripEvent{
|
out = append(out, &measurex.HTTPRoundTripEvent{
|
||||||
Failure: ev.Failure,
|
Failure: ev.Failure,
|
||||||
Request: ev.Request,
|
Method: ev.Method,
|
||||||
Response: h.simplifyHTTPResponse(ev.Response),
|
URL: ev.URL,
|
||||||
Finished: 0,
|
RequestHeaders: ev.RequestHeaders,
|
||||||
Started: 0,
|
StatusCode: ev.StatusCode,
|
||||||
Oddity: ev.Oddity,
|
ResponseHeaders: ev.ResponseHeaders,
|
||||||
|
ResponseBody: nil, // we don't transfer the body
|
||||||
|
ResponseBodyLength: ev.ResponseBodyLength,
|
||||||
|
ResponseBodyIsTruncated: ev.ResponseBodyIsTruncated,
|
||||||
|
ResponseBodyIsUTF8: ev.ResponseBodyIsUTF8,
|
||||||
|
Finished: ev.Finished,
|
||||||
|
Started: ev.Started,
|
||||||
|
Oddity: ev.Oddity,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *THHandler) simplifyHTTPResponse(
|
|
||||||
in *measurex.HTTPResponse) (out *measurex.HTTPResponse) {
|
|
||||||
if in != nil {
|
|
||||||
out = &measurex.HTTPResponse{
|
|
||||||
Code: in.Code,
|
|
||||||
Headers: in.Headers,
|
|
||||||
Body: nil,
|
|
||||||
BodyIsTruncated: in.BodyIsTruncated,
|
|
||||||
BodyLength: in.BodyLength,
|
|
||||||
BodyIsUTF8: in.BodyIsUTF8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type thMeasureURLHelper struct {
|
type thMeasureURLHelper struct {
|
||||||
epnts []*measurex.Endpoint
|
epnts []*measurex.Endpoint
|
||||||
}
|
}
|
||||||
|
@ -352,7 +337,7 @@ type thMeasureURLHelper struct {
|
||||||
func (thh *thMeasureURLHelper) LookupExtraHTTPEndpoints(
|
func (thh *thMeasureURLHelper) LookupExtraHTTPEndpoints(
|
||||||
ctx context.Context, URL *url.URL, headers http.Header,
|
ctx context.Context, URL *url.URL, headers http.Header,
|
||||||
serverEpnts ...*measurex.HTTPEndpoint) (
|
serverEpnts ...*measurex.HTTPEndpoint) (
|
||||||
epnts []*measurex.HTTPEndpoint, thMeaurement interface{}, err error) {
|
epnts []*measurex.HTTPEndpoint, thMeaurement *measurex.THMeasurement, err error) {
|
||||||
for _, epnt := range thh.epnts {
|
for _, epnt := range thh.epnts {
|
||||||
epnts = append(epnts, &measurex.HTTPEndpoint{
|
epnts = append(epnts, &measurex.HTTPEndpoint{
|
||||||
Domain: URL.Hostname(),
|
Domain: URL.Hostname(),
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
package measurex
|
package measurex
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -11,6 +14,10 @@ import (
|
||||||
// This file defines helpers to serialize to the OONI data format.
|
// This file defines helpers to serialize to the OONI data format.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
//
|
||||||
|
// BinaryData
|
||||||
|
//
|
||||||
|
|
||||||
// ArchivalBinaryData is the archival format for binary data.
|
// ArchivalBinaryData is the archival format for binary data.
|
||||||
type ArchivalBinaryData struct {
|
type ArchivalBinaryData struct {
|
||||||
Data []byte `json:"data"`
|
Data []byte `json:"data"`
|
||||||
|
@ -29,6 +36,130 @@ func NewArchivalBinaryData(data []byte) (out *ArchivalBinaryData) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// NetworkEvent
|
||||||
|
//
|
||||||
|
|
||||||
|
// ArchivalNetworkEvent is the OONI data format representation
|
||||||
|
// of a network event according to df-008-netevents.
|
||||||
|
type ArchivalNetworkEvent struct {
|
||||||
|
// JSON names compatible with df-008-netevents
|
||||||
|
RemoteAddr string `json:"address"`
|
||||||
|
Failure *string `json:"failure"`
|
||||||
|
Count int `json:"num_bytes,omitempty"`
|
||||||
|
Operation string `json:"operation"`
|
||||||
|
Network string `json:"proto"`
|
||||||
|
Finished float64 `json:"t"`
|
||||||
|
Started float64 `json:"started"`
|
||||||
|
|
||||||
|
// Names that are not part of the spec.
|
||||||
|
Oddity Oddity `json:"oddity"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewArchivalNetworkEvent converts a network event to its archival format.
|
||||||
|
func NewArchivalNetworkEvent(in *NetworkEvent) *ArchivalNetworkEvent {
|
||||||
|
return &ArchivalNetworkEvent{
|
||||||
|
RemoteAddr: in.RemoteAddr,
|
||||||
|
Failure: in.Failure,
|
||||||
|
Count: in.Count,
|
||||||
|
Operation: in.Operation,
|
||||||
|
Network: in.Network,
|
||||||
|
Finished: in.Finished,
|
||||||
|
Started: in.Started,
|
||||||
|
Oddity: in.Oddity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewArchivalNetworkEventList converts a list of NetworkEvent
|
||||||
|
// to a list of ArchivalNetworkEvent.
|
||||||
|
func NewArchivalNetworkEventList(in []*NetworkEvent) (out []*ArchivalNetworkEvent) {
|
||||||
|
for _, ev := range in {
|
||||||
|
out = append(out, NewArchivalNetworkEvent(ev))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// DNSRoundTripEvent
|
||||||
|
//
|
||||||
|
|
||||||
|
// ArchivalDNSRoundTripEvent is the OONI data format representation
|
||||||
|
// of a DNS round trip, which is currently not specified.
|
||||||
|
//
|
||||||
|
// We are trying to use names compatible with the names currently
|
||||||
|
// used by other specifications we currently use.
|
||||||
|
type ArchivalDNSRoundTripEvent struct {
|
||||||
|
Network string `json:"engine"`
|
||||||
|
Address string `json:"resolver_address"`
|
||||||
|
Query *ArchivalBinaryData `json:"raw_query"`
|
||||||
|
Started float64 `json:"started"`
|
||||||
|
Finished float64 `json:"t"`
|
||||||
|
Failure *string `json:"failure"`
|
||||||
|
Reply *ArchivalBinaryData `json:"raw_reply"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewArchivalDNSRoundTripEvent converts a DNSRoundTripEvent into is archival format.
|
||||||
|
func NewArchivalDNSRoundTripEvent(in *DNSRoundTripEvent) *ArchivalDNSRoundTripEvent {
|
||||||
|
return &ArchivalDNSRoundTripEvent{
|
||||||
|
Network: in.Network,
|
||||||
|
Address: in.Address,
|
||||||
|
Query: NewArchivalBinaryData(in.Query),
|
||||||
|
Started: in.Started,
|
||||||
|
Finished: in.Finished,
|
||||||
|
Failure: in.Failure,
|
||||||
|
Reply: NewArchivalBinaryData(in.Reply),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewArchivalDNSRoundTripEventList converts a DNSRoundTripEvent
|
||||||
|
// list to the corresponding archival format.
|
||||||
|
func NewArchivalDNSRoundTripEventList(in []*DNSRoundTripEvent) (out []*ArchivalDNSRoundTripEvent) {
|
||||||
|
for _, ev := range in {
|
||||||
|
out = append(out, NewArchivalDNSRoundTripEvent(ev))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// HTTPRoundTrip
|
||||||
|
//
|
||||||
|
|
||||||
|
// ArchivalHTTPRequest is the archival format of an HTTP
|
||||||
|
// request according to df-001-http.md.
|
||||||
|
type ArchivalHTTPRequest struct {
|
||||||
|
Method string `json:"method"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Headers ArchivalHeaders `json:"headers"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArchivalHTTPResponse is the archival format of an HTTP
|
||||||
|
// response according to df-001-http.md.
|
||||||
|
type ArchivalHTTPResponse struct {
|
||||||
|
// Names consistent with df-001-http.md
|
||||||
|
Code int64 `json:"code"`
|
||||||
|
Headers ArchivalHeaders `json:"headers"`
|
||||||
|
Body *ArchivalBinaryData `json:"body"`
|
||||||
|
BodyIsTruncated bool `json:"body_is_truncated"`
|
||||||
|
|
||||||
|
// Fields not part of the spec
|
||||||
|
BodyLength int64 `json:"x_body_length"`
|
||||||
|
BodyIsUTF8 bool `json:"x_body_is_utf8"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArchivalHTTPRoundTripEvent is the archival format of an
|
||||||
|
// HTTP response according to df-001-http.md.
|
||||||
|
type ArchivalHTTPRoundTripEvent struct {
|
||||||
|
// JSON names following the df-001-httpt data format.
|
||||||
|
Failure *string `json:"failure"`
|
||||||
|
Request *HTTPRequest `json:"request"`
|
||||||
|
Response *HTTPResponse `json:"response"`
|
||||||
|
Finished float64 `json:"t"`
|
||||||
|
Started float64 `json:"started"`
|
||||||
|
|
||||||
|
// Names not in the specification
|
||||||
|
Oddity Oddity `json:"oddity"`
|
||||||
|
}
|
||||||
|
|
||||||
// ArchivalHeaders is a list of HTTP headers.
|
// ArchivalHeaders is a list of HTTP headers.
|
||||||
type ArchivalHeaders map[string]string
|
type ArchivalHeaders map[string]string
|
||||||
|
|
||||||
|
@ -54,6 +185,64 @@ func NewArchivalHeaders(in http.Header) (out ArchivalHeaders) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewArchivalHTTPRoundTripEvent converts an HTTPRoundTrip to its archival format.
|
||||||
|
func NewArchivalHTTPRoundTripEvent(in *HTTPRoundTripEvent) *ArchivalHTTPRoundTripEvent {
|
||||||
|
return &ArchivalHTTPRoundTripEvent{
|
||||||
|
Failure: in.Failure,
|
||||||
|
Request: &HTTPRequest{
|
||||||
|
Method: in.Method,
|
||||||
|
URL: in.URL,
|
||||||
|
Headers: NewArchivalHeaders(in.RequestHeaders),
|
||||||
|
},
|
||||||
|
Response: &HTTPResponse{
|
||||||
|
Code: in.StatusCode,
|
||||||
|
Headers: NewArchivalHeaders(in.ResponseHeaders),
|
||||||
|
Body: NewArchivalBinaryData(in.ResponseBody),
|
||||||
|
BodyLength: in.ResponseBodyLength,
|
||||||
|
BodyIsTruncated: in.ResponseBodyIsTruncated,
|
||||||
|
BodyIsUTF8: in.ResponseBodyIsUTF8,
|
||||||
|
},
|
||||||
|
Finished: in.Finished,
|
||||||
|
Started: in.Started,
|
||||||
|
Oddity: in.Oddity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewArchivalHTTPRoundTripEventList converts a list of
|
||||||
|
// HTTPRoundTripEvent to a list of ArchivalRoundTripEvent.
|
||||||
|
func NewArchivalHTTPRoundTripEventList(in []*HTTPRoundTripEvent) (out []*ArchivalHTTPRoundTripEvent) {
|
||||||
|
for _, ev := range in {
|
||||||
|
out = append(out, NewArchivalHTTPRoundTripEvent(ev))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// QUICTLSHandshakeEvent
|
||||||
|
//
|
||||||
|
|
||||||
|
// ArchivalQUICTLSHandshakeEvent is the archival data format for a
|
||||||
|
// QUIC or TLS handshake event according to df-006-tlshandshake.
|
||||||
|
type ArchivalQUICTLSHandshakeEvent struct {
|
||||||
|
// JSON names compatible with df-006-tlshandshake
|
||||||
|
CipherSuite string `json:"cipher_suite"`
|
||||||
|
Failure *string `json:"failure"`
|
||||||
|
NegotiatedProto string `json:"negotiated_proto"`
|
||||||
|
TLSVersion string `json:"tls_version"`
|
||||||
|
PeerCerts []*ArchivalBinaryData `json:"peer_certificates"`
|
||||||
|
Finished float64 `json:"t"`
|
||||||
|
|
||||||
|
// JSON names that are consistent with the
|
||||||
|
// spirit of the spec but are not in it
|
||||||
|
RemoteAddr string `json:"address"`
|
||||||
|
SNI string `json:"server_name"` // used in prod
|
||||||
|
ALPN []string `json:"alpn"`
|
||||||
|
SkipVerify bool `json:"no_tls_verify"` // used in prod
|
||||||
|
Oddity Oddity `json:"oddity"`
|
||||||
|
Network string `json:"proto"`
|
||||||
|
Started float64 `json:"started"`
|
||||||
|
}
|
||||||
|
|
||||||
// NewArchivalTLSCertList builds a new []ArchivalBinaryData
|
// NewArchivalTLSCertList builds a new []ArchivalBinaryData
|
||||||
// from a list of raw x509 certificates data.
|
// from a list of raw x509 certificates data.
|
||||||
func NewArchivalTLSCerts(in [][]byte) (out []*ArchivalBinaryData) {
|
func NewArchivalTLSCerts(in [][]byte) (out []*ArchivalBinaryData) {
|
||||||
|
@ -66,13 +255,341 @@ func NewArchivalTLSCerts(in [][]byte) (out []*ArchivalBinaryData) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewArchivalFailure creates an archival failure from an error. We
|
// NewArchivalQUICTLSHandshakeEvent converts a QUICTLSHandshakeEvent
|
||||||
// cannot round trip an error using JSON, so we serialize to this
|
// to its archival data format.
|
||||||
// intermediate format that is a sort of Optional<string>.
|
func NewArchivalQUICTLSHandshakeEvent(in *QUICTLSHandshakeEvent) *ArchivalQUICTLSHandshakeEvent {
|
||||||
func NewArchivalFailure(err error) *string {
|
return &ArchivalQUICTLSHandshakeEvent{
|
||||||
if err == nil {
|
CipherSuite: in.CipherSuite,
|
||||||
return nil
|
Failure: in.Failure,
|
||||||
|
NegotiatedProto: in.NegotiatedProto,
|
||||||
|
TLSVersion: in.TLSVersion,
|
||||||
|
PeerCerts: NewArchivalTLSCerts(in.PeerCerts),
|
||||||
|
Finished: in.Finished,
|
||||||
|
RemoteAddr: in.RemoteAddr,
|
||||||
|
SNI: in.SNI,
|
||||||
|
ALPN: in.ALPN,
|
||||||
|
SkipVerify: in.SkipVerify,
|
||||||
|
Oddity: in.Oddity,
|
||||||
|
Network: in.Network,
|
||||||
|
Started: in.Started,
|
||||||
}
|
}
|
||||||
s := err.Error()
|
}
|
||||||
return &s
|
|
||||||
|
// NewArchivalQUICTLSHandshakeEventList converts a list of
|
||||||
|
// QUICTLSHandshakeEvent to a list of ArchivalQUICTLSHandshakeEvent.
|
||||||
|
func NewArchivalQUICTLSHandshakeEventList(in []*QUICTLSHandshakeEvent) (out []*ArchivalQUICTLSHandshakeEvent) {
|
||||||
|
for _, ev := range in {
|
||||||
|
out = append(out, NewArchivalQUICTLSHandshakeEvent(ev))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// DNSLookup
|
||||||
|
//
|
||||||
|
|
||||||
|
// ArchivalDNSLookupAnswer is the archival format of a
|
||||||
|
// DNS lookup answer according to df-002-dnst.
|
||||||
|
type ArchivalDNSLookupAnswer struct {
|
||||||
|
// JSON names compatible with df-002-dnst's spec
|
||||||
|
Type string `json:"answer_type"`
|
||||||
|
IPv4 string `json:"ipv4,omitempty"`
|
||||||
|
IPv6 string `json:"ivp6,omitempty"`
|
||||||
|
|
||||||
|
// Names not part of the spec.
|
||||||
|
ALPN string `json:"alpn,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArchivalDNSLookupEvent is the archival data format
|
||||||
|
// of a DNS lookup according to df-002-dnst.
|
||||||
|
type ArchivalDNSLookupEvent struct {
|
||||||
|
// fields inside df-002-dnst
|
||||||
|
Answers []ArchivalDNSLookupAnswer `json:"answers"`
|
||||||
|
Network string `json:"engine"`
|
||||||
|
Failure *string `json:"failure"`
|
||||||
|
Domain string `json:"hostname"`
|
||||||
|
QueryType string `json:"query_type"`
|
||||||
|
Address string `json:"resolver_address"`
|
||||||
|
Finished float64 `json:"t"`
|
||||||
|
|
||||||
|
// Names not part of the spec.
|
||||||
|
Started float64 `json:"started"`
|
||||||
|
Oddity Oddity `json:"oddity"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewArchivalDNSLookupAnswers creates a list of ArchivalDNSLookupAnswer.
|
||||||
|
func NewArchivalDNSLookupAnswers(in *DNSLookupEvent) (out []ArchivalDNSLookupAnswer) {
|
||||||
|
for _, ip := range in.A {
|
||||||
|
out = append(out, ArchivalDNSLookupAnswer{
|
||||||
|
Type: "A",
|
||||||
|
IPv4: ip,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, ip := range in.AAAA {
|
||||||
|
out = append(out, ArchivalDNSLookupAnswer{
|
||||||
|
Type: "AAAA",
|
||||||
|
IPv6: ip,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, alpn := range in.ALPN {
|
||||||
|
out = append(out, ArchivalDNSLookupAnswer{
|
||||||
|
Type: "ALPN",
|
||||||
|
ALPN: alpn,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewArchivalDNSLookupEvent converts a DNSLookupEvent
|
||||||
|
// to its archival representation.
|
||||||
|
func NewArchivalDNSLookupEvent(in *DNSLookupEvent) *ArchivalDNSLookupEvent {
|
||||||
|
return &ArchivalDNSLookupEvent{
|
||||||
|
Answers: NewArchivalDNSLookupAnswers(in),
|
||||||
|
Network: in.Network,
|
||||||
|
Failure: in.Failure,
|
||||||
|
Domain: in.Domain,
|
||||||
|
QueryType: in.QueryType,
|
||||||
|
Address: in.Address,
|
||||||
|
Finished: in.Finished,
|
||||||
|
Started: in.Started,
|
||||||
|
Oddity: in.Oddity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewArchivalDNSLookupEventList converts a list of DNSLookupEvent
|
||||||
|
// to a list of ArchivalDNSLookupEvent.
|
||||||
|
func NewArchivalDNSLookupEventList(in []*DNSLookupEvent) (out []*ArchivalDNSLookupEvent) {
|
||||||
|
for _, ev := range in {
|
||||||
|
out = append(out, NewArchivalDNSLookupEvent(ev))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// TCPConnect
|
||||||
|
//
|
||||||
|
|
||||||
|
// ArchivalTCPConnect is the archival form of TCP connect
|
||||||
|
// events in compliance with df-005-tcpconnect.
|
||||||
|
type ArchivalTCPConnect struct {
|
||||||
|
// Names part of the spec.
|
||||||
|
IP string `json:"ip"`
|
||||||
|
Port int64 `json:"port"`
|
||||||
|
Finished float64 `json:"t"`
|
||||||
|
Status *ArchivalTCPConnectStatus `json:"status"`
|
||||||
|
|
||||||
|
// Names not part of the spec.
|
||||||
|
Started float64 `json:"started"`
|
||||||
|
Oddity Oddity `json:"oddity"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArchivalTCPConnectStatus contains the status of a TCP connect.
|
||||||
|
type ArchivalTCPConnectStatus struct {
|
||||||
|
Blocked bool `json:"blocked"`
|
||||||
|
Failure *string `json:"failure"`
|
||||||
|
Success bool `json:"success"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewArchivalTCPConnect converts a NetworkEvent to an ArchivalTCPConnect.
|
||||||
|
func NewArchivalTCPConnect(in *NetworkEvent) *ArchivalTCPConnect {
|
||||||
|
// We ignore errors because values come from Go code that
|
||||||
|
// emits correct serialization of TCP/UDP addresses.
|
||||||
|
addr, port, _ := net.SplitHostPort(in.RemoteAddr)
|
||||||
|
portnum, _ := strconv.Atoi(port)
|
||||||
|
return &ArchivalTCPConnect{
|
||||||
|
IP: addr,
|
||||||
|
Port: int64(portnum),
|
||||||
|
Finished: in.Finished,
|
||||||
|
Status: &ArchivalTCPConnectStatus{
|
||||||
|
Blocked: in.Failure != nil,
|
||||||
|
Failure: in.Failure,
|
||||||
|
Success: in.Failure == nil,
|
||||||
|
},
|
||||||
|
Started: in.Started,
|
||||||
|
Oddity: in.Oddity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewArchivalTCPConnectList converts a list of NetworkEvent
|
||||||
|
// to a list of ArchivalTCPConnect. In doing that, the code
|
||||||
|
// only considers "connect" events using the TCP protocol.
|
||||||
|
func NewArchivalTCPConnectList(in []*NetworkEvent) (out []*ArchivalTCPConnect) {
|
||||||
|
for _, ev := range in {
|
||||||
|
if ev.Operation != "connect" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch ev.Network {
|
||||||
|
case "tcp", "tcp4", "tcp6":
|
||||||
|
out = append(out, NewArchivalTCPConnect(ev))
|
||||||
|
default:
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// URLMeasurement
|
||||||
|
//
|
||||||
|
|
||||||
|
// ArchivalURLMeasurement is the archival representation of URLMeasurement
|
||||||
|
type ArchivalURLMeasurement struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
DNS []*ArchivalDNSMeasurement `json:"dns"`
|
||||||
|
Endpoints []*ArchivalHTTPEndpointMeasurement `json:"endpoints"`
|
||||||
|
TH *ArchivalTHMeasurement `json:"th"`
|
||||||
|
TotalRuntime time.Duration `json:"x_total_runtime"`
|
||||||
|
DNSRuntime time.Duration `json:"x_dns_runtime"`
|
||||||
|
THRuntime time.Duration `json:"x_th_runtime"`
|
||||||
|
EpntsRuntime time.Duration `json:"x_epnts_runtime"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewArchivalURLMeasurement creates the archival representation
|
||||||
|
// of an URLMeasurement data structure.
|
||||||
|
func NewArchivalURLMeasurement(in *URLMeasurement) *ArchivalURLMeasurement {
|
||||||
|
return &ArchivalURLMeasurement{
|
||||||
|
URL: in.URL,
|
||||||
|
DNS: NewArchivalDNSMeasurementList(in.DNS),
|
||||||
|
Endpoints: NewArchivalHTTPEndpointMeasurementList(in.Endpoints),
|
||||||
|
TH: NewArchivalTHMeasurement(in.TH),
|
||||||
|
TotalRuntime: in.TotalRuntime,
|
||||||
|
DNSRuntime: in.DNSRuntime,
|
||||||
|
THRuntime: in.THRuntime,
|
||||||
|
EpntsRuntime: in.EpntsRuntime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// EndpointMeasurement
|
||||||
|
//
|
||||||
|
|
||||||
|
// ArchivalEndpointMeasurement is the archival representation of EndpointMeasurement.
|
||||||
|
type ArchivalEndpointMeasurement struct {
|
||||||
|
// Network is the network of this endpoint.
|
||||||
|
Network EndpointNetwork `json:"network"`
|
||||||
|
|
||||||
|
// Address is the address of this endpoint.
|
||||||
|
Address string `json:"address"`
|
||||||
|
|
||||||
|
// An EndpointMeasurement is a Measurement.
|
||||||
|
*ArchivalMeasurement
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewArchivalEndpointMeasurement converts an EndpointMeasurement
|
||||||
|
// to the corresponding archival data format.
|
||||||
|
func NewArchivalEndpointMeasurement(in *EndpointMeasurement) *ArchivalEndpointMeasurement {
|
||||||
|
return &ArchivalEndpointMeasurement{
|
||||||
|
Network: in.Network,
|
||||||
|
Address: in.Address,
|
||||||
|
ArchivalMeasurement: NewArchivalMeasurement(in.Measurement),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// THMeasurement
|
||||||
|
//
|
||||||
|
|
||||||
|
// ArchivalTHMeasurement is the archival representation of THMeasurement.
|
||||||
|
type ArchivalTHMeasurement struct {
|
||||||
|
DNS []*ArchivalDNSMeasurement `json:"dns"`
|
||||||
|
Endpoints []*ArchivalHTTPEndpointMeasurement `json:"endpoints"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewArchivalTHMeasurement creates the archival representation of THMeasurement.
|
||||||
|
func NewArchivalTHMeasurement(in *THMeasurement) *ArchivalTHMeasurement {
|
||||||
|
return &ArchivalTHMeasurement{
|
||||||
|
DNS: NewArchivalDNSMeasurementList(in.DNS),
|
||||||
|
Endpoints: NewArchivalHTTPEndpointMeasurementList(in.Endpoints),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// DNSMeasurement
|
||||||
|
//
|
||||||
|
|
||||||
|
// ArchivalDNSMeasurement is the archival representation of DNSMeasurement.
|
||||||
|
type ArchivalDNSMeasurement struct {
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
*ArchivalMeasurement
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewArchivalDNSMeasurement converts a DNSMeasurement to an ArchivalDNSMeasurement.
|
||||||
|
func NewArchivalDNSMeasurement(in *DNSMeasurement) *ArchivalDNSMeasurement {
|
||||||
|
return &ArchivalDNSMeasurement{
|
||||||
|
Domain: in.Domain,
|
||||||
|
ArchivalMeasurement: NewArchivalMeasurement(in.Measurement),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewArchivalDNSMeasurementList converts a list of DNSMeasurement
|
||||||
|
// to a list of ArchivalDNSMeasurement.
|
||||||
|
func NewArchivalDNSMeasurementList(in []*DNSMeasurement) (out []*ArchivalDNSMeasurement) {
|
||||||
|
for _, m := range in {
|
||||||
|
out = append(out, NewArchivalDNSMeasurement(m))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// HTTPEndpointMeasurement
|
||||||
|
//
|
||||||
|
|
||||||
|
// ArchivalHTTPEndpointMeasurement is the archival representation
|
||||||
|
// of an HTTPEndpointMeasurement.
|
||||||
|
type ArchivalHTTPEndpointMeasurement struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
Network EndpointNetwork `json:"network"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
*ArchivalMeasurement
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewArchivalHTTPEndpointMeasurement converts an HTTPEndpointMeasurement
|
||||||
|
// to an ArchivalHTTPEndpointMeasurement.
|
||||||
|
func NewArchivalHTTPEndpointMeasurement(in *HTTPEndpointMeasurement) *ArchivalHTTPEndpointMeasurement {
|
||||||
|
return &ArchivalHTTPEndpointMeasurement{
|
||||||
|
URL: in.URL,
|
||||||
|
Network: in.Network,
|
||||||
|
Address: in.Address,
|
||||||
|
ArchivalMeasurement: NewArchivalMeasurement(in.Measurement),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewArchivalHTTPEndpointMeasurementList converts a list of HTTPEndpointMeasurement
|
||||||
|
// to a list of ArchivalHTTPEndpointMeasurement.
|
||||||
|
func NewArchivalHTTPEndpointMeasurementList(in []*HTTPEndpointMeasurement) (out []*ArchivalHTTPEndpointMeasurement) {
|
||||||
|
for _, m := range in {
|
||||||
|
out = append(out, NewArchivalHTTPEndpointMeasurement(m))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Measurement
|
||||||
|
//
|
||||||
|
|
||||||
|
// ArchivalMeasurement is the archival representation of a Measurement.
|
||||||
|
type ArchivalMeasurement struct {
|
||||||
|
NetworkEvents []*ArchivalNetworkEvent `json:"network_events,omitempty"`
|
||||||
|
DNSEvents []*ArchivalDNSRoundTripEvent `json:"dns_events,omitempty"`
|
||||||
|
Queries []*ArchivalDNSLookupEvent `json:"queries,omitempty"`
|
||||||
|
TCPConnect []*ArchivalTCPConnect `json:"tcp_connect,omitempty"`
|
||||||
|
TLSHandshakes []*ArchivalQUICTLSHandshakeEvent `json:"tls_handshakes,omitempty"`
|
||||||
|
QUICHandshakes []*ArchivalQUICTLSHandshakeEvent `json:"quic_handshakes,omitempty"`
|
||||||
|
Requests []*ArchivalHTTPRoundTripEvent `json:"requests,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewArchivalMeasurement converts a Measurement to ArchivalMeasurement.
|
||||||
|
func NewArchivalMeasurement(in *Measurement) *ArchivalMeasurement {
|
||||||
|
out := &ArchivalMeasurement{
|
||||||
|
NetworkEvents: NewArchivalNetworkEventList(in.ReadWrite),
|
||||||
|
DNSEvents: NewArchivalDNSRoundTripEventList(in.DNSRoundTrip),
|
||||||
|
Queries: nil, // done below
|
||||||
|
TCPConnect: NewArchivalTCPConnectList(in.Connect),
|
||||||
|
TLSHandshakes: NewArchivalQUICTLSHandshakeEventList(in.TLSHandshake),
|
||||||
|
QUICHandshakes: NewArchivalQUICTLSHandshakeEventList(in.QUICHandshake),
|
||||||
|
Requests: NewArchivalHTTPRoundTripEventList(in.HTTPRoundTrip),
|
||||||
|
}
|
||||||
|
out.Queries = append(out.Queries, NewArchivalDNSLookupEventList(in.LookupHost)...)
|
||||||
|
out.Queries = append(out.Queries, NewArchivalDNSLookupEventList(in.LookupHTTPSSvc)...)
|
||||||
|
return out
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ type WritableDB interface {
|
||||||
InsertIntoClose(ev *NetworkEvent)
|
InsertIntoClose(ev *NetworkEvent)
|
||||||
|
|
||||||
// InsertIntoTLSHandshake saves a TLS handshake event.
|
// InsertIntoTLSHandshake saves a TLS handshake event.
|
||||||
InsertIntoTLSHandshake(ev *TLSHandshakeEvent)
|
InsertIntoTLSHandshake(ev *QUICTLSHandshakeEvent)
|
||||||
|
|
||||||
// InsertIntoLookupHost saves a lookup host event.
|
// InsertIntoLookupHost saves a lookup host event.
|
||||||
InsertIntoLookupHost(ev *DNSLookupEvent)
|
InsertIntoLookupHost(ev *DNSLookupEvent)
|
||||||
|
@ -46,7 +46,7 @@ type WritableDB interface {
|
||||||
InsertIntoHTTPRedirect(ev *HTTPRedirectEvent)
|
InsertIntoHTTPRedirect(ev *HTTPRedirectEvent)
|
||||||
|
|
||||||
// InsertIntoQUICHandshake saves a QUIC handshake event.
|
// InsertIntoQUICHandshake saves a QUIC handshake event.
|
||||||
InsertIntoQUICHandshake(ev *QUICHandshakeEvent)
|
InsertIntoQUICHandshake(ev *QUICTLSHandshakeEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MeasurementDB is a WritableDB that also allows high-level code
|
// MeasurementDB is a WritableDB that also allows high-level code
|
||||||
|
@ -56,13 +56,13 @@ type MeasurementDB struct {
|
||||||
dialTable []*NetworkEvent
|
dialTable []*NetworkEvent
|
||||||
readWriteTable []*NetworkEvent
|
readWriteTable []*NetworkEvent
|
||||||
closeTable []*NetworkEvent
|
closeTable []*NetworkEvent
|
||||||
tlsHandshakeTable []*TLSHandshakeEvent
|
tlsHandshakeTable []*QUICTLSHandshakeEvent
|
||||||
lookupHostTable []*DNSLookupEvent
|
lookupHostTable []*DNSLookupEvent
|
||||||
lookupHTTPSvcTable []*DNSLookupEvent
|
lookupHTTPSvcTable []*DNSLookupEvent
|
||||||
dnsRoundTripTable []*DNSRoundTripEvent
|
dnsRoundTripTable []*DNSRoundTripEvent
|
||||||
httpRoundTripTable []*HTTPRoundTripEvent
|
httpRoundTripTable []*HTTPRoundTripEvent
|
||||||
httpRedirectTable []*HTTPRedirectEvent
|
httpRedirectTable []*HTTPRedirectEvent
|
||||||
quicHandshakeTable []*QUICHandshakeEvent
|
quicHandshakeTable []*QUICTLSHandshakeEvent
|
||||||
|
|
||||||
// mu protects all the fields
|
// mu protects all the fields
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
|
@ -126,14 +126,14 @@ func (db *MeasurementDB) selectAllFromCloseUnlocked() (out []*NetworkEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// InsertIntoTLSHandshake implements EventDB.InsertIntoTLSHandshake.
|
// InsertIntoTLSHandshake implements EventDB.InsertIntoTLSHandshake.
|
||||||
func (db *MeasurementDB) InsertIntoTLSHandshake(ev *TLSHandshakeEvent) {
|
func (db *MeasurementDB) InsertIntoTLSHandshake(ev *QUICTLSHandshakeEvent) {
|
||||||
db.mu.Lock()
|
db.mu.Lock()
|
||||||
db.tlsHandshakeTable = append(db.tlsHandshakeTable, ev)
|
db.tlsHandshakeTable = append(db.tlsHandshakeTable, ev)
|
||||||
db.mu.Unlock()
|
db.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// selectAllFromTLSHandshakeUnlocked returns all TLS handshake events.
|
// selectAllFromTLSHandshakeUnlocked returns all TLS handshake events.
|
||||||
func (db *MeasurementDB) selectAllFromTLSHandshakeUnlocked() (out []*TLSHandshakeEvent) {
|
func (db *MeasurementDB) selectAllFromTLSHandshakeUnlocked() (out []*QUICTLSHandshakeEvent) {
|
||||||
out = append(out, db.tlsHandshakeTable...)
|
out = append(out, db.tlsHandshakeTable...)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -204,14 +204,14 @@ func (db *MeasurementDB) selectAllFromHTTPRedirectUnlocked() (out []*HTTPRedirec
|
||||||
}
|
}
|
||||||
|
|
||||||
// InsertIntoQUICHandshake implements EventDB.InsertIntoQUICHandshake.
|
// InsertIntoQUICHandshake implements EventDB.InsertIntoQUICHandshake.
|
||||||
func (db *MeasurementDB) InsertIntoQUICHandshake(ev *QUICHandshakeEvent) {
|
func (db *MeasurementDB) InsertIntoQUICHandshake(ev *QUICTLSHandshakeEvent) {
|
||||||
db.mu.Lock()
|
db.mu.Lock()
|
||||||
db.quicHandshakeTable = append(db.quicHandshakeTable, ev)
|
db.quicHandshakeTable = append(db.quicHandshakeTable, ev)
|
||||||
db.mu.Unlock()
|
db.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// selectAllFromQUICHandshakeUnlocked returns all QUIC handshake events.
|
// selectAllFromQUICHandshakeUnlocked returns all QUIC handshake events.
|
||||||
func (db *MeasurementDB) selectAllFromQUICHandshakeUnlocked() (out []*QUICHandshakeEvent) {
|
func (db *MeasurementDB) selectAllFromQUICHandshakeUnlocked() (out []*QUICTLSHandshakeEvent) {
|
||||||
out = append(out, db.quicHandshakeTable...)
|
out = append(out, db.quicHandshakeTable...)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,17 +54,14 @@ type dialerDB struct {
|
||||||
// NetworkEvent contains a network event. This kind of events
|
// NetworkEvent contains a network event. This kind of events
|
||||||
// are generated by Dialer, QUICDialer, Conn, QUICConn.
|
// are generated by Dialer, QUICDialer, Conn, QUICConn.
|
||||||
type NetworkEvent struct {
|
type NetworkEvent struct {
|
||||||
// JSON names compatible with df-008-netevents
|
RemoteAddr string
|
||||||
RemoteAddr string `json:"address"`
|
Failure *string
|
||||||
Failure *string `json:"failure"`
|
Count int
|
||||||
Count int `json:"num_bytes,omitempty"`
|
Operation string
|
||||||
Operation string `json:"operation"`
|
Network string
|
||||||
Network string `json:"proto"`
|
Oddity Oddity
|
||||||
Finished float64 `json:"t"`
|
Finished float64
|
||||||
Started float64 `json:"started"`
|
Started float64
|
||||||
|
|
||||||
// Names that are not part of the spec.
|
|
||||||
Oddity Oddity `json:"oddity"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dialerDB) DialContext(
|
func (d *dialerDB) DialContext(
|
||||||
|
@ -78,7 +75,7 @@ func (d *dialerDB) DialContext(
|
||||||
RemoteAddr: address,
|
RemoteAddr: address,
|
||||||
Started: started,
|
Started: started,
|
||||||
Finished: finished,
|
Finished: finished,
|
||||||
Failure: NewArchivalFailure(err),
|
Failure: NewFailure(err),
|
||||||
Oddity: d.computeOddity(err),
|
Oddity: d.computeOddity(err),
|
||||||
Count: 0,
|
Count: 0,
|
||||||
})
|
})
|
||||||
|
@ -128,7 +125,7 @@ func (c *connDB) Read(b []byte) (int, error) {
|
||||||
RemoteAddr: c.remoteAddr,
|
RemoteAddr: c.remoteAddr,
|
||||||
Started: started,
|
Started: started,
|
||||||
Finished: finished,
|
Finished: finished,
|
||||||
Failure: NewArchivalFailure(err),
|
Failure: NewFailure(err),
|
||||||
Count: count,
|
Count: count,
|
||||||
})
|
})
|
||||||
return count, err
|
return count, err
|
||||||
|
@ -144,7 +141,7 @@ func (c *connDB) Write(b []byte) (int, error) {
|
||||||
RemoteAddr: c.remoteAddr,
|
RemoteAddr: c.remoteAddr,
|
||||||
Started: started,
|
Started: started,
|
||||||
Finished: finished,
|
Finished: finished,
|
||||||
Failure: NewArchivalFailure(err),
|
Failure: NewFailure(err),
|
||||||
Count: count,
|
Count: count,
|
||||||
})
|
})
|
||||||
return count, err
|
return count, err
|
||||||
|
@ -160,7 +157,7 @@ func (c *connDB) Close() error {
|
||||||
RemoteAddr: c.remoteAddr,
|
RemoteAddr: c.remoteAddr,
|
||||||
Started: started,
|
Started: started,
|
||||||
Finished: finished,
|
Finished: finished,
|
||||||
Failure: NewArchivalFailure(err),
|
Failure: NewFailure(err),
|
||||||
Count: 0,
|
Count: 0,
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -32,15 +32,13 @@ type dnsxRoundTripperDB struct {
|
||||||
|
|
||||||
// DNSRoundTripEvent contains the result of a DNS round trip.
|
// DNSRoundTripEvent contains the result of a DNS round trip.
|
||||||
type DNSRoundTripEvent struct {
|
type DNSRoundTripEvent struct {
|
||||||
// This data structure is not in df-002-dns but the names and
|
Network string
|
||||||
// semantics try to be consistent with such a spec.
|
Address string
|
||||||
Network string `json:"engine"`
|
Query []byte
|
||||||
Address string `json:"resolver_address"`
|
Started float64
|
||||||
Query *ArchivalBinaryData `json:"raw_query"`
|
Finished float64
|
||||||
Started float64 `json:"started"`
|
Failure *string
|
||||||
Finished float64 `json:"t"`
|
Reply []byte
|
||||||
Failure *string `json:"failure"`
|
|
||||||
Reply *ArchivalBinaryData `json:"raw_reply"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (txp *dnsxRoundTripperDB) RoundTrip(ctx context.Context, query []byte) ([]byte, error) {
|
func (txp *dnsxRoundTripperDB) RoundTrip(ctx context.Context, query []byte) ([]byte, error) {
|
||||||
|
@ -50,11 +48,11 @@ func (txp *dnsxRoundTripperDB) RoundTrip(ctx context.Context, query []byte) ([]b
|
||||||
txp.db.InsertIntoDNSRoundTrip(&DNSRoundTripEvent{
|
txp.db.InsertIntoDNSRoundTrip(&DNSRoundTripEvent{
|
||||||
Network: txp.DNSTransport.Network(),
|
Network: txp.DNSTransport.Network(),
|
||||||
Address: txp.DNSTransport.Address(),
|
Address: txp.DNSTransport.Address(),
|
||||||
Query: NewArchivalBinaryData(query),
|
Query: query,
|
||||||
Started: started,
|
Started: started,
|
||||||
Finished: finished,
|
Finished: finished,
|
||||||
Failure: NewArchivalFailure(err),
|
Failure: NewFailure(err),
|
||||||
Reply: NewArchivalBinaryData(reply),
|
Reply: reply,
|
||||||
})
|
})
|
||||||
return reply, err
|
return reply, err
|
||||||
}
|
}
|
||||||
|
|
12
internal/measurex/failure.go
Normal file
12
internal/measurex/failure.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package measurex
|
||||||
|
|
||||||
|
// NewFailure creates a serializable failure from an error. We
|
||||||
|
// cannot round trip an error using JSON, so we serialize to this
|
||||||
|
// intermediate format that is a sort of Optional<string>.
|
||||||
|
func NewFailure(err error) *string {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
s := err.Error()
|
||||||
|
return &s
|
||||||
|
}
|
|
@ -124,31 +124,33 @@ type HTTPResponse struct {
|
||||||
|
|
||||||
// HTTPRoundTripEvent contains information about an HTTP round trip.
|
// HTTPRoundTripEvent contains information about an HTTP round trip.
|
||||||
type HTTPRoundTripEvent struct {
|
type HTTPRoundTripEvent struct {
|
||||||
// JSON names following the df-001-httpt data format.
|
Failure *string
|
||||||
Failure *string `json:"failure"`
|
Method string
|
||||||
Request *HTTPRequest `json:"request"`
|
URL string
|
||||||
Response *HTTPResponse `json:"response"`
|
RequestHeaders http.Header
|
||||||
Finished float64 `json:"t"`
|
StatusCode int64
|
||||||
Started float64 `json:"started"`
|
ResponseHeaders http.Header
|
||||||
|
ResponseBody []byte
|
||||||
// Names not in the specification
|
ResponseBodyLength int64
|
||||||
Oddity Oddity `json:"oddity"`
|
ResponseBodyIsTruncated bool
|
||||||
|
ResponseBodyIsUTF8 bool
|
||||||
|
Finished float64
|
||||||
|
Started float64
|
||||||
|
Oddity Oddity
|
||||||
}
|
}
|
||||||
|
|
||||||
func (txp *HTTPTransportDB) RoundTrip(req *http.Request) (*http.Response, error) {
|
func (txp *HTTPTransportDB) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
started := time.Since(txp.Begin).Seconds()
|
started := time.Since(txp.Begin).Seconds()
|
||||||
resp, err := txp.HTTPTransport.RoundTrip(req)
|
resp, err := txp.HTTPTransport.RoundTrip(req)
|
||||||
rt := &HTTPRoundTripEvent{
|
rt := &HTTPRoundTripEvent{
|
||||||
Request: &HTTPRequest{
|
Method: req.Method,
|
||||||
Method: req.Method,
|
URL: req.URL.String(),
|
||||||
URL: req.URL.String(),
|
RequestHeaders: req.Header,
|
||||||
Headers: NewArchivalHeaders(req.Header),
|
Started: started,
|
||||||
},
|
|
||||||
Started: started,
|
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rt.Finished = time.Since(txp.Begin).Seconds()
|
rt.Finished = time.Since(txp.Begin).Seconds()
|
||||||
rt.Failure = NewArchivalFailure(err)
|
rt.Failure = NewFailure(err)
|
||||||
txp.DB.InsertIntoHTTPRoundTrip(rt)
|
txp.DB.InsertIntoHTTPRoundTrip(rt)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -162,10 +164,8 @@ func (txp *HTTPTransportDB) RoundTrip(req *http.Request) (*http.Response, error)
|
||||||
case resp.StatusCode >= 400:
|
case resp.StatusCode >= 400:
|
||||||
rt.Oddity = OddityStatusOther
|
rt.Oddity = OddityStatusOther
|
||||||
}
|
}
|
||||||
rt.Response = &HTTPResponse{
|
rt.StatusCode = int64(resp.StatusCode)
|
||||||
Code: int64(resp.StatusCode),
|
rt.ResponseHeaders = resp.Header
|
||||||
Headers: NewArchivalHeaders(resp.Header),
|
|
||||||
}
|
|
||||||
r := io.LimitReader(resp.Body, txp.MaxBodySnapshotSize)
|
r := io.LimitReader(resp.Body, txp.MaxBodySnapshotSize)
|
||||||
body, err := netxlite.ReadAllContext(req.Context(), r)
|
body, err := netxlite.ReadAllContext(req.Context(), r)
|
||||||
if errors.Is(err, io.EOF) && resp.Close {
|
if errors.Is(err, io.EOF) && resp.Close {
|
||||||
|
@ -173,7 +173,7 @@ func (txp *HTTPTransportDB) RoundTrip(req *http.Request) (*http.Response, error)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rt.Finished = time.Since(txp.Begin).Seconds()
|
rt.Finished = time.Since(txp.Begin).Seconds()
|
||||||
rt.Failure = NewArchivalFailure(err)
|
rt.Failure = NewFailure(err)
|
||||||
txp.DB.InsertIntoHTTPRoundTrip(rt)
|
txp.DB.InsertIntoHTTPRoundTrip(rt)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -181,10 +181,10 @@ func (txp *HTTPTransportDB) RoundTrip(req *http.Request) (*http.Response, error)
|
||||||
Reader: io.MultiReader(bytes.NewReader(body), resp.Body),
|
Reader: io.MultiReader(bytes.NewReader(body), resp.Body),
|
||||||
Closer: resp.Body,
|
Closer: resp.Body,
|
||||||
}
|
}
|
||||||
rt.Response.Body = NewArchivalBinaryData(body)
|
rt.ResponseBody = body
|
||||||
rt.Response.BodyLength = int64(len(body))
|
rt.ResponseBodyLength = int64(len(body))
|
||||||
rt.Response.BodyIsTruncated = int64(len(body)) >= txp.MaxBodySnapshotSize
|
rt.ResponseBodyIsTruncated = int64(len(body)) >= txp.MaxBodySnapshotSize
|
||||||
rt.Response.BodyIsUTF8 = utf8.Valid(body)
|
rt.ResponseBodyIsUTF8 = utf8.Valid(body)
|
||||||
rt.Finished = time.Since(txp.Begin).Seconds()
|
rt.Finished = time.Since(txp.Begin).Seconds()
|
||||||
txp.DB.InsertIntoHTTPRoundTrip(rt)
|
txp.DB.InsertIntoHTTPRoundTrip(rt)
|
||||||
return resp, nil
|
return resp, nil
|
||||||
|
|
|
@ -18,33 +18,33 @@ import (
|
||||||
// a bunch of measurements detailing each measurement step.
|
// a bunch of measurements detailing each measurement step.
|
||||||
type URLMeasurement struct {
|
type URLMeasurement struct {
|
||||||
// URL is the URL we're measuring.
|
// URL is the URL we're measuring.
|
||||||
URL string `json:"url"`
|
URL string
|
||||||
|
|
||||||
// DNS contains all the DNS related measurements.
|
// DNS contains all the DNS related measurements.
|
||||||
DNS []*DNSMeasurement `json:"dns"`
|
DNS []*DNSMeasurement
|
||||||
|
|
||||||
// Endpoints contains a measurement for each endpoint
|
// Endpoints contains a measurement for each endpoint
|
||||||
// that we discovered via DNS or TH.
|
// that we discovered via DNS or TH.
|
||||||
Endpoints []*HTTPEndpointMeasurement `json:"endpoints"`
|
Endpoints []*HTTPEndpointMeasurement
|
||||||
|
|
||||||
// RedirectURLs contain the URLs to which we should fetch
|
// RedirectURLs contain the URLs to which we should fetch
|
||||||
// if we choose to follow redirections.
|
// if we choose to follow redirections.
|
||||||
RedirectURLs []string `json:"-"`
|
RedirectURLs []string
|
||||||
|
|
||||||
// THMeasurement is the measurement collected by the TH.
|
// TH is the measurement collected by the TH.
|
||||||
TH interface{} `json:"th,omitempty"`
|
TH *THMeasurement
|
||||||
|
|
||||||
// TotalRuntime is the total time to measure this URL.
|
// TotalRuntime is the total time to measure this URL.
|
||||||
TotalRuntime time.Duration `json:"-"`
|
TotalRuntime time.Duration
|
||||||
|
|
||||||
// DNSRuntime is the time to run all DNS checks.
|
// DNSRuntime is the time to run all DNS checks.
|
||||||
DNSRuntime time.Duration `json:"x_dns_runtime"`
|
DNSRuntime time.Duration
|
||||||
|
|
||||||
// THRuntime is the total time to invoke all test helpers.
|
// THRuntime is the total time to invoke all test helpers.
|
||||||
THRuntime time.Duration `json:"x_th_runtime"`
|
THRuntime time.Duration
|
||||||
|
|
||||||
// EpntsRuntime is the total time to check all the endpoints.
|
// EpntsRuntime is the total time to check all the endpoints.
|
||||||
EpntsRuntime time.Duration `json:"x_epnts_runtime"`
|
EpntsRuntime time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// fillRedirects takes in input a complete URLMeasurement and fills
|
// fillRedirects takes in input a complete URLMeasurement and fills
|
||||||
|
@ -67,40 +67,40 @@ func (m *URLMeasurement) fillRedirects() {
|
||||||
// data format is not compatible with the OONI data format.
|
// data format is not compatible with the OONI data format.
|
||||||
type Measurement struct {
|
type Measurement struct {
|
||||||
// Connect contains all the connect operations.
|
// Connect contains all the connect operations.
|
||||||
Connect []*NetworkEvent `json:"connect,omitempty"`
|
Connect []*NetworkEvent
|
||||||
|
|
||||||
// ReadWrite contains all the read and write operations.
|
// ReadWrite contains all the read and write operations.
|
||||||
ReadWrite []*NetworkEvent `json:"read_write,omitempty"`
|
ReadWrite []*NetworkEvent
|
||||||
|
|
||||||
// Close contains all the close operations.
|
// Close contains all the close operations.
|
||||||
Close []*NetworkEvent `json:"-"`
|
Close []*NetworkEvent
|
||||||
|
|
||||||
// TLSHandshake contains all the TLS handshakes.
|
// TLSHandshake contains all the TLS handshakes.
|
||||||
TLSHandshake []*TLSHandshakeEvent `json:"tls_handshake,omitempty"`
|
TLSHandshake []*QUICTLSHandshakeEvent
|
||||||
|
|
||||||
// QUICHandshake contains all the QUIC handshakes.
|
// QUICHandshake contains all the QUIC handshakes.
|
||||||
QUICHandshake []*QUICHandshakeEvent `json:"quic_handshake,omitempty"`
|
QUICHandshake []*QUICTLSHandshakeEvent
|
||||||
|
|
||||||
// LookupHost contains all the host lookups.
|
// LookupHost contains all the host lookups.
|
||||||
LookupHost []*DNSLookupEvent `json:"lookup_host,omitempty"`
|
LookupHost []*DNSLookupEvent
|
||||||
|
|
||||||
// LookupHTTPSSvc contains all the HTTPSSvc lookups.
|
// LookupHTTPSSvc contains all the HTTPSSvc lookups.
|
||||||
LookupHTTPSSvc []*DNSLookupEvent `json:"lookup_httpssvc,omitempty"`
|
LookupHTTPSSvc []*DNSLookupEvent
|
||||||
|
|
||||||
// DNSRoundTrip contains all the DNS round trips.
|
// DNSRoundTrip contains all the DNS round trips.
|
||||||
DNSRoundTrip []*DNSRoundTripEvent `json:"dns_round_trip,omitempty"`
|
DNSRoundTrip []*DNSRoundTripEvent
|
||||||
|
|
||||||
// HTTPRoundTrip contains all the HTTP round trips.
|
// HTTPRoundTrip contains all the HTTP round trips.
|
||||||
HTTPRoundTrip []*HTTPRoundTripEvent `json:"http_round_trip,omitempty"`
|
HTTPRoundTrip []*HTTPRoundTripEvent
|
||||||
|
|
||||||
// HTTPRedirect contains all the redirections.
|
// HTTPRedirect contains all the redirections.
|
||||||
HTTPRedirect []*HTTPRedirectEvent `json:"-"`
|
HTTPRedirect []*HTTPRedirectEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
// DNSMeasurement is a DNS measurement.
|
// DNSMeasurement is a DNS measurement.
|
||||||
type DNSMeasurement struct {
|
type DNSMeasurement struct {
|
||||||
// Domain is the domain this measurement refers to.
|
// Domain is the domain this measurement refers to.
|
||||||
Domain string `json:"domain"`
|
Domain string
|
||||||
|
|
||||||
// A DNSMeasurement is a Measurement.
|
// A DNSMeasurement is a Measurement.
|
||||||
*Measurement
|
*Measurement
|
||||||
|
@ -239,10 +239,10 @@ func AllHTTPEndpointsForURL(URL *url.URL,
|
||||||
// EndpointMeasurement is an endpoint measurement.
|
// EndpointMeasurement is an endpoint measurement.
|
||||||
type EndpointMeasurement struct {
|
type EndpointMeasurement struct {
|
||||||
// Network is the network of this endpoint.
|
// Network is the network of this endpoint.
|
||||||
Network EndpointNetwork `json:"network"`
|
Network EndpointNetwork
|
||||||
|
|
||||||
// Address is the address of this endpoint.
|
// Address is the address of this endpoint.
|
||||||
Address string `json:"address"`
|
Address string
|
||||||
|
|
||||||
// An EndpointMeasurement is a Measurement.
|
// An EndpointMeasurement is a Measurement.
|
||||||
*Measurement
|
*Measurement
|
||||||
|
@ -251,14 +251,24 @@ type EndpointMeasurement struct {
|
||||||
// HTTPEndpointMeasurement is an HTTP endpoint measurement.
|
// HTTPEndpointMeasurement is an HTTP endpoint measurement.
|
||||||
type HTTPEndpointMeasurement struct {
|
type HTTPEndpointMeasurement struct {
|
||||||
// URL is the URL this measurement refers to.
|
// URL is the URL this measurement refers to.
|
||||||
URL string `json:"url"`
|
URL string
|
||||||
|
|
||||||
// Network is the network of this endpoint.
|
// Network is the network of this endpoint.
|
||||||
Network EndpointNetwork `json:"network"`
|
Network EndpointNetwork
|
||||||
|
|
||||||
// Address is the address of this endpoint.
|
// Address is the address of this endpoint.
|
||||||
Address string `json:"address"`
|
Address string
|
||||||
|
|
||||||
// An HTTPEndpointMeasurement is a Measurement.
|
// An HTTPEndpointMeasurement is a Measurement.
|
||||||
*Measurement
|
*Measurement
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// THMeasurement is the measurement performed by the TH.
|
||||||
|
type THMeasurement struct {
|
||||||
|
// DNS contains all the DNS related measurements.
|
||||||
|
DNS []*DNSMeasurement
|
||||||
|
|
||||||
|
// Endpoints contains a measurement for each endpoint
|
||||||
|
// that was discovered by the probe or the TH.
|
||||||
|
Endpoints []*HTTPEndpointMeasurement
|
||||||
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
stdlog "log"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -712,7 +711,7 @@ type MeasureURLHelper interface {
|
||||||
// test helper protocol allows one to set.
|
// test helper protocol allows one to set.
|
||||||
LookupExtraHTTPEndpoints(ctx context.Context, URL *url.URL,
|
LookupExtraHTTPEndpoints(ctx context.Context, URL *url.URL,
|
||||||
headers http.Header, epnts ...*HTTPEndpoint) (
|
headers http.Header, epnts ...*HTTPEndpoint) (
|
||||||
newEpnts []*HTTPEndpoint, thMeasurement interface{}, err error)
|
newEpnts []*HTTPEndpoint, thMeasurement *THMeasurement, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MeasureURL measures an HTTP or HTTPS URL. The DNS resolvers
|
// MeasureURL measures an HTTP or HTTPS URL. The DNS resolvers
|
||||||
|
@ -802,12 +801,8 @@ func (mx *Measurer) maybeQUICFollowUp(ctx context.Context,
|
||||||
if epnt.QUICHandshake != nil {
|
if epnt.QUICHandshake != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for idx, rtrip := range epnt.HTTPRoundTrip {
|
for _, rtrip := range epnt.HTTPRoundTrip {
|
||||||
if rtrip.Response == nil {
|
if v := rtrip.ResponseHeaders.Get("alt-svc"); v != "" {
|
||||||
stdlog.Printf("malformed HTTPRoundTrip@%d: %+v", idx, rtrip)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if v := rtrip.Response.Headers.Get("alt-svc"); v != "" {
|
|
||||||
altsvc = append(altsvc, v)
|
altsvc = append(altsvc, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ func (c *udpLikeConnDB) WriteTo(p []byte, addr net.Addr) (int, error) {
|
||||||
RemoteAddr: addr.String(),
|
RemoteAddr: addr.String(),
|
||||||
Started: started,
|
Started: started,
|
||||||
Finished: finished,
|
Finished: finished,
|
||||||
Failure: NewArchivalFailure(err),
|
Failure: NewFailure(err),
|
||||||
Count: count,
|
Count: count,
|
||||||
})
|
})
|
||||||
return count, err
|
return count, err
|
||||||
|
@ -76,7 +76,7 @@ func (c *udpLikeConnDB) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||||
RemoteAddr: addrStringIfNotNil(addr),
|
RemoteAddr: addrStringIfNotNil(addr),
|
||||||
Started: started,
|
Started: started,
|
||||||
Finished: finished,
|
Finished: finished,
|
||||||
Failure: NewArchivalFailure(err),
|
Failure: NewFailure(err),
|
||||||
Count: count,
|
Count: count,
|
||||||
})
|
})
|
||||||
return count, addr, err
|
return count, addr, err
|
||||||
|
@ -92,15 +92,12 @@ func (c *udpLikeConnDB) Close() error {
|
||||||
RemoteAddr: "",
|
RemoteAddr: "",
|
||||||
Started: started,
|
Started: started,
|
||||||
Finished: finished,
|
Finished: finished,
|
||||||
Failure: NewArchivalFailure(err),
|
Failure: NewFailure(err),
|
||||||
Count: 0,
|
Count: 0,
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// QUICHandshakeEvent is the result of QUICHandshake.
|
|
||||||
type QUICHandshakeEvent = TLSHandshakeEvent
|
|
||||||
|
|
||||||
// NewQUICDialerWithoutResolver creates a new QUICDialer that is not
|
// NewQUICDialerWithoutResolver creates a new QUICDialer that is not
|
||||||
// attached to any resolver. This means that every attempt to dial any
|
// attached to any resolver. This means that every attempt to dial any
|
||||||
// address containing a domain name will fail. This QUICDialer will
|
// address containing a domain name will fail. This QUICDialer will
|
||||||
|
@ -138,7 +135,7 @@ func (qh *quicDialerDB) DialContext(ctx context.Context, network, address string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finished := time.Since(qh.begin).Seconds()
|
finished := time.Since(qh.begin).Seconds()
|
||||||
qh.db.InsertIntoQUICHandshake(&QUICHandshakeEvent{
|
qh.db.InsertIntoQUICHandshake(&QUICTLSHandshakeEvent{
|
||||||
Network: "quic",
|
Network: "quic",
|
||||||
RemoteAddr: address,
|
RemoteAddr: address,
|
||||||
SNI: tlsConfig.ServerName,
|
SNI: tlsConfig.ServerName,
|
||||||
|
@ -146,12 +143,12 @@ func (qh *quicDialerDB) DialContext(ctx context.Context, network, address string
|
||||||
SkipVerify: tlsConfig.InsecureSkipVerify,
|
SkipVerify: tlsConfig.InsecureSkipVerify,
|
||||||
Started: started,
|
Started: started,
|
||||||
Finished: finished,
|
Finished: finished,
|
||||||
Failure: NewArchivalFailure(err),
|
Failure: NewFailure(err),
|
||||||
Oddity: qh.computeOddity(err),
|
Oddity: qh.computeOddity(err),
|
||||||
TLSVersion: netxlite.TLSVersionString(state.Version),
|
TLSVersion: netxlite.TLSVersionString(state.Version),
|
||||||
CipherSuite: netxlite.TLSCipherSuiteString(state.CipherSuite),
|
CipherSuite: netxlite.TLSCipherSuiteString(state.CipherSuite),
|
||||||
NegotiatedProto: state.NegotiatedProtocol,
|
NegotiatedProto: state.NegotiatedProtocol,
|
||||||
PeerCerts: NewArchivalTLSCerts(peerCerts(nil, &state)),
|
PeerCerts: peerCerts(nil, &state),
|
||||||
})
|
})
|
||||||
return sess, err
|
return sess, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ package measurex
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -65,31 +64,19 @@ type resolverDB struct {
|
||||||
db WritableDB
|
db WritableDB
|
||||||
}
|
}
|
||||||
|
|
||||||
// DNSLookupAnswer is a DNS lookup answer.
|
|
||||||
type DNSLookupAnswer struct {
|
|
||||||
// JSON names compatible with df-002-dnst's spec
|
|
||||||
Type string `json:"answer_type"`
|
|
||||||
IPv4 string `json:"ipv4,omitempty"`
|
|
||||||
IPv6 string `json:"ivp6,omitempty"`
|
|
||||||
|
|
||||||
// Names not part of the spec.
|
|
||||||
ALPN string `json:"alpn,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// DNSLookupEvent contains the results of a DNS lookup.
|
// DNSLookupEvent contains the results of a DNS lookup.
|
||||||
type DNSLookupEvent struct {
|
type DNSLookupEvent struct {
|
||||||
// fields inside df-002-dnst
|
Network string
|
||||||
Answers []DNSLookupAnswer `json:"answers"`
|
Failure *string
|
||||||
Network string `json:"engine"`
|
Domain string
|
||||||
Failure *string `json:"failure"`
|
QueryType string
|
||||||
Domain string `json:"hostname"`
|
Address string
|
||||||
QueryType string `json:"query_type"`
|
Finished float64
|
||||||
Address string `json:"resolver_address"`
|
Started float64
|
||||||
Finished float64 `json:"t"`
|
Oddity Oddity
|
||||||
|
A []string
|
||||||
// Names not part of the spec.
|
AAAA []string
|
||||||
Started float64 `json:"started"`
|
ALPN []string
|
||||||
Oddity Oddity `json:"oddity"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SupportsHTTP3 returns true if this query is for HTTPS and
|
// SupportsHTTP3 returns true if this query is for HTTPS and
|
||||||
|
@ -98,12 +85,9 @@ func (ev *DNSLookupEvent) SupportsHTTP3() bool {
|
||||||
if ev.QueryType != "HTTPS" {
|
if ev.QueryType != "HTTPS" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for _, ans := range ev.Answers {
|
for _, alpn := range ev.ALPN {
|
||||||
switch ans.Type {
|
if alpn == "h3" {
|
||||||
case "ALPN":
|
return true
|
||||||
if ans.ALPN == "h3" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -111,18 +95,8 @@ func (ev *DNSLookupEvent) SupportsHTTP3() bool {
|
||||||
|
|
||||||
// Addrs returns all the IPv4/IPv6 addresses
|
// Addrs returns all the IPv4/IPv6 addresses
|
||||||
func (ev *DNSLookupEvent) Addrs() (out []string) {
|
func (ev *DNSLookupEvent) Addrs() (out []string) {
|
||||||
for _, ans := range ev.Answers {
|
out = append(out, ev.A...)
|
||||||
switch ans.Type {
|
out = append(out, ev.AAAA...)
|
||||||
case "A":
|
|
||||||
if net.ParseIP(ans.IPv4) != nil {
|
|
||||||
out = append(out, ans.IPv4)
|
|
||||||
}
|
|
||||||
case "AAAA":
|
|
||||||
if net.ParseIP(ans.IPv6) != nil {
|
|
||||||
out = append(out, ans.IPv6)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,35 +104,39 @@ func (r *resolverDB) LookupHost(ctx context.Context, domain string) ([]string, e
|
||||||
started := time.Since(r.begin).Seconds()
|
started := time.Since(r.begin).Seconds()
|
||||||
addrs, err := r.Resolver.LookupHost(ctx, domain)
|
addrs, err := r.Resolver.LookupHost(ctx, domain)
|
||||||
finished := time.Since(r.begin).Seconds()
|
finished := time.Since(r.begin).Seconds()
|
||||||
for _, qtype := range []string{"A", "AAAA"} {
|
r.saveLookupResults(domain, started, finished, err, addrs, "A")
|
||||||
ev := &DNSLookupEvent{
|
r.saveLookupResults(domain, started, finished, err, addrs, "AAAA")
|
||||||
Answers: r.computeAnswers(addrs, qtype),
|
|
||||||
Network: r.Resolver.Network(),
|
|
||||||
Address: r.Resolver.Address(),
|
|
||||||
Failure: NewArchivalFailure(err),
|
|
||||||
Domain: domain,
|
|
||||||
QueryType: qtype,
|
|
||||||
Finished: finished,
|
|
||||||
Started: started,
|
|
||||||
Oddity: r.computeOddityLookupHost(addrs, err),
|
|
||||||
}
|
|
||||||
r.db.InsertIntoLookupHost(ev)
|
|
||||||
}
|
|
||||||
return addrs, err
|
return addrs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *resolverDB) computeAnswers(addrs []string, qtype string) (out []DNSLookupAnswer) {
|
func (r *resolverDB) saveLookupResults(domain string, started, finished float64,
|
||||||
|
err error, addrs []string, qtype string) {
|
||||||
|
ev := &DNSLookupEvent{
|
||||||
|
Network: r.Resolver.Network(),
|
||||||
|
Address: r.Resolver.Address(),
|
||||||
|
Failure: NewFailure(err),
|
||||||
|
Domain: domain,
|
||||||
|
QueryType: qtype,
|
||||||
|
Finished: finished,
|
||||||
|
Started: started,
|
||||||
|
}
|
||||||
for _, addr := range addrs {
|
for _, addr := range addrs {
|
||||||
if qtype == "A" && !strings.Contains(addr, ":") {
|
if qtype == "A" && !strings.Contains(addr, ":") {
|
||||||
out = append(out, DNSLookupAnswer{Type: qtype, IPv4: addr})
|
ev.A = append(ev.A, addr)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if qtype == "AAAA" && strings.Contains(addr, ":") {
|
if qtype == "AAAA" && strings.Contains(addr, ":") {
|
||||||
out = append(out, DNSLookupAnswer{Type: qtype, IPv6: addr})
|
ev.AAAA = append(ev.AAAA, addr)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
switch qtype {
|
||||||
|
case "A":
|
||||||
|
ev.Oddity = r.computeOddityLookupHost(ev.A, err)
|
||||||
|
case "AAAA":
|
||||||
|
ev.Oddity = r.computeOddityLookupHost(ev.AAAA, err)
|
||||||
|
}
|
||||||
|
r.db.InsertIntoLookupHost(ev)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *resolverDB) computeOddityLookupHost(addrs []string, err error) Oddity {
|
func (r *resolverDB) computeOddityLookupHost(addrs []string, err error) Oddity {
|
||||||
|
@ -193,28 +171,13 @@ func (r *resolverDB) LookupHTTPS(ctx context.Context, domain string) (*HTTPSSvc,
|
||||||
QueryType: "HTTPS",
|
QueryType: "HTTPS",
|
||||||
Started: started,
|
Started: started,
|
||||||
Finished: finished,
|
Finished: finished,
|
||||||
Failure: NewArchivalFailure(err),
|
Failure: NewFailure(err),
|
||||||
Oddity: Oddity(r.computeOddityHTTPSSvc(https, err)),
|
Oddity: Oddity(r.computeOddityHTTPSSvc(https, err)),
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
for _, addr := range https.IPv4 {
|
ev.A = append(ev.A, https.IPv4...)
|
||||||
ev.Answers = append(ev.Answers, DNSLookupAnswer{
|
ev.AAAA = append(ev.AAAA, https.IPv6...)
|
||||||
Type: "A",
|
ev.ALPN = append(ev.ALPN, https.ALPN...)
|
||||||
IPv4: addr,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
for _, addr := range https.IPv6 {
|
|
||||||
ev.Answers = append(ev.Answers, DNSLookupAnswer{
|
|
||||||
Type: "AAAA",
|
|
||||||
IPv6: addr,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
for _, alpn := range https.ALPN {
|
|
||||||
ev.Answers = append(ev.Answers, DNSLookupAnswer{
|
|
||||||
Type: "ALPN",
|
|
||||||
ALPN: alpn,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
r.db.InsertIntoLookupHTTPSSvc(ev)
|
r.db.InsertIntoLookupHTTPSSvc(ev)
|
||||||
return https, err
|
return https, err
|
||||||
|
|
|
@ -38,25 +38,21 @@ type tlsHandshakerDB struct {
|
||||||
db WritableDB
|
db WritableDB
|
||||||
}
|
}
|
||||||
|
|
||||||
// TLSHandshakeEvent contains a TLS handshake event.
|
// QUICTLSHandshakeEvent contains a QUIC or TLS handshake event.
|
||||||
type TLSHandshakeEvent struct {
|
type QUICTLSHandshakeEvent struct {
|
||||||
// JSON names compatible with df-006-tlshandshake
|
CipherSuite string
|
||||||
CipherSuite string `json:"cipher_suite"`
|
Failure *string
|
||||||
Failure *string `json:"failure"`
|
NegotiatedProto string
|
||||||
NegotiatedProto string `json:"negotiated_proto"`
|
TLSVersion string
|
||||||
TLSVersion string `json:"tls_version"`
|
PeerCerts [][]byte
|
||||||
PeerCerts []*ArchivalBinaryData `json:"peer_certificates"`
|
Finished float64
|
||||||
Finished float64 `json:"t"`
|
RemoteAddr string
|
||||||
|
SNI string
|
||||||
// JSON names that are consistent with the
|
ALPN []string
|
||||||
// spirit of the spec but are not in it
|
SkipVerify bool
|
||||||
RemoteAddr string `json:"address"`
|
Oddity Oddity
|
||||||
SNI string `json:"server_name"` // used in prod
|
Network string
|
||||||
ALPN []string `json:"alpn"`
|
Started float64
|
||||||
SkipVerify bool `json:"no_tls_verify"` // used in prod
|
|
||||||
Oddity Oddity `json:"oddity"`
|
|
||||||
Network string `json:"proto"`
|
|
||||||
Started float64 `json:"started"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (thx *tlsHandshakerDB) Handshake(ctx context.Context,
|
func (thx *tlsHandshakerDB) Handshake(ctx context.Context,
|
||||||
|
@ -66,7 +62,7 @@ func (thx *tlsHandshakerDB) Handshake(ctx context.Context,
|
||||||
started := time.Since(thx.begin).Seconds()
|
started := time.Since(thx.begin).Seconds()
|
||||||
tconn, state, err := thx.TLSHandshaker.Handshake(ctx, conn, config)
|
tconn, state, err := thx.TLSHandshaker.Handshake(ctx, conn, config)
|
||||||
finished := time.Since(thx.begin).Seconds()
|
finished := time.Since(thx.begin).Seconds()
|
||||||
thx.db.InsertIntoTLSHandshake(&TLSHandshakeEvent{
|
thx.db.InsertIntoTLSHandshake(&QUICTLSHandshakeEvent{
|
||||||
Network: network,
|
Network: network,
|
||||||
RemoteAddr: remoteAddr,
|
RemoteAddr: remoteAddr,
|
||||||
SNI: config.ServerName,
|
SNI: config.ServerName,
|
||||||
|
@ -74,12 +70,12 @@ func (thx *tlsHandshakerDB) Handshake(ctx context.Context,
|
||||||
SkipVerify: config.InsecureSkipVerify,
|
SkipVerify: config.InsecureSkipVerify,
|
||||||
Started: started,
|
Started: started,
|
||||||
Finished: finished,
|
Finished: finished,
|
||||||
Failure: NewArchivalFailure(err),
|
Failure: NewFailure(err),
|
||||||
Oddity: thx.computeOddity(err),
|
Oddity: thx.computeOddity(err),
|
||||||
TLSVersion: netxlite.TLSVersionString(state.Version),
|
TLSVersion: netxlite.TLSVersionString(state.Version),
|
||||||
CipherSuite: netxlite.TLSCipherSuiteString(state.CipherSuite),
|
CipherSuite: netxlite.TLSCipherSuiteString(state.CipherSuite),
|
||||||
NegotiatedProto: state.NegotiatedProtocol,
|
NegotiatedProto: state.NegotiatedProtocol,
|
||||||
PeerCerts: NewArchivalTLSCerts(peerCerts(err, &state)),
|
PeerCerts: peerCerts(err, &state),
|
||||||
})
|
})
|
||||||
return tconn, state, err
|
return tconn, state, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,8 +167,12 @@ format, in the remainder of this program we're
|
||||||
going to serialize the `Measurement` to JSON and
|
going to serialize the `Measurement` to JSON and
|
||||||
print it to the standard output.
|
print it to the standard output.
|
||||||
|
|
||||||
|
Rather than serializing the raw `Measurement` struct,
|
||||||
|
we first convert it to the "archival" format. This is the
|
||||||
|
data format specified at [ooni/spec](https://github.com/ooni/spec/tree/master/data-formats).
|
||||||
|
|
||||||
```Go
|
```Go
|
||||||
data, err := json.Marshal(m)
|
data, err := json.Marshal(measurex.NewArchivalDNSMeasurement(m))
|
||||||
runtimex.PanicOnError(err, "json.Marshal failed")
|
runtimex.PanicOnError(err, "json.Marshal failed")
|
||||||
fmt.Printf("%s\n", string(data))
|
fmt.Printf("%s\n", string(data))
|
||||||
```
|
```
|
||||||
|
@ -194,13 +198,14 @@ go run -race ./internal/tutorial/measurex/chapter01 | jq
|
||||||
```
|
```
|
||||||
|
|
||||||
Where `jq` is being used to make the output more presentable.
|
Where `jq` is being used to make the output more presentable.
|
||||||
|
|
||||||
If you do that you obtain some logging messages, which are out of
|
If you do that you obtain some logging messages, which are out of
|
||||||
the scope of this tutorial, and the following JSON:
|
the scope of this tutorial, and the following JSON:
|
||||||
|
|
||||||
```JSON
|
```JSON
|
||||||
{
|
{
|
||||||
"domain": "example.com",
|
"domain": "example.com",
|
||||||
"lookup_host": [
|
"queries": [
|
||||||
{
|
{
|
||||||
"answers": [
|
"answers": [
|
||||||
{
|
{
|
||||||
|
@ -237,6 +242,9 @@ the scope of this tutorial, and the following JSON:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This JSON [implements the df-002-dnst](https://github.com/ooni/spec/blob/master/data-formats/df-002-dnst.md)
|
||||||
|
OONI data format.
|
||||||
|
|
||||||
You see that we have two messages here. OONI splits a DNS
|
You see that we have two messages here. OONI splits a DNS
|
||||||
resolution performed using the system resolver into two "fake"
|
resolution performed using the system resolver into two "fake"
|
||||||
DNS resolutions for A and AAAA. (Under the hood, this is
|
DNS resolutions for A and AAAA. (Under the hood, this is
|
||||||
|
@ -266,7 +274,7 @@ This is the output JSON:
|
||||||
```JSON
|
```JSON
|
||||||
{
|
{
|
||||||
"domain": "antani.ooni.org",
|
"domain": "antani.ooni.org",
|
||||||
"lookup_host": [
|
"queries": [
|
||||||
{
|
{
|
||||||
"answers": null,
|
"answers": null,
|
||||||
"engine": "system",
|
"engine": "system",
|
||||||
|
@ -327,7 +335,7 @@ To get this JSON:
|
||||||
```JSON
|
```JSON
|
||||||
{
|
{
|
||||||
"domain": "example.com",
|
"domain": "example.com",
|
||||||
"lookup_host": [
|
"queries": [
|
||||||
{
|
{
|
||||||
"answers": null,
|
"answers": null,
|
||||||
"engine": "system",
|
"engine": "system",
|
||||||
|
|
|
@ -168,8 +168,12 @@ func main() {
|
||||||
// going to serialize the `Measurement` to JSON and
|
// going to serialize the `Measurement` to JSON and
|
||||||
// print it to the standard output.
|
// print it to the standard output.
|
||||||
//
|
//
|
||||||
|
// Rather than serializing the raw `Measurement` struct,
|
||||||
|
// we first convert it to the "archival" format. This is the
|
||||||
|
// data format specified at [ooni/spec](https://github.com/ooni/spec/tree/master/data-formats).
|
||||||
|
//
|
||||||
// ```Go
|
// ```Go
|
||||||
data, err := json.Marshal(m)
|
data, err := json.Marshal(measurex.NewArchivalDNSMeasurement(m))
|
||||||
runtimex.PanicOnError(err, "json.Marshal failed")
|
runtimex.PanicOnError(err, "json.Marshal failed")
|
||||||
fmt.Printf("%s\n", string(data))
|
fmt.Printf("%s\n", string(data))
|
||||||
// ```
|
// ```
|
||||||
|
@ -195,13 +199,14 @@ func main() {
|
||||||
// ```
|
// ```
|
||||||
//
|
//
|
||||||
// Where `jq` is being used to make the output more presentable.
|
// Where `jq` is being used to make the output more presentable.
|
||||||
|
//
|
||||||
// If you do that you obtain some logging messages, which are out of
|
// If you do that you obtain some logging messages, which are out of
|
||||||
// the scope of this tutorial, and the following JSON:
|
// the scope of this tutorial, and the following JSON:
|
||||||
//
|
//
|
||||||
// ```JSON
|
// ```JSON
|
||||||
// {
|
// {
|
||||||
// "domain": "example.com",
|
// "domain": "example.com",
|
||||||
// "lookup_host": [
|
// "queries": [
|
||||||
// {
|
// {
|
||||||
// "answers": [
|
// "answers": [
|
||||||
// {
|
// {
|
||||||
|
@ -238,6 +243,9 @@ func main() {
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
//
|
//
|
||||||
|
// This JSON [implements the df-002-dnst](https://github.com/ooni/spec/blob/master/data-formats/df-002-dnst.md)
|
||||||
|
// OONI data format.
|
||||||
|
//
|
||||||
// You see that we have two messages here. OONI splits a DNS
|
// You see that we have two messages here. OONI splits a DNS
|
||||||
// resolution performed using the system resolver into two "fake"
|
// resolution performed using the system resolver into two "fake"
|
||||||
// DNS resolutions for A and AAAA. (Under the hood, this is
|
// DNS resolutions for A and AAAA. (Under the hood, this is
|
||||||
|
@ -267,7 +275,7 @@ func main() {
|
||||||
// ```JSON
|
// ```JSON
|
||||||
// {
|
// {
|
||||||
// "domain": "antani.ooni.org",
|
// "domain": "antani.ooni.org",
|
||||||
// "lookup_host": [
|
// "queries": [
|
||||||
// {
|
// {
|
||||||
// "answers": null,
|
// "answers": null,
|
||||||
// "engine": "system",
|
// "engine": "system",
|
||||||
|
@ -328,7 +336,7 @@ func main() {
|
||||||
// ```JSON
|
// ```JSON
|
||||||
// {
|
// {
|
||||||
// "domain": "example.com",
|
// "domain": "example.com",
|
||||||
// "lookup_host": [
|
// "queries": [
|
||||||
// {
|
// {
|
||||||
// "answers": null,
|
// "answers": null,
|
||||||
// "engine": "system",
|
// "engine": "system",
|
||||||
|
|
|
@ -69,10 +69,12 @@ address is quoted with "[" and "]" if IPv6, e.g., `[::1]:53`.)
|
||||||
|
|
||||||
### Printing the measurement
|
### Printing the measurement
|
||||||
|
|
||||||
The rest of the main function is just like in the previous chapter.
|
The rest of the main function is just like in the previous
|
||||||
|
chapter. Like we did before, we convert the obtained measurement
|
||||||
|
to the "archival" data format before printing.
|
||||||
|
|
||||||
```Go
|
```Go
|
||||||
data, err := json.Marshal(m)
|
data, err := json.Marshal(measurex.NewArchivalEndpointMeasurement(m))
|
||||||
runtimex.PanicOnError(err, "json.Marshal failed")
|
runtimex.PanicOnError(err, "json.Marshal failed")
|
||||||
fmt.Printf("%s\n", string(data))
|
fmt.Printf("%s\n", string(data))
|
||||||
}
|
}
|
||||||
|
@ -98,27 +100,31 @@ Here is the JSON we obtain in output:
|
||||||
|
|
||||||
// This block contains the results of the connect syscall
|
// This block contains the results of the connect syscall
|
||||||
// using the df-008-netevents data format.
|
// using the df-008-netevents data format.
|
||||||
"connect": [
|
"tcp_connect": [{
|
||||||
{
|
"ip": "8.8.4.4",
|
||||||
"address": "8.8.4.4:443",
|
"port": 443,
|
||||||
|
"t": 0.020303,
|
||||||
|
"status": {
|
||||||
|
"blocked": false,
|
||||||
"failure": null,
|
"failure": null,
|
||||||
"operation": "connect",
|
"success": true
|
||||||
"proto": "tcp",
|
},
|
||||||
"t": 0.026879041,
|
"started": 0.000109292,
|
||||||
"started": 8.8625e-05,
|
"oddity": ""
|
||||||
"oddity": ""
|
}]
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This JSON implements [the df-005-tcpconnect](https://github.com/ooni/spec/blob/master/data-formats/df-005-tcpconnect.md)
|
||||||
|
OONI data format.
|
||||||
|
|
||||||
This is what it says:
|
This is what it says:
|
||||||
|
|
||||||
- we are connecting a "tcp" socket;
|
- we are connecting a "tcp" socket;
|
||||||
|
|
||||||
- the destination endpoint address is "8.8.4.4:443";
|
- the destination endpoint address is "8.8.4.4:443";
|
||||||
|
|
||||||
- connect terminated ~0.027 seconds into the program's life;
|
- connect terminated ~0.020 seconds into the program's life (see `t`);
|
||||||
|
|
||||||
- the operation succeeded (`failure` is `nil`).
|
- the operation succeeded (`failure` is `nil`).
|
||||||
|
|
||||||
|
@ -138,19 +144,21 @@ We get this JSON:
|
||||||
{
|
{
|
||||||
"network": "tcp",
|
"network": "tcp",
|
||||||
"address": "127.0.0.1:1",
|
"address": "127.0.0.1:1",
|
||||||
"connect": [
|
"tcp_connect": [
|
||||||
{
|
{
|
||||||
"address": "127.0.0.1:1",
|
"ip": "127.0.0.1",
|
||||||
"failure": "connection_refused",
|
"port": 1,
|
||||||
"operation": "connect",
|
"t": 0.000457584,
|
||||||
"proto": "tcp",
|
"status": {
|
||||||
"t": 0.000372167,
|
"blocked": true,
|
||||||
"started": 8.4917e-05,
|
"failure": "connection_refused",
|
||||||
|
"success": false
|
||||||
|
},
|
||||||
|
"started": 0.000104792,
|
||||||
"oddity": "tcp.connect.refused"
|
"oddity": "tcp.connect.refused"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
And here's an error telling us the connection was refused and
|
And here's an error telling us the connection was refused and
|
||||||
|
@ -170,14 +178,17 @@ We get this JSON:
|
||||||
{
|
{
|
||||||
"network": "tcp",
|
"network": "tcp",
|
||||||
"address": "8.8.4.4:1",
|
"address": "8.8.4.4:1",
|
||||||
"connect": [
|
"tcp_connect": [
|
||||||
{
|
{
|
||||||
"address": "8.8.4.4:1",
|
"ip": "8.8.4.4",
|
||||||
"failure": "generic_timeout_error",
|
"port": 1,
|
||||||
"operation": "connect",
|
"t": 10.006558625,
|
||||||
"proto": "tcp",
|
"status": {
|
||||||
"t": 10.005494583,
|
"blocked": true,
|
||||||
"started": 8.4833e-05,
|
"failure": "generic_timeout_error",
|
||||||
|
"success": false
|
||||||
|
},
|
||||||
|
"started": 9.55e-05,
|
||||||
"oddity": "tcp.connect.timeout"
|
"oddity": "tcp.connect.timeout"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -201,21 +212,26 @@ To get this JSON:
|
||||||
{
|
{
|
||||||
"network": "tcp",
|
"network": "tcp",
|
||||||
"address": "8.8.4.4:1",
|
"address": "8.8.4.4:1",
|
||||||
"connect": [
|
"tcp_connect": [
|
||||||
{
|
{
|
||||||
"address": "8.8.4.4:1",
|
"ip": "8.8.4.4",
|
||||||
"failure": "generic_timeout_error",
|
"port": 1,
|
||||||
"operation": "connect",
|
"t": 0.105445125,
|
||||||
"proto": "tcp",
|
"status": {
|
||||||
"t": 0.10148025,
|
"blocked": true,
|
||||||
"started": 0.000122375,
|
"failure": "generic_timeout_error",
|
||||||
|
"success": false
|
||||||
|
},
|
||||||
|
"started": 9.4083e-05,
|
||||||
"oddity": "tcp.connect.timeout"
|
"oddity": "tcp.connect.timeout"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
We see a timeout after ~0.1s. We enforce a reasonably small
|
We see a timeout after ~0.1s.
|
||||||
|
|
||||||
|
We enforce a reasonably small
|
||||||
timeout for connecting, equal to 10 s, because we want to
|
timeout for connecting, equal to 10 s, because we want to
|
||||||
guarantee that measurements eventually terminate. Also, since
|
guarantee that measurements eventually terminate. Also, since
|
||||||
often censorship is implemented by timing out, we don't want
|
often censorship is implemented by timing out, we don't want
|
||||||
|
|
|
@ -70,10 +70,12 @@ func main() {
|
||||||
//
|
//
|
||||||
// ### Printing the measurement
|
// ### Printing the measurement
|
||||||
//
|
//
|
||||||
// The rest of the main function is just like in the previous chapter.
|
// The rest of the main function is just like in the previous
|
||||||
|
// chapter. Like we did before, we convert the obtained measurement
|
||||||
|
// to the "archival" data format before printing.
|
||||||
//
|
//
|
||||||
// ```Go
|
// ```Go
|
||||||
data, err := json.Marshal(m)
|
data, err := json.Marshal(measurex.NewArchivalEndpointMeasurement(m))
|
||||||
runtimex.PanicOnError(err, "json.Marshal failed")
|
runtimex.PanicOnError(err, "json.Marshal failed")
|
||||||
fmt.Printf("%s\n", string(data))
|
fmt.Printf("%s\n", string(data))
|
||||||
}
|
}
|
||||||
|
@ -99,27 +101,31 @@ func main() {
|
||||||
//
|
//
|
||||||
// // This block contains the results of the connect syscall
|
// // This block contains the results of the connect syscall
|
||||||
// // using the df-008-netevents data format.
|
// // using the df-008-netevents data format.
|
||||||
// "connect": [
|
// "tcp_connect": [{
|
||||||
// {
|
// "ip": "8.8.4.4",
|
||||||
// "address": "8.8.4.4:443",
|
// "port": 443,
|
||||||
|
// "t": 0.020303,
|
||||||
|
// "status": {
|
||||||
|
// "blocked": false,
|
||||||
// "failure": null,
|
// "failure": null,
|
||||||
// "operation": "connect",
|
// "success": true
|
||||||
// "proto": "tcp",
|
// },
|
||||||
// "t": 0.026879041,
|
// "started": 0.000109292,
|
||||||
// "started": 8.8625e-05,
|
// "oddity": ""
|
||||||
// "oddity": ""
|
// }]
|
||||||
// }
|
|
||||||
// ]
|
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
//
|
//
|
||||||
|
// This JSON implements [the df-005-tcpconnect](https://github.com/ooni/spec/blob/master/data-formats/df-005-tcpconnect.md)
|
||||||
|
// OONI data format.
|
||||||
|
//
|
||||||
// This is what it says:
|
// This is what it says:
|
||||||
//
|
//
|
||||||
// - we are connecting a "tcp" socket;
|
// - we are connecting a "tcp" socket;
|
||||||
//
|
//
|
||||||
// - the destination endpoint address is "8.8.4.4:443";
|
// - the destination endpoint address is "8.8.4.4:443";
|
||||||
//
|
//
|
||||||
// - connect terminated ~0.027 seconds into the program's life;
|
// - connect terminated ~0.020 seconds into the program's life (see `t`);
|
||||||
//
|
//
|
||||||
// - the operation succeeded (`failure` is `nil`).
|
// - the operation succeeded (`failure` is `nil`).
|
||||||
//
|
//
|
||||||
|
@ -139,19 +145,21 @@ func main() {
|
||||||
// {
|
// {
|
||||||
// "network": "tcp",
|
// "network": "tcp",
|
||||||
// "address": "127.0.0.1:1",
|
// "address": "127.0.0.1:1",
|
||||||
// "connect": [
|
// "tcp_connect": [
|
||||||
// {
|
// {
|
||||||
// "address": "127.0.0.1:1",
|
// "ip": "127.0.0.1",
|
||||||
// "failure": "connection_refused",
|
// "port": 1,
|
||||||
// "operation": "connect",
|
// "t": 0.000457584,
|
||||||
// "proto": "tcp",
|
// "status": {
|
||||||
// "t": 0.000372167,
|
// "blocked": true,
|
||||||
// "started": 8.4917e-05,
|
// "failure": "connection_refused",
|
||||||
|
// "success": false
|
||||||
|
// },
|
||||||
|
// "started": 0.000104792,
|
||||||
// "oddity": "tcp.connect.refused"
|
// "oddity": "tcp.connect.refused"
|
||||||
// }
|
// }
|
||||||
// ]
|
// ]
|
||||||
// }
|
// }
|
||||||
//
|
|
||||||
// ```
|
// ```
|
||||||
//
|
//
|
||||||
// And here's an error telling us the connection was refused and
|
// And here's an error telling us the connection was refused and
|
||||||
|
@ -171,14 +179,17 @@ func main() {
|
||||||
// {
|
// {
|
||||||
// "network": "tcp",
|
// "network": "tcp",
|
||||||
// "address": "8.8.4.4:1",
|
// "address": "8.8.4.4:1",
|
||||||
// "connect": [
|
// "tcp_connect": [
|
||||||
// {
|
// {
|
||||||
// "address": "8.8.4.4:1",
|
// "ip": "8.8.4.4",
|
||||||
// "failure": "generic_timeout_error",
|
// "port": 1,
|
||||||
// "operation": "connect",
|
// "t": 10.006558625,
|
||||||
// "proto": "tcp",
|
// "status": {
|
||||||
// "t": 10.005494583,
|
// "blocked": true,
|
||||||
// "started": 8.4833e-05,
|
// "failure": "generic_timeout_error",
|
||||||
|
// "success": false
|
||||||
|
// },
|
||||||
|
// "started": 9.55e-05,
|
||||||
// "oddity": "tcp.connect.timeout"
|
// "oddity": "tcp.connect.timeout"
|
||||||
// }
|
// }
|
||||||
// ]
|
// ]
|
||||||
|
@ -202,21 +213,26 @@ func main() {
|
||||||
// {
|
// {
|
||||||
// "network": "tcp",
|
// "network": "tcp",
|
||||||
// "address": "8.8.4.4:1",
|
// "address": "8.8.4.4:1",
|
||||||
// "connect": [
|
// "tcp_connect": [
|
||||||
// {
|
// {
|
||||||
// "address": "8.8.4.4:1",
|
// "ip": "8.8.4.4",
|
||||||
// "failure": "generic_timeout_error",
|
// "port": 1,
|
||||||
// "operation": "connect",
|
// "t": 0.105445125,
|
||||||
// "proto": "tcp",
|
// "status": {
|
||||||
// "t": 0.10148025,
|
// "blocked": true,
|
||||||
// "started": 0.000122375,
|
// "failure": "generic_timeout_error",
|
||||||
|
// "success": false
|
||||||
|
// },
|
||||||
|
// "started": 9.4083e-05,
|
||||||
// "oddity": "tcp.connect.timeout"
|
// "oddity": "tcp.connect.timeout"
|
||||||
// }
|
// }
|
||||||
// ]
|
// ]
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
//
|
//
|
||||||
// We see a timeout after ~0.1s. We enforce a reasonably small
|
// We see a timeout after ~0.1s.
|
||||||
|
//
|
||||||
|
// We enforce a reasonably small
|
||||||
// timeout for connecting, equal to 10 s, because we want to
|
// timeout for connecting, equal to 10 s, because we want to
|
||||||
// guarantee that measurements eventually terminate. Also, since
|
// guarantee that measurements eventually terminate. Also, since
|
||||||
// often censorship is implemented by timing out, we don't want
|
// often censorship is implemented by timing out, we don't want
|
||||||
|
|
|
@ -57,7 +57,7 @@ Also this operation returns a measurement, which
|
||||||
we print using the usual three-liner.
|
we print using the usual three-liner.
|
||||||
|
|
||||||
```Go
|
```Go
|
||||||
data, err := json.Marshal(m)
|
data, err := json.Marshal(measurex.NewArchivalDNSMeasurement(m))
|
||||||
runtimex.PanicOnError(err, "json.Marshal failed")
|
runtimex.PanicOnError(err, "json.Marshal failed")
|
||||||
fmt.Printf("%s\n", string(data))
|
fmt.Printf("%s\n", string(data))
|
||||||
}
|
}
|
||||||
|
@ -83,42 +83,22 @@ be generated and inserted into a `Measurement`.)
|
||||||
{
|
{
|
||||||
"domain": "example.com",
|
"domain": "example.com",
|
||||||
|
|
||||||
// This block tells us about the UDP connect events
|
|
||||||
// where we bind to the server's endpoint
|
|
||||||
"connect": [
|
|
||||||
{
|
|
||||||
"address": "8.8.4.4:53",
|
|
||||||
"failure": null,
|
|
||||||
"operation": "connect",
|
|
||||||
"proto": "udp",
|
|
||||||
"t": 0.00043175,
|
|
||||||
"started": 0.000191958,
|
|
||||||
"oddity": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "8.8.4.4:53",
|
|
||||||
"failure": null,
|
|
||||||
"operation": "connect",
|
|
||||||
"proto": "udp",
|
|
||||||
"t": 0.042198458,
|
|
||||||
"started": 0.042113208,
|
|
||||||
"oddity": ""
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
// This block shows the read and write events
|
// This block shows the read and write events
|
||||||
// occurred on the sockets (because we control
|
// occurred on the sockets (because we control
|
||||||
// in full the implementation of this DNS
|
// in full the implementation of this DNS
|
||||||
// over UDP resolver, we can see these events)
|
// over UDP resolver, we can see these events)
|
||||||
"read_write": [
|
//
|
||||||
|
// See https://github.com/ooni/spec/blob/master/data-formats/df-008-netevents.md
|
||||||
|
// for a description of this data format.
|
||||||
|
"network_events": [
|
||||||
{
|
{
|
||||||
"address": "8.8.4.4:53",
|
"address": "8.8.4.4:53",
|
||||||
"failure": null,
|
"failure": null,
|
||||||
"num_bytes": 29,
|
"num_bytes": 29,
|
||||||
"operation": "write",
|
"operation": "write",
|
||||||
"proto": "udp",
|
"proto": "udp",
|
||||||
"t": 0.000459583,
|
"t": 0.00048825,
|
||||||
"started": 0.00043825,
|
"started": 0.000462917,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -127,8 +107,8 @@ be generated and inserted into a `Measurement`.)
|
||||||
"num_bytes": 45,
|
"num_bytes": 45,
|
||||||
"operation": "read",
|
"operation": "read",
|
||||||
"proto": "udp",
|
"proto": "udp",
|
||||||
"t": 0.041955792,
|
"t": 0.022081833,
|
||||||
"started": 0.000471833,
|
"started": 0.000502625,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -137,8 +117,8 @@ be generated and inserted into a `Measurement`.)
|
||||||
"num_bytes": 29,
|
"num_bytes": 29,
|
||||||
"operation": "write",
|
"operation": "write",
|
||||||
"proto": "udp",
|
"proto": "udp",
|
||||||
"t": 0.042218917,
|
"t": 0.022433083,
|
||||||
"started": 0.042203,
|
"started": 0.022423875,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -147,19 +127,59 @@ be generated and inserted into a `Measurement`.)
|
||||||
"num_bytes": 57,
|
"num_bytes": 57,
|
||||||
"operation": "read",
|
"operation": "read",
|
||||||
"proto": "udp",
|
"proto": "udp",
|
||||||
"t": 0.196646583,
|
"t": 0.046706,
|
||||||
"started": 0.042233167,
|
"started": 0.022443833,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
// This block shows the query we sent (encoded as base64)
|
||||||
|
// and the response we received. Here we clearly see
|
||||||
|
// that we perform two DNS "round trip" (i.e., send request
|
||||||
|
// and receive response) to resolve a domain: one for
|
||||||
|
// A and the other for AAAA.
|
||||||
|
//
|
||||||
|
// We don't have a specification for this data format yet.
|
||||||
|
"dns_events": [
|
||||||
|
{
|
||||||
|
"engine": "udp",
|
||||||
|
"resolver_address": "8.8.4.4:53",
|
||||||
|
"raw_query": {
|
||||||
|
"data": "dGwBAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE=",
|
||||||
|
"format": "base64"
|
||||||
|
},
|
||||||
|
"started": 0.000205083,
|
||||||
|
"t": 0.022141333,
|
||||||
|
"failure": null,
|
||||||
|
"raw_reply": {
|
||||||
|
"data": "dGyBgAABAAEAAAAAB2V4YW1wbGUDY29tAAABAAHADAABAAEAAEuIAARduNgi",
|
||||||
|
"format": "base64"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"engine": "udp",
|
||||||
|
"resolver_address": "8.8.4.4:53",
|
||||||
|
"raw_query": {
|
||||||
|
"data": "Ts8BAAABAAAAAAAAB2V4YW1wbGUDY29tAAAcAAE=",
|
||||||
|
"format": "base64"
|
||||||
|
},
|
||||||
|
"started": 0.022221417,
|
||||||
|
"t": 0.046733125,
|
||||||
|
"failure": null,
|
||||||
|
"raw_reply": {
|
||||||
|
"data": "Ts+BgAABAAEAAAAAB2V4YW1wbGUDY29tAAAcAAHADAAcAAEAAFOQABAmBigAAiAAAQJIGJMlyBlG",
|
||||||
|
"format": "base64"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
// This is the same kind of result as before, we
|
// This is the same kind of result as before, we
|
||||||
// show the emitted queries and the resolved addrs.
|
// show the emitted queries and the resolved addrs.
|
||||||
//
|
//
|
||||||
// Also note how here the resolver_address is the
|
// Also note how here the resolver_address is the
|
||||||
// correct endpoint address and the engine tells us
|
// correct endpoint address and the engine tells us
|
||||||
// that we're using DNS over UDP.
|
// that we're using DNS over UDP.
|
||||||
"lookup_host": [
|
"queries": [
|
||||||
{
|
{
|
||||||
"answers": [
|
"answers": [
|
||||||
{
|
{
|
||||||
|
@ -172,8 +192,8 @@ be generated and inserted into a `Measurement`.)
|
||||||
"hostname": "example.com",
|
"hostname": "example.com",
|
||||||
"query_type": "A",
|
"query_type": "A",
|
||||||
"resolver_address": "8.8.4.4:53",
|
"resolver_address": "8.8.4.4:53",
|
||||||
"t": 0.196777042,
|
"t": 0.046766833,
|
||||||
"started": 0.000118542,
|
"started": 0.000124375,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -188,48 +208,10 @@ be generated and inserted into a `Measurement`.)
|
||||||
"hostname": "example.com",
|
"hostname": "example.com",
|
||||||
"query_type": "AAAA",
|
"query_type": "AAAA",
|
||||||
"resolver_address": "8.8.4.4:53",
|
"resolver_address": "8.8.4.4:53",
|
||||||
"t": 0.196777042,
|
"t": 0.046766833,
|
||||||
"started": 0.000118542,
|
"started": 0.000124375,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
}
|
}
|
||||||
],
|
|
||||||
|
|
||||||
// This block shows the query we sent (encoded as base64)
|
|
||||||
// and the response we received. Here we clearly see
|
|
||||||
// that we perform two DNS "round trip" (i.e., send request
|
|
||||||
// and receive response) to resolve a domain: one for
|
|
||||||
// A and the other for AAAA.
|
|
||||||
"dns_round_trip": [
|
|
||||||
{
|
|
||||||
"engine": "udp",
|
|
||||||
"resolver_address": "8.8.4.4:53",
|
|
||||||
"raw_query": {
|
|
||||||
"data": "PrcBAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE=",
|
|
||||||
"format": "base64"
|
|
||||||
},
|
|
||||||
"started": 0.000191625,
|
|
||||||
"t": 0.041998667,
|
|
||||||
"failure": null,
|
|
||||||
"raw_reply": {
|
|
||||||
"data": "PreBgAABAAEAAAAAB2V4YW1wbGUDY29tAAABAAHADAABAAEAAE8BAARduNgi",
|
|
||||||
"format": "base64"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"engine": "udp",
|
|
||||||
"resolver_address": "8.8.4.4:53",
|
|
||||||
"raw_query": {
|
|
||||||
"data": "LAwBAAABAAAAAAAAB2V4YW1wbGUDY29tAAAcAAE=",
|
|
||||||
"format": "base64"
|
|
||||||
},
|
|
||||||
"started": 0.04210775,
|
|
||||||
"t": 0.196701333,
|
|
||||||
"failure": null,
|
|
||||||
"raw_reply": {
|
|
||||||
"data": "LAyBgAABAAEAAAAAB2V4YW1wbGUDY29tAAAcAAHADAAcAAEAAE6nABAmBigAAiAAAQJIGJMlyBlG",
|
|
||||||
"format": "base64"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -254,9 +236,42 @@ This produces the following JSON:
|
||||||
```JavaScript
|
```JavaScript
|
||||||
{
|
{
|
||||||
"domain": "antani.ooni.org",
|
"domain": "antani.ooni.org",
|
||||||
"connect": [ /* snip */ ],
|
"network_events": [ /* snip */ ],
|
||||||
"read_write": [ /* snip */ ],
|
|
||||||
"lookup_host": [
|
"dns_events": [
|
||||||
|
{
|
||||||
|
"engine": "udp",
|
||||||
|
"resolver_address": "8.8.4.4:53",
|
||||||
|
"raw_query": {
|
||||||
|
"data": "p7YBAAABAAAAAAAABmFudGFuaQRvb25pA29yZwAAAQAB",
|
||||||
|
"format": "base64"
|
||||||
|
},
|
||||||
|
"started": 0.000152959,
|
||||||
|
"t": 0.051650917,
|
||||||
|
"failure": null,
|
||||||
|
"raw_reply": {
|
||||||
|
"data": "p7aBgwABAAAAAQAABmFudGFuaQRvb25pA29yZwAAAQABwBMABgABAAAHCAA9BGRuczERcmVnaXN0cmFyLXNlcnZlcnMDY29tAApob3N0bWFzdGVywDJhbwqOAACowAAADhAACTqAAAAOEQ==",
|
||||||
|
"format": "base64"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"engine": "udp",
|
||||||
|
"resolver_address": "8.8.4.4:53",
|
||||||
|
"raw_query": {
|
||||||
|
"data": "ILkBAAABAAAAAAAABmFudGFuaQRvb25pA29yZwAAHAAB",
|
||||||
|
"format": "base64"
|
||||||
|
},
|
||||||
|
"started": 0.051755209,
|
||||||
|
"t": 0.101094375,
|
||||||
|
"failure": null,
|
||||||
|
"raw_reply": {
|
||||||
|
"data": "ILmBgwABAAAAAQAABmFudGFuaQRvb25pA29yZwAAHAABwBMABgABAAAHCAA9BGRuczERcmVnaXN0cmFyLXNlcnZlcnMDY29tAApob3N0bWFzdGVywDJhbwqOAACowAAADhAACTqAAAAOEQ==",
|
||||||
|
"format": "base64"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"queries": [
|
||||||
{
|
{
|
||||||
"answers": null,
|
"answers": null,
|
||||||
"engine": "udp",
|
"engine": "udp",
|
||||||
|
@ -264,8 +279,8 @@ This produces the following JSON:
|
||||||
"hostname": "antani.ooni.org",
|
"hostname": "antani.ooni.org",
|
||||||
"query_type": "A",
|
"query_type": "A",
|
||||||
"resolver_address": "8.8.4.4:53",
|
"resolver_address": "8.8.4.4:53",
|
||||||
"t": 0.098208709,
|
"t": 0.101241667,
|
||||||
"started": 8.95e-05,
|
"started": 8.8e-05,
|
||||||
"oddity": "dns.lookup.nxdomain"
|
"oddity": "dns.lookup.nxdomain"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -275,42 +290,10 @@ This produces the following JSON:
|
||||||
"hostname": "antani.ooni.org",
|
"hostname": "antani.ooni.org",
|
||||||
"query_type": "AAAA",
|
"query_type": "AAAA",
|
||||||
"resolver_address": "8.8.4.4:53",
|
"resolver_address": "8.8.4.4:53",
|
||||||
"t": 0.098208709,
|
"t": 0.101241667,
|
||||||
"started": 8.95e-05,
|
"started": 8.8e-05,
|
||||||
"oddity": "dns.lookup.nxdomain"
|
"oddity": "dns.lookup.nxdomain"
|
||||||
}
|
}
|
||||||
],
|
|
||||||
"dns_round_trip": [
|
|
||||||
{
|
|
||||||
"engine": "udp",
|
|
||||||
"resolver_address": "8.8.4.4:53",
|
|
||||||
"raw_query": {
|
|
||||||
"data": "jLIBAAABAAAAAAAABmFudGFuaQRvb25pA29yZwAAAQAB",
|
|
||||||
"format": "base64"
|
|
||||||
},
|
|
||||||
"started": 0.000141542,
|
|
||||||
"t": 0.034689417,
|
|
||||||
"failure": null,
|
|
||||||
"raw_reply": {
|
|
||||||
"data": "jLKBgwABAAAAAQAABmFudGFuaQRvb25pA29yZwAAAQABwBMABgABAAAHCAA9BGRuczERcmVnaXN0cmFyLXNlcnZlcnMDY29tAApob3N0bWFzdGVywDJhABz8AACowAAADhAACTqAAAAOEQ==",
|
|
||||||
"format": "base64"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"engine": "udp",
|
|
||||||
"resolver_address": "8.8.4.4:53",
|
|
||||||
"raw_query": {
|
|
||||||
"data": "azEBAAABAAAAAAAABmFudGFuaQRvb25pA29yZwAAHAAB",
|
|
||||||
"format": "base64"
|
|
||||||
},
|
|
||||||
"started": 0.034776709,
|
|
||||||
"t": 0.098170542,
|
|
||||||
"failure": null,
|
|
||||||
"raw_reply": {
|
|
||||||
"data": "azGBgwABAAAAAQAABmFudGFuaQRvb25pA29yZwAAHAABwBMABgABAAAHCAA9BGRuczERcmVnaXN0cmFyLXNlcnZlcnMDY29tAApob3N0bWFzdGVywDJhABz8AACowAAADhAACTqAAAAOEQ==",
|
|
||||||
"format": "base64"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -323,10 +306,10 @@ Let us now decode one of the replies by using this program:
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -366,16 +349,15 @@ Here's the corresponding JSON:
|
||||||
```JavaScript
|
```JavaScript
|
||||||
{
|
{
|
||||||
"domain": "example.com",
|
"domain": "example.com",
|
||||||
"connect": [ /* snip */ ],
|
"network_events": [
|
||||||
"read_write": [
|
|
||||||
{
|
{
|
||||||
"address": "182.92.22.222:53",
|
"address": "182.92.22.222:53",
|
||||||
"failure": null,
|
"failure": null,
|
||||||
"num_bytes": 29,
|
"num_bytes": 29,
|
||||||
"operation": "write",
|
"operation": "write",
|
||||||
"proto": "udp",
|
"proto": "udp",
|
||||||
"t": 0.0005275,
|
"t": 0.000479583,
|
||||||
"started": 0.000500209,
|
"started": 0.00045525,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -383,56 +365,58 @@ Here's the corresponding JSON:
|
||||||
"failure": "generic_timeout_error", /* <--- */
|
"failure": "generic_timeout_error", /* <--- */
|
||||||
"operation": "read",
|
"operation": "read",
|
||||||
"proto": "udp",
|
"proto": "udp",
|
||||||
"t": 5.001140125,
|
"t": 5.006016292,
|
||||||
"started": 0.000544042,
|
"started": 0.000491792,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"lookup_host": [
|
"dns_events": [
|
||||||
{
|
|
||||||
"answers": null,
|
|
||||||
"engine": "udp",
|
|
||||||
"failure": "generic_timeout_error", /* <--- */
|
|
||||||
"hostname": "example.com",
|
|
||||||
"query_type": "A",
|
|
||||||
"resolver_address": "182.92.22.222:53",
|
|
||||||
"t": 5.001462084,
|
|
||||||
"started": 0.000127917,
|
|
||||||
"oddity": "dns.lookup.timeout" /* <--- */
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"answers": null,
|
|
||||||
"engine": "udp",
|
|
||||||
"failure": "generic_timeout_error",
|
|
||||||
"hostname": "example.com",
|
|
||||||
"query_type": "AAAA",
|
|
||||||
"resolver_address": "182.92.22.222:53",
|
|
||||||
"t": 5.001462084,
|
|
||||||
"started": 0.000127917,
|
|
||||||
"oddity": "dns.lookup.timeout"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"dns_round_trip": [
|
|
||||||
{
|
{
|
||||||
"engine": "udp",
|
"engine": "udp",
|
||||||
"resolver_address": "182.92.22.222:53",
|
"resolver_address": "182.92.22.222:53",
|
||||||
"raw_query": {
|
"raw_query": {
|
||||||
"data": "ej8BAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE=",
|
"data": "GRUBAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE=",
|
||||||
"format": "base64"
|
"format": "base64"
|
||||||
},
|
},
|
||||||
"started": 0.000220584,
|
"started": 0.00018225,
|
||||||
"t": 5.001317417,
|
"t": 5.006185667,
|
||||||
"failure": "generic_timeout_error",
|
"failure": "generic_timeout_error", /* <--- */
|
||||||
"raw_reply": null
|
"raw_reply": null
|
||||||
}
|
}
|
||||||
|
/* snip */
|
||||||
|
],
|
||||||
|
"queries": [
|
||||||
|
{
|
||||||
|
"answers": null,
|
||||||
|
"engine": "udp",
|
||||||
|
"failure": "generic_timeout_error", /* <--- */
|
||||||
|
"hostname": "example.com",
|
||||||
|
"query_type": "A",
|
||||||
|
"resolver_address": "182.92.22.222:53",
|
||||||
|
"t": 5.007385458,
|
||||||
|
"started": 0.000107583,
|
||||||
|
"oddity": "dns.lookup.timeout"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answers": null,
|
||||||
|
"engine": "udp",
|
||||||
|
"failure": "generic_timeout_error", /* <--- */
|
||||||
|
"hostname": "example.com",
|
||||||
|
"query_type": "AAAA",
|
||||||
|
"resolver_address": "182.92.22.222:53",
|
||||||
|
"t": 5.007385458,
|
||||||
|
"started": 0.000107583,
|
||||||
|
"oddity": "dns.lookup.timeout"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
We see that we fail with a timeout (I have marked some of them
|
We see that we fail with a timeout (I have marked some of them
|
||||||
with comments inside the JSON). We see the timeout at three different
|
with comments inside the JSON). We see the timeout at three different
|
||||||
levels of abstractions (from lower to higher abstraction): at the socket layer,
|
levels of abstractions (from lower to higher abstraction): at the socket
|
||||||
during the DNS round trip, during the DNS lookup.
|
layer (`network_events`), during the DNS round trip (`dns_events`),
|
||||||
|
during the DNS lookup (`queries`).
|
||||||
|
|
||||||
What we also see is that `t`'s value is ~5s when the `read` event
|
What we also see is that `t`'s value is ~5s when the `read` event
|
||||||
fails, which tells us about the socket's read timeout.
|
fails, which tells us about the socket's read timeout.
|
||||||
|
@ -451,18 +435,17 @@ Here's the answer I get:
|
||||||
```JavaScript
|
```JavaScript
|
||||||
{
|
{
|
||||||
"domain": "example.com",
|
"domain": "example.com",
|
||||||
"connect": [ /* snip */ ],
|
|
||||||
|
|
||||||
// The I/O events look normal this time
|
// The network events look normal this time
|
||||||
"read_write": [
|
"network_events": [
|
||||||
{
|
{
|
||||||
"address": "180.97.36.63:53",
|
"address": "180.97.36.63:53",
|
||||||
"failure": null,
|
"failure": null,
|
||||||
"num_bytes": 29,
|
"num_bytes": 29,
|
||||||
"operation": "write",
|
"operation": "write",
|
||||||
"proto": "udp",
|
"proto": "udp",
|
||||||
"t": 0.000333583,
|
"t": 0.000492125,
|
||||||
"started": 0.000312125,
|
"started": 0.000467042,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -471,8 +454,8 @@ Here's the answer I get:
|
||||||
"num_bytes": 29,
|
"num_bytes": 29,
|
||||||
"operation": "read",
|
"operation": "read",
|
||||||
"proto": "udp",
|
"proto": "udp",
|
||||||
"t": 0.334948125,
|
"t": 0.321373542,
|
||||||
"started": 0.000366625,
|
"started": 0.000504833,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -481,8 +464,8 @@ Here's the answer I get:
|
||||||
"num_bytes": 29,
|
"num_bytes": 29,
|
||||||
"operation": "write",
|
"operation": "write",
|
||||||
"proto": "udp",
|
"proto": "udp",
|
||||||
"t": 0.3358025,
|
"t": 0.322500875,
|
||||||
"started": 0.335725958,
|
"started": 0.322450042,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -491,15 +474,49 @@ Here's the answer I get:
|
||||||
"num_bytes": 29,
|
"num_bytes": 29,
|
||||||
"operation": "read",
|
"operation": "read",
|
||||||
"proto": "udp",
|
"proto": "udp",
|
||||||
"t": 0.739987666,
|
"t": 0.655514542,
|
||||||
"started": 0.335863875,
|
"started": 0.322557667,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
// But we see both in the error and in the oddity
|
// Exercise: do like I did before and decode the messages
|
||||||
|
"dns_events": [
|
||||||
|
{
|
||||||
|
"engine": "udp",
|
||||||
|
"resolver_address": "180.97.36.63:53",
|
||||||
|
"raw_query": {
|
||||||
|
"data": "WcgBAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE=",
|
||||||
|
"format": "base64"
|
||||||
|
},
|
||||||
|
"started": 0.000209875,
|
||||||
|
"t": 0.321504042,
|
||||||
|
"failure": null,
|
||||||
|
"raw_reply": {
|
||||||
|
"data": "WciBBQABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE=",
|
||||||
|
"format": "base64"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"engine": "udp",
|
||||||
|
"resolver_address": "180.97.36.63:53",
|
||||||
|
"raw_query": {
|
||||||
|
"data": "I9oBAAABAAAAAAAAB2V4YW1wbGUDY29tAAAcAAE=",
|
||||||
|
"format": "base64"
|
||||||
|
},
|
||||||
|
"started": 0.321672042,
|
||||||
|
"t": 0.655680792,
|
||||||
|
"failure": null,
|
||||||
|
"raw_reply": {
|
||||||
|
"data": "I9qBBQABAAAAAAAAB2V4YW1wbGUDY29tAAAcAAE=",
|
||||||
|
"format": "base64"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// We see both in the error and in the oddity
|
||||||
// that the response was "REFUSED"
|
// that the response was "REFUSED"
|
||||||
"lookup_host": [
|
"queries": [
|
||||||
{
|
{
|
||||||
"answers": null,
|
"answers": null,
|
||||||
"engine": "udp",
|
"engine": "udp",
|
||||||
|
@ -507,8 +524,8 @@ Here's the answer I get:
|
||||||
"hostname": "example.com",
|
"hostname": "example.com",
|
||||||
"query_type": "A",
|
"query_type": "A",
|
||||||
"resolver_address": "180.97.36.63:53",
|
"resolver_address": "180.97.36.63:53",
|
||||||
"t": 0.7402975,
|
"t": 0.655814875,
|
||||||
"started": 7.2291e-05,
|
"started": 0.000107417,
|
||||||
"oddity": "dns.lookup.refused"
|
"oddity": "dns.lookup.refused"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -518,44 +535,10 @@ Here's the answer I get:
|
||||||
"hostname": "example.com",
|
"hostname": "example.com",
|
||||||
"query_type": "AAAA",
|
"query_type": "AAAA",
|
||||||
"resolver_address": "180.97.36.63:53",
|
"resolver_address": "180.97.36.63:53",
|
||||||
"t": 0.7402975,
|
"t": 0.655814875,
|
||||||
"started": 7.2291e-05,
|
"started": 0.000107417,
|
||||||
"oddity": "dns.lookup.refused"
|
"oddity": "dns.lookup.refused"
|
||||||
}
|
}
|
||||||
],
|
|
||||||
|
|
||||||
// Exercise: do like I did before and decode the messages
|
|
||||||
"dns_round_trip": [
|
|
||||||
{
|
|
||||||
"engine": "udp",
|
|
||||||
"resolver_address": "180.97.36.63:53",
|
|
||||||
"raw_query": {
|
|
||||||
"data": "crkBAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE=",
|
|
||||||
"format": "base64"
|
|
||||||
},
|
|
||||||
"started": 0.000130666,
|
|
||||||
"t": 0.33509925,
|
|
||||||
"failure": null,
|
|
||||||
"raw_reply": {
|
|
||||||
"data": "crmBBQABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE=",
|
|
||||||
"format": "base64"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"engine": "udp",
|
|
||||||
"resolver_address": "180.97.36.63:53",
|
|
||||||
"raw_query": {
|
|
||||||
"data": "ywcBAAABAAAAAAAAB2V4YW1wbGUDY29tAAAcAAE=",
|
|
||||||
"format": "base64"
|
|
||||||
},
|
|
||||||
"started": 0.335321333,
|
|
||||||
"t": 0.740152375,
|
|
||||||
"failure": null,
|
|
||||||
"raw_reply": {
|
|
||||||
"data": "yweBBQABAAAAAAAAB2V4YW1wbGUDY29tAAAcAAE=",
|
|
||||||
"format": "base64"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
|
@ -58,7 +58,7 @@ func main() {
|
||||||
// we print using the usual three-liner.
|
// we print using the usual three-liner.
|
||||||
//
|
//
|
||||||
// ```Go
|
// ```Go
|
||||||
data, err := json.Marshal(m)
|
data, err := json.Marshal(measurex.NewArchivalDNSMeasurement(m))
|
||||||
runtimex.PanicOnError(err, "json.Marshal failed")
|
runtimex.PanicOnError(err, "json.Marshal failed")
|
||||||
fmt.Printf("%s\n", string(data))
|
fmt.Printf("%s\n", string(data))
|
||||||
}
|
}
|
||||||
|
@ -84,42 +84,22 @@ func main() {
|
||||||
// {
|
// {
|
||||||
// "domain": "example.com",
|
// "domain": "example.com",
|
||||||
//
|
//
|
||||||
// // This block tells us about the UDP connect events
|
|
||||||
// // where we bind to the server's endpoint
|
|
||||||
// "connect": [
|
|
||||||
// {
|
|
||||||
// "address": "8.8.4.4:53",
|
|
||||||
// "failure": null,
|
|
||||||
// "operation": "connect",
|
|
||||||
// "proto": "udp",
|
|
||||||
// "t": 0.00043175,
|
|
||||||
// "started": 0.000191958,
|
|
||||||
// "oddity": ""
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// "address": "8.8.4.4:53",
|
|
||||||
// "failure": null,
|
|
||||||
// "operation": "connect",
|
|
||||||
// "proto": "udp",
|
|
||||||
// "t": 0.042198458,
|
|
||||||
// "started": 0.042113208,
|
|
||||||
// "oddity": ""
|
|
||||||
// }
|
|
||||||
// ],
|
|
||||||
//
|
|
||||||
// // This block shows the read and write events
|
// // This block shows the read and write events
|
||||||
// // occurred on the sockets (because we control
|
// // occurred on the sockets (because we control
|
||||||
// // in full the implementation of this DNS
|
// // in full the implementation of this DNS
|
||||||
// // over UDP resolver, we can see these events)
|
// // over UDP resolver, we can see these events)
|
||||||
// "read_write": [
|
// //
|
||||||
|
// // See https://github.com/ooni/spec/blob/master/data-formats/df-008-netevents.md
|
||||||
|
// // for a description of this data format.
|
||||||
|
// "network_events": [
|
||||||
// {
|
// {
|
||||||
// "address": "8.8.4.4:53",
|
// "address": "8.8.4.4:53",
|
||||||
// "failure": null,
|
// "failure": null,
|
||||||
// "num_bytes": 29,
|
// "num_bytes": 29,
|
||||||
// "operation": "write",
|
// "operation": "write",
|
||||||
// "proto": "udp",
|
// "proto": "udp",
|
||||||
// "t": 0.000459583,
|
// "t": 0.00048825,
|
||||||
// "started": 0.00043825,
|
// "started": 0.000462917,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -128,8 +108,8 @@ func main() {
|
||||||
// "num_bytes": 45,
|
// "num_bytes": 45,
|
||||||
// "operation": "read",
|
// "operation": "read",
|
||||||
// "proto": "udp",
|
// "proto": "udp",
|
||||||
// "t": 0.041955792,
|
// "t": 0.022081833,
|
||||||
// "started": 0.000471833,
|
// "started": 0.000502625,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -138,8 +118,8 @@ func main() {
|
||||||
// "num_bytes": 29,
|
// "num_bytes": 29,
|
||||||
// "operation": "write",
|
// "operation": "write",
|
||||||
// "proto": "udp",
|
// "proto": "udp",
|
||||||
// "t": 0.042218917,
|
// "t": 0.022433083,
|
||||||
// "started": 0.042203,
|
// "started": 0.022423875,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -148,19 +128,59 @@ func main() {
|
||||||
// "num_bytes": 57,
|
// "num_bytes": 57,
|
||||||
// "operation": "read",
|
// "operation": "read",
|
||||||
// "proto": "udp",
|
// "proto": "udp",
|
||||||
// "t": 0.196646583,
|
// "t": 0.046706,
|
||||||
// "started": 0.042233167,
|
// "started": 0.022443833,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// }
|
// }
|
||||||
// ],
|
// ],
|
||||||
//
|
//
|
||||||
|
// // This block shows the query we sent (encoded as base64)
|
||||||
|
// // and the response we received. Here we clearly see
|
||||||
|
// // that we perform two DNS "round trip" (i.e., send request
|
||||||
|
// // and receive response) to resolve a domain: one for
|
||||||
|
// // A and the other for AAAA.
|
||||||
|
// //
|
||||||
|
// // We don't have a specification for this data format yet.
|
||||||
|
// "dns_events": [
|
||||||
|
// {
|
||||||
|
// "engine": "udp",
|
||||||
|
// "resolver_address": "8.8.4.4:53",
|
||||||
|
// "raw_query": {
|
||||||
|
// "data": "dGwBAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE=",
|
||||||
|
// "format": "base64"
|
||||||
|
// },
|
||||||
|
// "started": 0.000205083,
|
||||||
|
// "t": 0.022141333,
|
||||||
|
// "failure": null,
|
||||||
|
// "raw_reply": {
|
||||||
|
// "data": "dGyBgAABAAEAAAAAB2V4YW1wbGUDY29tAAABAAHADAABAAEAAEuIAARduNgi",
|
||||||
|
// "format": "base64"
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// "engine": "udp",
|
||||||
|
// "resolver_address": "8.8.4.4:53",
|
||||||
|
// "raw_query": {
|
||||||
|
// "data": "Ts8BAAABAAAAAAAAB2V4YW1wbGUDY29tAAAcAAE=",
|
||||||
|
// "format": "base64"
|
||||||
|
// },
|
||||||
|
// "started": 0.022221417,
|
||||||
|
// "t": 0.046733125,
|
||||||
|
// "failure": null,
|
||||||
|
// "raw_reply": {
|
||||||
|
// "data": "Ts+BgAABAAEAAAAAB2V4YW1wbGUDY29tAAAcAAHADAAcAAEAAFOQABAmBigAAiAAAQJIGJMlyBlG",
|
||||||
|
// "format": "base64"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ],
|
||||||
|
//
|
||||||
// // This is the same kind of result as before, we
|
// // This is the same kind of result as before, we
|
||||||
// // show the emitted queries and the resolved addrs.
|
// // show the emitted queries and the resolved addrs.
|
||||||
// //
|
// //
|
||||||
// // Also note how here the resolver_address is the
|
// // Also note how here the resolver_address is the
|
||||||
// // correct endpoint address and the engine tells us
|
// // correct endpoint address and the engine tells us
|
||||||
// // that we're using DNS over UDP.
|
// // that we're using DNS over UDP.
|
||||||
// "lookup_host": [
|
// "queries": [
|
||||||
// {
|
// {
|
||||||
// "answers": [
|
// "answers": [
|
||||||
// {
|
// {
|
||||||
|
@ -173,8 +193,8 @@ func main() {
|
||||||
// "hostname": "example.com",
|
// "hostname": "example.com",
|
||||||
// "query_type": "A",
|
// "query_type": "A",
|
||||||
// "resolver_address": "8.8.4.4:53",
|
// "resolver_address": "8.8.4.4:53",
|
||||||
// "t": 0.196777042,
|
// "t": 0.046766833,
|
||||||
// "started": 0.000118542,
|
// "started": 0.000124375,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -189,48 +209,10 @@ func main() {
|
||||||
// "hostname": "example.com",
|
// "hostname": "example.com",
|
||||||
// "query_type": "AAAA",
|
// "query_type": "AAAA",
|
||||||
// "resolver_address": "8.8.4.4:53",
|
// "resolver_address": "8.8.4.4:53",
|
||||||
// "t": 0.196777042,
|
// "t": 0.046766833,
|
||||||
// "started": 0.000118542,
|
// "started": 0.000124375,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// }
|
// }
|
||||||
// ],
|
|
||||||
//
|
|
||||||
// // This block shows the query we sent (encoded as base64)
|
|
||||||
// // and the response we received. Here we clearly see
|
|
||||||
// // that we perform two DNS "round trip" (i.e., send request
|
|
||||||
// // and receive response) to resolve a domain: one for
|
|
||||||
// // A and the other for AAAA.
|
|
||||||
// "dns_round_trip": [
|
|
||||||
// {
|
|
||||||
// "engine": "udp",
|
|
||||||
// "resolver_address": "8.8.4.4:53",
|
|
||||||
// "raw_query": {
|
|
||||||
// "data": "PrcBAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE=",
|
|
||||||
// "format": "base64"
|
|
||||||
// },
|
|
||||||
// "started": 0.000191625,
|
|
||||||
// "t": 0.041998667,
|
|
||||||
// "failure": null,
|
|
||||||
// "raw_reply": {
|
|
||||||
// "data": "PreBgAABAAEAAAAAB2V4YW1wbGUDY29tAAABAAHADAABAAEAAE8BAARduNgi",
|
|
||||||
// "format": "base64"
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// "engine": "udp",
|
|
||||||
// "resolver_address": "8.8.4.4:53",
|
|
||||||
// "raw_query": {
|
|
||||||
// "data": "LAwBAAABAAAAAAAAB2V4YW1wbGUDY29tAAAcAAE=",
|
|
||||||
// "format": "base64"
|
|
||||||
// },
|
|
||||||
// "started": 0.04210775,
|
|
||||||
// "t": 0.196701333,
|
|
||||||
// "failure": null,
|
|
||||||
// "raw_reply": {
|
|
||||||
// "data": "LAyBgAABAAEAAAAAB2V4YW1wbGUDY29tAAAcAAHADAAcAAEAAE6nABAmBigAAiAAAQJIGJMlyBlG",
|
|
||||||
// "format": "base64"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// ]
|
// ]
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
|
@ -255,9 +237,42 @@ func main() {
|
||||||
// ```JavaScript
|
// ```JavaScript
|
||||||
// {
|
// {
|
||||||
// "domain": "antani.ooni.org",
|
// "domain": "antani.ooni.org",
|
||||||
// "connect": [ /* snip */ ],
|
// "network_events": [ /* snip */ ],
|
||||||
// "read_write": [ /* snip */ ],
|
//
|
||||||
// "lookup_host": [
|
// "dns_events": [
|
||||||
|
// {
|
||||||
|
// "engine": "udp",
|
||||||
|
// "resolver_address": "8.8.4.4:53",
|
||||||
|
// "raw_query": {
|
||||||
|
// "data": "p7YBAAABAAAAAAAABmFudGFuaQRvb25pA29yZwAAAQAB",
|
||||||
|
// "format": "base64"
|
||||||
|
// },
|
||||||
|
// "started": 0.000152959,
|
||||||
|
// "t": 0.051650917,
|
||||||
|
// "failure": null,
|
||||||
|
// "raw_reply": {
|
||||||
|
// "data": "p7aBgwABAAAAAQAABmFudGFuaQRvb25pA29yZwAAAQABwBMABgABAAAHCAA9BGRuczERcmVnaXN0cmFyLXNlcnZlcnMDY29tAApob3N0bWFzdGVywDJhbwqOAACowAAADhAACTqAAAAOEQ==",
|
||||||
|
// "format": "base64"
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// "engine": "udp",
|
||||||
|
// "resolver_address": "8.8.4.4:53",
|
||||||
|
// "raw_query": {
|
||||||
|
// "data": "ILkBAAABAAAAAAAABmFudGFuaQRvb25pA29yZwAAHAAB",
|
||||||
|
// "format": "base64"
|
||||||
|
// },
|
||||||
|
// "started": 0.051755209,
|
||||||
|
// "t": 0.101094375,
|
||||||
|
// "failure": null,
|
||||||
|
// "raw_reply": {
|
||||||
|
// "data": "ILmBgwABAAAAAQAABmFudGFuaQRvb25pA29yZwAAHAABwBMABgABAAAHCAA9BGRuczERcmVnaXN0cmFyLXNlcnZlcnMDY29tAApob3N0bWFzdGVywDJhbwqOAACowAAADhAACTqAAAAOEQ==",
|
||||||
|
// "format": "base64"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ],
|
||||||
|
//
|
||||||
|
// "queries": [
|
||||||
// {
|
// {
|
||||||
// "answers": null,
|
// "answers": null,
|
||||||
// "engine": "udp",
|
// "engine": "udp",
|
||||||
|
@ -265,8 +280,8 @@ func main() {
|
||||||
// "hostname": "antani.ooni.org",
|
// "hostname": "antani.ooni.org",
|
||||||
// "query_type": "A",
|
// "query_type": "A",
|
||||||
// "resolver_address": "8.8.4.4:53",
|
// "resolver_address": "8.8.4.4:53",
|
||||||
// "t": 0.098208709,
|
// "t": 0.101241667,
|
||||||
// "started": 8.95e-05,
|
// "started": 8.8e-05,
|
||||||
// "oddity": "dns.lookup.nxdomain"
|
// "oddity": "dns.lookup.nxdomain"
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -276,42 +291,10 @@ func main() {
|
||||||
// "hostname": "antani.ooni.org",
|
// "hostname": "antani.ooni.org",
|
||||||
// "query_type": "AAAA",
|
// "query_type": "AAAA",
|
||||||
// "resolver_address": "8.8.4.4:53",
|
// "resolver_address": "8.8.4.4:53",
|
||||||
// "t": 0.098208709,
|
// "t": 0.101241667,
|
||||||
// "started": 8.95e-05,
|
// "started": 8.8e-05,
|
||||||
// "oddity": "dns.lookup.nxdomain"
|
// "oddity": "dns.lookup.nxdomain"
|
||||||
// }
|
// }
|
||||||
// ],
|
|
||||||
// "dns_round_trip": [
|
|
||||||
// {
|
|
||||||
// "engine": "udp",
|
|
||||||
// "resolver_address": "8.8.4.4:53",
|
|
||||||
// "raw_query": {
|
|
||||||
// "data": "jLIBAAABAAAAAAAABmFudGFuaQRvb25pA29yZwAAAQAB",
|
|
||||||
// "format": "base64"
|
|
||||||
// },
|
|
||||||
// "started": 0.000141542,
|
|
||||||
// "t": 0.034689417,
|
|
||||||
// "failure": null,
|
|
||||||
// "raw_reply": {
|
|
||||||
// "data": "jLKBgwABAAAAAQAABmFudGFuaQRvb25pA29yZwAAAQABwBMABgABAAAHCAA9BGRuczERcmVnaXN0cmFyLXNlcnZlcnMDY29tAApob3N0bWFzdGVywDJhABz8AACowAAADhAACTqAAAAOEQ==",
|
|
||||||
// "format": "base64"
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// "engine": "udp",
|
|
||||||
// "resolver_address": "8.8.4.4:53",
|
|
||||||
// "raw_query": {
|
|
||||||
// "data": "azEBAAABAAAAAAAABmFudGFuaQRvb25pA29yZwAAHAAB",
|
|
||||||
// "format": "base64"
|
|
||||||
// },
|
|
||||||
// "started": 0.034776709,
|
|
||||||
// "t": 0.098170542,
|
|
||||||
// "failure": null,
|
|
||||||
// "raw_reply": {
|
|
||||||
// "data": "azGBgwABAAAAAQAABmFudGFuaQRvb25pA29yZwAAHAABwBMABgABAAAHCAA9BGRuczERcmVnaXN0cmFyLXNlcnZlcnMDY29tAApob3N0bWFzdGVywDJhABz8AACowAAADhAACTqAAAAOEQ==",
|
|
||||||
// "format": "base64"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// ]
|
// ]
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
|
@ -324,10 +307,10 @@ func main() {
|
||||||
// package main
|
// package main
|
||||||
//
|
//
|
||||||
// import (
|
// import (
|
||||||
// "fmt"
|
// "fmt"
|
||||||
// "encoding/base64"
|
// "encoding/base64"
|
||||||
//
|
//
|
||||||
// "github.com/miekg/dns"
|
// "github.com/miekg/dns"
|
||||||
// )
|
// )
|
||||||
//
|
//
|
||||||
// func main() {
|
// func main() {
|
||||||
|
@ -367,16 +350,15 @@ func main() {
|
||||||
// ```JavaScript
|
// ```JavaScript
|
||||||
// {
|
// {
|
||||||
// "domain": "example.com",
|
// "domain": "example.com",
|
||||||
// "connect": [ /* snip */ ],
|
// "network_events": [
|
||||||
// "read_write": [
|
|
||||||
// {
|
// {
|
||||||
// "address": "182.92.22.222:53",
|
// "address": "182.92.22.222:53",
|
||||||
// "failure": null,
|
// "failure": null,
|
||||||
// "num_bytes": 29,
|
// "num_bytes": 29,
|
||||||
// "operation": "write",
|
// "operation": "write",
|
||||||
// "proto": "udp",
|
// "proto": "udp",
|
||||||
// "t": 0.0005275,
|
// "t": 0.000479583,
|
||||||
// "started": 0.000500209,
|
// "started": 0.00045525,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -384,56 +366,58 @@ func main() {
|
||||||
// "failure": "generic_timeout_error", /* <--- */
|
// "failure": "generic_timeout_error", /* <--- */
|
||||||
// "operation": "read",
|
// "operation": "read",
|
||||||
// "proto": "udp",
|
// "proto": "udp",
|
||||||
// "t": 5.001140125,
|
// "t": 5.006016292,
|
||||||
// "started": 0.000544042,
|
// "started": 0.000491792,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// }
|
// }
|
||||||
// ],
|
// ],
|
||||||
// "lookup_host": [
|
// "dns_events": [
|
||||||
// {
|
|
||||||
// "answers": null,
|
|
||||||
// "engine": "udp",
|
|
||||||
// "failure": "generic_timeout_error", /* <--- */
|
|
||||||
// "hostname": "example.com",
|
|
||||||
// "query_type": "A",
|
|
||||||
// "resolver_address": "182.92.22.222:53",
|
|
||||||
// "t": 5.001462084,
|
|
||||||
// "started": 0.000127917,
|
|
||||||
// "oddity": "dns.lookup.timeout" /* <--- */
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// "answers": null,
|
|
||||||
// "engine": "udp",
|
|
||||||
// "failure": "generic_timeout_error",
|
|
||||||
// "hostname": "example.com",
|
|
||||||
// "query_type": "AAAA",
|
|
||||||
// "resolver_address": "182.92.22.222:53",
|
|
||||||
// "t": 5.001462084,
|
|
||||||
// "started": 0.000127917,
|
|
||||||
// "oddity": "dns.lookup.timeout"
|
|
||||||
// }
|
|
||||||
// ],
|
|
||||||
// "dns_round_trip": [
|
|
||||||
// {
|
// {
|
||||||
// "engine": "udp",
|
// "engine": "udp",
|
||||||
// "resolver_address": "182.92.22.222:53",
|
// "resolver_address": "182.92.22.222:53",
|
||||||
// "raw_query": {
|
// "raw_query": {
|
||||||
// "data": "ej8BAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE=",
|
// "data": "GRUBAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE=",
|
||||||
// "format": "base64"
|
// "format": "base64"
|
||||||
// },
|
// },
|
||||||
// "started": 0.000220584,
|
// "started": 0.00018225,
|
||||||
// "t": 5.001317417,
|
// "t": 5.006185667,
|
||||||
// "failure": "generic_timeout_error",
|
// "failure": "generic_timeout_error", /* <--- */
|
||||||
// "raw_reply": null
|
// "raw_reply": null
|
||||||
// }
|
// }
|
||||||
|
// /* snip */
|
||||||
|
// ],
|
||||||
|
// "queries": [
|
||||||
|
// {
|
||||||
|
// "answers": null,
|
||||||
|
// "engine": "udp",
|
||||||
|
// "failure": "generic_timeout_error", /* <--- */
|
||||||
|
// "hostname": "example.com",
|
||||||
|
// "query_type": "A",
|
||||||
|
// "resolver_address": "182.92.22.222:53",
|
||||||
|
// "t": 5.007385458,
|
||||||
|
// "started": 0.000107583,
|
||||||
|
// "oddity": "dns.lookup.timeout"
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// "answers": null,
|
||||||
|
// "engine": "udp",
|
||||||
|
// "failure": "generic_timeout_error", /* <--- */
|
||||||
|
// "hostname": "example.com",
|
||||||
|
// "query_type": "AAAA",
|
||||||
|
// "resolver_address": "182.92.22.222:53",
|
||||||
|
// "t": 5.007385458,
|
||||||
|
// "started": 0.000107583,
|
||||||
|
// "oddity": "dns.lookup.timeout"
|
||||||
|
// }
|
||||||
// ]
|
// ]
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
//
|
//
|
||||||
// We see that we fail with a timeout (I have marked some of them
|
// We see that we fail with a timeout (I have marked some of them
|
||||||
// with comments inside the JSON). We see the timeout at three different
|
// with comments inside the JSON). We see the timeout at three different
|
||||||
// levels of abstractions (from lower to higher abstraction): at the socket layer,
|
// levels of abstractions (from lower to higher abstraction): at the socket
|
||||||
// during the DNS round trip, during the DNS lookup.
|
// layer (`network_events`), during the DNS round trip (`dns_events`),
|
||||||
|
// during the DNS lookup (`queries`).
|
||||||
//
|
//
|
||||||
// What we also see is that `t`'s value is ~5s when the `read` event
|
// What we also see is that `t`'s value is ~5s when the `read` event
|
||||||
// fails, which tells us about the socket's read timeout.
|
// fails, which tells us about the socket's read timeout.
|
||||||
|
@ -452,18 +436,17 @@ func main() {
|
||||||
// ```JavaScript
|
// ```JavaScript
|
||||||
// {
|
// {
|
||||||
// "domain": "example.com",
|
// "domain": "example.com",
|
||||||
// "connect": [ /* snip */ ],
|
|
||||||
//
|
//
|
||||||
// // The I/O events look normal this time
|
// // The network events look normal this time
|
||||||
// "read_write": [
|
// "network_events": [
|
||||||
// {
|
// {
|
||||||
// "address": "180.97.36.63:53",
|
// "address": "180.97.36.63:53",
|
||||||
// "failure": null,
|
// "failure": null,
|
||||||
// "num_bytes": 29,
|
// "num_bytes": 29,
|
||||||
// "operation": "write",
|
// "operation": "write",
|
||||||
// "proto": "udp",
|
// "proto": "udp",
|
||||||
// "t": 0.000333583,
|
// "t": 0.000492125,
|
||||||
// "started": 0.000312125,
|
// "started": 0.000467042,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -472,8 +455,8 @@ func main() {
|
||||||
// "num_bytes": 29,
|
// "num_bytes": 29,
|
||||||
// "operation": "read",
|
// "operation": "read",
|
||||||
// "proto": "udp",
|
// "proto": "udp",
|
||||||
// "t": 0.334948125,
|
// "t": 0.321373542,
|
||||||
// "started": 0.000366625,
|
// "started": 0.000504833,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -482,8 +465,8 @@ func main() {
|
||||||
// "num_bytes": 29,
|
// "num_bytes": 29,
|
||||||
// "operation": "write",
|
// "operation": "write",
|
||||||
// "proto": "udp",
|
// "proto": "udp",
|
||||||
// "t": 0.3358025,
|
// "t": 0.322500875,
|
||||||
// "started": 0.335725958,
|
// "started": 0.322450042,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -492,15 +475,49 @@ func main() {
|
||||||
// "num_bytes": 29,
|
// "num_bytes": 29,
|
||||||
// "operation": "read",
|
// "operation": "read",
|
||||||
// "proto": "udp",
|
// "proto": "udp",
|
||||||
// "t": 0.739987666,
|
// "t": 0.655514542,
|
||||||
// "started": 0.335863875,
|
// "started": 0.322557667,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// }
|
// }
|
||||||
// ],
|
// ],
|
||||||
//
|
//
|
||||||
// // But we see both in the error and in the oddity
|
// // Exercise: do like I did before and decode the messages
|
||||||
|
// "dns_events": [
|
||||||
|
// {
|
||||||
|
// "engine": "udp",
|
||||||
|
// "resolver_address": "180.97.36.63:53",
|
||||||
|
// "raw_query": {
|
||||||
|
// "data": "WcgBAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE=",
|
||||||
|
// "format": "base64"
|
||||||
|
// },
|
||||||
|
// "started": 0.000209875,
|
||||||
|
// "t": 0.321504042,
|
||||||
|
// "failure": null,
|
||||||
|
// "raw_reply": {
|
||||||
|
// "data": "WciBBQABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE=",
|
||||||
|
// "format": "base64"
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// "engine": "udp",
|
||||||
|
// "resolver_address": "180.97.36.63:53",
|
||||||
|
// "raw_query": {
|
||||||
|
// "data": "I9oBAAABAAAAAAAAB2V4YW1wbGUDY29tAAAcAAE=",
|
||||||
|
// "format": "base64"
|
||||||
|
// },
|
||||||
|
// "started": 0.321672042,
|
||||||
|
// "t": 0.655680792,
|
||||||
|
// "failure": null,
|
||||||
|
// "raw_reply": {
|
||||||
|
// "data": "I9qBBQABAAAAAAAAB2V4YW1wbGUDY29tAAAcAAE=",
|
||||||
|
// "format": "base64"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ],
|
||||||
|
//
|
||||||
|
// // We see both in the error and in the oddity
|
||||||
// // that the response was "REFUSED"
|
// // that the response was "REFUSED"
|
||||||
// "lookup_host": [
|
// "queries": [
|
||||||
// {
|
// {
|
||||||
// "answers": null,
|
// "answers": null,
|
||||||
// "engine": "udp",
|
// "engine": "udp",
|
||||||
|
@ -508,8 +525,8 @@ func main() {
|
||||||
// "hostname": "example.com",
|
// "hostname": "example.com",
|
||||||
// "query_type": "A",
|
// "query_type": "A",
|
||||||
// "resolver_address": "180.97.36.63:53",
|
// "resolver_address": "180.97.36.63:53",
|
||||||
// "t": 0.7402975,
|
// "t": 0.655814875,
|
||||||
// "started": 7.2291e-05,
|
// "started": 0.000107417,
|
||||||
// "oddity": "dns.lookup.refused"
|
// "oddity": "dns.lookup.refused"
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -519,44 +536,10 @@ func main() {
|
||||||
// "hostname": "example.com",
|
// "hostname": "example.com",
|
||||||
// "query_type": "AAAA",
|
// "query_type": "AAAA",
|
||||||
// "resolver_address": "180.97.36.63:53",
|
// "resolver_address": "180.97.36.63:53",
|
||||||
// "t": 0.7402975,
|
// "t": 0.655814875,
|
||||||
// "started": 7.2291e-05,
|
// "started": 0.000107417,
|
||||||
// "oddity": "dns.lookup.refused"
|
// "oddity": "dns.lookup.refused"
|
||||||
// }
|
// }
|
||||||
// ],
|
|
||||||
//
|
|
||||||
// // Exercise: do like I did before and decode the messages
|
|
||||||
// "dns_round_trip": [
|
|
||||||
// {
|
|
||||||
// "engine": "udp",
|
|
||||||
// "resolver_address": "180.97.36.63:53",
|
|
||||||
// "raw_query": {
|
|
||||||
// "data": "crkBAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE=",
|
|
||||||
// "format": "base64"
|
|
||||||
// },
|
|
||||||
// "started": 0.000130666,
|
|
||||||
// "t": 0.33509925,
|
|
||||||
// "failure": null,
|
|
||||||
// "raw_reply": {
|
|
||||||
// "data": "crmBBQABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE=",
|
|
||||||
// "format": "base64"
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// "engine": "udp",
|
|
||||||
// "resolver_address": "180.97.36.63:53",
|
|
||||||
// "raw_query": {
|
|
||||||
// "data": "ywcBAAABAAAAAAAAB2V4YW1wbGUDY29tAAAcAAE=",
|
|
||||||
// "format": "base64"
|
|
||||||
// },
|
|
||||||
// "started": 0.335321333,
|
|
||||||
// "t": 0.740152375,
|
|
||||||
// "failure": null,
|
|
||||||
// "raw_reply": {
|
|
||||||
// "data": "yweBBQABAAAAAAAAB2V4YW1wbGUDY29tAAAcAAE=",
|
|
||||||
// "format": "base64"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// ]
|
// ]
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
|
|
|
@ -69,7 +69,7 @@ As usual, the method to perform a measurement returns
|
||||||
the measurement itself, which we print below.
|
the measurement itself, which we print below.
|
||||||
|
|
||||||
```
|
```
|
||||||
data, err := json.Marshal(m)
|
data, err := json.Marshal(measurex.NewArchivalEndpointMeasurement(m))
|
||||||
runtimex.PanicOnError(err, "json.Marshal failed")
|
runtimex.PanicOnError(err, "json.Marshal failed")
|
||||||
fmt.Printf("%s\n", string(data))
|
fmt.Printf("%s\n", string(data))
|
||||||
}
|
}
|
||||||
|
@ -90,30 +90,16 @@ Let us comment the JSON in detail:
|
||||||
"network": "tcp",
|
"network": "tcp",
|
||||||
"address": "8.8.4.4:443",
|
"address": "8.8.4.4:443",
|
||||||
|
|
||||||
// This block is generated when connecting to a TCP
|
|
||||||
// socket, as we've already seen in chapter02
|
|
||||||
"connect": [
|
|
||||||
{
|
|
||||||
"address": "8.8.4.4:443",
|
|
||||||
"failure": null,
|
|
||||||
"operation": "connect",
|
|
||||||
"proto": "tcp",
|
|
||||||
"t": 0.046959084,
|
|
||||||
"started": 0.022998875,
|
|
||||||
"oddity": ""
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
// These are the I/O events during the handshake
|
// These are the I/O events during the handshake
|
||||||
"read_write": [
|
"network_events": [
|
||||||
{
|
{
|
||||||
"address": "8.8.4.4:443",
|
"address": "8.8.4.4:443",
|
||||||
"failure": null,
|
"failure": null,
|
||||||
"num_bytes": 280,
|
"num_bytes": 280,
|
||||||
"operation": "write",
|
"operation": "write",
|
||||||
"proto": "tcp",
|
"proto": "tcp",
|
||||||
"t": 0.048752875,
|
"t": 0.048268333,
|
||||||
"started": 0.04874125,
|
"started": 0.048246666,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -122,18 +108,18 @@ Let us comment the JSON in detail:
|
||||||
"num_bytes": 517,
|
"num_bytes": 517,
|
||||||
"operation": "read",
|
"operation": "read",
|
||||||
"proto": "tcp",
|
"proto": "tcp",
|
||||||
"t": 0.087221334,
|
"t": 0.086214708,
|
||||||
"started": 0.048760417,
|
"started": 0.048287791,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "8.8.4.4:443",
|
"address": "8.8.4.4:443",
|
||||||
"failure": null,
|
"failure": null,
|
||||||
"num_bytes": 4301,
|
"num_bytes": 4303,
|
||||||
"operation": "read",
|
"operation": "read",
|
||||||
"proto": "tcp",
|
"proto": "tcp",
|
||||||
"t": 0.088843584,
|
"t": 0.087951708,
|
||||||
"started": 0.088830959,
|
"started": 0.08792725,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -142,14 +128,33 @@ Let us comment the JSON in detail:
|
||||||
"num_bytes": 64,
|
"num_bytes": 64,
|
||||||
"operation": "write",
|
"operation": "write",
|
||||||
"proto": "tcp",
|
"proto": "tcp",
|
||||||
"t": 0.092078042,
|
"t": 0.090097833,
|
||||||
"started": 0.092064042,
|
"started": 0.090083875,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
// This block contains information about the handshake
|
// This block is generated when connecting to a TCP
|
||||||
"tls_handshake": [
|
// socket, as we've already seen in chapter02
|
||||||
|
"tcp_connect": [
|
||||||
|
{
|
||||||
|
"ip": "8.8.4.4",
|
||||||
|
"port": 443,
|
||||||
|
"t": 0.046022583,
|
||||||
|
"status": {
|
||||||
|
"blocked": false,
|
||||||
|
"failure": null,
|
||||||
|
"success": true
|
||||||
|
},
|
||||||
|
"started": 0.024424916,
|
||||||
|
"oddity": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// This block contains information about the handshake.
|
||||||
|
//
|
||||||
|
// See https://github.com/ooni/spec/blob/master/data-formats/df-006-tlshandshake.md
|
||||||
|
"tls_handshakes": [
|
||||||
{
|
{
|
||||||
"cipher_suite": "TLS_AES_128_GCM_SHA256",
|
"cipher_suite": "TLS_AES_128_GCM_SHA256",
|
||||||
"failure": null,
|
"failure": null,
|
||||||
|
@ -157,7 +162,7 @@ Let us comment the JSON in detail:
|
||||||
"tls_version": "TLSv1.3",
|
"tls_version": "TLSv1.3",
|
||||||
"peer_certificates": [
|
"peer_certificates": [
|
||||||
{
|
{
|
||||||
"data": "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",
|
"data": "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",
|
||||||
"format": "base64"
|
"format": "base64"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -169,7 +174,7 @@ Let us comment the JSON in detail:
|
||||||
"format": "base64"
|
"format": "base64"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"t": 0.092117709,
|
"t": 0.090150666,
|
||||||
"address": "8.8.4.4:443",
|
"address": "8.8.4.4:443",
|
||||||
"server_name": "dns.google",
|
"server_name": "dns.google",
|
||||||
"alpn": [
|
"alpn": [
|
||||||
|
@ -179,16 +184,12 @@ Let us comment the JSON in detail:
|
||||||
"no_tls_verify": false,
|
"no_tls_verify": false,
|
||||||
"oddity": "",
|
"oddity": "",
|
||||||
"proto": "tcp",
|
"proto": "tcp",
|
||||||
"started": 0.047288542
|
"started": 0.04635975
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
All the data formats we're using here are, by the way,
|
|
||||||
compatible with the data formats specified at
|
|
||||||
https://github.com/ooni/spec/tree/master/data-formats.
|
|
||||||
|
|
||||||
### Suggested follow-up experiments
|
### Suggested follow-up experiments
|
||||||
|
|
||||||
Try to run experiments in the following scenarios, and
|
Try to run experiments in the following scenarios, and
|
||||||
|
|
|
@ -70,7 +70,7 @@ func main() {
|
||||||
// the measurement itself, which we print below.
|
// the measurement itself, which we print below.
|
||||||
//
|
//
|
||||||
// ```
|
// ```
|
||||||
data, err := json.Marshal(m)
|
data, err := json.Marshal(measurex.NewArchivalEndpointMeasurement(m))
|
||||||
runtimex.PanicOnError(err, "json.Marshal failed")
|
runtimex.PanicOnError(err, "json.Marshal failed")
|
||||||
fmt.Printf("%s\n", string(data))
|
fmt.Printf("%s\n", string(data))
|
||||||
}
|
}
|
||||||
|
@ -91,30 +91,16 @@ func main() {
|
||||||
// "network": "tcp",
|
// "network": "tcp",
|
||||||
// "address": "8.8.4.4:443",
|
// "address": "8.8.4.4:443",
|
||||||
//
|
//
|
||||||
// // This block is generated when connecting to a TCP
|
|
||||||
// // socket, as we've already seen in chapter02
|
|
||||||
// "connect": [
|
|
||||||
// {
|
|
||||||
// "address": "8.8.4.4:443",
|
|
||||||
// "failure": null,
|
|
||||||
// "operation": "connect",
|
|
||||||
// "proto": "tcp",
|
|
||||||
// "t": 0.046959084,
|
|
||||||
// "started": 0.022998875,
|
|
||||||
// "oddity": ""
|
|
||||||
// }
|
|
||||||
// ],
|
|
||||||
//
|
|
||||||
// // These are the I/O events during the handshake
|
// // These are the I/O events during the handshake
|
||||||
// "read_write": [
|
// "network_events": [
|
||||||
// {
|
// {
|
||||||
// "address": "8.8.4.4:443",
|
// "address": "8.8.4.4:443",
|
||||||
// "failure": null,
|
// "failure": null,
|
||||||
// "num_bytes": 280,
|
// "num_bytes": 280,
|
||||||
// "operation": "write",
|
// "operation": "write",
|
||||||
// "proto": "tcp",
|
// "proto": "tcp",
|
||||||
// "t": 0.048752875,
|
// "t": 0.048268333,
|
||||||
// "started": 0.04874125,
|
// "started": 0.048246666,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -123,18 +109,18 @@ func main() {
|
||||||
// "num_bytes": 517,
|
// "num_bytes": 517,
|
||||||
// "operation": "read",
|
// "operation": "read",
|
||||||
// "proto": "tcp",
|
// "proto": "tcp",
|
||||||
// "t": 0.087221334,
|
// "t": 0.086214708,
|
||||||
// "started": 0.048760417,
|
// "started": 0.048287791,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
// "address": "8.8.4.4:443",
|
// "address": "8.8.4.4:443",
|
||||||
// "failure": null,
|
// "failure": null,
|
||||||
// "num_bytes": 4301,
|
// "num_bytes": 4303,
|
||||||
// "operation": "read",
|
// "operation": "read",
|
||||||
// "proto": "tcp",
|
// "proto": "tcp",
|
||||||
// "t": 0.088843584,
|
// "t": 0.087951708,
|
||||||
// "started": 0.088830959,
|
// "started": 0.08792725,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -143,14 +129,33 @@ func main() {
|
||||||
// "num_bytes": 64,
|
// "num_bytes": 64,
|
||||||
// "operation": "write",
|
// "operation": "write",
|
||||||
// "proto": "tcp",
|
// "proto": "tcp",
|
||||||
// "t": 0.092078042,
|
// "t": 0.090097833,
|
||||||
// "started": 0.092064042,
|
// "started": 0.090083875,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// }
|
// }
|
||||||
// ],
|
// ],
|
||||||
//
|
//
|
||||||
// // This block contains information about the handshake
|
// // This block is generated when connecting to a TCP
|
||||||
// "tls_handshake": [
|
// // socket, as we've already seen in chapter02
|
||||||
|
// "tcp_connect": [
|
||||||
|
// {
|
||||||
|
// "ip": "8.8.4.4",
|
||||||
|
// "port": 443,
|
||||||
|
// "t": 0.046022583,
|
||||||
|
// "status": {
|
||||||
|
// "blocked": false,
|
||||||
|
// "failure": null,
|
||||||
|
// "success": true
|
||||||
|
// },
|
||||||
|
// "started": 0.024424916,
|
||||||
|
// "oddity": ""
|
||||||
|
// }
|
||||||
|
// ],
|
||||||
|
//
|
||||||
|
// // This block contains information about the handshake.
|
||||||
|
// //
|
||||||
|
// // See https://github.com/ooni/spec/blob/master/data-formats/df-006-tlshandshake.md
|
||||||
|
// "tls_handshakes": [
|
||||||
// {
|
// {
|
||||||
// "cipher_suite": "TLS_AES_128_GCM_SHA256",
|
// "cipher_suite": "TLS_AES_128_GCM_SHA256",
|
||||||
// "failure": null,
|
// "failure": null,
|
||||||
|
@ -158,7 +163,7 @@ func main() {
|
||||||
// "tls_version": "TLSv1.3",
|
// "tls_version": "TLSv1.3",
|
||||||
// "peer_certificates": [
|
// "peer_certificates": [
|
||||||
// {
|
// {
|
||||||
// "data": "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",
|
// "data": "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",
|
||||||
// "format": "base64"
|
// "format": "base64"
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -170,7 +175,7 @@ func main() {
|
||||||
// "format": "base64"
|
// "format": "base64"
|
||||||
// }
|
// }
|
||||||
// ],
|
// ],
|
||||||
// "t": 0.092117709,
|
// "t": 0.090150666,
|
||||||
// "address": "8.8.4.4:443",
|
// "address": "8.8.4.4:443",
|
||||||
// "server_name": "dns.google",
|
// "server_name": "dns.google",
|
||||||
// "alpn": [
|
// "alpn": [
|
||||||
|
@ -180,16 +185,12 @@ func main() {
|
||||||
// "no_tls_verify": false,
|
// "no_tls_verify": false,
|
||||||
// "oddity": "",
|
// "oddity": "",
|
||||||
// "proto": "tcp",
|
// "proto": "tcp",
|
||||||
// "started": 0.047288542
|
// "started": 0.04635975
|
||||||
// }
|
// }
|
||||||
// ]
|
// ]
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
//
|
//
|
||||||
// All the data formats we're using here are, by the way,
|
|
||||||
// compatible with the data formats specified at
|
|
||||||
// https://github.com/ooni/spec/tree/master/data-formats.
|
|
||||||
//
|
|
||||||
// ### Suggested follow-up experiments
|
// ### Suggested follow-up experiments
|
||||||
//
|
//
|
||||||
// Try to run experiments in the following scenarios, and
|
// Try to run experiments in the following scenarios, and
|
||||||
|
|
|
@ -69,7 +69,7 @@ As we did in the previous chapters, here's the usual three
|
||||||
lines of code for printing the resulting measurement.
|
lines of code for printing the resulting measurement.
|
||||||
|
|
||||||
```
|
```
|
||||||
data, err := json.Marshal(m)
|
data, err := json.Marshal(measurex.NewArchivalEndpointMeasurement(m))
|
||||||
runtimex.PanicOnError(err, "json.Marshal failed")
|
runtimex.PanicOnError(err, "json.Marshal failed")
|
||||||
fmt.Printf("%s\n", string(data))
|
fmt.Printf("%s\n", string(data))
|
||||||
}
|
}
|
||||||
|
@ -100,15 +100,15 @@ Produces this JSON:
|
||||||
// are actually `recvfrom` and `sendto` but here
|
// are actually `recvfrom` and `sendto` but here
|
||||||
// we follow the Go convention of using read/write
|
// we follow the Go convention of using read/write
|
||||||
// more frequently than send/recv.)
|
// more frequently than send/recv.)
|
||||||
"read_write": [
|
"network_events": [
|
||||||
{
|
{
|
||||||
"address": "8.8.4.4:443",
|
"address": "8.8.4.4:443",
|
||||||
"failure": null,
|
"failure": null,
|
||||||
"num_bytes": 1252,
|
"num_bytes": 1252,
|
||||||
"operation": "write_to",
|
"operation": "write_to",
|
||||||
"proto": "quic",
|
"proto": "quic",
|
||||||
"t": 0.003903167,
|
"t": 0.027184208,
|
||||||
"started": 0.0037395,
|
"started": 0.027127208,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -117,8 +117,8 @@ Produces this JSON:
|
||||||
"num_bytes": 1252,
|
"num_bytes": 1252,
|
||||||
"operation": "read_from",
|
"operation": "read_from",
|
||||||
"proto": "quic",
|
"proto": "quic",
|
||||||
"t": 0.029389125,
|
"t": 0.053116458,
|
||||||
"started": 0.002954792,
|
"started": 0.025626583,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -127,8 +127,8 @@ Produces this JSON:
|
||||||
"num_bytes": 1252,
|
"num_bytes": 1252,
|
||||||
"operation": "write_to",
|
"operation": "write_to",
|
||||||
"proto": "quic",
|
"proto": "quic",
|
||||||
"t": 0.029757584,
|
"t": 0.054538792,
|
||||||
"started": 0.02972325,
|
"started": 0.054517542,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -137,8 +137,8 @@ Produces this JSON:
|
||||||
"num_bytes": 1252,
|
"num_bytes": 1252,
|
||||||
"operation": "read_from",
|
"operation": "read_from",
|
||||||
"proto": "quic",
|
"proto": "quic",
|
||||||
"t": 0.045039875,
|
"t": 0.069144958,
|
||||||
"started": 0.029424792,
|
"started": 0.053194208,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -147,8 +147,8 @@ Produces this JSON:
|
||||||
"num_bytes": 1252,
|
"num_bytes": 1252,
|
||||||
"operation": "read_from",
|
"operation": "read_from",
|
||||||
"proto": "quic",
|
"proto": "quic",
|
||||||
"t": 0.045055334,
|
"t": 0.069183458,
|
||||||
"started": 0.045049625,
|
"started": 0.069173292,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -157,28 +157,28 @@ Produces this JSON:
|
||||||
"num_bytes": 1252,
|
"num_bytes": 1252,
|
||||||
"operation": "read_from",
|
"operation": "read_from",
|
||||||
"proto": "quic",
|
"proto": "quic",
|
||||||
"t": 0.045073917,
|
"t": 0.06920225,
|
||||||
"started": 0.045069667,
|
"started": 0.069197875,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "8.8.4.4:443",
|
"address": "8.8.4.4:443",
|
||||||
"failure": null,
|
"failure": null,
|
||||||
"num_bytes": 1233,
|
"num_bytes": 1216,
|
||||||
"operation": "read_from",
|
"operation": "read_from",
|
||||||
"proto": "quic",
|
"proto": "quic",
|
||||||
"t": 0.04508,
|
"t": 0.069210958,
|
||||||
"started": 0.045075292,
|
"started": 0.069206875,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "8.8.4.4:443",
|
"address": "8.8.4.4:443",
|
||||||
"failure": null,
|
"failure": null,
|
||||||
"num_bytes": 64,
|
"num_bytes": 65,
|
||||||
"operation": "read_from",
|
"operation": "read_from",
|
||||||
"proto": "quic",
|
"proto": "quic",
|
||||||
"t": 0.045088167,
|
"t": 0.069220667,
|
||||||
"started": 0.045081167,
|
"started": 0.069217375,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -187,8 +187,8 @@ Produces this JSON:
|
||||||
"num_bytes": 44,
|
"num_bytes": 44,
|
||||||
"operation": "write_to",
|
"operation": "write_to",
|
||||||
"proto": "quic",
|
"proto": "quic",
|
||||||
"t": 0.045370417,
|
"t": 0.069433417,
|
||||||
"started": 0.045338667,
|
"started": 0.069417625,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -197,8 +197,8 @@ Produces this JSON:
|
||||||
"num_bytes": 44,
|
"num_bytes": 44,
|
||||||
"operation": "write_to",
|
"operation": "write_to",
|
||||||
"proto": "quic",
|
"proto": "quic",
|
||||||
"t": 0.045392125,
|
"t": 0.069677625,
|
||||||
"started": 0.045380959,
|
"started": 0.069647458,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -207,8 +207,8 @@ Produces this JSON:
|
||||||
"num_bytes": 83,
|
"num_bytes": 83,
|
||||||
"operation": "write_to",
|
"operation": "write_to",
|
||||||
"proto": "quic",
|
"proto": "quic",
|
||||||
"t": 0.047042542,
|
"t": 0.073461917,
|
||||||
"started": 0.047001917,
|
"started": 0.073432875,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -217,15 +217,15 @@ Produces this JSON:
|
||||||
"num_bytes": 33,
|
"num_bytes": 33,
|
||||||
"operation": "write_to",
|
"operation": "write_to",
|
||||||
"proto": "quic",
|
"proto": "quic",
|
||||||
"t": 0.047060834,
|
"t": 0.073559417,
|
||||||
"started": 0.047046875,
|
"started": 0.073542542,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
// This section describes the QUIC handshake and it has
|
// This section describes the QUIC handshake and it has
|
||||||
// basically the same fields as the TLS handshake.
|
// basically the same fields as the TLS handshake.
|
||||||
"quic_handshake": [
|
"quic_handshakes": [
|
||||||
{
|
{
|
||||||
"cipher_suite": "TLS_CHACHA20_POLY1305_SHA256",
|
"cipher_suite": "TLS_CHACHA20_POLY1305_SHA256",
|
||||||
"failure": null,
|
"failure": null,
|
||||||
|
@ -233,7 +233,7 @@ Produces this JSON:
|
||||||
"tls_version": "TLSv1.3",
|
"tls_version": "TLSv1.3",
|
||||||
"peer_certificates": [
|
"peer_certificates": [
|
||||||
{
|
{
|
||||||
"data": "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",
|
"data": "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",
|
||||||
"format": "base64"
|
"format": "base64"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -245,7 +245,7 @@ Produces this JSON:
|
||||||
"format": "base64"
|
"format": "base64"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"t": 0.047042459,
|
"t": 0.073469208,
|
||||||
"address": "8.8.4.4:443",
|
"address": "8.8.4.4:443",
|
||||||
"server_name": "dns.google",
|
"server_name": "dns.google",
|
||||||
"alpn": [
|
"alpn": [
|
||||||
|
@ -254,7 +254,7 @@ Produces this JSON:
|
||||||
"no_tls_verify": false,
|
"no_tls_verify": false,
|
||||||
"oddity": "",
|
"oddity": "",
|
||||||
"proto": "quic",
|
"proto": "quic",
|
||||||
"started": 0.002154834
|
"started": 0.025061583
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ func main() {
|
||||||
// lines of code for printing the resulting measurement.
|
// lines of code for printing the resulting measurement.
|
||||||
//
|
//
|
||||||
// ```
|
// ```
|
||||||
data, err := json.Marshal(m)
|
data, err := json.Marshal(measurex.NewArchivalEndpointMeasurement(m))
|
||||||
runtimex.PanicOnError(err, "json.Marshal failed")
|
runtimex.PanicOnError(err, "json.Marshal failed")
|
||||||
fmt.Printf("%s\n", string(data))
|
fmt.Printf("%s\n", string(data))
|
||||||
}
|
}
|
||||||
|
@ -101,15 +101,15 @@ func main() {
|
||||||
// // are actually `recvfrom` and `sendto` but here
|
// // are actually `recvfrom` and `sendto` but here
|
||||||
// // we follow the Go convention of using read/write
|
// // we follow the Go convention of using read/write
|
||||||
// // more frequently than send/recv.)
|
// // more frequently than send/recv.)
|
||||||
// "read_write": [
|
// "network_events": [
|
||||||
// {
|
// {
|
||||||
// "address": "8.8.4.4:443",
|
// "address": "8.8.4.4:443",
|
||||||
// "failure": null,
|
// "failure": null,
|
||||||
// "num_bytes": 1252,
|
// "num_bytes": 1252,
|
||||||
// "operation": "write_to",
|
// "operation": "write_to",
|
||||||
// "proto": "quic",
|
// "proto": "quic",
|
||||||
// "t": 0.003903167,
|
// "t": 0.027184208,
|
||||||
// "started": 0.0037395,
|
// "started": 0.027127208,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -118,8 +118,8 @@ func main() {
|
||||||
// "num_bytes": 1252,
|
// "num_bytes": 1252,
|
||||||
// "operation": "read_from",
|
// "operation": "read_from",
|
||||||
// "proto": "quic",
|
// "proto": "quic",
|
||||||
// "t": 0.029389125,
|
// "t": 0.053116458,
|
||||||
// "started": 0.002954792,
|
// "started": 0.025626583,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -128,8 +128,8 @@ func main() {
|
||||||
// "num_bytes": 1252,
|
// "num_bytes": 1252,
|
||||||
// "operation": "write_to",
|
// "operation": "write_to",
|
||||||
// "proto": "quic",
|
// "proto": "quic",
|
||||||
// "t": 0.029757584,
|
// "t": 0.054538792,
|
||||||
// "started": 0.02972325,
|
// "started": 0.054517542,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -138,8 +138,8 @@ func main() {
|
||||||
// "num_bytes": 1252,
|
// "num_bytes": 1252,
|
||||||
// "operation": "read_from",
|
// "operation": "read_from",
|
||||||
// "proto": "quic",
|
// "proto": "quic",
|
||||||
// "t": 0.045039875,
|
// "t": 0.069144958,
|
||||||
// "started": 0.029424792,
|
// "started": 0.053194208,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -148,8 +148,8 @@ func main() {
|
||||||
// "num_bytes": 1252,
|
// "num_bytes": 1252,
|
||||||
// "operation": "read_from",
|
// "operation": "read_from",
|
||||||
// "proto": "quic",
|
// "proto": "quic",
|
||||||
// "t": 0.045055334,
|
// "t": 0.069183458,
|
||||||
// "started": 0.045049625,
|
// "started": 0.069173292,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -158,28 +158,28 @@ func main() {
|
||||||
// "num_bytes": 1252,
|
// "num_bytes": 1252,
|
||||||
// "operation": "read_from",
|
// "operation": "read_from",
|
||||||
// "proto": "quic",
|
// "proto": "quic",
|
||||||
// "t": 0.045073917,
|
// "t": 0.06920225,
|
||||||
// "started": 0.045069667,
|
// "started": 0.069197875,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
// "address": "8.8.4.4:443",
|
// "address": "8.8.4.4:443",
|
||||||
// "failure": null,
|
// "failure": null,
|
||||||
// "num_bytes": 1233,
|
// "num_bytes": 1216,
|
||||||
// "operation": "read_from",
|
// "operation": "read_from",
|
||||||
// "proto": "quic",
|
// "proto": "quic",
|
||||||
// "t": 0.04508,
|
// "t": 0.069210958,
|
||||||
// "started": 0.045075292,
|
// "started": 0.069206875,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
// "address": "8.8.4.4:443",
|
// "address": "8.8.4.4:443",
|
||||||
// "failure": null,
|
// "failure": null,
|
||||||
// "num_bytes": 64,
|
// "num_bytes": 65,
|
||||||
// "operation": "read_from",
|
// "operation": "read_from",
|
||||||
// "proto": "quic",
|
// "proto": "quic",
|
||||||
// "t": 0.045088167,
|
// "t": 0.069220667,
|
||||||
// "started": 0.045081167,
|
// "started": 0.069217375,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -188,8 +188,8 @@ func main() {
|
||||||
// "num_bytes": 44,
|
// "num_bytes": 44,
|
||||||
// "operation": "write_to",
|
// "operation": "write_to",
|
||||||
// "proto": "quic",
|
// "proto": "quic",
|
||||||
// "t": 0.045370417,
|
// "t": 0.069433417,
|
||||||
// "started": 0.045338667,
|
// "started": 0.069417625,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -198,8 +198,8 @@ func main() {
|
||||||
// "num_bytes": 44,
|
// "num_bytes": 44,
|
||||||
// "operation": "write_to",
|
// "operation": "write_to",
|
||||||
// "proto": "quic",
|
// "proto": "quic",
|
||||||
// "t": 0.045392125,
|
// "t": 0.069677625,
|
||||||
// "started": 0.045380959,
|
// "started": 0.069647458,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -208,8 +208,8 @@ func main() {
|
||||||
// "num_bytes": 83,
|
// "num_bytes": 83,
|
||||||
// "operation": "write_to",
|
// "operation": "write_to",
|
||||||
// "proto": "quic",
|
// "proto": "quic",
|
||||||
// "t": 0.047042542,
|
// "t": 0.073461917,
|
||||||
// "started": 0.047001917,
|
// "started": 0.073432875,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -218,15 +218,15 @@ func main() {
|
||||||
// "num_bytes": 33,
|
// "num_bytes": 33,
|
||||||
// "operation": "write_to",
|
// "operation": "write_to",
|
||||||
// "proto": "quic",
|
// "proto": "quic",
|
||||||
// "t": 0.047060834,
|
// "t": 0.073559417,
|
||||||
// "started": 0.047046875,
|
// "started": 0.073542542,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// }
|
// }
|
||||||
// ],
|
// ],
|
||||||
//
|
//
|
||||||
// // This section describes the QUIC handshake and it has
|
// // This section describes the QUIC handshake and it has
|
||||||
// // basically the same fields as the TLS handshake.
|
// // basically the same fields as the TLS handshake.
|
||||||
// "quic_handshake": [
|
// "quic_handshakes": [
|
||||||
// {
|
// {
|
||||||
// "cipher_suite": "TLS_CHACHA20_POLY1305_SHA256",
|
// "cipher_suite": "TLS_CHACHA20_POLY1305_SHA256",
|
||||||
// "failure": null,
|
// "failure": null,
|
||||||
|
@ -234,7 +234,7 @@ func main() {
|
||||||
// "tls_version": "TLSv1.3",
|
// "tls_version": "TLSv1.3",
|
||||||
// "peer_certificates": [
|
// "peer_certificates": [
|
||||||
// {
|
// {
|
||||||
// "data": "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",
|
// "data": "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",
|
||||||
// "format": "base64"
|
// "format": "base64"
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -246,7 +246,7 @@ func main() {
|
||||||
// "format": "base64"
|
// "format": "base64"
|
||||||
// }
|
// }
|
||||||
// ],
|
// ],
|
||||||
// "t": 0.047042459,
|
// "t": 0.073469208,
|
||||||
// "address": "8.8.4.4:443",
|
// "address": "8.8.4.4:443",
|
||||||
// "server_name": "dns.google",
|
// "server_name": "dns.google",
|
||||||
// "alpn": [
|
// "alpn": [
|
||||||
|
@ -255,7 +255,7 @@ func main() {
|
||||||
// "no_tls_verify": false,
|
// "no_tls_verify": false,
|
||||||
// "oddity": "",
|
// "oddity": "",
|
||||||
// "proto": "quic",
|
// "proto": "quic",
|
||||||
// "started": 0.002154834
|
// "started": 0.025061583
|
||||||
// }
|
// }
|
||||||
// ]
|
// ]
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -172,7 +172,7 @@ go doc ./internal/measurex.HTTPEndpointMeasurement
|
||||||
Let us now print the resulting measurement.
|
Let us now print the resulting measurement.
|
||||||
|
|
||||||
```Go
|
```Go
|
||||||
print(m)
|
print(measurex.NewArchivalHTTPEndpointMeasurement(m))
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -182,7 +182,7 @@ Let us now print the resulting measurement.
|
||||||
Let us perform a vanilla run first:
|
Let us perform a vanilla run first:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go run -race ./internal/tutorial/measurex/chapter06
|
go run -race ./internal/tutorial/measurex/chapter06 | jq
|
||||||
```
|
```
|
||||||
|
|
||||||
This is the JSON output. Let us comment it in detail:
|
This is the JSON output. Let us comment it in detail:
|
||||||
|
@ -196,31 +196,17 @@ This is the JSON output. Let us comment it in detail:
|
||||||
"network": "tcp",
|
"network": "tcp",
|
||||||
"address": "8.8.4.4:443",
|
"address": "8.8.4.4:443",
|
||||||
|
|
||||||
// Internally, HTTPEndpointGetWithoutCookies calls
|
|
||||||
// TCPConnect and here we see the corresponding event
|
|
||||||
"connect": [
|
|
||||||
{
|
|
||||||
"address": "8.8.4.4:443",
|
|
||||||
"failure": null,
|
|
||||||
"operation": "connect",
|
|
||||||
"proto": "tcp",
|
|
||||||
"t": 0.02422375,
|
|
||||||
"started": 0.002269291,
|
|
||||||
"oddity": ""
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
// These are the I/O operations we have already seen
|
// These are the I/O operations we have already seen
|
||||||
// in previous chapters
|
// in previous chapters
|
||||||
"read_write": [
|
"network_events": [
|
||||||
{
|
{
|
||||||
"address": "8.8.4.4:443",
|
"address": "8.8.4.4:443",
|
||||||
"failure": null,
|
"failure": null,
|
||||||
"num_bytes": 280,
|
"num_bytes": 280,
|
||||||
"operation": "write",
|
"operation": "write",
|
||||||
"proto": "tcp",
|
"proto": "tcp",
|
||||||
"t": 0.024931791,
|
"t": 0.045800292,
|
||||||
"started": 0.024910416,
|
"started": 0.045782167,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -229,18 +215,18 @@ This is the JSON output. Let us comment it in detail:
|
||||||
"num_bytes": 517,
|
"num_bytes": 517,
|
||||||
"operation": "read",
|
"operation": "read",
|
||||||
"proto": "tcp",
|
"proto": "tcp",
|
||||||
"t": 0.063629791,
|
"t": 0.082571,
|
||||||
"started": 0.024935666,
|
"started": 0.045805458,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "8.8.4.4:443",
|
"address": "8.8.4.4:443",
|
||||||
"failure": null,
|
"failure": null,
|
||||||
"num_bytes": 4301,
|
"num_bytes": 4303,
|
||||||
"operation": "read",
|
"operation": "read",
|
||||||
"proto": "tcp",
|
"proto": "tcp",
|
||||||
"t": 0.064183,
|
"t": 0.084400542,
|
||||||
"started": 0.064144208,
|
"started": 0.084372667,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -249,8 +235,8 @@ This is the JSON output. Let us comment it in detail:
|
||||||
"num_bytes": 64,
|
"num_bytes": 64,
|
||||||
"operation": "write",
|
"operation": "write",
|
||||||
"proto": "tcp",
|
"proto": "tcp",
|
||||||
"t": 0.065464041,
|
"t": 0.086762625,
|
||||||
"started": 0.065441333,
|
"started": 0.086748292,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -259,8 +245,8 @@ This is the JSON output. Let us comment it in detail:
|
||||||
"num_bytes": 86,
|
"num_bytes": 86,
|
||||||
"operation": "write",
|
"operation": "write",
|
||||||
"proto": "tcp",
|
"proto": "tcp",
|
||||||
"t": 0.067256083,
|
"t": 0.087851,
|
||||||
"started": 0.067224375,
|
"started": 0.087837625,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -269,8 +255,8 @@ This is the JSON output. Let us comment it in detail:
|
||||||
"num_bytes": 201,
|
"num_bytes": 201,
|
||||||
"operation": "write",
|
"operation": "write",
|
||||||
"proto": "tcp",
|
"proto": "tcp",
|
||||||
"t": 0.067674416,
|
"t": 0.089527292,
|
||||||
"started": 0.067652375,
|
"started": 0.089507958,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -279,8 +265,8 @@ This is the JSON output. Let us comment it in detail:
|
||||||
"num_bytes": 93,
|
"num_bytes": 93,
|
||||||
"operation": "read",
|
"operation": "read",
|
||||||
"proto": "tcp",
|
"proto": "tcp",
|
||||||
"t": 0.086618708,
|
"t": 0.168585625,
|
||||||
"started": 0.067599208,
|
"started": 0.088068375,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -289,18 +275,28 @@ This is the JSON output. Let us comment it in detail:
|
||||||
"num_bytes": 31,
|
"num_bytes": 31,
|
||||||
"operation": "write",
|
"operation": "write",
|
||||||
"proto": "tcp",
|
"proto": "tcp",
|
||||||
"t": 0.086703625,
|
"t": 0.168713542,
|
||||||
"started": 0.0866745,
|
"started": 0.168671417,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "8.8.4.4:443",
|
"address": "8.8.4.4:443",
|
||||||
"failure": null,
|
"failure": null,
|
||||||
"num_bytes": 2028,
|
"num_bytes": 2000,
|
||||||
"operation": "read",
|
"operation": "read",
|
||||||
"proto": "tcp",
|
"proto": "tcp",
|
||||||
"t": 0.337785916,
|
"t": 0.468671417,
|
||||||
"started": 0.086717333,
|
"started": 0.168759333,
|
||||||
|
"oddity": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "8.8.4.4:443",
|
||||||
|
"failure": null,
|
||||||
|
"num_bytes": 39,
|
||||||
|
"operation": "read",
|
||||||
|
"proto": "tcp",
|
||||||
|
"t": 0.47118175,
|
||||||
|
"started": 0.471169667,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -309,8 +305,8 @@ This is the JSON output. Let us comment it in detail:
|
||||||
"num_bytes": 39,
|
"num_bytes": 39,
|
||||||
"operation": "write",
|
"operation": "write",
|
||||||
"proto": "tcp",
|
"proto": "tcp",
|
||||||
"t": 0.338514916,
|
"t": 0.471335458,
|
||||||
"started": 0.338485375,
|
"started": 0.471268583,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -319,17 +315,25 @@ This is the JSON output. Let us comment it in detail:
|
||||||
"num_bytes": 24,
|
"num_bytes": 24,
|
||||||
"operation": "write",
|
"operation": "write",
|
||||||
"proto": "tcp",
|
"proto": "tcp",
|
||||||
"t": 0.338800833,
|
"t": 0.471865,
|
||||||
"started": 0.338788625,
|
"started": 0.471836292,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
},
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// Internally, HTTPEndpointGetWithoutCookies calls
|
||||||
|
// TCPConnect and here we see the corresponding event
|
||||||
|
"tcp_connect": [
|
||||||
{
|
{
|
||||||
"address": "8.8.4.4:443",
|
"ip": "8.8.4.4",
|
||||||
"failure": "connection_already_closed",
|
"port": 443,
|
||||||
"operation": "read",
|
"t": 0.043644958,
|
||||||
"proto": "tcp",
|
"status": {
|
||||||
"t": 0.338888041,
|
"blocked": false,
|
||||||
"started": 0.338523291,
|
"failure": null,
|
||||||
|
"success": true
|
||||||
|
},
|
||||||
|
"started": 0.022849458,
|
||||||
"oddity": ""
|
"oddity": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -339,7 +343,7 @@ This is the JSON output. Let us comment it in detail:
|
||||||
// specified a QUIC endpoint we would instead see here a
|
// specified a QUIC endpoint we would instead see here a
|
||||||
// QUIC handshake event. And, we would not see any handshake
|
// QUIC handshake event. And, we would not see any handshake
|
||||||
// if the URL was instead an HTTP URL.
|
// if the URL was instead an HTTP URL.
|
||||||
"tls_handshake": [
|
"tls_handshakes": [
|
||||||
{
|
{
|
||||||
"cipher_suite": "TLS_AES_128_GCM_SHA256",
|
"cipher_suite": "TLS_AES_128_GCM_SHA256",
|
||||||
"failure": null,
|
"failure": null,
|
||||||
|
@ -347,7 +351,7 @@ This is the JSON output. Let us comment it in detail:
|
||||||
"tls_version": "TLSv1.3",
|
"tls_version": "TLSv1.3",
|
||||||
"peer_certificates": [
|
"peer_certificates": [
|
||||||
{
|
{
|
||||||
"data": "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",
|
"data": "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",
|
||||||
"format": "base64"
|
"format": "base64"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -359,7 +363,7 @@ This is the JSON output. Let us comment it in detail:
|
||||||
"format": "base64"
|
"format": "base64"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"t": 0.065514708,
|
"t": 0.086816667,
|
||||||
"address": "8.8.4.4:443",
|
"address": "8.8.4.4:443",
|
||||||
"server_name": "dns.google",
|
"server_name": "dns.google",
|
||||||
"alpn": [
|
"alpn": [
|
||||||
|
@ -369,13 +373,13 @@ This is the JSON output. Let us comment it in detail:
|
||||||
"no_tls_verify": false,
|
"no_tls_verify": false,
|
||||||
"oddity": "",
|
"oddity": "",
|
||||||
"proto": "tcp",
|
"proto": "tcp",
|
||||||
"started": 0.024404083
|
"started": 0.043971083
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
// Finally here we see information about the round trip, which
|
// Finally here we see information about the round trip, which
|
||||||
// is formatted according the df-001-httpt data format:
|
// is formatted according to https://github.com/ooni/spec/blob/master/data-formats/df-001-httpt.md:
|
||||||
"http_round_trip": [
|
"requests": [
|
||||||
{
|
{
|
||||||
|
|
||||||
// This field indicates whether there was an error during
|
// This field indicates whether there was an error during
|
||||||
|
@ -389,21 +393,20 @@ This is the JSON output. Let us comment it in detail:
|
||||||
"headers": {
|
"headers": {
|
||||||
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
||||||
"accept-language": "en-US;q=0.8,en;q=0.5",
|
"accept-language": "en-US;q=0.8,en;q=0.5",
|
||||||
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36"
|
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// This field contains the response status code, body,
|
// This field contains the response status code, body, and headers.
|
||||||
// and headers.
|
|
||||||
"response": {
|
"response": {
|
||||||
"code": 200,
|
"code": 200,
|
||||||
"headers": {
|
"headers": {
|
||||||
"accept-ranges": "none",
|
"accept-ranges": "none",
|
||||||
"alt-svc": "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"",
|
"alt-svc": "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"",
|
||||||
"cache-control": "private",
|
"cache-control": "private",
|
||||||
"content-security-policy": "object-src 'none';base-uri 'self';script-src 'nonce-bSLcJjaotppZl3Y2moIaxg==' 'strict-dynamic' 'report-sample' 'unsafe-eval' 'unsafe-inline' https: http:;report-uri https://csp.withgoogle.com/csp/honest_dns/1_0;frame-ancestors 'none'",
|
"content-security-policy": "object-src 'none';base-uri 'self';script-src 'nonce-y/OGliLR2gbEfTG2i4MAaw==' 'strict-dynamic' 'report-sample' 'unsafe-eval' 'unsafe-inline' https: http:;report-uri https://csp.withgoogle.com/csp/honest_dns/1_0;frame-ancestors 'none'",
|
||||||
"content-type": "text/html; charset=UTF-8",
|
"content-type": "text/html; charset=UTF-8",
|
||||||
"date": "Fri, 24 Sep 2021 08:51:01 GMT",
|
"date": "Fri, 05 Nov 2021 08:59:37 GMT",
|
||||||
"server": "scaffolding on HTTPServer2",
|
"server": "scaffolding on HTTPServer2",
|
||||||
"strict-transport-security": "max-age=31536000; includeSubDomains; preload",
|
"strict-transport-security": "max-age=31536000; includeSubDomains; preload",
|
||||||
"vary": "Accept-Encoding",
|
"vary": "Accept-Encoding",
|
||||||
|
@ -416,7 +419,7 @@ This is the JSON output. Let us comment it in detail:
|
||||||
// body: we don't want to read and submit to the OONI
|
// body: we don't want to read and submit to the OONI
|
||||||
// collector large bodies.
|
// collector large bodies.
|
||||||
"body": {
|
"body": {
|
||||||
"data": "PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ImVuIj4gPGhlYWQ+IDx0aXRsZT5Hb29nbGUgUHVibGljIEROUzwvdGl0bGU+ICA8bWV0YSBjaGFyc2V0PSJVVEYtOCI+IDxsaW5rIGhyZWY9Ii9zdGF0aWMvOTNkZDU5NTQvZmF2aWNvbi5wbmciIHJlbD0ic2hvcnRjdXQgaWNvbiIgdHlwZT0iaW1hZ2UvcG5nIj4gPGxpbmsgaHJlZj0iL3N0YXRpYy84MzZhZWJjNi9tYXR0ZXIubWluLmNzcyIgcmVsPSJzdHlsZXNoZWV0Ij4gPGxpbmsgaHJlZj0iL3N0YXRpYy9iODUzNmMzNy9zaGFyZWQuY3NzIiByZWw9InN0eWxlc2hlZXQiPiA8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEiPiAgPGxpbmsgaHJlZj0iL3N0YXRpYy9kMDVjZDZiYS9yb290LmNzcyIgcmVsPSJzdHlsZXNoZWV0Ij4gPC9oZWFkPiA8Ym9keT4gPHNwYW4gY2xhc3M9ImZpbGxlciB0b3AiPjwvc3Bhbj4gICA8ZGl2IGNsYXNzPSJsb2dvIiB0aXRsZT0iR29vZ2xlIFB1YmxpYyBETlMiPiA8ZGl2IGNsYXNzPSJsb2dvLXRleHQiPjxzcGFuPlB1YmxpYyBETlM8L3NwYW4+PC9kaXY+IDwvZGl2PiAgPGZvcm0gYWN0aW9uPSIvcXVlcnkiIG1ldGhvZD0iR0VUIj4gIDxkaXYgY2xhc3M9InJvdyI+IDxsYWJlbCBjbGFzcz0ibWF0dGVyLXRleHRmaWVsZC1vdXRsaW5lZCI+IDxpbnB1dCB0eXBlPSJ0ZXh0IiBuYW1lPSJuYW1lIiBwbGFjZWhvbGRlcj0iJm5ic3A7Ij4gPHNwYW4+RE5TIE5hbWU8L3NwYW4+IDxwIGNsYXNzPSJoZWxwIj4gRW50ZXIgYSBkb21haW4gKGxpa2UgZXhhbXBsZS5jb20pIG9yIElQIGFkZHJlc3MgKGxpa2UgOC44LjguOCBvciAyMDAxOjQ4NjA6NDg2MDo6ODg0NCkgaGVyZS4gPC9wPiA8L2xhYmVsPiA8YnV0dG9uIGNsYXNzPSJtYXR0ZXItYnV0dG9uLWNvbnRhaW5lZCBtYXR0ZXItcHJpbWFyeSIgdHlwZT0ic3VibWl0Ij5SZXNvbHZlPC9idXR0b24+IDwvZGl2PiA8L2Zvcm0+ICA8c3BhbiBjbGFzcz0iZmlsbGVyIGJvdHRvbSI+PC9zcGFuPiA8Zm9vdGVyIGNsYXNzPSJyb3ciPiA8YSBocmVmPSJodHRwczovL2RldmVsb3BlcnMuZ29vZ2xlLmNvbS9zcGVlZC9wdWJsaWMtZG5zIj5IZWxwPC9hPiA8YSBocmVmPSIvY2FjaGUiPkNhY2hlIEZsdXNoPC9hPiA8c3BhbiBjbGFzcz0iZmlsbGVyIj48L3NwYW4+IDxhIGhyZWY9Imh0dHBzOi8vZGV2ZWxvcGVycy5nb29nbGUuY29tL3NwZWVkL3B1YmxpYy1kbnMvZG9jcy91c2luZyI+IEdldCBTdGFydGVkIHdpdGggR29vZ2xlIFB1YmxpYyBETlMgPC9hPiA8L2Zvb3Rlcj4gICA8c2NyaXB0IG5vbmNlPSJiU0xjSmphb3RwcFpsM1kybW9JYXhnPT0iPmRvY3VtZW50LmZvcm1zWzBdLm5hbWUuZm9jdXMoKTs8L3NjcmlwdD4gPC9ib2R5PiA8L2h0bWw+",
|
"data": "PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ImVuIj4gPGhlYWQ+IDx0aXRsZT5Hb29nbGUgUHVibGljIEROUzwvdGl0bGU+ICA8bWV0YSBjaGFyc2V0PSJVVEYtOCI+IDxsaW5rIGhyZWY9Ii9zdGF0aWMvOTNkZDU5NTQvZmF2aWNvbi5wbmciIHJlbD0ic2hvcnRjdXQgaWNvbiIgdHlwZT0iaW1hZ2UvcG5nIj4gPGxpbmsgaHJlZj0iL3N0YXRpYy84MzZhZWJjNi9tYXR0ZXIubWluLmNzcyIgcmVsPSJzdHlsZXNoZWV0Ij4gPGxpbmsgaHJlZj0iL3N0YXRpYy9iODUzNmMzNy9zaGFyZWQuY3NzIiByZWw9InN0eWxlc2hlZXQiPiA8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEiPiAgPGxpbmsgaHJlZj0iL3N0YXRpYy9kMDVjZDZiYS9yb290LmNzcyIgcmVsPSJzdHlsZXNoZWV0Ij4gPC9oZWFkPiA8Ym9keT4gPHNwYW4gY2xhc3M9ImZpbGxlciB0b3AiPjwvc3Bhbj4gICA8ZGl2IGNsYXNzPSJsb2dvIiB0aXRsZT0iR29vZ2xlIFB1YmxpYyBETlMiPiA8ZGl2IGNsYXNzPSJsb2dvLXRleHQiPjxzcGFuPlB1YmxpYyBETlM8L3NwYW4+PC9kaXY+IDwvZGl2PiAgPGZvcm0gYWN0aW9uPSIvcXVlcnkiIG1ldGhvZD0iR0VUIj4gIDxkaXYgY2xhc3M9InJvdyI+IDxsYWJlbCBjbGFzcz0ibWF0dGVyLXRleHRmaWVsZC1vdXRsaW5lZCI+IDxpbnB1dCB0eXBlPSJ0ZXh0IiBuYW1lPSJuYW1lIiBwbGFjZWhvbGRlcj0iJm5ic3A7Ij4gPHNwYW4+RE5TIE5hbWU8L3NwYW4+IDxwIGNsYXNzPSJoZWxwIj4gRW50ZXIgYSBkb21haW4gKGxpa2UgZXhhbXBsZS5jb20pIG9yIElQIGFkZHJlc3MgKGxpa2UgOC44LjguOCBvciAyMDAxOjQ4NjA6NDg2MDo6ODg0NCkgaGVyZS4gPC9wPiA8L2xhYmVsPiA8YnV0dG9uIGNsYXNzPSJtYXR0ZXItYnV0dG9uLWNvbnRhaW5lZCBtYXR0ZXItcHJpbWFyeSIgdHlwZT0ic3VibWl0Ij5SZXNvbHZlPC9idXR0b24+IDwvZGl2PiA8L2Zvcm0+ICA8c3BhbiBjbGFzcz0iZmlsbGVyIGJvdHRvbSI+PC9zcGFuPiA8Zm9vdGVyIGNsYXNzPSJyb3ciPiA8YSBocmVmPSJodHRwczovL2RldmVsb3BlcnMuZ29vZ2xlLmNvbS9zcGVlZC9wdWJsaWMtZG5zIj5IZWxwPC9hPiA8YSBocmVmPSIvY2FjaGUiPkNhY2hlIEZsdXNoPC9hPiA8c3BhbiBjbGFzcz0iZmlsbGVyIj48L3NwYW4+IDxhIGhyZWY9Imh0dHBzOi8vZGV2ZWxvcGVycy5nb29nbGUuY29tL3NwZWVkL3B1YmxpYy1kbnMvZG9jcy91c2luZyI+IEdldCBTdGFydGVkIHdpdGggR29vZ2xlIFB1YmxpYyBETlMgPC9hPiA8L2Zvb3Rlcj4gICA8c2NyaXB0IG5vbmNlPSJ5L09HbGlMUjJnYkVmVEcyaTRNQWF3PT0iPmRvY3VtZW50LmZvcm1zWzBdLm5hbWUuZm9jdXMoKTs8L3NjcmlwdD4gPC9ib2R5PiA8L2h0bWw+",
|
||||||
"format": "base64"
|
"format": "base64"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -436,14 +439,14 @@ This is the JSON output. Let us comment it in detail:
|
||||||
// The t field is the moment where we finished the
|
// The t field is the moment where we finished the
|
||||||
// round trip and saved the event. The started field
|
// round trip and saved the event. The started field
|
||||||
// is instead when we started the round trip.
|
// is instead when we started the round trip.
|
||||||
|
//
|
||||||
// You may notice that the start of the round trip
|
// You may notice that the start of the round trip
|
||||||
// if after the `t` of the handshake. This tells us
|
// if after the `t` of the handshake. This tells us
|
||||||
// that the code first connects, then handshakes, and
|
// that the code first connects, then handshakes, and
|
||||||
// finally creates HTTP code for performing the
|
// finally creates HTTP code for performing the
|
||||||
// round trip.
|
// round trip.
|
||||||
"t": 0.338674625,
|
"t": 0.471535167,
|
||||||
"started": 0.065926625,
|
"started": 0.087176458,
|
||||||
|
|
||||||
// As usual we also compute an oddity value related
|
// As usual we also compute an oddity value related
|
||||||
// in this case to the HTTP round trip.
|
// in this case to the HTTP round trip.
|
||||||
|
|
|
@ -173,7 +173,7 @@ func main() {
|
||||||
// Let us now print the resulting measurement.
|
// Let us now print the resulting measurement.
|
||||||
//
|
//
|
||||||
// ```Go
|
// ```Go
|
||||||
print(m)
|
print(measurex.NewArchivalHTTPEndpointMeasurement(m))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ```
|
// ```
|
||||||
|
@ -183,7 +183,7 @@ func main() {
|
||||||
// Let us perform a vanilla run first:
|
// Let us perform a vanilla run first:
|
||||||
//
|
//
|
||||||
// ```bash
|
// ```bash
|
||||||
// go run -race ./internal/tutorial/measurex/chapter06
|
// go run -race ./internal/tutorial/measurex/chapter06 | jq
|
||||||
// ```
|
// ```
|
||||||
//
|
//
|
||||||
// This is the JSON output. Let us comment it in detail:
|
// This is the JSON output. Let us comment it in detail:
|
||||||
|
@ -197,31 +197,17 @@ func main() {
|
||||||
// "network": "tcp",
|
// "network": "tcp",
|
||||||
// "address": "8.8.4.4:443",
|
// "address": "8.8.4.4:443",
|
||||||
//
|
//
|
||||||
// // Internally, HTTPEndpointGetWithoutCookies calls
|
|
||||||
// // TCPConnect and here we see the corresponding event
|
|
||||||
// "connect": [
|
|
||||||
// {
|
|
||||||
// "address": "8.8.4.4:443",
|
|
||||||
// "failure": null,
|
|
||||||
// "operation": "connect",
|
|
||||||
// "proto": "tcp",
|
|
||||||
// "t": 0.02422375,
|
|
||||||
// "started": 0.002269291,
|
|
||||||
// "oddity": ""
|
|
||||||
// }
|
|
||||||
// ],
|
|
||||||
//
|
|
||||||
// // These are the I/O operations we have already seen
|
// // These are the I/O operations we have already seen
|
||||||
// // in previous chapters
|
// // in previous chapters
|
||||||
// "read_write": [
|
// "network_events": [
|
||||||
// {
|
// {
|
||||||
// "address": "8.8.4.4:443",
|
// "address": "8.8.4.4:443",
|
||||||
// "failure": null,
|
// "failure": null,
|
||||||
// "num_bytes": 280,
|
// "num_bytes": 280,
|
||||||
// "operation": "write",
|
// "operation": "write",
|
||||||
// "proto": "tcp",
|
// "proto": "tcp",
|
||||||
// "t": 0.024931791,
|
// "t": 0.045800292,
|
||||||
// "started": 0.024910416,
|
// "started": 0.045782167,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -230,18 +216,18 @@ func main() {
|
||||||
// "num_bytes": 517,
|
// "num_bytes": 517,
|
||||||
// "operation": "read",
|
// "operation": "read",
|
||||||
// "proto": "tcp",
|
// "proto": "tcp",
|
||||||
// "t": 0.063629791,
|
// "t": 0.082571,
|
||||||
// "started": 0.024935666,
|
// "started": 0.045805458,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
// "address": "8.8.4.4:443",
|
// "address": "8.8.4.4:443",
|
||||||
// "failure": null,
|
// "failure": null,
|
||||||
// "num_bytes": 4301,
|
// "num_bytes": 4303,
|
||||||
// "operation": "read",
|
// "operation": "read",
|
||||||
// "proto": "tcp",
|
// "proto": "tcp",
|
||||||
// "t": 0.064183,
|
// "t": 0.084400542,
|
||||||
// "started": 0.064144208,
|
// "started": 0.084372667,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -250,8 +236,8 @@ func main() {
|
||||||
// "num_bytes": 64,
|
// "num_bytes": 64,
|
||||||
// "operation": "write",
|
// "operation": "write",
|
||||||
// "proto": "tcp",
|
// "proto": "tcp",
|
||||||
// "t": 0.065464041,
|
// "t": 0.086762625,
|
||||||
// "started": 0.065441333,
|
// "started": 0.086748292,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -260,8 +246,8 @@ func main() {
|
||||||
// "num_bytes": 86,
|
// "num_bytes": 86,
|
||||||
// "operation": "write",
|
// "operation": "write",
|
||||||
// "proto": "tcp",
|
// "proto": "tcp",
|
||||||
// "t": 0.067256083,
|
// "t": 0.087851,
|
||||||
// "started": 0.067224375,
|
// "started": 0.087837625,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -270,8 +256,8 @@ func main() {
|
||||||
// "num_bytes": 201,
|
// "num_bytes": 201,
|
||||||
// "operation": "write",
|
// "operation": "write",
|
||||||
// "proto": "tcp",
|
// "proto": "tcp",
|
||||||
// "t": 0.067674416,
|
// "t": 0.089527292,
|
||||||
// "started": 0.067652375,
|
// "started": 0.089507958,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -280,8 +266,8 @@ func main() {
|
||||||
// "num_bytes": 93,
|
// "num_bytes": 93,
|
||||||
// "operation": "read",
|
// "operation": "read",
|
||||||
// "proto": "tcp",
|
// "proto": "tcp",
|
||||||
// "t": 0.086618708,
|
// "t": 0.168585625,
|
||||||
// "started": 0.067599208,
|
// "started": 0.088068375,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -290,18 +276,28 @@ func main() {
|
||||||
// "num_bytes": 31,
|
// "num_bytes": 31,
|
||||||
// "operation": "write",
|
// "operation": "write",
|
||||||
// "proto": "tcp",
|
// "proto": "tcp",
|
||||||
// "t": 0.086703625,
|
// "t": 0.168713542,
|
||||||
// "started": 0.0866745,
|
// "started": 0.168671417,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
// "address": "8.8.4.4:443",
|
// "address": "8.8.4.4:443",
|
||||||
// "failure": null,
|
// "failure": null,
|
||||||
// "num_bytes": 2028,
|
// "num_bytes": 2000,
|
||||||
// "operation": "read",
|
// "operation": "read",
|
||||||
// "proto": "tcp",
|
// "proto": "tcp",
|
||||||
// "t": 0.337785916,
|
// "t": 0.468671417,
|
||||||
// "started": 0.086717333,
|
// "started": 0.168759333,
|
||||||
|
// "oddity": ""
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// "address": "8.8.4.4:443",
|
||||||
|
// "failure": null,
|
||||||
|
// "num_bytes": 39,
|
||||||
|
// "operation": "read",
|
||||||
|
// "proto": "tcp",
|
||||||
|
// "t": 0.47118175,
|
||||||
|
// "started": 0.471169667,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -310,8 +306,8 @@ func main() {
|
||||||
// "num_bytes": 39,
|
// "num_bytes": 39,
|
||||||
// "operation": "write",
|
// "operation": "write",
|
||||||
// "proto": "tcp",
|
// "proto": "tcp",
|
||||||
// "t": 0.338514916,
|
// "t": 0.471335458,
|
||||||
// "started": 0.338485375,
|
// "started": 0.471268583,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -320,17 +316,25 @@ func main() {
|
||||||
// "num_bytes": 24,
|
// "num_bytes": 24,
|
||||||
// "operation": "write",
|
// "operation": "write",
|
||||||
// "proto": "tcp",
|
// "proto": "tcp",
|
||||||
// "t": 0.338800833,
|
// "t": 0.471865,
|
||||||
// "started": 0.338788625,
|
// "started": 0.471836292,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// },
|
// }
|
||||||
|
// ],
|
||||||
|
//
|
||||||
|
// // Internally, HTTPEndpointGetWithoutCookies calls
|
||||||
|
// // TCPConnect and here we see the corresponding event
|
||||||
|
// "tcp_connect": [
|
||||||
// {
|
// {
|
||||||
// "address": "8.8.4.4:443",
|
// "ip": "8.8.4.4",
|
||||||
// "failure": "connection_already_closed",
|
// "port": 443,
|
||||||
// "operation": "read",
|
// "t": 0.043644958,
|
||||||
// "proto": "tcp",
|
// "status": {
|
||||||
// "t": 0.338888041,
|
// "blocked": false,
|
||||||
// "started": 0.338523291,
|
// "failure": null,
|
||||||
|
// "success": true
|
||||||
|
// },
|
||||||
|
// "started": 0.022849458,
|
||||||
// "oddity": ""
|
// "oddity": ""
|
||||||
// }
|
// }
|
||||||
// ],
|
// ],
|
||||||
|
@ -340,7 +344,7 @@ func main() {
|
||||||
// // specified a QUIC endpoint we would instead see here a
|
// // specified a QUIC endpoint we would instead see here a
|
||||||
// // QUIC handshake event. And, we would not see any handshake
|
// // QUIC handshake event. And, we would not see any handshake
|
||||||
// // if the URL was instead an HTTP URL.
|
// // if the URL was instead an HTTP URL.
|
||||||
// "tls_handshake": [
|
// "tls_handshakes": [
|
||||||
// {
|
// {
|
||||||
// "cipher_suite": "TLS_AES_128_GCM_SHA256",
|
// "cipher_suite": "TLS_AES_128_GCM_SHA256",
|
||||||
// "failure": null,
|
// "failure": null,
|
||||||
|
@ -348,7 +352,7 @@ func main() {
|
||||||
// "tls_version": "TLSv1.3",
|
// "tls_version": "TLSv1.3",
|
||||||
// "peer_certificates": [
|
// "peer_certificates": [
|
||||||
// {
|
// {
|
||||||
// "data": "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",
|
// "data": "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",
|
||||||
// "format": "base64"
|
// "format": "base64"
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
@ -360,7 +364,7 @@ func main() {
|
||||||
// "format": "base64"
|
// "format": "base64"
|
||||||
// }
|
// }
|
||||||
// ],
|
// ],
|
||||||
// "t": 0.065514708,
|
// "t": 0.086816667,
|
||||||
// "address": "8.8.4.4:443",
|
// "address": "8.8.4.4:443",
|
||||||
// "server_name": "dns.google",
|
// "server_name": "dns.google",
|
||||||
// "alpn": [
|
// "alpn": [
|
||||||
|
@ -370,13 +374,13 @@ func main() {
|
||||||
// "no_tls_verify": false,
|
// "no_tls_verify": false,
|
||||||
// "oddity": "",
|
// "oddity": "",
|
||||||
// "proto": "tcp",
|
// "proto": "tcp",
|
||||||
// "started": 0.024404083
|
// "started": 0.043971083
|
||||||
// }
|
// }
|
||||||
// ],
|
// ],
|
||||||
//
|
//
|
||||||
// // Finally here we see information about the round trip, which
|
// // Finally here we see information about the round trip, which
|
||||||
// // is formatted according the df-001-httpt data format:
|
// // is formatted according to https://github.com/ooni/spec/blob/master/data-formats/df-001-httpt.md:
|
||||||
// "http_round_trip": [
|
// "requests": [
|
||||||
// {
|
// {
|
||||||
//
|
//
|
||||||
// // This field indicates whether there was an error during
|
// // This field indicates whether there was an error during
|
||||||
|
@ -390,21 +394,20 @@ func main() {
|
||||||
// "headers": {
|
// "headers": {
|
||||||
// "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
// "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
||||||
// "accept-language": "en-US;q=0.8,en;q=0.5",
|
// "accept-language": "en-US;q=0.8,en;q=0.5",
|
||||||
// "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36"
|
// "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36"
|
||||||
// }
|
// }
|
||||||
// },
|
// },
|
||||||
//
|
//
|
||||||
// // This field contains the response status code, body,
|
// // This field contains the response status code, body, and headers.
|
||||||
// // and headers.
|
|
||||||
// "response": {
|
// "response": {
|
||||||
// "code": 200,
|
// "code": 200,
|
||||||
// "headers": {
|
// "headers": {
|
||||||
// "accept-ranges": "none",
|
// "accept-ranges": "none",
|
||||||
// "alt-svc": "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"",
|
// "alt-svc": "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"",
|
||||||
// "cache-control": "private",
|
// "cache-control": "private",
|
||||||
// "content-security-policy": "object-src 'none';base-uri 'self';script-src 'nonce-bSLcJjaotppZl3Y2moIaxg==' 'strict-dynamic' 'report-sample' 'unsafe-eval' 'unsafe-inline' https: http:;report-uri https://csp.withgoogle.com/csp/honest_dns/1_0;frame-ancestors 'none'",
|
// "content-security-policy": "object-src 'none';base-uri 'self';script-src 'nonce-y/OGliLR2gbEfTG2i4MAaw==' 'strict-dynamic' 'report-sample' 'unsafe-eval' 'unsafe-inline' https: http:;report-uri https://csp.withgoogle.com/csp/honest_dns/1_0;frame-ancestors 'none'",
|
||||||
// "content-type": "text/html; charset=UTF-8",
|
// "content-type": "text/html; charset=UTF-8",
|
||||||
// "date": "Fri, 24 Sep 2021 08:51:01 GMT",
|
// "date": "Fri, 05 Nov 2021 08:59:37 GMT",
|
||||||
// "server": "scaffolding on HTTPServer2",
|
// "server": "scaffolding on HTTPServer2",
|
||||||
// "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
|
// "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
|
||||||
// "vary": "Accept-Encoding",
|
// "vary": "Accept-Encoding",
|
||||||
|
@ -417,7 +420,7 @@ func main() {
|
||||||
// // body: we don't want to read and submit to the OONI
|
// // body: we don't want to read and submit to the OONI
|
||||||
// // collector large bodies.
|
// // collector large bodies.
|
||||||
// "body": {
|
// "body": {
|
||||||
// "data": "PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ImVuIj4gPGhlYWQ+IDx0aXRsZT5Hb29nbGUgUHVibGljIEROUzwvdGl0bGU+ICA8bWV0YSBjaGFyc2V0PSJVVEYtOCI+IDxsaW5rIGhyZWY9Ii9zdGF0aWMvOTNkZDU5NTQvZmF2aWNvbi5wbmciIHJlbD0ic2hvcnRjdXQgaWNvbiIgdHlwZT0iaW1hZ2UvcG5nIj4gPGxpbmsgaHJlZj0iL3N0YXRpYy84MzZhZWJjNi9tYXR0ZXIubWluLmNzcyIgcmVsPSJzdHlsZXNoZWV0Ij4gPGxpbmsgaHJlZj0iL3N0YXRpYy9iODUzNmMzNy9zaGFyZWQuY3NzIiByZWw9InN0eWxlc2hlZXQiPiA8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEiPiAgPGxpbmsgaHJlZj0iL3N0YXRpYy9kMDVjZDZiYS9yb290LmNzcyIgcmVsPSJzdHlsZXNoZWV0Ij4gPC9oZWFkPiA8Ym9keT4gPHNwYW4gY2xhc3M9ImZpbGxlciB0b3AiPjwvc3Bhbj4gICA8ZGl2IGNsYXNzPSJsb2dvIiB0aXRsZT0iR29vZ2xlIFB1YmxpYyBETlMiPiA8ZGl2IGNsYXNzPSJsb2dvLXRleHQiPjxzcGFuPlB1YmxpYyBETlM8L3NwYW4+PC9kaXY+IDwvZGl2PiAgPGZvcm0gYWN0aW9uPSIvcXVlcnkiIG1ldGhvZD0iR0VUIj4gIDxkaXYgY2xhc3M9InJvdyI+IDxsYWJlbCBjbGFzcz0ibWF0dGVyLXRleHRmaWVsZC1vdXRsaW5lZCI+IDxpbnB1dCB0eXBlPSJ0ZXh0IiBuYW1lPSJuYW1lIiBwbGFjZWhvbGRlcj0iJm5ic3A7Ij4gPHNwYW4+RE5TIE5hbWU8L3NwYW4+IDxwIGNsYXNzPSJoZWxwIj4gRW50ZXIgYSBkb21haW4gKGxpa2UgZXhhbXBsZS5jb20pIG9yIElQIGFkZHJlc3MgKGxpa2UgOC44LjguOCBvciAyMDAxOjQ4NjA6NDg2MDo6ODg0NCkgaGVyZS4gPC9wPiA8L2xhYmVsPiA8YnV0dG9uIGNsYXNzPSJtYXR0ZXItYnV0dG9uLWNvbnRhaW5lZCBtYXR0ZXItcHJpbWFyeSIgdHlwZT0ic3VibWl0Ij5SZXNvbHZlPC9idXR0b24+IDwvZGl2PiA8L2Zvcm0+ICA8c3BhbiBjbGFzcz0iZmlsbGVyIGJvdHRvbSI+PC9zcGFuPiA8Zm9vdGVyIGNsYXNzPSJyb3ciPiA8YSBocmVmPSJodHRwczovL2RldmVsb3BlcnMuZ29vZ2xlLmNvbS9zcGVlZC9wdWJsaWMtZG5zIj5IZWxwPC9hPiA8YSBocmVmPSIvY2FjaGUiPkNhY2hlIEZsdXNoPC9hPiA8c3BhbiBjbGFzcz0iZmlsbGVyIj48L3NwYW4+IDxhIGhyZWY9Imh0dHBzOi8vZGV2ZWxvcGVycy5nb29nbGUuY29tL3NwZWVkL3B1YmxpYy1kbnMvZG9jcy91c2luZyI+IEdldCBTdGFydGVkIHdpdGggR29vZ2xlIFB1YmxpYyBETlMgPC9hPiA8L2Zvb3Rlcj4gICA8c2NyaXB0IG5vbmNlPSJiU0xjSmphb3RwcFpsM1kybW9JYXhnPT0iPmRvY3VtZW50LmZvcm1zWzBdLm5hbWUuZm9jdXMoKTs8L3NjcmlwdD4gPC9ib2R5PiA8L2h0bWw+",
|
// "data": "PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ImVuIj4gPGhlYWQ+IDx0aXRsZT5Hb29nbGUgUHVibGljIEROUzwvdGl0bGU+ICA8bWV0YSBjaGFyc2V0PSJVVEYtOCI+IDxsaW5rIGhyZWY9Ii9zdGF0aWMvOTNkZDU5NTQvZmF2aWNvbi5wbmciIHJlbD0ic2hvcnRjdXQgaWNvbiIgdHlwZT0iaW1hZ2UvcG5nIj4gPGxpbmsgaHJlZj0iL3N0YXRpYy84MzZhZWJjNi9tYXR0ZXIubWluLmNzcyIgcmVsPSJzdHlsZXNoZWV0Ij4gPGxpbmsgaHJlZj0iL3N0YXRpYy9iODUzNmMzNy9zaGFyZWQuY3NzIiByZWw9InN0eWxlc2hlZXQiPiA8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEiPiAgPGxpbmsgaHJlZj0iL3N0YXRpYy9kMDVjZDZiYS9yb290LmNzcyIgcmVsPSJzdHlsZXNoZWV0Ij4gPC9oZWFkPiA8Ym9keT4gPHNwYW4gY2xhc3M9ImZpbGxlciB0b3AiPjwvc3Bhbj4gICA8ZGl2IGNsYXNzPSJsb2dvIiB0aXRsZT0iR29vZ2xlIFB1YmxpYyBETlMiPiA8ZGl2IGNsYXNzPSJsb2dvLXRleHQiPjxzcGFuPlB1YmxpYyBETlM8L3NwYW4+PC9kaXY+IDwvZGl2PiAgPGZvcm0gYWN0aW9uPSIvcXVlcnkiIG1ldGhvZD0iR0VUIj4gIDxkaXYgY2xhc3M9InJvdyI+IDxsYWJlbCBjbGFzcz0ibWF0dGVyLXRleHRmaWVsZC1vdXRsaW5lZCI+IDxpbnB1dCB0eXBlPSJ0ZXh0IiBuYW1lPSJuYW1lIiBwbGFjZWhvbGRlcj0iJm5ic3A7Ij4gPHNwYW4+RE5TIE5hbWU8L3NwYW4+IDxwIGNsYXNzPSJoZWxwIj4gRW50ZXIgYSBkb21haW4gKGxpa2UgZXhhbXBsZS5jb20pIG9yIElQIGFkZHJlc3MgKGxpa2UgOC44LjguOCBvciAyMDAxOjQ4NjA6NDg2MDo6ODg0NCkgaGVyZS4gPC9wPiA8L2xhYmVsPiA8YnV0dG9uIGNsYXNzPSJtYXR0ZXItYnV0dG9uLWNvbnRhaW5lZCBtYXR0ZXItcHJpbWFyeSIgdHlwZT0ic3VibWl0Ij5SZXNvbHZlPC9idXR0b24+IDwvZGl2PiA8L2Zvcm0+ICA8c3BhbiBjbGFzcz0iZmlsbGVyIGJvdHRvbSI+PC9zcGFuPiA8Zm9vdGVyIGNsYXNzPSJyb3ciPiA8YSBocmVmPSJodHRwczovL2RldmVsb3BlcnMuZ29vZ2xlLmNvbS9zcGVlZC9wdWJsaWMtZG5zIj5IZWxwPC9hPiA8YSBocmVmPSIvY2FjaGUiPkNhY2hlIEZsdXNoPC9hPiA8c3BhbiBjbGFzcz0iZmlsbGVyIj48L3NwYW4+IDxhIGhyZWY9Imh0dHBzOi8vZGV2ZWxvcGVycy5nb29nbGUuY29tL3NwZWVkL3B1YmxpYy1kbnMvZG9jcy91c2luZyI+IEdldCBTdGFydGVkIHdpdGggR29vZ2xlIFB1YmxpYyBETlMgPC9hPiA8L2Zvb3Rlcj4gICA8c2NyaXB0IG5vbmNlPSJ5L09HbGlMUjJnYkVmVEcyaTRNQWF3PT0iPmRvY3VtZW50LmZvcm1zWzBdLm5hbWUuZm9jdXMoKTs8L3NjcmlwdD4gPC9ib2R5PiA8L2h0bWw+",
|
||||||
// "format": "base64"
|
// "format": "base64"
|
||||||
// },
|
// },
|
||||||
//
|
//
|
||||||
|
@ -437,14 +440,14 @@ func main() {
|
||||||
// // The t field is the moment where we finished the
|
// // The t field is the moment where we finished the
|
||||||
// // round trip and saved the event. The started field
|
// // round trip and saved the event. The started field
|
||||||
// // is instead when we started the round trip.
|
// // is instead when we started the round trip.
|
||||||
//
|
// //
|
||||||
// // You may notice that the start of the round trip
|
// // You may notice that the start of the round trip
|
||||||
// // if after the `t` of the handshake. This tells us
|
// // if after the `t` of the handshake. This tells us
|
||||||
// // that the code first connects, then handshakes, and
|
// // that the code first connects, then handshakes, and
|
||||||
// // finally creates HTTP code for performing the
|
// // finally creates HTTP code for performing the
|
||||||
// // round trip.
|
// // round trip.
|
||||||
// "t": 0.338674625,
|
// "t": 0.471535167,
|
||||||
// "started": 0.065926625,
|
// "started": 0.087176458,
|
||||||
//
|
//
|
||||||
// // As usual we also compute an oddity value related
|
// // As usual we also compute an oddity value related
|
||||||
// // in this case to the HTTP round trip.
|
// // in this case to the HTTP round trip.
|
||||||
|
|
|
@ -111,7 +111,8 @@ We are almost done now: we loop over all the endpoints and apply the
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally, we print the results.
|
Finally, we print the results. (Note that here we are not
|
||||||
|
converting to the OONI archival data format.)
|
||||||
|
|
||||||
```Go
|
```Go
|
||||||
print(m)
|
print(m)
|
||||||
|
@ -128,7 +129,10 @@ go run -race ./internal/tutorial/measurex/chapter07 | jq
|
||||||
```
|
```
|
||||||
|
|
||||||
Please, check the JSON output. Do you recognize the fields
|
Please, check the JSON output. Do you recognize the fields
|
||||||
we have described in previous chapters?
|
we have described in previous chapters, even though we didn't
|
||||||
|
convert to the OONI data format? Can you modify the code to
|
||||||
|
use the OONI data format in the output by calling the proper
|
||||||
|
conversion functions exported by `measurex`?
|
||||||
|
|
||||||
Can you provoke common errors such as DNS resolution
|
Can you provoke common errors such as DNS resolution
|
||||||
errors, TCP connect errors, TLS handshake errors, and
|
errors, TCP connect errors, TLS handshake errors, and
|
||||||
|
|
|
@ -112,7 +112,8 @@ func main() {
|
||||||
}
|
}
|
||||||
// ```
|
// ```
|
||||||
//
|
//
|
||||||
// Finally, we print the results.
|
// Finally, we print the results. (Note that here we are not
|
||||||
|
// converting to the OONI archival data format.)
|
||||||
//
|
//
|
||||||
// ```Go
|
// ```Go
|
||||||
print(m)
|
print(m)
|
||||||
|
@ -129,7 +130,10 @@ func main() {
|
||||||
// ```
|
// ```
|
||||||
//
|
//
|
||||||
// Please, check the JSON output. Do you recognize the fields
|
// Please, check the JSON output. Do you recognize the fields
|
||||||
// we have described in previous chapters?
|
// we have described in previous chapters, even though we didn't
|
||||||
|
// convert to the OONI data format? Can you modify the code to
|
||||||
|
// use the OONI data format in the output by calling the proper
|
||||||
|
// conversion functions exported by `measurex`?
|
||||||
//
|
//
|
||||||
// Can you provoke common errors such as DNS resolution
|
// Can you provoke common errors such as DNS resolution
|
||||||
// errors, TCP connect errors, TLS handshake errors, and
|
// errors, TCP connect errors, TLS handshake errors, and
|
||||||
|
|
|
@ -101,6 +101,15 @@ This is it. The rest of the program is exactly the same.
|
||||||
for _, epnt := range httpEndpoints {
|
for _, epnt := range httpEndpoints {
|
||||||
m.Endpoints = append(m.Endpoints, mx.HTTPEndpointGetWithoutCookies(ctx, epnt))
|
m.Endpoints = append(m.Endpoints, mx.HTTPEndpointGetWithoutCookies(ctx, epnt))
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
(Note that here, like in the previous chapter, we are not converting
|
||||||
|
to the OONI data format. Rather, we're just dumping the internally
|
||||||
|
used data structures. Exercise: can you modify this program to emit
|
||||||
|
a JSON compliant with the OONI data format by using the proper]
|
||||||
|
conversion functions exported by `measurex`?)
|
||||||
|
|
||||||
|
```Go
|
||||||
print(m)
|
print(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -102,6 +102,15 @@ func main() {
|
||||||
for _, epnt := range httpEndpoints {
|
for _, epnt := range httpEndpoints {
|
||||||
m.Endpoints = append(m.Endpoints, mx.HTTPEndpointGetWithoutCookies(ctx, epnt))
|
m.Endpoints = append(m.Endpoints, mx.HTTPEndpointGetWithoutCookies(ctx, epnt))
|
||||||
}
|
}
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// (Note that here, like in the previous chapter, we are not converting
|
||||||
|
// to the OONI data format. Rather, we're just dumping the internally
|
||||||
|
// used data structures. Exercise: can you modify this program to emit
|
||||||
|
// a JSON compliant with the OONI data format by using the proper]
|
||||||
|
// conversion functions exported by `measurex`?)
|
||||||
|
//
|
||||||
|
// ```Go
|
||||||
print(m)
|
print(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,6 +101,11 @@ fully measured, this method closes the returned channel.
|
||||||
Like we did before, we append the resulting measurements to
|
Like we did before, we append the resulting measurements to
|
||||||
our `m` container and we print it.
|
our `m` container and we print it.
|
||||||
|
|
||||||
|
Exercise: here we're not using the OONI data format and we're
|
||||||
|
instead printing the internally used data structures. Can
|
||||||
|
you modify the code to emit data using OONI's data format here?
|
||||||
|
(Hint: there are conversion functions in `measurex`.)
|
||||||
|
|
||||||
```Go
|
```Go
|
||||||
print(m)
|
print(m)
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,6 +102,11 @@ func main() {
|
||||||
// Like we did before, we append the resulting measurements to
|
// Like we did before, we append the resulting measurements to
|
||||||
// our `m` container and we print it.
|
// our `m` container and we print it.
|
||||||
//
|
//
|
||||||
|
// Exercise: here we're not using the OONI data format and we're
|
||||||
|
// instead printing the internally used data structures. Can
|
||||||
|
// you modify the code to emit data using OONI's data format here?
|
||||||
|
// (Hint: there are conversion functions in `measurex`.)
|
||||||
|
//
|
||||||
// ```Go
|
// ```Go
|
||||||
print(m)
|
print(m)
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type measurement struct {
|
type measurement struct {
|
||||||
URLs []*measurex.URLMeasurement
|
URLs []*measurex.ArchivalURLMeasurement
|
||||||
}
|
}
|
||||||
|
|
||||||
func print(v interface{}) {
|
func print(v interface{}) {
|
||||||
|
@ -67,7 +67,7 @@ is closed when done by `MeasureURLAndFollowRedirections`, so we leave the loop.
|
||||||
|
|
||||||
```Go
|
```Go
|
||||||
for m := range mx.MeasureURLAndFollowRedirections(ctx, *URL, headers, cookies) {
|
for m := range mx.MeasureURLAndFollowRedirections(ctx, *URL, headers, cookies) {
|
||||||
all.URLs = append(all.URLs, m)
|
all.URLs = append(all.URLs, measurex.NewArchivalURLMeasurement(m))
|
||||||
}
|
}
|
||||||
print(all)
|
print(all)
|
||||||
}
|
}
|
||||||
|
@ -86,6 +86,9 @@ Take a look at the JSON. You should see several redirects
|
||||||
and that we measure each endpoint of each redirect, including
|
and that we measure each endpoint of each redirect, including
|
||||||
QUIC endpoints that we discover on the way.
|
QUIC endpoints that we discover on the way.
|
||||||
|
|
||||||
|
Exercise: remove code for converting to OONI data format
|
||||||
|
and compare output with previous chapter. See any difference?
|
||||||
|
|
||||||
## Conclusion
|
## Conclusion
|
||||||
|
|
||||||
We have introduced `MeasureURLAndFollowRedirect`, the
|
We have introduced `MeasureURLAndFollowRedirect`, the
|
||||||
|
|
|
@ -33,7 +33,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type measurement struct {
|
type measurement struct {
|
||||||
URLs []*measurex.URLMeasurement
|
URLs []*measurex.ArchivalURLMeasurement
|
||||||
}
|
}
|
||||||
|
|
||||||
func print(v interface{}) {
|
func print(v interface{}) {
|
||||||
|
@ -68,7 +68,7 @@ func main() {
|
||||||
//
|
//
|
||||||
// ```Go
|
// ```Go
|
||||||
for m := range mx.MeasureURLAndFollowRedirections(ctx, *URL, headers, cookies) {
|
for m := range mx.MeasureURLAndFollowRedirections(ctx, *URL, headers, cookies) {
|
||||||
all.URLs = append(all.URLs, m)
|
all.URLs = append(all.URLs, measurex.NewArchivalURLMeasurement(m))
|
||||||
}
|
}
|
||||||
print(all)
|
print(all)
|
||||||
}
|
}
|
||||||
|
@ -87,6 +87,9 @@ func main() {
|
||||||
// and that we measure each endpoint of each redirect, including
|
// and that we measure each endpoint of each redirect, including
|
||||||
// QUIC endpoints that we discover on the way.
|
// QUIC endpoints that we discover on the way.
|
||||||
//
|
//
|
||||||
|
// Exercise: remove code for converting to OONI data format
|
||||||
|
// and compare output with previous chapter. See any difference?
|
||||||
|
//
|
||||||
// ## Conclusion
|
// ## Conclusion
|
||||||
//
|
//
|
||||||
// We have introduced `MeasureURLAndFollowRedirect`, the
|
// We have introduced `MeasureURLAndFollowRedirect`, the
|
||||||
|
|
|
@ -45,10 +45,10 @@ that a Web Connectivity measurement should have.
|
||||||
```Go
|
```Go
|
||||||
|
|
||||||
type measurement struct {
|
type measurement struct {
|
||||||
Queries []*measurex.DNSLookupEvent `json:"queries"`
|
Queries []*measurex.ArchivalDNSLookupEvent `json:"queries"`
|
||||||
TCPConnect []*measurex.NetworkEvent `json:"tcp_connect"`
|
TCPConnect []*measurex.ArchivalTCPConnect `json:"tcp_connect"`
|
||||||
TLSHandshakes []*measurex.TLSHandshakeEvent `json:"tls_handshakes"`
|
TLSHandshakes []*measurex.ArchivalQUICTLSHandshakeEvent `json:"tls_handshakes"`
|
||||||
Requests []*measurex.HTTPRoundTripEvent `json:"requests"`
|
Requests []*measurex.ArchivalHTTPRoundTripEvent `json:"requests"`
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -96,7 +96,8 @@ the input URL's domain using the system resolver.
|
||||||
|
|
||||||
```Go
|
```Go
|
||||||
dns := mx.LookupHostSystem(ctx, parsedURL.Hostname())
|
dns := mx.LookupHostSystem(ctx, parsedURL.Hostname())
|
||||||
m.Queries = append(m.Queries, dns.LookupHost...)
|
m.Queries = append(
|
||||||
|
m.Queries, measurex.NewArchivalDNSLookupEventList(dns.LookupHost)...)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -128,7 +129,8 @@ whether the input URL is HTTP or HTTPS.
|
||||||
switch parsedURL.Scheme {
|
switch parsedURL.Scheme {
|
||||||
case "http":
|
case "http":
|
||||||
tcp := mx.TCPConnect(ctx, epnt.Address)
|
tcp := mx.TCPConnect(ctx, epnt.Address)
|
||||||
m.TCPConnect = append(m.TCPConnect, tcp.Connect...)
|
m.TCPConnect = append(
|
||||||
|
m.TCPConnect, measurex.NewArchivalTCPConnectList(tcp.Connect)...)
|
||||||
case "https":
|
case "https":
|
||||||
config := &tls.Config{
|
config := &tls.Config{
|
||||||
ServerName: parsedURL.Hostname(),
|
ServerName: parsedURL.Hostname(),
|
||||||
|
@ -136,8 +138,10 @@ whether the input URL is HTTP or HTTPS.
|
||||||
RootCAs: netxlite.NewDefaultCertPool(),
|
RootCAs: netxlite.NewDefaultCertPool(),
|
||||||
}
|
}
|
||||||
tls := mx.TLSConnectAndHandshake(ctx, epnt.Address, config)
|
tls := mx.TLSConnectAndHandshake(ctx, epnt.Address, config)
|
||||||
m.TCPConnect = append(m.TCPConnect, tls.Connect...)
|
m.TCPConnect = append(
|
||||||
m.TLSHandshakes = append(m.TLSHandshakes, tls.TLSHandshake...)
|
m.TCPConnect, measurex.NewArchivalTCPConnectList(tls.Connect)...)
|
||||||
|
m.TLSHandshakes = append(m.TLSHandshakes,
|
||||||
|
measurex.NewArchivalQUICTLSHandshakeEventList(tls.TLSHandshake)...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,7 +226,8 @@ the chapters we have seen so far.
|
||||||
|
|
||||||
```Go
|
```Go
|
||||||
|
|
||||||
m.Requests = append(m.Requests, db.AsMeasurement().HTTPRoundTrip...)
|
m.Requests = append(m.Requests, measurex.NewArchivalHTTPRoundTripEventList(
|
||||||
|
db.AsMeasurement().HTTPRoundTrip)...)
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,10 +46,10 @@ func print(v interface{}) {
|
||||||
// ```Go
|
// ```Go
|
||||||
|
|
||||||
type measurement struct {
|
type measurement struct {
|
||||||
Queries []*measurex.DNSLookupEvent `json:"queries"`
|
Queries []*measurex.ArchivalDNSLookupEvent `json:"queries"`
|
||||||
TCPConnect []*measurex.NetworkEvent `json:"tcp_connect"`
|
TCPConnect []*measurex.ArchivalTCPConnect `json:"tcp_connect"`
|
||||||
TLSHandshakes []*measurex.TLSHandshakeEvent `json:"tls_handshakes"`
|
TLSHandshakes []*measurex.ArchivalQUICTLSHandshakeEvent `json:"tls_handshakes"`
|
||||||
Requests []*measurex.HTTPRoundTripEvent `json:"requests"`
|
Requests []*measurex.ArchivalHTTPRoundTripEvent `json:"requests"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ```
|
// ```
|
||||||
|
@ -97,7 +97,8 @@ func webConnectivity(ctx context.Context, URL string) (*measurement, error) {
|
||||||
//
|
//
|
||||||
// ```Go
|
// ```Go
|
||||||
dns := mx.LookupHostSystem(ctx, parsedURL.Hostname())
|
dns := mx.LookupHostSystem(ctx, parsedURL.Hostname())
|
||||||
m.Queries = append(m.Queries, dns.LookupHost...)
|
m.Queries = append(
|
||||||
|
m.Queries, measurex.NewArchivalDNSLookupEventList(dns.LookupHost)...)
|
||||||
|
|
||||||
// ```
|
// ```
|
||||||
//
|
//
|
||||||
|
@ -129,7 +130,8 @@ func webConnectivity(ctx context.Context, URL string) (*measurement, error) {
|
||||||
switch parsedURL.Scheme {
|
switch parsedURL.Scheme {
|
||||||
case "http":
|
case "http":
|
||||||
tcp := mx.TCPConnect(ctx, epnt.Address)
|
tcp := mx.TCPConnect(ctx, epnt.Address)
|
||||||
m.TCPConnect = append(m.TCPConnect, tcp.Connect...)
|
m.TCPConnect = append(
|
||||||
|
m.TCPConnect, measurex.NewArchivalTCPConnectList(tcp.Connect)...)
|
||||||
case "https":
|
case "https":
|
||||||
config := &tls.Config{
|
config := &tls.Config{
|
||||||
ServerName: parsedURL.Hostname(),
|
ServerName: parsedURL.Hostname(),
|
||||||
|
@ -137,8 +139,10 @@ func webConnectivity(ctx context.Context, URL string) (*measurement, error) {
|
||||||
RootCAs: netxlite.NewDefaultCertPool(),
|
RootCAs: netxlite.NewDefaultCertPool(),
|
||||||
}
|
}
|
||||||
tls := mx.TLSConnectAndHandshake(ctx, epnt.Address, config)
|
tls := mx.TLSConnectAndHandshake(ctx, epnt.Address, config)
|
||||||
m.TCPConnect = append(m.TCPConnect, tls.Connect...)
|
m.TCPConnect = append(
|
||||||
m.TLSHandshakes = append(m.TLSHandshakes, tls.TLSHandshake...)
|
m.TCPConnect, measurex.NewArchivalTCPConnectList(tls.Connect)...)
|
||||||
|
m.TLSHandshakes = append(m.TLSHandshakes,
|
||||||
|
measurex.NewArchivalQUICTLSHandshakeEventList(tls.TLSHandshake)...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,7 +227,8 @@ func webConnectivity(ctx context.Context, URL string) (*measurement, error) {
|
||||||
//
|
//
|
||||||
// ```Go
|
// ```Go
|
||||||
|
|
||||||
m.Requests = append(m.Requests, db.AsMeasurement().HTTPRoundTrip...)
|
m.Requests = append(m.Requests, measurex.NewArchivalHTTPRoundTripEventList(
|
||||||
|
db.AsMeasurement().HTTPRoundTrip)...)
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user