func (d *Domain) queryOneNS(ns string) {
	client := dns.Client{
		Timeout: opts.timeout,
		Net:     networkPerOptions(),
	}
	m := dns.Msg{}
	m.SetQuestion(d.name, d.qtype)
	m.SetEdns0(uint16(opts.edns0_size), false)
	m.RecursionDesired = false
	var (
		err        error
		firstError error
		netError   net.Error
		msg        *dns.Msg
		i          uint
		allStart   time.Time
		thisStart  time.Time
	)
	qr := NewQueryResult()
	allStart = time.Now()
	for i = 0; i < opts.dnsAttempts; i++ {
		if i > 0 {
			time.Sleep(retryJitter((2 << (i - 1)) * time.Second))
		}
		thisStart = time.Now()
		msg, _, err = client.ExchangeContext(d.ctx, &m, ns+":53")
		qr.DurationQ = time.Since(thisStart)
		if err != nil {
			if i == 0 {
				firstError = err
			}
			if errors.As(err, &netError) && netError.Timeout() {
				continue
			}
		}
		break
	}
	qr.DurationAll = time.Since(allStart)
	// skip the duration rtt
	d.Lock()
	defer d.Unlock()
	if err != nil {
		d.errors = append(d.errors, firstError)
		d.errorsPerNS[ns] = append(d.errorsPerNS[ns], firstError)
		return
	}
	for i := range msg.Answer {
		suffix := ""
		if h := msg.Answer[i].Header(); h != nil {
			if h.Rrtype == dns.TypeDNSKEY {
				dk := msg.Answer[i].(*dns.DNSKEY)
				suffix += "\t;"
				if dk.Flags&dns.SEP != 0 {
					suffix += " [SEP]"
				}
				if dk.Flags&dns.REVOKE != 0 {
					suffix += " [REVOKE]"
				}
				if dk.Flags&dns.ZONE != 0 {
					suffix += " [ZONE]"
				}
				ds := dk.ToDS(dns.SHA256)
				suffix += fmt.Sprintf(" | DS keytag=%d alg=%d digesttype=%d digest=%s",
					ds.KeyTag, ds.Algorithm, ds.DigestType, ds.Digest)
			}
		}
		qr.Add(msg.Answer[i].String() + suffix)
	}
	if len(msg.Answer) == 0 {
		if msg.Truncated {
			d.errorsPerNS[ns] = append(d.errorsPerNS[ns], fmt.Errorf("truncated: %s", ns))
		} else {
			qr.Add("; no results, " + dns.RcodeToString[msg.Rcode])
		}
	} else if msg.Truncated {
		qr.Add("; ... truncated ...")
	}
	// FIXME: prior result?
	d.resultsPerNS[ns] = qr
}
// called under concurrency, must lock Domain before updating maps/hashes
called under concurrency, must lock Domain before updating maps/hashes
// called under concurrency must lock Domain before updating maps / hashes
func (d *Domain) queryOneNS(ns string) {
	client := dns.Client{
		Timeout: opts.timeout,
		Net:     networkPerOptions(),
	}
	m := dns.Msg{}
	m.SetQuestion(d.name, d.qtype)
	m.SetEdns0(uint16(opts.edns0_size), false)
	m.RecursionDesired = false
	var (
		err        error
		firstError error
		netError   net.Error
		msg        *dns.Msg
		i          uint
		allStart   time.Time
		thisStart  time.Time
	)
	qr := NewQueryResult()
	allStart = time.Now()
	for i = 0; i < opts.dnsAttempts; i++ {
		if i > 0 {
			time.Sleep(retryJitter((2 << (i - 1)) * time.Second))
		}
		thisStart = time.Now()
		msg, _, err = client.ExchangeContext(d.ctx, &m, ns+":53")
		qr.DurationQ = time.Since(thisStart)
		if err != nil {
			if i == 0 {
				firstError = err
			}
			if errors.As(err, &netError) && netError.Timeout() {
				continue
			}
		}
		break
	}
	qr.DurationAll = time.Since(allStart)
	d.Lock()
	defer d.Unlock()
	if err != nil {
		d.errors = append(d.errors, firstError)
		d.errorsPerNS[ns] = append(d.errorsPerNS[ns], firstError)
		return
	}
	for i := range msg.Answer {
		suffix := ""
		if h := msg.Answer[i].Header(); h != nil {
			if h.Rrtype == dns.TypeDNSKEY {
				dk := msg.Answer[i].(*dns.DNSKEY)
				suffix += "\t;"
				if dk.Flags&dns.SEP != 0 {
					suffix += " [SEP]"
				}
				if dk.Flags&dns.REVOKE != 0 {
					suffix += " [REVOKE]"
				}
				if dk.Flags&dns.ZONE != 0 {
					suffix += " [ZONE]"
				}
				ds := dk.ToDS(dns.SHA256)
				suffix += fmt.Sprintf(" | DS keytag=%d alg=%d digesttype=%d digest=%s",
					ds.KeyTag, ds.Algorithm, ds.DigestType, ds.Digest)
			}
		}
		qr.Add(msg.Answer[i].String() + suffix)
	}
	if len(msg.Answer) == 0 {
		if msg.Truncated {
			d.errorsPerNS[ns] = append(d.errorsPerNS[ns], fmt.Errorf("truncated: %s", ns))
		} else {
			qr.Add("; no results, " + dns.RcodeToString[msg.Rcode])
		}
	} else if msg.Truncated {
		qr.Add("; ... truncated ...")
	}
	d.resultsPerNS[ns] = qr
}
func ( d * Domain ) queryOneNS ( ns string ) { client := dns . Client { Timeout : opts . timeout , Net : networkPerOptions ( ) , } m := dns . Msg { } m . SetQuestion ( d . name , d . qtype ) m . SetEdns0 ( uint16 ( opts . edns0_size ) , false ) m . RecursionDesired = false var ( err error firstError error netError net . Error msg * dns . Msg i uint allStart time . Time thisStart time . Time ) qr := NewQueryResult ( ) allStart = time . Now ( ) for i = 0 ; i < opts . dnsAttempts ; i ++ { if i > 0 { time . Sleep ( retryJitter ( ( 2 << ( i - 1 ) ) * time . Second ) ) } thisStart = time . Now ( ) msg , _ , err = client . ExchangeContext ( d . ctx , & m , ns + " " ) qr . DurationQ = time . Since ( thisStart ) if err != nil { if i == 0 { firstError = err } if errors . As ( err , & netError ) && netError . Timeout ( ) { continue } } break } qr . DurationAll = time . Since ( allStart ) d . Lock ( ) defer d . Unlock ( ) if err != nil { d . errors = append ( d . errors , firstError ) d . errorsPerNS [ ns ] = append ( d . errorsPerNS [ ns ] , firstError ) return } for i := range msg . Answer { suffix := " " if h := msg . Answer [ i ] . Header ( ) ; h != nil { if h . Rrtype == dns . TypeDNSKEY { dk := msg . Answer [ i ] . ( * dns . DNSKEY ) suffix += " \t " if dk . Flags & dns . SEP != 0 { suffix += " " } if dk . Flags & dns . REVOKE != 0 { suffix += " " } if dk . Flags & dns . ZONE != 0 { suffix += " " } ds := dk . ToDS ( dns . SHA256 ) suffix += fmt . Sprintf ( " " , ds . KeyTag , ds . Algorithm , ds . DigestType , ds . Digest ) } } qr . Add ( msg . Answer [ i ] . String ( ) + suffix ) } if len ( msg . Answer ) == 0 { if msg . Truncated { d . errorsPerNS [ ns ] = append ( d . errorsPerNS [ ns ] , fmt . Errorf ( " " , ns ) ) } else { qr . Add ( " " + dns . RcodeToString [ msg . Rcode ] ) } } else if msg . Truncated { qr . Add ( " " ) } d . resultsPerNS [ ns ] = qr }
called under concurrency, must lock Domain before updating maps/hashes
called under concurrency must lock Domain before updating maps / hashes
// skip the duration rtt // FIXME: prior result?