82 lines
1.7 KiB
Go
82 lines
1.7 KiB
Go
package resolver
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
var _ net.Conn = &dohConn{}
|
|
|
|
type dohConn struct {
|
|
addr string
|
|
|
|
once sync.Once
|
|
onceErr error
|
|
|
|
in, ret bytes.Buffer
|
|
}
|
|
|
|
func (c *dohConn) Close() error { return nil }
|
|
func (c *dohConn) LocalAddr() net.Addr { return nil }
|
|
func (c *dohConn) RemoteAddr() net.Addr { return nil }
|
|
func (c *dohConn) SetDeadline(t time.Time) error { return nil }
|
|
func (c *dohConn) SetReadDeadline(t time.Time) error { return nil }
|
|
func (c *dohConn) SetWriteDeadline(t time.Time) error { return nil }
|
|
|
|
func (c *dohConn) Write(b []byte) (int, error) { return c.in.Write(b) }
|
|
|
|
func (c *dohConn) Read(b []byte) (int, error) {
|
|
c.once.Do(func() {
|
|
url, err := url.Parse(c.addr)
|
|
if err != nil {
|
|
c.onceErr = err
|
|
return
|
|
}
|
|
// RFC 8484
|
|
url.Path = "/dns-query"
|
|
|
|
// Skip 2 bytes which are length
|
|
reqBody := bytes.NewReader(c.in.Bytes()[2:])
|
|
req, err := http.NewRequest("POST", url.String(), reqBody)
|
|
if err != nil {
|
|
c.onceErr = err
|
|
return
|
|
}
|
|
req.Header.Set("content-type", "application/dns-message")
|
|
req.Header.Set("accept", "application/dns-message")
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
c.onceErr = err
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
respBody, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
c.onceErr = err
|
|
return
|
|
}
|
|
|
|
l := uint16(len(respBody))
|
|
_, err = c.ret.Write([]byte{uint8(l >> 8), uint8(l & ((1 << 8) - 1))})
|
|
if err != nil {
|
|
c.onceErr = err
|
|
return
|
|
}
|
|
|
|
_, err = c.ret.Write(respBody)
|
|
if err != nil {
|
|
c.onceErr = err
|
|
return
|
|
}
|
|
})
|
|
if c.onceErr != nil {
|
|
return 0, c.onceErr
|
|
}
|
|
return c.ret.Read(b)
|
|
}
|