Skip to content
Commits on Source (3)
  • Dimitry Kolyshev's avatar
    Pull request: Update gh actions · deabec82
    Dimitry Kolyshev authored
    Merge in DNS/dnsproxy from update-gh-actions to master
    
    Squashed commit of the following:
    
    commit b26c007c
    Author: Dimitry Kolyshev <dkolyshev@adguard.com>
    Date:   Wed Apr 6 21:21:12 2022 +0200
    
        upd ling gh action
    
    commit 0a15cced
    Author: Dimitry Kolyshev <dkolyshev@adguard.com>
    Date:   Wed Apr 6 21:15:05 2022 +0200
    
        upd ling gh action
    deabec82
  • Eugene Burkov's avatar
    Pull request: 4505 tls conn leak · 65e42210
    Eugene Burkov authored
    Merge in DNS/dnsproxy from 4505-tls-conn-leak to master
    
    Updates AdguardTeam/AdGuardHome#4505.
    
    Squashed commit of the following:
    
    commit 4a4b52f710e51d1007b5dcda7cc04c3e5d54c0ff
    Author: Eugene Burkov <E.Burkov@AdGuard.COM>
    Date:   Thu Apr 21 15:51:47 2022 +0300
    
        upstream: close conn on err
    65e42210
  • Eugene Burkov's avatar
    Pull request: proxy: imp code · 6b1a62dc
    Eugene Burkov authored
    Merge in DNS/dnsproxy from imp-https to master
    
    Squashed commit of the following:
    
    commit bb558cd77742f2cb897b9db14df77e6b2ef877be
    Author: Eugene Burkov <E.Burkov@AdGuard.COM>
    Date:   Tue Apr 26 19:58:18 2022 +0300
    
        proxy: rm subnetdetector
    
    commit f4c34762e675183c96f62f3b8a684fe6758a6b2a
    Author: Eugene Burkov <E.Burkov@AdGuard.COM>
    Date:   Tue Apr 26 19:55:50 2022 +0300
    
        proxy: imp code
    6b1a62dc
