Skip to content

Commit

Permalink
Pull request: all: upd golibs
Browse files Browse the repository at this point in the history
Squashed commit of the following:

commit 9b7e21e
Author: Dimitry Kolyshev <[email protected]>
Date:   Tue Dec 12 10:05:31 2023 +0200

    proxy: imp code

commit d5b40a4
Author: Dimitry Kolyshev <[email protected]>
Date:   Mon Dec 11 12:02:26 2023 +0200

    all: upd golibs
  • Loading branch information
Mizzick committed Dec 12, 2023
1 parent afd2d3e commit 638288e
Show file tree
Hide file tree
Showing 12 changed files with 82 additions and 101 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ Application Options:
--ipv6-disabled If specified, all AAAA requests will be replied with NoError RCode and empty answer
--bogus-nxdomain= Transform the responses containing at least a single IP that matches specified addresses and CIDRs into NXDOMAIN. Can be specified multiple times.
--udp-buf-size= Set the size of the UDP buffer in bytes. A value <= 0 will use the system default.
--max-go-routines= Set the maximum number of go routines. A value <= 0 will not not set a maximum.
--max-go-routines= Set the maximum number of go routines. A zero value will not not set a maximum.
--pprof If present, exposes pprof information on localhost:6060.
--version Prints the program version
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/AdguardTeam/dnsproxy
go 1.20

