SamplemDNSClient.c [plain text]
#include <libc.h>
#include <arpa/nameser.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <CoreFoundation/CoreFoundation.h>
#include <AvailabilityMacros.h>
#undef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED
#define AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED
#undef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3
#define AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3
#include <DNSServiceDiscovery/DNSServiceDiscovery.h>
typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16;
static char operation;
static dns_service_discovery_ref client = NULL;
static int num_printed;
static char addtest = 0;
static DNSRecordReference record;
static char myhinfo9[11] = "\003Mac\006OS 9.2";
static char myhinfoX[ 9] = "\003Mac\004OS X";
static char updatetest[3] = "\002AA";
static char bigNULL[4096];
static void MyHandleMachMessage(CFMachPortRef port, void *msg, CFIndex size, void *info)
{
(void)port; (void)size; (void)info; DNSServiceDiscovery_handleReply(msg);
}
static int AddDNSServiceClientToRunLoop(dns_service_discovery_ref c)
{
mach_port_t port = DNSServiceDiscoveryMachPort(c);
if (!port)
return(-1);
else
{
CFMachPortContext context = { 0, 0, NULL, NULL, NULL };
Boolean shouldFreeInfo;
CFMachPortRef cfMachPort = CFMachPortCreateWithPort(kCFAllocatorDefault, port, MyHandleMachMessage, &context, &shouldFreeInfo);
CFRunLoopSourceRef rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
CFRelease(rls);
return(0);
}
}
static void printtimestamp(void)
{
struct timeval tv;
struct tm tm;
gettimeofday(&tv, NULL);
localtime_r((time_t*)&tv.tv_sec, &tm);
printf("%2d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec/1000);
}
#define DomainMsg(X) ((X) == DNSServiceDomainEnumerationReplyAddDomain ? "Added" : \
(X) == DNSServiceDomainEnumerationReplyAddDomainDefault ? "(Default)" : \
(X) == DNSServiceDomainEnumerationReplyRemoveDomain ? "Removed" : "Unknown")
static void regdom_reply(DNSServiceDomainEnumerationReplyResultType resultType, const char *replyDomain,
DNSServiceDiscoveryReplyFlags flags, void *context)
{
(void)context; printtimestamp();
printf("Recommended Registration Domain %s %s", replyDomain, DomainMsg(resultType));
if (flags) printf(" Flags: %X", flags);
printf("\n");
}
static void browsedom_reply(DNSServiceDomainEnumerationReplyResultType resultType, const char *replyDomain,
DNSServiceDiscoveryReplyFlags flags, void *context)
{
(void)context; printtimestamp();
printf("Recommended Browsing Domain %s %s", replyDomain, DomainMsg(resultType));
if (flags) printf(" Flags: %X", flags);
printf("\n");
}
static void browse_reply(DNSServiceBrowserReplyResultType resultType,
const char *replyName, const char *replyType, const char *replyDomain, DNSServiceDiscoveryReplyFlags flags, void *context)
{
char *op = (resultType == DNSServiceBrowserReplyAddInstance) ? "Add" : "Rmv";
(void)context; if (num_printed++ == 0) printf("Timestamp A/R Flags %-24s %-24s %s\n", "Domain", "Service Type", "Instance Name");
printtimestamp();
printf("%s%6X %-24s %-24s %s\n", op, flags, replyDomain, replyType, replyName);
}
static void resolve_reply(struct sockaddr *interface, struct sockaddr *address, const char *txtRecord, DNSServiceDiscoveryReplyFlags flags, void *context)
{
(void)interface; (void)context; if (address->sa_family != AF_INET && address->sa_family != AF_INET6)
printf("Unknown address family %d\n", address->sa_family);
else
{
const char *src = txtRecord;
printtimestamp();
if (address->sa_family == AF_INET)
{
struct sockaddr_in *ip = (struct sockaddr_in *)address;
union { uint32_t l; u_char b[4]; } addr = { ip->sin_addr.s_addr };
union { uint16_t s; u_char b[2]; } port = { ip->sin_port };
uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1];
char ipstring[16];
sprintf(ipstring, "%d.%d.%d.%d", addr.b[0], addr.b[1], addr.b[2], addr.b[3]);
printf("Service can be reached at %-15s:%u", ipstring, PortAsNumber);
}
else if (address->sa_family == AF_INET6)
{
struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)address;
u_int8_t *b = ip6->sin6_addr.__u6_addr.__u6_addr8;
union { uint16_t s; u_char b[2]; } port = { ip6->sin6_port };
uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1];
char ipstring[40];
char ifname[IF_NAMESIZE + 1] = "";
sprintf(ipstring, "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X",
b[0x0], b[0x1], b[0x2], b[0x3], b[0x4], b[0x5], b[0x6], b[0x7],
b[0x8], b[0x9], b[0xA], b[0xB], b[0xC], b[0xD], b[0xE], b[0xF]);
if (ip6->sin6_scope_id) { ifname[0] = '%'; if_indextoname(ip6->sin6_scope_id, &ifname[1]); }
printf("%s%s:%u", ipstring, ifname, PortAsNumber);
}
if (flags) printf(" Flags: %X", flags);
if (*src)
{
char txtInfo[64]; char *dst = txtInfo;
const char *const lim = &txtInfo[sizeof(txtInfo)];
while (*src && dst < lim-1)
{
if (*src == '\\') *dst++ = '\\'; if (*src >= ' ') *dst++ = *src++; else
{
*dst++ = '\\'; if (*src == 1) *dst++ = ' '; else {
static const char hexchars[16] = "0123456789ABCDEF";
*dst++ = '0';
*dst++ = 'x';
*dst++ = hexchars[*src >> 4];
*dst++ = hexchars[*src & 0xF];
}
src++;
}
}
*dst++ = 0;
printf(" TXT %s", txtInfo);
}
printf("\n");
}
}
static void myCFRunLoopTimerCallBack(CFRunLoopTimerRef timer, void *info)
{
(void)timer; (void)info;
switch (operation)
{
case 'A':
{
switch (addtest)
{
case 0: printf("Adding Test HINFO record\n");
record = DNSServiceRegistrationAddRecord(client, ns_t_hinfo, sizeof(myhinfo9), &myhinfo9[0], 120);
addtest = 1;
break;
case 1: printf("Updating Test HINFO record\n");
DNSServiceRegistrationUpdateRecord(client, record, sizeof(myhinfoX), &myhinfoX[0], 120);
addtest = 2;
break;
case 2: printf("Removing Test HINFO record\n");
DNSServiceRegistrationRemoveRecord(client, record);
addtest = 0;
break;
}
}
break;
case 'U':
{
if (updatetest[1] != 'Z') updatetest[1]++;
else updatetest[1] = 'A';
updatetest[0] = 3 - updatetest[0];
updatetest[2] = updatetest[1];
printf("Updating Test TXT record to %c\n", updatetest[1]);
DNSServiceRegistrationUpdateRecord(client, 0, 1+updatetest[0], &updatetest[0], 120);
}
break;
case 'N':
{
printf("Adding big NULL record\n");
DNSServiceRegistrationAddRecord(client, ns_t_null, sizeof(bigNULL), &bigNULL[0], 120);
CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
}
break;
}
}
static void reg_reply(DNSServiceRegistrationReplyErrorType errorCode, void *context)
{
(void)context; printf("Got a reply from the server: ");
switch (errorCode)
{
case kDNSServiceDiscoveryNoError: printf("Name now registered and active\n"); break;
case kDNSServiceDiscoveryNameConflict: printf("Name in use, please choose another\n"); exit(-1);
default: printf("Error %d\n", errorCode); return;
}
if (operation == 'A' || operation == 'U' || operation == 'N')
{
CFRunLoopTimerContext myCFRunLoopTimerContext = { 0, 0, NULL, NULL, NULL };
CFRunLoopTimerRef timer = CFRunLoopTimerCreate(kCFAllocatorDefault,
CFAbsoluteTimeGetCurrent() + 5.0, 5.0, 0, 1, myCFRunLoopTimerCallBack, &myCFRunLoopTimerContext);
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
}
}
int main(int argc, char **argv)
{
const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
char *d;
setlinebuf(stdout);
if (argc < 2) goto Fail; operation = getopt(argc, (char * const *)argv, "EFBLRAUNTMI");
if (operation == -1) goto Fail;
switch (operation)
{
case 'E': printf("Looking for recommended registration domains:\n");
client = DNSServiceDomainEnumerationCreate(1, regdom_reply, nil);
break;
case 'F': printf("Looking for recommended browsing domains:\n");
client = DNSServiceDomainEnumerationCreate(0, browsedom_reply, nil);
break;
case 'B': if (argc < optind+1) goto Fail;
d = (argc < optind+2) ? "" : argv[optind+1]; if (d[0] == '.' && d[1] == 0) d[0] = 0; printf("Browsing for %s%s\n", argv[optind+0], d);
client = DNSServiceBrowserCreate(argv[optind+0], d, browse_reply, nil);
break;
case 'L': if (argc < optind+2) goto Fail;
d = (argc < optind+3) ? "" : argv[optind+2];
if (d[0] == '.' && d[1] == 0) d = "local"; printf("Lookup %s.%s%s\n", argv[optind+0], argv[optind+1], d);
client = DNSServiceResolverResolve(argv[optind+0], argv[optind+1], d, resolve_reply, nil);
break;
case 'R': if (argc < optind+4) goto Fail;
{
char *nam = argv[optind+0];
char *typ = argv[optind+1];
char *dom = argv[optind+2];
uint16_t PortAsNumber = atoi(argv[optind+3]);
Opaque16 registerPort = { { PortAsNumber >> 8, PortAsNumber & 0xFF } };
char txt[2048];
char *ptr = txt;
int i;
if (nam[0] == '.' && nam[1] == 0) nam[0] = 0; if (dom[0] == '.' && dom[1] == 0) dom[0] = 0;
for (i = optind+4; i < argc; i++)
{
int len = strlen(argv[i]);
if (len > 255 || ptr + len + 1 >= txt + sizeof(txt)) break;
strcpy(ptr, argv[i]);
ptr += len;
*ptr++ = 1;
}
if (ptr > txt) ptr--;
*ptr = 0;
printf("Registering Service %s.%s%s port %s %s\n", nam, typ, dom, argv[optind+3], txt);
client = DNSServiceRegistrationCreate(nam, typ, dom, registerPort.NotAnInteger, txt, reg_reply, nil);
break;
}
case 'A':
case 'U':
case 'N': {
Opaque16 registerPort = { { 0x12, 0x34 } };
static const char TXT[] = "First String\001Second String\001Third String";
printf("Registering Service Test._testupdate._tcp.local.\n");
client = DNSServiceRegistrationCreate("Test", "_testupdate._tcp.", "", registerPort.NotAnInteger, TXT, reg_reply, nil);
break;
}
case 'T': {
Opaque16 registerPort = { { 0x23, 0x45 } };
char TXT[1000];
unsigned int i;
for (i=0; i<sizeof(TXT)-1; i++)
if ((i & 0x1F) == 0x1F) TXT[i] = 1; else TXT[i] = 'A' + (i >> 5);
TXT[i] = 0;
printf("Registering Service Test._testlargetxt._tcp.local.\n");
client = DNSServiceRegistrationCreate("Test", "_testlargetxt._tcp.", "", registerPort.NotAnInteger, TXT, reg_reply, nil);
break;
}
case 'M': {
pid_t pid = getpid();
Opaque16 registerPort = { { pid >> 8, pid & 0xFF } };
static const char TXT1[] = "First String\001Second String\001Third String";
static const char TXT2[] = "\x0D" "Fourth String" "\x0C" "Fifth String" "\x0C" "Sixth String";
printf("Registering Service Test._testdualtxt._tcp.local.\n");
client = DNSServiceRegistrationCreate("", "_testdualtxt._tcp.", "", registerPort.NotAnInteger, TXT1, reg_reply, nil);
record = DNSServiceRegistrationAddRecord(client, ns_t_txt, sizeof(TXT2)-1, TXT2, 120);
break;
}
case 'I': {
pid_t pid = getpid();
Opaque16 registerPort = { { pid >> 8, pid & 0xFF } };
static const char TXT[] = "\x09" "Test Data";
printf("Registering Service Test._testtxt._tcp.local.\n");
client = DNSServiceRegistrationCreate("", "_testtxt._tcp.", "", registerPort.NotAnInteger, "", reg_reply, nil);
if (client) DNSServiceRegistrationUpdateRecord(client, 0, 1+TXT[0], &TXT[0], 120);
break;
}
default: goto Exit;
}
if (!client) { fprintf(stderr, "DNSService call failed\n"); return (-1); }
if (AddDNSServiceClientToRunLoop(client) != 0) { fprintf(stderr, "AddDNSServiceClientToRunLoop failed\n"); return (-1); }
printf("Talking to DNS SD Daemon at Mach port %d\n", DNSServiceDiscoveryMachPort(client));
CFRunLoopRun();
DNSServiceDiscoveryDeallocate(client);
Exit:
return 0;
Fail:
fprintf(stderr, "%s -E (Enumerate recommended registration domains)\n", progname);
fprintf(stderr, "%s -F (Enumerate recommended browsing domains)\n", progname);
fprintf(stderr, "%s -B <Type> <Domain> (Browse for services instances)\n", progname);
fprintf(stderr, "%s -L <Name> <Type> <Domain> (Look up a service instance)\n", progname);
fprintf(stderr, "%s -R <Name> <Type> <Domain> <Port> [<TXT>...] (Register a service)\n", progname);
fprintf(stderr, "%s -A (Test Adding/Updating/Deleting a record)\n", progname);
fprintf(stderr, "%s -U (Test updating a TXT record)\n", progname);
fprintf(stderr, "%s -N (Test adding a large NULL record)\n", progname);
fprintf(stderr, "%s -T (Test creating a large TXT record)\n", progname);
fprintf(stderr, "%s -M (Test creating a registration with multiple TXT records)\n", progname);
fprintf(stderr, "%s -I (Test registering and then immediately updating TXT record)\n", progname);
return 0;
}
#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
#define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
const char VersionString_SCCS[] = "@(#) mDNS " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
#if _BUILDING_XCODE_PROJECT_
const char *__crashreporter_info__ = VersionString_SCCS + 5;
asm(".desc ___crashreporter_info__, 0x10");
#endif