-
Notifications
You must be signed in to change notification settings - Fork 17.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
net: use libSystem bindings for DNS resolution on macos if cgo is una…
…vailable This change adds directives to link the res_search function in libSystem. The corresponding Go function is then used in `lookup_darwin.go` for resolution when cgo is disabled. This makes DNS resolution logic more reliable as macOS has some unique quirks such as the `/etc/resolver/` directory for specifying nameservers. Fixes #12524 Change-Id: I367263c4951383965b3ef6491196152f78e614b1 GitHub-Last-Rev: 3c3ff6b GitHub-Pull-Request: #30686 Reviewed-on: https://go-review.googlesource.com/c/go/+/166297 Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Keith Randall <[email protected]>
- Loading branch information
1 parent
bead358
commit f6b42a5
Showing
8 changed files
with
386 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
// Copyright 2019 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 !netgo,!cgo | ||
// +build darwin | ||
|
||
package net | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"sync" | ||
|
||
"golang.org/x/net/dns/dnsmessage" | ||
) | ||
|
||
type addrinfoErrno int | ||
|
||
func (eai addrinfoErrno) Error() string { return "<nil>" } | ||
func (eai addrinfoErrno) Temporary() bool { return false } | ||
func (eai addrinfoErrno) Timeout() bool { return false } | ||
|
||
func cgoLookupHost(ctx context.Context, name string) (addrs []string, err error, completed bool) { | ||
resources, err := resolverGetResources(ctx, name, int32(dnsmessage.TypeALL), int32(dnsmessage.ClassINET)) | ||
if err != nil { | ||
return | ||
} | ||
addrs, err = parseHostsFromResources(resources) | ||
if err != nil { | ||
return | ||
} | ||
return addrs, nil, true | ||
} | ||
|
||
func cgoLookupPort(ctx context.Context, network, service string) (port int, err error, completed bool) { | ||
port, err = goLookupPort(network, service) // we can just use netgo lookup | ||
return port, err, err == nil | ||
} | ||
|
||
func cgoLookupIP(ctx context.Context, network, name string) (addrs []IPAddr, err error, completed bool) { | ||
|
||
var resources []dnsmessage.Resource | ||
switch ipVersion(network) { | ||
case '4': | ||
resources, err = resolverGetResources(ctx, name, int32(dnsmessage.TypeA), int32(dnsmessage.ClassINET)) | ||
case '6': | ||
resources, err = resolverGetResources(ctx, name, int32(dnsmessage.TypeAAAA), int32(dnsmessage.ClassINET)) | ||
default: | ||
resources, err = resolverGetResources(ctx, name, int32(dnsmessage.TypeALL), int32(dnsmessage.ClassINET)) | ||
} | ||
if err != nil { | ||
return | ||
} | ||
|
||
addrs, err = parseIPsFromResources(resources) | ||
if err != nil { | ||
return | ||
} | ||
|
||
return addrs, nil, true | ||
} | ||
|
||
func cgoLookupCNAME(ctx context.Context, name string) (cname string, err error, completed bool) { | ||
resources, err := resolverGetResources(ctx, name, int32(dnsmessage.TypeCNAME), int32(dnsmessage.ClassINET)) | ||
if err != nil { | ||
return | ||
} | ||
cname, err = parseCNAMEFromResources(resources) | ||
if err != nil { | ||
return "", err, false | ||
} | ||
return cname, nil, true | ||
} | ||
|
||
func cgoLookupPTR(ctx context.Context, addr string) (ptrs []string, err error, completed bool) { | ||
resources, err := resolverGetResources(ctx, addr, int32(dnsmessage.TypePTR), int32(dnsmessage.ClassINET)) | ||
if err != nil { | ||
return | ||
} | ||
ptrs, err = parsePTRsFromResources(resources) | ||
if err != nil { | ||
return | ||
} | ||
return ptrs, nil, true | ||
} | ||
|
||
var ( | ||
resInitOnce sync.Once | ||
errCode int32 | ||
) | ||
|
||
// resolverGetResources will make a call to the 'res_search' routine in libSystem | ||
// and parse the output as a slice of resource resources which can then be parsed | ||
func resolverGetResources(ctx context.Context, hostname string, rtype, class int32) ([]dnsmessage.Resource, error) { | ||
|
||
resInitOnce.Do(func() { | ||
errCode = res_init() | ||
}) | ||
if errCode < 0 { | ||
return nil, errors.New("could not initialize name resolver data") | ||
} | ||
|
||
var byteHostname = []byte(hostname) | ||
var responseBuffer [512]byte | ||
var size int32 | ||
|
||
size, errCode = res_search(&byteHostname[0], class, rtype, &responseBuffer[0], int32(len(responseBuffer))) | ||
if errCode != 0 { | ||
return nil, errors.New("could not complete domain resolution return code " + string(errCode)) | ||
} | ||
if size == 0 { | ||
return nil, errors.New("received empty response") | ||
} | ||
|
||
var msg dnsmessage.Message | ||
err := msg.Unpack(responseBuffer[:]) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var dnsParser dnsmessage.Parser | ||
if _, err := dnsParser.Start(responseBuffer[:]); err != nil { | ||
return nil, err | ||
} | ||
|
||
var resources []dnsmessage.Resource | ||
for { | ||
r, err := dnsParser.Answer() | ||
if err == dnsmessage.ErrSectionDone { | ||
break | ||
} | ||
if err != nil { | ||
return nil, err | ||
} | ||
resources = append(resources, r) | ||
} | ||
return resources, nil | ||
} | ||
|
||
func parseHostsFromResources(resources []dnsmessage.Resource) ([]string, error) { | ||
var answers []string | ||
|
||
for i := range resources { | ||
switch resources[i].Header.Type { | ||
case dnsmessage.TypeA: | ||
b := resources[i].Body.(*dnsmessage.AResource) | ||
answers = append(answers, string(b.A[:])) | ||
case dnsmessage.TypeAAAA: | ||
b := resources[i].Body.(*dnsmessage.AAAAResource) | ||
answers = append(answers, string(b.AAAA[:])) | ||
default: | ||
return nil, errors.New("could not parse an A or AAAA response from message buffer") | ||
} | ||
} | ||
return answers, nil | ||
} | ||
|
||
func parseIPsFromResources(resources []dnsmessage.Resource) ([]IPAddr, error) { | ||
var answers []IPAddr | ||
|
||
for i := range resources { | ||
switch resources[i].Header.Type { | ||
case dnsmessage.TypeA: | ||
b := resources[i].Body.(*dnsmessage.AResource) | ||
ip := parseIPv4(string(b.A[:])) | ||
answers = append(answers, IPAddr{IP: ip}) | ||
case dnsmessage.TypeAAAA: | ||
b := resources[i].Body.(*dnsmessage.AAAAResource) | ||
ip, zone := parseIPv6Zone(string(b.AAAA[:])) | ||
answers = append(answers, IPAddr{IP: ip, Zone: zone}) | ||
default: | ||
return nil, errors.New("could not parse an A or AAAA response from message buffer") | ||
} | ||
} | ||
return answers, nil | ||
} | ||
|
||
func parseCNAMEFromResources(resources []dnsmessage.Resource) (string, error) { | ||
if len(resources) == 0 { | ||
return "", errors.New("no CNAME record received") | ||
} | ||
c, ok := resources[0].Body.(*dnsmessage.CNAMEResource) | ||
if !ok { | ||
return "", errors.New("could not parse CNAME record") | ||
} | ||
return c.CNAME.String(), nil | ||
} | ||
|
||
func parsePTRsFromResources(resources []dnsmessage.Resource) ([]string, error) { | ||
var answers []string | ||
for i := range resources { | ||
switch resources[i].Header.Type { | ||
case dnsmessage.TypePTR: | ||
p := resources[0].Body.(*dnsmessage.PTRResource) | ||
answers = append(answers, p.PTR.String()) | ||
default: | ||
return nil, errors.New("could not parse a PTR response from message buffer") | ||
|
||
} | ||
} | ||
return answers, nil | ||
} | ||
|
||
// res_init and res_search are defined in runtime/lookup_darwin.go | ||
|
||
func res_init() int32 | ||
|
||
func res_search(dname *byte, class int32, rtype int32, answer *byte, anslen int32) (int32, int32) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
// license that can be found in the LICENSE file. | ||
|
||
// +build !cgo netgo | ||
// +build !darwin | ||
|
||
package net | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// Copyright 2019 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. | ||
|
||
package runtime | ||
|
||
import ( | ||
"unsafe" | ||
) | ||
|
||
//go:linkname res_init net.res_init | ||
//go:nosplit | ||
//go:cgo_unsafe_args | ||
func res_init() int32 { | ||
return libcCall(unsafe.Pointer(funcPC(res_init_trampoline)), nil) | ||
} | ||
func res_init_trampoline() | ||
|
||
//go:linkname res_search net.res_search | ||
//go:nosplit | ||
//go:cgo_unsafe_args | ||
func res_search(dname *byte, class int32, rtype int32, answer *byte, anslen int32) (int32, int32) { | ||
args := struct { | ||
dname *byte | ||
class, rtype int32 | ||
answer *byte | ||
anslen, retSize, retErr int32 | ||
}{dname, class, rtype, answer, anslen, 0, 0} | ||
libcCall(unsafe.Pointer(funcPC(res_search_trampoline)), unsafe.Pointer(&args)) | ||
return args.retSize, args.retErr | ||
} | ||
func res_search_trampoline() | ||
|
||
//go:cgo_import_dynamic libc_res_search res_search "/usr/lib/libSystem.B.dylib" | ||
//go:cgo_import_dynamic libc_res_init res_init "/usr/lib/libSystem.B.dylib" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// Copyright 2019 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. | ||
|
||
#include "go_asm.h" | ||
#include "go_tls.h" | ||
#include "textflag.h" | ||
|
||
TEXT runtime·res_init_trampoline(SB),NOSPLIT,$0 | ||
PUSHL BP | ||
MOVL SP, BP | ||
SUBL $8, SP | ||
CALL libc_res_init(SB) | ||
CMPL AX, $-1 | ||
JNE ok | ||
CALL libc_error(SB) | ||
ok: | ||
MOVL BP, SP | ||
POPL BP | ||
RET | ||
|
||
TEXT runtime·res_search_trampoline(SB),NOSPLIT,$0 | ||
PUSHL BP | ||
MOVL SP, BP | ||
SUBL $24, SP | ||
MOVL 32(SP), CX | ||
MOVL 16(CX), AX // arg 5 anslen | ||
MOVL AX, 16(SP) | ||
MOVL 12(CX), AX // arg 4 answer | ||
MOVL AX, 12(SP) | ||
MOVL 8(CX), AX // arg 3 type | ||
MOVL AX, 8(SP) | ||
MOVL 4(CX), AX // arg 2 class | ||
MOVL AX, 4(SP) | ||
MOVL 0(CX), AX // arg 1 name | ||
MOVL AX, 0(SP) | ||
CALL libc_res_search(SB) | ||
XORL DX, DX | ||
CMPL AX, $-1 | ||
JNE ok | ||
CALL libc_error(SB) | ||
MOVL (AX), DX | ||
XORL AX, AX | ||
ok: | ||
MOVL 32(SP), CX | ||
MOVL AX, 20(CX) | ||
MOVL DX, 24(CX) | ||
MOVL BP, SP | ||
POPL BP | ||
RET |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
// Copyright 2019 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. | ||
|
||
#include "go_asm.h" | ||
#include "go_tls.h" | ||
#include "textflag.h" | ||
|
||
TEXT runtime·res_init_trampoline(SB),NOSPLIT,$0 | ||
PUSHQ BP | ||
MOVQ SP, BP | ||
CALL libc_res_init(SB) | ||
CMPQ AX, $-1 | ||
JNE ok | ||
CALL libc_error(SB) | ||
ok: | ||
POPQ BP | ||
RET | ||
|
||
TEXT runtime·res_search_trampoline(SB),NOSPLIT,$0 | ||
PUSHQ BP | ||
MOVQ SP, BP | ||
MOVQ DI, BX // move DI into BX to preserve struct addr | ||
MOVL 24(BX), R8 // arg 5 anslen | ||
MOVQ 16(BX), CX // arg 4 answer | ||
MOVL 12(BX), DX // arg 3 type | ||
MOVL 8(BX), SI // arg 2 class | ||
MOVQ 0(BX), DI // arg 1 name | ||
CALL libc_res_search(SB) | ||
XORL DX, DX | ||
CMPQ AX, $-1 | ||
JNE ok | ||
CALL libc_error(SB) | ||
MOVLQSX (AX), DX // move return from libc_error into DX | ||
XORL AX, AX // size on error is 0 | ||
ok: | ||
MOVQ AX, 28(BX) // size | ||
MOVQ DX, 32(BX) // error code | ||
POPQ BP | ||
RET |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// Copyright 2015 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. | ||
|
||
// System calls and other sys.stuff for ARM64, Darwin | ||
// System calls are implemented in libSystem, this file contains | ||
// trampolines that convert from Go to C calling convention. | ||
|
||
#include "go_asm.h" | ||
#include "go_tls.h" | ||
#include "textflag.h" | ||
|
||
// On darwin/arm, the runtime always uses runtime/cgo | ||
// for resolution. This will just exit with a nominal | ||
// exit code. | ||
|
||
TEXT runtime·res_search_trampoline(SB),NOSPLIT,$0 | ||
MOVW $90, R0 | ||
BL libc_exit(SB) | ||
RET | ||
|
||
TEXT runtime·res_init_trampoline(SB),NOSPLIT,$0 | ||
MOVW $91, R0 | ||
BL libc_exit(SB) | ||
RET |
Oops, something went wrong.