I'm trying to use Apple's DNSServiceQueryRecord from Go, so I have access to the resource records. The function takes a callback that I have not been able to make it work.
I'm trying to pass a function as the callback context, so it can communicate back the end of the DNS lookup. Go code calls queryDNS, which calls DNSServiceQueryRecord, which later calls goCallback, which is supposed to call the context function. This is the relevant part of my app code:
//go:build darwin
package main
/*
#include <stdlib.h>
#include <dns_sd.h>
extern void goCallback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
DNSServiceErrorType errorCode, char* fullname, uint16_t rrtype, uint16_t rrclass,
uint16_t rdlen, void* rdata, uint32_t ttl, void* context);
static DNSServiceErrorType queryDNS(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
const char *fullname, uint16_t rrtype, uint16_t rrclass,
void *context) {
return DNSServiceQueryRecord(sdRef, flags, interfaceIndex, fullname, rrtype, rrclass,
(DNSServiceQueryRecordReply)goCallback, context);
}
*/
import "C"
import (
"context"
"fmt"
"runtime"
"time"
"unsafe"
"golang.org/x/net/dns/dnsmessage"
)
//export goCallback
func goCallback(sdRef C.DNSServiceRef, flags C.DNSServiceFlags, interfaceIndex C.uint32_t,
errorCode C.DNSServiceErrorType, fullname *C.char, rrtype C.uint16_t, rrclass C.uint16_t,
rdlen C.uint16_t, rdata unsafe.Pointer, ttl C.uint32_t, context unsafe.Pointer) {
(*(*func())(context))()
fmt.Println("callback", errorCode)
}
func queryCNAME(ctx context.Context, qname string) (string, error) {
var sdRef C.DNSServiceRef
cQname := C.CString(qname)
defer C.free(unsafe.Pointer(cQname))
done := make(chan struct{})
callback := func() {
fmt.Println("callback")
done <- struct{}{}
}
callbackPtr := unsafe.Pointer(&callback)
var pinner runtime.Pinner
pinner.Pin(callbackPtr)
defer pinner.Unpin()
fmt.Println("starting")
// See https://developer.apple.com/documentation/dnssd/1804747-dnsservicequeryrecord?language=objc
queryErr := C.queryDNS(&sdRef, 0, 0,
cQname, C.uint16_t(dnsmessage.TypeCNAME), C.uint16_t(dnsmessage.ClassINET),
callbackPtr)
fmt.Println("returned")
if queryErr != 0 {
return "", fmt.Errorf("failed to start DNS query: %v", queryErr)
}
defer C.DNSServiceRefDeallocate(sdRef)
time.Sleep(2 * time.Second)
<-done
return "", nil
}
When I run, I get:
go run .
starting
panic: runtime error: cgo argument has Go pointer to unpinned Go pointer
goroutine 19 [running]:
main.queryCNAME.func3(0x102d18728?, 0x14000116020?, 0x1400027dbd0?)
/Users/fortuna/code/gio-test/sysresolver_darwin.go:79 +0x60
main.queryCNAME({0x14003e849c0?, 0x102bcb239?}, {0x14003e8c1e3, 0x3})
/Users/fortuna/code/gio-test/sysresolver_darwin.go:79 +0x188
main.main.func1.1()
/Users/fortuna/code/gio-test/main.go:88 +0x5e0
main.main.func1()
/Users/fortuna/code/gio-test/main.go:153 +0x1c
created by main.main in goroutine 1
/Users/fortuna/code/gio-test/main.go:37 +0x24
exit status 2
I've tried using the runtime.Pinner, but it didn't work. How can I make it work?
By the way, I also had to declare the queryDNS C function static, otherwise the linker would complain with duplicate symbol, no idea why.