require (
github.com/AdguardTeam/golibs v0.18.0
github.com/AdguardTeam/golibs v0.18.1
github.com/ameshkov/dnscrypt/v2 v2.2.7
github.com/ameshkov/dnsstamps v1.0.3
github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
github.com/AdguardTeam/golibs v0.18.0 h1:ckS2YK7t2Ub6UkXl0fnreVaM15Zb07Hh1gmFqttjpWg=
github.com/AdguardTeam/golibs v0.18.0/go.mod h1:DKhCIXHcUYtBhU8ibTLKh1paUL96n5zhQBlx763sj+U=
github.com/AdguardTeam/golibs v0.18.1 h1:6u0fvrIj2qjUsRdbIGJ9AR0g5QRSWdKIo/DYl3tp5aM=
github.com/AdguardTeam/golibs v0.18.1/go.mod h1:DKhCIXHcUYtBhU8ibTLKh1paUL96n5zhQBlx763sj+U=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
Expand Down
4 changes: 2 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,8 @@ type Options struct {
// UDP buffer size value
UDPBufferSize int `yaml:"udp-buf-size" long:"udp-buf-size" description:"Set the size of the UDP buffer in bytes. A value <= 0 will use the system default."`

// The maximum number of go routines
MaxGoRoutines int `yaml:"max-go-routines" long:"max-go-routines" description:"Set the maximum number of go routines. A value <= 0 will not not set a maximum."`
// MaxGoRoutines is the maximum number of goroutines.
MaxGoRoutines uint `yaml:"max-go-routines" long:"max-go-routines" description:"Set the maximum number of go routines. A zero value will not not set a maximum."`

// Pprof defines whether the pprof information needs to be exposed via
// localhost:6060 or not.
Expand Down
2 changes: 1 addition & 1 deletion proxy/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ type Config struct {
// TODO(a.garipov): Rename this to something like
// “MaxDNSRequestGoroutines” in a later major version, as it doesn't
// actually limit all goroutines.
MaxGoroutines int
MaxGoroutines uint

// The size of the read buffer on the underlying socket. Larger read buffers can handle
// larger bursts of requests before packets get dropped.
Expand Down
12 changes: 5 additions & 7 deletions proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/syncutil"
"github.com/ameshkov/dnscrypt/v2"
"github.com/miekg/dns"
gocache "github.com/patrickmn/go-cache"
Expand Down Expand Up @@ -166,15 +167,15 @@ type Proxy struct {
// RWMutex protects the whole proxy.
sync.RWMutex

// requestGoroutinesSema limits the number of simultaneous requests.
// requestsSema limits the number of simultaneous requests.
//
// TODO(a.garipov): Currently we have to pass this exact semaphore to
// the workers, to prevent races on restart. In the future we will need
// a better restarting mechanism that completely prevents such invalid
// states.
//
// See also: https://github.com/AdguardTeam/AdGuardHome/issues/2242.
requestGoroutinesSema semaphore
requestsSema syncutil.Semaphore

// Config is the proxy configuration.
//
Expand All @@ -195,12 +196,9 @@ func (p *Proxy) Init() (err error) {
if p.MaxGoroutines > 0 {
log.Info("dnsproxy: max goroutines is set to %d", p.MaxGoroutines)

p.requestGoroutinesSema, err = newChanSemaphore(p.MaxGoroutines)
if err != nil {
return fmt.Errorf("can't init semaphore: %w", err)
}
p.requestsSema = syncutil.NewChanSemaphore(p.MaxGoroutines)
} else {
p.requestGoroutinesSema = newNoopSemaphore()
p.requestsSema = syncutil.EmptySemaphore{}
}

p.udpOOBSize = proxynetutil.UDPGetOOBSize()
Expand Down
58 changes: 0 additions & 58 deletions proxy/sema.go

This file was deleted.

8 changes: 4 additions & 4 deletions proxy/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@ func (p *Proxy) startListeners(ctx context.Context) error {
}

for _, l := range p.udpListen {
go p.udpPacketLoop(l, p.requestGoroutinesSema)
go p.udpPacketLoop(l, p.requestsSema)
}

for _, l := range p.tcpListen {
go p.tcpPacketLoop(l, ProtoTCP, p.requestGoroutinesSema)
go p.tcpPacketLoop(l, ProtoTCP, p.requestsSema)
}

for _, l := range p.tlsListen {
go p.tcpPacketLoop(l, ProtoTLS, p.requestGoroutinesSema)
go p.tcpPacketLoop(l, ProtoTLS, p.requestsSema)
}

for _, l := range p.httpsListen {
Expand All @@ -64,7 +64,7 @@ func (p *Proxy) startListeners(ctx context.Context) error {
}

for _, l := range p.quicListen {
go p.quicPacketLoop(l, p.requestGoroutinesSema)
go p.quicPacketLoop(l, p.requestsSema)
}

for _, l := range p.dnsCryptUDPListen {
Expand Down
16 changes: 11 additions & 5 deletions proxy/server_dnscrypt.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package proxy

import (
"context"
"fmt"
"net"

"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/syncutil"
"github.com/ameshkov/dnscrypt/v2"
"github.com/miekg/dns"
)
Expand All @@ -28,7 +30,7 @@ func (p *Proxy) createDNSCryptListeners() (err error) {
Handler: &dnsCryptHandler{
proxy: p,

requestGoroutinesSema: p.requestGoroutinesSema,
reqSema: p.requestsSema,
},
}

Expand Down Expand Up @@ -61,20 +63,24 @@ func (p *Proxy) createDNSCryptListeners() (err error) {
type dnsCryptHandler struct {
proxy *Proxy

requestGoroutinesSema semaphore
reqSema syncutil.Semaphore
}

// compile-time type check
var _ dnscrypt.Handler = &dnsCryptHandler{}

// ServeDNS - processes the DNS query
func (h *dnsCryptHandler) ServeDNS(rw dnscrypt.ResponseWriter, req *dns.Msg) error {
func (h *dnsCryptHandler) ServeDNS(rw dnscrypt.ResponseWriter, req *dns.Msg) (err error) {
d := h.proxy.newDNSContext(ProtoDNSCrypt, req)
d.Addr = netutil.NetAddrToAddrPort(rw.RemoteAddr())
d.DNSCryptResponseWriter = rw

h.requestGoroutinesSema.acquire()
defer h.requestGoroutinesSema.release()
// TODO(d.kolyshev): Pass and use context from above.
err = h.reqSema.Acquire(context.Background())
if err != nil {
return fmt.Errorf("dnsproxy: dnscrypt: acquiring semaphore: %w", err)
}
defer h.reqSema.Release()

return h.proxy.handleDNSRequest(d)
}
Expand Down
42 changes: 30 additions & 12 deletions proxy/server_quic.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/syncutil"
"github.com/bluele/gcache"
"github.com/miekg/dns"
"github.com/quic-go/quic-go"
Expand Down Expand Up @@ -82,11 +83,12 @@ func (p *Proxy) createQUICListeners() error {

// quicPacketLoop listens for incoming QUIC packets.
//
// See also the comment on Proxy.requestGoroutinesSema.
func (p *Proxy) quicPacketLoop(l *quic.EarlyListener, requestGoroutinesSema semaphore) {
// See also the comment on Proxy.requestsSema.
func (p *Proxy) quicPacketLoop(l *quic.EarlyListener, reqSema syncutil.Semaphore) {
log.Info("Entering the DNS-over-QUIC listener loop on %s", l.Addr())
for {
conn, err := l.Accept(context.Background())
ctx := context.Background()
conn, err := l.Accept(ctx)
if err != nil {
if isQUICErrorForDebugLog(err) {
log.Debug("accepting quic conn: closed or timed out: %s", err)
Expand All @@ -97,26 +99,34 @@ func (p *Proxy) quicPacketLoop(l *quic.EarlyListener, requestGoroutinesSema sema
break
}

requestGoroutinesSema.acquire()
err = reqSema.Acquire(ctx)
if err != nil {
log.Error("dnsproxy: quic: acquiring semaphore: %s", err)

break
}
go func() {
p.handleQUICConnection(conn, requestGoroutinesSema)
requestGoroutinesSema.release()
defer reqSema.Release()

p.handleQUICConnection(conn, reqSema)
}()
}
}

// handleQUICConnection handles a new QUIC connection. It waits for new streams
// and passes them to handleQUICStream.
//
// See also the comment on Proxy.requestGoroutinesSema.
func (p *Proxy) handleQUICConnection(conn quic.Connection, requestGoroutinesSema semaphore) {
// See also the comment on Proxy.requestsSema.
func (p *Proxy) handleQUICConnection(conn quic.Connection, reqSema syncutil.Semaphore) {
for {
ctx := context.Background()

// The stub to resolver DNS traffic follows a simple pattern in which
// the client sends a query, and the server provides a response. This
// design specifies that for each subsequent query on a QUIC connection
// the client MUST select the next available client-initiated
// bidirectional stream.
stream, err := conn.AcceptStream(context.Background())
stream, err := conn.AcceptStream(ctx)
if err != nil {
if isQUICErrorForDebugLog(err) {
log.Debug("accepting quic stream: closed or timed out: %s", err)
Expand All @@ -130,16 +140,24 @@ func (p *Proxy) handleQUICConnection(conn quic.Connection, requestGoroutinesSema
return
}

requestGoroutinesSema.acquire()
err = reqSema.Acquire(ctx)
if err != nil {
log.Error("dnsproxy: quic: acquiring semaphore: %s", err)

// Close the connection to make sure resources are freed.
closeQUICConn(conn, DoQCodeNoError)

return
}
go func() {
defer reqSema.Release()

p.handleQUICStream(stream, conn)

// The server MUST send the response(s) on the same stream and MUST
// indicate, after the last response, through the STREAM FIN
// mechanism that no further data will be sent on that stream.
_ = stream.Close()

requestGoroutinesSema.release()
}()
}
}
Expand Down
16 changes: 12 additions & 4 deletions proxy/server_tcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/syncutil"
"github.com/miekg/dns"
)

Expand Down Expand Up @@ -60,8 +61,8 @@ func (p *Proxy) createTLSListeners() (err error) {
// tcpPacketLoop listens for incoming TCP packets. proto must be either "tcp"
// or "tls".
//
// See also the comment on Proxy.requestGoroutinesSema.
func (p *Proxy) tcpPacketLoop(l net.Listener, proto Proto, requestGoroutinesSema semaphore) {
// See also the comment on Proxy.requestsSema.
func (p *Proxy) tcpPacketLoop(l net.Listener, proto Proto, reqSema syncutil.Semaphore) {
log.Info("dnsproxy: entering %s listener loop on %s", proto, l.Addr())

for {
Expand All @@ -76,10 +77,17 @@ func (p *Proxy) tcpPacketLoop(l net.Listener, proto Proto, requestGoroutinesSema
break
}

requestGoroutinesSema.acquire()
// TODO(d.kolyshev): Pass and use context from above.
err = reqSema.Acquire(context.Background())
if err != nil {
log.Error("dnsproxy: tcp: acquiring semaphore: %s", err)

break
}
go func() {
defer reqSema.Release()

p.handleTCPConnection(clientConn, proto)
requestGoroutinesSema.release()
}()
}
}
Expand Down
17 changes: 13 additions & 4 deletions proxy/server_udp.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/syncutil"
"github.com/miekg/dns"
)

Expand Down Expand Up @@ -60,8 +61,8 @@ func (p *Proxy) udpCreate(ctx context.Context, udpAddr *net.UDPAddr) (*net.UDPCo

// udpPacketLoop listens for incoming UDP packets.
//
// See also the comment on Proxy.requestGoroutinesSema.
func (p *Proxy) udpPacketLoop(conn *net.UDPConn, requestGoroutinesSema semaphore) {
// See also the comment on Proxy.requestsSema.
func (p *Proxy) udpPacketLoop(conn *net.UDPConn, reqSema syncutil.Semaphore) {
log.Info("dnsproxy: entering udp listener loop on %s", conn.LocalAddr())

b := make([]byte, dns.MaxMsgSize)
Expand All @@ -79,10 +80,18 @@ func (p *Proxy) udpPacketLoop(conn *net.UDPConn, requestGoroutinesSema semaphore
// we need the contents to survive the call because we're handling them in goroutine
packet := make([]byte, n)
copy(packet, b)
requestGoroutinesSema.acquire()

// TODO(d.kolyshev): Pass and use context from above.
sErr := reqSema.Acquire(context.Background())
if sErr != nil {
log.Error("dnsproxy: udp: acquiring semaphore: %s", sErr)

break
}
go func() {
defer reqSema.Release()

p.udpHandlePacket(packet, localIP, remoteAddr, conn)
requestGoroutinesSema.release()
}()
}
if err != nil {
Expand Down

0 comments on commit 638288e

Please sign in to comment.