package net
import (
)
const (
useTCPOnly = true
useUDPOrTCP = false
maxDNSPacketSize = 1232
)
var (
errLameReferral = errors.New("lame referral")
errCannotUnmarshalDNSMessage = errors.New("cannot unmarshal DNS message")
errCannotMarshalDNSMessage = errors.New("cannot marshal DNS message")
errServerMisbehaving = errors.New("server misbehaving")
errInvalidDNSResponse = errors.New("invalid DNS response")
errNoAnswerFromDNSServer = errors.New("no answer from DNS server")
errServerTemporarilyMisbehaving = &temporaryError{"server misbehaving"}
)
var netedns0 = godebug.New("netedns0")
func ( dnsmessage.Question, bool) ( uint16, , []byte, error) {
= uint16(randInt())
:= dnsmessage.NewBuilder(make([]byte, 2, 514), dnsmessage.Header{ID: , RecursionDesired: true, AuthenticData: })
if := .StartQuestions(); != nil {
return 0, nil, nil,
}
if := .Question(); != nil {
return 0, nil, nil,
}
if netedns0.Value() == "0" {
netedns0.IncNonDefault()
} else {
if := .StartAdditionals(); != nil {
return 0, nil, nil,
}
var dnsmessage.ResourceHeader
if := .SetEDNS0(maxDNSPacketSize, dnsmessage.RCodeSuccess, false); != nil {
return 0, nil, nil,
}
if := .OPTResource(, dnsmessage.OPTResource{}); != nil {
return 0, nil, nil,
}
}
, = .Finish()
if != nil {
return 0, nil, nil,
}
= [2:]
:= len() - 2
[0] = byte( >> 8)
[1] = byte()
return , , , nil
}
func ( uint16, dnsmessage.Question, dnsmessage.Header, dnsmessage.Question) bool {
if !.Response {
return false
}
if != .ID {
return false
}
if .Type != .Type || .Class != .Class || !equalASCIIName(.Name, .Name) {
return false
}
return true
}
func ( Conn, uint16, dnsmessage.Question, []byte) (dnsmessage.Parser, dnsmessage.Header, error) {
if , := .Write(); != nil {
return dnsmessage.Parser{}, dnsmessage.Header{},
}
= make([]byte, maxDNSPacketSize)
for {
, := .Read()
if != nil {
return dnsmessage.Parser{}, dnsmessage.Header{},
}
var dnsmessage.Parser
, := .Start([:])
if != nil {
continue
}
, := .Question()
if != nil || !checkResponse(, , , ) {
continue
}
return , , nil
}
}
func ( Conn, uint16, dnsmessage.Question, []byte) (dnsmessage.Parser, dnsmessage.Header, error) {
if , := .Write(); != nil {
return dnsmessage.Parser{}, dnsmessage.Header{},
}
= make([]byte, 1280)
if , := io.ReadFull(, [:2]); != nil {
return dnsmessage.Parser{}, dnsmessage.Header{},
}
:= int([0])<<8 | int([1])
if > len() {
= make([]byte, )
}
, := io.ReadFull(, [:])
if != nil {
return dnsmessage.Parser{}, dnsmessage.Header{},
}
var dnsmessage.Parser
, := .Start([:])
if != nil {
return dnsmessage.Parser{}, dnsmessage.Header{}, errCannotUnmarshalDNSMessage
}
, := .Question()
if != nil {
return dnsmessage.Parser{}, dnsmessage.Header{}, errCannotUnmarshalDNSMessage
}
if !checkResponse(, , , ) {
return dnsmessage.Parser{}, dnsmessage.Header{}, errInvalidDNSResponse
}
return , , nil
}
func ( *Resolver) ( context.Context, string, dnsmessage.Question, time.Duration, , bool) (dnsmessage.Parser, dnsmessage.Header, error) {
.Class = dnsmessage.ClassINET
, , , := newRequest(, )
if != nil {
return dnsmessage.Parser{}, dnsmessage.Header{}, errCannotMarshalDNSMessage
}
var []string
if {
= []string{"tcp"}
} else {
= []string{"udp", "tcp"}
}
for , := range {
, := context.WithDeadline(, time.Now().Add())
defer ()
, := .dial(, , )
if != nil {
return dnsmessage.Parser{}, dnsmessage.Header{},
}
if , := .Deadline(); && !.IsZero() {
.SetDeadline()
}
var dnsmessage.Parser
var dnsmessage.Header
if , := .(PacketConn); {
, , = dnsPacketRoundTrip(, , , )
} else {
, , = dnsStreamRoundTrip(, , , )
}
.Close()
if != nil {
return dnsmessage.Parser{}, dnsmessage.Header{}, mapErr()
}
if := .SkipQuestion(); != dnsmessage.ErrSectionDone {
return dnsmessage.Parser{}, dnsmessage.Header{}, errInvalidDNSResponse
}
if .Truncated && == "udp" {
continue
}
return , , nil
}
return dnsmessage.Parser{}, dnsmessage.Header{}, errNoAnswerFromDNSServer
}
func ( *dnsmessage.Parser, dnsmessage.Header) error {
, := extractExtendedRCode(*, )
if == dnsmessage.RCodeNameError {
return errNoSuchHost
}
, := .AnswerHeader()
if != nil && != dnsmessage.ErrSectionDone {
return errCannotUnmarshalDNSMessage
}
if == dnsmessage.RCodeSuccess && !.Authoritative && !.RecursionAvailable && == dnsmessage.ErrSectionDone && ! {
return errLameReferral
}
if != dnsmessage.RCodeSuccess && != dnsmessage.RCodeNameError {
if == dnsmessage.RCodeServerFailure {
return errServerTemporarilyMisbehaving
}
return errServerMisbehaving
}
return nil
}
func ( *dnsmessage.Parser, dnsmessage.Type) error {
for {
, := .AnswerHeader()
if == dnsmessage.ErrSectionDone {
return errNoSuchHost
}
if != nil {
return errCannotUnmarshalDNSMessage
}
if .Type == {
return nil
}
if := .SkipAnswer(); != nil {
return errCannotUnmarshalDNSMessage
}
}
}
func ( dnsmessage.Parser, dnsmessage.Header) (dnsmessage.RCode, bool) {
.SkipAllAnswers()
.SkipAllAuthorities()
:= false
for {
, := .AdditionalHeader()
if != nil {
return .RCode,
}
= true
if .Type == dnsmessage.TypeOPT {
return .ExtendedRCode(.RCode),
}
if := .SkipAdditional(); != nil {
return .RCode,
}
}
}
func ( *Resolver) ( context.Context, *dnsConfig, string, dnsmessage.Type) (dnsmessage.Parser, string, error) {
var error
:= .serverOffset()
:= uint32(len(.servers))
, := dnsmessage.NewName()
if != nil {
return dnsmessage.Parser{}, "", &DNSError{Err: errCannotMarshalDNSMessage.Error(), Name: }
}
:= dnsmessage.Question{
Name: ,
Type: ,
Class: dnsmessage.ClassINET,
}
for := 0; < .attempts; ++ {
for := uint32(0); < ; ++ {
:= .servers[(+)%]
, , := .exchange(, , , .timeout, .useTCP, .trustAD)
if != nil {
:= newDNSError(, , )
if , := .(*OpError); {
.IsTemporary = true
}
=
continue
}
if := checkHeader(&, ); != nil {
if == errNoSuchHost {
return , , newDNSError(errNoSuchHost, , )
}
= newDNSError(, , )
continue
}
if := skipToAnswer(&, ); != nil {
if == errNoSuchHost {
return , , newDNSError(errNoSuchHost, , )
}
= newDNSError(, , )
continue
}
return , , nil
}
}
return dnsmessage.Parser{}, "",
}
type resolverConfig struct {
initOnce sync.Once
ch chan struct{}
lastChecked time.Time
dnsConfig atomic.Pointer[dnsConfig]
}
var resolvConf resolverConfig
func () *dnsConfig {
resolvConf.tryUpdate("/etc/resolv.conf")
return resolvConf.dnsConfig.Load()
}
func ( *resolverConfig) () {
.dnsConfig.Store(dnsReadConfig("/etc/resolv.conf"))
.lastChecked = time.Now()
.ch = make(chan struct{}, 1)
}
func ( *resolverConfig) ( string) {
.initOnce.Do(.init)
if .dnsConfig.Load().noReload {
return
}
if !.tryAcquireSema() {
return
}
defer .releaseSema()
:= time.Now()
if .lastChecked.After(.Add(-5 * time.Second)) {
return
}
.lastChecked =
switch runtime.GOOS {
case "windows":
default:
var time.Time
if , := os.Stat(); == nil {
= .ModTime()
}
if .Equal(.dnsConfig.Load().mtime) {
return
}
}
:= dnsReadConfig()
.dnsConfig.Store()
}
func ( *resolverConfig) () bool {
select {
case .ch <- struct{}{}:
return true
default:
return false
}
}
func ( *resolverConfig) () {
<-.ch
}
func ( *Resolver) ( context.Context, string, dnsmessage.Type, *dnsConfig) (dnsmessage.Parser, string, error) {
if !isDomainName() {
return dnsmessage.Parser{}, "", newDNSError(errNoSuchHost, , "")
}
if == nil {
= getSystemDNSConfig()
}
var (
dnsmessage.Parser
string
error
)
for , := range .nameList() {
, , = .tryOneName(, , , )
if == nil {
break
}
if , := .(Error); && .Temporary() && .strictErrors() {
break
}
}
if == nil {
return , , nil
}
if , := .(*DNSError); {
.Name =
}
return dnsmessage.Parser{}, "",
}
func ( string) bool {
if == "" {
return true
}
= stringslite.TrimSuffix(, ".")
return stringsHasSuffixFold(, ".onion")
}
func ( *dnsConfig) ( string) []string {
:= len()
:= > 0 && [-1] == '.'
if > 254 || == 254 && ! {
return nil
}
if {
if avoidDNS() {
return nil
}
return []string{}
}
:= bytealg.CountString(, '.') >= .ndots
+= "."
++
:= make([]string, 0, 1+len(.search))
if && !avoidDNS() {
= append(, )
}
for , := range .search {
:= +
if !avoidDNS() && len() <= 254 {
= append(, )
}
}
if ! && !avoidDNS() {
= append(, )
}
return
}
type hostLookupOrder int
const (
hostLookupCgo hostLookupOrder = iota
hostLookupFilesDNS
hostLookupDNSFiles
hostLookupFiles
hostLookupDNS
)
var lookupOrderName = map[hostLookupOrder]string{
hostLookupCgo: "cgo",
hostLookupFilesDNS: "files,dns",
hostLookupDNSFiles: "dns,files",
hostLookupFiles: "files",
hostLookupDNS: "dns",
}
func ( hostLookupOrder) () string {
if , := lookupOrderName[]; {
return
}
return "hostLookupOrder=" + itoa.Itoa(int()) + "??"
}
func ( *Resolver) ( context.Context, string, hostLookupOrder, *dnsConfig) ( []string, error) {
if == hostLookupFilesDNS || == hostLookupFiles {
, _ = lookupStaticHost()
if len() > 0 {
return
}
if == hostLookupFiles {
return nil, newDNSError(errNoSuchHost, , "")
}
}
, , := .goLookupIPCNAMEOrder(, "ip", , , )
if != nil {
return
}
= make([]string, 0, len())
for , := range {
= append(, .String())
}
return
}
func ( string) ( []IPAddr, string) {
, := lookupStaticHost()
for , := range {
, := splitHostZone()
if := ParseIP(); != nil {
:= IPAddr{IP: , Zone: }
= append(, )
}
}
sortByRFC6724()
return ,
}
func ( *Resolver) ( context.Context, , string, hostLookupOrder, *dnsConfig) ( []IPAddr, error) {
, _, = .goLookupIPCNAMEOrder(, , , , )
return
}
func ( *Resolver) ( context.Context, , string, hostLookupOrder, *dnsConfig) ( []IPAddr, dnsmessage.Name, error) {
if == hostLookupFilesDNS || == hostLookupFiles {
var string
, = goLookupIPFiles()
if len() > 0 {
var error
, = dnsmessage.NewName()
if != nil {
return nil, dnsmessage.Name{},
}
return , , nil
}
if == hostLookupFiles {
return nil, dnsmessage.Name{}, newDNSError(errNoSuchHost, , "")
}
}
if !isDomainName() {
return nil, dnsmessage.Name{}, newDNSError(errNoSuchHost, , "")
}
type struct {
dnsmessage.Parser
string
error
}
if == nil {
= getSystemDNSConfig()
}
:= make(chan , 1)
:= []dnsmessage.Type{dnsmessage.TypeA, dnsmessage.TypeAAAA}
if == "CNAME" {
= append(, dnsmessage.TypeCNAME)
}
switch ipVersion() {
case '4':
= []dnsmessage.Type{dnsmessage.TypeA}
case '6':
= []dnsmessage.Type{dnsmessage.TypeAAAA}
}
var func( string, dnsmessage.Type)
var func( string, dnsmessage.Type)
if .singleRequest {
= func( string, dnsmessage.Type) {}
= func( string, dnsmessage.Type) {
dnsWaitGroup.Add(1)
defer dnsWaitGroup.Done()
, , := .tryOneName(, , , )
return {, , }
}
} else {
= func( string, dnsmessage.Type) {
dnsWaitGroup.Add(1)
go func( dnsmessage.Type) {
, , := .tryOneName(, , , )
<- {, , }
dnsWaitGroup.Done()
}()
}
= func( string, dnsmessage.Type) {
return <-
}
}
var error
for , := range .nameList() {
for , := range {
(, )
}
:= false
for , := range {
:= (, )
if . != nil {
if , := ..(Error); && .Temporary() && .strictErrors() {
= true
= .
} else if == nil || == +"." {
= .
}
continue
}
:
for {
, := ..AnswerHeader()
if != nil && != dnsmessage.ErrSectionDone {
= &DNSError{
Err: errCannotUnmarshalDNSMessage.Error(),
Name: ,
Server: .,
}
}
if != nil {
break
}
switch .Type {
case dnsmessage.TypeA:
, := ..AResource()
if != nil {
= &DNSError{
Err: errCannotUnmarshalDNSMessage.Error(),
Name: ,
Server: .,
}
break
}
= append(, IPAddr{IP: IP(.A[:])})
if .Length == 0 && .Name.Length != 0 {
= .Name
}
case dnsmessage.TypeAAAA:
, := ..AAAAResource()
if != nil {
= &DNSError{
Err: errCannotUnmarshalDNSMessage.Error(),
Name: ,
Server: .,
}
break
}
= append(, IPAddr{IP: IP(.AAAA[:])})
if .Length == 0 && .Name.Length != 0 {
= .Name
}
case dnsmessage.TypeCNAME:
, := ..CNAMEResource()
if != nil {
= &DNSError{
Err: errCannotUnmarshalDNSMessage.Error(),
Name: ,
Server: .,
}
break
}
if .Length == 0 && .CNAME.Length > 0 {
= .CNAME
}
default:
if := ..SkipAnswer(); != nil {
= &DNSError{
Err: errCannotUnmarshalDNSMessage.Error(),
Name: ,
Server: .,
}
break
}
continue
}
}
}
if {
= nil
break
}
if len() > 0 || == "CNAME" && .Length > 0 {
break
}
}
if , := .(*DNSError); {
.Name =
}
sortByRFC6724()
if len() == 0 && !( == "CNAME" && .Length > 0) {
if == hostLookupDNSFiles {
var string
, = goLookupIPFiles()
if len() > 0 {
var error
, = dnsmessage.NewName()
if != nil {
return nil, dnsmessage.Name{},
}
return , , nil
}
}
if != nil {
return nil, dnsmessage.Name{},
}
}
return , , nil
}
func ( *Resolver) ( context.Context, string, hostLookupOrder, *dnsConfig) (string, error) {
, , := .goLookupIPCNAMEOrder(, "CNAME", , , )
return .String(),
}
func ( *Resolver) ( context.Context, string, hostLookupOrder, *dnsConfig) ([]string, error) {
if == hostLookupFiles || == hostLookupFilesDNS {
:= lookupStaticAddr()
if len() > 0 {
return , nil
}
if == hostLookupFiles {
return nil, newDNSError(errNoSuchHost, , "")
}
}
, := reverseaddr()
if != nil {
return nil,
}
, , := .lookup(, , dnsmessage.TypePTR, )
if != nil {
var *DNSError
if errors.As(, &) && .IsNotFound {
if == hostLookupDNSFiles {
:= lookupStaticAddr()
if len() > 0 {
return , nil
}
}
}
return nil,
}
var []string
for {
, := .AnswerHeader()
if == dnsmessage.ErrSectionDone {
break
}
if != nil {
return nil, &DNSError{
Err: errCannotUnmarshalDNSMessage.Error(),
Name: ,
Server: ,
}
}
if .Type != dnsmessage.TypePTR {
:= .SkipAnswer()
if != nil {
return nil, &DNSError{
Err: errCannotUnmarshalDNSMessage.Error(),
Name: ,
Server: ,
}
}
continue
}
, := .PTRResource()
if != nil {
return nil, &DNSError{
Err: errCannotUnmarshalDNSMessage.Error(),
Name: ,
Server: ,
}
}
= append(, .PTR.String())
}
return , nil
}