2019-06-28 12:29:08 +08:00
|
|
|
package dns
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"context"
|
|
|
|
"crypto/tls"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
|
2020-02-09 17:02:48 +08:00
|
|
|
"github.com/Dreamacro/clash/component/dialer"
|
|
|
|
|
2019-06-28 12:29:08 +08:00
|
|
|
D "github.com/miekg/dns"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// dotMimeType is the DoH mimetype that should be used.
|
|
|
|
dotMimeType = "application/dns-message"
|
|
|
|
)
|
|
|
|
|
|
|
|
var dohTransport = &http.Transport{
|
|
|
|
TLSClientConfig: &tls.Config{ClientSessionCache: globalSessionCache},
|
2020-02-09 17:02:48 +08:00
|
|
|
DialContext: dialer.DialContext,
|
2019-06-28 12:29:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|