Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

net: use libSystem bindings for DNS resolution on macos if cgo is unavailable #30686

Closed
wants to merge 36 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6bc69c1
Add cgo bindings to include the libSystem res_query function and amd6…
grantseltzer Mar 1, 2019
0252e5c
move bindings to net package
grantseltzer Mar 4, 2019
5652e61
Update lookup_darwin.go
grantseltzer Mar 5, 2019
b26bb0f
add res search logic to the actual resolver logic
grantseltzer Mar 6, 2019
ea4ec37
Add reminder for fixing CNAME logic
grantseltzer Mar 6, 2019
61fa16b
Remove unneeded check, remove superfluous file
grantseltzer Mar 7, 2019
c38ac96
Make sure we can query CNAME's
grantseltzer Mar 7, 2019
1dc855b
seperate out x86_64 and 32 bit assembly
grantseltzer Mar 7, 2019
a438a8e
Remove res_search from runtime package
grantseltzer Mar 7, 2019
534863c
fix formatting
grantseltzer Mar 8, 2019
6fb0200
Remove accidentally added demo file
grantseltzer Mar 8, 2019
82fd805
Fix inconsistent whitespace in assembly files
grantseltzer Mar 8, 2019
f36b065
Add back wrongly removed arg offset
grantseltzer Mar 8, 2019
ed3ed8d
add arm bindings for res_search
grantseltzer Mar 8, 2019
834f4b3
Replace arm routines with stubs
grantseltzer Mar 8, 2019
edbad08
Fix first set of undefined errors...
grantseltzer Mar 8, 2019
5597c45
Create unique lookup file in net for darwin, move libSystem bindings …
grantseltzer Mar 8, 2019
931c26b
Remove redundant build tag
grantseltzer Mar 8, 2019
88fb0bc
Move the res_search function to runtime package while linking in net
grantseltzer Mar 9, 2019
0e90bc3
create generic file for resolver methods that are shared between darw…
grantseltzer Mar 11, 2019
6155aa3
Add error checking to res_search assembly routines
grantseltzer Mar 12, 2019
4de3446
define new cgo_stub for darwin which uses libSystem bindings
grantseltzer Mar 18, 2019
046d717
Fix build tags. Add res_ninit and res_nsearch
grantseltzer Mar 20, 2019
966dd03
fix typo
grantseltzer Mar 21, 2019
14cc8ed
Update 32 bit and arm assembly for ninit and nsearch
grantseltzer Mar 21, 2019
50e8025
Roll back to res_search and res_init
grantseltzer Mar 22, 2019
808f8be
Add sync.Once to res_init call and fix import
grantseltzer Mar 25, 2019
cea6b6f
Fix style issues
grantseltzer Mar 26, 2019
c238eb9
Fix undefined variable issue
grantseltzer Mar 26, 2019
8c9efdd
Add second return to res_search, standardize arm return code
grantseltzer Mar 28, 2019
d69aca6
add return code to error message from res_search
grantseltzer Mar 28, 2019
1f3c262
remove whitespace
grantseltzer Mar 29, 2019
a9ccfe8
fix mismatched string and int32
grantseltzer Mar 29, 2019
a9ff324
define stub type and methods for vet
grantseltzer Apr 1, 2019
ce26e64
trivial change
grantseltzer Apr 1, 2019
3c3ff6b
Add copyright and fix grammar of comments
grantseltzer Apr 3, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
209 changes: 209 additions & 0 deletions src/net/cgo_darwin_stub.go
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)
1 change: 1 addition & 0 deletions src/net/cgo_stub.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.

// +build !cgo netgo
// +build !darwin

package net

Expand Down
5 changes: 5 additions & 0 deletions src/net/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ func initConfVal() {
// their own DNS requests. So always use cgo instead, which
// avoids that.
if runtime.GOOS == "darwin" {
// Normally we force netGo to be true if building without cgo enabled.
// On Darwin, we can call libc even if cgo is not enabled, so only set netGo to true
// if explicitly requested.
confVal.netGo = dnsMode == "go"

confVal.forceCgoLookupHost = true
return
}
Expand Down
35 changes: 35 additions & 0 deletions src/runtime/lookup_darwin.go
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"
50 changes: 50 additions & 0 deletions src/runtime/lookup_darwin_386.s
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
40 changes: 40 additions & 0 deletions src/runtime/lookup_darwin_amd64.s
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
25 changes: 25 additions & 0 deletions src/runtime/lookup_darwin_arm.s
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
Loading