#include <dns_sd.h>
#include <unistd.h>
#include <DNSServiceDiscovery/DNSServiceDiscovery.h> // include Mach API to ensure no conflicts exist
#include <CoreFoundation/CoreFoundation.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define BIND_8_COMPAT 1
#include <nameser.h>
#ifndef T_SRV
#define T_SRV 33
#endif
#define MAX_DOMAIN_LABEL 63
#define MAX_DOMAIN_NAME 255
#define MAX_CSTRING 2044
typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16;
typedef struct { u_char c[ 64]; } domainlabel;
typedef struct { u_char c[256]; } domainname;
typedef struct
{
uint16_t priority;
uint16_t weight;
uint16_t port;
domainname target;
} srv_rdata;
static void sighdlr(int signo);
static char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc);
static char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc);
static void print_rdata(int type, int len, const u_char *rdata);
static void query_cb(const DNSServiceRef DNSServiceRef, const DNSServiceFlags flags, const u_int32_t interfaceIndex, const DNSServiceErrorType errorCode, const char *name, const u_int16_t rrtype, const u_int16_t rrclass, const u_int16_t rdlen, const void *rdata, const u_int32_t ttl, void *context);
static void resolve_cb(const DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const char *txtRecord, void *context);
static void my_enum_cb( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *replyDomain, void *context);
static void my_regecordcb(DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, void *context);
static void browse_cb(DNSServiceRef sdr, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err, const char *serviceName, const char *regtype, const char *domain, void *context);
static DNSServiceRef sdr = NULL;
static uint32_t InterfaceIndex = 0;
static void regservice_cb(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, void *context)
{
#pragma unused (sdRef, flags, errorCode, context)
printf("regservice_cb %s %s %s\n", name, regtype, domain);
}
int main (int argc, char * argv[]) {
int err, t, i;
char *name, *type, *domain;
DNSServiceFlags flags;
DNSRecordRef recordrefs[10];
char host[256];
int ipaddr = 12345;
char full[1024];
if (!strcmp(argv[1], "-lo")) { InterfaceIndex = -1; argv++; argc--; }
if (signal(SIGINT, sighdlr) == SIG_ERR) fprintf(stderr, "ERROR - can't catch interupt!\n");
if (argc < 2) exit(1);
if (!strcmp(argv[1], "-regrecord"))
{
err = DNSServiceCreateConnection(&sdr);
if (err)
{
printf("DNSServiceCreateConnection returned %d\n", err);
exit(1);
}
printf("registering 10 address records...\n");
for (i = 0; i < 10; i++)
{
sprintf(host, "testhost-%d.local.", i);
ipaddr++;
err = DNSServiceRegisterRecord(sdr, &recordrefs[i], kDNSServiceFlagsUnique, InterfaceIndex,
host, 1, 1, 4, &ipaddr, 60, my_regecordcb, NULL);
if (err)
{
printf("DNSServiceRegisterRecord returned error %d\n", err);
exit(1);
}
}
printf("processing results...\n");
for (i = 0; i < 10; i++) DNSServiceProcessResult(sdr);
printf("deregistering half of the records\n");
for (i = 0; i < 10; i++)
{
if (i % 2)
{
err = DNSServiceRemoveRecord(sdr, recordrefs[i], 0);
if (err)
{
printf("DNSServiceRemoveRecord returned error %d\n" ,err);
exit(1);
}
}
}
printf("sleeping 10...\n");
sleep(10);
printf("deregistering all remaining records\n");;
DNSServiceRefDeallocate(sdr);
printf("done. sleeping 10..\n");
sleep(10);
exit(1);
}
if (!strcmp(argv[1], "-browse"))
{
if (argc < 3) exit(1);
err = DNSServiceBrowse(&sdr, 0, InterfaceIndex, argv[2], NULL , browse_cb, NULL);
if (err)
{
printf("DNSServiceBrowse returned error %d\n", err);
exit(1);
}
while(1) DNSServiceProcessResult(sdr);
}
if (!strcmp(argv[1], "-enum"))
{
if (!strcmp(argv[2], "browse")) flags = kDNSServiceFlagsBrowseDomains;
else if (!strcmp(argv[2], "register")) flags = kDNSServiceFlagsRegistrationDomains;
else exit(1);
err = DNSServiceEnumerateDomains(&sdr, flags, InterfaceIndex, my_enum_cb, NULL);
if (err)
{
printf("EnumerateDomains returned error %d\n", err);
exit(1);
}
while(1) DNSServiceProcessResult(sdr);
}
if (!strcmp(argv[1], "-query"))
{
t = atol(argv[5]);
err = DNSServiceConstructFullName(full, argv[2], argv[3], argv[4]);
if (err) exit(1);
printf("resolving fullname %s type %d\n", full, t);
err = DNSServiceQueryRecord(&sdr, 0, 0, full, t, 1, query_cb, NULL);
while (1) DNSServiceProcessResult(sdr);
}
if (!strcmp(argv[1], "-regservice"))
{
char *regtype = "_http._tcp";
char txtstring[] = "\x0DMy Txt Record";
if (argc > 2) name = argv[2];
else name = NULL;
if (argc > 3) regtype = argv[3];
uint16_t PortAsNumber = 123;
if (argc > 4) PortAsNumber = atoi(argv[4]);
Opaque16 registerPort = { { PortAsNumber >> 8, PortAsNumber & 0xFF } };
err = DNSServiceRegister(&sdr, 0, InterfaceIndex, name, regtype, "local.", NULL, registerPort.NotAnInteger, sizeof(txtstring)-1, txtstring, regservice_cb, NULL);
if (err)
{
printf("DNSServiceRegister returned error %d\n", err);
exit(1);
}
while (1) DNSServiceProcessResult(sdr);
}
if (!strcmp(argv[1], "-resolve"))
{
name = argv[2];
type = argv[3];
domain = argv[4];
err = DNSServiceResolve(&sdr, 0, InterfaceIndex, name, type, domain, resolve_cb, NULL);
if (err)
{
printf("DNSServiceResolve returned error %d\n", err);
exit(1);
}
while(1) DNSServiceProcessResult(sdr);
}
exit(1);
}
static void browse_cb(DNSServiceRef sdr, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err, const char *serviceName, const char *regtype, const char *domain, void *context)
{
#pragma unused(sdr, ifi, context)
if (err)
{
printf("Callback: error %d\n", err);
return;
}
printf("BrowseCB: %s %s %s %s (%s)\n", serviceName, regtype, domain, (flags & kDNSServiceFlagsMoreComing ? "(more coming)" : ""), flags & kDNSServiceFlagsAdd ? "(ADD)" : "(REMOVE)");
}
static void my_enum_cb( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *replyDomain, void *context)
{
#pragma unused(sdRef, context)
char *type;
if (flags == kDNSServiceFlagsAdd) type = "add";
else if (flags == kDNSServiceFlagsRemove) type = "remove";
else if (flags == (kDNSServiceFlagsAdd | kDNSServiceFlagsDefault)) type = "add default";
else type = "unknown";
if (errorCode) printf("EnumerateDomainsCB: error code %d\n", errorCode);
else printf("%s domain %s on interface %d\n", type, replyDomain, interfaceIndex);
}
static void query_cb(const DNSServiceRef DNSServiceRef, const DNSServiceFlags flags, const u_int32_t interfaceIndex, const DNSServiceErrorType errorCode, const char *name, const u_int16_t rrtype, const u_int16_t rrclass, const u_int16_t rdlen, const void *rdata, const u_int32_t ttl, void *context)
{
(void)DNSServiceRef;
(void)flags;
(void)interfaceIndex;
(void)rrclass;
(void)ttl;
(void)context;
if (errorCode)
{
printf("query callback: error==%d\n", errorCode);
return;
}
printf("query callback - name = %s, rdata=\n", name);
print_rdata(rrtype, rdlen, rdata);
}
static void resolve_cb(const DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const char *txtRecord, void *context)
{
int i;
#pragma unused(sdRef, flags, interfaceIndex, errorCode, context, txtRecord)
printf("Resolved %s to %s:%d (%d bytes txt data)\n", fullname, hosttarget, port, txtLen);
printf("TXT Data:\n");
for (i = 0; i < txtLen; i++)
if (txtRecord[i] >= ' ') printf("%c", txtRecord[i]);
}
static void my_regecordcb(DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, void *context)
{
#pragma unused (sdRef, RecordRef, flags, context)
if (errorCode) printf("regrecord CB received error %d\n", errorCode);
else printf("regrecord callback - no errors\n");
}
static char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc)
{
const u_char * src = label->c; const u_char len = *src++; const u_char *const end = src + len; if (len > MAX_DOMAIN_LABEL) return(NULL); while (src < end) {
u_char c = *src++;
if (esc)
{
if (c == '.') *ptr++ = esc; else if (c <= ' ') { *ptr++ = esc;
*ptr++ = (char) ('0' + (c / 100) );
*ptr++ = (char) ('0' + (c / 10) % 10);
c = (u_char)('0' + (c ) % 10);
}
}
*ptr++ = (char)c; }
*ptr = 0; return(ptr); }
static char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc)
{
const u_char *src = name->c; const u_char *const max = name->c + MAX_DOMAIN_NAME;
if (*src == 0) *ptr++ = '.';
while (*src) {
if (src + 1 + *src >= max) return(NULL);
ptr = ConvertDomainLabelToCString_withescape((const domainlabel *)src, ptr, esc);
if (!ptr) return(NULL);
src += 1 + *src;
*ptr++ = '.'; }
*ptr++ = 0; return(ptr); }
static void print_rdata(int type, int len, const u_char *rdata)
{
int i;
srv_rdata *srv;
char targetstr[MAX_CSTRING];
struct in_addr in;
switch (type)
{
case T_TXT:
for (i = 0; i < len; i++)
if (rdata[i] >= 32 && rdata[i] <= 127) printf("%c", rdata[i]);
printf("\n");
return;
case T_SRV:
srv = (srv_rdata *)rdata;
ConvertDomainNameToCString_withescape(&srv->target, targetstr, 0);
printf("pri=%d, w=%d, port=%d, target=%s\n", srv->priority, srv->weight, srv->port, targetstr);
return;
case T_A:
assert(len == 4);
memcpy(&in, rdata, sizeof(in));
printf("%s\n", inet_ntoa(in));
return;
case T_PTR:
ConvertDomainNameToCString_withescape((domainname *)rdata, targetstr, 0);
printf("%s\n", targetstr);
return;
default:
printf("ERROR: I dont know how to print RData of type %d\n", type);
return;
}
}
static void sighdlr(int signo)
{
assert(signo == SIGINT);
fprintf(stderr, "Received sigint - deallocating serviceref and exiting\n");
if (sdr)
DNSServiceRefDeallocate(sdr);
exit(1);
}