]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/daemon.c
mDNSResponder-58.1.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / daemon.c
1 /*
2 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 *
25 * Formatting notes:
26 * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
27 * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
28 * but for the sake of brevity here I will say just this: Curly braces are not syntactially
29 * part of an "if" statement; they are the beginning and ending markers of a compound statement;
30 * therefore common sense dictates that if they are part of a compound statement then they
31 * should be indented to the same level as everything else in that compound statement.
32 * Indenting curly braces at the same level as the "if" implies that curly braces are
33 * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
34 * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
35 * understand why variable y is not of type "char*" just proves the point that poor code
36 * layout leads people to unfortunate misunderstandings about how the C language really works.)
37
38 Change History (most recent first):
39
40 $Log: daemon.c,v $
41 Revision 1.134.2.2 2003/12/05 00:03:35 cheshire
42 <rdar://problem/3487869> Use buffer size MAX_ESCAPED_DOMAIN_NAME instead of 256
43
44 Revision 1.134.2.1 2003/12/03 11:00:09 cheshire
45 Update "mDNSResponderVersion" mechanism to allow dots so we can do mDNSResponder-58.1 for SUPan
46
47 Revision 1.134 2003/08/21 20:01:37 cheshire
48 <rdar://problem/3387941> Traffic reduction: Detect long-lived Resolve() calls, and report them in syslog
49
50 Revision 1.133 2003/08/20 23:39:31 cheshire
51 <rdar://problem/3344098> Review syslog messages, and remove as appropriate
52
53 Revision 1.132 2003/08/20 01:44:56 cheshire
54 Fix errors in LogOperation() calls (only used for debugging)
55
56 Revision 1.131 2003/08/19 05:39:43 cheshire
57 <rdar://problem/3380097> SIGINFO dump should include resolves started by DNSServiceQueryRecord
58
59 Revision 1.130 2003/08/16 03:39:01 cheshire
60 <rdar://problem/3338440> InterfaceID -1 indicates "local only"
61
62 Revision 1.129 2003/08/15 20:16:03 cheshire
63 <rdar://problem/3366590> mDNSResponder takes too much RPRVT
64 We want to avoid touching the rdata pages, so we don't page them in.
65 1. RDLength was stored with the rdata, which meant touching the page just to find the length.
66 Moved this from the RData to the ResourceRecord object.
67 2. To avoid unnecessarily touching the rdata just to compare it,
68 compute a hash of the rdata and store the hash in the ResourceRecord object.
69
70 Revision 1.128 2003/08/14 19:30:36 cheshire
71 <rdar://problem/3378473> Include list of cache records in SIGINFO output
72
73 Revision 1.127 2003/08/14 02:18:21 cheshire
74 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
75
76 Revision 1.126 2003/08/12 19:56:25 cheshire
77 Update to APSL 2.0
78
79 Revision 1.125 2003/08/08 18:36:04 cheshire
80 <rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug
81
82 Revision 1.124 2003/07/25 18:28:23 cheshire
83 Minor fix to error messages in syslog: Display string parameters with quotes
84
85 Revision 1.123 2003/07/23 17:45:28 cheshire
86 <rdar://problem/3339388> mDNSResponder leaks a bit
87 Don't allocate memory for the reply until after we've verified that the reply is valid
88
89 Revision 1.122 2003/07/23 00:00:04 cheshire
90 Add comments
91
92 Revision 1.121 2003/07/20 03:38:51 ksekar
93 Bug #: 3320722
94 Completed support for Unix-domain socket based API.
95
96 Revision 1.120 2003/07/18 00:30:00 cheshire
97 <rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead
98
99 Revision 1.119 2003/07/17 19:08:58 cheshire
100 <rdar://problem/3332153> Remove calls to enable obsolete UDS code
101
102 Revision 1.118 2003/07/15 21:12:28 cheshire
103 Added extra debugging checks in validatelists() (not used in final shipping version)
104
105 Revision 1.117 2003/07/15 01:55:15 cheshire
106 <rdar://problem/3315777> Need to implement service registration with subtypes
107
108 Revision 1.116 2003/07/02 21:19:51 cheshire
109 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
110
111 Revision 1.115 2003/07/02 02:41:24 cheshire
112 <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
113
114 Revision 1.114 2003/07/01 21:10:20 cheshire
115 Reinstate checkin 1.111, inadvertently overwritten by checkin 1.112
116
117 Revision 1.113 2003/06/28 17:27:43 vlubet
118 <rdar://problem/3221246> Redirect standard input, standard output, and
119 standard error file descriptors to /dev/null just like any other
120 well behaved daemon
121
122 Revision 1.112 2003/06/25 23:42:19 ksekar
123 Bug #: <rdar://problem/3249292>: Feature: New Rendezvous APIs (#7875)
124 Reviewed by: Stuart Cheshire
125 Added files necessary to implement Unix domain sockets based enhanced
126 Rendezvous APIs, and integrated with existing Mach-port based daemon.
127
128 Revision 1.111 2003/06/11 01:02:43 cheshire
129 <rdar://problem/3287858> mDNSResponder binary compatibility
130 Make single binary that can run on both Jaguar and Panther.
131
132 Revision 1.110 2003/06/10 01:14:11 cheshire
133 <rdar://problem/3286004> New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call
134
135 Revision 1.109 2003/06/06 19:53:43 cheshire
136 For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass
137 (Global search-and-replace; no functional change to code execution.)
138
139 Revision 1.108 2003/06/06 14:08:06 cheshire
140 For clarity, pull body of main while() loop out into a separate function called mDNSDaemonIdle()
141
142 Revision 1.107 2003/05/29 05:44:55 cheshire
143 Minor fixes to log messages
144
145 Revision 1.106 2003/05/27 18:30:55 cheshire
146 <rdar://problem/3262962> Need a way to easily examine current mDNSResponder state
147 Dean Reece suggested SIGINFO is more appropriate than SIGHUP
148
149 Revision 1.105 2003/05/26 03:21:29 cheshire
150 Tidy up address structure naming:
151 mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr)
152 mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
153 mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
154
155 Revision 1.104 2003/05/26 00:42:06 cheshire
156 <rdar://problem/3268876> Temporarily include mDNSResponder version in packets
157
158 Revision 1.103 2003/05/23 23:07:44 cheshire
159 <rdar://problem/3268199> Must not write to stderr when running as daemon
160
161 Revision 1.102 2003/05/22 01:32:31 cheshire
162 Fix typo in Log message format string
163
164 Revision 1.101 2003/05/22 00:26:55 cheshire
165 <rdar://problem/3239284> DNSServiceRegistrationCreate() should return error on dup
166 Modify error message to explain that this is technically legal, but may indicate a bug.
167
168 Revision 1.100 2003/05/21 21:02:24 ksekar
169 Bug #: <rdar://problem/3247035>: Service should be prefixed
170 Changed kmDNSBootstrapName to "com.apple.mDNSResponderRestart" since we're changing the main
171 Mach message port to "com.apple.mDNSResponder.
172
173 Revision 1.99 2003/05/21 17:33:49 cheshire
174 Fix warnings (mainly printf format string warnings, like using "%d" where it should say "%lu", etc.)
175
176 Revision 1.98 2003/05/20 00:33:07 cheshire
177 <rdar://problem/3262962> Need a way to easily examine current mDNSResponder state
178 SIGHUP now writes state summary to syslog
179
180 Revision 1.97 2003/05/08 00:19:08 cheshire
181 <rdar://problem/3250330> Forgot to set "err = mStatus_BadParamErr" in a couple of places
182
183 Revision 1.96 2003/05/07 22:10:46 cheshire
184 <rdar://problem/3250330> Add a few more error logging messages
185
186 Revision 1.95 2003/05/07 19:20:17 cheshire
187 <rdar://problem/3251391> Add version number to mDNSResponder builds
188
189 Revision 1.94 2003/05/07 00:28:18 cheshire
190 <rdar://problem/3250330> Need to make mDNSResponder more defensive against bad clients
191
192 Revision 1.93 2003/05/06 00:00:49 cheshire
193 <rdar://problem/3248914> Rationalize naming of domainname manipulation functions
194
195 Revision 1.92 2003/04/04 20:38:57 cheshire
196 Add $Log header
197
198 */
199
200 #include <mach/mach.h>
201 #include <mach/mach_error.h>
202 #include <servers/bootstrap.h>
203 #include <sys/types.h>
204 #include <unistd.h>
205 #include <paths.h>
206 #include <fcntl.h>
207
208 #include "DNSServiceDiscoveryRequestServer.h"
209 #include "DNSServiceDiscoveryReply.h"
210
211 #include "mDNSClientAPI.h" // Defines the interface to the client layer above
212 #include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform
213
214 #include <DNSServiceDiscovery/DNSServiceDiscovery.h>
215
216 #define ENABLE_UDS 1
217
218 //*************************************************************************************************************
219 // Macros
220
221 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
222 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
223 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
224 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
225 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
226
227 //*************************************************************************************************************
228 // Globals
229
230 mDNSexport mDNS mDNSStorage;
231 static mDNS_PlatformSupport PlatformStorage;
232 #define RR_CACHE_SIZE 64
233 static CacheRecord rrcachestorage[RR_CACHE_SIZE];
234 static const char PID_FILE[] = "/var/run/mDNSResponder.pid";
235
236 static const char kmDNSBootstrapName[] = "com.apple.mDNSResponderRestart";
237 static mach_port_t client_death_port = MACH_PORT_NULL;
238 static mach_port_t exit_m_port = MACH_PORT_NULL;
239 static mach_port_t info_m_port = MACH_PORT_NULL;
240 static mach_port_t server_priv_port = MACH_PORT_NULL;
241
242 // mDNS Mach Message Timeout, in milliseconds.
243 // We need this to be short enough that we don't deadlock the mDNSResponder if a client
244 // fails to service its mach message queue, but long enough to give a well-written
245 // client a chance to service its mach message queue without getting cut off.
246 // Empirically, 50ms seems to work, so we set the timeout to 250ms to give
247 // even extra-slow clients a fair chance before we cut them off.
248 #define MDNS_MM_TIMEOUT 250
249
250 static int restarting_via_mach_init = 0;
251
252 #if MDNS_DEBUGMSGS
253 int debug_mode = 1;
254 #else
255 int debug_mode = 0;
256 #endif
257
258 //*************************************************************************************************************
259 // Active client list structures
260
261 typedef struct DNSServiceDomainEnumeration_struct DNSServiceDomainEnumeration;
262 struct DNSServiceDomainEnumeration_struct
263 {
264 DNSServiceDomainEnumeration *next;
265 mach_port_t ClientMachPort;
266 DNSQuestion dom; // Question asking for domains
267 DNSQuestion def; // Question asking for default domain
268 };
269
270 typedef struct DNSServiceBrowserResult_struct DNSServiceBrowserResult;
271 struct DNSServiceBrowserResult_struct
272 {
273 DNSServiceBrowserResult *next;
274 int resultType;
275 domainname result;
276 };
277
278 typedef struct DNSServiceBrowser_struct DNSServiceBrowser;
279 struct DNSServiceBrowser_struct
280 {
281 DNSServiceBrowser *next;
282 mach_port_t ClientMachPort;
283 DNSQuestion q;
284 DNSServiceBrowserResult *results;
285 mDNSs32 lastsuccess;
286 };
287
288 typedef struct DNSServiceResolver_struct DNSServiceResolver;
289 struct DNSServiceResolver_struct
290 {
291 DNSServiceResolver *next;
292 mach_port_t ClientMachPort;
293 ServiceInfoQuery q;
294 ServiceInfo i;
295 mDNSs32 ReportTime;
296 };
297
298 typedef struct DNSServiceRegistration_struct DNSServiceRegistration;
299 struct DNSServiceRegistration_struct
300 {
301 DNSServiceRegistration *next;
302 mach_port_t ClientMachPort;
303 mDNSBool autoname;
304 mDNSBool autorename;
305 domainlabel name;
306 ServiceRecordSet s;
307 // Don't add any fields after ServiceRecordSet.
308 // This is where the implicit extra space goes if we allocate an oversized ServiceRecordSet object
309 };
310
311 static DNSServiceDomainEnumeration *DNSServiceDomainEnumerationList = NULL;
312 static DNSServiceBrowser *DNSServiceBrowserList = NULL;
313 static DNSServiceResolver *DNSServiceResolverList = NULL;
314 static DNSServiceRegistration *DNSServiceRegistrationList = NULL;
315
316 //*************************************************************************************************************
317 // General Utility Functions
318
319 #if MACOSX_MDNS_MALLOC_DEBUGGING
320
321 char _malloc_options[] = "AXZ";
322
323 static void validatelists(mDNS *const m)
324 {
325 DNSServiceDomainEnumeration *e;
326 DNSServiceBrowser *b;
327 DNSServiceResolver *l;
328 DNSServiceRegistration *r;
329 AuthRecord *rr;
330 CacheRecord *cr;
331 DNSQuestion *q;
332 mDNSs32 slot;
333
334 for (e = DNSServiceDomainEnumerationList; e; e=e->next)
335 if (e->ClientMachPort == 0 || e->ClientMachPort == (mach_port_t)~0)
336 LogMsg("!!!! DNSServiceDomainEnumerationList: %p is garbage (%X) !!!!", e, e->ClientMachPort);
337
338 for (b = DNSServiceBrowserList; b; b=b->next)
339 if (b->ClientMachPort == 0 || b->ClientMachPort == (mach_port_t)~0)
340 LogMsg("!!!! DNSServiceBrowserList: %p is garbage (%X) !!!!", b, b->ClientMachPort);
341
342 for (l = DNSServiceResolverList; l; l=l->next)
343 if (l->ClientMachPort == 0 || l->ClientMachPort == (mach_port_t)~0)
344 LogMsg("!!!! DNSServiceResolverList: %p is garbage (%X) !!!!", l, l->ClientMachPort);
345
346 for (r = DNSServiceRegistrationList; r; r=r->next)
347 if (r->ClientMachPort == 0 || r->ClientMachPort == (mach_port_t)~0)
348 LogMsg("!!!! DNSServiceRegistrationList: %p is garbage (%X) !!!!", r, r->ClientMachPort);
349
350 for (rr = m->ResourceRecords; rr; rr=rr->next)
351 if (rr->RecordType == 0 || rr->RecordType == 0xFF)
352 LogMsg("!!!! ResourceRecords list: %p is garbage (%X) !!!!", rr, rr->RecordType);
353
354 for (rr = m->DuplicateRecords; rr; rr=rr->next)
355 if (rr->RecordType == 0 || rr->RecordType == 0xFF)
356 LogMsg("!!!! DuplicateRecords list: %p is garbage (%X) !!!!", rr, rr->RecordType);
357
358 for (q = m->Questions; q; q=q->next)
359 if (q->ThisQInterval == (mDNSs32)~0)
360 LogMsg("!!!! Questions list: %p is garbage (%lX) !!!!", q, q->ThisQInterval);
361
362 for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
363 for (cr = mDNSStorage.rrcache_hash[slot]; cr; cr=cr->next)
364 if (cr->RecordType == 0 || cr->RecordType == 0xFF)
365 LogMsg("!!!! Cache slot %lu: %p is garbage (%X) !!!!", slot, rr, rr->RecordType);
366 }
367
368 void *mallocL(char *msg, unsigned int size)
369 {
370 unsigned long *mem = malloc(size+8);
371 if (!mem)
372 {
373 LogMsg("malloc( %s : %d ) failed", msg, size);
374 return(NULL);
375 }
376 else
377 {
378 LogMalloc("malloc( %s : %lu ) = %p", msg, size, &mem[2]);
379 mem[0] = 0xDEAD1234;
380 mem[1] = size;
381 //bzero(&mem[2], size);
382 memset(&mem[2], 0xFF, size);
383 validatelists(&mDNSStorage);
384 return(&mem[2]);
385 }
386 }
387
388 void freeL(char *msg, void *x)
389 {
390 if (!x)
391 LogMsg("free( %s @ NULL )!", msg);
392 else
393 {
394 unsigned long *mem = ((unsigned long *)x) - 2;
395 if (mem[0] != 0xDEAD1234)
396 { LogMsg("free( %s @ %p ) !!!! NOT ALLOCATED !!!!", msg, &mem[2]); return; }
397 if (mem[1] > 8000)
398 { LogMsg("free( %s : %ld @ %p) too big!", msg, mem[1], &mem[2]); return; }
399 LogMalloc("free( %s : %ld @ %p)", msg, mem[1], &mem[2]);
400 //bzero(mem, mem[1]+8);
401 memset(mem, 0xFF, mem[1]+8);
402 validatelists(&mDNSStorage);
403 free(mem);
404 }
405 }
406
407 #endif
408
409 //*************************************************************************************************************
410 // Client Death Detection
411
412 mDNSlocal void FreeDNSServiceRegistration(DNSServiceRegistration *x)
413 {
414 while (x->s.Extras)
415 {
416 ExtraResourceRecord *extras = x->s.Extras;
417 x->s.Extras = x->s.Extras->next;
418 if (extras->r.resrec.rdata != &extras->r.rdatastorage)
419 freeL("Extra RData", extras->r.resrec.rdata);
420 freeL("ExtraResourceRecord", extras);
421 }
422
423 if (x->s.RR_TXT.resrec.rdata != &x->s.RR_TXT.rdatastorage)
424 freeL("TXT RData", x->s.RR_TXT.resrec.rdata);
425
426 if (x->s.SubTypes) freeL("ServiceSubTypes", x->s.SubTypes);
427
428 freeL("DNSServiceRegistration", x);
429 }
430
431 // AbortClient finds whatever client is identified by the given Mach port,
432 // stops whatever operation that client was doing, and frees its memory.
433 // In the case of a service registration, the actual freeing may be deferred
434 // until we get the mStatus_MemFree message, if necessary
435 mDNSlocal void AbortClient(mach_port_t ClientMachPort, void *m)
436 {
437 DNSServiceDomainEnumeration **e = &DNSServiceDomainEnumerationList;
438 DNSServiceBrowser **b = &DNSServiceBrowserList;
439 DNSServiceResolver **l = &DNSServiceResolverList;
440 DNSServiceRegistration **r = &DNSServiceRegistrationList;
441
442 while (*e && (*e)->ClientMachPort != ClientMachPort) e = &(*e)->next;
443 if (*e)
444 {
445 DNSServiceDomainEnumeration *x = *e;
446 *e = (*e)->next;
447 if (m && m != x)
448 LogMsg("%5d: DNSServiceDomainEnumeration(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->dom.qname.c, m, x);
449 else LogOperation("%5d: DNSServiceDomainEnumeration(%##s) STOP", ClientMachPort, x->dom.qname.c);
450 mDNS_StopGetDomains(&mDNSStorage, &x->dom);
451 mDNS_StopGetDomains(&mDNSStorage, &x->def);
452 freeL("DNSServiceDomainEnumeration", x);
453 return;
454 }
455
456 while (*b && (*b)->ClientMachPort != ClientMachPort) b = &(*b)->next;
457 if (*b)
458 {
459 DNSServiceBrowser *x = *b;
460 *b = (*b)->next;
461 if (m && m != x)
462 LogMsg("%5d: DNSServiceBrowser(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->q.qname.c, m, x);
463 else LogOperation("%5d: DNSServiceBrowser(%##s) STOP", ClientMachPort, x->q.qname.c);
464 mDNS_StopBrowse(&mDNSStorage, &x->q);
465 while (x->results)
466 {
467 DNSServiceBrowserResult *r = x->results;
468 x->results = x->results->next;
469 freeL("DNSServiceBrowserResult", r);
470 }
471 freeL("DNSServiceBrowser", x);
472 return;
473 }
474
475 while (*l && (*l)->ClientMachPort != ClientMachPort) l = &(*l)->next;
476 if (*l)
477 {
478 DNSServiceResolver *x = *l;
479 *l = (*l)->next;
480 if (m && m != x)
481 LogMsg("%5d: DNSServiceResolver(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->i.name.c, m, x);
482 else LogOperation("%5d: DNSServiceResolver(%##s) STOP", ClientMachPort, x->i.name.c);
483 mDNS_StopResolveService(&mDNSStorage, &x->q);
484 freeL("DNSServiceResolver", x);
485 return;
486 }
487
488 while (*r && (*r)->ClientMachPort != ClientMachPort) r = &(*r)->next;
489 if (*r)
490 {
491 DNSServiceRegistration *x = *r;
492 *r = (*r)->next;
493 x->autorename = mDNSfalse;
494 if (m && m != x)
495 LogMsg("%5d: DNSServiceRegistration(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->s.RR_SRV.resrec.name.c, m, x);
496 else LogOperation("%5d: DNSServiceRegistration(%##s) STOP", ClientMachPort, x->s.RR_SRV.resrec.name.c);
497 // If mDNS_DeregisterService() returns mStatus_NoError, that means that the service was found in the list,
498 // is sending its goodbye packet, and we'll get an mStatus_MemFree message when we can free the memory.
499 // If mDNS_DeregisterService() returns an error, it means that the service had already been removed from
500 // the list, so we should go ahead and free the memory right now
501 if (mDNS_DeregisterService(&mDNSStorage, &x->s) != mStatus_NoError)
502 FreeDNSServiceRegistration(x);
503 return;
504 }
505
506 LogMsg("%5d: died or deallocated, but no record of client can be found!", ClientMachPort);
507 }
508
509 #define AbortBlockedClient(C,MSG,M) AbortClientWithLogMessage((C), "stopped accepting Mach messages", " (" MSG ")", (M))
510
511 mDNSlocal void AbortClientWithLogMessage(mach_port_t c, char *reason, char *msg, void *m)
512 {
513 DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList;
514 DNSServiceBrowser *b = DNSServiceBrowserList;
515 DNSServiceResolver *l = DNSServiceResolverList;
516 DNSServiceRegistration *r = DNSServiceRegistrationList;
517 while (e && e->ClientMachPort != c) e = e->next;
518 while (b && b->ClientMachPort != c) b = b->next;
519 while (l && l->ClientMachPort != c) l = l->next;
520 while (r && r->ClientMachPort != c) r = r->next;
521 if (e) LogMsg("%5d: DomainEnumeration(%##s) %s%s", c, e->dom.qname.c, reason, msg);
522 else if (b) LogMsg("%5d: Browser(%##s) %s%s", c, b->q.qname.c, reason, msg);
523 else if (l) LogMsg("%5d: Resolver(%##s) %s%s", c, l->i.name.c, reason, msg);
524 else if (r) LogMsg("%5d: Registration(%##s) %s%s", c, r->s.RR_SRV.resrec.name.c, reason, msg);
525 else LogMsg("%5d: (%s) %s, but no record of client can be found!", c, reason, msg);
526
527 AbortClient(c, m);
528 }
529
530 mDNSlocal mDNSBool CheckForExistingClient(mach_port_t c)
531 {
532 DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList;
533 DNSServiceBrowser *b = DNSServiceBrowserList;
534 DNSServiceResolver *l = DNSServiceResolverList;
535 DNSServiceRegistration *r = DNSServiceRegistrationList;
536 while (e && e->ClientMachPort != c) e = e->next;
537 while (b && b->ClientMachPort != c) b = b->next;
538 while (l && l->ClientMachPort != c) l = l->next;
539 while (r && r->ClientMachPort != c) r = r->next;
540 if (e) LogMsg("%5d: DomainEnumeration(%##s) already exists!", c, e->dom.qname.c);
541 if (b) LogMsg("%5d: Browser(%##s) already exists!", c, b->q.qname.c);
542 if (l) LogMsg("%5d: Resolver(%##s) already exists!", c, l->i.name.c);
543 if (r) LogMsg("%5d: Registration(%##s) already exists!", c, r->s.RR_SRV.resrec.name.c);
544 return(e || b || l || r);
545 }
546
547 mDNSlocal void ClientDeathCallback(CFMachPortRef unusedport, void *voidmsg, CFIndex size, void *info)
548 {
549 mach_msg_header_t *msg = (mach_msg_header_t *)voidmsg;
550 (void)unusedport; // Unused
551 (void)size; // Unused
552 (void)info; // Unused
553 if (msg->msgh_id == MACH_NOTIFY_DEAD_NAME)
554 {
555 const mach_dead_name_notification_t *const deathMessage = (mach_dead_name_notification_t *)msg;
556 AbortClient(deathMessage->not_port, NULL);
557
558 /* Deallocate the send right that came in the dead name notification */
559 mach_port_destroy( mach_task_self(), deathMessage->not_port );
560 }
561 }
562
563 mDNSlocal void EnableDeathNotificationForClient(mach_port_t ClientMachPort, void *m)
564 {
565 mach_port_t prev;
566 kern_return_t r = mach_port_request_notification(mach_task_self(), ClientMachPort, MACH_NOTIFY_DEAD_NAME, 0,
567 client_death_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev);
568 // If the port already died while we were thinking about it, then abort the operation right away
569 if (r != KERN_SUCCESS)
570 AbortClientWithLogMessage(ClientMachPort, "died/deallocated before we could enable death notification", "", m);
571 }
572
573 //*************************************************************************************************************
574 // Domain Enumeration
575
576 mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
577 {
578 kern_return_t status;
579 #pragma unused(m)
580 char buffer[MAX_ESCAPED_DOMAIN_NAME];
581 DNSServiceDomainEnumerationReplyResultType rt;
582 DNSServiceDomainEnumeration *x = (DNSServiceDomainEnumeration *)question->QuestionContext;
583
584 debugf("FoundDomain: %##s PTR %##s", answer->name.c, answer->rdata->u.name.c);
585 if (answer->rrtype != kDNSType_PTR) return;
586 if (!x) { debugf("FoundDomain: DNSServiceDomainEnumeration is NULL"); return; }
587
588 if (AddRecord)
589 {
590 if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyAddDomain;
591 else rt = DNSServiceDomainEnumerationReplyAddDomainDefault;
592 }
593 else
594 {
595 if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyRemoveDomain;
596 else return;
597 }
598
599 LogOperation("%5d: DNSServiceDomainEnumeration(%##s) %##s %s",
600 x->ClientMachPort, x->dom.qname.c, answer->rdata->u.name.c,
601 !AddRecord ? "RemoveDomain" :
602 question == &x->dom ? "AddDomain" : "AddDomainDefault");
603
604 ConvertDomainNameToCString(&answer->rdata->u.name, buffer);
605 status = DNSServiceDomainEnumerationReply_rpc(x->ClientMachPort, rt, buffer, 0, MDNS_MM_TIMEOUT);
606 if (status == MACH_SEND_TIMED_OUT)
607 AbortBlockedClient(x->ClientMachPort, "enumeration", x);
608 }
609
610 mDNSexport kern_return_t provide_DNSServiceDomainEnumerationCreate_rpc(mach_port_t unusedserver, mach_port_t client,
611 int regDom)
612 {
613 // Check client parameter
614 (void)unusedserver; // Unused
615 mStatus err = mStatus_NoError;
616 const char *errormsg = "Unknown";
617 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
618 if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
619
620 mDNS_DomainType dt1 = regDom ? mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse;
621 mDNS_DomainType dt2 = regDom ? mDNS_DomainTypeRegistrationDefault : mDNS_DomainTypeBrowseDefault;
622 const DNSServiceDomainEnumerationReplyResultType rt = DNSServiceDomainEnumerationReplyAddDomainDefault;
623
624 // Allocate memory, and handle failure
625 DNSServiceDomainEnumeration *x = mallocL("DNSServiceDomainEnumeration", sizeof(*x));
626 if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
627
628 // Set up object, and link into list
629 x->ClientMachPort = client;
630 x->next = DNSServiceDomainEnumerationList;
631 DNSServiceDomainEnumerationList = x;
632
633 // Generate initial response
634 verbosedebugf("%5d: Enumerate %s Domains", client, regDom ? "Registration" : "Browsing");
635 // We always give local. as the initial default browse domain, and then look for more
636 kern_return_t status = DNSServiceDomainEnumerationReply_rpc(x->ClientMachPort, rt, "local.", 0, MDNS_MM_TIMEOUT);
637 if (status == MACH_SEND_TIMED_OUT)
638 { AbortBlockedClient(x->ClientMachPort, "local enumeration", x); return(mStatus_UnknownErr); }
639
640 // Do the operation
641 err = mDNS_GetDomains(&mDNSStorage, &x->dom, dt1, mDNSInterface_Any, FoundDomain, x);
642 if (!err) err = mDNS_GetDomains(&mDNSStorage, &x->def, dt2, mDNSInterface_Any, FoundDomain, x);
643 if (err) { AbortClient(client, x); errormsg = "mDNS_GetDomains"; goto fail; }
644
645 // Succeeded: Wrap up and return
646 LogOperation("%5d: DNSServiceDomainEnumeration(%##s) START", client, x->dom.qname.c);
647 EnableDeathNotificationForClient(client, x);
648 return(mStatus_NoError);
649
650 fail:
651 LogMsg("%5d: DNSServiceDomainEnumeration(%d) failed: %s (%ld)", client, regDom, errormsg, err);
652 return(err);
653 }
654
655 //*************************************************************************************************************
656 // Browse for services
657
658 mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
659 {
660 (void)m; // Unused
661
662 if (answer->rrtype != kDNSType_PTR)
663 { LogMsg("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer->rrtype); return; }
664
665 domainlabel name;
666 domainname type, domain;
667 if (!DeconstructServiceName(&answer->rdata->u.name, &name, &type, &domain))
668 {
669 LogMsg("FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer",
670 answer->name.c, answer->rdata->u.name.c);
671 return;
672 }
673
674 DNSServiceBrowserResult *x = mallocL("DNSServiceBrowserResult", sizeof(*x));
675 if (!x) { LogMsg("FoundInstance: Failed to allocate memory for result %##s", answer->rdata->u.name.c); return; }
676
677 verbosedebugf("FoundInstance: %s %##s", AddRecord ? "Add" : "Rmv", answer->rdata->u.name.c);
678 AssignDomainName(x->result, answer->rdata->u.name);
679 if (AddRecord)
680 x->resultType = DNSServiceBrowserReplyAddInstance;
681 else x->resultType = DNSServiceBrowserReplyRemoveInstance;
682 x->next = NULL;
683
684 DNSServiceBrowser *browser = (DNSServiceBrowser *)question->QuestionContext;
685 DNSServiceBrowserResult **p = &browser->results;
686 while (*p) p = &(*p)->next;
687 *p = x;
688 }
689
690 mDNSexport kern_return_t provide_DNSServiceBrowserCreate_rpc(mach_port_t unusedserver, mach_port_t client,
691 DNSCString regtype, DNSCString domain)
692 {
693 // Check client parameter
694 (void)unusedserver; // Unused
695 mStatus err = mStatus_NoError;
696 const char *errormsg = "Unknown";
697 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
698 if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
699
700 // Check other parameters
701 domainname t, d;
702 if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Illegal regtype"; goto badparam; }
703 if (!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) { errormsg = "Illegal domain"; goto badparam; }
704
705 // Allocate memory, and handle failure
706 DNSServiceBrowser *x = mallocL("DNSServiceBrowser", sizeof(*x));
707 if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
708
709 // Set up object, and link into list
710 x->ClientMachPort = client;
711 x->results = NULL;
712 x->lastsuccess = 0;
713 x->next = DNSServiceBrowserList;
714 DNSServiceBrowserList = x;
715
716 // Do the operation
717 LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", client, t.c, d.c);
718 err = mDNS_StartBrowse(&mDNSStorage, &x->q, &t, &d, mDNSInterface_Any, FoundInstance, x);
719 if (err) { AbortClient(client, x); errormsg = "mDNS_StartBrowse"; goto fail; }
720
721 // Succeeded: Wrap up and return
722 EnableDeathNotificationForClient(client, x);
723 return(mStatus_NoError);
724
725 badparam:
726 err = mStatus_BadParamErr;
727 fail:
728 LogMsg("%5d: DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%ld)", client, regtype, domain, errormsg, err);
729 return(err);
730 }
731
732 //*************************************************************************************************************
733 // Resolve Service Info
734
735 mDNSlocal void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query)
736 {
737 kern_return_t status;
738 DNSServiceResolver *x = (DNSServiceResolver *)query->ServiceInfoQueryContext;
739 NetworkInterfaceInfoOSX *ifx = (NetworkInterfaceInfoOSX *)query->info->InterfaceID;
740 if (query->info->InterfaceID == (mDNSInterfaceID)~0) ifx = mDNSNULL;
741 struct sockaddr_storage interface;
742 struct sockaddr_storage address;
743 char cstring[1024];
744 int i, pstrlen = query->info->TXTinfo[0];
745 (void)m; // Unused
746
747 //debugf("FoundInstanceInfo %.4a %.4a %##s", &query->info->InterfaceAddr, &query->info->ip, &query->info->name);
748
749 if (query->info->TXTlen > sizeof(cstring)) return;
750
751 bzero(&interface, sizeof(interface));
752 bzero(&address, sizeof(address));
753
754 if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv4)
755 {
756 struct sockaddr_in *sin = (struct sockaddr_in*)&interface;
757 sin->sin_len = sizeof(*sin);
758 sin->sin_family = AF_INET;
759 sin->sin_port = 0;
760 sin->sin_addr.s_addr = ifx->ifinfo.ip.ip.v4.NotAnInteger;
761 }
762 else if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv6)
763 {
764 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&interface;
765 sin6->sin6_len = sizeof(*sin6);
766 sin6->sin6_family = AF_INET6;
767 sin6->sin6_flowinfo = 0;
768 sin6->sin6_port = 0;
769 sin6->sin6_addr = *(struct in6_addr*)&ifx->ifinfo.ip.ip.v6;
770 sin6->sin6_scope_id = ifx->scope_id;
771 }
772
773 if (query->info->ip.type == mDNSAddrType_IPv4)
774 {
775 struct sockaddr_in *sin = (struct sockaddr_in*)&address;
776 sin->sin_len = sizeof(*sin);
777 sin->sin_family = AF_INET;
778 sin->sin_port = query->info->port.NotAnInteger;
779 sin->sin_addr.s_addr = query->info->ip.ip.v4.NotAnInteger;
780 }
781 else
782 {
783 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&address;
784 sin6->sin6_len = sizeof(*sin6);
785 sin6->sin6_family = AF_INET6;
786 sin6->sin6_port = query->info->port.NotAnInteger;
787 sin6->sin6_flowinfo = 0;
788 sin6->sin6_addr = *(struct in6_addr*)&query->info->ip.ip.v6;
789 sin6->sin6_scope_id = ifx ? ifx->scope_id : 0;
790 }
791
792 // The OS X DNSServiceResolverResolve() API is defined using a C-string,
793 // but the mDNS_StartResolveService() call actually returns a packed block of P-strings.
794 // Hence we have to convert the P-string(s) to a C-string before returning the result to the client.
795 // ASCII-1 characters are used in the C-string as boundary markers,
796 // to indicate the boundaries between the original constituent P-strings.
797 for (i=1; i<query->info->TXTlen; i++)
798 {
799 if (--pstrlen >= 0)
800 cstring[i-1] = query->info->TXTinfo[i];
801 else
802 {
803 cstring[i-1] = 1;
804 pstrlen = query->info->TXTinfo[i];
805 }
806 }
807 cstring[i-1] = 0; // Put the terminating NULL on the end
808
809 LogOperation("%5d: DNSServiceResolver(%##s) -> %#a:%d", x->ClientMachPort,
810 x->i.name.c, &query->info->ip, (int)query->info->port.b[0] << 8 | query->info->port.b[1]);
811 status = DNSServiceResolverReply_rpc(x->ClientMachPort,
812 (char*)&interface, (char*)&address, cstring, 0, MDNS_MM_TIMEOUT);
813 if (status == MACH_SEND_TIMED_OUT)
814 AbortBlockedClient(x->ClientMachPort, "resolve", x);
815 }
816
817 mDNSexport kern_return_t provide_DNSServiceResolverResolve_rpc(mach_port_t unusedserver, mach_port_t client,
818 DNSCString name, DNSCString regtype, DNSCString domain)
819 {
820 // Check client parameter
821 (void)unusedserver; // Unused
822 mStatus err = mStatus_NoError;
823 const char *errormsg = "Unknown";
824 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
825 if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
826
827 // Check other parameters
828 domainlabel n;
829 domainname t, d, srv;
830 if (!name[0] || !MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; }
831 if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; }
832 if (!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) { errormsg = "Bad Domain"; goto badparam; }
833 if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; }
834
835 // Allocate memory, and handle failure
836 DNSServiceResolver *x = mallocL("DNSServiceResolver", sizeof(*x));
837 if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
838
839 // Set up object, and link into list
840 x->ClientMachPort = client;
841 x->i.InterfaceID = mDNSInterface_Any;
842 x->i.name = srv;
843 x->ReportTime = (mDNSPlatformTimeNow() + 130 * mDNSPlatformOneSecond) | 1;
844 // Don't report errors for old iChat ("_ichat._tcp") service.
845 // New iChat ("_presence._tcp") uses DNSServiceQueryRecord() (from /usr/include/dns_sd.h) instead,
846 // and so should other applications that have valid reasons to be doing ongoing record monitoring.
847 if (SameDomainLabel(t.c, (mDNSu8*)"\x6_ichat")) x->ReportTime = 0;
848 x->next = DNSServiceResolverList;
849 DNSServiceResolverList = x;
850
851 // Do the operation
852 LogOperation("%5d: DNSServiceResolver(%##s) START", client, x->i.name.c);
853 err = mDNS_StartResolveService(&mDNSStorage, &x->q, &x->i, FoundInstanceInfo, x);
854 if (err) { AbortClient(client, x); errormsg = "mDNS_StartResolveService"; goto fail; }
855
856 // Succeeded: Wrap up and return
857 EnableDeathNotificationForClient(client, x);
858 return(mStatus_NoError);
859
860 badparam:
861 err = mStatus_BadParamErr;
862 fail:
863 LogMsg("%5d: DNSServiceResolve(\"%s\", \"%s\", \"%s\") failed: %s (%ld)", client, name, regtype, domain, errormsg, err);
864 return(err);
865 }
866
867 //*************************************************************************************************************
868 // Registration
869
870 mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result)
871 {
872 DNSServiceRegistration *x = (DNSServiceRegistration*)sr->ServiceContext;
873
874 if (result == mStatus_NoError)
875 {
876 kern_return_t status;
877 LogOperation("%5d: DNSServiceRegistration(%##s) Name Registered", x->ClientMachPort, sr->RR_SRV.resrec.name.c);
878 status = DNSServiceRegistrationReply_rpc(x->ClientMachPort, result, MDNS_MM_TIMEOUT);
879 if (status == MACH_SEND_TIMED_OUT)
880 AbortBlockedClient(x->ClientMachPort, "registration success", x);
881 }
882
883 else if (result == mStatus_NameConflict)
884 {
885 LogOperation("%5d: DNSServiceRegistration(%##s) Name Conflict", x->ClientMachPort, sr->RR_SRV.resrec.name.c);
886 // Note: By the time we get the mStatus_NameConflict message, the service is already deregistered
887 // and the memory is free, so we don't have to wait for an mStatus_MemFree message as well.
888 if (x->autoname)
889 mDNS_RenameAndReregisterService(m, sr, mDNSNULL);
890 else
891 {
892 // If we get a name conflict, we tell the client about it, and then they are expected to dispose
893 // of their registration in the usual way (which we will catch via client death notification).
894 // If the Mach queue is full, we forcibly abort the client immediately.
895 kern_return_t status = DNSServiceRegistrationReply_rpc(x->ClientMachPort, result, MDNS_MM_TIMEOUT);
896 if (status == MACH_SEND_TIMED_OUT)
897 AbortBlockedClient(x->ClientMachPort, "registration conflict", x);
898 }
899 }
900
901 else if (result == mStatus_MemFree)
902 {
903 if (x->autorename)
904 {
905 debugf("RegCallback renaming %#s to %#s", x->name.c, mDNSStorage.nicelabel.c);
906 x->autorename = mDNSfalse;
907 x->name = mDNSStorage.nicelabel;
908 mDNS_RenameAndReregisterService(m, &x->s, &x->name);
909 }
910 else
911 {
912 DNSServiceRegistration **r = &DNSServiceRegistrationList;
913 while (*r && *r != x) r = &(*r)->next;
914 if (*r)
915 {
916 LogMsg("RegCallback: %##s Still in DNSServiceRegistration list; removing now", sr->RR_SRV.resrec.name.c);
917 *r = (*r)->next;
918 }
919 LogOperation("%5d: DNSServiceRegistration(%##s) Memory Free", x->ClientMachPort, sr->RR_SRV.resrec.name.c);
920 FreeDNSServiceRegistration(x);
921 }
922 }
923
924 else
925 LogMsg("%5d: DNSServiceRegistration(%##s) Unknown Result %ld",
926 x->ClientMachPort, sr->RR_SRV.resrec.name.c, result);
927 }
928
929 mDNSlocal void CheckForDuplicateRegistrations(DNSServiceRegistration *x, domainname *srv, mDNSIPPort port)
930 {
931 int count = 1; // Start with the one we're planning to register, then see if there are any more
932 AuthRecord *rr;
933 for (rr = mDNSStorage.ResourceRecords; rr; rr=rr->next)
934 if (rr->resrec.rrtype == kDNSType_SRV &&
935 rr->resrec.rdata->u.srv.port.NotAnInteger == port.NotAnInteger &&
936 SameDomainName(&rr->resrec.name, srv))
937 count++;
938
939 if (count > 1)
940 LogMsg("%5d: Client application registered %d identical instances of service %##s port %d.",
941 x->ClientMachPort, count, srv->c, (int)port.b[0] << 8 | port.b[1]);
942 }
943
944 mDNSexport kern_return_t provide_DNSServiceRegistrationCreate_rpc(mach_port_t unusedserver, mach_port_t client,
945 DNSCString name, DNSCString regtype, DNSCString domain, int notAnIntPort, DNSCString txtRecord)
946 {
947 // Check client parameter
948 (void)unusedserver; // Unused
949 mStatus err = mStatus_NoError;
950 const char *errormsg = "Unknown";
951 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
952 if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
953
954 // Check for sub-types after the service type
955 AuthRecord *SubTypes = mDNSNULL;
956 mDNSu32 i, NumSubTypes = 0;
957 char *comma = regtype;
958 while (*comma && *comma != ',') comma++;
959 if (*comma) // If we found a comma...
960 {
961 *comma = 0; // Overwrite the first comma with a nul
962 char *p = comma + 1; // Start scanning from the next character
963 while (*p)
964 {
965 if ( !(*p && *p != ',')) { errormsg = "Bad Service SubType"; goto badparam; }
966 while (*p && *p != ',') p++;
967 if (*p) *p++ = 0;
968 NumSubTypes++;
969 }
970 }
971
972 // Check other parameters
973 domainlabel n;
974 domainname t, d;
975 domainname srv;
976 if (!name[0]) n = mDNSStorage.nicelabel;
977 else if (!MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; }
978 if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; }
979 if (!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) { errormsg = "Bad Domain"; goto badparam; }
980 if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; }
981
982 mDNSIPPort port;
983 port.NotAnInteger = notAnIntPort;
984
985 unsigned char txtinfo[1024] = "";
986 unsigned int data_len = 0;
987 unsigned int size = sizeof(RDataBody);
988 unsigned char *pstring = &txtinfo[data_len];
989 char *ptr = txtRecord;
990
991 // The OS X DNSServiceRegistrationCreate() API is defined using a C-string,
992 // but the mDNS_RegisterService() call actually requires a packed block of P-strings.
993 // Hence we have to convert the C-string to a P-string.
994 // ASCII-1 characters are allowed in the C-string as boundary markers,
995 // so that a single C-string can be used to represent one or more P-strings.
996 while (*ptr)
997 {
998 if (++data_len >= sizeof(txtinfo)) { errormsg = "TXT record too long"; goto badtxt; }
999 if (*ptr == 1) // If this is our boundary marker, start a new P-string
1000 {
1001 pstring = &txtinfo[data_len];
1002 pstring[0] = 0;
1003 ptr++;
1004 }
1005 else
1006 {
1007 if (pstring[0] == 255) { errormsg = "TXT record invalid (component longer than 255)"; goto badtxt; }
1008 pstring[++pstring[0]] = *ptr++;
1009 }
1010 }
1011
1012 data_len++;
1013 if (size < data_len)
1014 size = data_len;
1015
1016 // Allocate memory, and handle failure
1017 DNSServiceRegistration *x = mallocL("DNSServiceRegistration", sizeof(*x) - sizeof(RDataBody) + size);
1018 if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1019
1020 if (NumSubTypes)
1021 {
1022 SubTypes = mallocL("ServiceSubTypes", NumSubTypes * sizeof(AuthRecord));
1023 if (!SubTypes) { freeL("DNSServiceRegistration", x); err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1024 for (i = 0; i < NumSubTypes; i++)
1025 {
1026 comma++; // Advance over the nul character
1027 MakeDomainNameFromDNSNameString(&SubTypes[i].resrec.name, comma);
1028 while (*comma) comma++; // Advance comma to point to the next terminating nul
1029 }
1030 }
1031
1032 // Set up object, and link into list
1033 x->ClientMachPort = client;
1034 x->autoname = (!name[0]);
1035 x->autorename = mDNSfalse;
1036 x->name = n;
1037 x->next = DNSServiceRegistrationList;
1038 DNSServiceRegistrationList = x;
1039
1040 // Do the operation
1041 LogOperation("%5d: DNSServiceRegistration(\"%s\", \"%s\", \"%s\") START", x->ClientMachPort, name, regtype, domain);
1042 // Some clients use mDNS for lightweight copy protection, registering a pseudo-service with
1043 // a port number of zero. When two instances of the protected client are allowed to run on one
1044 // machine, we don't want to see misleading "Bogus client" messages in syslog and the console.
1045 if (port.NotAnInteger) CheckForDuplicateRegistrations(x, &srv, port);
1046
1047 err = mDNS_RegisterService(&mDNSStorage, &x->s,
1048 &x->name, &t, &d, // Name, type, domain
1049 mDNSNULL, port, // Host and port
1050 txtinfo, data_len, // TXT data, length
1051 SubTypes, NumSubTypes, // Subtypes
1052 mDNSInterface_Any, // Interace ID
1053 RegCallback, x); // Callback and context
1054
1055 if (err) { AbortClient(client, x); errormsg = "mDNS_RegisterService"; goto fail; }
1056
1057 // Succeeded: Wrap up and return
1058 EnableDeathNotificationForClient(client, x);
1059 return(mStatus_NoError);
1060
1061 badtxt:
1062 LogMsg("%5d: TXT record: %.100s...", client, txtRecord);
1063 badparam:
1064 err = mStatus_BadParamErr;
1065 fail:
1066 LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%ld)",
1067 client, name, regtype, domain, notAnIntPort, errormsg, err);
1068 return(err);
1069 }
1070
1071 mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result)
1072 {
1073 (void)m; // Unused
1074 if (result == mStatus_ConfigChanged)
1075 {
1076 DNSServiceRegistration *r;
1077 for (r = DNSServiceRegistrationList; r; r=r->next)
1078 if (r->autoname && !SameDomainLabel(r->name.c, mDNSStorage.nicelabel.c))
1079 {
1080 debugf("NetworkChanged renaming %#s to %#s", r->name.c, mDNSStorage.nicelabel.c);
1081 r->autorename = mDNStrue;
1082 mDNS_DeregisterService(&mDNSStorage, &r->s);
1083 }
1084 }
1085 else if (result == mStatus_GrowCache)
1086 {
1087 // If we've run out of cache space, then double the total cache size and give the memory to mDNSCore
1088 mDNSu32 numrecords = m->rrcache_size;
1089 CacheRecord *storage = mallocL("mStatus_GrowCache", sizeof(CacheRecord) * numrecords);
1090 if (storage) mDNS_GrowCache(&mDNSStorage, storage, numrecords);
1091 }
1092 }
1093
1094 //*************************************************************************************************************
1095 // Add / Update / Remove records from existing Registration
1096
1097 mDNSexport kern_return_t provide_DNSServiceRegistrationAddRecord_rpc(mach_port_t unusedserver, mach_port_t client,
1098 int type, const char *data, mach_msg_type_number_t data_len, uint32_t ttl, natural_t *reference)
1099 {
1100 // Check client parameter
1101 (void)unusedserver; // Unused
1102 mStatus err = mStatus_NoError;
1103 const char *errormsg = "Unknown";
1104 domainname *name = (domainname *)"";
1105 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1106 DNSServiceRegistration *x = DNSServiceRegistrationList;
1107 while (x && x->ClientMachPort != client) x = x->next;
1108 if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; }
1109 name = &x->s.RR_SRV.resrec.name;
1110
1111 // Check other parameters
1112 if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; }
1113 unsigned int size = sizeof(RDataBody);
1114 if (size < data_len)
1115 size = data_len;
1116
1117 // Allocate memory, and handle failure
1118 ExtraResourceRecord *extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size);
1119 if (!extra) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1120
1121 // Fill in type, length, and data of new record
1122 extra->r.resrec.rrtype = type;
1123 extra->r.rdatastorage.MaxRDLength = size;
1124 extra->r.resrec.rdlength = data_len;
1125 memcpy(&extra->r.rdatastorage.u.data, data, data_len);
1126
1127 // Do the operation
1128 LogOperation("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) REF %p",
1129 client, x->s.RR_SRV.resrec.name.c, type, data_len, extra);
1130 err = mDNS_AddRecordToService(&mDNSStorage, &x->s, extra, &extra->r.rdatastorage, ttl);
1131 *reference = (natural_t)extra;
1132 if (err) { errormsg = "mDNS_AddRecordToService"; goto fail; }
1133
1134 // Succeeded: Wrap up and return
1135 return(mStatus_NoError);
1136
1137 fail:
1138 LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%ld)", client, name->c, type, data_len, errormsg, err);
1139 return(err);
1140 }
1141
1142 mDNSlocal void UpdateCallback(mDNS *const m, AuthRecord *const rr, RData *OldRData)
1143 {
1144 (void)m; // Unused
1145 if (OldRData != &rr->rdatastorage)
1146 freeL("Old RData", OldRData);
1147 }
1148
1149 mDNSexport kern_return_t provide_DNSServiceRegistrationUpdateRecord_rpc(mach_port_t unusedserver, mach_port_t client,
1150 natural_t reference, const char *data, mach_msg_type_number_t data_len, uint32_t ttl)
1151 {
1152 // Check client parameter
1153 (void)unusedserver; // Unused
1154 mStatus err = mStatus_NoError;
1155 const char *errormsg = "Unknown";
1156 domainname *name = (domainname *)"";
1157 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1158 DNSServiceRegistration *x = DNSServiceRegistrationList;
1159 while (x && x->ClientMachPort != client) x = x->next;
1160 if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; }
1161 name = &x->s.RR_SRV.resrec.name;
1162
1163 // Check other parameters
1164 if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; }
1165 unsigned int size = sizeof(RDataBody);
1166 if (size < data_len)
1167 size = data_len;
1168
1169 // Find the record we're updating. NULL reference means update the primary TXT record
1170 AuthRecord *rr = &x->s.RR_TXT;
1171 if (reference) // Scan our list to make sure we're updating a valid record that was previously added
1172 {
1173 ExtraResourceRecord *e = x->s.Extras;
1174 while (e && e != (ExtraResourceRecord*)reference) e = e->next;
1175 if (!e) { err = mStatus_BadReferenceErr; errormsg = "No such record"; goto fail; }
1176 rr = &e->r;
1177 }
1178
1179 // Allocate memory, and handle failure
1180 RData *newrdata = mallocL("RData", sizeof(*newrdata) - sizeof(RDataBody) + size);
1181 if (!newrdata) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
1182
1183 // Fill in new length, and data
1184 newrdata->MaxRDLength = size;
1185 memcpy(&newrdata->u, data, data_len);
1186
1187 // Do the operation
1188 LogOperation("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, new length %d)",
1189 client, x->s.RR_SRV.resrec.name.c, reference, data_len);
1190 err = mDNS_Update(&mDNSStorage, rr, ttl, data_len, newrdata, UpdateCallback);
1191 if (err) { errormsg = "mDNS_Update"; goto fail; }
1192
1193 // Succeeded: Wrap up and return
1194 return(mStatus_NoError);
1195
1196 fail:
1197 LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, %d) failed: %s (%ld)", client, name->c, reference, data_len, errormsg, err);
1198 return(err);
1199 }
1200
1201 mDNSexport kern_return_t provide_DNSServiceRegistrationRemoveRecord_rpc(mach_port_t unusedserver, mach_port_t client,
1202 natural_t reference)
1203 {
1204 // Check client parameter
1205 (void)unusedserver; // Unused
1206 mStatus err = mStatus_NoError;
1207 const char *errormsg = "Unknown";
1208 domainname *name = (domainname *)"";
1209 if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
1210 DNSServiceRegistration *x = DNSServiceRegistrationList;
1211 while (x && x->ClientMachPort != client) x = x->next;
1212 if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; }
1213 name = &x->s.RR_SRV.resrec.name;
1214
1215 // Do the operation
1216 LogOperation("%5d: DNSServiceRegistrationRemoveRecord(%##s, %X)", client, x->s.RR_SRV.resrec.name.c, reference);
1217 ExtraResourceRecord *extra = (ExtraResourceRecord*)reference;
1218 err = mDNS_RemoveRecordFromService(&mDNSStorage, &x->s, extra);
1219 if (err) { errormsg = "mDNS_RemoveRecordFromService (No such record)"; goto fail; }
1220
1221 // Succeeded: Wrap up and return
1222 if (extra->r.resrec.rdata != &extra->r.rdatastorage)
1223 freeL("Extra RData", extra->r.resrec.rdata);
1224 freeL("ExtraResourceRecord", extra);
1225 return(mStatus_NoError);
1226
1227 fail:
1228 LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%##s, %X) failed: %s (%ld)", client, name->c, reference, errormsg, err);
1229 return(err);
1230 }
1231
1232 //*************************************************************************************************************
1233 // Support Code
1234
1235 mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
1236 {
1237 mig_reply_error_t *request = msg;
1238 mig_reply_error_t *reply;
1239 mach_msg_return_t mr;
1240 int options;
1241 (void)port; // Unused
1242 (void)size; // Unused
1243 (void)info; // Unused
1244
1245 /* allocate a reply buffer */
1246 reply = CFAllocatorAllocate(NULL, provide_DNSServiceDiscoveryRequest_subsystem.maxsize, 0);
1247
1248 /* call the MiG server routine */
1249 (void) DNSServiceDiscoveryRequest_server(&request->Head, &reply->Head);
1250
1251 if (!(reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && (reply->RetCode != KERN_SUCCESS))
1252 {
1253 if (reply->RetCode == MIG_NO_REPLY)
1254 {
1255 /*
1256 * This return code is a little tricky -- it appears that the
1257 * demux routine found an error of some sort, but since that
1258 * error would not normally get returned either to the local
1259 * user or the remote one, we pretend it's ok.
1260 */
1261 CFAllocatorDeallocate(NULL, reply);
1262 return;
1263 }
1264
1265 /*
1266 * destroy any out-of-line data in the request buffer but don't destroy
1267 * the reply port right (since we need that to send an error message).
1268 */
1269 request->Head.msgh_remote_port = MACH_PORT_NULL;
1270 mach_msg_destroy(&request->Head);
1271 }
1272
1273 if (reply->Head.msgh_remote_port == MACH_PORT_NULL)
1274 {
1275 /* no reply port, so destroy the reply */
1276 if (reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
1277 mach_msg_destroy(&reply->Head);
1278 CFAllocatorDeallocate(NULL, reply);
1279 return;
1280 }
1281
1282 /*
1283 * send reply.
1284 *
1285 * We don't want to block indefinitely because the client
1286 * isn't receiving messages from the reply port.
1287 * If we have a send-once right for the reply port, then
1288 * this isn't a concern because the send won't block.
1289 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
1290 * To avoid falling off the kernel's fast RPC path unnecessarily,
1291 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
1292 */
1293
1294 options = MACH_SEND_MSG;
1295 if (MACH_MSGH_BITS_REMOTE(reply->Head.msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE)
1296 options |= MACH_SEND_TIMEOUT;
1297
1298 mr = mach_msg(&reply->Head, /* msg */
1299 options, /* option */
1300 reply->Head.msgh_size, /* send_size */
1301 0, /* rcv_size */
1302 MACH_PORT_NULL, /* rcv_name */
1303 MACH_MSG_TIMEOUT_NONE, /* timeout */
1304 MACH_PORT_NULL); /* notify */
1305
1306 /* Has a message error occurred? */
1307 switch (mr)
1308 {
1309 case MACH_SEND_INVALID_DEST:
1310 case MACH_SEND_TIMED_OUT:
1311 /* the reply can't be delivered, so destroy it */
1312 mach_msg_destroy(&reply->Head);
1313 break;
1314
1315 default :
1316 /* Includes success case. */
1317 break;
1318 }
1319
1320 CFAllocatorDeallocate(NULL, reply);
1321 }
1322
1323 mDNSlocal kern_return_t registerBootstrapService()
1324 {
1325 kern_return_t status;
1326 mach_port_t service_send_port, service_rcv_port;
1327
1328 debugf("Registering Bootstrap Service");
1329
1330 /*
1331 * See if our service name is already registered and if we have privilege to check in.
1332 */
1333 status = bootstrap_check_in(bootstrap_port, (char*)kmDNSBootstrapName, &service_rcv_port);
1334 if (status == KERN_SUCCESS)
1335 {
1336 /*
1337 * If so, we must be a followup instance of an already defined server. In that case,
1338 * the bootstrap port we inherited from our parent is the server's privilege port, so set
1339 * that in case we have to unregister later (which requires the privilege port).
1340 */
1341 server_priv_port = bootstrap_port;
1342 restarting_via_mach_init = TRUE;
1343 }
1344 else if (status == BOOTSTRAP_UNKNOWN_SERVICE)
1345 {
1346 status = bootstrap_create_server(bootstrap_port, "/usr/sbin/mDNSResponder", getuid(),
1347 FALSE /* relaunch immediately, not on demand */, &server_priv_port);
1348 if (status != KERN_SUCCESS) return status;
1349
1350 status = bootstrap_create_service(server_priv_port, (char*)kmDNSBootstrapName, &service_send_port);
1351 if (status != KERN_SUCCESS)
1352 {
1353 mach_port_deallocate(mach_task_self(), server_priv_port);
1354 return status;
1355 }
1356
1357 status = bootstrap_check_in(server_priv_port, (char*)kmDNSBootstrapName, &service_rcv_port);
1358 if (status != KERN_SUCCESS)
1359 {
1360 mach_port_deallocate(mach_task_self(), server_priv_port);
1361 mach_port_deallocate(mach_task_self(), service_send_port);
1362 return status;
1363 }
1364 assert(service_send_port == service_rcv_port);
1365 }
1366
1367 /*
1368 * We have no intention of responding to requests on the service port. We are not otherwise
1369 * a Mach port-based service. We are just using this mechanism for relaunch facilities.
1370 * So, we can dispose of all the rights we have for the service port. We don't destroy the
1371 * send right for the server's privileged bootstrap port - in case we have to unregister later.
1372 */
1373 mach_port_destroy(mach_task_self(), service_rcv_port);
1374 return status;
1375 }
1376
1377 mDNSlocal kern_return_t destroyBootstrapService()
1378 {
1379 debugf("Destroying Bootstrap Service");
1380 return bootstrap_register(server_priv_port, (char*)kmDNSBootstrapName, MACH_PORT_NULL);
1381 }
1382
1383 mDNSlocal void ExitCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
1384 {
1385 (void)port; // Unused
1386 (void)msg; // Unused
1387 (void)size; // Unused
1388 (void)info; // Unused
1389 /*
1390 CacheRecord *rr;
1391 int rrcache_active = 0;
1392 for (rr = mDNSStorage.rrcache; rr; rr=rr->next) if (CacheRRActive(&mDNSStorage, rr)) rrcache_active++;
1393 debugf("ExitCallback: RR Cache now using %d records, %d active", mDNSStorage.rrcache_used, rrcache_active);
1394 */
1395
1396 LogMsg("%s stopping", mDNSResponderVersionString);
1397
1398 debugf("ExitCallback: destroyBootstrapService");
1399 if (!debug_mode)
1400 destroyBootstrapService();
1401
1402 debugf("ExitCallback: Aborting MIG clients");
1403 while (DNSServiceDomainEnumerationList)
1404 AbortClient(DNSServiceDomainEnumerationList->ClientMachPort, DNSServiceDomainEnumerationList);
1405 while (DNSServiceBrowserList)
1406 AbortClient(DNSServiceBrowserList ->ClientMachPort, DNSServiceBrowserList);
1407 while (DNSServiceResolverList)
1408 AbortClient(DNSServiceResolverList ->ClientMachPort, DNSServiceResolverList);
1409 while (DNSServiceRegistrationList)
1410 AbortClient(DNSServiceRegistrationList ->ClientMachPort, DNSServiceRegistrationList);
1411
1412 debugf("ExitCallback: mDNS_Close");
1413 mDNS_Close(&mDNSStorage);
1414 #if ENABLE_UDS
1415 if (udsserver_exit() < 0) LogMsg("ExitCallback: udsserver_exit failed");
1416 #endif
1417 exit(0);
1418 }
1419
1420 // Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit
1421 mDNSlocal void HandleSIGTERM(int signal)
1422 {
1423 (void)signal; // Unused
1424 debugf(" ");
1425 debugf("SIGINT/SIGTERM");
1426 mach_msg_header_t header;
1427 header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
1428 header.msgh_remote_port = exit_m_port;
1429 header.msgh_local_port = MACH_PORT_NULL;
1430 header.msgh_size = sizeof(header);
1431 header.msgh_id = 0;
1432 if (mach_msg_send(&header) != MACH_MSG_SUCCESS)
1433 { LogMsg("HandleSIGTERM: mach_msg_send failed; Exiting immediately."); exit(-1); }
1434 }
1435
1436 mDNSlocal void INFOCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
1437 {
1438 (void)port; // Unused
1439 (void)msg; // Unused
1440 (void)size; // Unused
1441 (void)info; // Unused
1442 DNSServiceDomainEnumeration *e;
1443 DNSServiceBrowser *b;
1444 DNSServiceResolver *l;
1445 DNSServiceRegistration *r;
1446 mDNSs32 slot;
1447 CacheRecord *rr;
1448 mDNSu32 CacheUsed = 0, CacheActive = 0;
1449
1450 LogMsg("%s ---- BEGIN STATE LOG ----", mDNSResponderVersionString);
1451
1452 for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
1453 for (rr = mDNSStorage.rrcache_hash[slot]; rr; rr=rr->next)
1454 {
1455 CacheUsed++;
1456 if (rr->CRActiveQuestion) CacheActive++;
1457 LogMsg("%s %-5s%-6s%s", rr->CRActiveQuestion ? "Active: " : "Inactive:", DNSTypeName(rr->resrec.rrtype),
1458 ((NetworkInterfaceInfoOSX *)rr->resrec.InterfaceID)->ifa_name, GetRRDisplayString(&mDNSStorage, rr));
1459 usleep(1000); // Limit rate a little so we don't flood syslog too fast
1460 }
1461 if (mDNSStorage.rrcache_totalused != CacheUsed)
1462 LogMsg("Cache use mismatch: rrcache_totalused is %lu, true count %lu", mDNSStorage.rrcache_totalused, CacheUsed);
1463 if (mDNSStorage.rrcache_active != CacheActive)
1464 LogMsg("Cache use mismatch: rrcache_active is %lu, true count %lu", mDNSStorage.rrcache_active, CacheActive);
1465 LogMsg("Cache currently contains %lu records; %lu referenced by active questions", CacheUsed, CacheActive);
1466
1467 for (e = DNSServiceDomainEnumerationList; e; e=e->next)
1468 LogMsg("%5d: DomainEnumeration %##s", e->ClientMachPort, e->dom.qname.c);
1469
1470 for (b = DNSServiceBrowserList; b; b=b->next)
1471 LogMsg("%5d: ServiceBrowse %##s", b->ClientMachPort, b->q.qname.c);
1472
1473 for (l = DNSServiceResolverList; l; l=l->next)
1474 LogMsg("%5d: ServiceResolve %##s", l->ClientMachPort, l->i.name.c);
1475
1476 for (r = DNSServiceRegistrationList; r; r=r->next)
1477 LogMsg("%5d: ServiceRegistration %##s", r->ClientMachPort, r->s.RR_SRV.resrec.name.c);
1478
1479 udsserver_info();
1480
1481 LogMsg("%s ---- END STATE LOG ----", mDNSResponderVersionString);
1482 }
1483
1484 mDNSlocal void HandleSIGINFO(int signal)
1485 {
1486 (void)signal; // Unused
1487 mach_msg_header_t header;
1488 header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
1489 header.msgh_remote_port = info_m_port;
1490 header.msgh_local_port = MACH_PORT_NULL;
1491 header.msgh_size = sizeof(header);
1492 header.msgh_id = 0;
1493 if (mach_msg_send(&header) != MACH_MSG_SUCCESS)
1494 LogMsg("HandleSIGINFO: mach_msg_send failed; No state log will be generated.");
1495 }
1496
1497 mDNSlocal kern_return_t mDNSDaemonInitialize(void)
1498 {
1499 mStatus err;
1500 CFMachPortRef d_port = CFMachPortCreate(NULL, ClientDeathCallback, NULL, NULL);
1501 CFMachPortRef s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL);
1502 CFMachPortRef e_port = CFMachPortCreate(NULL, ExitCallback, NULL, NULL);
1503 CFMachPortRef i_port = CFMachPortCreate(NULL, INFOCallback, NULL, NULL);
1504 mach_port_t m_port = CFMachPortGetPort(s_port);
1505 char *MachServerName = mDNSMacOSXSystemBuildNumber(NULL) < 7 ? "DNSServiceDiscoveryServer" : "com.apple.mDNSResponder";
1506 kern_return_t status = bootstrap_register(bootstrap_port, MachServerName, m_port);
1507 CFRunLoopSourceRef d_rls = CFMachPortCreateRunLoopSource(NULL, d_port, 0);
1508 CFRunLoopSourceRef s_rls = CFMachPortCreateRunLoopSource(NULL, s_port, 0);
1509 CFRunLoopSourceRef e_rls = CFMachPortCreateRunLoopSource(NULL, e_port, 0);
1510 CFRunLoopSourceRef i_rls = CFMachPortCreateRunLoopSource(NULL, i_port, 0);
1511
1512 if (status)
1513 {
1514 if (status == 1103)
1515 LogMsg("Bootstrap_register failed(): A copy of the daemon is apparently already running");
1516 else
1517 LogMsg("Bootstrap_register failed(): %s %d", mach_error_string(status), status);
1518 return(status);
1519 }
1520
1521 err = mDNS_Init(&mDNSStorage, &PlatformStorage,
1522 rrcachestorage, RR_CACHE_SIZE,
1523 mDNS_Init_AdvertiseLocalAddresses,
1524 mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext);
1525 if (err) { LogMsg("Daemon start: mDNS_Init failed %ld", err); return(err); }
1526
1527 client_death_port = CFMachPortGetPort(d_port);
1528 exit_m_port = CFMachPortGetPort(e_port);
1529 info_m_port = CFMachPortGetPort(i_port);
1530
1531 CFRunLoopAddSource(CFRunLoopGetCurrent(), d_rls, kCFRunLoopDefaultMode);
1532 CFRunLoopAddSource(CFRunLoopGetCurrent(), s_rls, kCFRunLoopDefaultMode);
1533 CFRunLoopAddSource(CFRunLoopGetCurrent(), e_rls, kCFRunLoopDefaultMode);
1534 CFRunLoopAddSource(CFRunLoopGetCurrent(), i_rls, kCFRunLoopDefaultMode);
1535 CFRelease(d_rls);
1536 CFRelease(s_rls);
1537 CFRelease(e_rls);
1538 CFRelease(i_rls);
1539 if (debug_mode) printf("Service registered with Mach Port %d\n", m_port);
1540 #if ENABLE_UDS
1541 err = udsserver_init();
1542 if (err) { LogMsg("Daemon start: udsserver_init failed"); return err; }
1543 err = udsserver_add_rl_source();
1544 if (err) { LogMsg("Daemon start: udsserver_add_rl_source failed"); return err; }
1545 #endif
1546 return(err);
1547 }
1548
1549 mDNSlocal mDNSs32 mDNSDaemonIdle(void)
1550 {
1551 // 1. Call mDNS_Execute() to let mDNSCore do what it needs to do
1552 mDNSs32 nextevent = mDNS_Execute(&mDNSStorage);
1553
1554 mDNSs32 now = mDNSPlatformTimeNow();
1555
1556 // 2. Deliver any waiting browse messages to clients
1557 DNSServiceBrowser *b = DNSServiceBrowserList;
1558
1559 while (b)
1560 {
1561 // NOTE: Need to advance b to the next element BEFORE we call DeliverInstance(), because in the
1562 // event that the client Mach queue overflows, DeliverInstance() will call AbortBlockedClient()
1563 // and that will cause the DNSServiceBrowser object's memory to be freed before it returns
1564 DNSServiceBrowser *x = b;
1565 b = b->next;
1566 if (x->results) // Try to deliver the list of results
1567 {
1568 while (x->results)
1569 {
1570 DNSServiceBrowserResult *const r = x->results;
1571 domainlabel name;
1572 domainname type, domain;
1573 DeconstructServiceName(&r->result, &name, &type, &domain); // Don't need to check result; already validated in FoundInstance()
1574 char cname[MAX_DOMAIN_LABEL+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL.
1575 char ctype[MAX_ESCAPED_DOMAIN_NAME];
1576 char cdom [MAX_ESCAPED_DOMAIN_NAME];
1577 ConvertDomainLabelToCString_unescaped(&name, cname);
1578 ConvertDomainNameToCString(&type, ctype);
1579 ConvertDomainNameToCString(&domain, cdom);
1580 DNSServiceDiscoveryReplyFlags flags = (r->next) ? DNSServiceDiscoverReplyFlagsMoreComing : 0;
1581 kern_return_t status = DNSServiceBrowserReply_rpc(x->ClientMachPort, r->resultType, cname, ctype, cdom, flags, 1);
1582 // If we failed to send the mach message, try again in one second
1583 if (status == MACH_SEND_TIMED_OUT)
1584 {
1585 if (nextevent - now > mDNSPlatformOneSecond)
1586 nextevent = now + mDNSPlatformOneSecond;
1587 break;
1588 }
1589 else
1590 {
1591 x->lastsuccess = now;
1592 x->results = x->results->next;
1593 freeL("DNSServiceBrowserResult", r);
1594 }
1595 }
1596 // If this client hasn't read a single message in the last 60 seconds, abort it
1597 if (now - x->lastsuccess >= 60 * mDNSPlatformOneSecond)
1598 AbortBlockedClient(x->ClientMachPort, "browse", x);
1599 }
1600 }
1601
1602 DNSServiceResolver *l;
1603 for (l = DNSServiceResolverList; l; l=l->next)
1604 if (l->ReportTime && now - l->ReportTime >= 0)
1605 {
1606 l->ReportTime = 0;
1607 LogMsg("%5d: DNSServiceResolver(%##s) has remained active for over two minutes. "
1608 "This places considerable burden on the network.", l->ClientMachPort, l->i.name.c);
1609 }
1610
1611 return(nextevent);
1612 }
1613
1614 mDNSexport int main(int argc, char **argv)
1615 {
1616 int i;
1617 kern_return_t status;
1618 FILE *fp;
1619
1620 for (i=1; i<argc; i++)
1621 {
1622 if (!strcmp(argv[i], "-d")) debug_mode = 1;
1623 }
1624
1625 signal(SIGINT, HandleSIGTERM); // SIGINT is what you get for a Ctrl-C
1626 signal(SIGTERM, HandleSIGTERM);
1627 signal(SIGINFO, HandleSIGINFO);
1628
1629 // Register the server with mach_init for automatic restart only during debug mode
1630 if (!debug_mode)
1631 registerBootstrapService();
1632
1633 if (!debug_mode && !restarting_via_mach_init)
1634 exit(0); /* mach_init will restart us immediately as a daemon */
1635
1636 // Unlike deamon(), mach_init does redirect standard file descriptors to /dev/null
1637 if (!debug_mode)
1638 {
1639 int fd = open(_PATH_DEVNULL, O_RDWR, 0);
1640 if (fd != -1)
1641 {
1642 // Avoid to unnecessarily duplicate a file descriptor to itself
1643 if (fd != STDIN_FILENO) (void)dup2(fd, STDIN_FILENO);
1644 if (fd != STDOUT_FILENO) (void)dup2(fd, STDOUT_FILENO);
1645 if (fd != STDERR_FILENO) (void)dup2(fd, STDERR_FILENO);
1646 if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO)
1647 (void)close (fd);
1648 }
1649 }
1650
1651 fp = fopen(PID_FILE, "w");
1652 if (fp != NULL)
1653 {
1654 fprintf(fp, "%d\n", getpid());
1655 fclose(fp);
1656 }
1657
1658 LogMsg("%s starting", mDNSResponderVersionString);
1659 status = mDNSDaemonInitialize();
1660
1661 if (status == 0)
1662 {
1663 int numevents = 0;
1664 int RunLoopStatus = kCFRunLoopRunTimedOut;
1665
1666 // This is the main work loop:
1667 // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time
1668 // (2) Then we make sure we've delivered all waiting browse messages to our clients
1669 // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner
1670 // (4) On wakeup we first process *all* events
1671 // (5) then when no more events remain, we go back to (1) to finish off any deferred work and do it all again
1672 while (RunLoopStatus == kCFRunLoopRunTimedOut)
1673 {
1674 // 1. Before going into a blocking wait call and letting our process to go sleep,
1675 // call mDNSDaemonIdle to allow any deferred work to be completed.
1676 mDNSs32 nextevent = mDNSDaemonIdle();
1677 #if ENABLE_UDS
1678 nextevent = udsserver_idle(nextevent);
1679 #endif
1680
1681 // 2. Work out how long we expect to sleep before the next scheduled task
1682 mDNSs32 ticks = nextevent - mDNSPlatformTimeNow();
1683 if (ticks < 1) ticks = 1;
1684 CFAbsoluteTime interval = (CFAbsoluteTime)ticks / (CFAbsoluteTime)mDNSPlatformOneSecond;
1685
1686 // 3. Now do a blocking "CFRunLoopRunInMode" call so we sleep until
1687 // (a) our next wakeup time, or (b) an event occurs.
1688 // The 'true' parameter makes it return after handling any event that occurs
1689 // This gives us chance to regain control so we can call mDNS_Execute() before sleeping again
1690 verbosedebugf("main: Handled %d events; now sleeping for %d ticks", numevents, ticks);
1691 numevents = 0;
1692 RunLoopStatus = CFRunLoopRunInMode(kCFRunLoopDefaultMode, interval, true);
1693
1694 // 4. Time to do some work? Handle all remaining events as quickly as we can, before returning to mDNSDaemonIdle()
1695 while (RunLoopStatus == kCFRunLoopRunHandledSource)
1696 {
1697 numevents++;
1698 RunLoopStatus = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, true);
1699 }
1700 }
1701
1702 LogMsg("ERROR: CFRunLoopRun Exiting.");
1703 mDNS_Close(&mDNSStorage);
1704 }
1705
1706 destroyBootstrapService();
1707
1708 return(status);
1709 }
1710
1711 // For convenience when using the "strings" command, this is the last thing in the file
1712 mDNSexport const char mDNSResponderVersionString[] = STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";