2 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
25 * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
26 * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
27 * but for the sake of brevity here I will say just this: Curly braces are not syntactially
28 * part of an "if" statement; they are the beginning and ending markers of a compound statement;
29 * therefore common sense dictates that if they are part of a compound statement then they
30 * should be indented to the same level as everything else in that compound statement.
31 * Indenting curly braces at the same level as the "if" implies that curly braces are
32 * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
33 * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
34 * understand why variable y is not of type "char*" just proves the point that poor code
35 * layout leads people to unfortunate misunderstandings about how the C language really works.)
39 #include <arpa/nameser.h>
40 #include <CoreFoundation/CoreFoundation.h>
41 #include <DNSServiceDiscovery/DNSServiceDiscovery.h>
43 //*************************************************************************************************************
46 typedef union { unsigned char b
[2]; unsigned short NotAnInteger
; } Opaque16
;
48 static char operation
;
49 static dns_service_discovery_ref client
= NULL
;
50 static char addtest
= 0;
51 static DNSRecordReference record
;
52 static char myhinfo9
[11] = "\003Mac\006OS 9.2";
53 static char myhinfoX
[ 9] = "\003Mac\004OS X";
54 static char updatetest
[2] = "\001A";
55 static char bigNULL
[4096];
57 //*************************************************************************************************************
58 // Supporting Utility Functions
60 // This code takes care of:
61 // 1. Extracting the mach_port_t from the dns_service_discovery_ref
62 // 2. Making a CFMachPortRef from it
63 // 3. Making a CFRunLoopSourceRef from that
64 // 4. Adding that source to the current RunLoop
65 // 5. and passing the resulting messages back to DNSServiceDiscovery_handleReply() for processing
67 // Code that's not based around a CFRunLoop will need its own mechanism to receive Mach messages
68 // from the mDNSResponder daemon and pass them to the DNSServiceDiscovery_handleReply() routine.
69 // (There is no way to automate this, because it varies depending on the application's existing
70 // event handling model.)
72 static void MyHandleMachMessage(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
74 DNSServiceDiscovery_handleReply(msg
);
77 static int AddDNSServiceClientToRunLoop(dns_service_discovery_ref client
)
79 mach_port_t port
= DNSServiceDiscoveryMachPort(client
);
84 CFMachPortContext context
= { 0, 0, NULL
, NULL
, NULL
};
85 Boolean shouldFreeInfo
;
86 CFMachPortRef cfMachPort
= CFMachPortCreateWithPort(kCFAllocatorDefault
, port
, MyHandleMachMessage
, &context
, &shouldFreeInfo
);
87 CFRunLoopSourceRef rls
= CFMachPortCreateRunLoopSource(NULL
, cfMachPort
, 0);
88 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
94 //*************************************************************************************************************
95 // Sample callback functions for each of the operation types
97 #define DomainMsg(X) ((X) == DNSServiceDomainEnumerationReplyAddDomain ? "Added" : \
98 (X) == DNSServiceDomainEnumerationReplyAddDomainDefault ? "(Default)" : \
99 (X) == DNSServiceDomainEnumerationReplyRemoveDomain ? "Removed" : "Unknown")
101 static void regdom_reply(DNSServiceDomainEnumerationReplyResultType resultType
, const char *replyDomain
,
102 DNSServiceDiscoveryReplyFlags flags
, void *context
)
104 printf("Recommended Registration Domain %s %s", replyDomain
, DomainMsg(resultType
));
105 if (flags
) printf(" Flags: %X", flags
);
109 static void browsedom_reply(DNSServiceDomainEnumerationReplyResultType resultType
, const char *replyDomain
,
110 DNSServiceDiscoveryReplyFlags flags
, void *context
)
112 printf("Recommended Browsing Domain %s %s", replyDomain
, DomainMsg(resultType
));
113 if (flags
) printf(" Flags: %X", flags
);
117 static void browse_reply(DNSServiceBrowserReplyResultType resultType
,
118 const char *replyName
, const char *replyType
, const char *replyDomain
, DNSServiceDiscoveryReplyFlags flags
, void *context
)
120 char *op
= (resultType
== DNSServiceBrowserReplyAddInstance
) ? "Found" : "Removed";
121 printf("Service \"%s\", type \"%s\", domain \"%s\" %s", replyName
, replyType
, replyDomain
, op
);
122 if (flags
) printf(" Flags: %X", flags
);
126 static void resolve_reply(struct sockaddr
*interface
, struct sockaddr
*address
, const char *txtRecord
, DNSServiceDiscoveryReplyFlags flags
, void *context
)
128 if (address
->sa_family
!= AF_INET
)
129 printf("Unknown address family %d\n", address
->sa_family
);
132 struct sockaddr_in
*ip
= (struct sockaddr_in
*)address
;
133 union { uint32_t l
; u_char b
[4]; } addr
= { ip
->sin_addr
.s_addr
};
134 union { uint16_t s
; u_char b
[2]; } port
= { ip
->sin_port
};
135 uint16_t PortAsNumber
= ((uint16_t)port
.b
[0]) << 8 | port
.b
[1];
136 const char *src
= txtRecord
;
137 printf("Service can be reached at %d.%d.%d.%d:%u", addr
.b
[0], addr
.b
[1], addr
.b
[2], addr
.b
[3], PortAsNumber
);
142 const char *const lim
= &txtInfo
[sizeof(txtInfo
)];
143 while (*src
&& *src
!= 1 && dst
< lim
-1) *dst
++ = *src
++;
145 printf(" TXT \"%s\"", txtInfo
);
146 if (*src
== 1) src
++;
148 if (flags
) printf(" Flags: %X", flags
);
153 static void myCFRunLoopTimerCallBack(CFRunLoopTimerRef timer
, void *info
)
155 (void)timer
; // Parameter not used
156 (void)info
; // Parameter not used
164 case 0: printf("Adding Test HINFO record\n");
165 record
= DNSServiceRegistrationAddRecord(client
, T_HINFO
, sizeof(myhinfo9
), &myhinfo9
[0], 120);
168 case 1: printf("Updating Test HINFO record\n");
169 DNSServiceRegistrationUpdateRecord(client
, record
, sizeof(myhinfoX
), &myhinfoX
[0], 120);
172 case 2: printf("Removing Test HINFO record\n");
173 DNSServiceRegistrationRemoveRecord(client
, record
);
182 if (updatetest
[1] != 'Z') updatetest
[1]++;
183 else updatetest
[1] = 'A';
184 printf("Updating Test TXT record to %c\n", updatetest
[1]);
185 DNSServiceRegistrationUpdateRecord(client
, 0, sizeof(updatetest
), &updatetest
[0], 120);
191 printf("Adding big NULL record\n");
192 DNSServiceRegistrationAddRecord(client
, T_NULL
, sizeof(bigNULL
), &bigNULL
[0], 120);
193 CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer
, kCFRunLoopDefaultMode
);
199 static void reg_reply(DNSServiceRegistrationReplyErrorType errorCode
, void *context
)
201 printf("Got a reply from the server: ");
204 case kDNSServiceDiscoveryNoError
: printf("Name now registered and active\n"); break;
205 case kDNSServiceDiscoveryNameConflict
: printf("Name in use, please choose another\n"); exit(-1);
206 default: printf("Error %d\n", errorCode
); return;
209 if (operation
== 'A' || operation
== 'U' || operation
== 'N')
211 CFRunLoopTimerContext myCFRunLoopTimerContext
= { 0, 0, NULL
, NULL
, NULL
};
212 CFRunLoopTimerRef timer
= CFRunLoopTimerCreate(kCFAllocatorDefault
,
213 CFAbsoluteTimeGetCurrent() + 5.0, 5.0, 0, 1, // Next fire time, periodic interval, flags, and order
214 myCFRunLoopTimerCallBack
, &myCFRunLoopTimerContext
);
215 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer
, kCFRunLoopDefaultMode
);
219 //*************************************************************************************************************
220 // The main test function
222 int main(int argc
, char **argv
)
226 if (argc
< 2) goto Fail
; // Minimum command line is the command name and one argument
227 operation
= getopt(argc
, (char * const *)argv
, "EFBLRAUNTM");
228 if (operation
== -1) goto Fail
;
232 case 'E': printf("Looking for recommended registration domains:\n");
233 client
= DNSServiceDomainEnumerationCreate(1, regdom_reply
, nil
);
236 case 'F': printf("Looking for recommended browsing domains:\n");
237 client
= DNSServiceDomainEnumerationCreate(0, browsedom_reply
, nil
);
240 case 'B': if (argc
< optind
+1) goto Fail
;
241 dom
= (argc
< optind
+2) ? "" : argv
[optind
+1];
242 if (dom
[0] == '.' && dom
[1] == 0) dom
[0] = 0; // We allow '.' on the command line as a synonym for empty string
243 printf("Browsing for %s%s\n", argv
[optind
+0], dom
);
244 client
= DNSServiceBrowserCreate(argv
[optind
+0], dom
, browse_reply
, nil
);
247 case 'L': if (argc
< optind
+2) goto Fail
;
248 dom
= (argc
< optind
+3) ? "" : argv
[optind
+2];
249 if (dom
[0] == '.' && dom
[1] == 0) dom
[0] = 0; // We allow '.' on the command line as a synonym for empty string
250 printf("Lookup %s.%s%s\n", argv
[optind
+0], argv
[optind
+1], dom
);
251 client
= DNSServiceResolverResolve(argv
[optind
+0], argv
[optind
+1], dom
, resolve_reply
, nil
);
254 case 'R': if (argc
< optind
+4) goto Fail
;
256 char *nam
= argv
[optind
+0];
257 char *typ
= argv
[optind
+1];
258 char *dom
= argv
[optind
+2];
259 uint16_t PortAsNumber
= atoi(argv
[optind
+3]);
260 Opaque16 registerPort
= { { PortAsNumber
>> 8, PortAsNumber
& 0xFF } };
261 char *txt
= (argc
> optind
+4) ? argv
[optind
+4] : "";
262 if (nam
[0] == '.' && nam
[1] == 0) nam
[0] = 0; // We allow '.' on the command line as a synonym for empty string
263 if (dom
[0] == '.' && dom
[1] == 0) dom
[0] = 0; // We allow '.' on the command line as a synonym for empty string
264 printf("Registering Service %s.%s%s port %s %s\n", nam
, typ
, dom
, argv
[optind
+3], txt
);
265 client
= DNSServiceRegistrationCreate(nam
, typ
, dom
, registerPort
.NotAnInteger
, txt
, reg_reply
, nil
);
272 Opaque16 registerPort
= { { 0x12, 0x34 } };
273 static const char TXT
[] = "First String\001Second String\001Third String";
274 printf("Registering Service Test._testupdate._tcp.local.\n");
275 client
= DNSServiceRegistrationCreate("Test", "_testupdate._tcp.", "", registerPort
.NotAnInteger
, TXT
, reg_reply
, nil
);
280 Opaque16 registerPort
= { { 0x23, 0x45 } };
283 for (i
=0; i
<sizeof(TXT
)-1; i
++)
284 if ((i
& 0x1F) == 0x1F) TXT
[i
] = 1; else TXT
[i
] = 'A' + (i
>> 5);
286 printf("Registering Service Test._testlargetxt._tcp.local.\n");
287 client
= DNSServiceRegistrationCreate("Test", "_testlargetxt._tcp.", "", registerPort
.NotAnInteger
, TXT
, reg_reply
, nil
);
292 Opaque16 registerPort
= { { 0x23, 0x45 } };
293 static const char TXT1
[] = "First String\001Second String\001Third String";
294 static const char TXT2
[] = "\x0D" "Fourth String" "\x0C" "Fifth String" "\x0C" "Sixth String";
295 printf("Registering Service Test._testdualtxt._tcp.local.\n");
296 client
= DNSServiceRegistrationCreate("Test", "_testdualtxt._tcp.", "", registerPort
.NotAnInteger
, TXT1
, reg_reply
, nil
);
297 record
= DNSServiceRegistrationAddRecord(client
, T_TXT
, sizeof(TXT2
), TXT2
, 120);
304 if (!client
) { fprintf(stderr
, "DNSService call failed\n"); return (-1); }
305 if (AddDNSServiceClientToRunLoop(client
) != 0) { fprintf(stderr
, "AddDNSServiceClientToRunLoop failed\n"); return (-1); }
306 printf("Talking to DNS SD Daemon at Mach port %d\n", DNSServiceDiscoveryMachPort(client
));
309 // Be sure to deallocate the dns_service_discovery_ref when you're finished
310 // Note: What other cleanup has to be done here?
311 // We should probably invalidate, remove and release our CFRunLoopSourceRef?
312 DNSServiceDiscoveryDeallocate(client
);
318 fprintf(stderr
, "%s -E (Enumerate recommended registration domains)\n", argv
[0]);
319 fprintf(stderr
, "%s -F (Enumerate recommended browsing domains)\n", argv
[0]);
320 fprintf(stderr
, "%s -B <Type> <Domain> (Browse for services instances)\n", argv
[0]);
321 fprintf(stderr
, "%s -L <Name> <Type> <Domain> (Look up a service instance)\n", argv
[0]);
322 fprintf(stderr
, "%s -R <Name> <Type> <Domain> <Port> <TXT> (Register a service)\n", argv
[0]);
323 fprintf(stderr
, "%s -A (Test Adding/Updating/Deleting a record)\n", argv
[0]);
324 fprintf(stderr
, "%s -U (Test updating a TXT record)\n", argv
[0]);
325 fprintf(stderr
, "%s -N (Test adding a large NULL record)\n", argv
[0]);
326 fprintf(stderr
, "%s -T (Test creating a large TXT record)\n", argv
[0]);
327 fprintf(stderr
, "%s -M (Test creating a registration with multiple TXT records)\n", argv
[0]);