......@@ -17,9 +17,14 @@ jobs:
- ubuntu-latest
- macos-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v3
with:
go-version: 1.17.x
- uses: actions/checkout@v3
- name: golangci-lint
uses: golangci/golangci-lint-action@v2.3.0
uses: golangci/golangci-lint-action@v3
with:
# This field is required. Dont set the patch version to always use
# the latest patch version.
......
......@@ -94,7 +94,7 @@ type Proxy struct {
ratelimitLock sync.Mutex // Synchronizes access to ratelimitBuckets
// proxyVerifier checks if the proxy is in the trusted list.
proxyVerifier *subnetDetector
proxyVerifier netutil.SubnetSet
// DNS cache
// --
......@@ -176,11 +176,14 @@ func (p *Proxy) Init() (err error) {
}
}
p.proxyVerifier, err = newSubnetDetector(p.TrustedProxies)
var trusted []*net.IPNet
trusted, err = netutil.ParseSubnets(p.TrustedProxies...)
if err != nil {
return fmt.Errorf("initializing subnet detector for proxies verifying: %w", err)
}
p.proxyVerifier = netutil.SliceSubnetSet(trusted)
return nil
}
......
......@@ -10,6 +10,7 @@ import (
"strings"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/miekg/dns"
"golang.org/x/net/http2"
)
......@@ -111,9 +112,9 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
d.HTTPResponseWriter = w
if prx != nil {
ip := ipFromAddr(prx)
ip, _ := netutil.IPAndPortFromAddr(prx)
log.Debug("request came from proxy server %s", prx)
if !p.proxyVerifier.detect(ip) {
if !p.proxyVerifier.Contains(ip) {
log.Debug("proxy %s is not trusted, using original remote addr", ip)
d.Addr = prx
}
......@@ -125,22 +126,6 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
// ipFromAddr returns an IP address from addr. If addr is neither
// a *net.TCPAddr nor a *net.UDPAddr, it returns nil.
//
// TODO(a.garipov): Create package netutil in the golibs module and move it
// there.
func ipFromAddr(addr net.Addr) (ip net.IP) {
switch addr := addr.(type) {
case *net.TCPAddr:
return addr.IP
case *net.UDPAddr:
return addr.IP
}
return nil
}
// Writes a response to the DOH client
func (p *Proxy) respondHTTPS(d *DNSContext) error {
resp := d.Res
......
......@@ -11,6 +11,7 @@ import (
"strings"
"testing"
"github.com/AdguardTeam/golibs/netutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
......@@ -105,14 +106,16 @@ func TestHttpsProxy(t *testing.T) {
reply := doRequest(t, proxyIP.String())
assertResponse(t, reply)
assert.True(t, ipFromAddr(gotAddr).Equal(clientIP))
ip, _ := netutil.IPAndPortFromAddr(gotAddr)
assert.True(t, ip.Equal(clientIP))
})
t.Run("not_in_trusted", func(t *testing.T) {
reply := doRequest(t, "127.0.0.2")
assertResponse(t, reply)
assert.True(t, ipFromAddr(gotAddr).Equal(proxyIP))
ip, _ := netutil.IPAndPortFromAddr(gotAddr)
assert.True(t, ip.Equal(proxyIP))
})
}
......@@ -309,8 +312,11 @@ func TestRemoteAddr(t *testing.T) {
require.NoError(t, err)
assert.True(t, ipFromAddr(addr).Equal(tc.wantIP))
assert.True(t, tc.wantProxy.Equal(ipFromAddr(prx)))
ip, _ := netutil.IPAndPortFromAddr(addr)
assert.True(t, ip.Equal(tc.wantIP))
prxIP, _ := netutil.IPAndPortFromAddr(prx)
assert.True(t, tc.wantProxy.Equal(prxIP))
})
}
}
package proxy
import (
"fmt"
"net"
)
// subnetDetector is used to check addresses for containing in the set of IP
// networks.
//
// TODO(e.burkov): There is an exact copy of this functionality in
// github.com/AdguardTeam/AdGuardHome. Maybe bring it into the golibs.
type subnetDetector struct {
// nets stores the networks against which the IP address should be
// checked.
nets []*net.IPNet
}
// detect returns true if IP address is contained by any of the IP address
// registries. It's safe for concurrent use.
//
// TODO(e.burkov): Think about memoization.
func (sd *subnetDetector) detect(ip net.IP) (ok bool) {
for _, ipnet := range sd.nets {
if ipnet.Contains(ip) {
return true
}
}
return false
}
// parseIPAsCIDR makes a 1/256 C-class network of IP address parsed from ipStr.
func parseIPAsCIDR(ipStr string) (network *net.IPNet) {
ip := net.ParseIP(ipStr)
if ip == nil {
return nil
}
// bits is the number of ones in 1/256 C-class CIDR.
const bits = 8 * net.IPv6len
return &net.IPNet{
IP: ip,
Mask: net.CIDRMask(bits, bits),
}
}
// newSubnetDetector returns a new IP detector initialized with nets. nets'
// strings could be either a CIDR or an IP address.
func newSubnetDetector(nets []string) (sd *subnetDetector, err error) {
sd = &subnetDetector{
nets: make([]*net.IPNet, len(nets)),
}
for i, ipnetStr := range nets {
var ipnet *net.IPNet
_, ipnet, err = net.ParseCIDR(ipnetStr)
if err != nil {
if ipnet = parseIPAsCIDR(ipnetStr); ipnet == nil {
return nil, fmt.Errorf("bad CIDR or IP at index %d: %w", i, err)
}
}
sd.nets[i] = ipnet
}
return sd, nil
}
......@@ -6,6 +6,7 @@ import (
"net/url"
"sync"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/miekg/dns"
)
......@@ -38,7 +39,7 @@ func newDoT(uu *url.URL, opts *Options) (u Upstream, err error) {
func (p *dnsOverTLS) Address() string { return p.boot.URL.String() }
func (p *dnsOverTLS) Exchange(m *dns.Msg) (*dns.Msg, error) {
func (p *dnsOverTLS) Exchange(m *dns.Msg) (reply *dns.Msg, err error) {
var pool *TLSPool
p.RLock()
pool = p.pool
......@@ -58,7 +59,7 @@ func (p *dnsOverTLS) Exchange(m *dns.Msg) (*dns.Msg, error) {
}
logBegin(p.Address(), m)
reply, err := p.exchangeConn(poolConn, m)
reply, err = p.exchangeConn(poolConn, m)
logFinish(p.Address(), err)
if err != nil {
log.Tracef("The TLS connection is expired due to %s", err)
......@@ -88,19 +89,27 @@ func (p *dnsOverTLS) Exchange(m *dns.Msg) (*dns.Msg, error) {
return reply, err
}
func (p *dnsOverTLS) exchangeConn(poolConn net.Conn, m *dns.Msg) (*dns.Msg, error) {
c := dns.Conn{Conn: poolConn}
err := c.WriteMsg(m)
func (p *dnsOverTLS) exchangeConn(conn net.Conn, m *dns.Msg) (reply *dns.Msg, err error) {
defer func() {
if err == nil {
return
}
if cerr := conn.Close(); cerr != nil {
err = &errors.Pair{Returned: err, Deferred: cerr}
}
}()
dnsConn := dns.Conn{Conn: conn}
err = dnsConn.WriteMsg(m)
if err != nil {
poolConn.Close()
return nil, fmt.Errorf("sending request to %s: %w", p.Address(), err)
}
reply, err := c.ReadMsg()
reply, err = dnsConn.ReadMsg()
if err != nil {
poolConn.Close()
return nil, fmt.Errorf("reading request from %s: %w", p.Address(), err)
return nil, fmt.Errorf("reading response from %s: %w", p.Address(), err)
} else if reply.Id != m.Id {
err = dns.ErrId
}
......