1package resolver
2
3import (
4	"context"
5	"time"
6
7	"github.com/ooni/probe-engine/legacy/netx/dialid"
8	"github.com/ooni/probe-engine/legacy/netx/modelx"
9	"github.com/ooni/probe-engine/legacy/netx/transactionid"
10)
11
12// EmitterTransport is a RoundTripper that emits events when they occur.
13type EmitterTransport struct {
14	RoundTripper
15}
16
17// RoundTrip implements RoundTripper.RoundTrip
18func (txp EmitterTransport) RoundTrip(ctx context.Context, querydata []byte) ([]byte, error) {
19	root := modelx.ContextMeasurementRootOrDefault(ctx)
20	root.Handler.OnMeasurement(modelx.Measurement{
21		DNSQuery: &modelx.DNSQueryEvent{
22			Data:                   querydata,
23			DialID:                 dialid.ContextDialID(ctx),
24			DurationSinceBeginning: time.Now().Sub(root.Beginning),
25		},
26	})
27	replydata, err := txp.RoundTripper.RoundTrip(ctx, querydata)
28	if err != nil {
29		return nil, err
30	}
31	root.Handler.OnMeasurement(modelx.Measurement{
32		DNSReply: &modelx.DNSReplyEvent{
33			Data:                   replydata,
34			DialID:                 dialid.ContextDialID(ctx),
35			DurationSinceBeginning: time.Now().Sub(root.Beginning),
36		},
37	})
38	return replydata, nil
39}
40
41// EmitterResolver is a resolver that emits events
42type EmitterResolver struct {
43	Resolver
44}
45
46// LookupHost returns the IP addresses of a host
47func (r EmitterResolver) LookupHost(ctx context.Context, hostname string) ([]string, error) {
48	var (
49		network string
50		address string
51	)
52	type queryableResolver interface {
53		Transport() RoundTripper
54	}
55	if qr, ok := r.Resolver.(queryableResolver); ok {
56		txp := qr.Transport()
57		network, address = txp.Network(), txp.Address()
58	}
59	dialID := dialid.ContextDialID(ctx)
60	txID := transactionid.ContextTransactionID(ctx)
61	root := modelx.ContextMeasurementRootOrDefault(ctx)
62	root.Handler.OnMeasurement(modelx.Measurement{
63		ResolveStart: &modelx.ResolveStartEvent{
64			DialID:                 dialID,
65			DurationSinceBeginning: time.Now().Sub(root.Beginning),
66			Hostname:               hostname,
67			TransactionID:          txID,
68			TransportAddress:       address,
69			TransportNetwork:       network,
70		},
71	})
72	addrs, err := r.Resolver.LookupHost(ctx, hostname)
73	root.Handler.OnMeasurement(modelx.Measurement{
74		ResolveDone: &modelx.ResolveDoneEvent{
75			Addresses:              addrs,
76			DialID:                 dialID,
77			DurationSinceBeginning: time.Now().Sub(root.Beginning),
78			Error:                  err,
79			Hostname:               hostname,
80			TransactionID:          txID,
81			TransportAddress:       address,
82			TransportNetwork:       network,
83		},
84	})
85	return addrs, err
86}
87
88var _ RoundTripper = EmitterTransport{}
89var _ Resolver = EmitterResolver{}
90