2 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
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
20 * @APPLE_LICENSE_HEADER_END@
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.)
38 #include <mach/mach.h>
39 #include <mach/mach_error.h>
40 #include <servers/bootstrap.h>
41 #include <sys/types.h>
44 #include "DNSServiceDiscoveryRequestServer.h"
45 #include "DNSServiceDiscoveryReply.h"
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();
52 #include <DNSServiceDiscovery/DNSServiceDiscovery.h>
54 //*************************************************************************************************************
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";
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
;
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
77 static int restarting_via_mach_init
= 0;
80 static int debug_mode
= 1;
82 static int debug_mode
= 0;
85 //*************************************************************************************************************
86 // Active client list structures
88 typedef struct DNSServiceDomainEnumeration_struct DNSServiceDomainEnumeration
;
89 struct DNSServiceDomainEnumeration_struct
91 DNSServiceDomainEnumeration
*next
;
92 mach_port_t ClientMachPort
;
93 DNSQuestion dom
; // Question asking for domains
94 DNSQuestion def
; // Question asking for default domain
97 typedef struct DNSServiceBrowser_struct DNSServiceBrowser
;
98 struct DNSServiceBrowser_struct
100 DNSServiceBrowser
*next
;
101 mach_port_t ClientMachPort
;
103 int resultType
; // Set to -1 if no outstanding reply
104 char name
[256], type
[256], dom
[256];
107 typedef struct DNSServiceResolver_struct DNSServiceResolver
;
108 struct DNSServiceResolver_struct
110 DNSServiceResolver
*next
;
111 mach_port_t ClientMachPort
;
116 typedef struct DNSServiceRegistration_struct DNSServiceRegistration
;
117 struct DNSServiceRegistration_struct
119 DNSServiceRegistration
*next
;
120 mach_port_t ClientMachPort
;
125 // Don't add any fields after ServiceRecordSet.
126 // This is where the implicit extra space goes if we allocate an oversized ServiceRecordSet object
129 static DNSServiceDomainEnumeration
*DNSServiceDomainEnumerationList
= NULL
;
130 static DNSServiceBrowser
*DNSServiceBrowserList
= NULL
;
131 static DNSServiceResolver
*DNSServiceResolverList
= NULL
;
132 static DNSServiceRegistration
*DNSServiceRegistrationList
= NULL
;
134 //*************************************************************************************************************
135 // General Utility Functions
137 void LogErrorMessage(const char *format
, ...)
139 unsigned char buffer
[512];
141 va_start(ptr
,format
);
142 buffer
[mDNS_vsprintf((char *)buffer
, format
, ptr
)] = 0;
144 openlog("mDNSResponder", LOG_CONS
| LOG_PERROR
| LOG_PID
, LOG_DAEMON
);
145 fprintf(stderr
, "%s\n", buffer
);
146 syslog(LOG_ERR
, "%s", buffer
);
151 #if MACOSX_MDNS_MALLOC_DEBUGGING
153 char _malloc_options
[] = "AXZ";
155 static void validatelists(mDNS
*const m
)
157 DNSServiceDomainEnumeration
*e
;
158 DNSServiceBrowser
*b
;
159 DNSServiceResolver
*l
;
160 DNSServiceRegistration
*r
;
163 for (e
= DNSServiceDomainEnumerationList
; e
; e
=e
->next
)
164 if (e
->ClientMachPort
== 0)
165 LogErrorMessage("!!!! DNSServiceDomainEnumerationList %X is garbage !!!!", e
);
167 for (b
= DNSServiceBrowserList
; b
; b
=b
->next
)
168 if (b
->ClientMachPort
== 0)
169 LogErrorMessage("!!!! DNSServiceBrowserList %X is garbage !!!!", b
);
171 for (l
= DNSServiceResolverList
; l
; l
=l
->next
)
172 if (l
->ClientMachPort
== 0)
173 LogErrorMessage("!!!! DNSServiceResolverList %X is garbage !!!!", l
);
175 for (r
= DNSServiceRegistrationList
; r
; r
=r
->next
)
176 if (r
->ClientMachPort
== 0)
177 LogErrorMessage("!!!! DNSServiceRegistrationList %X is garbage !!!!", r
);
179 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
180 if (rr
->RecordType
== 0)
181 LogErrorMessage("!!!! ResourceRecords %X list is garbage !!!!");
184 void *mallocL(char *msg
, unsigned int size
)
186 unsigned long *mem
= malloc(size
+8);
188 { LogErrorMessage("malloc(%s:%d) failed", msg
, size
); return(NULL
); }
191 LogErrorMessage("malloc(%s:%d) = %X", msg
, size
, &mem
[2]);
194 bzero(&mem
[2], size
);
195 validatelists(&mDNSStorage
);
200 void freeL(char *msg
, void *x
)
203 LogErrorMessage("free(%s@NULL)!", msg
);
206 unsigned long *mem
= ((unsigned long *)x
) - 2;
207 if (mem
[0] != 0xDEADBEEF)
208 { LogErrorMessage("free(%s@%X) !!!! NOT ALLOCATED !!!!", msg
, &mem
[2]); return; }
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
);
220 //*************************************************************************************************************
221 // Client Death Detection
223 mDNSlocal
void AbortClient(mach_port_t ClientMachPort
)
225 DNSServiceDomainEnumeration
**e
= &DNSServiceDomainEnumerationList
;
226 DNSServiceBrowser
**b
= &DNSServiceBrowserList
;
227 DNSServiceResolver
**l
= &DNSServiceResolverList
;
228 DNSServiceRegistration
**r
= &DNSServiceRegistrationList
;
230 while (*e
&& (*e
)->ClientMachPort
!= ClientMachPort
) e
= &(*e
)->next
;
233 DNSServiceDomainEnumeration
*x
= *e
;
235 debugf("Aborting DNSServiceDomainEnumeration %d", ClientMachPort
);
236 mDNS_StopGetDomains(&mDNSStorage
, &x
->dom
);
237 mDNS_StopGetDomains(&mDNSStorage
, &x
->def
);
238 freeL("DNSServiceDomainEnumeration", x
);
242 while (*b
&& (*b
)->ClientMachPort
!= ClientMachPort
) b
= &(*b
)->next
;
245 DNSServiceBrowser
*x
= *b
;
247 debugf("Aborting DNSServiceBrowser %d", ClientMachPort
);
248 mDNS_StopBrowse(&mDNSStorage
, &x
->q
);
249 freeL("DNSServiceBrowser", x
);
253 while (*l
&& (*l
)->ClientMachPort
!= ClientMachPort
) l
= &(*l
)->next
;
256 DNSServiceResolver
*x
= *l
;
258 debugf("Aborting DNSServiceResolver %d", ClientMachPort
);
259 mDNS_StopResolveService(&mDNSStorage
, &x
->q
);
260 freeL("DNSServiceResolver", x
);
264 while (*r
&& (*r
)->ClientMachPort
!= ClientMachPort
) r
= &(*r
)->next
;
267 DNSServiceRegistration
*x
= *r
;
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
276 mDNSlocal
void AbortBlockedClient(mach_port_t c
, char *m
)
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
);
295 mDNSlocal
void ClientDeathCallback(CFMachPortRef unusedport
, void *voidmsg
, CFIndex size
, void *info
)
297 mach_msg_header_t
*msg
= (mach_msg_header_t
*)voidmsg
;
298 if (msg
->msgh_id
== MACH_NOTIFY_DEAD_NAME
)
300 const mach_dead_name_notification_t
*const deathMessage
= (mach_dead_name_notification_t
*)msg
;
301 AbortClient(deathMessage
->not_port
);
303 /* Deallocate the send right that came in the dead name notification */
304 mach_port_destroy( mach_task_self(), deathMessage
->not_port
);
308 mDNSlocal
void EnableDeathNotificationForClient(mach_port_t ClientMachPort
)
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
)
316 if (ClientMachPort
!= (mach_port_t
)-1)
317 LogErrorMessage("Client %5d died before we could enable death notification", ClientMachPort
);
318 AbortClient(ClientMachPort
);
322 //*************************************************************************************************************
323 // Domain Enumeration
325 mDNSlocal
void FoundDomain(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
)
327 kern_return_t status
;
330 DNSServiceDomainEnumerationReplyResultType rt
;
331 DNSServiceDomainEnumeration
*x
= (DNSServiceDomainEnumeration
*)question
->Context
;
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; }
337 if (answer
->rrremainingttl
> 0)
339 if (question
== &x
->dom
) rt
= DNSServiceDomainEnumerationReplyAddDomain
;
340 else rt
= DNSServiceDomainEnumerationReplyAddDomainDefault
;
344 if (question
== &x
->dom
) rt
= DNSServiceDomainEnumerationReplyRemoveDomain
;
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");
354 mDNSexport kern_return_t
provide_DNSServiceDomainEnumerationCreate_rpc(mach_port_t unusedserver
, mach_port_t client
,
357 kern_return_t status
;
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
;
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
)
374 AbortBlockedClient(x
->ClientMachPort
, "local enumeration");
375 return(mStatus_UnknownErr
);
378 err
= mDNS_GetDomains(&mDNSStorage
, &x
->dom
, dt1
, zeroIPAddr
, FoundDomain
, x
);
379 if (!err
) err
= mDNS_GetDomains(&mDNSStorage
, &x
->def
, dt2
, zeroIPAddr
, FoundDomain
, x
);
381 if (err
) AbortClient(client
);
382 else EnableDeathNotificationForClient(client
);
384 if (err
) debugf("provide_DNSServiceDomainEnumerationCreate_rpc: mDNS_GetDomains error %d", err
);
388 //*************************************************************************************************************
389 // Browse for services
391 mDNSlocal
void DeliverInstance(DNSServiceBrowser
*x
, DNSServiceDiscoveryReplyFlags flags
)
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
);
398 if (status
== MACH_SEND_TIMED_OUT
)
399 AbortBlockedClient(x
->ClientMachPort
, "browse");
402 mDNSlocal
void DeliverInstanceTimerCallBack(CFRunLoopTimerRef timer
, void *info
)
404 DNSServiceBrowser
*b
= DNSServiceBrowserList
;
405 (void)timer
; // Parameter not used
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
;
414 if (x
->resultType
!= -1)
415 DeliverInstance(x
, 0);
419 mDNSlocal
void FoundInstance(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
)
421 DNSServiceBrowser
*x
= (DNSServiceBrowser
*)question
->Context
;
423 domainname type
, domain
;
425 if (answer
->rrtype
!= kDNSType_PTR
)
427 debugf("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer
->rrtype
);
431 if (!DeconstructServiceName(&answer
->rdata
->u
.name
, &name
, &type
, &domain
))
433 debugf("FoundInstance: %##s PTR %##s is not valid NIAS service pointer", &answer
->name
, &answer
->rdata
->u
.name
);
437 if (x
->resultType
!= -1) DeliverInstance(x
, DNSServiceDiscoverReplyFlagsMoreComing
);
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
;
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);
453 mDNSexport kern_return_t
provide_DNSServiceBrowserCreate_rpc(mach_port_t unusedserver
, mach_port_t client
,
454 DNSCString regtype
, DNSCString domain
)
458 DNSServiceBrowser
*x
= mallocL("DNSServiceBrowser", sizeof(*x
));
459 if (!x
) { debugf("provide_DNSServiceBrowserCreate_rpc: No memory!"); return(mStatus_NoMemoryErr
); }
460 x
->ClientMachPort
= client
;
462 x
->next
= DNSServiceBrowserList
;
463 DNSServiceBrowserList
= x
;
465 ConvertCStringToDomainName(regtype
, &t
);
466 ConvertCStringToDomainName(*domain
? domain
: "local.", &d
);
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
);
472 if (err
) AbortClient(client
);
473 else EnableDeathNotificationForClient(client
);
475 if (err
) debugf("provide_DNSServiceBrowserCreate_rpc: mDNS_StartBrowse error %d", err
);
479 //*************************************************************************************************************
480 // Resolve Service Info
482 mDNSlocal
void FoundInstanceInfo(mDNS
*const m
, ServiceInfoQuery
*query
)
484 kern_return_t status
;
485 DNSServiceResolver
*x
= (DNSServiceResolver
*)query
->Context
;
486 struct sockaddr_in interface
;
487 struct sockaddr_in address
;
489 int i
, pstrlen
= query
->info
->TXTinfo
[0];
491 //debugf("FoundInstanceInfo %.4a %.4a %##s", &query->info->InterfaceAddr, &query->info->ip, &query->info->name);
493 if (query
->info
->TXTlen
> sizeof(cstring
)) return;
495 bzero(&interface
, sizeof(interface
));
496 bzero(&address
, sizeof(address
));
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
;
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
;
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
++)
516 cstring
[i
-1] = query
->info
->TXTinfo
[i
];
520 pstrlen
= query
->info
->TXTinfo
[i
];
523 cstring
[i
-1] = 0; // Put the terminating NULL on the end
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");
531 mDNSexport kern_return_t
provide_DNSServiceResolverResolve_rpc(mach_port_t unusedserver
, mach_port_t client
,
532 DNSCString name
, DNSCString regtype
, DNSCString domain
)
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
;
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
;
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
);
553 if (err
) AbortClient(client
);
554 else EnableDeathNotificationForClient(client
);
556 if (err
) debugf("provide_DNSServiceResolverResolve_rpc: mDNS_StartResolveService error %d", err
);
560 //*************************************************************************************************************
563 mDNSlocal
void FreeDNSServiceRegistration(DNSServiceRegistration
*x
)
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
);
574 if (x
->s
.RR_TXT
.rdata
!= &x
->s
.RR_TXT
.rdatastorage
)
575 freeL("TXT RData", x
->s
.RR_TXT
.rdata
);
577 freeL("DNSServiceRegistration", x
);
580 mDNSlocal
void RegCallback(mDNS
*const m
, ServiceRecordSet
*const sr
, mStatus result
)
582 DNSServiceRegistration
*x
= (DNSServiceRegistration
*)sr
->Context
;
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;
592 if (result
== mStatus_NoError
)
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");
599 if (result
== mStatus_NameConflict
)
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.
604 mDNS_RenameAndReregisterService(m
, sr
, mDNSNULL
);
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
);
617 if (result
== mStatus_MemFree
)
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
);
628 DNSServiceRegistration
**r
= &DNSServiceRegistrationList
;
629 while (*r
&& *r
!= x
) r
= &(*r
)->next
;
632 debugf("RegCallback: %##s Still in DNSServiceRegistration list; removing now", sr
->RR_SRV
.name
.c
);
635 debugf("RegCallback: Freeing DNSServiceRegistration %##s %d", sr
->RR_SRV
.name
.c
, x
->ClientMachPort
);
636 FreeDNSServiceRegistration(x
);
641 mDNSlocal
void CheckForDuplicateRegistrations(DNSServiceRegistration
*x
, domainlabel
*n
, domainname
*t
, domainname
*d
)
647 ConstructServiceName(&srvname
, n
, t
, d
);
648 mDNS_sprintf(name
, "%##s", &srvname
);
650 for (rr
= mDNSStorage
.ResourceRecords
; rr
; rr
=rr
->next
)
651 if (rr
->rrtype
== kDNSType_SRV
&& SameDomainName(&rr
->name
, &srvname
))
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
);
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
)
669 unsigned char txtinfo
[1024] = "";
671 int size
= sizeof(RDataBody
);
672 unsigned char *pstring
= &txtinfo
[data_len
];
673 char *ptr
= txtRecord
;
674 DNSServiceRegistration
*x
;
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.
683 if (++data_len
>= sizeof(txtinfo
)) return(mStatus_BadParamErr
);
684 if (*ptr
== 1) // If this is our boundary marker, start a new P-string
686 pstring
= &txtinfo
[data_len
];
692 if (pstring
[0] == 255) return(mStatus_BadParamErr
);
693 pstring
[++pstring
[0]] = *ptr
++;
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
;
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
;
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
);
721 if (err
) AbortClient(client
);
722 else EnableDeathNotificationForClient(client
);
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
);
730 void NetworkChanged(void)
732 DNSServiceRegistration
*r
;
733 for (r
= DNSServiceRegistrationList
; r
; r
=r
->next
)
734 if (r
->autoname
&& !SameDomainLabel(r
->name
.c
, mDNSStorage
.nicelabel
.c
))
736 debugf("NetworkChanged renaming %#s to %#s", &r
->name
, &mDNSStorage
.nicelabel
);
737 r
->autorename
= mDNStrue
;
738 mDNS_DeregisterService(&mDNSStorage
, &r
->s
);
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
)
746 DNSServiceRegistration
*x
= DNSServiceRegistrationList
;
747 ExtraResourceRecord
*extra
;
748 int size
= sizeof(RDataBody
);
752 // Find this registered service
753 while (x
&& x
->ClientMachPort
!= client
) x
= x
->next
;
756 debugf("provide_DNSServiceRegistrationAddRecord_rpc bad client %X", client
);
757 return(mStatus_BadReferenceErr
);
760 // Allocate storage for our new record
761 extra
= mallocL("ExtraResourceRecord", sizeof(*extra
) - sizeof(RDataBody
) + size
);
762 if (!extra
) return(mStatus_NoMemoryErr
);
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
);
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
);
778 mDNSlocal
void UpdateCallback(mDNS
*const m
, ResourceRecord
*const rr
, RData
*OldRData
)
780 if (OldRData
!= &rr
->rdatastorage
)
781 freeL("Old RData", OldRData
);
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
)
788 DNSServiceRegistration
*x
= DNSServiceRegistrationList
;
791 int size
= sizeof(RDataBody
);
795 // Find this registered service
796 while (x
&& x
->ClientMachPort
!= client
) x
= x
->next
;
799 debugf("provide_DNSServiceRegistrationUpdateRecord_rpc bad client %X", client
);
800 return(mStatus_BadReferenceErr
);
803 // Find the record we're updating
804 if (!reference
) // NULL reference means update the primary TXT record
806 else // Else, scan our list to make sure we're updating a valid record that was previously added
808 ExtraResourceRecord
*e
= x
->s
.Extras
;
809 while (e
&& e
!= (ExtraResourceRecord
*)reference
) e
= e
->next
;
812 debugf("provide_DNSServiceRegistrationUpdateRecord_rpc failed to find record %X", reference
);
813 return(mStatus_BadReferenceErr
);
818 // Allocate storage for our new data
819 newrdata
= mallocL("RData", sizeof(*newrdata
) - sizeof(RDataBody
) + size
);
820 if (!newrdata
) return(mStatus_NoMemoryErr
);
822 // Fill in new length, and data
823 newrdata
->MaxRDLength
= size
;
824 newrdata
->RDLength
= data_len
;
825 memcpy(&newrdata
->u
, data
, data_len
);
827 // And update our record
828 err
= mDNS_Update(&mDNSStorage
, rr
, ttl
, newrdata
, UpdateCallback
);
831 debugf("Received a request to update the record of length: %d for reference: %X; failed %d",
832 data_len
, reference
, err
);
836 debugf("Received a request to update the record of length: %d for reference: %X", data_len
, reference
);
840 mDNSexport kern_return_t
provide_DNSServiceRegistrationRemoveRecord_rpc(mach_port_t unusedserver
, mach_port_t client
,
844 DNSServiceRegistration
*x
= DNSServiceRegistrationList
;
845 ExtraResourceRecord
*extra
= (ExtraResourceRecord
*)reference
;
847 // Find this registered service
848 while (x
&& x
->ClientMachPort
!= client
) x
= x
->next
;
851 LogErrorMessage("DNSServiceRegistrationRemoveRecord Client %5d not found", client
);
852 debugf("provide_DNSServiceRegistrationRemoveRecord_rpc bad client %X", client
);
853 return(mStatus_BadReferenceErr
);
856 err
= mDNS_RemoveRecordFromService(&mDNSStorage
, &x
->s
, extra
);
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
);
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
);
871 //*************************************************************************************************************
874 mDNSlocal
void DNSserverCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
876 mig_reply_error_t
*request
= msg
;
877 mig_reply_error_t
*reply
;
878 mach_msg_return_t mr
;
881 /* allocate a reply buffer */
882 reply
= CFAllocatorAllocate(NULL
, provide_DNSServiceDiscoveryRequest_subsystem
.maxsize
, 0);
884 /* call the MiG server routine */
885 (void) DNSServiceDiscoveryRequest_server(&request
->Head
, &reply
->Head
);
887 if (!(reply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) && (reply
->RetCode
!= KERN_SUCCESS
))
889 if (reply
->RetCode
== MIG_NO_REPLY
)
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.
897 CFAllocatorDeallocate(NULL
, reply
);
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).
905 request
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
906 mach_msg_destroy(&request
->Head
);
909 if (reply
->Head
.msgh_remote_port
== MACH_PORT_NULL
)
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
);
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.
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
;
934 mr
= mach_msg(&reply
->Head
, /* msg */
935 options
, /* option */
936 reply
->Head
.msgh_size
, /* send_size */
938 MACH_PORT_NULL
, /* rcv_name */
939 MACH_MSG_TIMEOUT_NONE
, /* timeout */
940 MACH_PORT_NULL
); /* notify */
942 /* Has a message error occurred? */
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
);
952 /* Includes success case. */
956 CFAllocatorDeallocate(NULL
, reply
);
959 mDNSlocal kern_return_t
registerBootstrapService()
961 kern_return_t status
;
962 mach_port_t service_send_port
, service_rcv_port
;
964 debugf("Registering Bootstrap Service");
967 * See if our service name is already registered and if we have privilege to check in.
969 status
= bootstrap_check_in(bootstrap_port
, (char*)kmDNSBootstrapName
, &service_rcv_port
);
970 if (status
== KERN_SUCCESS
)
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).
977 server_priv_port
= bootstrap_port
;
978 restarting_via_mach_init
= TRUE
;
980 else if (status
== BOOTSTRAP_UNKNOWN_SERVICE
)
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
;
986 status
= bootstrap_create_service(server_priv_port
, (char*)kmDNSBootstrapName
, &service_send_port
);
987 if (status
!= KERN_SUCCESS
)
989 mach_port_deallocate(mach_task_self(), server_priv_port
);
993 status
= bootstrap_check_in(server_priv_port
, (char*)kmDNSBootstrapName
, &service_rcv_port
);
994 if (status
!= KERN_SUCCESS
)
996 mach_port_deallocate(mach_task_self(), server_priv_port
);
997 mach_port_deallocate(mach_task_self(), service_send_port
);
1000 assert(service_send_port
== service_rcv_port
);
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.
1009 mach_port_destroy(mach_task_self(), service_rcv_port
);
1013 mDNSlocal kern_return_t
destroyBootstrapService()
1015 debugf("Destroying Bootstrap Service");
1016 return bootstrap_register(server_priv_port
, (char*)kmDNSBootstrapName
, MACH_PORT_NULL
);
1019 mDNSlocal
void ExitCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1021 debugf("ExitCallback: destroyBootstrapService");
1023 destroyBootstrapService();
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
);
1031 debugf("ExitCallback: mDNS_Close");
1032 mDNS_Close(&mDNSStorage
);
1036 mDNSlocal kern_return_t
start(const char *bundleName
, const char *bundleDir
)
1038 extern void (*NotifyClientNetworkChanged
)(void); // Temp fix for catching name changes
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);
1053 LogErrorMessage("Bootstrap_register failed(): A copy of the daemon is apparently already running");
1055 LogErrorMessage("Bootstrap_register failed(): %s %d", mach_error_string(status
), status
);
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,
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
);
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
); }
1074 client_death_port
= CFMachPortGetPort(d_port
);
1075 exit_m_port
= CFMachPortGetPort(e_port
);
1077 CFRunLoopAddSource(CFRunLoopGetCurrent(), d_rls
, kCFRunLoopDefaultMode
);
1078 CFRunLoopAddSource(CFRunLoopGetCurrent(), s_rls
, kCFRunLoopDefaultMode
);
1079 CFRunLoopAddSource(CFRunLoopGetCurrent(), e_rls
, kCFRunLoopDefaultMode
);
1083 if (debug_mode
) printf("Service registered with Mach Port %d\n", m_port
);
1085 NotifyClientNetworkChanged
= NetworkChanged
;
1090 mDNSlocal
void HandleSIG(int signal
)
1093 debugf("HandleSIG");
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
;
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
);
1105 msg_result
= mach_msg_send(&header
);
1108 mDNSexport
int main(int argc
, char **argv
)
1111 kern_return_t status
;
1114 for (i
=1; i
<argc
; i
++)
1116 if (!strcmp(argv
[i
], "-d")) debug_mode
= 1;
1119 signal(SIGINT
, HandleSIG
); // SIGINT is what you get for a Ctrl-C
1120 signal(SIGTERM
, HandleSIG
);
1122 // Register the server with mach_init for automatic restart only during debug mode
1124 registerBootstrapService();
1126 if (!debug_mode
&& !restarting_via_mach_init
)
1127 exit(0); /* mach_init will restart us immediately as a daemon */
1129 fp
= fopen(PID_FILE
, "w");
1132 fprintf(fp
, "%d\n", getpid());
1136 LogErrorMessage("mDNSResponder (%s %s) starting", __DATE__
, __TIME__
);
1137 status
= start(NULL
, NULL
);
1142 LogErrorMessage("CFRunLoopRun Exiting. This is bad.");
1143 mDNS_Close(&mDNSStorage
);
1146 destroyBootstrapService();