diff --git a/src/net/lookup_darwin.go b/src/net/lookup_darwin.go new file mode 100644 index 00000000000000..5c6175ee93462b --- /dev/null +++ b/src/net/lookup_darwin.go @@ -0,0 +1,376 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin + +package net + +import ( + "context" + "internal/bytealg" + "sync" + "syscall" + + "internal/x/net/dns/dnsmessage" +) + +var onceReadProtocols sync.Once + +// readProtocols loads contents of /etc/protocols into protocols map +// for quick access. +func readProtocols() { + file, err := open("/etc/protocols") + if err != nil { + return + } + defer file.close() + + for line, ok := file.readLine(); ok; line, ok = file.readLine() { + // tcp 6 TCP # transmission control protocol + if i := bytealg.IndexByteString(line, '#'); i >= 0 { + line = line[0:i] + } + f := getFields(line) + if len(f) < 2 { + continue + } + if proto, _, ok := dtoi(f[1]); ok { + if _, ok := protocols[f[0]]; !ok { + protocols[f[0]] = proto + } + for _, alias := range f[2:] { + if _, ok := protocols[alias]; !ok { + protocols[alias] = proto + } + } + } + } +} + +// lookupProtocol looks up IP protocol name in /etc/protocols and +// returns correspondent protocol number. +func lookupProtocol(_ context.Context, name string) (int, error) { + onceReadProtocols.Do(readProtocols) + return lookupProtocolMap(name) +} + +func (r *Resolver) dial(ctx context.Context, network, server string) (Conn, error) { + // Calling Dial here is scary -- we have to be sure not to + // dial a name that will require a DNS lookup, or Dial will + // call back here to translate it. The DNS config parser has + // already checked that all the cfg.servers are IP + // addresses, which Dial will use without a DNS lookup. + var c Conn + var err error + if r != nil && r.Dial != nil { + c, err = r.Dial(ctx, network, server) + } else { + var d Dialer + c, err = d.DialContext(ctx, network, server) + } + if err != nil { + return nil, mapErr(err) + } + return c, nil +} + +func (r *Resolver) lookupHost(ctx context.Context, host string) (addrs []string, err error) { + order := systemConf().hostLookupOrder(r, host) + if !r.preferGo() && order == hostLookupCgo { + if addrs, err, ok := cgoLookupHost(ctx, host); ok { + return addrs, err + } + // cgo not available (or netgo); fall back to linked bindings + order = hostLookupFilesDNS + } + + // darwin has unique resolution files, use libSystem binding if cgo is disabled. + addrs, err := resolverSearch(ctx, host, int32(dnsmessage.TypeALL), int32(dnsmessage.ClassINET)) + if err == nil { + return addrs, nil + } + // something went wrong, fallback to Go's DNS resolver + + return r.goLookupHostOrder(ctx, host, order) +} + +func (r *Resolver) lookupIP(ctx context.Context, network, host string) (addrs []IPAddr, err error) { + if r.preferGo() { + return r.goLookupIP(ctx, host) + } + order := systemConf().hostLookupOrder(r, host) + if order == hostLookupCgo { + if addrs, err, ok := cgoLookupIP(ctx, network, host); ok { + return addrs, err + } + + // darwin has unique resolution files, use libSystem binding if cgo is disabled. + addrs, err := resolverSearch(ctx, host, int32(dnsmessage.TypeALL), int32(dnsmessage.ClassINET)) + if err == nil { + return addrs, nil + } + // something went wrong, fallback to Go's DNS resolver + + order = hostLookupFilesDNS + } + ips, _, err := r.goLookupIPCNAMEOrder(ctx, host, order) + return ips, err +} + +func (r *Resolver) lookupPort(ctx context.Context, network, service string) (int, error) { + if !r.preferGo() && systemConf().canUseCgo() { + if port, err, ok := cgoLookupPort(ctx, network, service); ok { + if err != nil { + // Issue 18213: if cgo fails, first check to see whether we + // have the answer baked-in to the net package. + if port, err := goLookupPort(network, service); err == nil { + return port, nil + } + } + return port, err + } + } + return goLookupPort(network, service) +} + +func (r *Resolver) lookupCNAME(ctx context.Context, name string) (string, error) { + if !r.preferGo() && systemConf().canUseCgo() { + if cname, err, ok := cgoLookupCNAME(ctx, name); ok { + return cname, err + } + } + + // darwin has unique resolution files, use libSystem binding if cgo is not an option. + addrs, err := resolverSearch(ctx, name, int32(dnsmessage.TypeCNAME), int32(dnsmessage.ClassINET)) + if err == nil { + return addrs, nil + } + + // something went wrong, fallback to Go's DNS resolver + return r.goLookupCNAME(ctx, name) +} + +func (r *Resolver) lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) { + var target string + if service == "" && proto == "" { + target = name + } else { + target = "_" + service + "._" + proto + "." + name + } + p, server, err := r.lookup(ctx, target, dnsmessage.TypeSRV) + if err != nil { + return "", nil, err + } + var srvs []*SRV + var cname dnsmessage.Name + for { + h, err := p.AnswerHeader() + if err == dnsmessage.ErrSectionDone { + break + } + if err != nil { + return "", nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + if h.Type != dnsmessage.TypeSRV { + if err := p.SkipAnswer(); err != nil { + return "", nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + continue + } + if cname.Length == 0 && h.Name.Length != 0 { + cname = h.Name + } + srv, err := p.SRVResource() + if err != nil { + return "", nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + srvs = append(srvs, &SRV{Target: srv.Target.String(), Port: srv.Port, Priority: srv.Priority, Weight: srv.Weight}) + } + byPriorityWeight(srvs).sort() + return cname.String(), srvs, nil +} + +func (r *Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) { + p, server, err := r.lookup(ctx, name, dnsmessage.TypeMX) + if err != nil { + return nil, err + } + var mxs []*MX + for { + h, err := p.AnswerHeader() + if err == dnsmessage.ErrSectionDone { + break + } + if err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + if h.Type != dnsmessage.TypeMX { + if err := p.SkipAnswer(); err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + continue + } + mx, err := p.MXResource() + if err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + mxs = append(mxs, &MX{Host: mx.MX.String(), Pref: mx.Pref}) + + } + byPref(mxs).sort() + return mxs, nil +} + +func (r *Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) { + p, server, err := r.lookup(ctx, name, dnsmessage.TypeNS) + if err != nil { + return nil, err + } + var nss []*NS + for { + h, err := p.AnswerHeader() + if err == dnsmessage.ErrSectionDone { + break + } + if err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + if h.Type != dnsmessage.TypeNS { + if err := p.SkipAnswer(); err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + continue + } + ns, err := p.NSResource() + if err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + nss = append(nss, &NS{Host: ns.NS.String()}) + } + return nss, nil +} + +func (r *Resolver) lookupTXT(ctx context.Context, name string) ([]string, error) { + p, server, err := r.lookup(ctx, name, dnsmessage.TypeTXT) + if err != nil { + return nil, err + } + var txts []string + for { + h, err := p.AnswerHeader() + if err == dnsmessage.ErrSectionDone { + break + } + if err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + if h.Type != dnsmessage.TypeTXT { + if err := p.SkipAnswer(); err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + continue + } + txt, err := p.TXTResource() + if err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + // Multiple strings in one TXT record need to be + // concatenated without separator to be consistent + // with previous Go resolver. + n := 0 + for _, s := range txt.TXT { + n += len(s) + } + txtJoin := make([]byte, 0, n) + for _, s := range txt.TXT { + txtJoin = append(txtJoin, s...) + } + if len(txts) == 0 { + txts = make([]string, 0, 1) + } + txts = append(txts, string(txtJoin)) + } + return txts, nil +} + +func (r *Resolver) lookupAddr(ctx context.Context, addr string) ([]string, error) { + if !r.preferGo() && systemConf().canUseCgo() { + if ptrs, err, ok := cgoLookupPTR(ctx, addr); ok { + return ptrs, err + } + } + return r.goLookupPTR(ctx, addr) +} + +// concurrentThreadsLimit returns the number of threads we permit to +// run concurrently doing DNS lookups via cgo. A DNS lookup may use a +// file descriptor so we limit this to less than the number of +// permitted open files. On some systems, notably Darwin, if +// getaddrinfo is unable to open a file descriptor it simply returns +// EAI_NONAME rather than a useful error. Limiting the number of +// concurrent getaddrinfo calls to less than the permitted number of +// file descriptors makes that error less likely. We don't bother to +// apply the same limit to DNS lookups run directly from Go, because +// there we will return a meaningful "too many open files" error. +func concurrentThreadsLimit() int { + var rlim syscall.Rlimit + if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlim); err != nil { + return 500 + } + r := int(rlim.Cur) + if r > 500 { + r = 500 + } else if r > 30 { + r -= 30 + } + return r +} diff --git a/src/net/lookup_unix.go b/src/net/lookup_unix.go index 6a22cdccb9ada8..b659aefbc953e3 100644 --- a/src/net/lookup_unix.go +++ b/src/net/lookup_unix.go @@ -2,20 +2,15 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +// +build aix dragonfly freebsd linux netbsd openbsd solaris package net import ( "context" - "errors" - "fmt" "internal/bytealg" - "runtime" - "strings" "sync" "syscall" - "unsafe" "internal/x/net/dns/dnsmessage" ) @@ -86,19 +81,9 @@ func (r *Resolver) lookupHost(ctx context.Context, host string) (addrs []string, if addrs, err, ok := cgoLookupHost(ctx, host); ok { return addrs, err } - // cgo not available (or netgo); fall back to linked bindings + // cgo not available (or netgo); fall back to Go's DNS resolver order = hostLookupFilesDNS } - - // darwin has unique resolution files, use libSystem binding if cgo is disabled. - if runtime.GOOS == "darwin" { - addrs, err := resolverSearch(ctx, host, int32(dnsmessage.TypeALL), int32(dnsmessage.ClassINET)) - if err == nil { - return addrs, nil - } - // something went wrong, fallback to Go's DNS resolver - } - return r.goLookupHostOrder(ctx, host, order) } @@ -111,16 +96,7 @@ func (r *Resolver) lookupIP(ctx context.Context, network, host string) (addrs [] if addrs, err, ok := cgoLookupIP(ctx, network, host); ok { return addrs, err } - - // darwin has unique resolution files, use libSystem binding if cgo is disabled. - if runtime.GOOS == "darwin" { - addrs, err := resolverSearch(ctx, host, int32(dnsmessage.TypeALL), int32(dnsmessage.ClassINET)) - if err == nil { - return addrs, nil - } - // something went wrong, fallback to Go's DNS resolver - } - + // cgo not available (or netgo); fall back to Go's DNS resolver order = hostLookupFilesDNS } ips, _, err := r.goLookupIPCNAMEOrder(ctx, host, order) @@ -149,16 +125,6 @@ func (r *Resolver) lookupCNAME(ctx context.Context, name string) (string, error) return cname, err } } - - // darwin has unique resolution files, use libSystem binding if cgo is not an option. - if runtime.GOOS == "darwin" { - addrs, err := resolverSearch(ctx, name, int32(dnsmessage.TypeCNAME), int32(dnsmessage.ClassINET)) - if err == nil { - return addrs, nil - } - // something went wrong, fallback to Go's DNS resolver - } - return r.goLookupCNAME(ctx, name) } @@ -385,79 +351,3 @@ func concurrentThreadsLimit() int { } return r } - -// resolverSearch is a darwin specific function. It will make a call to the 'res_search' routine in libSystem -// and parse the output as a slice of IPAddr's -func resolverSearch(ctx context.Context, hostname string, rtype, class int32) ([]string, error) { - - var byteHostname = []byte(hostname) - var responseBuffer = [512]byte{} - - retcode := res_search(&byteHostname[0], class, rtype, &responseBuffer[0], 512) - if retcode < 0 { - return nil, errors.New("could not complete domain resolution") - } - - msg := &dnsmessage.Message{} - err := msg.Unpack(responseBuffer[:]) - if err != nil { - return nil, fmt.Errorf("could not parse dns response: %s", err.Error()) - } - - // parse received answers - var dnsParser dnsmessage.Parser - - if _, err := dnsParser.Start(responseBuffer); err != nil { - return nil, err - } - - var answers []string - for { - h, err := dnsParser.AnswerHeader() - if err == dnsmessage.ErrSectionDone { - break - } - if err != nil { - return nil, err - } - - if !strings.EqualFold(h.Name.String(), hostname) { - if err := dnsParser.SkipAnswer(); err != nil { - return nil, err - } - continue - } - - switch h.Type { - case dnsmessage.TypeA: - r, err := dnsParser.AResource() - if err != nil { - return nil, err - } - answers = append(answers, fmt.Stringf("%s", r.A)) - case dnsmessage.TypeAAAA: - r, err := dnsParser.AAAAResource() - if err != nil { - return nil, err - } - answers = append(answers, fmt.Stringf("%s", r.AAAA)) - - case dnsmessage.TypeCNAME: - r, err := dnsParser.CNAMEResource() - if err != nil { - return nil, err - } - answers = append(answers, fmt.Stringf("%s", r.Name)) - } - } - return answers, nil -} - -//go:nosplit -//go:cgo_unsafe_args -func res_search(name *byte, class int32, rtype int32, answer *byte, anslen int32) int32 { - return libcCall(unsafe.Pointer(funcPC(res_search_trampoline)), unsafe.Pointer(&name)) -} -func res_search_trampoline() - -//go:cgo_import_dynamic libc_res_search res_search "/usr/lib/libSystem.B.dylib" diff --git a/src/runtime/lookup_darwin.go b/src/runtime/lookup_darwin.go new file mode 100644 index 00000000000000..388ac13874d401 --- /dev/null +++ b/src/runtime/lookup_darwin.go @@ -0,0 +1,86 @@ +package runtime + +import ( + "context" + "errors" + "strings" + "unsafe" + + "internal/x/net/dns/dnsmessage" +) + +// ResolverSearch is a darwin specific function. It will make a call to the 'res_search' routine in libSystem +// and parse the output as a slice of IPAddr's +func ResolverSearch(ctx context.Context, hostname string, rtype, class int32) ([]string, error) { + + var byteHostname = []byte(hostname) + var responseBuffer = [512]byte{} + + retcode := res_search(&byteHostname[0], class, rtype, &responseBuffer[0], 512) + if retcode < 0 { + return nil, errors.New("could not complete domain resolution") + } + + msg := &dnsmessage.Message{} + err := msg.Unpack(responseBuffer[:]) + if err != nil { + return nil, fmt.Errorf("could not parse dns response: %s", err.Error()) + } + + // parse received answers + var dnsParser dnsmessage.Parser + + if _, err := dnsParser.Start(responseBuffer); err != nil { + return nil, err + } + + var answers []string + for { + h, err := dnsParser.AnswerHeader() + if err == dnsmessage.ErrSectionDone { + break + } + if err != nil { + return nil, err + } + + if !strings.EqualFold(h.Name.String(), hostname) { + if err := dnsParser.SkipAnswer(); err != nil { + return nil, err + } + continue + } + + switch h.Type { + case dnsmessage.TypeA: + r, err := dnsParser.AResource() + if err != nil { + return nil, err + } + answers = append(answers, fmt.Stringf("%s", r.A)) + case dnsmessage.TypeAAAA: + r, err := dnsParser.AAAAResource() + if err != nil { + return nil, err + } + answers = append(answers, fmt.Stringf("%s", r.AAAA)) + + case dnsmessage.TypeCNAME: + r, err := dnsParser.CNAMEResource() + if err != nil { + return nil, err + } + answers = append(answers, fmt.Stringf("%s", r.Name)) + } + } + return answers, nil +} + +//go:nosplit +//go:cgo_unsafe_args +func res_search(name *byte, class int32, rtype int32, answer *byte, anslen int32) int32 { + return libcCall(unsafe.Pointer(funcPC(res_search_trampoline)), unsafe.Pointer(&name)) +} +func res_search_trampoline() + +//go:cgo_import_dynamic libc_res_search res_search "/usr/lib/libSystem.B.dylib" diff --git a/src/net/lookup_darwin_386.s b/src/runtime/lookup_darwin_386.s similarity index 100% rename from src/net/lookup_darwin_386.s rename to src/runtime/lookup_darwin_386.s diff --git a/src/net/lookup_darwin_amd64.s b/src/runtime/lookup_darwin_amd64.s similarity index 100% rename from src/net/lookup_darwin_amd64.s rename to src/runtime/lookup_darwin_amd64.s diff --git a/src/net/lookup_darwin_arm.s b/src/runtime/lookup_darwin_arm.s similarity index 100% rename from src/net/lookup_darwin_arm.s rename to src/runtime/lookup_darwin_arm.s diff --git a/src/net/lookup_darwin_arm64.s b/src/runtime/lookup_darwin_arm64.s similarity index 100% rename from src/net/lookup_darwin_arm64.s rename to src/runtime/lookup_darwin_arm64.s