package net
import (
)
const (
useTCPOnly = true
useUDPOrTCP = false
)
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 = errors.New("server misbehaving")
)
func ( dnsmessage.Question) ( uint16, , []byte, error) {
= uint16(randInt())
:= dnsmessage.NewBuilder(make([]byte, 2, 514), dnsmessage.Header{ID: , RecursionDesired: true})
.EnableCompression()
if := .StartQuestions(); != nil {
return 0, nil, nil,
}
if := .Question(); != nil {
return 0, nil, nil,
}
, = .Finish()
= [2:]
:= len() - 2
[0] = byte( >> 8)
[1] = byte()
return , , ,
}
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, 512)
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 {
continue
}
return , , nil
}
return dnsmessage.Parser{}, dnsmessage.Header{}, errNoAnswerFromDNSServer
}
func ( *dnsmessage.Parser, dnsmessage.Header) error {
if .RCode == dnsmessage.RCodeNameError {
return errNoSuchHost
}
, := .AnswerHeader()
if != nil && != dnsmessage.ErrSectionDone {
return errCannotUnmarshalDNSMessage
}
if .RCode == dnsmessage.RCodeSuccess && !.Authoritative && !.RecursionAvailable && == dnsmessage.ErrSectionDone {
return errLameReferral
}
if .RCode != dnsmessage.RCodeSuccess && .RCode != dnsmessage.RCodeNameError {
if .RCode == 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 ( *Resolver) ( context.Context, *dnsConfig, string, dnsmessage.Type) (dnsmessage.Parser, string, error) {
var error
:= .serverOffset()
:= uint32(len(.servers))
, := dnsmessage.NewName()
if != nil {
return dnsmessage.Parser{}, "", errCannotMarshalDNSMessage
}
:= dnsmessage.Question{
Name: ,
Type: ,
Class: dnsmessage.ClassINET,
}
for := 0; < .attempts; ++ {
for := uint32(0); < ; ++ {
:= .servers[(+)%]
, , := .exchange(, , , .timeout, .useTCP)
if != nil {
:= &DNSError{
Err: .Error(),
Name: ,
Server: ,
}
if , := .(Error); && .Timeout() {
.IsTimeout = true
}
if , := .(*OpError); {
.IsTemporary = true
}
=
continue
}
if := checkHeader(&, ); != nil {
:= &DNSError{
Err: .Error(),
Name: ,
Server: ,
}
if == errServerTemporarilyMisbehaving {
.IsTemporary = true
}
if == errNoSuchHost {
.IsNotFound = true
return , ,
}
=
continue
}
= skipToAnswer(&, )
if == nil {
return , , nil
}
= &DNSError{
Err: .Error(),
Name: ,
Server: ,
}
if == errNoSuchHost {
.(*DNSError).IsNotFound = true
return , ,
}
}
}
return dnsmessage.Parser{}, "",
}
type resolverConfig struct {
initOnce sync.Once
ch chan struct{}
lastChecked time.Time
mu sync.RWMutex
dnsConfig *dnsConfig
}
var resolvConf resolverConfig
func ( *resolverConfig) () {
.dnsConfig = systemConf().resolv
if .dnsConfig == nil {
.dnsConfig = dnsReadConfig("/etc/resolv.conf")
}
.lastChecked = time.Now()
.ch = make(chan struct{}, 1)
}
func ( *resolverConfig) ( string) {
.initOnce.Do(.init)
if !.tryAcquireSema() {
return
}
defer .releaseSema()
:= time.Now()
if .lastChecked.After(.Add(-5 * time.Second)) {
return
}
.lastChecked =
var time.Time
if , := os.Stat(); == nil {
= .ModTime()
}
if .Equal(.dnsConfig.mtime) {
return
}
:= dnsReadConfig()
.mu.Lock()
.dnsConfig =
.mu.Unlock()
}
func ( *resolverConfig) () bool {
select {
case .ch <- struct{}{}:
return true
default:
return false
}
}
func ( *resolverConfig) () {
<-.ch
}
func ( *Resolver) ( context.Context, string, dnsmessage.Type) (dnsmessage.Parser, string, error) {
if !isDomainName() {
return dnsmessage.Parser{}, "", &DNSError{Err: errNoSuchHost.Error(), Name: , IsNotFound: true}
}
resolvConf.tryUpdate("/etc/resolv.conf")
resolvConf.mu.RLock()
:= resolvConf.dnsConfig
resolvConf.mu.RUnlock()
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
}
if [len()-1] == '.' {
= [:len()-1]
}
return stringsHasSuffixFold(, ".onion")
}
func ( *dnsConfig) ( string) []string {
if avoidDNS() {
return nil
}
:= len()
:= > 0 && [-1] == '.'
if > 254 || == 254 && {
return nil
}
if {
return []string{}
}
:= count(, '.') >= .ndots
+= "."
++
:= make([]string, 0, 1+len(.search))
if {
= append(, )
}
for , := range .search {
if +len() <= 254 {
= append(, +)
}
}
if ! {
= 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(int()) + "??"
}
func ( *Resolver) ( context.Context, string) ( []string, error) {
return .goLookupHostOrder(, , hostLookupFilesDNS)
}
func ( *Resolver) ( context.Context, string, hostLookupOrder) ( []string, error) {
if == hostLookupFilesDNS || == hostLookupFiles {
= lookupStaticHost()
if len() > 0 || == hostLookupFiles {
return
}
}
, , := .goLookupIPCNAMEOrder(, , )
if != nil {
return
}
= make([]string, 0, len())
for , := range {
= append(, .String())
}
return
}
func ( string) ( []IPAddr) {
for , := range lookupStaticHost() {
, := splitHostZone()
if := ParseIP(); != nil {
:= IPAddr{IP: , Zone: }
= append(, )
}
}
sortByRFC6724()
return
}
func ( *Resolver) ( context.Context, string) ( []IPAddr, error) {
:= systemConf().hostLookupOrder(, )
, _, = .goLookupIPCNAMEOrder(, , )
return
}
func ( *Resolver) ( context.Context, string, hostLookupOrder) ( []IPAddr, dnsmessage.Name, error) {
if == hostLookupFilesDNS || == hostLookupFiles {
= goLookupIPFiles()
if len() > 0 || == hostLookupFiles {
return , dnsmessage.Name{}, nil
}
}
if !isDomainName() {
return nil, dnsmessage.Name{}, &DNSError{Err: errNoSuchHost.Error(), Name: , IsNotFound: true}
}
resolvConf.tryUpdate("/etc/resolv.conf")
resolvConf.mu.RLock()
:= resolvConf.dnsConfig
resolvConf.mu.RUnlock()
type struct {
dnsmessage.Parser
string
error
}
:= make(chan , 1)
:= [...]dnsmessage.Type{dnsmessage.TypeA, 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: "cannot marshal DNS message",
Name: ,
Server: .,
}
}
if != nil {
break
}
switch .Type {
case dnsmessage.TypeA:
, := ..AResource()
if != nil {
= &DNSError{
Err: "cannot marshal DNS message",
Name: ,
Server: .,
}
break
}
= append(, IPAddr{IP: IP(.A[:])})
case dnsmessage.TypeAAAA:
, := ..AAAAResource()
if != nil {
= &DNSError{
Err: "cannot marshal DNS message",
Name: ,
Server: .,
}
break
}
= append(, IPAddr{IP: IP(.AAAA[:])})
default:
if := ..SkipAnswer(); != nil {
= &DNSError{
Err: "cannot marshal DNS message",
Name: ,
Server: .,
}
break
}
continue
}
if .Length == 0 && .Name.Length != 0 {
= .Name
}
}
}
if {
= nil
break
}
if len() > 0 {
break
}
}
if , := .(*DNSError); {
.Name =
}
sortByRFC6724()
if len() == 0 {
if == hostLookupDNSFiles {
= goLookupIPFiles()
}
if len() == 0 && != nil {
return nil, dnsmessage.Name{},
}
}
return , , nil
}
func ( *Resolver) ( context.Context, string) (string, error) {
:= systemConf().hostLookupOrder(, )
, , := .goLookupIPCNAMEOrder(, , )
return .String(),
}
func ( *Resolver) ( context.Context, string) ([]string, error) {
:= lookupStaticAddr()
if len() > 0 {
return , nil
}
, := reverseaddr()
if != nil {
return nil,
}
, , := .lookup(, , dnsmessage.TypePTR)
if != nil {
return nil,
}
var []string
for {
, := .AnswerHeader()
if == dnsmessage.ErrSectionDone {
break
}
if != nil {
return nil, &DNSError{
Err: "cannot marshal DNS message",
Name: ,
Server: ,
}
}
if .Type != dnsmessage.TypePTR {
:= .SkipAnswer()
if != nil {
return nil, &DNSError{
Err: "cannot marshal DNS message",
Name: ,
Server: ,
}
}
continue
}
, := .PTRResource()
if != nil {
return nil, &DNSError{
Err: "cannot marshal DNS message",
Name: ,
Server: ,
}
}
= append(, .PTR.String())
}
return , nil
}