Files
coredns/plugin/pkg/proxy/proxy.go
Filippo125 e4990abfa3 feat(forward): add source_address directive (#8011)
* fix(dnssec): avoid caching empty signing results (#7996)

Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* save only

Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* do #8008

Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* Fix Address used if tcp

Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* Fix bad using of dialer type

Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* core: Add full TSIG verification in gRPC transport (#8006)

* core: Add full TSIG verification in gRPC transport

This PR add full TSIG verification in gRPC using dns.TsigVerify() so invalid signatures and timestamps are correctly detected instead of only checking key presence.

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>

* Fix

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>

* Fix

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>

---------

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* core: Add full TSIG verification in QUIC transport (#8007)

* core: Add full TSIG verification in QUIC transport

This PR add full TSIG verification in QUIC using dns.TsigVerify()

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>

* Fix

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>

---------

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* fix(test): deduplicate TSIG test helpers (#8009)

Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* fix(dnssec): return nil sigs on sign error (#7999)

Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* fix(dnssec): return nil from ParseKeyFile on error (#8000)

Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* fix(dnsserver): allow view server blocks in any declaration order (#8001)

When using the view plugin, filtered and unfiltered server blocks can
share the same zone and port. The zone overlap validation rejected this
configuration when the unfiltered block was not declared last, because
filtered configs treated an already-registered zone as an error.

Skip the 'already defined' check for configs that have filter functions,
since they are expected to coexist with an unfiltered catch-all block on
the same zone/port.

Fixes #7733

Signed-off-by: umut-polat <52835619+umut-polat@users.noreply.github.com>
Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* fix(doh): use per-connection local address for PROXY protocol (#8005)

Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* fix(transfer): batch AXFR records by message size instead of count (#8002)

Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* fix(tls): use temp dir for keylog test path (#8010)

Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* Rename local_address option to source_address

Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* Rename local_address also in readme
Add test

Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* Resolve change request in pr

Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* fix ci lint

Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* Improve doc on source_address routing needs

Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* Remove added timeout

Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* add use of source address also for health check query

Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* remove untrailing newline from health_test.go

Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* fix file format

Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* Update plugin/forward/setup_test.go

Co-authored-by: Ville Vesilehto <ville@vesilehto.fi>
Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* Remove dead code in TestHealthLocalAddress

Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* Fix misspelling

Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* Try to set default timeout

Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

* fix format in health.go

Signed-off-by: Filippo <filippo.ferrazini@gmail.com>

---------

Signed-off-by: Filippo <filippo.ferrazini@gmail.com>
Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
Signed-off-by: umut-polat <52835619+umut-polat@users.noreply.github.com>
Co-authored-by: Ville Vesilehto <ville@vesilehto.fi>
Co-authored-by: Yong Tang <yong.tang.github@outlook.com>
Co-authored-by: Umut Polat <52835619+umut-polat@users.noreply.github.com>
Co-authored-by: Cedric Wang <wangzongqi@msn.com>
2026-07-09 17:38:22 -07:00

156 lines
3.7 KiB
Go

package proxy
import (
"crypto/tls"
"net"
"net/http"
"runtime"
"sync/atomic"
"time"
"github.com/coredns/coredns/plugin/pkg/log"
"github.com/coredns/coredns/plugin/pkg/up"
)
// Proxy defines an upstream host.
type Proxy struct {
fails uint32
addr string
proxyName string
transport *Transport
protocol string
dohMethod string
readTimeout time.Duration
// health checking
probe *up.Probe
health HealthChecker
}
// NewProxy returns a new proxy.
func NewProxy(proxyName, addr, protocol string) *Proxy {
p := &Proxy{
addr: addr,
fails: 0,
probe: up.New(),
readTimeout: 2 * time.Second,
transport: newTransport(proxyName, addr),
protocol: protocol,
dohMethod: http.MethodPost,
health: NewHealthChecker(proxyName, protocol, true, "."),
proxyName: proxyName,
}
runtime.SetFinalizer(p, (*Proxy).finalizer)
return p
}
func (p *Proxy) Addr() string { return p.addr }
// SetTLSConfig sets the TLS config in the lower p.transport and in the healthchecking client.
func (p *Proxy) SetTLSConfig(cfg *tls.Config) {
p.transport.SetTLSConfig(cfg)
p.health.SetTLSConfig(cfg)
if p.transport.httpClient != nil {
p.transport.httpClient.Transport.(*http.Transport).TLSClientConfig = cfg
}
}
// SetExpire sets the expire duration in the lower p.transport.
func (p *Proxy) SetExpire(expire time.Duration) { p.transport.SetExpire(expire) }
// SetMaxAge sets the maximum connection lifetime in the lower p.transport.
// A value of 0 (default) disables max-age.
func (p *Proxy) SetMaxAge(maxAge time.Duration) { p.transport.SetMaxAge(maxAge) }
// SetMaxIdleConns sets the maximum idle connections per transport type.
// A value of 0 means unlimited (default).
func (p *Proxy) SetMaxIdleConns(n int) { p.transport.SetMaxIdleConns(n) }
func (p *Proxy) SetHTTPClient(client *http.Client) {
p.transport.httpClient = client
}
func (p *Proxy) SetDOHRequestOptions(method string) {
p.dohMethod = method
}
func (p *Proxy) GetHealthchecker() HealthChecker {
return p.health
}
func (p *Proxy) GetTransport() *Transport {
return p.transport
}
func (p *Proxy) Fails() uint32 {
return atomic.LoadUint32(&p.fails)
}
// Healthcheck kicks of a round of health checks for this proxy.
func (p *Proxy) Healthcheck() {
if p.health == nil {
log.Warning("No healthchecker")
return
}
p.probe.Do(func() error {
return p.health.Check(p)
})
}
// Down returns true if this proxy is down, i.e. has *more* fails than maxfails.
func (p *Proxy) Down(maxfails uint32) bool {
if maxfails == 0 {
return false
}
fails := atomic.LoadUint32(&p.fails)
return fails > maxfails
}
// Stop close stops the health checking goroutine.
func (p *Proxy) Stop() { p.probe.Stop() }
func (p *Proxy) finalizer() { p.transport.Stop() }
// Start starts the proxy's healthchecking.
func (p *Proxy) Start(duration time.Duration) {
p.probe.Start(duration)
p.transport.Start()
}
func (p *Proxy) SetReadTimeout(duration time.Duration) {
p.readTimeout = duration
}
// incrementFails increments the number of fails safely.
func (p *Proxy) incrementFails() {
curVal := atomic.LoadUint32(&p.fails)
if curVal > curVal+1 {
// overflow occurred, do not update the counter again
return
}
atomic.AddUint32(&p.fails, 1)
}
// SetLocalAddress sets the local address for the proxy, used as the source address for outbound connections.
func (p *Proxy) SetLocalAddress(addr net.IP) {
p.transport.SetLocalAddress(addr)
if p.transport.httpClient != nil {
httpTransport := p.transport.httpClient.Transport.(*http.Transport)
if addr == nil {
httpTransport.DialContext = nil
return
}
dialer := &net.Dialer{LocalAddr: &net.TCPAddr{IP: addr}}
httpTransport.DialContext = dialer.DialContext
}
}
const (
maxTimeout = 2 * time.Second
)