Update
This commit is contained in:
1 parent
3530afb854
commit
6903fcff27
2146 files changed
+112256
-190703
No files matched your search
+253
-71
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// Copyright (c) Tailscale Inc & contributors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package resolver implements a stub DNS resolver that can also serve
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
"net/netip"
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -39,6 +39,7 @@ import (
|
||||
"tailscale.com/util/clientmetric"
|
||||
"tailscale.com/util/cloudenv"
|
||||
"tailscale.com/util/dnsname"
|
||||
"tailscale.com/util/set"
|
||||
)
|
||||
|
||||
const dnsSymbolicFQDN = "magicdns.localhost-tailscale-daemon."
|
||||
@@ -48,8 +49,26 @@ const dnsSymbolicFQDN = "magicdns.localhost-tailscale-daemon."
|
||||
// truncation in a platform-agnostic way.
|
||||
const maxResponseBytes = 4095
|
||||
|
||||
// defaultTTL is the TTL of all responses from Resolver.
|
||||
const defaultTTL = 600 * time.Second
|
||||
// defaultTTL is the TTL of positive responses from Resolver.
|
||||
//
|
||||
// It's short because the source of truth (the netmap-fed host maps)
|
||||
// is local and in-memory, so re-queries are nearly free, while
|
||||
// anything cached downstream (e.g. mDNSResponder on macOS) delays
|
||||
// clients noticing node renames for the full TTL (tailscale/corp#45631).
|
||||
const defaultTTL = 5 * time.Second
|
||||
|
||||
// negativeTTL is how long resolvers may cache the nonexistence of a
|
||||
// name (or of records of the queried type) for domains we're
|
||||
// authoritative for. It's advertised via the SOA record attached to
|
||||
// the authority section of NXDOMAIN and no-data responses, per RFC
|
||||
// 2308. Without it, some resolvers (notably mDNSResponder) seem to
|
||||
// cache negative entries for a really long time, so a name queried
|
||||
// shortly before a node rename doesn't start resolving for a while
|
||||
// (tailscale/corp#45631).
|
||||
const negativeTTL = 10 * time.Second
|
||||
|
||||
// timeNow is time.Now, except in tests.
|
||||
var timeNow = time.Now
|
||||
|
||||
var (
|
||||
errNotQuery = errors.New("not a DNS query")
|
||||
@@ -69,6 +88,9 @@ type packet struct {
|
||||
// Else forward the query to the most specific matching entry in Routes.
|
||||
// Else return SERVFAIL.
|
||||
type Config struct {
|
||||
// True if [Prefs.CorpDNS] is true or --accept-dns=true was specified.
|
||||
// This should only be used for error handling and health reporting.
|
||||
AcceptDNS bool
|
||||
// Routes is a map of DNS name suffix to the resolvers to use for
|
||||
// queries within that suffix.
|
||||
// Queries only match the most specific suffix.
|
||||
@@ -79,6 +101,12 @@ type Config struct {
|
||||
// LocalDomains is a list of DNS name suffixes that should not be
|
||||
// routed to upstream resolvers.
|
||||
LocalDomains []dnsname.FQDN
|
||||
// SubdomainHosts is a set of FQDNs from Hosts that should also
|
||||
// resolve subdomain queries to the same IPs. If a query like
|
||||
// "sub.node.tailnet.ts.net" doesn't match Hosts directly, and
|
||||
// "node.tailnet.ts.net" is in SubdomainHosts, the query resolves
|
||||
// to the IPs for "node.tailnet.ts.net".
|
||||
SubdomainHosts set.Set[dnsname.FQDN]
|
||||
}
|
||||
|
||||
// WriteToBufioWriter write a debug version of c for logs to w, omitting
|
||||
@@ -162,7 +190,7 @@ func WriteRoutes(w *bufio.Writer, routes map[dnsname.FQDN][]*dnstype.Resolver) {
|
||||
}
|
||||
kk = append(kk, k)
|
||||
}
|
||||
sort.Slice(kk, func(i, j int) bool { return kk[i] < kk[j] })
|
||||
slices.Sort(kk)
|
||||
w.WriteByte('{')
|
||||
for i, k := range kk {
|
||||
if i > 0 {
|
||||
@@ -214,10 +242,61 @@ type Resolver struct {
|
||||
closed chan struct{}
|
||||
|
||||
// mu guards the following fields from being updated while used.
|
||||
mu syncs.Mutex
|
||||
localDomains []dnsname.FQDN
|
||||
hostToIP map[dnsname.FQDN][]netip.Addr
|
||||
ipToHost map[netip.Addr]dnsname.FQDN
|
||||
mu syncs.Mutex
|
||||
localDomains []dnsname.FQDN
|
||||
hostToIP map[dnsname.FQDN][]netip.Addr
|
||||
ipToHost map[netip.Addr]dnsname.FQDN
|
||||
subdomainHosts set.Set[dnsname.FQDN]
|
||||
magicHosts MagicDNSHosts // or nil if none installed
|
||||
}
|
||||
|
||||
// MagicDNSHosts is a live source of MagicDNS host records, installed
|
||||
// via [Resolver.SetMagicDNSHosts].
|
||||
//
|
||||
// It replaces the per-node entries of [Config.Hosts]: instead of the
|
||||
// caller pushing a full snapshot of every node's name and addresses
|
||||
// into the resolver on every (possibly incremental) netmap change,
|
||||
// the resolver pulls the answer for one name on demand from the
|
||||
// caller's live indexes. [Config.Hosts] remains for control's
|
||||
// DNS.ExtraRecords entries, which are few, and is consulted first.
|
||||
//
|
||||
// Implementations must be safe for concurrent use and cheap: the
|
||||
// methods are called on the DNS query serving path. Name lookups are
|
||||
// case-insensitive: the resolver passes lowercase names, but
|
||||
// implementations must not rely on that.
|
||||
//
|
||||
// Short names ("foo") are only resolved if MagicDNS is enabled
|
||||
// for the current tailnet.
|
||||
type MagicDNSHosts interface {
|
||||
// LookupHost returns the IPs to answer for the node with the
|
||||
// given MagicDNS FQDN, and whether the name is known. It returns
|
||||
// all answerable IPs regardless of record type; the resolver
|
||||
// filters them by the query's type, and a known name with no IPs
|
||||
// of the query's family is "name exists, no records", not
|
||||
// NXDOMAIN.
|
||||
LookupHost(dnsname.FQDN) (ips []netip.Addr, ok bool)
|
||||
|
||||
// LookupPTR returns the MagicDNS FQDN of the node that owns
|
||||
// the given Tailscale IP, and whether the IP is known.
|
||||
LookupPTR(netip.Addr) (_ dnsname.FQDN, ok bool)
|
||||
|
||||
// SubdomainHost reports whether fqdn names a node with the
|
||||
// [tailcfg.NodeAttrDNSSubdomainResolve] attribute, whose
|
||||
// subdomains all resolve to the node's own addresses.
|
||||
SubdomainHost(dnsname.FQDN) bool
|
||||
}
|
||||
|
||||
// SetMagicDNSHosts installs the live MagicDNS host source consulted
|
||||
// by forward and reverse MagicDNS lookups that miss [Config.Hosts].
|
||||
// It is expected to be called once, before the resolver serves
|
||||
// queries.
|
||||
func (r *Resolver) SetMagicDNSHosts(h MagicDNSHosts) {
|
||||
if !buildfeatures.HasDNS {
|
||||
return
|
||||
}
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.magicHosts = h
|
||||
}
|
||||
|
||||
type ForwardLinkSelector interface {
|
||||
@@ -255,6 +334,16 @@ func New(logf logger.Logf, linkSel ForwardLinkSelector, dialer *tsdial.Dialer, h
|
||||
|
||||
func (r *Resolver) TestOnlySetHook(hook func(Config)) { r.saveConfigForTests = hook }
|
||||
|
||||
// ProbeLocks acquires and releases the resolver's internal mutexes.
|
||||
func (r *Resolver) ProbeLocks() {
|
||||
r.mu.Lock()
|
||||
r.mu.Unlock()
|
||||
|
||||
if r.forwarder != nil {
|
||||
r.forwarder.probeLocks()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Resolver) SetConfig(cfg Config) error {
|
||||
if !buildfeatures.HasDNS {
|
||||
return nil
|
||||
@@ -271,16 +360,29 @@ func (r *Resolver) SetConfig(cfg Config) error {
|
||||
}
|
||||
}
|
||||
|
||||
r.forwarder.setRoutes(cfg.Routes)
|
||||
r.forwarder.setRoutes(cfg.Routes, cfg.AcceptDNS)
|
||||
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.localDomains = cfg.LocalDomains
|
||||
r.hostToIP = cfg.Hosts
|
||||
r.ipToHost = reverse
|
||||
r.subdomainHosts = cfg.SubdomainHosts
|
||||
return nil
|
||||
}
|
||||
|
||||
// CustomSchemeHandler takes a URI (retrieved from [dnstype.Resolver.Addr]) and
|
||||
// returns an updated URI to use for the current query. The result is only valid
|
||||
// for right now and may change over time.
|
||||
type CustomSchemeHandler func(addr string) (newAddr string, err error)
|
||||
|
||||
// RegisterCustomScheme adds a [CustomSchemaHandler] that is called to provide
|
||||
// an updated address to the forwarder when a [dnstype.Resolver.Addr] uses that
|
||||
// scheme.
|
||||
func (r *Resolver) RegisterCustomScheme(scheme string, h CustomSchemeHandler) error {
|
||||
return r.forwarder.RegisterCustomScheme(scheme, h)
|
||||
}
|
||||
|
||||
// Close shuts down the resolver and ensures poll goroutines have exited.
|
||||
// The Resolver cannot be used again after Close is called.
|
||||
func (r *Resolver) Close() {
|
||||
@@ -328,7 +430,12 @@ func (r *Resolver) Query(ctx context.Context, bs []byte, family string, from net
|
||||
return (<-responses).bs, nil
|
||||
}
|
||||
|
||||
return out, err
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
|
||||
out = checkResponseSizeAndSetTC(out, bs, family, r.logf)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// GetUpstreamResolvers returns the resolvers that would be used to resolve
|
||||
@@ -642,9 +749,26 @@ func (r *Resolver) resolveLocal(domain dnsname.FQDN, typ dns.Type) (netip.Addr,
|
||||
r.mu.Lock()
|
||||
hosts := r.hostToIP
|
||||
localDomains := r.localDomains
|
||||
subdomainHosts := r.subdomainHosts
|
||||
magicHosts := r.magicHosts
|
||||
r.mu.Unlock()
|
||||
|
||||
addrs, found := hosts[domain]
|
||||
if !found && magicHosts != nil {
|
||||
addrs, found = magicHosts.LookupHost(domain)
|
||||
}
|
||||
if !found {
|
||||
for parent := domain.Parent(); parent != ""; parent = parent.Parent() {
|
||||
if subdomainHosts.Contains(parent) {
|
||||
addrs, found = hosts[parent]
|
||||
break
|
||||
}
|
||||
if magicHosts != nil && magicHosts.SubdomainHost(parent) {
|
||||
addrs, found = magicHosts.LookupHost(parent)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
for _, suffix := range localDomains {
|
||||
if suffix.Contains(domain) {
|
||||
@@ -715,23 +839,16 @@ func (r *Resolver) resolveLocal(domain dnsname.FQDN, typ dns.Type) (netip.Addr,
|
||||
}
|
||||
|
||||
// resolveViaDomain synthesizes an IP address for quad-A DNS requests of the form
|
||||
// `<IPv4-address-with-hypens-instead-of-dots>-via-<siteid>[.*]`. Two prior formats that
|
||||
// didn't pan out (due to a Chrome issue and DNS search ndots issues) were
|
||||
// `<IPv4-address>.via-<X>` and the older `via-<X>.<IPv4-address>`,
|
||||
// where X is a decimal, or hex-encoded number with a '0x' prefix.
|
||||
// `<IPv4-address-with-hypens-instead-of-dots>-via-<siteid>[.*]`.
|
||||
// For example: "192-168-1-2-via-7" or "192-168-1-2-via-7.foo.ts.net."
|
||||
//
|
||||
// This exists as a convenient mapping into Tailscales 'Via Range'.
|
||||
//
|
||||
// It returns a zero netip.Addr and true to indicate a successful response with
|
||||
// an empty answers section if the specified domain is a valid Tailscale 4via6
|
||||
// domain, but the request type is neither quad-A nor ALL.
|
||||
//
|
||||
// TODO(maisem/bradfitz/tom): `<IPv4-address>.via-<X>` was introduced
|
||||
// (2022-06-02) to work around an issue in Chrome where it would treat
|
||||
// "http://via-1.1.2.3.4" as a search string instead of a URL. We should rip out
|
||||
// the old format in early 2023.
|
||||
func (r *Resolver) resolveViaDomain(domain dnsname.FQDN, typ dns.Type) (netip.Addr, bool) {
|
||||
fqdn := string(domain.WithoutTrailingDot())
|
||||
func (r *Resolver) resolveViaDomain(dnsName dnsname.FQDN, typ dns.Type) (netip.Addr, bool) {
|
||||
fqdn := string(dnsName.WithoutTrailingDot())
|
||||
switch typ {
|
||||
case dns.TypeA, dns.TypeAAAA, dns.TypeALL:
|
||||
// For Type A requests, we should return a successful response
|
||||
@@ -745,45 +862,23 @@ func (r *Resolver) resolveViaDomain(domain dnsname.FQDN, typ dns.Type) (netip.Ad
|
||||
default:
|
||||
return netip.Addr{}, false
|
||||
}
|
||||
if len(fqdn) < len("via-X.0.0.0.0") {
|
||||
if len(fqdn) < len("0-0-0-0-via-0") {
|
||||
return netip.Addr{}, false // too short to be valid
|
||||
}
|
||||
|
||||
var siteID string
|
||||
var ip4Str string
|
||||
switch {
|
||||
case strings.Contains(fqdn, "-via-"):
|
||||
// Format number 3: "192-168-1-2-via-7" or "192-168-1-2-via-7.foo.ts.net."
|
||||
// Third time's a charm. The earlier two formats follow after this block.
|
||||
firstLabel, domain, _ := strings.Cut(fqdn, ".") // "192-168-1-2-via-7"
|
||||
if !(domain == "" || dnsname.HasSuffix(domain, "ts.net") || dnsname.HasSuffix(domain, "tailscale.net")) {
|
||||
return netip.Addr{}, false
|
||||
}
|
||||
v4hyphens, suffix, ok := strings.Cut(firstLabel, "-via-")
|
||||
if !ok {
|
||||
return netip.Addr{}, false
|
||||
}
|
||||
siteID = suffix
|
||||
ip4Str = strings.ReplaceAll(v4hyphens, "-", ".")
|
||||
case strings.HasPrefix(fqdn, "via-"):
|
||||
firstDot := strings.Index(fqdn, ".")
|
||||
if firstDot < 0 {
|
||||
return netip.Addr{}, false // missing dot delimiters
|
||||
}
|
||||
siteID = fqdn[len("via-"):firstDot]
|
||||
ip4Str = fqdn[firstDot+1:]
|
||||
default:
|
||||
lastDot := strings.LastIndex(fqdn, ".")
|
||||
if lastDot < 0 {
|
||||
return netip.Addr{}, false // missing dot delimiters
|
||||
}
|
||||
suffix := fqdn[lastDot+1:]
|
||||
if !strings.HasPrefix(suffix, "via-") {
|
||||
return netip.Addr{}, false
|
||||
}
|
||||
siteID = suffix[len("via-"):]
|
||||
ip4Str = fqdn[:lastDot]
|
||||
if !strings.Contains(fqdn, "-via-") {
|
||||
return netip.Addr{}, false // not a 4via6 domain
|
||||
}
|
||||
firstLabel, domain, _ := strings.Cut(fqdn, ".") // "192-168-1-2-via-7"
|
||||
if !(domain == "" || dnsname.HasSuffix(domain, "ts.net") || dnsname.HasSuffix(domain, "tailscale.net")) {
|
||||
return netip.Addr{}, false
|
||||
}
|
||||
v4hyphens, suffix, ok := strings.Cut(firstLabel, "-via-")
|
||||
if !ok {
|
||||
return netip.Addr{}, false
|
||||
}
|
||||
siteID := suffix
|
||||
ip4Str := strings.ReplaceAll(v4hyphens, "-", ".")
|
||||
|
||||
ip4, err := netip.ParseAddr(ip4Str)
|
||||
if err != nil {
|
||||
@@ -846,6 +941,9 @@ func (r *Resolver) fqdnForIPLocked(ip netip.Addr, name dnsname.FQDN) (dnsname.FQ
|
||||
}
|
||||
|
||||
ret, ok := r.ipToHost[ip]
|
||||
if !ok && r.magicHosts != nil {
|
||||
ret, ok = r.magicHosts.LookupPTR(ip)
|
||||
}
|
||||
if !ok {
|
||||
for _, suffix := range r.localDomains {
|
||||
if suffix.Contains(name) {
|
||||
@@ -883,6 +981,14 @@ type response struct {
|
||||
|
||||
// NSs are the responses to an NS query.
|
||||
NSs []*net.NS
|
||||
|
||||
// SOAZone, if non-empty, is the zone we're authoritative for
|
||||
// that contains Question's name. marshalResponse attaches its
|
||||
// SOA record to the authority section so that resolvers bound
|
||||
// their negative caching to negativeTTL, per RFC 2308. It must
|
||||
// only be set on negative responses: NXDOMAIN, or success with
|
||||
// no records of the queried type.
|
||||
SOAZone dnsname.FQDN
|
||||
}
|
||||
|
||||
var dnsParserPool = &sync.Pool{
|
||||
@@ -1064,6 +1170,35 @@ func marshalSRV(queryName dns.Name, srvs []*net.SRV, builder *dns.Builder) error
|
||||
return nil
|
||||
}
|
||||
|
||||
// marshalSOA serializes zone's SOA record into the authority section
|
||||
// of an active builder, which must have had StartAuthorities called.
|
||||
// Its only purpose is communicating negativeTTL to caching resolvers
|
||||
// (RFC 2308), so all fields other than the TTLs are placeholders.
|
||||
func marshalSOA(zone dnsname.FQDN, builder *dns.Builder) error {
|
||||
name, err := dns.NewName(zone.WithTrailingDot())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return builder.SOAResource(dns.ResourceHeader{
|
||||
Name: name,
|
||||
Type: dns.TypeSOA,
|
||||
Class: dns.ClassINET,
|
||||
TTL: uint32(negativeTTL / time.Second),
|
||||
}, dns.SOAResource{
|
||||
NS: name,
|
||||
MBox: name,
|
||||
// A serial should only change when the zone data does,
|
||||
// but nothing consumes ours (no secondaries, no zone
|
||||
// transfers), and the current time is at least monotonic
|
||||
// and cheap. Unix seconds fit in the uint32 until 2106.
|
||||
Serial: uint32(timeNow().Unix()),
|
||||
Refresh: uint32(negativeTTL / time.Second),
|
||||
Retry: uint32(negativeTTL / time.Second),
|
||||
Expire: uint32(negativeTTL / time.Second),
|
||||
MinTTL: uint32(negativeTTL / time.Second),
|
||||
})
|
||||
}
|
||||
|
||||
// marshalResponse serializes the DNS response into a new buffer.
|
||||
func marshalResponse(resp *response) ([]byte, error) {
|
||||
resp.Header.Response = true
|
||||
@@ -1098,6 +1233,14 @@ func marshalResponse(resp *response) ([]byte, error) {
|
||||
|
||||
// Only successful responses contain answers.
|
||||
if !isSuccess {
|
||||
if resp.SOAZone != "" {
|
||||
if err := builder.StartAuthorities(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := marshalSOA(resp.SOAZone, &builder); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return builder.Finish()
|
||||
}
|
||||
|
||||
@@ -1131,6 +1274,15 @@ func marshalResponse(resp *response) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.SOAZone != "" {
|
||||
if err := builder.StartAuthorities(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := marshalSOA(resp.SOAZone, &builder); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return builder.Finish()
|
||||
}
|
||||
|
||||
@@ -1248,6 +1400,19 @@ func rdnsNameToIPv6(name dnsname.FQDN) (ip netip.Addr, ok bool) {
|
||||
|
||||
// respondReverse returns a DNS response to a PTR query.
|
||||
// It is assumed that resp.Question is populated by respond before this is called.
|
||||
// authoritativeZoneFor returns the zone from the configured local
|
||||
// domains that contains name, or "" if none does.
|
||||
func (r *Resolver) authoritativeZoneFor(name dnsname.FQDN) dnsname.FQDN {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
for _, suffix := range r.localDomains {
|
||||
if suffix.Contains(name) {
|
||||
return suffix
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r *Resolver) respondReverse(query []byte, name dnsname.FQDN, resp *response) ([]byte, error) {
|
||||
if hasRDNSBonjourPrefix(name) {
|
||||
metricDNSReverseMissBonjour.Add(1)
|
||||
@@ -1259,6 +1424,9 @@ func (r *Resolver) respondReverse(query []byte, name dnsname.FQDN, resp *respons
|
||||
metricDNSReverseMissOther.Add(1)
|
||||
return nil, errNotOurName
|
||||
}
|
||||
if resp.Header.RCode == dns.RCodeNameError {
|
||||
resp.SOAZone = r.authoritativeZoneFor(name)
|
||||
}
|
||||
|
||||
metricDNSMagicDNSSuccessReverse.Add(1)
|
||||
return marshalResponse(resp)
|
||||
@@ -1315,6 +1483,17 @@ func (r *Resolver) respond(query []byte) ([]byte, error) {
|
||||
resp := parser.response()
|
||||
resp.Header.RCode = rcode
|
||||
resp.IP = ip
|
||||
switch {
|
||||
case rcode == dns.RCodeNameError:
|
||||
resp.SOAZone = r.authoritativeZoneFor(name)
|
||||
case rcode == dns.RCodeSuccess && !ip.IsValid():
|
||||
switch parser.Question.Type {
|
||||
case dns.TypeA, dns.TypeAAAA, dns.TypeALL:
|
||||
// The name exists but has no records of the queried
|
||||
// type (e.g. an AAAA query for an IPv4-only node).
|
||||
resp.SOAZone = r.authoritativeZoneFor(name)
|
||||
}
|
||||
}
|
||||
metricDNSMagicDNSSuccessName.Add(1)
|
||||
return marshalResponse(resp)
|
||||
}
|
||||
@@ -1376,21 +1555,24 @@ var (
|
||||
metricDNSFwdErrorType = clientmetric.NewCounter("dns_query_fwd_error_type")
|
||||
metricDNSFwdTruncated = clientmetric.NewCounter("dns_query_fwd_truncated")
|
||||
|
||||
metricDNSFwdUDP = clientmetric.NewCounter("dns_query_fwd_udp") // on entry
|
||||
metricDNSFwdUDPWrote = clientmetric.NewCounter("dns_query_fwd_udp_wrote") // sent UDP packet
|
||||
metricDNSFwdUDPErrorWrite = clientmetric.NewCounter("dns_query_fwd_udp_error_write")
|
||||
metricDNSFwdUDPErrorServer = clientmetric.NewCounter("dns_query_fwd_udp_error_server")
|
||||
metricDNSFwdUDPErrorTxID = clientmetric.NewCounter("dns_query_fwd_udp_error_txid")
|
||||
metricDNSFwdUDPErrorRead = clientmetric.NewCounter("dns_query_fwd_udp_error_read")
|
||||
metricDNSFwdUDPSuccess = clientmetric.NewCounter("dns_query_fwd_udp_success")
|
||||
metricDNSFwdUDP = clientmetric.NewCounter("dns_query_fwd_udp") // on entry
|
||||
metricDNSFwdUDPWrote = clientmetric.NewCounter("dns_query_fwd_udp_wrote") // sent UDP packet
|
||||
metricDNSFwdUDPDropSrc = clientmetric.NewCounter("dns_query_fwd_udp_drop_src")
|
||||
metricDNSFwdUDPErrorWrite = clientmetric.NewCounter("dns_query_fwd_udp_error_write")
|
||||
metricDNSFwdUDPErrorServer = clientmetric.NewCounter("dns_query_fwd_udp_error_server")
|
||||
metricDNSFwdUDPErrorRefused = clientmetric.NewCounter("dns_query_fwd_udp_error_refused")
|
||||
metricDNSFwdUDPErrorTxID = clientmetric.NewCounter("dns_query_fwd_udp_error_txid")
|
||||
metricDNSFwdUDPErrorRead = clientmetric.NewCounter("dns_query_fwd_udp_error_read")
|
||||
metricDNSFwdUDPSuccess = clientmetric.NewCounter("dns_query_fwd_udp_success")
|
||||
|
||||
metricDNSFwdTCP = clientmetric.NewCounter("dns_query_fwd_tcp") // on entry
|
||||
metricDNSFwdTCPWrote = clientmetric.NewCounter("dns_query_fwd_tcp_wrote") // sent TCP packet
|
||||
metricDNSFwdTCPErrorWrite = clientmetric.NewCounter("dns_query_fwd_tcp_error_write")
|
||||
metricDNSFwdTCPErrorServer = clientmetric.NewCounter("dns_query_fwd_tcp_error_server")
|
||||
metricDNSFwdTCPErrorTxID = clientmetric.NewCounter("dns_query_fwd_tcp_error_txid")
|
||||
metricDNSFwdTCPErrorRead = clientmetric.NewCounter("dns_query_fwd_tcp_error_read")
|
||||
metricDNSFwdTCPSuccess = clientmetric.NewCounter("dns_query_fwd_tcp_success")
|
||||
metricDNSFwdTCP = clientmetric.NewCounter("dns_query_fwd_tcp") // on entry
|
||||
metricDNSFwdTCPWrote = clientmetric.NewCounter("dns_query_fwd_tcp_wrote") // sent TCP packet
|
||||
metricDNSFwdTCPErrorWrite = clientmetric.NewCounter("dns_query_fwd_tcp_error_write")
|
||||
metricDNSFwdTCPErrorServer = clientmetric.NewCounter("dns_query_fwd_tcp_error_server")
|
||||
metricDNSFwdTCPErrorRefused = clientmetric.NewCounter("dns_query_fwd_tcp_error_refused")
|
||||
metricDNSFwdTCPErrorTxID = clientmetric.NewCounter("dns_query_fwd_tcp_error_txid")
|
||||
metricDNSFwdTCPErrorRead = clientmetric.NewCounter("dns_query_fwd_tcp_error_read")
|
||||
metricDNSFwdTCPSuccess = clientmetric.NewCounter("dns_query_fwd_tcp_success")
|
||||
|
||||
metricDNSFwdDoH = clientmetric.NewCounter("dns_query_fwd_doh")
|
||||
metricDNSFwdDoHErrorStatus = clientmetric.NewCounter("dns_query_fwd_doh_error_status")
|
||||
|
||||
Reference in new issue
Block a user