2023-01-27 21:24:30 +00:00
|
|
|
package netutil
|
|
|
|
|
|
|
|
import (
|
2024-04-17 18:45:48 +00:00
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"math/rand"
|
|
|
|
"net"
|
2023-01-27 21:24:30 +00:00
|
|
|
"strings"
|
2024-04-17 18:45:48 +00:00
|
|
|
"time"
|
2023-01-27 21:24:30 +00:00
|
|
|
)
|
|
|
|
|
2024-06-12 09:47:44 +00:00
|
|
|
type resolver interface {
|
|
|
|
LookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*net.SRV, err error)
|
|
|
|
LookupIPAddr(ctx context.Context, host string) ([]net.IPAddr, error)
|
|
|
|
LookupMX(ctx context.Context, name string) ([]*net.MX, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Resolver is default DNS resolver.
|
|
|
|
var Resolver resolver
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
Resolver = &net.Resolver{
|
|
|
|
PreferGo: true,
|
|
|
|
StrictErrors: true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-27 21:24:30 +00:00
|
|
|
// IsTrivialNetworkError returns true if the err can be ignored during logging.
|
|
|
|
func IsTrivialNetworkError(err error) bool {
|
|
|
|
// Suppress trivial network errors, which could occur at remote side.
|
|
|
|
s := err.Error()
|
|
|
|
if strings.Contains(s, "broken pipe") || strings.Contains(s, "reset by peer") {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
2024-04-17 18:45:48 +00:00
|
|
|
|
|
|
|
// DialMaybeSRV dials the given addr.
|
|
|
|
//
|
|
|
|
// The addr may be either the usual TCP address or srv+host form, where host is SRV addr.
|
|
|
|
// If the addr has srv+host form, then the host is resolved with SRV into randomly chosen TCP address for the connection.
|
|
|
|
func DialMaybeSRV(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
if strings.HasPrefix(addr, "srv+") {
|
|
|
|
addr = strings.TrimPrefix(addr, "srv+")
|
2024-04-17 18:47:59 +00:00
|
|
|
if n := strings.IndexByte(addr, ':'); n >= 0 {
|
|
|
|
// Drop port, since it should be automatically resolved via DNS SRV lookup below.
|
|
|
|
addr = addr[:n]
|
|
|
|
}
|
2024-04-17 18:45:48 +00:00
|
|
|
_, addrs, err := Resolver.LookupSRV(ctx, "", "", addr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("cannot resolve SRV addr %s: %w", addr, err)
|
|
|
|
}
|
|
|
|
if len(addrs) == 0 {
|
|
|
|
return nil, fmt.Errorf("missing SRV records for %s", addr)
|
|
|
|
}
|
|
|
|
n := rand.Intn(len(addrs))
|
|
|
|
addr = fmt.Sprintf("%s:%d", addrs[n].Target, addrs[n].Port)
|
|
|
|
}
|
|
|
|
return Dialer.DialContext(ctx, network, addr)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Dialer is default network dialer.
|
|
|
|
var Dialer = &net.Dialer{
|
|
|
|
Timeout: 30 * time.Second,
|
|
|
|
KeepAlive: 30 * time.Second,
|
|
|
|
DualStack: TCP6Enabled(),
|
|
|
|
}
|