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