]> git.saurik.com Git - apple/mdnsresponder.git/blob - daemon.c
c012ab16bd2cb3d15e0a69efec47a981bd769b06
[apple/mdnsresponder.git] / daemon.c
1 /*
2 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23 /*
24 * Formatting notes:
25 * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
26 * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
27 * but for the sake of brevity here I will say just this: Curly braces are not syntactially
28 * part of an "if" statement; they are the beginning and ending markers of a compound statement;
29 * therefore common sense dictates that if they are part of a compound statement then they
30 * should be indented to the same level as everything else in that compound statement.
31 * Indenting curly braces at the same level as the "if" implies that curly braces are
32 * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
33 * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
34 * understand why variable y is not of type "char*" just proves the point that poor code
35 * layout leads people to unfortunate misunderstandings about how the C language really works.)
36 */
37
38 #include <mach/mach.h>
39 #include <mach/mach_error.h>
40 #include <servers/bootstrap.h>
41 #include <sys/types.h>
42 #include <unistd.h>
43
44 #include "DNSServiceDiscoveryRequestServer.h"
45 #include "DNSServiceDiscoveryReply.h"
46
47 #include "mDNSClientAPI.h" // Defines the interface to the client layer above
48 #include "mDNSPlatformEnvironment.h" // Defines the specific types needed to run mDNS on this platform
49 #include "mDNSsprintf.h"
50 #include "mDNSvsprintf.h" // Used to implement LogErrorMessage();
51
52 #include <DNSServiceDiscovery/DNSServiceDiscovery.h>
53
54 //*************************************************************************************************************
55 // Globals
56
57 static mDNS mDNSStorage;
58 static mDNS_PlatformSupport PlatformStorage;
59 #define RR_CACHE_SIZE 500
60 static ResourceRecord rrcachestorage[RR_CACHE_SIZE];
61 static const char PID_FILE[] = "/var/run/mDNSResponder.pid";
62
63 static const char kmDNSBootstrapName[] = "com.apple.mDNSResponder";
64 static mach_port_t client_death_port = MACH_PORT_NULL;
65 static mach_port_t exit_m_port = MACH_PORT_NULL;
66 static mach_port_t server_priv_port = MACH_PORT_NULL;
67 static CFRunLoopTimerRef DeliverInstanceTimer;
68
69 // mDNS Mach Message Timeout, in milliseconds.
70 // We need this to be short enough that we don't deadlock the mDNSResponder if a client
71 // fails to service its mach message queue, but long enough to give a well-written
72 // client a chance to service its mach message queue without getting cut off.
73 // Empirically, 50ms seems to work, so we set the timeout to 250ms to give
74 // even extra-slow clients a fair chance before we cut them off.
75 #define MDNS_MM_TIMEOUT 250
76
77 static int restarting_via_mach_init = 0;
78
79 #if DEBUGBREAKS
80 static int debug_mode = 1;
81 #else
82 static int debug_mode = 0;
83 #endif
84
85 //*************************************************************************************************************
86 // Active client list structures
87
88 typedef struct DNSServiceDomainEnumeration_struct DNSServiceDomainEnumeration;
89 struct DNSServiceDomainEnumeration_struct
90 {
91 DNSServiceDomainEnumeration *next;
92 mach_port_t ClientMachPort;
93 DNSQuestion dom; // Question asking for domains
94 DNSQuestion def; // Question asking for default domain
95 };
96
97 typedef struct DNSServiceBrowser_struct DNSServiceBrowser;
98 struct DNSServiceBrowser_struct
99 {
100 DNSServiceBrowser *next;
101 mach_port_t ClientMachPort;
102 DNSQuestion q;
103 int resultType; // Set to -1 if no outstanding reply
104 char name[256], type[256], dom[256];
105 };
106
107 typedef struct DNSServiceResolver_struct DNSServiceResolver;
108 struct DNSServiceResolver_struct
109 {
110 DNSServiceResolver *next;
111 mach_port_t ClientMachPort;
112 ServiceInfoQuery q;
113 ServiceInfo i;
114 };
115
116 typedef struct DNSServiceRegistration_struct DNSServiceRegistration;
117 struct DNSServiceRegistration_struct
118 {
119 DNSServiceRegistration *next;
120 mach_port_t ClientMachPort;
121 mDNSBool autoname;
122 ServiceRecordSet s;
123 // Don't add any fields after ServiceRecordSet.
124 // This is where the implicit extra space goes if we allocate an oversized ServiceRecordSet object
125 };
126
127 static DNSServiceDomainEnumeration *DNSServiceDomainEnumerationList = NULL;
128 static DNSServiceBrowser *DNSServiceBrowserList = NULL;
129 static DNSServiceResolver *DNSServiceResolverList = NULL;
130 static DNSServiceRegistration *DNSServiceRegistrationList = NULL;
131
132 //*************************************************************************************************************
133 // General Utility Functions
134
135 void LogErrorMessage(const char *format, ...)
136 {
137 unsigned char buffer[512];
138 va_list ptr;
139 va_start(ptr,format);
140 buffer[mDNS_vsprintf((char *)buffer, format, ptr)] = 0;
141 va_end(ptr);
142 openlog("mDNSResponder", LOG_CONS | LOG_PERROR | LOG_PID, LOG_DAEMON);
143 fprintf(stderr, "%s\n", buffer);
144 syslog(LOG_ERR, "%s", buffer);
145 closelog();
146 fflush(stderr);
147 }
148
149 #if MACOSX_MDNS_MALLOC_DEBUGGING
150
151 char _malloc_options[] = "AXZ";
152
153 static void validatelists(mDNS *const m)
154 {
155 DNSServiceDomainEnumeration *e;
156 DNSServiceBrowser *b;
157 DNSServiceResolver *l;
158 DNSServiceRegistration *r;
159 ResourceRecord *rr;
160
161 for (e = DNSServiceDomainEnumerationList; e; e=e->next)
162 if (e->ClientMachPort == 0)
163 LogErrorMessage("!!!! DNSServiceDomainEnumerationList %X is garbage !!!!", e);
164
165 for (b = DNSServiceBrowserList; b; b=b->next)
166 if (b->ClientMachPort == 0)
167 LogErrorMessage("!!!! DNSServiceBrowserList %X is garbage !!!!", b);
168
169 for (l = DNSServiceResolverList; l; l=l->next)
170 if (l->ClientMachPort == 0)
171 LogErrorMessage("!!!! DNSServiceResolverList %X is garbage !!!!", l);
172
173 for (r = DNSServiceRegistrationList; r; r=r->next)
174 if (r->ClientMachPort == 0)
175 LogErrorMessage("!!!! DNSServiceRegistrationList %X is garbage !!!!", r);
176
177 for (rr = m->ResourceRecords; rr; rr=rr->next)
178 if (rr->RecordType == 0)
179 LogErrorMessage("!!!! ResourceRecords %X list is garbage !!!!");
180 }
181
182 void *mallocL(char *msg, unsigned int size)
183 {
184 unsigned long *mem = malloc(size+8);
185 if (!mem)
186 { LogErrorMessage("malloc(%s:%d) failed", msg, size); return(NULL); }
187 else
188 {
189 LogErrorMessage("malloc(%s:%d) = %X", msg, size, &mem[2]);
190 mem[0] = 0xDEADBEEF;
191 mem[1] = size;
192 bzero(&mem[2], size);
193 validatelists(&mDNSStorage);
194 return(&mem[2]);
195 }
196 }
197
198 void freeL(char *msg, void *x)
199 {
200 if (!x)
201 LogErrorMessage("free(%s@NULL)!", msg);
202 else
203 {
204 unsigned long *mem = ((unsigned long *)x) - 2;
205 if (mem[0] != 0xDEADBEEF)
206 { LogErrorMessage("free(%s@%X) !!!! NOT ALLOCATED !!!!", msg, &mem[2]); return; }
207 if (mem[1] > 8000)
208 { LogErrorMessage("free(%s:%d@%X) too big!", msg, mem[1], &mem[2]); return; }
209 LogErrorMessage("free(%s:%d@%X)", msg, mem[1], &mem[2]);
210 bzero(mem, mem[1]+8);
211 validatelists(&mDNSStorage);
212 free(mem);
213 }
214 }
215
216 #endif
217
218 //*************************************************************************************************************
219 // Client Death Detection
220
221 mDNSlocal void AbortClient(mach_port_t ClientMachPort)
222 {
223 DNSServiceDomainEnumeration **e = &DNSServiceDomainEnumerationList;
224 DNSServiceBrowser **b = &DNSServiceBrowserList;
225 DNSServiceResolver **l = &DNSServiceResolverList;
226 DNSServiceRegistration **r = &DNSServiceRegistrationList;
227
228 while (*e && (*e)->ClientMachPort != ClientMachPort) e = &(*e)->next;
229 if (*e)
230 {
231 DNSServiceDomainEnumeration *x = *e;
232 *e = (*e)->next;
233 debugf("Aborting DNSServiceDomainEnumeration %d", ClientMachPort);
234 mDNS_StopGetDomains(&mDNSStorage, &x->dom);
235 mDNS_StopGetDomains(&mDNSStorage, &x->def);
236 freeL("DNSServiceDomainEnumeration", x);
237 return;
238 }
239
240 while (*b && (*b)->ClientMachPort != ClientMachPort) b = &(*b)->next;
241 if (*b)
242 {
243 DNSServiceBrowser *x = *b;
244 *b = (*b)->next;
245 debugf("Aborting DNSServiceBrowser %d", ClientMachPort);
246 mDNS_StopBrowse(&mDNSStorage, &x->q);
247 freeL("DNSServiceBrowser", x);
248 return;
249 }
250
251 while (*l && (*l)->ClientMachPort != ClientMachPort) l = &(*l)->next;
252 if (*l)
253 {
254 DNSServiceResolver *x = *l;
255 *l = (*l)->next;
256 debugf("Aborting DNSServiceResolver %d", ClientMachPort);
257 mDNS_StopResolveService(&mDNSStorage, &x->q);
258 freeL("DNSServiceResolver", x);
259 return;
260 }
261
262 while (*r && (*r)->ClientMachPort != ClientMachPort) r = &(*r)->next;
263 if (*r)
264 {
265 DNSServiceRegistration *x = *r;
266 *r = (*r)->next;
267 mDNS_DeregisterService(&mDNSStorage, &x->s);
268 // Note that we don't do the "free(x);" here -- wait for the mStatus_MemFree message
269 return;
270 }
271 }
272
273 mDNSlocal void AbortBlockedClient(mach_port_t c, char *m)
274 {
275 DNSServiceDomainEnumeration **e = &DNSServiceDomainEnumerationList;
276 DNSServiceBrowser **b = &DNSServiceBrowserList;
277 DNSServiceResolver **l = &DNSServiceResolverList;
278 DNSServiceRegistration **r = &DNSServiceRegistrationList;
279 while (*e && (*e)->ClientMachPort != c) e = &(*e)->next;
280 while (*b && (*b)->ClientMachPort != c) b = &(*b)->next;
281 while (*l && (*l)->ClientMachPort != c) l = &(*l)->next;
282 while (*r && (*r)->ClientMachPort != c) r = &(*r)->next;
283 if (*e) LogErrorMessage("%5d: DomainEnumeration(%##s) stopped accepting Mach messages (%s)", c, &e[0]->dom.name, m);
284 else if (*b) LogErrorMessage("%5d: Browser(%##s) stopped accepting Mach messages (%s)", c, &b[0]->q.name, m);
285 else if (*l) LogErrorMessage("%5d: Resolver(%##s) stopped accepting Mach messages (%s)", c, &l[0]->i.name, m);
286 else if (*r) LogErrorMessage("%5d: Registration(%##s) stopped accepting Mach messages (%s)", c, &r[0]->s.RR_SRV.name, m);
287 else LogErrorMessage("%5d (%s) stopped accepting Mach messages, but no record of client can be found!", c, m);
288
289 AbortClient(c);
290 }
291
292 mDNSlocal void ClientDeathCallback(CFMachPortRef unusedport, void *voidmsg, CFIndex size, void *info)
293 {
294 mach_msg_header_t *msg = (mach_msg_header_t *)voidmsg;
295 if (msg->msgh_id == MACH_NOTIFY_DEAD_NAME)
296 {
297 const mach_dead_name_notification_t *const deathMessage = (mach_dead_name_notification_t *)msg;
298 AbortClient(deathMessage->not_port);
299
300 /* Deallocate the send right that came in the dead name notification */
301 mach_port_destroy( mach_task_self(), deathMessage->not_port );
302 }
303 }
304
305 mDNSlocal void EnableDeathNotificationForClient(mach_port_t ClientMachPort)
306 {
307 mach_port_t prev;
308 kern_return_t r = mach_port_request_notification(mach_task_self(), ClientMachPort, MACH_NOTIFY_DEAD_NAME, 0,
309 client_death_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev);
310 // If the port already died while we were thinking about it, then abort the operation right away
311 if (r != KERN_SUCCESS)
312 {
313 if (ClientMachPort != (mach_port_t)-1)
314 LogErrorMessage("Client %5d died before we could enable death notification", ClientMachPort);
315 AbortClient(ClientMachPort);
316 }
317 }
318
319 //*************************************************************************************************************
320 // Domain Enumeration
321
322 mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer)
323 {
324 kern_return_t status;
325 #pragma unused(m)
326 char buffer[256];
327 DNSServiceDomainEnumerationReplyResultType rt;
328 DNSServiceDomainEnumeration *x = (DNSServiceDomainEnumeration *)question->Context;
329
330 debugf("FoundDomain: %##s PTR %##s", answer->name.c, answer->rdata->u.name.c);
331 if (answer->rrtype != kDNSType_PTR) return;
332 if (!x) { debugf("FoundDomain: DNSServiceDomainEnumeration is NULL"); return; }
333
334 if (answer->rrremainingttl > 0)
335 {
336 if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyAddDomain;
337 else rt = DNSServiceDomainEnumerationReplyAddDomainDefault;
338 }
339 else
340 {
341 if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyRemoveDomain;
342 else return;
343 }
344
345 ConvertDomainNameToCString(&answer->rdata->u.name, buffer);
346 status = DNSServiceDomainEnumerationReply_rpc(x->ClientMachPort, rt, buffer, 0, MDNS_MM_TIMEOUT);
347 if (status == MACH_SEND_TIMED_OUT)
348 AbortBlockedClient(x->ClientMachPort, "enumeration");
349 }
350
351 mDNSexport kern_return_t provide_DNSServiceDomainEnumerationCreate_rpc(mach_port_t unusedserver, mach_port_t client,
352 int regDom)
353 {
354 kern_return_t status;
355 mStatus err;
356
357 mDNS_DomainType dt1 = regDom ? mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse;
358 mDNS_DomainType dt2 = regDom ? mDNS_DomainTypeRegistrationDefault : mDNS_DomainTypeBrowseDefault;
359 const DNSServiceDomainEnumerationReplyResultType rt = DNSServiceDomainEnumerationReplyAddDomainDefault;
360 DNSServiceDomainEnumeration *x = mallocL("DNSServiceDomainEnumeration", sizeof(*x));
361 if (!x) { debugf("provide_DNSServiceDomainEnumerationCreate_rpc: No memory!"); return(mStatus_NoMemoryErr); }
362 x->ClientMachPort = client;
363 x->next = DNSServiceDomainEnumerationList;
364 DNSServiceDomainEnumerationList = x;
365
366 debugf("Client %d: Enumerate %s Domains", client, regDom ? "Registration" : "Browsing");
367 // We always give local. as the initial default browse domain, and then look for more
368 status = DNSServiceDomainEnumerationReply_rpc(x->ClientMachPort, rt, "local.", 0, MDNS_MM_TIMEOUT);
369 if (status == MACH_SEND_TIMED_OUT)
370 {
371 AbortBlockedClient(x->ClientMachPort, "local enumeration");
372 return(mStatus_UnknownErr);
373 }
374
375 err = mDNS_GetDomains(&mDNSStorage, &x->dom, dt1, zeroIPAddr, FoundDomain, x);
376 if (!err) err = mDNS_GetDomains(&mDNSStorage, &x->def, dt2, zeroIPAddr, FoundDomain, x);
377
378 if (err) AbortClient(client);
379 else EnableDeathNotificationForClient(client);
380
381 if (err) debugf("provide_DNSServiceDomainEnumerationCreate_rpc: mDNS_GetDomains error %d", err);
382 return(err);
383 }
384
385 //*************************************************************************************************************
386 // Browse for services
387
388 mDNSlocal void DeliverInstance(DNSServiceBrowser *x, DNSServiceDiscoveryReplyFlags flags)
389 {
390 kern_return_t status;
391 debugf("DNSServiceBrowserReply_rpc sending reply for %s (%s)", x->name,
392 (flags & DNSServiceDiscoverReplyFlagsMoreComing) ? "more coming" : "last in batch");
393 status = DNSServiceBrowserReply_rpc(x->ClientMachPort, x->resultType, x->name, x->type, x->dom, flags, MDNS_MM_TIMEOUT);
394 x->resultType = -1;
395 if (status == MACH_SEND_TIMED_OUT)
396 AbortBlockedClient(x->ClientMachPort, "browse");
397 }
398
399 mDNSlocal void DeliverInstanceTimerCallBack(CFRunLoopTimerRef timer, void *info)
400 {
401 DNSServiceBrowser *b = DNSServiceBrowserList;
402 (void)timer; // Parameter not used
403
404 while (b)
405 {
406 // NOTE: Need to advance b to the next element BEFORE we call DeliverInstance(), because in the
407 // event that the client Mach queue overflows, DeliverInstance() will call AbortBlockedClient()
408 // and that will cause the DNSServiceBrowser object's memory to be freed before it returns
409 DNSServiceBrowser *x = b;
410 b = b->next;
411 if (x->resultType != -1)
412 DeliverInstance(x, 0);
413 }
414 }
415
416 mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer)
417 {
418 DNSServiceBrowser *x = (DNSServiceBrowser *)question->Context;
419 domainlabel name;
420 domainname type, domain;
421
422 if (answer->rrtype != kDNSType_PTR)
423 {
424 debugf("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer->rrtype);
425 return;
426 }
427
428 if (!DeconstructServiceName(&answer->rdata->u.name, &name, &type, &domain))
429 {
430 debugf("FoundInstance: %##s PTR %##s is not valid NIAS service pointer", &answer->name, &answer->rdata->u.name);
431 return;
432 }
433
434 if (x->resultType != -1) DeliverInstance(x, DNSServiceDiscoverReplyFlagsMoreComing);
435
436 debugf("FoundInstance: %##s", answer->rdata->u.name.c);
437 ConvertDomainLabelToCString_unescaped(&name, x->name);
438 ConvertDomainNameToCString(&type, x->type);
439 ConvertDomainNameToCString(&domain, x->dom);
440 if (answer->rrremainingttl)
441 x->resultType = DNSServiceBrowserReplyAddInstance;
442 else x->resultType = DNSServiceBrowserReplyRemoveInstance;
443
444 // We schedule this timer 1/10 second in the future because CFRunLoop doesn't respect
445 // the relative priority between CFSocket and CFRunLoopTimer, and continues to call
446 // the timer callback even though there are packets waiting to be processed.
447 CFRunLoopTimerSetNextFireDate(DeliverInstanceTimer, CFAbsoluteTimeGetCurrent() + 0.1);
448 }
449
450 mDNSexport kern_return_t provide_DNSServiceBrowserCreate_rpc(mach_port_t unusedserver, mach_port_t client,
451 DNSCString regtype, DNSCString domain)
452 {
453 mStatus err;
454 domainname t, d;
455 DNSServiceBrowser *x = mallocL("DNSServiceBrowser", sizeof(*x));
456 if (!x) { debugf("provide_DNSServiceBrowserCreate_rpc: No memory!"); return(mStatus_NoMemoryErr); }
457 x->ClientMachPort = client;
458 x->resultType = -1;
459 x->next = DNSServiceBrowserList;
460 DNSServiceBrowserList = x;
461
462 ConvertCStringToDomainName(regtype, &t);
463 ConvertCStringToDomainName(*domain ? domain : "local.", &d);
464
465 debugf("Client %d: provide_DNSServiceBrowserCreate_rpc", client);
466 debugf("Client %d: Browse for Services: %##s%##s", client, &t, &d);
467 err = mDNS_StartBrowse(&mDNSStorage, &x->q, &t, &d, zeroIPAddr, FoundInstance, x);
468
469 if (err) AbortClient(client);
470 else EnableDeathNotificationForClient(client);
471
472 if (err) debugf("provide_DNSServiceBrowserCreate_rpc: mDNS_StartBrowse error %d", err);
473 return(err);
474 }
475
476 //*************************************************************************************************************
477 // Resolve Service Info
478
479 mDNSlocal void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query)
480 {
481 kern_return_t status;
482 DNSServiceResolver *x = (DNSServiceResolver *)query->Context;
483 struct sockaddr_in interface;
484 struct sockaddr_in address;
485 char cstring[1024];
486 int i, pstrlen = query->info->TXTinfo[0];
487
488 //debugf("FoundInstanceInfo %.4a %.4a %##s", &query->info->InterfaceAddr, &query->info->ip, &query->info->name);
489
490 if (query->info->TXTlen > sizeof(cstring)) return;
491
492 bzero(&interface, sizeof(interface));
493 bzero(&address, sizeof(address));
494
495 interface.sin_len = sizeof(interface);
496 interface.sin_family = AF_INET;
497 interface.sin_port = 0;
498 interface.sin_addr.s_addr = query->info->InterfaceAddr.NotAnInteger;
499
500 address.sin_len = sizeof(address);
501 address.sin_family = AF_INET;
502 address.sin_port = query->info->port.NotAnInteger;
503 address.sin_addr.s_addr = query->info->ip.NotAnInteger;
504
505 // The OS X DNSServiceResolverResolve() API is defined using a C-string,
506 // but the mDNS_StartResolveService() call actually returns a packed block of P-strings.
507 // Hence we have to convert the P-string(s) to a C-string before returning the result to the client.
508 // ASCII-1 characters are used in the C-string as boundary markers,
509 // to indicate the boundaries between the original constituent P-strings.
510 for (i=1; i<query->info->TXTlen; i++)
511 {
512 if (--pstrlen >= 0)
513 cstring[i-1] = query->info->TXTinfo[i];
514 else
515 {
516 cstring[i-1] = 1;
517 pstrlen = query->info->TXTinfo[i];
518 }
519 }
520 cstring[i-1] = 0; // Put the terminating NULL on the end
521
522 status = DNSServiceResolverReply_rpc(x->ClientMachPort,
523 (char*)&interface, (char*)&address, cstring, 0, MDNS_MM_TIMEOUT);
524 if (status == MACH_SEND_TIMED_OUT)
525 AbortBlockedClient(x->ClientMachPort, "resolve");
526 }
527
528 mDNSexport kern_return_t provide_DNSServiceResolverResolve_rpc(mach_port_t unusedserver, mach_port_t client,
529 DNSCString name, DNSCString regtype, DNSCString domain)
530 {
531 mStatus err;
532 domainlabel n;
533 domainname t, d;
534 DNSServiceResolver *x = mallocL("DNSServiceResolver", sizeof(*x));
535 if (!x) { debugf("provide_DNSServiceResolverResolve_rpc: No memory!"); return(mStatus_NoMemoryErr); }
536 x->ClientMachPort = client;
537 x->next = DNSServiceResolverList;
538 DNSServiceResolverList = x;
539
540 ConvertCStringToDomainLabel(name, &n);
541 ConvertCStringToDomainName(regtype, &t);
542 ConvertCStringToDomainName(*domain ? domain : "local.", &d);
543 ConstructServiceName(&x->i.name, &n, &t, &d);
544 x->i.InterfaceAddr = zeroIPAddr;
545
546 debugf("Client %d: provide_DNSServiceResolverResolve_rpc", client);
547 debugf("Client %d: Resolve Service: %##s", client, &x->i.name);
548 err = mDNS_StartResolveService(&mDNSStorage, &x->q, &x->i, FoundInstanceInfo, x);
549
550 if (err) AbortClient(client);
551 else EnableDeathNotificationForClient(client);
552
553 if (err) debugf("provide_DNSServiceResolverResolve_rpc: mDNS_StartResolveService error %d", err);
554 return(err);
555 }
556
557 //*************************************************************************************************************
558 // Registration
559
560 mDNSlocal void FreeDNSServiceRegistration(DNSServiceRegistration *x)
561 {
562 while (x->s.Extras)
563 {
564 ExtraResourceRecord *extras = x->s.Extras;
565 x->s.Extras = x->s.Extras->next;
566 if (extras->r.rdata != &extras->r.rdatastorage)
567 freeL("Extra RData", extras->r.rdata);
568 freeL("ExtraResourceRecord", extras);
569 }
570
571 if (x->s.RR_TXT.rdata != &x->s.RR_TXT.rdatastorage)
572 freeL("TXT RData", x->s.RR_TXT.rdata);
573
574 freeL("DNSServiceRegistration", x);
575 }
576
577 mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result)
578 {
579 DNSServiceRegistration *x = (DNSServiceRegistration*)sr->Context;
580
581 switch (result)
582 {
583 case mStatus_NoError: debugf("RegCallback: %##s Name Registered", sr->RR_SRV.name.c); break;
584 case mStatus_NameConflict: debugf("RegCallback: %##s Name Conflict", sr->RR_SRV.name.c); break;
585 case mStatus_MemFree: debugf("RegCallback: %##s Memory Free", sr->RR_SRV.name.c); break;
586 default: debugf("RegCallback: %##s Unknown Result %d", sr->RR_SRV.name.c, result); break;
587 }
588
589 if (result == mStatus_NoError)
590 {
591 kern_return_t status = DNSServiceRegistrationReply_rpc(x->ClientMachPort, result, MDNS_MM_TIMEOUT);
592 if (status == MACH_SEND_TIMED_OUT)
593 AbortBlockedClient(x->ClientMachPort, "registration success");
594 }
595
596 if (result == mStatus_NameConflict)
597 {
598 // Note: By the time we get the mStatus_NameConflict message, the service is already deregistered
599 // and the memory is free, so we don't have to wait for an mStatus_MemFree message as well.
600 if (x->autoname)
601 mDNS_RenameAndReregisterService(m, sr);
602 else
603 {
604 kern_return_t status;
605 // AbortClient unlinks our DNSServiceRegistration from the list so we can safely free it
606 AbortClient(x->ClientMachPort);
607 status = DNSServiceRegistrationReply_rpc(x->ClientMachPort, result, MDNS_MM_TIMEOUT);
608 if (status == MACH_SEND_TIMED_OUT)
609 AbortBlockedClient(x->ClientMachPort, "registration conflict"); // Yes, this IS safe :-)
610 FreeDNSServiceRegistration(x);
611 }
612 }
613
614 if (result == mStatus_MemFree)
615 {
616 DNSServiceRegistration **r = &DNSServiceRegistrationList;
617 while (*r && *r != x) r = &(*r)->next;
618 if (*r)
619 {
620 debugf("RegCallback: %##s Still in DNSServiceRegistration list; removing now", sr->RR_SRV.name.c);
621 *r = (*r)->next;
622 }
623 debugf("RegCallback: Freeing DNSServiceRegistration %##s %d", sr->RR_SRV.name.c, x->ClientMachPort);
624 FreeDNSServiceRegistration(x);
625 }
626 }
627
628 mDNSlocal void CheckForDuplicateRegistrations(DNSServiceRegistration *x, domainlabel *n, domainname *t, domainname *d)
629 {
630 char name[256];
631 int count = 0;
632 ResourceRecord *rr;
633 domainname srvname;
634 ConstructServiceName(&srvname, n, t, d);
635 mDNS_sprintf(name, "%##s", &srvname);
636
637 for (rr = mDNSStorage.ResourceRecords; rr; rr=rr->next)
638 if (rr->rrtype == kDNSType_SRV && SameDomainName(&rr->name, &srvname))
639 count++;
640
641 if (count)
642 {
643 debugf("Client %5d registering Service Record Set \"%##s\"; WARNING! now have %d instances",
644 x->ClientMachPort, &srvname, count+1);
645 LogErrorMessage("%5d: WARNING! Bogus client application has now registered %d identical instances of service %##s",
646 x->ClientMachPort, count+1, &srvname);
647 }
648 }
649
650 mDNSexport kern_return_t provide_DNSServiceRegistrationCreate_rpc(mach_port_t unusedserver, mach_port_t client,
651 DNSCString name, DNSCString regtype, DNSCString domain, int notAnIntPort, DNSCString txtRecord)
652 {
653 mStatus err;
654 domainlabel n;
655 domainname t, d;
656 mDNSIPPort port;
657 unsigned char txtinfo[1024] = "";
658 int data_len = 0;
659 int size = sizeof(RDataBody);
660 unsigned char *pstring = &txtinfo[data_len];
661 char *ptr = txtRecord;
662 DNSServiceRegistration *x;
663
664 // The OS X DNSServiceRegistrationCreate() API is defined using a C-string,
665 // but the mDNS_RegisterService() call actually requires a packed block of P-strings.
666 // Hence we have to convert the C-string to a P-string.
667 // ASCII-1 characters are allowed in the C-string as boundary markers,
668 // so that a single C-string can be used to represent one or more P-strings.
669 while (*ptr)
670 {
671 if (++data_len >= sizeof(txtinfo)) return(mStatus_BadParamErr);
672 if (*ptr == 1) // If this is our boundary marker, start a new P-string
673 {
674 pstring = &txtinfo[data_len];
675 pstring[0] = 0;
676 ptr++;
677 }
678 else
679 {
680 if (pstring[0] == 255) return(mStatus_BadParamErr);
681 pstring[++pstring[0]] = *ptr++;
682 }
683 }
684
685 data_len++;
686 if (size < data_len)
687 size = data_len;
688
689 x = mallocL("DNSServiceRegistration", sizeof(*x) - sizeof(RDataBody) + size);
690 if (!x) { debugf("provide_DNSServiceRegistrationCreate_rpc: No memory!"); return(mStatus_NoMemoryErr); }
691 x->ClientMachPort = client;
692 x->next = DNSServiceRegistrationList;
693 DNSServiceRegistrationList = x;
694
695 x->autoname = (*name == 0);
696 if (x->autoname) n = mDNSStorage.nicelabel;
697 else ConvertCStringToDomainLabel(name, &n);
698 ConvertCStringToDomainName(regtype, &t);
699 ConvertCStringToDomainName(*domain ? domain : "local.", &d);
700 port.NotAnInteger = notAnIntPort;
701
702 debugf("Client %d: provide_DNSServiceRegistrationCreate_rpc", client);
703 debugf("Client %d: Register Service: %#s.%##s%##s %d %.30s",
704 client, &n, &t, &d, (int)port.b[0] << 8 | port.b[1], txtRecord);
705 CheckForDuplicateRegistrations(x, &n, &t, &d);
706 err = mDNS_RegisterService(&mDNSStorage, &x->s, &n, &t, &d, mDNSNULL, port, txtinfo, data_len, RegCallback, x);
707
708 if (err) AbortClient(client);
709 else EnableDeathNotificationForClient(client);
710
711 if (err) debugf("provide_DNSServiceRegistrationCreate_rpc: mDNS_RegisterService error %d", err);
712 else debugf("Made Service Record Set for %##s", &x->s.RR_SRV.name);
713
714 return(err);
715 }
716
717 mDNSexport kern_return_t provide_DNSServiceRegistrationAddRecord_rpc(mach_port_t unusedserver, mach_port_t client,
718 int type, const char *data, mach_msg_type_number_t data_len, uint32_t ttl, natural_t *reference)
719 {
720 mStatus err;
721 DNSServiceRegistration *x = DNSServiceRegistrationList;
722 ExtraResourceRecord *extra;
723 int size = sizeof(RDataBody);
724 if (size < data_len)
725 size = data_len;
726
727 // Find this registered service
728 while (x && x->ClientMachPort != client) x = x->next;
729 if (!x)
730 {
731 debugf("provide_DNSServiceRegistrationAddRecord_rpc bad client %X", client);
732 return(mStatus_BadReferenceErr);
733 }
734
735 // Allocate storage for our new record
736 extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size);
737 if (!extra) return(mStatus_NoMemoryErr);
738
739 // Fill in type, length, and data
740 extra->r.rrtype = type;
741 extra->r.rdatastorage.MaxRDLength = size;
742 extra->r.rdatastorage.RDLength = data_len;
743 memcpy(&extra->r.rdatastorage.u.data, data, data_len);
744
745 // And register it
746 err = mDNS_AddRecordToService(&mDNSStorage, &x->s, extra, &extra->r.rdatastorage, ttl);
747 *reference = (natural_t)extra;
748 debugf("Received a request to add the record of type: %d length: %d; returned reference %X",
749 type, data_len, *reference);
750 return(err);
751 }
752
753 mDNSlocal void UpdateCallback(mDNS *const m, ResourceRecord *const rr, RData *OldRData)
754 {
755 if (OldRData != &rr->rdatastorage)
756 freeL("Old RData", OldRData);
757 }
758
759 mDNSexport kern_return_t provide_DNSServiceRegistrationUpdateRecord_rpc(mach_port_t unusedserver, mach_port_t client,
760 natural_t reference, const char *data, mach_msg_type_number_t data_len, uint32_t ttl)
761 {
762 mStatus err;
763 DNSServiceRegistration *x = DNSServiceRegistrationList;
764 ResourceRecord *rr;
765 RData *newrdata;
766 int size = sizeof(RDataBody);
767 if (size < data_len)
768 size = data_len;
769
770 // Find this registered service
771 while (x && x->ClientMachPort != client) x = x->next;
772 if (!x)
773 {
774 debugf("provide_DNSServiceRegistrationUpdateRecord_rpc bad client %X", client);
775 return(mStatus_BadReferenceErr);
776 }
777
778 // Find the record we're updating
779 if (!reference) // NULL reference means update the primary TXT record
780 rr = &x->s.RR_TXT;
781 else // Else, scan our list to make sure we're updating a valid record that was previously added
782 {
783 ExtraResourceRecord *e = x->s.Extras;
784 while (e && e != (ExtraResourceRecord*)reference) e = e->next;
785 if (!e)
786 {
787 debugf("provide_DNSServiceRegistrationUpdateRecord_rpc failed to find record %X", reference);
788 return(mStatus_BadReferenceErr);
789 }
790 rr = &e->r;
791 }
792
793 // Allocate storage for our new data
794 newrdata = mallocL("RData", sizeof(*newrdata) - sizeof(RDataBody) + size);
795 if (!newrdata) return(mStatus_NoMemoryErr);
796
797 // Fill in new length, and data
798 newrdata->MaxRDLength = size;
799 newrdata->RDLength = data_len;
800 memcpy(&newrdata->u, data, data_len);
801
802 // And update our record
803 err = mDNS_Update(&mDNSStorage, rr, ttl, newrdata, UpdateCallback);
804 if (err)
805 {
806 debugf("Received a request to update the record of length: %d for reference: %X; failed %d",
807 data_len, reference, err);
808 return(err);
809 }
810
811 debugf("Received a request to update the record of length: %d for reference: %X", data_len, reference);
812 return(err);
813 }
814
815 mDNSexport kern_return_t provide_DNSServiceRegistrationRemoveRecord_rpc(mach_port_t unusedserver, mach_port_t client,
816 natural_t reference)
817 {
818 mStatus err;
819 DNSServiceRegistration *x = DNSServiceRegistrationList;
820 ExtraResourceRecord *extra = (ExtraResourceRecord*)reference;
821
822 // Find this registered service
823 while (x && x->ClientMachPort != client) x = x->next;
824 if (!x)
825 {
826 LogErrorMessage("DNSServiceRegistrationRemoveRecord Client %5d not found", client);
827 debugf("provide_DNSServiceRegistrationRemoveRecord_rpc bad client %X", client);
828 return(mStatus_BadReferenceErr);
829 }
830
831 err = mDNS_RemoveRecordFromService(&mDNSStorage, &x->s, extra);
832 if (err)
833 {
834 LogErrorMessage("DNSServiceRegistrationRemoveRecord Client %5d does not have record %X", client, extra);
835 debugf("Received a request to remove the record of reference: %X (failed %d)", extra, err);
836 return(err);
837 }
838
839 debugf("Received a request to remove the record of reference: %X", extra);
840 if (extra->r.rdata != &extra->r.rdatastorage)
841 freeL("Extra RData", extra->r.rdata);
842 freeL("ExtraResourceRecord", extra);
843 return(err);
844 }
845
846 //*************************************************************************************************************
847 // Support Code
848
849 mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
850 {
851 mig_reply_error_t *request = msg;
852 mig_reply_error_t *reply;
853 mach_msg_return_t mr;
854 int options;
855
856 /* allocate a reply buffer */
857 reply = CFAllocatorAllocate(NULL, provide_DNSServiceDiscoveryRequest_subsystem.maxsize, 0);
858
859 /* call the MiG server routine */
860 (void) DNSServiceDiscoveryRequest_server(&request->Head, &reply->Head);
861
862 if (!(reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && (reply->RetCode != KERN_SUCCESS))
863 {
864 if (reply->RetCode == MIG_NO_REPLY)
865 {
866 /*
867 * This return code is a little tricky -- it appears that the
868 * demux routine found an error of some sort, but since that
869 * error would not normally get returned either to the local
870 * user or the remote one, we pretend it's ok.
871 */
872 CFAllocatorDeallocate(NULL, reply);
873 return;
874 }
875
876 /*
877 * destroy any out-of-line data in the request buffer but don't destroy
878 * the reply port right (since we need that to send an error message).
879 */
880 request->Head.msgh_remote_port = MACH_PORT_NULL;
881 mach_msg_destroy(&request->Head);
882 }
883
884 if (reply->Head.msgh_remote_port == MACH_PORT_NULL)
885 {
886 /* no reply port, so destroy the reply */
887 if (reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
888 mach_msg_destroy(&reply->Head);
889 CFAllocatorDeallocate(NULL, reply);
890 return;
891 }
892
893 /*
894 * send reply.
895 *
896 * We don't want to block indefinitely because the client
897 * isn't receiving messages from the reply port.
898 * If we have a send-once right for the reply port, then
899 * this isn't a concern because the send won't block.
900 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
901 * To avoid falling off the kernel's fast RPC path unnecessarily,
902 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
903 */
904
905 options = MACH_SEND_MSG;
906 if (MACH_MSGH_BITS_REMOTE(reply->Head.msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE)
907 options |= MACH_SEND_TIMEOUT;
908
909 mr = mach_msg(&reply->Head, /* msg */
910 options, /* option */
911 reply->Head.msgh_size, /* send_size */
912 0, /* rcv_size */
913 MACH_PORT_NULL, /* rcv_name */
914 MACH_MSG_TIMEOUT_NONE, /* timeout */
915 MACH_PORT_NULL); /* notify */
916
917 /* Has a message error occurred? */
918 switch (mr)
919 {
920 case MACH_SEND_INVALID_DEST:
921 case MACH_SEND_TIMED_OUT:
922 /* the reply can't be delivered, so destroy it */
923 mach_msg_destroy(&reply->Head);
924 break;
925
926 default :
927 /* Includes success case. */
928 break;
929 }
930
931 CFAllocatorDeallocate(NULL, reply);
932 }
933
934 mDNSlocal kern_return_t registerBootstrapService()
935 {
936 kern_return_t status;
937 mach_port_t service_send_port, service_rcv_port;
938
939 debugf("Registering Bootstrap Service");
940
941 /*
942 * See if our service name is already registered and if we have privilege to check in.
943 */
944 status = bootstrap_check_in(bootstrap_port, (char*)kmDNSBootstrapName, &service_rcv_port);
945 if (status == KERN_SUCCESS)
946 {
947 /*
948 * If so, we must be a followup instance of an already defined server. In that case,
949 * the bootstrap port we inherited from our parent is the server's privilege port, so set
950 * that in case we have to unregister later (which requires the privilege port).
951 */
952 server_priv_port = bootstrap_port;
953 restarting_via_mach_init = TRUE;
954 }
955 else if (status == BOOTSTRAP_UNKNOWN_SERVICE)
956 {
957 status = bootstrap_create_server(bootstrap_port, "/usr/sbin/mDNSResponder", getuid(),
958 FALSE /* relaunch immediately, not on demand */, &server_priv_port);
959 if (status != KERN_SUCCESS) return status;
960
961 status = bootstrap_create_service(server_priv_port, (char*)kmDNSBootstrapName, &service_send_port);
962 if (status != KERN_SUCCESS)
963 {
964 mach_port_deallocate(mach_task_self(), server_priv_port);
965 return status;
966 }
967
968 status = bootstrap_check_in(server_priv_port, (char*)kmDNSBootstrapName, &service_rcv_port);
969 if (status != KERN_SUCCESS)
970 {
971 mach_port_deallocate(mach_task_self(), server_priv_port);
972 mach_port_deallocate(mach_task_self(), service_send_port);
973 return status;
974 }
975 assert(service_send_port == service_rcv_port);
976 }
977
978 /*
979 * We have no intention of responding to requests on the service port. We are not otherwise
980 * a Mach port-based service. We are just using this mechanism for relaunch facilities.
981 * So, we can dispose of all the rights we have for the service port. We don't destroy the
982 * send right for the server's privileged bootstrap port - in case we have to unregister later.
983 */
984 mach_port_destroy(mach_task_self(), service_rcv_port);
985 return status;
986 }
987
988 mDNSlocal kern_return_t destroyBootstrapService()
989 {
990 debugf("Destroying Bootstrap Service");
991 return bootstrap_register(server_priv_port, (char*)kmDNSBootstrapName, MACH_PORT_NULL);
992 }
993
994 mDNSlocal void ExitCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
995 {
996 debugf("ExitCallback: destroyBootstrapService");
997 if (!debug_mode)
998 destroyBootstrapService();
999
1000 debugf("ExitCallback: Aborting MIG clients");
1001 while (DNSServiceDomainEnumerationList) AbortClient(DNSServiceDomainEnumerationList->ClientMachPort);
1002 while (DNSServiceBrowserList) AbortClient(DNSServiceBrowserList->ClientMachPort);
1003 while (DNSServiceResolverList) AbortClient(DNSServiceResolverList->ClientMachPort);
1004 while (DNSServiceRegistrationList) AbortClient(DNSServiceRegistrationList->ClientMachPort);
1005
1006 debugf("ExitCallback: mDNS_Close");
1007 mDNS_Close(&mDNSStorage);
1008 exit(0);
1009 }
1010
1011 mDNSlocal kern_return_t start(const char *bundleName, const char *bundleDir)
1012 {
1013 mStatus err;
1014 CFRunLoopTimerContext myCFRunLoopTimerContext = { 0, &mDNSStorage, NULL, NULL, NULL };
1015 CFMachPortRef d_port = CFMachPortCreate(NULL, ClientDeathCallback, NULL, NULL);
1016 CFMachPortRef s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL);
1017 CFMachPortRef e_port = CFMachPortCreate(NULL, ExitCallback, NULL, NULL);
1018 mach_port_t m_port = CFMachPortGetPort(s_port);
1019 kern_return_t status = bootstrap_register(bootstrap_port, DNS_SERVICE_DISCOVERY_SERVER, m_port);
1020 CFRunLoopSourceRef d_rls = CFMachPortCreateRunLoopSource(NULL, d_port, 0);
1021 CFRunLoopSourceRef s_rls = CFMachPortCreateRunLoopSource(NULL, s_port, 0);
1022 CFRunLoopSourceRef e_rls = CFMachPortCreateRunLoopSource(NULL, e_port, 0);
1023
1024 if (status)
1025 {
1026 if (status == 1103)
1027 LogErrorMessage("Bootstrap_register failed(): A copy of the daemon is apparently already running");
1028 else
1029 LogErrorMessage("Bootstrap_register failed(): %s %d", mach_error_string(status), status);
1030 return(status);
1031 }
1032
1033 // Note: Every CFRunLoopTimer has to be created with an initial fire time, and a repeat interval, or it becomes
1034 // a one-shot timer and you can't use CFRunLoopTimerSetNextFireDate(timer, when) to schedule subsequent firings.
1035 // Here we create it with an initial fire time 24 hours from now, and a repeat interval of 24 hours, with
1036 // the intention that we'll actually reschedule it using CFRunLoopTimerSetNextFireDate(timer, when) as necessary.
1037 DeliverInstanceTimer = CFRunLoopTimerCreate(kCFAllocatorDefault,
1038 CFAbsoluteTimeGetCurrent() + 24.0*60.0*60.0, 24.0*60.0*60.0,
1039 0, // no flags
1040 9, // low priority execution (after all packets, etc., have been handled).
1041 DeliverInstanceTimerCallBack, &myCFRunLoopTimerContext);
1042 if (!DeliverInstanceTimer) return(-1);
1043 CFRunLoopAddTimer(CFRunLoopGetCurrent(), DeliverInstanceTimer, kCFRunLoopDefaultMode);
1044
1045 err = mDNS_Init(&mDNSStorage, &PlatformStorage, rrcachestorage, RR_CACHE_SIZE, NULL, NULL);
1046 if (err) { LogErrorMessage("Daemon start: mDNS_Init failed %ld", err); return(err); }
1047
1048 client_death_port = CFMachPortGetPort(d_port);
1049 exit_m_port = CFMachPortGetPort(e_port);
1050
1051 CFRunLoopAddSource(CFRunLoopGetCurrent(), d_rls, kCFRunLoopDefaultMode);
1052 CFRunLoopAddSource(CFRunLoopGetCurrent(), s_rls, kCFRunLoopDefaultMode);
1053 CFRunLoopAddSource(CFRunLoopGetCurrent(), e_rls, kCFRunLoopDefaultMode);
1054 CFRelease(d_rls);
1055 CFRelease(s_rls);
1056 CFRelease(e_rls);
1057 if (debug_mode) printf("Service registered with Mach Port %d\n", m_port);
1058
1059 return(err);
1060 }
1061
1062 mDNSlocal void HandleSIG(int signal)
1063 {
1064 debugf("");
1065 debugf("HandleSIG");
1066
1067 // Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit
1068 mach_msg_return_t msg_result;
1069 mach_msg_header_t header;
1070
1071 header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
1072 header.msgh_remote_port = exit_m_port;
1073 header.msgh_local_port = MACH_PORT_NULL;
1074 header.msgh_size = sizeof(header);
1075 header.msgh_id = 0;
1076
1077 msg_result = mach_msg_send(&header);
1078 }
1079
1080 mDNSexport int main(int argc, char **argv)
1081 {
1082 int i;
1083 kern_return_t status;
1084 FILE *fp;
1085
1086 for (i=1; i<argc; i++)
1087 {
1088 if (!strcmp(argv[i], "-d")) debug_mode = 1;
1089 }
1090
1091 signal(SIGINT, HandleSIG); // SIGINT is what you get for a Ctrl-C
1092 signal(SIGTERM, HandleSIG);
1093
1094 // Register the server with mach_init for automatic restart only during debug mode
1095 if (!debug_mode)
1096 registerBootstrapService();
1097
1098 if (!debug_mode && !restarting_via_mach_init)
1099 exit(0); /* mach_init will restart us immediately as a daemon */
1100
1101 fp = fopen(PID_FILE, "w");
1102 if (fp != NULL)
1103 {
1104 fprintf(fp, "%d\n", getpid());
1105 fclose(fp);
1106 }
1107
1108 LogErrorMessage("mDNSResponder (%s %s) starting", __DATE__, __TIME__);
1109 status = start(NULL, NULL);
1110
1111 if (status == 0)
1112 {
1113 CFRunLoopRun();
1114 LogErrorMessage("CFRunLoopRun Exiting. This is bad.");
1115 mDNS_Close(&mDNSStorage);
1116 }
1117
1118 destroyBootstrapService();
1119
1120 return(status);
1121 }