forked from ooni/probe-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdnsoverhttps.go
128 lines (111 loc) · 3.81 KB
/
dnsoverhttps.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package netxlite
//
// DNS-over-HTTPS transport
//
import (
"bytes"
"context"
"errors"
"io"
"net/http"
"time"
"github.com/ooni/probe-cli/v3/internal/model"
)
// DNSOverHTTPSTransport is a DNS-over-HTTPS DNSTransport.
type DNSOverHTTPSTransport struct {
// Client is the MANDATORY http client to use.
Client model.HTTPClient
// Decoder is the MANDATORY DNSDecoder.
Decoder model.DNSDecoder
// URL is the MANDATORY URL of the DNS-over-HTTPS server.
URL string
// HostOverride is OPTIONAL and allows to override the
// Host header sent in every request.
HostOverride string
}
// NewUnwrappedDNSOverHTTPSTransport creates a new DNSOverHTTPSTransport
// instance that has not been wrapped yet.
//
// Arguments:
//
// - client is a model.HTTPClient type;
//
// - URL is the DoH resolver URL (e.g., https://dns.google/dns-query).
func NewUnwrappedDNSOverHTTPSTransport(client model.HTTPClient, URL string) *DNSOverHTTPSTransport {
return NewUnwrappedDNSOverHTTPSTransportWithHostOverride(client, URL, "")
}
// NewDNSOverHTTPSTransport is like NewUnwrappedDNSOverHTTPSTransport but
// returns an already wrapped DNSTransport.
func NewDNSOverHTTPSTransport(client model.HTTPClient, URL string) model.DNSTransport {
return WrapDNSTransport(NewUnwrappedDNSOverHTTPSTransport(client, URL))
}
// NewDNSOverHTTPSTransportWithHTTPTransport is like NewDNSOverHTTPSTransport
// but takes in input an HTTPTransport rather than an HTTPClient.
func NewDNSOverHTTPSTransportWithHTTPTransport(txp model.HTTPTransport, URL string) model.DNSTransport {
return WrapDNSTransport(NewUnwrappedDNSOverHTTPSTransport(NewHTTPClient(txp), URL))
}
// NewUnwrappedDNSOverHTTPSTransportWithHostOverride creates a new DNSOverHTTPSTransport
// with the given Host header override. This instance has not been wrapped yet.
func NewUnwrappedDNSOverHTTPSTransportWithHostOverride(
client model.HTTPClient, URL, hostOverride string) *DNSOverHTTPSTransport {
return &DNSOverHTTPSTransport{
Client: client,
Decoder: &DNSDecoderMiekg{},
URL: URL,
HostOverride: hostOverride,
}
}
// RoundTrip sends a query and receives a reply.
func (t *DNSOverHTTPSTransport) RoundTrip(
ctx context.Context, query model.DNSQuery) (model.DNSResponse, error) {
rawQuery, err := query.Bytes()
if err != nil {
return nil, err
}
ctx, cancel := context.WithTimeout(ctx, 45*time.Second)
defer cancel()
req, err := http.NewRequest("POST", t.URL, bytes.NewReader(rawQuery))
if err != nil {
return nil, err
}
req.Host = t.HostOverride
req.Header.Set("user-agent", model.HTTPHeaderUserAgent)
req.Header.Set("content-type", "application/dns-message")
resp, err := t.Client.Do(req.WithContext(ctx))
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
// TODO(bassosimone): we should map the status code to a
// proper Error in the DNS context.
return nil, errors.New("doh: server returned error")
}
if resp.Header.Get("content-type") != "application/dns-message" {
return nil, errors.New("doh: invalid content-type")
}
const maxresponsesize = 1 << 20
limitReader := io.LimitReader(resp.Body, maxresponsesize)
rawResponse, err := ReadAllContext(ctx, limitReader)
if err != nil {
return nil, err
}
return t.Decoder.DecodeResponse(rawResponse, query)
}
// RequiresPadding returns true for DoH according to RFC8467.
func (t *DNSOverHTTPSTransport) RequiresPadding() bool {
return true
}
// Network returns the transport network, i.e., "doh".
func (t *DNSOverHTTPSTransport) Network() string {
return "doh"
}
// Address returns the URL we're using for the DoH server.
func (t *DNSOverHTTPSTransport) Address() string {
return t.URL
}
// CloseIdleConnections closes idle connections, if any.
func (t *DNSOverHTTPSTransport) CloseIdleConnections() {
t.Client.CloseIdleConnections()
}
var _ model.DNSTransport = &DNSOverHTTPSTransport{}