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