clash/dns/doh.go

76 lines
1.5 KiB
Go

package dns
import (
"bytes"
"context"
"crypto/tls"
"io/ioutil"
"net/http"
D "github.com/miekg/dns"
)
const (
// dotMimeType is the DoH mimetype that should be used.
dotMimeType = "application/dns-message"
// dotPath is the URL path that should be used.
dotPath = "/dns-query"
)
var dohTransport = &http.Transport{
TLSClientConfig: &tls.Config{ClientSessionCache: globalSessionCache},
}
type dohClient struct {
url string
}
func (dc *dohClient) Exchange(m *D.Msg) (msg *D.Msg, err error) {
return dc.ExchangeContext(context.Background(), m)
}
func (dc *dohClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) {
req, err := dc.newRequest(m)
if err != nil {
return nil, err
}
req = req.WithContext(ctx)
return dc.doRequest(req)
}
// newRequest returns a new DoH request given a dns.Msg.
func (dc *dohClient) newRequest(m *D.Msg) (*http.Request, error) {
buf, err := m.Pack()
if err != nil {
return nil, err
}
req, err := http.NewRequest(http.MethodPost, dc.url+"?bla=foo:443", bytes.NewReader(buf))
if err != nil {
return req, err
}
req.Header.Set("content-type", dotMimeType)
req.Header.Set("accept", dotMimeType)
return req, nil
}
func (dc *dohClient) doRequest(req *http.Request) (msg *D.Msg, err error) {
client := &http.Client{Transport: dohTransport}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
buf, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
msg = &D.Msg{}
err = msg.Unpack(buf)
return msg, err
}