|
|
|
|
@ -16,6 +16,7 @@ import ( |
|
|
|
|
"math/rand" |
|
|
|
|
"net" |
|
|
|
|
"net/http" |
|
|
|
|
"runtime" |
|
|
|
|
"strings" |
|
|
|
|
"sync" |
|
|
|
|
"time" |
|
|
|
|
@ -159,6 +160,7 @@ type forwarder struct { |
|
|
|
|
logf logger.Logf |
|
|
|
|
linkMon *monitor.Mon |
|
|
|
|
linkSel ForwardLinkSelector |
|
|
|
|
dohSem chan struct{} |
|
|
|
|
|
|
|
|
|
ctx context.Context // good until Close
|
|
|
|
|
ctxCancel context.CancelFunc // closes ctx
|
|
|
|
|
@ -180,11 +182,18 @@ func init() { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func newForwarder(logf logger.Logf, responses chan packet, linkMon *monitor.Mon, linkSel ForwardLinkSelector) *forwarder { |
|
|
|
|
maxDoHInFlight := 1000 // effectively unlimited
|
|
|
|
|
if runtime.GOOS == "ios" { |
|
|
|
|
// No HTTP/2 on iOS yet (for size reasons), so DoH is
|
|
|
|
|
// pricier.
|
|
|
|
|
maxDoHInFlight = 10 |
|
|
|
|
} |
|
|
|
|
f := &forwarder{ |
|
|
|
|
logf: logger.WithPrefix(logf, "forward: "), |
|
|
|
|
linkMon: linkMon, |
|
|
|
|
linkSel: linkSel, |
|
|
|
|
responses: responses, |
|
|
|
|
dohSem: make(chan struct{}, maxDoHInFlight), |
|
|
|
|
} |
|
|
|
|
f.ctx, f.ctxCancel = context.WithCancel(context.Background()) |
|
|
|
|
return f |
|
|
|
|
@ -262,7 +271,22 @@ func (f *forwarder) getDoHClient(ip netaddr.IP) (urlBase string, c *http.Client, |
|
|
|
|
|
|
|
|
|
const dohType = "application/dns-message" |
|
|
|
|
|
|
|
|
|
func (f *forwarder) releaseDoHSem() { <-f.dohSem } |
|
|
|
|
|
|
|
|
|
func (f *forwarder) sendDoH(ctx context.Context, urlBase string, c *http.Client, packet []byte) ([]byte, error) { |
|
|
|
|
// Bound the number of HTTP requests in flight. This primarily
|
|
|
|
|
// matters for iOS where we're very memory constrained and
|
|
|
|
|
// HTTP requests are heavier on iOS where we don't include
|
|
|
|
|
// HTTP/2 for binary size reasons (as binaries on iOS linked
|
|
|
|
|
// with Go code cost memory proportional to the binary size,
|
|
|
|
|
// for reasons not fully understood).
|
|
|
|
|
select { |
|
|
|
|
case f.dohSem <- struct{}{}: |
|
|
|
|
case <-ctx.Done(): |
|
|
|
|
return nil, ctx.Err() |
|
|
|
|
} |
|
|
|
|
defer f.releaseDoHSem() |
|
|
|
|
|
|
|
|
|
req, err := http.NewRequestWithContext(ctx, "POST", urlBase, bytes.NewReader(packet)) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, err |
|
|
|
|
|