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
;
123 // Don't add any fields after ServiceRecordSet.
124 // This is where the implicit extra space goes if we allocate an oversized ServiceRecordSet object
127 static DNSServiceDomainEnumeration
*DNSServiceDomainEnumerationList
= NULL
;
128 static DNSServiceBrowser
*DNSServiceBrowserList
= NULL
;
129 static DNSServiceResolver
*DNSServiceResolverList
= NULL
;
130 static DNSServiceRegistration
*DNSServiceRegistrationList
= NULL
;
132 //*************************************************************************************************************
133 // General Utility Functions
135 void LogErrorMessage(const char *format
, ...)
137 unsigned char buffer
[512];
139 va_start(ptr
,format
);
140 buffer
[mDNS_vsprintf((char *)buffer
, format
, ptr
)] = 0;
142 openlog("mDNSResponder", LOG_CONS
| LOG_PERROR
| LOG_PID
, LOG_DAEMON
);
143 fprintf(stderr
, "%s\n", buffer
);
144 syslog(LOG_ERR
, "%s", buffer
);
149 #if MACOSX_MDNS_MALLOC_DEBUGGING
151 char _malloc_options
[] = "AXZ";
153 static void validatelists(mDNS
*const m
)
155 DNSServiceDomainEnumeration
*e
;
156 DNSServiceBrowser
*b
;
157 DNSServiceResolver
*l
;
158 DNSServiceRegistration
*r
;
161 for (e
= DNSServiceDomainEnumerationList
; e
; e
=e
->next
)
162 if (e
->ClientMachPort
== 0)
163 LogErrorMessage("!!!! DNSServiceDomainEnumerationList %X is garbage !!!!", e
);
165 for (b
= DNSServiceBrowserList
; b
; b
=b
->next
)
166 if (b
->ClientMachPort
== 0)
167 LogErrorMessage("!!!! DNSServiceBrowserList %X is garbage !!!!", b
);
169 for (l
= DNSServiceResolverList
; l
; l
=l
->next
)
170 if (l
->ClientMachPort
== 0)
171 LogErrorMessage("!!!! DNSServiceResolverList %X is garbage !!!!", l
);
173 for (r
= DNSServiceRegistrationList
; r
; r
=r
->next
)
174 if (r
->ClientMachPort
== 0)
175 LogErrorMessage("!!!! DNSServiceRegistrationList %X is garbage !!!!", r
);
177 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
178 if (rr
->RecordType
== 0)
179 LogErrorMessage("!!!! ResourceRecords %X list is garbage !!!!");
182 void *mallocL(char *msg
, unsigned int size
)
184 unsigned long *mem
= malloc(size
+8);
186 { LogErrorMessage("malloc(%s:%d) failed", msg
, size
); return(NULL
); }
189 LogErrorMessage("malloc(%s:%d) = %X", msg
, size
, &mem
[2]);
192 bzero(&mem
[2], size
);
193 validatelists(&mDNSStorage
);
198 void freeL(char *msg
, void *x
)
201 LogErrorMessage("free(%s@NULL)!", msg
);
204 unsigned long *mem
= ((unsigned long *)x
) - 2;
205 if (mem
[0] != 0xDEADBEEF)
206 { LogErrorMessage("free(%s@%X) !!!! NOT ALLOCATED !!!!", msg
, &mem
[2]); return; }
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
);
218 //*************************************************************************************************************
219 // Client Death Detection
221 mDNSlocal
void AbortClient(mach_port_t ClientMachPort
)
223 DNSServiceDomainEnumeration
**e
= &DNSServiceDomainEnumerationList
;
224 DNSServiceBrowser
**b
= &DNSServiceBrowserList
;
225 DNSServiceResolver
**l
= &DNSServiceResolverList
;
226 DNSServiceRegistration
**r
= &DNSServiceRegistrationList
;
228 while (*e
&& (*e
)->ClientMachPort
!= ClientMachPort
) e
= &(*e
)->next
;
231 DNSServiceDomainEnumeration
*x
= *e
;
233 debugf("Aborting DNSServiceDomainEnumeration %d", ClientMachPort
);
234 mDNS_StopGetDomains(&mDNSStorage
, &x
->dom
);
235 mDNS_StopGetDomains(&mDNSStorage
, &x
->def
);
236 freeL("DNSServiceDomainEnumeration", x
);
240 while (*b
&& (*b
)->ClientMachPort
!= ClientMachPort
) b
= &(*b
)->next
;
243 DNSServiceBrowser
*x
= *b
;
245 debugf("Aborting DNSServiceBrowser %d", ClientMachPort
);
246 mDNS_StopBrowse(&mDNSStorage
, &x
->q
);
247 freeL("DNSServiceBrowser", x
);
251 while (*l
&& (*l
)->ClientMachPort
!= ClientMachPort
) l
= &(*l
)->next
;
254 DNSServiceResolver
*x
= *l
;
256 debugf("Aborting DNSServiceResolver %d", ClientMachPort
);
257 mDNS_StopResolveService(&mDNSStorage
, &x
->q
);
258 freeL("DNSServiceResolver", x
);
262 while (*r
&& (*r
)->ClientMachPort
!= ClientMachPort
) r
= &(*r
)->next
;
265 DNSServiceRegistration
*x
= *r
;
267 mDNS_DeregisterService(&mDNSStorage
, &x
->s
);
268 // Note that we don't do the "free(x);" here -- wait for the mStatus_MemFree message
273 mDNSlocal
void AbortBlockedClient(mach_port_t c
, char *m
)
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
);
292 mDNSlocal
void ClientDeathCallback(CFMachPortRef unusedport
, void *voidmsg
, CFIndex size
, void *info
)
294 mach_msg_header_t
*msg
= (mach_msg_header_t
*)voidmsg
;
295 if (msg
->msgh_id
== MACH_NOTIFY_DEAD_NAME
)
297 const mach_dead_name_notification_t
*const deathMessage
= (mach_dead_name_notification_t
*)msg
;
298 AbortClient(deathMessage
->not_port
);
300 /* Deallocate the send right that came in the dead name notification */
301 mach_port_destroy( mach_task_self(), deathMessage
->not_port
);
305 mDNSlocal
void EnableDeathNotificationForClient(mach_port_t ClientMachPort
)
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
)
313 if (ClientMachPort
!= (mach_port_t
)-1)
314 LogErrorMessage("Client %5d died before we could enable death notification", ClientMachPort
);
315 AbortClient(ClientMachPort
);
319 //*************************************************************************************************************
320 // Domain Enumeration
322 mDNSlocal
void FoundDomain(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
)
324 kern_return_t status
;
327 DNSServiceDomainEnumerationReplyResultType rt
;
328 DNSServiceDomainEnumeration
*x
= (DNSServiceDomainEnumeration
*)question
->Context
;
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; }
334 if (answer
->rrremainingttl
> 0)
336 if (question
== &x
->dom
) rt
= DNSServiceDomainEnumerationReplyAddDomain
;
337 else rt
= DNSServiceDomainEnumerationReplyAddDomainDefault
;
341 if (question
== &x
->dom
) rt
= DNSServiceDomainEnumerationReplyRemoveDomain
;
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");
351 mDNSexport kern_return_t
provide_DNSServiceDomainEnumerationCreate_rpc(mach_port_t unusedserver
, mach_port_t client
,
354 kern_return_t status
;
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
;
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
)
371 AbortBlockedClient(x
->ClientMachPort
, "local enumeration");
372 return(mStatus_UnknownErr
);
375 err
= mDNS_GetDomains(&mDNSStorage
, &x
->dom
, dt1
, zeroIPAddr
, FoundDomain
, x
);
376 if (!err
) err
= mDNS_GetDomains(&mDNSStorage
, &x
->def
, dt2
, zeroIPAddr
, FoundDomain
, x
);
378 if (err
) AbortClient(client
);
379 else EnableDeathNotificationForClient(client
);
381 if (err
) debugf("provide_DNSServiceDomainEnumerationCreate_rpc: mDNS_GetDomains error %d", err
);
385 //*************************************************************************************************************
386 // Browse for services
388 mDNSlocal
void DeliverInstance(DNSServiceBrowser
*x
, DNSServiceDiscoveryReplyFlags flags
)
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
);
395 if (status
== MACH_SEND_TIMED_OUT
)
396 AbortBlockedClient(x
->ClientMachPort
, "browse");
399 mDNSlocal
void DeliverInstanceTimerCallBack(CFRunLoopTimerRef timer
, void *info
)
401 DNSServiceBrowser
*b
= DNSServiceBrowserList
;
402 (void)timer
; // Parameter not used
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
;
411 if (x
->resultType
!= -1)
412 DeliverInstance(x
, 0);
416 mDNSlocal
void FoundInstance(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
)
418 DNSServiceBrowser
*x
= (DNSServiceBrowser
*)question
->Context
;
420 domainname type
, domain
;
422 if (answer
->rrtype
!= kDNSType_PTR
)
424 debugf("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer
->rrtype
);
428 if (!DeconstructServiceName(&answer
->rdata
->u
.name
, &name
, &type
, &domain
))
430 debugf("FoundInstance: %##s PTR %##s is not valid NIAS service pointer", &answer
->name
, &answer
->rdata
->u
.name
);
434 if (x
->resultType
!= -1) DeliverInstance(x
, DNSServiceDiscoverReplyFlagsMoreComing
);
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
;
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);
450 mDNSexport kern_return_t
provide_DNSServiceBrowserCreate_rpc(mach_port_t unusedserver
, mach_port_t client
,
451 DNSCString regtype
, DNSCString domain
)
455 DNSServiceBrowser
*x
= mallocL("DNSServiceBrowser", sizeof(*x
));
456 if (!x
) { debugf("provide_DNSServiceBrowserCreate_rpc: No memory!"); return(mStatus_NoMemoryErr
); }
457 x
->ClientMachPort
= client
;
459 x
->next
= DNSServiceBrowserList
;
460 DNSServiceBrowserList
= x
;
462 ConvertCStringToDomainName(regtype
, &t
);
463 ConvertCStringToDomainName(*domain
? domain
: "local.", &d
);
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
);
469 if (err
) AbortClient(client
);
470 else EnableDeathNotificationForClient(client
);
472 if (err
) debugf("provide_DNSServiceBrowserCreate_rpc: mDNS_StartBrowse error %d", err
);
476 //*************************************************************************************************************
477 // Resolve Service Info
479 mDNSlocal
void FoundInstanceInfo(mDNS
*const m
, ServiceInfoQuery
*query
)
481 kern_return_t status
;
482 DNSServiceResolver
*x
= (DNSServiceResolver
*)query
->Context
;
483 struct sockaddr_in interface
;
484 struct sockaddr_in address
;
486 int i
, pstrlen
= query
->info
->TXTinfo
[0];
488 //debugf("FoundInstanceInfo %.4a %.4a %##s", &query->info->InterfaceAddr, &query->info->ip, &query->info->name);
490 if (query
->info
->TXTlen
> sizeof(cstring
)) return;
492 bzero(&interface
, sizeof(interface
));
493 bzero(&address
, sizeof(address
));
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
;
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
;
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
++)
513 cstring
[i
-1] = query
->info
->TXTinfo
[i
];
517 pstrlen
= query
->info
->TXTinfo
[i
];
520 cstring
[i
-1] = 0; // Put the terminating NULL on the end
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");
528 mDNSexport kern_return_t
provide_DNSServiceResolverResolve_rpc(mach_port_t unusedserver
, mach_port_t client
,
529 DNSCString name
, DNSCString regtype
, DNSCString domain
)
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
;
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
;
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
);
550 if (err
) AbortClient(client
);
551 else EnableDeathNotificationForClient(client
);
553 if (err
) debugf("provide_DNSServiceResolverResolve_rpc: mDNS_StartResolveService error %d", err
);
557 //*************************************************************************************************************
560 mDNSlocal
void FreeDNSServiceRegistration(DNSServiceRegistration
*x
)
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
);
571 if (x
->s
.RR_TXT
.rdata
!= &x
->s
.RR_TXT
.rdatastorage
)
572 freeL("TXT RData", x
->s
.RR_TXT
.rdata
);
574 freeL("DNSServiceRegistration", x
);
577 mDNSlocal
void RegCallback(mDNS
*const m
, ServiceRecordSet
*const sr
, mStatus result
)
579 DNSServiceRegistration
*x
= (DNSServiceRegistration
*)sr
->Context
;
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;
589 if (result
== mStatus_NoError
)
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");
596 if (result
== mStatus_NameConflict
)
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.
601 mDNS_RenameAndReregisterService(m
, sr
);
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
);
614 if (result
== mStatus_MemFree
)
616 DNSServiceRegistration
**r
= &DNSServiceRegistrationList
;
617 while (*r
&& *r
!= x
) r
= &(*r
)->next
;
620 debugf("RegCallback: %##s Still in DNSServiceRegistration list; removing now", sr
->RR_SRV
.name
.c
);
623 debugf("RegCallback: Freeing DNSServiceRegistration %##s %d", sr
->RR_SRV
.name
.c
, x
->ClientMachPort
);
624 FreeDNSServiceRegistration(x
);
628 mDNSlocal
void CheckForDuplicateRegistrations(DNSServiceRegistration
*x
, domainlabel
*n
, domainname
*t
, domainname
*d
)
634 ConstructServiceName(&srvname
, n
, t
, d
);
635 mDNS_sprintf(name
, "%##s", &srvname
);
637 for (rr
= mDNSStorage
.ResourceRecords
; rr
; rr
=rr
->next
)
638 if (rr
->rrtype
== kDNSType_SRV
&& SameDomainName(&rr
->name
, &srvname
))
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
);
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
)
657 unsigned char txtinfo
[1024] = "";
659 int size
= sizeof(RDataBody
);
660 unsigned char *pstring
= &txtinfo
[data_len
];
661 char *ptr
= txtRecord
;
662 DNSServiceRegistration
*x
;
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.
671 if (++data_len
>= sizeof(txtinfo
)) return(mStatus_BadParamErr
);
672 if (*ptr
== 1) // If this is our boundary marker, start a new P-string
674 pstring
= &txtinfo
[data_len
];
680 if (pstring
[0] == 255) return(mStatus_BadParamErr
);
681 pstring
[++pstring
[0]] = *ptr
++;
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
;
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
;
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
);
708 if (err
) AbortClient(client
);
709 else EnableDeathNotificationForClient(client
);
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
);
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
)
721 DNSServiceRegistration
*x
= DNSServiceRegistrationList
;
722 ExtraResourceRecord
*extra
;
723 int size
= sizeof(RDataBody
);
727 // Find this registered service
728 while (x
&& x
->ClientMachPort
!= client
) x
= x
->next
;
731 debugf("provide_DNSServiceRegistrationAddRecord_rpc bad client %X", client
);
732 return(mStatus_BadReferenceErr
);
735 // Allocate storage for our new record
736 extra
= mallocL("ExtraResourceRecord", sizeof(*extra
) - sizeof(RDataBody
) + size
);
737 if (!extra
) return(mStatus_NoMemoryErr
);
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
);
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
);
753 mDNSlocal
void UpdateCallback(mDNS
*const m
, ResourceRecord
*const rr
, RData
*OldRData
)
755 if (OldRData
!= &rr
->rdatastorage
)
756 freeL("Old RData", OldRData
);
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
)
763 DNSServiceRegistration
*x
= DNSServiceRegistrationList
;
766 int size
= sizeof(RDataBody
);
770 // Find this registered service
771 while (x
&& x
->ClientMachPort
!= client
) x
= x
->next
;
774 debugf("provide_DNSServiceRegistrationUpdateRecord_rpc bad client %X", client
);
775 return(mStatus_BadReferenceErr
);
778 // Find the record we're updating
779 if (!reference
) // NULL reference means update the primary TXT record
781 else // Else, scan our list to make sure we're updating a valid record that was previously added
783 ExtraResourceRecord
*e
= x
->s
.Extras
;
784 while (e
&& e
!= (ExtraResourceRecord
*)reference
) e
= e
->next
;
787 debugf("provide_DNSServiceRegistrationUpdateRecord_rpc failed to find record %X", reference
);
788 return(mStatus_BadReferenceErr
);
793 // Allocate storage for our new data
794 newrdata
= mallocL("RData", sizeof(*newrdata
) - sizeof(RDataBody
) + size
);
795 if (!newrdata
) return(mStatus_NoMemoryErr
);
797 // Fill in new length, and data
798 newrdata
->MaxRDLength
= size
;
799 newrdata
->RDLength
= data_len
;
800 memcpy(&newrdata
->u
, data
, data_len
);
802 // And update our record
803 err
= mDNS_Update(&mDNSStorage
, rr
, ttl
, newrdata
, UpdateCallback
);
806 debugf("Received a request to update the record of length: %d for reference: %X; failed %d",
807 data_len
, reference
, err
);
811 debugf("Received a request to update the record of length: %d for reference: %X", data_len
, reference
);
815 mDNSexport kern_return_t
provide_DNSServiceRegistrationRemoveRecord_rpc(mach_port_t unusedserver
, mach_port_t client
,
819 DNSServiceRegistration
*x
= DNSServiceRegistrationList
;
820 ExtraResourceRecord
*extra
= (ExtraResourceRecord
*)reference
;
822 // Find this registered service
823 while (x
&& x
->ClientMachPort
!= client
) x
= x
->next
;
826 LogErrorMessage("DNSServiceRegistrationRemoveRecord Client %5d not found", client
);
827 debugf("provide_DNSServiceRegistrationRemoveRecord_rpc bad client %X", client
);
828 return(mStatus_BadReferenceErr
);
831 err
= mDNS_RemoveRecordFromService(&mDNSStorage
, &x
->s
, extra
);
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
);
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
);
846 //*************************************************************************************************************
849 mDNSlocal
void DNSserverCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
851 mig_reply_error_t
*request
= msg
;
852 mig_reply_error_t
*reply
;
853 mach_msg_return_t mr
;
856 /* allocate a reply buffer */
857 reply
= CFAllocatorAllocate(NULL
, provide_DNSServiceDiscoveryRequest_subsystem
.maxsize
, 0);
859 /* call the MiG server routine */
860 (void) DNSServiceDiscoveryRequest_server(&request
->Head
, &reply
->Head
);
862 if (!(reply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) && (reply
->RetCode
!= KERN_SUCCESS
))
864 if (reply
->RetCode
== MIG_NO_REPLY
)
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.
872 CFAllocatorDeallocate(NULL
, reply
);
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).
880 request
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
881 mach_msg_destroy(&request
->Head
);
884 if (reply
->Head
.msgh_remote_port
== MACH_PORT_NULL
)
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
);
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.
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
;
909 mr
= mach_msg(&reply
->Head
, /* msg */
910 options
, /* option */
911 reply
->Head
.msgh_size
, /* send_size */
913 MACH_PORT_NULL
, /* rcv_name */
914 MACH_MSG_TIMEOUT_NONE
, /* timeout */
915 MACH_PORT_NULL
); /* notify */
917 /* Has a message error occurred? */
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
);
927 /* Includes success case. */
931 CFAllocatorDeallocate(NULL
, reply
);
934 mDNSlocal kern_return_t
registerBootstrapService()
936 kern_return_t status
;
937 mach_port_t service_send_port
, service_rcv_port
;
939 debugf("Registering Bootstrap Service");
942 * See if our service name is already registered and if we have privilege to check in.
944 status
= bootstrap_check_in(bootstrap_port
, (char*)kmDNSBootstrapName
, &service_rcv_port
);
945 if (status
== KERN_SUCCESS
)
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).
952 server_priv_port
= bootstrap_port
;
953 restarting_via_mach_init
= TRUE
;
955 else if (status
== BOOTSTRAP_UNKNOWN_SERVICE
)
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
;
961 status
= bootstrap_create_service(server_priv_port
, (char*)kmDNSBootstrapName
, &service_send_port
);
962 if (status
!= KERN_SUCCESS
)
964 mach_port_deallocate(mach_task_self(), server_priv_port
);
968 status
= bootstrap_check_in(server_priv_port
, (char*)kmDNSBootstrapName
, &service_rcv_port
);
969 if (status
!= KERN_SUCCESS
)
971 mach_port_deallocate(mach_task_self(), server_priv_port
);
972 mach_port_deallocate(mach_task_self(), service_send_port
);
975 assert(service_send_port
== service_rcv_port
);
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.
984 mach_port_destroy(mach_task_self(), service_rcv_port
);
988 mDNSlocal kern_return_t
destroyBootstrapService()
990 debugf("Destroying Bootstrap Service");
991 return bootstrap_register(server_priv_port
, (char*)kmDNSBootstrapName
, MACH_PORT_NULL
);
994 mDNSlocal
void ExitCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
996 debugf("ExitCallback: destroyBootstrapService");
998 destroyBootstrapService();
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
);
1006 debugf("ExitCallback: mDNS_Close");
1007 mDNS_Close(&mDNSStorage
);
1011 mDNSlocal kern_return_t
start(const char *bundleName
, const char *bundleDir
)
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);
1027 LogErrorMessage("Bootstrap_register failed(): A copy of the daemon is apparently already running");
1029 LogErrorMessage("Bootstrap_register failed(): %s %d", mach_error_string(status
), status
);
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,
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
);
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
); }
1048 client_death_port
= CFMachPortGetPort(d_port
);
1049 exit_m_port
= CFMachPortGetPort(e_port
);
1051 CFRunLoopAddSource(CFRunLoopGetCurrent(), d_rls
, kCFRunLoopDefaultMode
);
1052 CFRunLoopAddSource(CFRunLoopGetCurrent(), s_rls
, kCFRunLoopDefaultMode
);
1053 CFRunLoopAddSource(CFRunLoopGetCurrent(), e_rls
, kCFRunLoopDefaultMode
);
1057 if (debug_mode
) printf("Service registered with Mach Port %d\n", m_port
);
1062 mDNSlocal
void HandleSIG(int signal
)
1065 debugf("HandleSIG");
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
;
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
);
1077 msg_result
= mach_msg_send(&header
);
1080 mDNSexport
int main(int argc
, char **argv
)
1083 kern_return_t status
;
1086 for (i
=1; i
<argc
; i
++)
1088 if (!strcmp(argv
[i
], "-d")) debug_mode
= 1;
1091 signal(SIGINT
, HandleSIG
); // SIGINT is what you get for a Ctrl-C
1092 signal(SIGTERM
, HandleSIG
);
1094 // Register the server with mach_init for automatic restart only during debug mode
1096 registerBootstrapService();
1098 if (!debug_mode
&& !restarting_via_mach_init
)
1099 exit(0); /* mach_init will restart us immediately as a daemon */
1101 fp
= fopen(PID_FILE
, "w");
1104 fprintf(fp
, "%d\n", getpid());
1108 LogErrorMessage("mDNSResponder (%s %s) starting", __DATE__
, __TIME__
);
1109 status
= start(NULL
, NULL
);
1114 LogErrorMessage("CFRunLoopRun Exiting. This is bad.");
1115 mDNS_Close(&mDNSStorage
);
1118 destroyBootstrapService();