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