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