]> git.saurik.com Git - apple/mdnsresponder.git/blob - SamplemDNSClient.c
mDNSResponder-22.tar.gz
[apple/mdnsresponder.git] / SamplemDNSClient.c
1 /*
2 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23 /*
24 * Formatting notes:
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.)
36 */
37
38 #include <libc.h>
39 #include <arpa/nameser.h>
40 #include <CoreFoundation/CoreFoundation.h>
41 #include <DNSServiceDiscovery/DNSServiceDiscovery.h>
42
43 //*************************************************************************************************************
44 // Globals
45
46 typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16;
47
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];
56
57 //*************************************************************************************************************
58 // Supporting Utility Functions
59 //
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
66 //
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.)
71
72 static void MyHandleMachMessage(CFMachPortRef port, void *msg, CFIndex size, void *info)
73 {
74 DNSServiceDiscovery_handleReply(msg);
75 }
76
77 static int AddDNSServiceClientToRunLoop(dns_service_discovery_ref client)
78 {
79 mach_port_t port = DNSServiceDiscoveryMachPort(client);
80 if (!port)
81 return(-1);
82 else
83 {
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);
89 CFRelease(rls);
90 return(0);
91 }
92 }
93
94 //*************************************************************************************************************
95 // Sample callback functions for each of the operation types
96
97 #define DomainMsg(X) ((X) == DNSServiceDomainEnumerationReplyAddDomain ? "Added" : \
98 (X) == DNSServiceDomainEnumerationReplyAddDomainDefault ? "(Default)" : \
99 (X) == DNSServiceDomainEnumerationReplyRemoveDomain ? "Removed" : "Unknown")
100
101 static void regdom_reply(DNSServiceDomainEnumerationReplyResultType resultType, const char *replyDomain,
102 DNSServiceDiscoveryReplyFlags flags, void *context)
103 {
104 printf("Recommended Registration Domain %s %s", replyDomain, DomainMsg(resultType));
105 if (flags) printf(" Flags: %X", flags);
106 printf("\n");
107 }
108
109 static void browsedom_reply(DNSServiceDomainEnumerationReplyResultType resultType, const char *replyDomain,
110 DNSServiceDiscoveryReplyFlags flags, void *context)
111 {
112 printf("Recommended Browsing Domain %s %s", replyDomain, DomainMsg(resultType));
113 if (flags) printf(" Flags: %X", flags);
114 printf("\n");
115 }
116
117 static void browse_reply(DNSServiceBrowserReplyResultType resultType,
118 const char *replyName, const char *replyType, const char *replyDomain, DNSServiceDiscoveryReplyFlags flags, void *context)
119 {
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);
123 printf("\n");
124 }
125
126 static void resolve_reply(struct sockaddr *interface, struct sockaddr *address, const char *txtRecord, DNSServiceDiscoveryReplyFlags flags, void *context)
127 {
128 if (address->sa_family != AF_INET)
129 printf("Unknown address family %d\n", address->sa_family);
130 else
131 {
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);
138 while (*src)
139 {
140 char txtInfo[256];
141 char *dst = txtInfo;
142 const char *const lim = &txtInfo[sizeof(txtInfo)];
143 while (*src && *src != 1 && dst < lim-1) *dst++ = *src++;
144 *dst++ = 0;
145 printf(" TXT \"%s\"", txtInfo);
146 if (*src == 1) src++;
147 }
148 if (flags) printf(" Flags: %X", flags);
149 printf("\n");
150 }
151 }
152
153 static void myCFRunLoopTimerCallBack(CFRunLoopTimerRef timer, void *info)
154 {
155 (void)timer; // Parameter not used
156 (void)info; // Parameter not used
157
158 switch (operation)
159 {
160 case 'A':
161 {
162 switch (addtest)
163 {
164 case 0: printf("Adding Test HINFO record\n");
165 record = DNSServiceRegistrationAddRecord(client, T_HINFO, sizeof(myhinfo9), &myhinfo9[0], 120);
166 addtest = 1;
167 break;
168 case 1: printf("Updating Test HINFO record\n");
169 DNSServiceRegistrationUpdateRecord(client, record, sizeof(myhinfoX), &myhinfoX[0], 120);
170 addtest = 2;
171 break;
172 case 2: printf("Removing Test HINFO record\n");
173 DNSServiceRegistrationRemoveRecord(client, record);
174 addtest = 0;
175 break;
176 }
177 }
178 break;
179
180 case 'U':
181 {
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);
186 }
187 break;
188
189 case 'N':
190 {
191 printf("Adding big NULL record\n");
192 DNSServiceRegistrationAddRecord(client, T_NULL, sizeof(bigNULL), &bigNULL[0], 120);
193 CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
194 }
195 break;
196 }
197 }
198
199 static void reg_reply(DNSServiceRegistrationReplyErrorType errorCode, void *context)
200 {
201 printf("Got a reply from the server: ");
202 switch (errorCode)
203 {
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;
207 }
208
209 if (operation == 'A' || operation == 'U' || operation == 'N')
210 {
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);
216 }
217 }
218
219 //*************************************************************************************************************
220 // The main test function
221
222 int main(int argc, char **argv)
223 {
224 char *dom;
225
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;
229
230 switch (operation)
231 {
232 case 'E': printf("Looking for recommended registration domains:\n");
233 client = DNSServiceDomainEnumerationCreate(1, regdom_reply, nil);
234 break;
235
236 case 'F': printf("Looking for recommended browsing domains:\n");
237 client = DNSServiceDomainEnumerationCreate(0, browsedom_reply, nil);
238 break;
239
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);
245 break;
246
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);
252 break;
253
254 case 'R': if (argc < optind+4) goto Fail;
255 {
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);
266 break;
267 }
268
269 case 'A':
270 case 'U':
271 case 'N': {
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);
276 break;
277 }
278
279 case 'T': {
280 Opaque16 registerPort = { { 0x23, 0x45 } };
281 char TXT[512];
282 int i;
283 for (i=0; i<sizeof(TXT)-1; i++)
284 if ((i & 0x1F) == 0x1F) TXT[i] = 1; else TXT[i] = 'A' + (i >> 5);
285 TXT[i] = 0;
286 printf("Registering Service Test._testlargetxt._tcp.local.\n");
287 client = DNSServiceRegistrationCreate("Test", "_testlargetxt._tcp.", "", registerPort.NotAnInteger, TXT, reg_reply, nil);
288 break;
289 }
290
291 case 'M': {
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);
298 break;
299 }
300
301 default: goto Exit;
302 }
303
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));
307 CFRunLoopRun();
308
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);
313
314 Exit:
315 return 0;
316
317 Fail:
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]);
328 return 0;
329 }