mirror of https://github.com/tailscale/tailscale/
ipn/ipnlocal, net/tsdial: make SOCKS/HTTP dials use ExitDNS
And simplify, unexport some tsdial/netstack stuff in the the process. Fixes #3475 Change-Id: I186a5a5cbd8958e25c075b4676f7f6e70f3ff76e Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>pull/3497/head
parent
9f6249b26d
commit
9c5c9d0a50
@ -0,0 +1,86 @@
|
|||||||
|
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package tsdial
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// dohConn is a net.PacketConn suitable for returning from
|
||||||
|
// net.Dialer.Dial to send DNS queries over PeerAPI to exit nodes'
|
||||||
|
// ExitDNS DoH proxy service.
|
||||||
|
type dohConn struct {
|
||||||
|
ctx context.Context
|
||||||
|
baseURL string
|
||||||
|
hc *http.Client // if nil, default is used
|
||||||
|
|
||||||
|
rbuf bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ net.Conn = (*dohConn)(nil)
|
||||||
|
_ net.PacketConn = (*dohConn)(nil) // be a PacketConn to change net.Resolver semantics
|
||||||
|
)
|
||||||
|
|
||||||
|
func (*dohConn) Close() error { return nil }
|
||||||
|
func (*dohConn) LocalAddr() net.Addr { return todoAddr{} }
|
||||||
|
func (*dohConn) RemoteAddr() net.Addr { return todoAddr{} }
|
||||||
|
func (*dohConn) SetDeadline(t time.Time) error { return nil }
|
||||||
|
func (*dohConn) SetReadDeadline(t time.Time) error { return nil }
|
||||||
|
func (*dohConn) SetWriteDeadline(t time.Time) error { return nil }
|
||||||
|
|
||||||
|
func (c *dohConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||||
|
return c.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dohConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||||
|
n, err = c.Read(p)
|
||||||
|
return n, todoAddr{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dohConn) Read(p []byte) (n int, err error) {
|
||||||
|
return c.rbuf.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dohConn) Write(packet []byte) (n int, err error) {
|
||||||
|
req, err := http.NewRequestWithContext(c.ctx, "POST", c.baseURL, bytes.NewReader(packet))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
const dohType = "application/dns-message"
|
||||||
|
req.Header.Set("Content-Type", dohType)
|
||||||
|
hc := c.hc
|
||||||
|
if hc == nil {
|
||||||
|
hc = http.DefaultClient
|
||||||
|
}
|
||||||
|
hres, err := hc.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer hres.Body.Close()
|
||||||
|
if hres.StatusCode != 200 {
|
||||||
|
return 0, errors.New(hres.Status)
|
||||||
|
}
|
||||||
|
if ct := hres.Header.Get("Content-Type"); ct != dohType {
|
||||||
|
return 0, fmt.Errorf("unexpected response Content-Type %q", ct)
|
||||||
|
}
|
||||||
|
_, err = io.Copy(&c.rbuf, hres.Body)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return len(packet), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type todoAddr struct{}
|
||||||
|
|
||||||
|
func (todoAddr) Network() string { return "unused" }
|
||||||
|
func (todoAddr) String() string { return "unused-todoAddr" }
|
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package tsdial
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var dohBase = flag.String("doh-base", "", "DoH base URL for manual DoH tests; e.g. \"http://100.68.82.120:47830/dns-query\"")
|
||||||
|
|
||||||
|
func TestDoHResolve(t *testing.T) {
|
||||||
|
if *dohBase == "" {
|
||||||
|
t.Skip("skipping manual test without --doh-base= set")
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
var r net.Resolver
|
||||||
|
r.Dial = func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
|
return &dohConn{ctx: ctx, baseURL: *dohBase}, nil
|
||||||
|
}
|
||||||
|
addrs, err := r.LookupIP(ctx, "ip4", "google.com.")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Logf("Got: %q", addrs)
|
||||||
|
}
|
Loading…
Reference in New Issue