daemon.c   [plain text]


/* -*- Mode: C; tab-width: 4 -*-
 *
 * Copyright (c) 2002-2006 Apple Computer, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Formatting notes:
 * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
 * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
 * but for the sake of brevity here I will say just this: Curly braces are not syntactially
 * part of an "if" statement; they are the beginning and ending markers of a compound statement;
 * therefore common sense dictates that if they are part of a compound statement then they
 * should be indented to the same level as everything else in that compound statement.
 * Indenting curly braces at the same level as the "if" implies that curly braces are
 * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
 * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
 * understand why variable y is not of type "char*" just proves the point that poor code
 * layout leads people to unfortunate misunderstandings about how the C language really works.)

    Change History (most recent first):

$Log: daemon.c,v $
Revision 1.434  2009/06/27 00:55:27  cheshire
Added code for displaying the size of various structures like CacheRecord and CacheGroup

Revision 1.433  2009/06/25 23:36:57  cheshire
To facilitate testing, added command-line switch "-OfferSleepProxyService"
to re-enable the previously-supported mode of operation where we offer
sleep proxy service on desktop Macs that are set to never sleep.

Revision 1.432  2009/05/13 17:25:33  mkrochma
<rdar://problem/6879926> Should not schedule maintenance wake when machine has no advertised services
Sleep proxy client should only look for services being advertised via Multicast

Revision 1.431  2009/05/12 23:21:18  cheshire
<rdar://problem/6879926> Should not schedule maintenance wake when machine has no advertised services
Use mDNSCoreHaveAdvertisedServices routine to determine whether we should schedule a maintenance wake

Revision 1.430  2009/05/01 19:17:36  cheshire
<rdar://problem/6501561> Sleep Proxy: Reduce the frequency of maintenance wakes: ODD, fans, power

Revision 1.429  2009/04/30 20:07:50  mcguire
<rdar://problem/6822674> Support multiple UDSs from launchd

Revision 1.428  2009/04/22 19:43:37  cheshire
To facilitate debugging, added -DebugLogging and -UnicastPacketLogging switches
as launch-time alternatives to sending SIGUSR1 and SIGUSR2 signals later

Revision 1.427  2009/04/22 01:19:57  jessic2
<rdar://problem/6814585> Daemon: mDNSResponder is logging garbage for error codes because it's using %ld for int 32

Revision 1.426  2009/04/20 19:25:26  cheshire
For readability, changed "nomulticastadvertisements" to "NoMulticastAdvertisements"

Revision 1.425  2009/04/20 19:17:19  cheshire
Added a comment explaining why we don't need our CatchABRT handler on 10.5 and later

Revision 1.424  2009/04/17 19:10:27  mcguire
<rdar://problem/6802833> May still ping-pong with kernel when a framework calls abort()

Revision 1.423  2009/04/16 16:03:08  mcguire
<rdar://problem/6792024> abort() causes high CPU usage instead of crash & restart

Revision 1.422  2009/04/11 01:43:28  jessic2
<rdar://problem/4426780> Daemon: Should be able to turn on LogOperation dynamically

Revision 1.421  2009/04/11 00:20:06  jessic2
<rdar://problem/4426780> Daemon: Should be able to turn on LogOperation dynamically

Revision 1.420  2009/03/20 23:53:03  jessic2
<rdar://problem/6646228> SIGHUP should restart all in-progress queries

Revision 1.419  2009/03/20 21:30:04  cheshire
<rdar://problem/6705866> Crash passing invalid parameters to DNSServiceBrowserCreate()
Do not append a new question to the browser list until *after* we verify that mDNS_StartBrowse() succeeded

Revision 1.418  2009/03/17 21:32:15  cheshire
Improved "DHCPWakeTime: SCDynamicStoreCopyDHCPInfo failed" error message

Revision 1.417  2009/03/17 01:25:39  cheshire
<rdar://problem/6601427> Sleep Proxy: Retransmit and retry Sleep Proxy Server requests
In SIGINFO output, show three best Sleep Proxies

Revision 1.416  2009/02/21 01:47:36  cheshire
<rdar://problem/6600825> Race condition when sleep initiated and then immediately canceled

Revision 1.415  2009/02/21 01:45:33  cheshire
Move declaration of "mDNSs32 interval"

Revision 1.414  2009/02/14 00:05:32  cheshire
Left-justify interface names

Revision 1.413  2009/02/13 18:16:05  cheshire
Fixed some compile warnings

Revision 1.412  2009/02/13 06:34:41  cheshire
Converted LogOperation messages to LogInfo or LogSPS

Revision 1.411  2009/02/12 20:57:26  cheshire
Renamed 'LogAllOperation' switch to 'LogClientOperations'; added new 'LogSleepProxyActions' switch

Revision 1.410  2009/02/11 02:32:18  cheshire
m->p->SystemWakeForNetworkAccessEnabled renamed to m->SystemWakeOnLANEnabled

Revision 1.409  2009/02/09 21:16:14  cheshire
Improved debugging messages

Revision 1.408  2009/02/07 06:08:44  cheshire
Commented out testing code

Revision 1.407  2009/02/07 02:57:31  cheshire
<rdar://problem/6084043> Sleep Proxy: Need to adopt IOPMConnection

Revision 1.406  2009/02/06 03:06:49  mcguire
<rdar://problem/5858533> Adopt vproc_transaction API in mDNSResponder

Revision 1.405  2009/02/04 23:00:28  cheshire
Move logic for deciding when to next wake up into a subroutine called AllowSleepNow

Revision 1.404  2009/02/02 22:18:32  cheshire
If we wake up and find no wireless network, don't just give up and go back to sleep and never try again

Revision 1.403  2009/01/15 21:58:18  cheshire
Stop using ifa_name field of NetworkInterfaceInfoOSX structure, because it will be going away

Revision 1.402  2009/01/07 23:08:18  cheshire
Updated debugging messages and comments

Revision 1.401  2008/12/17 05:05:26  cheshire
Fixed alignment of NAT mapping syslog messages

Revision 1.400  2008/12/10 19:30:57  cheshire
Use symbolic name OSXVers_10_3_Panther in version check instead of literal integer "7"

Revision 1.399  2008/12/10 02:13:04  cheshire
Fix alignment of SIGINFO output for longer interface names like "bridge0"

Revision 1.398  2008/12/04 21:08:51  mcguire
<rdar://problem/6116863> mDNS: Provide mechanism to disable Multicast advertisements

Revision 1.397  2008/12/04 02:18:50  cheshire
Improved sleep/wake debugging messages

Revision 1.396  2008/11/26 23:37:44  cheshire
Use SCDynamicStoreCopyDHCPInfo to compute desired wakeup time for our next DHCP lease renewal

Revision 1.395  2008/11/14 21:56:31  cheshire
Moved debugging routine ShowTaskSchedulingError() from daemon.c into DNSCommon.c

Revision 1.394  2008/11/14 02:20:03  cheshire
Include m->NextScheduledSPS in task scheduling calculations

Revision 1.393  2008/11/14 01:22:38  cheshire
Include SPS-registered records when computing the next required wakeup time

Revision 1.392  2008/11/11 01:55:16  cheshire
Improved comments; minium requested sleep is 60 seconds

Revision 1.391  2008/11/04 02:29:55  cheshire
Clear interface list before sleeping

Revision 1.390  2008/11/02 21:22:05  cheshire
Changed mallocL size parameter back to "unsigned int"

Revision 1.389  2008/11/02 21:14:58  cheshire
Fixes to make mallocL/freeL debugging checks work on 64-bit

Revision 1.388  2008/10/31 23:05:30  cheshire
Move logic to decide when to at as Sleep Proxy Server from daemon.c to mDNSMacOSX.c

Revision 1.387  2008/10/30 01:08:18  cheshire
After waking for network maintenance operations go back to sleep again

Revision 1.386  2008/10/29 22:03:39  cheshire
Compute correct required wakeup time for NAT traversals and uDNS-registered records

Revision 1.385  2008/10/28 20:40:13  cheshire
Now that the BPF code in mDNSMacOSX.c makes its own CFSocketCreateWithNative directly, the
udsSupportAddFDToEventLoop/udsSupportRemoveFDFromEventLoop routines can go back to using kqueue

Revision 1.384  2008/10/27 22:22:59  cheshire
Extra sanity checking in udsSupportAddFDToEventLoop/udsSupportRemoveFDFromEventLoop

Revision 1.383  2008/10/27 07:24:53  cheshire
Need a "usleep(1000)" (workaround for <rdar://problem/3585273>) to avoid crashes

Revision 1.382  2008/10/24 01:51:48  cheshire
Before going to sleep, request a future wakeup to renew NAT-PMP mappings, SPS registrations, etc.

Revision 1.381  2008/10/23 22:25:58  cheshire
Renamed field "id" to more descriptive "updateid"

Revision 1.380  2008/10/23 02:25:08  cheshire
Added locking in InternetSharingChanged()

Revision 1.379  2008/10/22 23:23:59  cheshire
Moved definition of OSXVers from daemon.c into mDNSMacOSX.c

Revision 1.378  2008/10/22 19:55:35  cheshire
Miscellaneous fixes; renamed FindFirstAnswerInCache to FindSPSInCache

Revision 1.377  2008/10/22 17:17:22  cheshire
Need to open and close BPF fds when turning Sleep Proxy Server on and off

Revision 1.376  2008/10/22 01:42:39  cheshire
Before allowing sleep, delay until NetWakeResolve queries have completed

Revision 1.375  2008/10/20 22:31:31  cheshire
Instead of requesting a single BPF descriptor via mDNSRequestBPF(), call mDNSMacOSXNetworkChanged()
to signal that UDS is now available to handle BPF requests, and let it work out what it needs

Revision 1.374  2008/10/16 22:40:48  cheshire
Removed "usleep(100000);" from CFSCallBack()

Revision 1.373  2008/10/16 20:49:30  cheshire
When kevent/kqueue fails, fall back to using old CFSocket RunLoopSource instead

Revision 1.372  2008/10/15 00:03:21  cheshire
When finally going to sleep, update m->SleepState from SleepState_Transferring to SleepState_Sleeping

Revision 1.371  2008/10/14 19:09:53  cheshire
When going to sleep, delay sleep until we've got our acknowledgment from the SPS

Revision 1.370  2008/10/09 22:32:27  cheshire
Include MAC address in interface listing in SIGINFO output

Revision 1.369  2008/10/09 19:32:39  cheshire
Updated SIGINFO output to indicate whether we've found a sleep proxy server on a given interface
Hollow sun with rays "☼" indicates we're still looking; solid sun with rays "☀" indicates we found one

Revision 1.368  2008/10/03 21:23:17  mkrochma
Fix crash by not passing NULL to CFGetTypeID

Revision 1.367  2008/10/03 18:25:17  cheshire
Instead of calling "m->MainCallback" function pointer directly, call mDNSCore routine "mDNS_ConfigChanged(m);"

Revision 1.366  2008/10/03 00:34:55  cheshire
<rdar://problem/6134215> Mac with Internet Sharing should also offer Sleep Proxy service
Start and stop Sleep Proxy service when user starts and stops Internet Sharing

Revision 1.365  2008/10/02 22:23:13  cheshire
Additional debugging message giving explanation if shutdown is delayed

Revision 1.364  2008/10/01 21:23:40  cheshire
In SIGINFO interface listing, indicate whether NetWake is set

Revision 1.363  2008/09/27 01:29:15  cheshire
Call mDNSRequestBPF() to request the helper to send us the BPF fd

Revision 1.362  2008/09/26 19:47:42  cheshire
Fixed locking error: lock is supposed to be held when calling mDNS_PurgeCacheResourceRecord

Revision 1.361  2008/09/15 23:52:30  cheshire
<rdar://problem/6218902> mDNSResponder-177 fails to compile on Linux with .desc pseudo-op
Made __crashreporter_info__ symbol conditional, so we only use it for OS X build

Revision 1.360  2008/03/13 20:55:16  mcguire
<rdar://problem/5769316> fix deprecated warnings/errors
Additional cleanup: use a conditional macro instead of lots of #if

Revision 1.359  2008/03/12 23:02:58  mcguire
<rdar://problem/5769316> fix deprecated warnings/errors

Revision 1.358  2008/03/06 21:26:11  cheshire
Moved duplicated STRINGIFY macro from individual C files to DNSCommon.h

Revision 1.357  2008/02/13 17:40:43  cheshire
<rdar://problem/5740501> Investigate mysterious SIGABRTs in mDNSResponder

Revision 1.356  2007/12/18 00:28:56  cheshire
<rdar://problem/5526800> BTMM: Need to deregister records and services on shutdown/sleep
Error in ReadyForSleep() logic -- missing "not" in "!mDNSOpaque16IsZero(q->TargetQID)"

Revision 1.355  2007/12/17 22:29:22  cheshire
<rdar://problem/5526800> BTMM: Need to deregister records and services on shutdown/sleep
Log message indicating when we make IOAllowPowerChange call; make sure nextTimerEvent is set appropriately

Revision 1.354  2007/12/15 01:12:28  cheshire
<rdar://problem/5526796> Need to remove active LLQs from server upon question cancellation, on sleep, and on shutdown

Revision 1.353  2007/12/14 19:14:02  cheshire
Added (commented out) code for testing sleep/wake

Revision 1.352  2007/12/14 00:58:29  cheshire
<rdar://problem/5526800> BTMM: Need to deregister records and services on shutdown/sleep
Additional fixes: When going to sleep, mDNSResponder needs to postpone sleep
until TLS/TCP deregistrations have completed (up to five seconds maximum)

Revision 1.351  2007/12/12 21:34:18  cheshire
Now that <rdar://problem/5124399> "Not getting Keychain events" is apparently fixed,
it makes sense to reduce our workaround retry count from 5 to 2 retries. Once we've
confirmed that the bug is definitely fixed we'll remove the workaround altogether.

Revision 1.350  2007/12/07 00:45:58  cheshire
<rdar://problem/5526800> BTMM: Need to clean up registrations on shutdown

Revision 1.349  2007/12/04 22:00:54  cheshire
Fixed mistake in comment

Revision 1.348  2007/12/01 00:27:43  cheshire
Fixed compile warning: declaration of 'r' shadows a previous local

Revision 1.347  2007/11/02 22:00:13  cheshire
<rdar://problem/5575583> BTMM: Work around keychain notification bug <rdar://problem/5124399>
Need to hold the lock while calling SetDomainSecrets

Revision 1.346  2007/11/02 20:18:13  cheshire
<rdar://problem/5575583> BTMM: Work around keychain notification bug <rdar://problem/5124399>

Revision 1.345  2007/10/17 18:41:21  cheshire
For debugging, make SIGUSR1 simulate a KeychainChanged event as well as a NetworkChanged

Revision 1.344  2007/09/29 01:06:17  mcguire
<rdar://problem/5507862> 9A564: mDNSResponder crash in mDNS_Execute

Revision 1.343  2007/09/24 05:02:41  cheshire
Debugging: In SIGINFO output, indicate explicitly when a given section is empty

Revision 1.342  2007/09/18 19:09:02  cheshire
<rdar://problem/5489549> mDNSResponderHelper (and other binaries) missing SCCS version strings

Revision 1.341  2007/09/12 01:22:13  cheshire
Improve validatelists() checking to detect when 'next' pointer gets smashed to ~0

Revision 1.340  2007/09/07 22:44:03  mcguire
<rdar://problem/5448420> Move CFUserNotification code to mDNSResponderHelper

Revision 1.339  2007/09/06 19:08:29  cheshire
LogClientOperations check needs to be "#if LogClientOperations || MDNS_DEBUGMSGS"

Revision 1.338  2007/09/05 23:34:27  mcguire
Revert logging change

Revision 1.337  2007/09/05 20:45:50  cheshire
Added list of KQSocketEventSources in SIGINFO output

Revision 1.336  2007/08/31 17:15:37  cheshire
Reordered startup log messages so that "mDNSResponder ... starting" is the first message

Revision 1.335  2007/08/31 02:00:16  cheshire
Added comment explaining use of zero-width non-breaking space character to tag literal strings

Revision 1.334  2007/08/24 23:40:24  cheshire
Added comment about FreeServiceInstance

Revision 1.333  2007/08/23 21:02:35  cheshire
SecKeychainSetPreferenceDomain() call should be in platform-support layer, not daemon.c

Revision 1.332  2007/08/22 23:54:54  mcguire
<rdar://problem/5422558> BTMM: mDNSResponder should be able to run from the cmdline

Revision 1.331  2007/08/18 01:02:03  mcguire
<rdar://problem/5415593> No Bonjour services are getting registered at boot

Revision 1.330  2007/08/10 22:25:57  mkrochma
<rdar://problem/5396302> mDNSResponder continually complains about slow UDP packet reception -- about 400 msecs

Revision 1.329  2007/08/08 22:34:58  mcguire
<rdar://problem/5197869> Security: Run mDNSResponder as user id mdnsresponder instead of root

Revision 1.328  2007/07/27 22:43:37  cheshire
Improved mallocL/freeL "suspiciously large" debugging messages

Revision 1.327  2007/07/27 19:30:41  cheshire
Changed mDNSQuestionCallback parameter from mDNSBool to QC_result,
to properly reflect tri-state nature of the possible responses

Revision 1.326  2007/07/24 17:23:33  cheshire
<rdar://problem/5357133> Add list validation checks for debugging

Revision 1.325  2007/07/11 23:43:43  cheshire
Rename PurgeCacheResourceRecord to mDNS_PurgeCacheResourceRecord

Revision 1.324  2007/07/11 22:44:40  cheshire
<rdar://problem/5328801> SIGHUP should purge the cache

Revision 1.323  2007/07/11 03:01:50  cheshire
<rdar://problem/5303807> Register IPv6-only hostname and don't create port mappings for AutoTunnel services

Revision 1.322  2007/07/06 18:58:16  cheshire
Check m->NextScheduledNATOp in ShowTaskSchedulingError()

Revision 1.321  2007/07/02 21:54:20  cheshire
Fix compile error in MACOSX_MDNS_MALLOC_DEBUGGING checks

Revision 1.320  2007/06/28 21:16:27  cheshire
Rename "m->nextevent" as more informative "m->NextuDNSEvent"

Revision 1.319  2007/06/22 20:47:08  cheshire
<rdar://problem/5285417> DOS charset changes from CP932 to CP850 after Computer Name conflict
Made a "SafeSCPreferencesSetComputerName" routine to set the Computer Name without changing the machine's default character set

Revision 1.318  2007/06/20 01:45:40  cheshire
When showing dormant interfaces, display last-seen IP address for that dormant interface

Revision 1.317  2007/06/20 01:10:12  cheshire
<rdar://problem/5280520> Sync iPhone changes into main mDNSResponder code

Revision 1.316  2007/06/19 19:27:11  cheshire
<rdar://problem/5141540> Sandbox mDNSResponder
Weak-link sandbox_init, so mDNSResponder can be run on Tiger for regression testing

Revision 1.315  2007/06/15 21:54:51  cheshire
<rdar://problem/4883206> Add packet logging to help debugging private browsing over TLS

Revision 1.314  2007/06/15 19:23:17  cheshire
<rdar://problem/5254053> mDNSResponder renames my host without asking
Improve log messages, to distinguish user-initiated renames from automatic (name conflict) renames

Revision 1.313  2007/05/25 16:02:05  cheshire
When MACOSX_MDNS_MALLOC_DEBUGGING is enabled, log suspiciously large memory allocations

Revision 1.312  2007/05/22 19:07:21  cheshire
Add comment explaining RR_CACHE_SIZE calculation

Revision 1.311  2007/05/15 21:47:21  cheshire
Get rid of "#pragma unused(m)"

Revision 1.310  2007/05/08 00:56:17  cheshire
<rdar://problem/4118503> Share single socket instead of creating separate socket for each active interface

Revision 1.309  2007/04/30 21:33:38  cheshire
Fix crash when a callback unregisters a service while the UpdateSRVRecords() loop
is iterating through the m->ServiceRegistrations list

Revision 1.308  2007/04/28 01:31:59  cheshire
Improve debugging support for catching memory corruption problems

Revision 1.307  2007/04/24 18:32:00  cheshire
Grab a copy of KQtask string pointer in case KQcallback deletes the task

Revision 1.306  2007/04/22 19:11:51  cheshire
Some quirk of RemoveFromList (GenLinkedList.c) was corrupting the list and causing a crash
if the element being removed was not the last in the list. Fixed by removing GenLinkedList.c
from the project and just using simple vanilla C linked-list manipulation instead.

Revision 1.305  2007/04/22 06:02:03  cheshire
<rdar://problem/4615977> Query should immediately return failure when no server

Revision 1.304  2007/04/21 21:47:47  cheshire
<rdar://problem/4376383> Daemon: Add watchdog timer

Revision 1.303  2007/04/18 00:50:47  cheshire
<rdar://problem/5141540> Sandbox mDNSResponder

Revision 1.302  2007/04/07 01:01:48  cheshire
<rdar://problem/5095167> mDNSResponder periodically blocks in SSLRead

Revision 1.301  2007/04/05 19:13:48  cheshire
Use better name in SCPreferencesCreate

Revision 1.300  2007/04/04 21:22:18  cheshire
Suppress "Local Hostname changed" syslog message when name has not actually changed

Revision 1.299  2007/04/03 19:19:33  cheshire
Use mDNSIPPortIsZero() instead of peeking into 'NotAnInteger' field

Revision 1.298  2007/03/30 21:51:45  cheshire
Minor code tidying

Revision 1.297  2007/03/27 22:47:19  cheshire
On memory corruption, if ForceAlerts is set, force a crash to get a stack trace

Revision 1.296  2007/03/24 01:23:29  cheshire
Call validator for uDNS data structures

Revision 1.295  2007/03/20 23:32:49  cheshire
Minor textual tidying

Revision 1.294  2007/03/07 02:50:50  cheshire
<rdar://problem/4574528> Name conflict dialog doesn't appear if Bonjour is persistantly unable to find an available hostname

Revision 1.293  2007/03/06 22:59:01  cheshire
<rdar://problem/4157921> Security: Null dereference possible in daemon.c

Revision 1.292  2007/02/28 21:55:10  cheshire
<rdar://problem/3862944> UI: Name conflict notifications should be localized
Additional fix: We were not getting our NotificationCallBackDismissed messages
because we were scheduling our CFUserNotification RunLoopSource on the wrong runloop.
(We were incorrectly assuming CFRunLoopGetCurrent() would be the right runloop.)

Revision 1.291  2007/02/28 03:51:24  cheshire
<rdar://problem/3862944> UI: Name conflict notifications should be localized
Moved curly quotes out of the literal text and into the localized text, so they
can be replaced with alternate characters as appropriate for other languages.

Revision 1.290  2007/02/14 01:58:19  cheshire
<rdar://problem/4995831> Don't delete Unix Domain Socket on exit if we didn't create it on startup

Revision 1.289  2007/02/07 19:32:00  cheshire
<rdar://problem/4980353> All mDNSResponder components should contain version strings in SCCS-compatible format

Revision 1.288  2007/02/07 01:01:24  cheshire
<rdar://problem/3956518> Need to go native with launchd
Additional refinements -- was unnecessarily calling launch_data_free()

Revision 1.287  2007/02/06 19:06:48  cheshire
<rdar://problem/3956518> Need to go native with launchd

Revision 1.286  2007/01/06 01:00:33  cheshire
Improved SIGINFO output

Revision 1.285  2007/01/05 08:30:47  cheshire
Trim excessive "$Log" checkin history from before 2006
(checkin history still available via "cvs log ..." of course)

Revision 1.284  2007/01/05 05:44:35  cheshire
Move automatic browse/registration management from uDNS.c to mDNSShared/uds_daemon.c,
so that mDNSPosix embedded clients will compile again

Revision 1.283  2007/01/04 23:11:14  cheshire
<rdar://problem/4720673> uDNS: Need to start caching unicast records
When an automatic browsing domain is removed, generate appropriate "remove" events for legacy queries

Revision 1.282  2006/12/21 00:09:45  cheshire
Use mDNSPlatformMemZero instead of bzero

Revision 1.281  2006/11/18 05:01:32  cheshire
Preliminary support for unifying the uDNS and mDNS code,
including caching of uDNS answers

Revision 1.280  2006/11/10 00:54:16  cheshire
<rdar://problem/4816598> Changing case of Computer Name doesn't work

Revision 1.279  2006/11/02 17:44:01  cheshire
No longer have a separate uDNS ActiveQueries list

Revision 1.278  2006/10/05 04:04:24  herscher
Remove embedded uDNS_info struct from DNSQuestion_struct

Revision 1.277  2006/09/21 21:01:24  cheshire
Change 'autorename' to more accurate name 'renameonmemfree'

Revision 1.276  2006/09/17 19:12:02  cheshire
Further changes for removal of uDNS_info substructure from mDNS_struct

Revision 1.275  2006/09/15 21:20:16  cheshire
Remove uDNS_info substructure from mDNS_struct

Revision 1.274  2006/08/14 23:24:39  cheshire
Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0

Revision 1.273  2006/07/30 05:43:19  cheshire
<rdar://problem/4049048> Convert mDNSResponder to use kqueue
Problems using KQueueFD with select() -- for now we'll stick to pure kevent()

Revision 1.272  2006/07/27 03:24:35  cheshire
<rdar://problem/4049048> Convert mDNSResponder to use kqueue
Further refinement: Declare KQueueEntry parameter "const"

Revision 1.271  2006/07/27 02:59:26  cheshire
<rdar://problem/4049048> Convert mDNSResponder to use kqueue
Further refinements: CFRunLoop thread needs to explicitly wake the kqueue thread
after releasing BigMutex, in case actions it took have resulted in new work for the
kqueue thread (e.g. NetworkChanged events may result in the kqueue thread having to
add new active interfaces to its list, and consequently schedule queries to be sent).

Revision 1.270  2006/07/25 17:16:36  mkrochma
Quick fix to solve kqueue related crashes and hangs

Revision 1.269  2006/07/22 06:11:37  cheshire
<rdar://problem/4049048> Convert mDNSResponder to use kqueue

Revision 1.268  2006/07/15 02:01:32  cheshire
<rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder
Fix broken "empty string" browsing

Revision 1.267  2006/07/07 01:09:10  cheshire
<rdar://problem/4472013> Add Private DNS server functionality to dnsextd
Only use mallocL/freeL debugging routines when building mDNSResponder, not dnsextd

Revision 1.266  2006/07/05 23:34:53  cheshire
<rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder

Revision 1.265  2006/06/29 07:32:08  cheshire
Added missing LogOperation logging for DNSServiceBrowse results

Revision 1.264  2006/06/29 05:33:30  cheshire
<rdar://problem/4607043> mDNSResponder conditional compilation options

Revision 1.263  2006/06/08 23:23:48  cheshire
Fix errant indentation of curly brace at the end of provide_DNSServiceBrowserCreate_rpc()

Revision 1.262  2006/03/18 21:49:11  cheshire
Added comment in ShowTaskSchedulingError(mDNS *const m)

Revision 1.261  2006/01/06 01:22:28  cheshire
<rdar://problem/4108164> Reword "mach_absolute_time went backwards" dialog

*/

#include <mach/mach.h>
#include <mach/mach_error.h>
#include <servers/bootstrap.h>
#include <sys/types.h>
#include <unistd.h>
#include <paths.h>
#include <fcntl.h>
#include <launch.h>
#include <pwd.h>
#include <sys/event.h>
#include <pthread.h>
#include <sandbox.h>
#include <SystemConfiguration/SCPreferencesSetSpecific.h>
#include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>

#if TARGET_OS_EMBEDDED
#include <bootstrap_priv.h>

#define bootstrap_register(A,B,C) bootstrap_register2((A),(B),(C),0)
#endif

#include "DNSServiceDiscoveryRequestServer.h"
#include "DNSServiceDiscoveryReply.h"

#include "uDNS.h"
#include "DNSCommon.h"
#include "mDNSMacOSX.h"				// Defines the specific types needed to run mDNS on this platform

#include "uds_daemon.h"				// Interface to the server side implementation of dns_sd.h

#include <DNSServiceDiscovery/DNSServiceDiscovery.h>
#include "helper.h"
#include "safe_vproc.h"

//*************************************************************************************************************
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - Globals
#endif

static mDNS_PlatformSupport PlatformStorage;

// Start off with a default cache of 16K (99 records)
// Each time we grow the cache we add another 99 records
// 99 * 164 = 16236 bytes.
// This fits in four 4kB pages, with 148 bytes spare for memory block headers and similar overhead
#define RR_CACHE_SIZE ((16*1024) / sizeof(CacheRecord))
static CacheEntity rrcachestorage[RR_CACHE_SIZE];

static const char kmDNSBootstrapName[] = "com.apple.mDNSResponderRestart";
static mach_port_t m_port            = MACH_PORT_NULL;
static mach_port_t client_death_port = MACH_PORT_NULL;
static mach_port_t signal_port       = MACH_PORT_NULL;
static mach_port_t server_priv_port  = MACH_PORT_NULL;

static dnssd_sock_t *launchd_fds = mDNSNULL;
static mDNSu32 launchd_fds_count = 0;

// mDNS Mach Message Timeout, in milliseconds.
// We need this to be short enough that we don't deadlock the mDNSResponder if a client
// fails to service its mach message queue, but long enough to give a well-written
// client a chance to service its mach message queue without getting cut off.
// Empirically, 50ms seems to work, so we set the timeout to 250ms to give
// even extra-slow clients a fair chance before we cut them off.
#define MDNS_MM_TIMEOUT 250

static int restarting_via_mach_init = 0;	// Used on Jaguar/Panther when daemon is started via mach_init mechanism
static int started_via_launchdaemon = 0;	// Indicates we're running on Tiger or later, where daemon is managed by launchd
static mDNSBool advertise = mDNS_Init_AdvertiseLocalAddresses; // By default, advertise addresses (& other records) via multicast

//*************************************************************************************************************
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark - Active client list structures
#endif

typedef struct DNSServiceDomainEnumeration_struct DNSServiceDomainEnumeration;
struct DNSServiceDomainEnumeration_struct
	{
	DNSServiceDomainEnumeration *next;
	mach_port_t ClientMachPort;
	DNSQuestion dom;	// Question asking for domains
	DNSQuestion def;	// Question asking for default domain
	};

typedef struct DNSServiceBrowserResult_struct DNSServiceBrowserResult;
struct DNSServiceBrowserResult_struct
	{
	DNSServiceBrowserResult *next;
	int resultType;
	domainname result;
	};

typedef struct DNSServiceBrowser_struct DNSServiceBrowser;

typedef struct DNSServiceBrowserQuestion
	{
	struct DNSServiceBrowserQuestion *next;
	DNSQuestion q;
    domainname domain;
	} DNSServiceBrowserQuestion;

struct DNSServiceBrowser_struct
	{
	DNSServiceBrowser *next;
	mach_port_t ClientMachPort;
	DNSServiceBrowserQuestion *qlist;
	DNSServiceBrowserResult *results;
	mDNSs32 lastsuccess;
    mDNSBool DefaultDomain;                // was the browse started on an explicit domain?
    domainname type;                       // registration type
	};

typedef struct DNSServiceResolver_struct DNSServiceResolver;
struct DNSServiceResolver_struct
	{
	DNSServiceResolver *next;
	mach_port_t ClientMachPort;
	ServiceInfoQuery q;
	ServiceInfo      i;
	mDNSs32          ReportTime;
	};

// A single registered service: ServiceRecordSet + bookkeeping
// Note that we duplicate some fields from parent DNSServiceRegistration object
// to facilitate cleanup, when instances and parent may be deallocated at different times.
typedef struct ServiceInstance
	{
    struct ServiceInstance *next;
	mach_port_t ClientMachPort;
    mDNSBool autoname;			// Set if this name is tied to the Computer Name
    mDNSBool renameonmemfree;	// Set if we just got a name conflict and now need to automatically pick a new name
    domainlabel name;
    domainname domain;
    ServiceRecordSet srs;
	// Don't add any fields after ServiceRecordSet.
	// This is where the implicit extra space goes if we allocate an oversized ServiceRecordSet object
	} ServiceInstance;

// A client-created service.  May reference several ServiceInstance objects if default
// settings cause registration in multiple domains.
typedef struct DNSServiceRegistration
	{
    struct DNSServiceRegistration *next;
	mach_port_t ClientMachPort;
    mDNSBool DefaultDomain;
    mDNSBool autoname;
    size_t rdsize;
    int NumSubTypes;
    char regtype[MAX_ESCAPED_DOMAIN_NAME]; // for use in AllocateSubtypes
    domainlabel name;  // used only if autoname is false
    domainname type;
    mDNSIPPort port;
    unsigned char txtinfo[1024];
    size_t txt_len;
    uint32_t NextRef;
    ServiceInstance *regs;
	} DNSServiceRegistration;

static DNSServiceDomainEnumeration *DNSServiceDomainEnumerationList = NULL;
static DNSServiceBrowser           *DNSServiceBrowserList           = NULL;
static DNSServiceResolver          *DNSServiceResolverList          = NULL;
static DNSServiceRegistration      *DNSServiceRegistrationList      = NULL;

// We keep a list of client-supplied event sources in KQSocketEventSource records
typedef struct KQSocketEventSource
	{
	struct  KQSocketEventSource *next;
	int                         fd;
	KQueueEntry                 kqs;
	} KQSocketEventSource;

static KQSocketEventSource *gEventSources;

//*************************************************************************************************************
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark - General Utility Functions
#endif

#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING

char _malloc_options[] = "AXZ";

mDNSexport void LogMemCorruption(const char *format, ...)
	{
	char buffer[512];
	va_list ptr;
	va_start(ptr,format);
	buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0;
	va_end(ptr);
	LogMsg("!!!! %s !!!!", buffer);
	NotifyOfElusiveBug("Memory Corruption", buffer);
#if ForceAlerts
	*(long*)0 = 0;	// Trick to crash and get a stack trace right here, if that's what we want
#endif
	}

mDNSlocal void validatelists(mDNS *const m)
	{
	// Check local lists
	KQSocketEventSource *k;
	for (k = gEventSources; k; k=k->next)
		if (k->next == (KQSocketEventSource *)~0 || k->fd < 0)
			LogMemCorruption("gEventSources: %p is garbage (%d)", k, k->fd);

	// Check Mach client lists
	DNSServiceDomainEnumeration *e;
	for (e = DNSServiceDomainEnumerationList; e; e=e->next)
		if (e->next == (DNSServiceDomainEnumeration *)~0 || e->ClientMachPort == 0 || e->ClientMachPort == (mach_port_t)~0)
			LogMemCorruption("DNSServiceDomainEnumerationList: %p is garbage (%X)", e, e->ClientMachPort);

	DNSServiceBrowser           *b;
	for (b = DNSServiceBrowserList; b; b=b->next)
		if (b->next == (DNSServiceBrowser *)~0 || b->ClientMachPort == 0 || b->ClientMachPort == (mach_port_t)~0)
			LogMemCorruption("DNSServiceBrowserList: %p is garbage (%X)", b, b->ClientMachPort);

	DNSServiceResolver          *l;
	for (l = DNSServiceResolverList; l; l=l->next)
		if (l->next == (DNSServiceResolver *)~0 || l->ClientMachPort == 0 || l->ClientMachPort == (mach_port_t)~0)
			LogMemCorruption("DNSServiceResolverList: %p is garbage (%X)", l, l->ClientMachPort);

	DNSServiceRegistration      *r;
	for (r = DNSServiceRegistrationList; r; r=r->next)
		if (r->next == (DNSServiceRegistration *)~0 || r->ClientMachPort == 0 || r->ClientMachPort == (mach_port_t)~0)
			LogMemCorruption("DNSServiceRegistrationList: %p is garbage (%X)", r, r->ClientMachPort);

	// Check Unix Domain Socket client lists (uds_daemon.c)
	uds_validatelists();

	// Check core mDNS lists
	AuthRecord                  *rr;
	for (rr = m->ResourceRecords; rr; rr=rr->next)
		{
		if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF)
			LogMemCorruption("ResourceRecords list: %p is garbage (%X)", rr, rr->resrec.RecordType);
		if (rr->resrec.name != &rr->namestorage)
			LogMemCorruption("ResourceRecords list: %p name %p does not point to namestorage %p %##s",
				rr, rr->resrec.name->c, rr->namestorage.c, rr->namestorage.c);
		}

	for (rr = m->DuplicateRecords; rr; rr=rr->next)
		if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF)
			LogMemCorruption("DuplicateRecords list: %p is garbage (%X)", rr, rr->resrec.RecordType);

	rr = m->NewLocalRecords;
	if (rr)
		if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF)
			LogMemCorruption("NewLocalRecords: %p is garbage (%X)", rr, rr->resrec.RecordType);

	rr = m->CurrentRecord;
	if (rr)
		if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF)
			LogMemCorruption("CurrentRecord: %p is garbage (%X)", rr, rr->resrec.RecordType);

	DNSQuestion                 *q;
	for (q = m->Questions; q; q=q->next)
		if (q->next == (DNSQuestion*)~0 || q->ThisQInterval == (mDNSs32)~0)
			LogMemCorruption("Questions list: %p is garbage (%lX %p)", q, q->ThisQInterval, q->next);

	CacheGroup                  *cg;
	CacheRecord                 *cr;
	mDNSu32 slot;
	FORALL_CACHERECORDS(slot, cg, cr)
		{
		if (cr->resrec.RecordType == 0 || cr->resrec.RecordType == 0xFF)
			LogMemCorruption("Cache slot %lu: %p is garbage (%X)", slot, cr, cr->resrec.RecordType);
		if (cr->CRActiveQuestion)
			{
			for (q = m->Questions; q; q=q->next) if (q == cr->CRActiveQuestion) break;
			if (!q) LogMemCorruption("Cache slot %lu: CRActiveQuestion %p not in m->Questions list %s", slot, cr->CRActiveQuestion, CRDisplayString(m, cr));
			}
		}

	// Check core uDNS lists
	udns_validatelists(m);

	// Check platform-layer lists
	NetworkInterfaceInfoOSX     *i;
	for (i = m->p->InterfaceList; i; i = i->next)
		if (i->next == (NetworkInterfaceInfoOSX *)~0 || !i->m || i->m == (mDNS *)~0)
			LogMemCorruption("m->p->InterfaceList: %p is garbage (%p)", i, i->ifinfo.ifname);

	ClientTunnel *t;
	for (t = m->TunnelClients; t; t=t->next)
		if (t->next == (ClientTunnel *)~0 || t->dstname.c[0] > 63)
			LogMemCorruption("m->TunnelClients: %p is garbage (%d)", t, t->dstname.c[0]);
	}

mDNSexport void *mallocL(char *msg, unsigned int size)
	{
	// Allocate space for two words of sanity checking data before the requested block
	mDNSu32 *mem = malloc(sizeof(mDNSu32) * 2 + size);
	if (!mem)
		{ LogMsg("malloc( %s : %d ) failed", msg, size); return(NULL); }
	else
		{
		if      (size > 24000)                      LogMsg("malloc( %s : %lu ) = %p suspiciously large", msg, size, &mem[2]);
		else if (MACOSX_MDNS_MALLOC_DEBUGGING >= 2) LogMsg("malloc( %s : %lu ) = %p",                    msg, size, &mem[2]);
		mem[0] = 0xDEAD1234;
		mem[1] = size;
		//mDNSPlatformMemZero(&mem[2], size);
		memset(&mem[2], 0xFF, size);
		validatelists(&mDNSStorage);
		return(&mem[2]);
		}
	}

mDNSexport void freeL(char *msg, void *x)
	{
	if (!x)
		LogMsg("free( %s @ NULL )!", msg);
	else
		{
		mDNSu32 *mem = ((mDNSu32 *)x) - 2;
		if      (mem[0] != 0xDEAD1234)            { LogMsg("free( %s @ %p ) !!!! NOT ALLOCATED !!!!", msg, &mem[2]); return; }
		if      (mem[1] > 24000)                    LogMsg("free( %s : %ld @ %p) suspiciously large", msg, mem[1], &mem[2]);
		else if (MACOSX_MDNS_MALLOC_DEBUGGING >= 2) LogMsg("free( %s : %ld @ %p)",                    msg, mem[1], &mem[2]);
		//mDNSPlatformMemZero(mem, sizeof(mDNSu32) * 2 + mem[1]);
		memset(mem, 0xFF, sizeof(mDNSu32) * 2 + mem[1]);
		validatelists(&mDNSStorage);
		free(mem);
		}
	}

#endif

//*************************************************************************************************************
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark - Mach client request handlers
#endif

//*************************************************************************************************************
// Client Death Detection

// This gets called after ALL constituent records of the Service Record Set have been deregistered
mDNSlocal void FreeServiceInstance(ServiceInstance *x)
	{
	ServiceRecordSet *s = &x->srs;
	ExtraResourceRecord *e = x->srs.Extras, *tmp;
	
	while (e)
		{
		e->r.RecordContext = e;
		tmp = e;
		e = e->next;
		FreeExtraRR(&mDNSStorage, &tmp->r, mStatus_MemFree);
		}
	
	if (s->RR_TXT.resrec.rdata != &s->RR_TXT.rdatastorage)
		freeL("TXT RData", s->RR_TXT.resrec.rdata);

	if (s->SubTypes) freeL("ServiceSubTypes", s->SubTypes);
	freeL("ServiceInstance", x);
	}

// AbortClient finds whatever client is identified by the given Mach port,
// stops whatever operation that client was doing, and frees its memory.
// In the case of a service registration, the actual freeing may be deferred
// until we get the mStatus_MemFree message, if necessary
mDNSlocal void AbortClient(mach_port_t ClientMachPort, void *m)
	{
	DNSServiceDomainEnumeration **e = &DNSServiceDomainEnumerationList;
	DNSServiceBrowser           **b = &DNSServiceBrowserList;
	DNSServiceResolver          **l = &DNSServiceResolverList;
	DNSServiceRegistration      **r = &DNSServiceRegistrationList;

	while (*e && (*e)->ClientMachPort != ClientMachPort) e = &(*e)->next;
	if (*e)
		{
		DNSServiceDomainEnumeration *x = *e;
		*e = (*e)->next;
		if (m && m != x)
			LogMsg("%5d: DNSServiceDomainEnumeration(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->dom.qname.c, m, x);
		else LogOperation("%5d: DNSServiceDomainEnumeration(%##s) STOP", ClientMachPort, x->dom.qname.c);
		mDNS_StopGetDomains(&mDNSStorage, &x->dom);
		mDNS_StopGetDomains(&mDNSStorage, &x->def);
		freeL("DNSServiceDomainEnumeration", x);
		return;
		}

	while (*b && (*b)->ClientMachPort != ClientMachPort) b = &(*b)->next;
	if (*b)
		{
		DNSServiceBrowser *x = *b;
		DNSServiceBrowserQuestion *freePtr, *qptr = x->qlist;
		*b = (*b)->next;
		while (qptr)
			{
			if (m && m != x)
				LogMsg("%5d: DNSServiceBrowse(%##s) STOP; WARNING m %p != x %p", ClientMachPort, qptr->q.qname.c, m, x);
			else LogOperation("%5d: DNSServiceBrowse(%##s) STOP", ClientMachPort, qptr->q.qname.c);
			mDNS_StopBrowse(&mDNSStorage, &qptr->q);
			freePtr = qptr;
			qptr = qptr->next;
			freeL("DNSServiceBrowserQuestion", freePtr);
			}
		while (x->results)
			{
			DNSServiceBrowserResult *t = x->results;
			x->results = x->results->next;
			freeL("DNSServiceBrowserResult", t);
			}
		freeL("DNSServiceBrowser", x);
		return;
		}

	while (*l && (*l)->ClientMachPort != ClientMachPort) l = &(*l)->next;
	if (*l)
		{
		DNSServiceResolver *x = *l;
		*l = (*l)->next;
		if (m && m != x)
			LogMsg("%5d: DNSServiceResolve(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->i.name.c, m, x);
		else LogOperation("%5d: DNSServiceResolve(%##s) STOP", ClientMachPort, x->i.name.c);
		mDNS_StopResolveService(&mDNSStorage, &x->q);
		freeL("DNSServiceResolver", x);
		return;
		}

	while (*r && (*r)->ClientMachPort != ClientMachPort) r = &(*r)->next;
	if (*r)
		{
		ServiceInstance *si = NULL;
		DNSServiceRegistration *x = *r;
		*r = (*r)->next;

		si = x->regs;
		while (si)
			{
			ServiceInstance *instance = si;
			si = si->next;
			instance->renameonmemfree = mDNSfalse;
			if (m && m != x) LogMsg("%5d: DNSServiceRegistration(%##s, %u) STOP; WARNING m %p != x %p", ClientMachPort, instance->srs.RR_SRV.resrec.name->c, SRS_PORT(&instance->srs), m, x);
			else LogOperation("%5d: DNSServiceRegistration(%##s, %u) STOP", ClientMachPort, instance->srs.RR_SRV.resrec.name->c, SRS_PORT(&instance->srs));

			// If mDNS_DeregisterService() returns mStatus_NoError, that means that the service was found in the list,
			// is sending its goodbye packet, and we'll get an mStatus_MemFree message when we can free the memory.
			// If mDNS_DeregisterService() returns an error, it means that the service had already been removed from
			// the list, so we should go ahead and free the memory right now
			if (mDNS_DeregisterService(&mDNSStorage, &instance->srs)) FreeServiceInstance(instance); // FreeServiceInstance invalidates pointer
			}
		x->regs = NULL;
		freeL("DNSServiceRegistration", x);
		return;
		}

	LogMsg("%5d: died or deallocated, but no record of client can be found!", ClientMachPort);
	}

#define AbortBlockedClient(C,MSG,M) AbortClientWithLogMessage((C), "stopped accepting Mach messages", " (" MSG ")", (M))

mDNSlocal void AbortClientWithLogMessage(mach_port_t c, char *reason, char *msg, void *m)
	{
	DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList;
	DNSServiceBrowser           *b = DNSServiceBrowserList;
	DNSServiceResolver          *l = DNSServiceResolverList;
	DNSServiceRegistration      *r = DNSServiceRegistrationList;
	DNSServiceBrowserQuestion   *qptr;

	while (e && e->ClientMachPort != c) e = e->next;
	while (b && b->ClientMachPort != c) b = b->next;
	while (l && l->ClientMachPort != c) l = l->next;
	while (r && r->ClientMachPort != c) r = r->next;

	if      (e) LogMsg("%5d: DomainEnumeration(%##s) %s%s",                   c, e->dom.qname.c,                reason, msg);
	else if (b)
			{
			for (qptr = b->qlist; qptr; qptr = qptr->next)
				LogMsg("%5d: Browser(%##s) %s%s",                             c, qptr->q.qname.c,               reason, msg);
			}
	else if (l) LogMsg("%5d: Resolver(%##s) %s%s",                            c, l->i.name.c,                   reason, msg);
	else if (r)
			{
			ServiceInstance *si;
			for (si = r->regs; si; si = si->next)
				LogMsg("%5d: Registration(%##s) %s%s",                        c, si->srs.RR_SRV.resrec.name->c, reason, msg);
			}
	else        LogMsg("%5d: (%s) %s, but no record of client can be found!", c,                                reason, msg);

	AbortClient(c, m);
	}

mDNSlocal mDNSBool CheckForExistingClient(mach_port_t c)
	{
	DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList;
	DNSServiceBrowser           *b = DNSServiceBrowserList;
	DNSServiceResolver          *l = DNSServiceResolverList;
	DNSServiceRegistration      *r = DNSServiceRegistrationList;
	DNSServiceBrowserQuestion   *qptr;

	while (e && e->ClientMachPort != c) e = e->next;
	while (b && b->ClientMachPort != c) b = b->next;
	while (l && l->ClientMachPort != c) l = l->next;
	while (r && r->ClientMachPort != c) r = r->next;
	if (e) LogMsg("%5d: DomainEnumeration(%##s) already exists!", c, e->dom.qname.c);
	if (b)
		{
		for (qptr = b->qlist; qptr; qptr = qptr->next)
			LogMsg("%5d: Browser(%##s) already exists!",          c, qptr->q.qname.c);
		}
	if (l) LogMsg("%5d: Resolver(%##s) already exists!",          c, l->i.name.c);
	if (r) LogMsg("%5d: Registration(%##s) already exists!",      c, r->regs ? r->regs->srs.RR_SRV.resrec.name->c : NULL);
	return(e || b || l || r);
	}

mDNSlocal void ClientDeathCallback(CFMachPortRef unusedport, void *voidmsg, CFIndex size, void *info)
	{
	KQueueLock(&mDNSStorage);
	mach_msg_header_t *msg = (mach_msg_header_t *)voidmsg;
	(void)unusedport; // Unused
	(void)size; // Unused
	(void)info; // Unused
	if (msg->msgh_id == MACH_NOTIFY_DEAD_NAME)
		{
		const mach_dead_name_notification_t *const deathMessage = (mach_dead_name_notification_t *)msg;
		AbortClient(deathMessage->not_port, NULL);

		/* Deallocate the send right that came in the dead name notification */
		mach_port_destroy(mach_task_self(), deathMessage->not_port);
		}
	KQueueUnlock(&mDNSStorage, "Mach AbortClient");
	}

mDNSlocal void EnableDeathNotificationForClient(mach_port_t ClientMachPort, void *m)
	{
	mach_port_t prev;
	kern_return_t r = mach_port_request_notification(mach_task_self(), ClientMachPort, MACH_NOTIFY_DEAD_NAME, 0,
													 client_death_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev);
	// If the port already died while we were thinking about it, then abort the operation right away
	if (r != KERN_SUCCESS)
		AbortClientWithLogMessage(ClientMachPort, "died/deallocated before we could enable death notification", "", m);
	}

//*************************************************************************************************************
// Domain Enumeration

mDNSlocal void DomainEnumFound(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
	{
	kern_return_t status;
	char buffer[MAX_ESCAPED_DOMAIN_NAME];
	DNSServiceDomainEnumerationReplyResultType rt;
	DNSServiceDomainEnumeration *x = (DNSServiceDomainEnumeration *)question->QuestionContext;
	(void)m; // Unused

	debugf("DomainEnumFound: %##s PTR %##s", answer->name->c, answer->rdata->u.name.c);
	if (answer->rrtype != kDNSType_PTR) return;
	if (!x) { debugf("DomainEnumFound: DNSServiceDomainEnumeration is NULL"); return; }

	if (AddRecord)
		{
		if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyAddDomain;
		else                     rt = DNSServiceDomainEnumerationReplyAddDomainDefault;
		}
	else
		{
		if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyRemoveDomain;
		else return;
		}

	LogOperation("%5d: DNSServiceDomainEnumeration(%##s) %##s %s",
		x->ClientMachPort, x->dom.qname.c, answer->rdata->u.name.c,
		!AddRecord ? "RemoveDomain" :
		question == &x->dom ? "AddDomain" : "AddDomainDefault");

	ConvertDomainNameToCString(&answer->rdata->u.name, buffer);
	status = DNSServiceDomainEnumerationReply_rpc(x->ClientMachPort, rt, buffer, 0, MDNS_MM_TIMEOUT);
	if (status == MACH_SEND_TIMED_OUT)
		AbortBlockedClient(x->ClientMachPort, "enumeration", x);
	}

mDNSexport kern_return_t provide_DNSServiceDomainEnumerationCreate_rpc(mach_port_t unusedserver, mach_port_t client,
	int regDom)
	{
	// Check client parameter
	(void)unusedserver; // Unused
	mStatus err = mStatus_NoError;
	const char *errormsg = "Unknown";
	if (client == (mach_port_t)-1)      { err = mStatus_Invalid; errormsg = "Client id -1 invalid";     goto fail; }
	if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }

	mDNS_DomainType dt1 = regDom ? mDNS_DomainTypeRegistration        : mDNS_DomainTypeBrowse;
	mDNS_DomainType dt2 = regDom ? mDNS_DomainTypeRegistrationDefault : mDNS_DomainTypeBrowseDefault;

	// Allocate memory, and handle failure
	DNSServiceDomainEnumeration *x = mallocL("DNSServiceDomainEnumeration", sizeof(*x));
	if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }

	// Set up object, and link into list
	x->ClientMachPort = client;
	x->next = DNSServiceDomainEnumerationList;
	DNSServiceDomainEnumerationList = x;

	verbosedebugf("%5d: Enumerate %s Domains", client, regDom ? "Registration" : "Browsing");

	// Do the operation
	err           = mDNS_GetDomains(&mDNSStorage, &x->dom, dt1, NULL, mDNSInterface_LocalOnly, DomainEnumFound, x);
	if (!err) err = mDNS_GetDomains(&mDNSStorage, &x->def, dt2, NULL, mDNSInterface_LocalOnly, DomainEnumFound, x);
	if (err) { AbortClient(client, x); errormsg = "mDNS_GetDomains"; goto fail; }

	// Succeeded: Wrap up and return
	LogOperation("%5d: DNSServiceDomainEnumeration(%##s) START", client, x->dom.qname.c);
	EnableDeathNotificationForClient(client, x);
	return(mStatus_NoError);

fail:
	LogMsg("%5d: DNSServiceDomainEnumeration(%d) failed: %s (%d)", client, regDom, errormsg, err);
	return(err);
	}

//*************************************************************************************************************
// Browse for services

mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
	{
	(void)m;		// Unused

	if (answer->rrtype != kDNSType_PTR)
		{ LogMsg("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer->rrtype); return; }

	domainlabel name;
	domainname type, domain;
	if (!DeconstructServiceName(&answer->rdata->u.name, &name, &type, &domain))
		{
		LogMsg("FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer",
			answer->name->c, answer->rdata->u.name.c);
		return;
		}

	DNSServiceBrowserResult *x = mallocL("DNSServiceBrowserResult", sizeof(*x));
	if (!x) { LogMsg("FoundInstance: Failed to allocate memory for result %##s", answer->rdata->u.name.c); return; }

	verbosedebugf("FoundInstance: %s %##s", AddRecord ? "Add" : "Rmv", answer->rdata->u.name.c);
	AssignDomainName(&x->result, &answer->rdata->u.name);
	if (AddRecord)
		 x->resultType = DNSServiceBrowserReplyAddInstance;
	else x->resultType = DNSServiceBrowserReplyRemoveInstance;
	x->next = NULL;

	DNSServiceBrowser *browser = (DNSServiceBrowser *)question->QuestionContext;
	DNSServiceBrowserResult **p = &browser->results;
	while (*p) p = &(*p)->next;
	*p = x;

	LogOperation("%5d: DNSServiceBrowse(%##s, %s) RESULT %s %s",
		browser->ClientMachPort, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "Add" : "Rmv", RRDisplayString(m, answer));
	}

mDNSlocal mStatus AddDomainToBrowser(DNSServiceBrowser *browser, const domainname *d)
	{
	mStatus err = mStatus_NoError;
	DNSServiceBrowserQuestion *ptr, *question = NULL;

	for (ptr = browser->qlist; ptr; ptr = ptr->next)
		{
		if (SameDomainName(&ptr->q.qname, d))
			{ debugf("Domain %##s already contained in browser", d->c); return mStatus_AlreadyRegistered; }
		}
	
	question = mallocL("DNSServiceBrowserQuestion", sizeof(DNSServiceBrowserQuestion));
	if (!question) { LogMsg("Error: malloc"); return mStatus_NoMemoryErr; }
	AssignDomainName(&question->domain, d);
	question->next = browser->qlist;
	LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", browser->ClientMachPort, browser->type.c, d->c);
	err = mDNS_StartBrowse(&mDNSStorage, &question->q, &browser->type, d, mDNSInterface_Any, mDNSfalse, FoundInstance, browser);
	if (!err)
		browser->qlist = question;
	else
		{
		LogMsg("Error: AddDomainToBrowser: mDNS_StartBrowse %d", err);
		freeL("DNSServiceBrowserQuestion", question);
		}
	return err;
	}

mDNSexport void machserver_automatic_browse_domain_changed(const domainname *d, mDNSBool add)
	{
	DNSServiceBrowser *ptr;
	for (ptr = DNSServiceBrowserList; ptr; ptr = ptr->next)
		{
		if (ptr->DefaultDomain)
			{
			if (add)
				{
				mStatus err = AddDomainToBrowser(ptr, d);
				if (err && err != mStatus_AlreadyRegistered) LogMsg("Default browse in domain %##s for client %5d failed. Continuing", d, ptr->ClientMachPort);
				}
			else
				{
				DNSServiceBrowserQuestion **q = &ptr->qlist;
				while (*q)
					{
					if (SameDomainName(&(*q)->domain, d))
						{
						DNSServiceBrowserQuestion *rem = *q;
						*q = (*q)->next;
						mDNS_StopQueryWithRemoves(&mDNSStorage, &rem->q);
						freeL("DNSServiceBrowserQuestion", rem);
						return;
						}
					q = &(*q)->next;
					}
			    LogMsg("Requested removal of default domain %##s not in client %5d's list", d->c, ptr->ClientMachPort);
				}
			}
		}
	}

mDNSexport kern_return_t provide_DNSServiceBrowserCreate_rpc(mach_port_t unusedserver, mach_port_t client,
	DNSCString regtype, DNSCString domain)
	{
	// Check client parameter
	(void)unusedserver;		// Unused
	mStatus err = mStatus_NoError;
	const char *errormsg = "Unknown";

	if (client == (mach_port_t)-1)      { err = mStatus_Invalid; errormsg = "Client id -1 invalid";     goto fail; }
	if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }

	// Check other parameters
	domainname t, d;
	t.c[0] = 0;
	mDNSs32 NumSubTypes = ChopSubTypes(regtype);	// Note: Modifies regtype string to remove trailing subtypes
	if (NumSubTypes < 0 || NumSubTypes > 1)               { errormsg = "Bad Service SubType"; goto badparam; }
	if (NumSubTypes == 1 && !AppendDNSNameString(&t, regtype + strlen(regtype) + 1))
	                                                      { errormsg = "Bad Service SubType"; goto badparam; }
	if (!regtype[0] || !AppendDNSNameString(&t, regtype)) { errormsg = "Illegal regtype";     goto badparam; }
	domainname temp;
	if (!MakeDomainNameFromDNSNameString(&temp, regtype)) { errormsg = "Illegal regtype";     goto badparam; }
	if (temp.c[0] > 15 && (!domain || domain[0] == 0)) domain = "local."; // For over-long service types, we only allow domain "local"

	// Allocate memory, and handle failure
	DNSServiceBrowser *x = mallocL("DNSServiceBrowser", sizeof(*x));
	if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }

	// Set up object, and link into list
	AssignDomainName(&x->type, &t);
	x->ClientMachPort = client;
	x->results = NULL;
	x->lastsuccess = 0;
	x->qlist = NULL;
	x->next = DNSServiceBrowserList;
	DNSServiceBrowserList = x;

	if (domain[0])
		{
		// Start browser for an explicit domain
		x->DefaultDomain = mDNSfalse;
		if (!MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Illegal domain";  goto badparam; }
		err = AddDomainToBrowser(x, &d);
		if (err) { AbortClient(client, x); errormsg = "AddDomainToBrowser"; goto fail; }
		}
	else
		{
		DNameListElem *sdPtr;
		// Start browser on all domains
		x->DefaultDomain = mDNStrue;
		if (!AutoBrowseDomains) { AbortClient(client, x); errormsg = "GetSearchDomainList"; goto fail; }
		for (sdPtr = AutoBrowseDomains; sdPtr; sdPtr = sdPtr->next)
			{
			err = AddDomainToBrowser(x, &sdPtr->name);
			if (err)
				{
				// only terminally bail if .local fails
				if (!SameDomainName(&localdomain, &sdPtr->name))
					LogMsg("Default browse in domain %##s failed. Continuing", sdPtr->name.c);
				else { AbortClient(client, x); errormsg = "AddDomainToBrowser"; goto fail; }
				}
			}
		}
	
	// Succeeded: Wrap up and return
	EnableDeathNotificationForClient(client, x);
	return(mStatus_NoError);

	badparam:
	err = mStatus_BadParamErr;
fail:
	LogMsg("%5d: DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%d)", client, regtype, domain, errormsg, err);
	return(err);
	}

//*************************************************************************************************************
// Resolve Service Info
	
mDNSlocal void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query)
	{
	kern_return_t status;
	DNSServiceResolver *x = (DNSServiceResolver *)query->ServiceInfoQueryContext;
	NetworkInterfaceInfoOSX *ifx = (NetworkInterfaceInfoOSX *)query->info->InterfaceID;
	if (query->info->InterfaceID == mDNSInterface_LocalOnly) ifx = mDNSNULL;
	struct sockaddr_storage interface;
	struct sockaddr_storage address;
	char cstring[1024];
	int i, pstrlen = query->info->TXTinfo[0];
	(void)m;		// Unused

	//debugf("FoundInstanceInfo %.4a %.4a %##s", &query->info->InterfaceAddr, &query->info->ip, &query->info->name);

	if (query->info->TXTlen > sizeof(cstring)) return;

	mDNSPlatformMemZero(&interface, sizeof(interface));
	mDNSPlatformMemZero(&address,   sizeof(address));

	if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv4)
		{
		struct sockaddr_in *s = (struct sockaddr_in*)&interface;
		s->sin_len         = sizeof(*s);
		s->sin_family      = AF_INET;
		s->sin_port        = 0;
		s->sin_addr.s_addr = ifx->ifinfo.ip.ip.v4.NotAnInteger;
		}
	else if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv6)
		{
		struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&interface;
		sin6->sin6_len       = sizeof(*sin6);
		sin6->sin6_family    = AF_INET6;
		sin6->sin6_flowinfo  = 0;
		sin6->sin6_port      = 0;
		sin6->sin6_addr      = *(struct in6_addr*)&ifx->ifinfo.ip.ip.v6;
		sin6->sin6_scope_id  = ifx->scope_id;
		}

	if (query->info->ip.type == mDNSAddrType_IPv4)
		{
		struct sockaddr_in *s = (struct sockaddr_in*)&address;
		s->sin_len         = sizeof(*s);
		s->sin_family      = AF_INET;
		s->sin_port        = query->info->port.NotAnInteger;
		s->sin_addr.s_addr = query->info->ip.ip.v4.NotAnInteger;
		}
	else
		{
		struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&address;
		sin6->sin6_len           = sizeof(*sin6);
		sin6->sin6_family        = AF_INET6;
		sin6->sin6_port          = query->info->port.NotAnInteger;
		sin6->sin6_flowinfo      = 0;
		sin6->sin6_addr          = *(struct in6_addr*)&query->info->ip.ip.v6;
		sin6->sin6_scope_id      = ifx ? ifx->scope_id : 0;
		}

	// The OS X DNSServiceResolverResolve() API is defined using a C-string,
	// but the mDNS_StartResolveService() call actually returns a packed block of P-strings.
	// Hence we have to convert the P-string(s) to a C-string before returning the result to the client.
	// ASCII-1 characters are used in the C-string as boundary markers,
	// to indicate the boundaries between the original constituent P-strings.
	for (i=1; i<query->info->TXTlen; i++)
		{
		if (--pstrlen >= 0)
			cstring[i-1] = query->info->TXTinfo[i];
		else
			{
			cstring[i-1] = 1;
			pstrlen = query->info->TXTinfo[i];
			}
		}
	cstring[i-1] = 0;		// Put the terminating NULL on the end

	LogOperation("%5d: DNSServiceResolver(%##s) -> %#a:%u", x->ClientMachPort,
		x->i.name.c, &query->info->ip, mDNSVal16(query->info->port));
	status = DNSServiceResolverReply_rpc(x->ClientMachPort,
		(char*)&interface, (char*)&address, cstring, 0, MDNS_MM_TIMEOUT);
	if (status == MACH_SEND_TIMED_OUT)
		AbortBlockedClient(x->ClientMachPort, "resolve", x);
	}

mDNSexport kern_return_t provide_DNSServiceResolverResolve_rpc(mach_port_t unusedserver, mach_port_t client,
	DNSCString name, DNSCString regtype, DNSCString domain)
	{
	// Check client parameter
	(void)unusedserver;		// Unused
	mStatus err = mStatus_NoError;
	const char *errormsg = "Unknown";
	if (client == (mach_port_t)-1)      { err = mStatus_Invalid; errormsg = "Client id -1 invalid";     goto fail; }
	if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }

	// Check other parameters
	domainlabel n;
	domainname t, d, srv;
	if (!name[0]    || !MakeDomainLabelFromLiteralString(&n, name))        { errormsg = "Bad Instance Name"; goto badparam; }
	if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype))      { errormsg = "Bad Service Type";  goto badparam; }
	if (!domain[0]  || !MakeDomainNameFromDNSNameString(&d, domain))       { errormsg = "Bad Domain";        goto badparam; }
	if (!ConstructServiceName(&srv, &n, &t, &d))                           { errormsg = "Bad Name";          goto badparam; }

	// Allocate memory, and handle failure
	DNSServiceResolver *x = mallocL("DNSServiceResolver", sizeof(*x));
	if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }

	// Set up object, and link into list
	x->ClientMachPort = client;
	x->i.InterfaceID = mDNSInterface_Any;
	x->i.name = srv;
	x->ReportTime = NonZeroTime(mDNS_TimeNow(&mDNSStorage) + 130 * mDNSPlatformOneSecond);
	x->next = DNSServiceResolverList;
	DNSServiceResolverList = x;

	// Do the operation
	LogOperation("%5d: DNSServiceResolve(%##s) START", client, x->i.name.c);
	err = mDNS_StartResolveService(&mDNSStorage, &x->q, &x->i, FoundInstanceInfo, x);
	if (err) { AbortClient(client, x); errormsg = "mDNS_StartResolveService"; goto fail; }

	// Succeeded: Wrap up and return
	EnableDeathNotificationForClient(client, x);
	return(mStatus_NoError);

badparam:
	err = mStatus_BadParamErr;
fail:
	LogMsg("%5d: DNSServiceResolve(\"%s\", \"%s\", \"%s\") failed: %s (%d)", client, name, regtype, domain, errormsg, err);
	return(err);
	}

//*************************************************************************************************************
// Registration

mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay)
	{
	m->p->NotifyUser = NonZeroTime(m->timenow + delay);
	}

mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const srs, mStatus result)
	{
	ServiceInstance *si = (ServiceInstance*)srs->ServiceContext;

	if (result == mStatus_NoError)
		{
		kern_return_t status;
		LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Registered", si->ClientMachPort, srs->RR_SRV.resrec.name->c, SRS_PORT(srs));
		status = DNSServiceRegistrationReply_rpc(si->ClientMachPort, result, MDNS_MM_TIMEOUT);
		if (status == MACH_SEND_TIMED_OUT)
			AbortBlockedClient(si->ClientMachPort, "registration success", si);
		if (si->autoname && CountPeerRegistrations(m, srs) == 0)
			RecordUpdatedNiceLabel(m, 0);	// Successfully got new name, tell user immediately
		}

	else if (result == mStatus_NameConflict)
		{
		LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Conflict", si->ClientMachPort, srs->RR_SRV.resrec.name->c, SRS_PORT(srs));
		// Note: By the time we get the mStatus_NameConflict message, the service is already deregistered
		// and the memory is free, so we don't have to wait for an mStatus_MemFree message as well.
		if (si->autoname && CountPeerRegistrations(m, srs) == 0)
			{
			// On conflict for an autoname service, rename and reregister *all* autoname services
			IncrementLabelSuffix(&m->nicelabel, mDNStrue);
			mDNS_ConfigChanged(m);
			}
		else if (si->autoname)
			{
            mDNS_RenameAndReregisterService(m, srs, mDNSNULL);
            return;
			}
		else
			{
			// If we get a name conflict, we tell the client about it, and then they are expected to dispose
			// of their registration in the usual way (which we will catch via client death notification).
			// If the Mach queue is full, we forcibly abort the client immediately.
			kern_return_t status = DNSServiceRegistrationReply_rpc(si->ClientMachPort, result, MDNS_MM_TIMEOUT);
			if (status == MACH_SEND_TIMED_OUT)
				AbortBlockedClient(si->ClientMachPort, "registration conflict", NULL);
			}
		}

	else if (result == mStatus_MemFree)
		{
		if (si->renameonmemfree)	// We intentionally terminated registration so we could re-register with new name
			{
			debugf("RegCallback renaming %#s to %#s", si->name.c, m->nicelabel.c);
			si->renameonmemfree = mDNSfalse;
			si->name            = m->nicelabel;
			mDNS_RenameAndReregisterService(m, srs, &si->name);
			}
		else
			{
			// SANITY CHECK: make sure service instance is no longer in any ServiceRegistration's list
			DNSServiceRegistration *r;
			for (r = DNSServiceRegistrationList; r; r = r->next)
				{
				ServiceInstance **sp = &r->regs;
				while (*sp)
					{
					if (*sp == si) { LogMsg("RegCallback: %##s Still in list; removing", srs->RR_SRV.resrec.name->c); *sp = (*sp)->next; break; }
					sp = &(*sp)->next;
					}
			    }
			// END SANITY CHECK
			FreeServiceInstance(si);
			}
		}

	else if (result != mStatus_NATTraversal)
		LogMsg("%5d: DNSServiceRegistration(%##s, %u) Unknown Result %d", si->ClientMachPort, srs->RR_SRV.resrec.name->c, SRS_PORT(srs), result);
	}

mDNSlocal mStatus AddServiceInstance(DNSServiceRegistration *x, const domainname *domain)
	{
	mStatus err = 0;
	ServiceInstance *si = NULL;
	AuthRecord *SubTypes = NULL;

	for (si = x->regs; si; si = si->next)
		{
		if (SameDomainName(&si->domain, domain))
			{ LogMsg("Requested addition of domain %##s already in list", domain->c); return mStatus_AlreadyRegistered; }
		}
	
	SubTypes = AllocateSubTypes(x->NumSubTypes, x->regtype);
	if (x->NumSubTypes && !SubTypes) return mStatus_NoMemoryErr;
	
	si = mallocL("ServiceInstance", sizeof(*si) - sizeof(RDataBody) + x->rdsize);
	if (!si) return mStatus_NoMemoryErr;

	si->ClientMachPort  = x->ClientMachPort;
	si->renameonmemfree = mDNSfalse;
	si->autoname        = x->autoname;
	si->name            = x->autoname ? mDNSStorage.nicelabel : x->name;
	si->domain          = *domain;

	err = mDNS_RegisterService(&mDNSStorage, &si->srs, &si->name, &x->type, domain, NULL,
		x->port, x->txtinfo, x->txt_len, SubTypes, x->NumSubTypes, mDNSInterface_Any, RegCallback, si);
	if (!err)
		{
		si->next = x->regs;
		x->regs = si;
		}
	else
		{
		LogMsg("Error %d for registration of service in domain %##s", err, domain->c);
		freeL("ServiceInstance", si);
		}
	return err;
	}

mDNSexport void machserver_automatic_registration_domain_changed(const domainname *d, mDNSBool add)
	{
	DNSServiceRegistration *reg;

	for (reg = DNSServiceRegistrationList; reg; reg = reg->next)
		{
		if (reg->DefaultDomain)
			{
			if (add)
				AddServiceInstance(reg, d);
			else
				{
				ServiceInstance **si = &reg->regs;
				while (*si)
					{
					if (SameDomainName(&(*si)->domain, d))
						{
						ServiceInstance *s = *si;
						*si = (*si)->next;
						if (mDNS_DeregisterService(&mDNSStorage, &s->srs)) FreeServiceInstance(s);  // only free memory synchronously on error
						break;
						}
					si = &(*si)->next;
					}
				if (!si) debugf("Requested removal of default domain %##s not in client %5d's list", d, reg->ClientMachPort); // normal if registration failed
				}
			}
		}
	}

mDNSexport kern_return_t provide_DNSServiceRegistrationCreate_rpc(mach_port_t unusedserver, mach_port_t client,
	DNSCString name, DNSCString regtype, DNSCString domain, IPPort IpPort, DNSCString txtRecord)
	{
	(void)unusedserver;		// Unused
	mStatus err = mStatus_NoError;
	const char *errormsg = "Unknown";

	// older versions of this code passed the port via mach IPC as an int.
	// we continue to pass it as 4 bytes to maintain binary compatibility,
	// but now ensure that the network byte order is preserved by using a struct
	mDNSIPPort port;
	port.b[0] = IpPort.bytes[2];
	port.b[1] = IpPort.bytes[3];

	if (client == (mach_port_t)-1)      { err = mStatus_Invalid; errormsg = "Client id -1 invalid";     goto fail; }
	if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }

    // Check for sub-types after the service type
	size_t reglen = strlen(regtype) + 1;
	if (reglen > MAX_ESCAPED_DOMAIN_NAME) { errormsg = "reglen too long"; goto badparam; }
	mDNSs32 NumSubTypes = ChopSubTypes(regtype);	// Note: Modifies regtype string to remove trailing subtypes
	if (NumSubTypes < 0) { errormsg = "Bad Service SubType"; goto badparam; }

	// Check other parameters
	domainlabel n;
	domainname t, d;
	domainname srv;
	if (!name[0]) n = mDNSStorage.nicelabel;
	else if (!MakeDomainLabelFromLiteralString(&n, name))                  { errormsg = "Bad Instance Name"; goto badparam; }
	if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype))      { errormsg = "Bad Service Type";  goto badparam; }
	if (!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) { errormsg = "Bad Domain";        goto badparam; }
	if (!ConstructServiceName(&srv, &n, &t, &d))                           { errormsg = "Bad Name";          goto badparam; }

	unsigned char txtinfo[1024] = "";
	unsigned int data_len = 0;
	unsigned int size = sizeof(RDataBody);
	unsigned char *pstring = &txtinfo[data_len];
	char *ptr = txtRecord;

	// The OS X DNSServiceRegistrationCreate() API is defined using a C-string,
	// but the mDNS_RegisterService() call actually requires a packed block of P-strings.
	// Hence we have to convert the C-string to a P-string.
	// ASCII-1 characters are allowed in the C-string as boundary markers,
	// so that a single C-string can be used to represent one or more P-strings.
	while (*ptr)
		{
		if (++data_len >= sizeof(txtinfo)) { errormsg = "TXT record too long"; goto badtxt; }
		if (*ptr == 1)		// If this is our boundary marker, start a new P-string
			{
			pstring = &txtinfo[data_len];
			pstring[0] = 0;
			ptr++;
			}
		else
			{
			if (pstring[0] == 255) { errormsg = "TXT record invalid (component longer than 255)"; goto badtxt; }
			pstring[++pstring[0]] = *ptr++;
			}
		}

	data_len++;
	if (size < data_len)
		size = data_len;

	// Some clients use mDNS for lightweight copy protection, registering a pseudo-service with
	// a port number of zero. When two instances of the protected client are allowed to run on one
	// machine, we don't want to see misleading "Bogus client" messages in syslog and the console.
	if (!mDNSIPPortIsZero(port))
		{
		int count = CountExistingRegistrations(&srv, port);
		if (count)
			LogMsg("%5d: Client application registered %d identical instances of service %##s port %u.",
				   client, count+1, srv.c, mDNSVal16(port));
		}
	
	// Allocate memory, and handle failure
	DNSServiceRegistration *x = mallocL("DNSServiceRegistration", sizeof(*x));
	if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
	mDNSPlatformMemZero(x, sizeof(*x));

	// Set up object, and link into list
	x->ClientMachPort = client;
	x->DefaultDomain = !domain[0];
	x->autoname = (!name[0]);
	x->rdsize = size;
	x->NumSubTypes = NumSubTypes;
	memcpy(x->regtype, regtype, reglen);
	x->name = n;
	x->type = t;
	x->port = port;
	memcpy(x->txtinfo, txtinfo, 1024);
	x->txt_len = data_len;
	x->NextRef = 0;
	x->regs = NULL;
	
	x->next = DNSServiceRegistrationList;
	DNSServiceRegistrationList = x;

	LogOperation("%5d: DNSServiceRegistration(\"%s\", \"%s\", \"%s\", %u) START",
		x->ClientMachPort, name, regtype, domain, mDNSVal16(port));

   	err = AddServiceInstance(x, &d);
	if (err) { AbortClient(client, x); errormsg = "mDNS_RegisterService"; goto fail; }  // bail if .local (or explicit domain) fails

	if (x->DefaultDomain)
		{
		DNameListElem *p;
		for (p = AutoRegistrationDomains; p; p = p->next)
			AddServiceInstance(x, &p->name);
		}

	// Succeeded: Wrap up and return
	EnableDeathNotificationForClient(client, x);
	return(mStatus_NoError);

badtxt:
	LogMsg("%5d: TXT record: %.100s...", client, txtRecord);
badparam:
	err = mStatus_BadParamErr;
fail:
	LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%d)",
		   client, name, regtype, domain, mDNSVal16(port), errormsg, err);
	return(err);
	}

mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result)
	{
	(void)m; // Unused
	if (result == mStatus_NoError)
		{
		if (!SameDomainLabelCS(m->p->userhostlabel.c, m->hostlabel.c))
			LogInfo("Local Hostname changed from \"%#s.local\" to \"%#s.local\"", m->p->userhostlabel.c, m->hostlabel.c);
		// One second pause in case we get a Computer Name update too -- don't want to alert the user twice
		RecordUpdatedNiceLabel(m, mDNSPlatformOneSecond);
		}
	else if (result == mStatus_NameConflict)
		{
		LogInfo("Local Hostname conflict for \"%#s.local\"", m->hostlabel.c);
		if (!m->p->HostNameConflict) m->p->HostNameConflict = NonZeroTime(m->timenow);
		else if (m->timenow - m->p->HostNameConflict > 60 * mDNSPlatformOneSecond)
			{
			// Tell the helper we've given up
			mDNSPreferencesSetName(kmDNSLocalHostName, &m->p->userhostlabel, NULL);
			}
		}
	else if (result == mStatus_GrowCache)
		{
		// Allocate another chunk of cache storage
		CacheEntity *storage = mallocL("mStatus_GrowCache", sizeof(CacheEntity) * RR_CACHE_SIZE);
		//LogInfo("GrowCache %d * %d = %d", sizeof(CacheEntity), RR_CACHE_SIZE, sizeof(CacheEntity) * RR_CACHE_SIZE);
		if (storage) mDNS_GrowCache(m, storage, RR_CACHE_SIZE);
		}
	else if (result == mStatus_ConfigChanged)
		{
		// Tell the helper we've seen a change in the labels.  It will dismiss the name conflict alert if needed.
		mDNSPreferencesSetName(kmDNSComputerName, &m->p->usernicelabel, &m->nicelabel);
		mDNSPreferencesSetName(kmDNSLocalHostName, &m->p->userhostlabel, &m->hostlabel);

		// First we check our list of old Mach-based registered services, to see if any need to be updated to a new name
		DNSServiceRegistration *r;
		for (r = DNSServiceRegistrationList; r; r=r->next)
			if (r->autoname)
				{
				ServiceInstance *si;
				for (si = r->regs; si; si = si->next)
					{
					if (!SameDomainLabelCS(si->name.c, m->nicelabel.c))
						{
						debugf("NetworkChanged renaming %##s to %#s", si->srs.RR_SRV.resrec.name->c, m->nicelabel.c);
						si->renameonmemfree = mDNStrue;
						if (mDNS_DeregisterService(m, &si->srs))	// If service deregistered already, we can re-register immediately
							RegCallback(m, &si->srs, mStatus_MemFree);
						}
					}
				}

		// Then we call into the UDS daemon code, to let it do the same
		udsserver_handle_configchange(m);
		}
	}

//*************************************************************************************************************
// Add / Update / Remove records from existing Registration

mDNSexport kern_return_t provide_DNSServiceRegistrationAddRecord_rpc(mach_port_t unusedserver, mach_port_t client,
	int type, const char *data, mach_msg_type_number_t data_len, uint32_t ttl, natural_t *reference)
	{
	// Check client parameter
	uint32_t id;
	mStatus err = mStatus_NoError;
	const char *errormsg = "Unknown";
	DNSServiceRegistration *x = DNSServiceRegistrationList;
	if (client == (mach_port_t)-1) { err = mStatus_Invalid;         errormsg = "Client id -1 invalid"; goto fail; }
	ServiceInstance *si;
	size_t size;
	(void)unusedserver;		// Unused
	while (x && x->ClientMachPort != client) x = x->next;
	if (!x)                        { err = mStatus_BadReferenceErr; errormsg = "No such client";       goto fail; }

	// Check other parameters
	if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; }
	if (data_len > sizeof(RDataBody)) size = data_len;
	else size = sizeof(RDataBody);
	
	id = x->NextRef++;
	*reference = (natural_t)id;
	for (si = x->regs; si; si = si->next)
		{
		// Allocate memory, and handle failure
		ExtraResourceRecord *extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size);
		if (!extra) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
		
		// Fill in type, length, and data of new record
		extra->r.resrec.rrtype = type;
		extra->r.rdatastorage.MaxRDLength = size;
		extra->r.resrec.rdlength          = data_len;
		memcpy(&extra->r.rdatastorage.u.data, data, data_len);

		// Do the operation
		LogOperation("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) REF %p",
					 client, si->srs.RR_SRV.resrec.name->c, type, data_len, extra);
		err = mDNS_AddRecordToService(&mDNSStorage, &si->srs, extra, &extra->r.rdatastorage, ttl);

		if (err)
			{
			freeL("Extra Resource Record", extra);
			errormsg = "mDNS_AddRecordToService";
			goto fail;
			}

		extra->ClientID = id;
		}

	return mStatus_NoError;

fail:
	LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%d)", client, x ? x->name.c : (mDNSu8*)"\x8""«NULL»", type, data_len, errormsg, err);
	return mStatus_UnknownErr;
	}

mDNSlocal void UpdateCallback(mDNS *const m, AuthRecord *const rr, RData *OldRData)
	{
	(void)m;		// Unused
	if (OldRData != &rr->rdatastorage)
		freeL("Old RData", OldRData);
	}

mDNSlocal mStatus UpdateRecord(ServiceRecordSet *srs, mach_port_t client, AuthRecord *rr, const char *data, mach_msg_type_number_t data_len, uint32_t ttl)
	{
    // Check client parameter
	mStatus err = mStatus_NoError;
	const char *errormsg = "Unknown";
	const domainname *name = (const domainname *)"";

	name = srs->RR_SRV.resrec.name;

	unsigned int size = sizeof(RDataBody);
    if (size < data_len)
		size = data_len;

	// Allocate memory, and handle failure
	RData *newrdata = mallocL("RData", sizeof(*newrdata) - sizeof(RDataBody) + size);
	if (!newrdata) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }

	// Fill in new length, and data
	newrdata->MaxRDLength = size;
	memcpy(&newrdata->u, data, data_len);
	
	// BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
	// since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
	// Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
	if (rr->resrec.rrtype == kDNSType_TXT && data_len == 0) { data_len = 1; newrdata->u.txt.c[0] = 0; }

	// Do the operation
	LogOperation("%5d: DNSServiceRegistrationUpdateRecord(%##s, new length %d)",
		client, srs->RR_SRV.resrec.name->c, data_len);

	err = mDNS_Update(&mDNSStorage, rr, ttl, data_len, newrdata, UpdateCallback);
	if (err)
		{
		errormsg = "mDNS_Update";
		freeL("RData", newrdata);
		return err;
		}
	return(mStatus_NoError);

fail:
	LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %d) failed: %s (%d)", client, name->c, data_len, errormsg, err);
	return(err);
	}

mDNSexport kern_return_t provide_DNSServiceRegistrationUpdateRecord_rpc(mach_port_t unusedserver, mach_port_t client,
		natural_t reference, const char *data, mach_msg_type_number_t data_len, uint32_t ttl)
   	{
    // Check client parameter
	mStatus err = mStatus_NoError;
	const char *errormsg = "Unknown";
	const domainname *name = (const domainname *)"";
	ServiceInstance *si;

	(void)unusedserver; // unused
    if (client == (mach_port_t)-1) { err = mStatus_Invalid;         errormsg = "Client id -1 invalid"; goto fail; }
	DNSServiceRegistration *x = DNSServiceRegistrationList;
	while (x && x->ClientMachPort != client) x = x->next;
	if (!x)                        { err = mStatus_BadReferenceErr; errormsg = "No such client";       goto fail; }

	// Check other parameters
	if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; }

	for (si = x->regs; si; si = si->next)
		{
		AuthRecord *r = NULL;

		// Find the record we're updating. NULL reference means update the primary TXT record
		if (!reference) r = &si->srs.RR_TXT;
		else
			{
			ExtraResourceRecord *ptr;
			for (ptr = si->srs.Extras; ptr; ptr = ptr->next)
				{
				if ((natural_t)ptr->ClientID == reference)
					{ r = &ptr->r; break; }
				}
			if (!r) { err = mStatus_BadReferenceErr; errormsg = "No such record"; goto fail; }
			}
		err = UpdateRecord(&si->srs, client, r, data, data_len, ttl);
		if (err) goto fail;  //!!!KRS this will cause failures for non-local defaults!
		}
					
	return mStatus_NoError;

fail:
	LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, %d) failed: %s (%d)", client, name->c, reference, data_len, errormsg, err);
	return(err);
	}

mDNSlocal mStatus RemoveRecord(ServiceRecordSet *srs, ExtraResourceRecord *extra, mach_port_t client)
	{
	const domainname *const name = srs->RR_SRV.resrec.name;
	mStatus err = mStatus_NoError;

	// Do the operation
	LogOperation("%5d: DNSServiceRegistrationRemoveRecord(%##s)", client, srs->RR_SRV.resrec.name->c);

	err = mDNS_RemoveRecordFromService(&mDNSStorage, srs, extra, FreeExtraRR, extra);
	if (err) LogMsg("%5d: DNSServiceRegistrationRemoveRecord (%##s) failed: %d", client, name->c, err);
	
	return err;
	}

mDNSexport kern_return_t provide_DNSServiceRegistrationRemoveRecord_rpc(mach_port_t unusedserver, mach_port_t client,
	natural_t reference)
	{
	// Check client parameter
	(void)unusedserver;		// Unused
	mStatus err = mStatus_NoError;
	const char *errormsg = "Unknown";
	if (client == (mach_port_t)-1) { err = mStatus_Invalid;         errormsg = "Client id -1 invalid"; goto fail; }
	DNSServiceRegistration *x = DNSServiceRegistrationList;
	ServiceInstance *si;

	while (x && x->ClientMachPort != client) x = x->next;
	if (!x)                        { err = mStatus_BadReferenceErr; errormsg = "No such client";       goto fail; }

	for (si = x->regs; si; si = si->next)
		{
		ExtraResourceRecord *e;
		for (e = si->srs.Extras; e; e = e->next)
			{
			if ((natural_t)e->ClientID == reference)
				{
				err = RemoveRecord(&si->srs, e, client);
				break;
				}
			}
		if (!e) { err = mStatus_BadReferenceErr; errormsg = "No such reference"; goto fail; }
		}

	return mStatus_NoError;

fail:
	LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%X) failed: %s (%d)", client, reference, errormsg, err);
	return(err);
	}

//*************************************************************************************************************
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark - Startup, shutdown, and supporting code
#endif

mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
	{
	mig_reply_error_t *request = msg;
	mig_reply_error_t *reply;
	mach_msg_return_t mr;
	int               options;
	(void)port;		// Unused
	(void)size;		// Unused
	(void)info;		// Unused

	KQueueLock(&mDNSStorage);
	
	/* allocate a reply buffer */
	reply = CFAllocatorAllocate(NULL, provide_DNSServiceDiscoveryRequest_subsystem.maxsize, 0);

	/* call the MiG server routine */
	(void) DNSServiceDiscoveryRequest_server(&request->Head, &reply->Head);

	if (!(reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && (reply->RetCode != KERN_SUCCESS))
		{
        if (reply->RetCode == MIG_NO_REPLY)
			{
            /*
             * This return code is a little tricky -- it appears that the
             * demux routine found an error of some sort, but since that
             * error would not normally get returned either to the local
             * user or the remote one, we pretend it's ok.
             */
            CFAllocatorDeallocate(NULL, reply);
            goto done;
			}

        /*
         * destroy any out-of-line data in the request buffer but don't destroy
         * the reply port right (since we need that to send an error message).
         */
        request->Head.msgh_remote_port = MACH_PORT_NULL;
        mach_msg_destroy(&request->Head);
		}

    if (reply->Head.msgh_remote_port == MACH_PORT_NULL)
		{
        /* no reply port, so destroy the reply */
        if (reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
            mach_msg_destroy(&reply->Head);
        CFAllocatorDeallocate(NULL, reply);
        goto done;
		}

    /*
     * send reply.
     *
     * We don't want to block indefinitely because the client
     * isn't receiving messages from the reply port.
     * If we have a send-once right for the reply port, then
     * this isn't a concern because the send won't block.
     * If we have a send right, we need to use MACH_SEND_TIMEOUT.
     * To avoid falling off the kernel's fast RPC path unnecessarily,
     * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
     */

    options = MACH_SEND_MSG;
    if (MACH_MSGH_BITS_REMOTE(reply->Head.msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE)
        options |= MACH_SEND_TIMEOUT;

    mr = mach_msg(&reply->Head,		/* msg */
		      options,			/* option */
		      reply->Head.msgh_size,	/* send_size */
		      0,			/* rcv_size */
		      MACH_PORT_NULL,		/* rcv_name */
		      MACH_MSG_TIMEOUT_NONE,	/* timeout */
		      MACH_PORT_NULL);		/* notify */

    /* Has a message error occurred? */
    switch (mr)
		{
        case MACH_SEND_INVALID_DEST:
        case MACH_SEND_TIMED_OUT:
            /* the reply can't be delivered, so destroy it */
            mach_msg_destroy(&reply->Head);
            break;

        default :
            /* Includes success case. */
            break;
		}

    CFAllocatorDeallocate(NULL, reply);
	
done:
	KQueueUnlock(&mDNSStorage, "Mach client event");
	}

mDNSlocal kern_return_t registerBootstrapService()
	{
	kern_return_t status;
	mach_port_t service_send_port, service_rcv_port;

	debugf("Registering Bootstrap Service");

	/*
	 * See if our service name is already registered and if we have privilege to check in.
	 */
	status = bootstrap_check_in(bootstrap_port, (char*)kmDNSBootstrapName, &service_rcv_port);
	if (status == KERN_SUCCESS)
		{
		/*
		 * If so, we must be a followup instance of an already defined server.  In that case,
		 * the bootstrap port we inherited from our parent is the server's privilege port, so set
		 * that in case we have to unregister later (which requires the privilege port).
		 */
		server_priv_port = bootstrap_port;
		restarting_via_mach_init = TRUE;
		}
	else if (status == BOOTSTRAP_UNKNOWN_SERVICE)
		{
		status = bootstrap_create_server(bootstrap_port, "/usr/sbin/mDNSResponder", getuid(),
			FALSE /* relaunch immediately, not on demand */, &server_priv_port);
		if (status != KERN_SUCCESS) return status;

		status = bootstrap_create_service(server_priv_port, (char*)kmDNSBootstrapName, &service_send_port);
		if (status != KERN_SUCCESS)
			{
			mach_port_deallocate(mach_task_self(), server_priv_port);
			return status;
			}

		status = bootstrap_check_in(server_priv_port, (char*)kmDNSBootstrapName, &service_rcv_port);
		if (status != KERN_SUCCESS)
			{
			mach_port_deallocate(mach_task_self(), server_priv_port);
			mach_port_deallocate(mach_task_self(), service_send_port);
			return status;
			}
		assert(service_send_port == service_rcv_port);
		}

	/*
	 * We have no intention of responding to requests on the service port.  We are not otherwise
	 * a Mach port-based service.  We are just using this mechanism for relaunch facilities.
	 * So, we can dispose of all the rights we have for the service port.  We don't destroy the
	 * send right for the server's privileged bootstrap port - in case we have to unregister later.
	 */
	mach_port_destroy(mach_task_self(), service_rcv_port);
	return status;
	}

mDNSlocal kern_return_t destroyBootstrapService()
	{
	debugf("Destroying Bootstrap Service");
	return bootstrap_register(server_priv_port, (char*)kmDNSBootstrapName, MACH_PORT_NULL);
	}

mDNSlocal void ExitCallback(int sig)
	{
	(void)sig; // Unused
	LogMsg("%s stopping", mDNSResponderVersionString);

	debugf("ExitCallback");
	if (!mDNS_DebugMode && !started_via_launchdaemon)
		destroyBootstrapService();

	debugf("ExitCallback: Aborting MIG clients");
	while (DNSServiceDomainEnumerationList)
		AbortClient(DNSServiceDomainEnumerationList->ClientMachPort, DNSServiceDomainEnumerationList);
	while (DNSServiceBrowserList)
		AbortClient(DNSServiceBrowserList          ->ClientMachPort, DNSServiceBrowserList);
	while (DNSServiceResolverList)
		AbortClient(DNSServiceResolverList         ->ClientMachPort, DNSServiceResolverList);
	while (DNSServiceRegistrationList)
		AbortClient(DNSServiceRegistrationList     ->ClientMachPort, DNSServiceRegistrationList);

	if (udsserver_exit() < 0) LogMsg("ExitCallback: udsserver_exit failed");

	debugf("ExitCallback: mDNS_StartExit");
	mDNS_StartExit(&mDNSStorage);
	}

// Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit
mDNSlocal void HandleSIG(int sig)
	{
	// WARNING: can't call syslog or fprintf from signal handler
	mach_msg_header_t header;
	header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
	header.msgh_remote_port = signal_port;
	header.msgh_local_port = MACH_PORT_NULL;
	header.msgh_size = sizeof(header);
	header.msgh_id = sig;
	if (mach_msg_send(&header) != MACH_MSG_SUCCESS)
		if (sig == SIGTERM || sig == SIGINT) exit(-1);
	}

mDNSlocal void CatchABRT(int sig)
	{
	// WARNING: can't call syslog or fprintf from signal handler
	// We want a CrashReporter stack trace so we can find out what library called abort()
	// So that we will crash, unblock all signals (that abort() may have blocked)
	sigset_t mask;
	sigfillset(&mask);
	sigprocmask(SIG_UNBLOCK, &mask, NULL);
	(void)sig;
	while(1) *(long*)0 = 0;
	}

mDNSlocal void INFOCallback(void)
	{
	mDNSs32 utc = mDNSPlatformUTC();
	DNSServiceDomainEnumeration *e;
	DNSServiceBrowser           *b;
	DNSServiceResolver          *l;
	DNSServiceRegistration      *r;
	NetworkInterfaceInfoOSX     *i;
	DNSServer *s;

	LogMsg("---- BEGIN STATE LOG ----");
	
	udsserver_info(&mDNSStorage);

	LogMsgNoIdent("--------- Mach Clients ---------");
	if (!DNSServiceDomainEnumerationList && !DNSServiceBrowserList && !DNSServiceResolverList && !DNSServiceRegistrationList)
		LogMsgNoIdent("<None>");
	else
		{
		for (e = DNSServiceDomainEnumerationList; e; e=e->next)
			LogMsgNoIdent("%5d: Mach DomainEnumeration   %##s", e->ClientMachPort, e->dom.qname.c);
	
		for (b = DNSServiceBrowserList; b; b=b->next)
			{
			DNSServiceBrowserQuestion *qptr;
			for (qptr = b->qlist; qptr; qptr = qptr->next)
				LogMsgNoIdent("%5d: Mach ServiceBrowse       %##s", b->ClientMachPort, qptr->q.qname.c);
			}
		for (l = DNSServiceResolverList; l; l=l->next)
			LogMsgNoIdent("%5d: Mach ServiceResolve      %##s", l->ClientMachPort, l->i.name.c);
	
		for (r = DNSServiceRegistrationList; r; r=r->next)
			{
			ServiceInstance *si;
			for (si = r->regs; si; si = si->next)
				LogMsgNoIdent("%5d: Mach ServiceInstance     %##s %u", si->ClientMachPort, si->srs.RR_SRV.resrec.name->c, mDNSVal16(si->srs.RR_SRV.resrec.rdata->u.srv.port));
			}
		}

	LogMsgNoIdent("----- KQSocketEventSources -----");
	if (!gEventSources) LogMsgNoIdent("<None>");
	else
		{
		KQSocketEventSource *k;
		for (k = gEventSources; k; k=k->next)
			LogMsgNoIdent("%3d %s", k->fd, k->kqs.KQtask);
		}

	LogMsgNoIdent("------ Network Interfaces ------");
	if (!mDNSStorage.p->InterfaceList) LogMsgNoIdent("<None>");
	else
		{
		for (i = mDNSStorage.p->InterfaceList; i; i = i->next)
			{
			// Allow six characters for interface name, for names like "vmnet8"
			if (!i->Exists)
				LogMsgNoIdent("%p %s %-6s(%lu) %.6a %.6a %#-14a dormant for %d seconds",
					i->ifinfo.InterfaceID,
					i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifinfo.ifname, i->scope_id, &i->ifinfo.MAC, &i->BSSID,
					&i->ifinfo.ip, utc - i->LastSeen);
			else
				{
				const CacheRecord *sps[3];
				FindSPSInCache(&mDNSStorage, &i->ifinfo.NetWakeBrowse, sps);
				LogMsgNoIdent("%p %s %-6s(%lu) %.6a %.6a %s %s %-15.4a %s %s %s %s %#a",
					i->ifinfo.InterfaceID,
					i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifinfo.ifname, i->scope_id, &i->ifinfo.MAC, &i->BSSID,
					i->ifinfo.InterfaceActive ? "Active" : "      ",
					i->ifinfo.IPv4Available ? "v4" : "  ",
					i->ifinfo.IPv4Available ? (mDNSv4Addr*)&i->ifa_v4addr : &zerov4Addr,
					i->ifinfo.IPv6Available ? "v6" : "  ",
					i->ifinfo.Advertise ? "⊙" : " ",
					i->ifinfo.McastTxRx ? "⇆" : " ",
					!(i->ifinfo.InterfaceActive && i->ifinfo.NetWake) ? " " : !sps[0] ? "☼" : "☀",
					&i->ifinfo.ip);

				if (sps[0]) LogMsgNoIdent("  %13d %#s", SPSMetric(sps[0]->resrec.rdata->u.name.c), sps[0]->resrec.rdata->u.name.c);
				if (sps[1]) LogMsgNoIdent("  %13d %#s", SPSMetric(sps[1]->resrec.rdata->u.name.c), sps[1]->resrec.rdata->u.name.c);
				if (sps[2]) LogMsgNoIdent("  %13d %#s", SPSMetric(sps[2]->resrec.rdata->u.name.c), sps[2]->resrec.rdata->u.name.c);
				}
			}
		}

	LogMsgNoIdent("--------- DNS Servers ----------");
	if (!mDNSStorage.DNSServers) LogMsgNoIdent("<None>");
	else
		{
		for (s = mDNSStorage.DNSServers; s; s = s->next)
			{
			NetworkInterfaceInfoOSX *ifx = (NetworkInterfaceInfoOSX *)s->interface;
			LogMsgNoIdent("DNS Server %##s %s%s%#a:%d %s",
				s->domain.c, ifx ? ifx->ifinfo.ifname : "", ifx ? " " : "", &s->addr, mDNSVal16(s->port),
				s->teststate == DNSServer_Untested ? "(Untested)" :
				s->teststate == DNSServer_Passed   ? ""           :
				s->teststate == DNSServer_Failed   ? "(Failed)"   :
				s->teststate == DNSServer_Disabled ? "(Disabled)" : "(Unknown state)");
			}
		}

	mDNSs32 now = mDNS_TimeNow(&mDNSStorage);
	LogMsgNoIdent("Timenow 0x%08lX (%d)", (mDNSu32)now, now);

	LogMsg("----  END STATE LOG  ----");
	}

mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
	{
	(void)port;		// Unused
	(void)size;		// Unused
	(void)info;		// Unused
	mach_msg_header_t *msg_header = (mach_msg_header_t *)msg;
	mDNS *const m = &mDNSStorage;

	// We're running on the CFRunLoop (Mach port) thread, not the kqueue thread, so we need to grab the KQueueLock before proceeding
	KQueueLock(m);
	switch(msg_header->msgh_id)
		{
		case SIGHUP:	{
						mDNSu32 slot;
						CacheGroup *cg;
						CacheRecord *rr;
						LogMsg("SIGHUP: Purge cache");
						mDNS_Lock(m);
						FORALL_CACHERECORDS(slot, cg, rr) mDNS_PurgeCacheResourceRecord(m, rr);
						// Restart unicast and multicast queries
						mDNSCoreRestartQueries(m);
						mDNS_Unlock(m);
						} break;
		case SIGINT:
		case SIGTERM:	ExitCallback(msg_header->msgh_id); break;
		case SIGINFO:	INFOCallback(); break;
		case SIGUSR1:	mDNS_LoggingEnabled = mDNS_LoggingEnabled ? 0 : 1;
						LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled ? "Enabled" : "Disabled");
						WatchDogReportingThreshold = mDNS_LoggingEnabled ? 50 : 250;
						break;
		case SIGUSR2:	mDNS_PacketLoggingEnabled = mDNS_PacketLoggingEnabled ? 0 : 1;
						LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled");
						break;
		default: LogMsg("SignalCallback: Unknown signal %d", msg_header->msgh_id); break;
		}
	KQueueUnlock(m, "Unix Signal");
	}

// On 10.2 the MachServerName is DNSServiceDiscoveryServer
// On 10.3 and later, the MachServerName is com.apple.mDNSResponder

mDNSlocal kern_return_t mDNSDaemonInitialize(void)
	{
	mStatus            err;
	CFMachPortRef      s_port;

	// If launchd already created our Mach port for us, then use that, else we create a new one of our own
	if (m_port != MACH_PORT_NULL)
		s_port = CFMachPortCreateWithPort(NULL, m_port, DNSserverCallback, NULL, NULL);
	else
		{
		s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL);
		m_port = CFMachPortGetPort(s_port);
		char *MachServerName = OSXVers < OSXVers_10_3_Panther ? "DNSServiceDiscoveryServer" : "com.apple.mDNSResponder";
		kern_return_t status = bootstrap_register(bootstrap_port, MachServerName, m_port);
	
		if (status)
			{
			if (status == 1103)
				LogMsg("bootstrap_register() failed: A copy of the daemon is apparently already running");
			else
				LogMsg("bootstrap_register() failed: %s %d", mach_error_string(status), status);
			return(status);
			}
		}

	CFMachPortRef      d_port = CFMachPortCreate(NULL, ClientDeathCallback, NULL, NULL);
	CFMachPortRef      i_port = CFMachPortCreate(NULL, SignalCallback, NULL, NULL);
	CFRunLoopSourceRef d_rls  = CFMachPortCreateRunLoopSource(NULL, d_port, 0);
	CFRunLoopSourceRef s_rls  = CFMachPortCreateRunLoopSource(NULL, s_port, 0);
	CFRunLoopSourceRef i_rls  = CFMachPortCreateRunLoopSource(NULL, i_port, 0);

	err = mDNS_Init(&mDNSStorage, &PlatformStorage,
		rrcachestorage, RR_CACHE_SIZE,
		advertise,
		mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext);

	if (err) { LogMsg("Daemon start: mDNS_Init failed %d", err); return(err); }

	client_death_port = CFMachPortGetPort(d_port);
	signal_port       = CFMachPortGetPort(i_port);

	CFRunLoopAddSource(PlatformStorage.CFRunLoop, d_rls, kCFRunLoopDefaultMode);
	CFRunLoopAddSource(PlatformStorage.CFRunLoop, s_rls, kCFRunLoopDefaultMode);
	CFRunLoopAddSource(PlatformStorage.CFRunLoop, i_rls, kCFRunLoopDefaultMode);
	CFRelease(d_rls);
	CFRelease(s_rls);
	CFRelease(i_rls);
	if (mDNS_DebugMode) printf("Service registered with Mach Port %d\n", m_port);
	return(err);
	}

mDNSlocal mDNSs32 mDNSDaemonIdle(mDNS *const m)
	{
	mDNSs32 now = mDNS_TimeNow(m);

	// 1. If we have network change events to handle, do them FIRST, before calling mDNS_Execute()
	// Detailed reason:
	// mDNSMacOSXNetworkChanged() currently closes and re-opens its sockets. If there are received packets waiting, they are lost.
	// mDNS_Execute() generates packets, including multicasts that are looped back to ourself.
	// If we call mDNS_Execute() first, and generate packets, and then call mDNSMacOSXNetworkChanged() immediately afterwards
	// we then systematically lose our own looped-back packets.
	if (m->p->NetworkChanged && now - m->p->NetworkChanged >= 0) mDNSMacOSXNetworkChanged(m);

	if (m->p->RequestReSleep && now - m->p->RequestReSleep >= 0) { m->p->RequestReSleep = 0; mDNSPowerRequest(0, 0); }

	// KeyChain frequently fails to notify clients of change events. To work around this
	// we set a timer and periodically poll to detect if any changes have occurred.
	// Without this Back To My Mac just does't work for a large number of users.
	// See <rdar://problem/5124399> Not getting Keychain Changed events when enabling BTMM
	if (m->p->KeyChainBugTimer && now - m->p->KeyChainBugTimer >= 0)
		{
		m->p->KeyChainBugInterval *= 2;
		m->p->KeyChainBugTimer = NonZeroTime(now + m->p->KeyChainBugInterval);
		if (m->p->KeyChainBugInterval > 2 * mDNSPlatformOneSecond) m->p->KeyChainBugTimer = 0;
		mDNS_Lock(m);
		SetDomainSecrets(m);
		mDNS_Unlock(m);
		}

	// 2. Call mDNS_Execute() to let mDNSCore do what it needs to do
	mDNSs32 nextevent = mDNS_Execute(m);

	if (m->p->NetworkChanged)
		if (nextevent - m->p->NetworkChanged > 0)
			nextevent = m->p->NetworkChanged;

	if (m->p->KeyChainBugTimer)
		if (nextevent - m->p->KeyChainBugTimer > 0)
			nextevent = m->p->KeyChainBugTimer;

	if (m->p->RequestReSleep)
		if (nextevent - m->p->RequestReSleep > 0)
			nextevent = m->p->RequestReSleep;

	// 3. Deliver any waiting browse messages to clients
	DNSServiceBrowser *b = DNSServiceBrowserList;

	while (b)
		{
		// Note: Need to advance b to the next element BEFORE we call DeliverInstance(), because in the
		// event that the client Mach queue overflows, DeliverInstance() will call AbortBlockedClient()
		// and that will cause the DNSServiceBrowser object's memory to be freed before it returns
		DNSServiceBrowser *x = b;
		b = b->next;
		if (x->results)			// Try to deliver the list of results
			{
			while (x->results)
				{
				DNSServiceBrowserResult *const r = x->results;
				domainlabel name;
				domainname type, domain;
				DeconstructServiceName(&r->result, &name, &type, &domain);	// Don't need to check result; already validated in FoundInstance()
				char cname[MAX_DOMAIN_LABEL+1];			// Unescaped name: up to 63 bytes plus C-string terminating NULL.
				char ctype[MAX_ESCAPED_DOMAIN_NAME];
				char cdom [MAX_ESCAPED_DOMAIN_NAME];
				ConvertDomainLabelToCString_unescaped(&name, cname);
				ConvertDomainNameToCString(&type, ctype);
				ConvertDomainNameToCString(&domain, cdom);
				DNSServiceDiscoveryReplyFlags flags = (r->next) ? DNSServiceDiscoverReplyFlagsMoreComing : 0;
				kern_return_t status = DNSServiceBrowserReply_rpc(x->ClientMachPort, r->resultType, cname, ctype, cdom, flags, 1);
				// If we failed to send the mach message, try again in one second
				if (status == MACH_SEND_TIMED_OUT)
					{
					if (nextevent - now > mDNSPlatformOneSecond)
						nextevent = now + mDNSPlatformOneSecond;
					break;
					}
				else
					{
					x->lastsuccess = now;
					x->results = x->results->next;
					freeL("DNSServiceBrowserResult", r);
					}
				}
			// If this client hasn't read a single message in the last 60 seconds, abort it
			if (now - x->lastsuccess >= 60 * mDNSPlatformOneSecond)
				AbortBlockedClient(x->ClientMachPort, "browse", x);
			}
		}

	DNSServiceResolver *l;
	for (l = DNSServiceResolverList; l; l=l->next)
		if (l->ReportTime && now - l->ReportTime >= 0)
			{
			l->ReportTime = 0;
			LogMsgNoIdent("Client application bug: DNSServiceResolver(%##s) active for over two minutes. "
				"This places considerable burden on the network.", l->i.name.c);
			}

	if (m->p->NotifyUser)
		{
		if (m->p->NotifyUser - now < 0)
			{
			if (!SameDomainLabelCS(m->p->usernicelabel.c, m->nicelabel.c))
				{
				LogMsg("Name Conflict: Updated Computer Name from \"%#s\" to \"%#s\"", m->p->usernicelabel.c, m->nicelabel.c);
				mDNSPreferencesSetName(kmDNSComputerName, &m->p->usernicelabel, &m->nicelabel);
				m->p->usernicelabel = m->nicelabel;
				}
			if (!SameDomainLabelCS(m->p->userhostlabel.c, m->hostlabel.c))
				{
				LogMsg("Name Conflict: Updated Local Hostname from \"%#s.local\" to \"%#s.local\"", m->p->userhostlabel.c, m->hostlabel.c);
				mDNSPreferencesSetName(kmDNSLocalHostName, &m->p->userhostlabel, &m->hostlabel);
				m->p->HostNameConflict = 0;	// Clear our indicator, now name change has been successful
				m->p->userhostlabel = m->hostlabel;
				}
			m->p->NotifyUser = 0;
			}
		else
			if (nextevent - m->p->NotifyUser > 0)
				nextevent = m->p->NotifyUser;
		}

	return(nextevent);
	}

// Right now we consider *ALL* of our DHCP leases
// It might make sense to be a bit more selective and only consider the leases on interfaces
// (a) that are capable and enabled for wake-on-LAN, and
// (b) where we have found (and successfully registered with) a Sleep Proxy
// If we can't be woken for traffic on a given interface, then why keep waking to renew its lease?
mDNSlocal mDNSu32 DHCPWakeTime(void)
	{
	mDNSu32 e = 24 * 3600;		// Maximum maintenance wake interval is 24 hours
	const CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
	if (!now) LogMsg("DHCPWakeTime: CFAbsoluteTimeGetCurrent failed");
	else
		{
		const SCPreferencesRef prefs = SCPreferencesCreate(NULL, CFSTR("mDNSResponder:DHCPWakeTime"), NULL);
		if (!prefs) LogMsg("DHCPWakeTime: SCPreferencesCreate failed");
		else
			{
			const SCNetworkSetRef currentset = SCNetworkSetCopyCurrent(prefs);
			if (!currentset) LogMsg("DHCPWakeTime: SCNetworkSetCopyCurrent failed");
			else
				{
				const CFArrayRef services = SCNetworkSetCopyServices(currentset);
				if (!services) LogMsg("DHCPWakeTime: SCNetworkSetCopyServices failed");
				else
					{
					int i;
					for (i = 0; i < CFArrayGetCount(services); i++)
						{
						const SCNetworkServiceRef service = CFArrayGetValueAtIndex(services, i);
						if (!service) LogMsg("DHCPWakeTime: CFArrayGetValueAtIndex %d failed", i);
						else
							{
							const CFStringRef serviceid = SCNetworkServiceGetServiceID(service);
							if (!serviceid) LogMsg("DHCPWakeTime: SCNetworkServiceGetServiceID %d failed", i);
							else
								{
								// Note: It's normal for this call to return NULL, for interfaces not using DHCP
								const CFDictionaryRef dhcp = SCDynamicStoreCopyDHCPInfo(NULL, serviceid);
								if (dhcp)
									{
									const CFDateRef start = DHCPInfoGetLeaseStartTime(dhcp);
									const CFDataRef lease = DHCPInfoGetOptionData(dhcp, 51);	// Option 51 = IP Address Lease Time
									if (!start || !lease || CFDataGetLength(lease) < 4)
										LogMsg("DHCPWakeTime: SCDynamicStoreCopyDHCPInfo index %d failed "
											"CFDateRef start %p CFDataRef lease %p CFDataGetLength(lease) %d",
											i, start, lease, lease ? CFDataGetLength(lease) : 0);
									else
										{
										const UInt8 *d = CFDataGetBytePtr(lease);
										if (!d) LogMsg("DHCPWakeTime: CFDataGetBytePtr %d failed", i);
										else
											{
											const mDNSu32 elapsed   = now - CFDateGetAbsoluteTime(start);
											const mDNSu32 lifetime  = (mDNSs32) ((mDNSs32)d[0] << 24 | (mDNSs32)d[1] << 16 | (mDNSs32)d[2] << 8 | d[3]);
											const mDNSu32 remaining = lifetime - elapsed;
											const mDNSu32 wake      = remaining > 60 ? remaining - remaining/10 : 54;	// Wake at 90% of the lease time
											LogSPS("DHCP Address Lease Elapsed %6u Lifetime %6u Remaining %6u Wake %6u", elapsed, lifetime, remaining, wake);
											if (e > wake) e = wake;
											}
										}
									CFRelease(dhcp);
									}
								}
							}
						}
					CFRelease(services);
					}
				CFRelease(currentset);
				}
			CFRelease(prefs);
			}
		}
	return(e);
	}

// We deliberately schedule our wakeup for halfway between when we'd *like* it and when we *need* it.
// For example, if our DHCP lease expires in two hours, we'll typically renew it at the halfway point, after one hour.
// If we scheduled our wakeup for the one-hour renewal time, that might be just seconds from now, and sleeping
// for a few seconds and then waking again is silly and annoying.
// If we scheduled our wakeup for the two-hour expiry time, and we were slow to wake, we might lose our lease.
// Scheduling our wakeup for halfway in between -- 90 minutes -- avoids short wakeups while still
// allowing us an adequate safety margin to renew our lease before we lose it.

mDNSlocal mDNSBool AllowSleepNow(mDNS *const m, mDNSs32 now)
	{
	mDNSBool ready = mDNSCoreReadyForSleep(m);
	if (m->SleepState && !ready && now - m->SleepLimit < 0) return(mDNSfalse);

	m->p->WakeAtUTC = 0;
	int result = kIOReturnSuccess;
	CFDictionaryRef opts = NULL;

	// If the sleep request was cancelled, and we're no longer planning to sleep, don't need to
	// do the stuff below, but we *DO* still need to acknowledge the sleep message we received.
	if (!m->SleepState)
		LogMsg("AllowSleepNow: Sleep request was canceled with %d ticks remaining", m->SleepLimit - now);
	else
		{
		if (!m->SystemWakeOnLANEnabled || !mDNSCoreHaveAdvertisedMulticastServices(m))
			LogSPS("AllowSleepNow: Not scheduling wakeup: SystemWakeOnLAN %s enabled; %s advertised services",
				m->SystemWakeOnLANEnabled                  ? "is" : "not",
				mDNSCoreHaveAdvertisedMulticastServices(m) ? "have" : "no");
		else
			{
			mDNSs32 dhcp = DHCPWakeTime();
			LogSPS("ComputeWakeTime: DHCP Wake %d", dhcp);
			mDNSs32 interval = mDNSCoreIntervalToNextWake(m, now) / mDNSPlatformOneSecond;
			if (interval > dhcp) interval = dhcp;
	
			// If we're not ready to sleep (failed to register with Sleep Proxy, maybe because of
			// transient network problem) then schedule a wakeup in one hour to try again. Otherwise,
			// a single SPS failure could result in a remote machine falling permanently asleep, requiring
			// someone to go to the machine in person to wake it up again, which would be unacceptable.
			if (!ready && interval > 3600) interval = 3600;
	
			//interval = 48; // For testing
	
#ifdef kIOPMAcknowledgmentOptionSystemCapabilityRequirements
			if (m->p->IOPMConnection)	// If lightweight-wake capability is available, use that
				{
				const CFDateRef WakeDate = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent() + interval);
				if (!WakeDate) LogMsg("ScheduleNextWake: CFDateCreate failed");
				else
					{
					const mDNSs32     reqs         = kIOPMSystemPowerStateCapabilityNetwork;
					const CFNumberRef Requirements = CFNumberCreate(NULL, kCFNumberSInt32Type, &reqs);
					if (!Requirements) LogMsg("ScheduleNextWake: CFNumberCreate failed");
					else
						{
						const void *OptionKeys[2] = { CFSTR("WakeDate"), CFSTR("Requirements") };
						const void *OptionVals[2] = {        WakeDate,          Requirements   };
						opts = CFDictionaryCreate(NULL, (void*)OptionKeys, (void*)OptionVals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
						if (!opts) LogMsg("ScheduleNextWake: CFDictionaryCreate failed");
						CFRelease(Requirements);
						}
					CFRelease(WakeDate);
					}
				LogSPS("AllowSleepNow: Will request lightweight wakeup in %d seconds", interval);
				}
			else 						// else schedule the wakeup using the old API instead to
#endif
				{
				// If we wake within +/- 30 seconds of our requested time we'll assume the system woke for us,
				// so we should put it back to sleep. To avoid frustrating the user, we always request at least
				// 60 seconds sleep, so if they immediately re-wake the system within seconds of it going to sleep,
				// we then shouldn't hit our 30-second window, and we won't attempt to re-sleep the machine.
				if (interval < 60) interval = 60;
	
				result = mDNSPowerRequest(1, interval);
	
				if (result == kIOReturnNotReady)
					{
					LogMsg("Requested wakeup in %d seconds unsuccessful; retrying with longer intervals", interval);
					// IOPMSchedulePowerEvent fails with kIOReturnNotReady (-536870184/0xe00002d8) if the
					// requested wake time is "too soon", but there's no API to find out what constitutes
					// "too soon" on any given OS/hardware combination, so if we get kIOReturnNotReady
					// we just have to iterate with successively longer intervals until it doesn't fail.
					// Additionally, if our power request is deemed "too soon" for the machine to get to
					// sleep and wake back up again, we attempt to cancel the sleep request, since the
					// implication is that the system won't manage to be awake again at the time we need it.
					do
						{
						interval += (interval < 20) ? 1 : ((interval+3) / 4);
						result = mDNSPowerRequest(1, interval);
						}
					while (result == kIOReturnNotReady);
					}
	
				if (result) LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful: %d %X", interval, result, result);
				else LogSPS("AllowSleepNow: Requested wakeup in %d seconds", interval);
				m->p->WakeAtUTC = mDNSPlatformUTC() + interval;
				}
			}
	
		// Clear our interface list to empty state, ready to go to sleep
		// As a side effect of doing this, we'll also cancel any outstanding SPS Resolve calls that didn't complete
		m->SleepState = SleepState_Sleeping;
		mDNSMacOSXNetworkChanged(m);
		}

	LogSPS("AllowSleepNow: %s(%lX) %s at %ld (%d ticks remaining)",
#ifdef kIOPMAcknowledgmentOptionSystemCapabilityRequirements
		(m->p->IOPMConnection) ? "IOPMConnectionAcknowledgeEventWithOptions" :
#endif
		(result == kIOReturnSuccess) ? "IOAllowPowerChange" : "IOCancelPowerChange",
		m->p->SleepCookie, ready ? "ready for sleep" : "giving up", now, m->SleepLimit - now);

	m->SleepLimit = 0;	// Don't clear m->SleepLimit until after we've logged it above

#ifdef kIOPMAcknowledgmentOptionSystemCapabilityRequirements
	if (m->p->IOPMConnection) IOPMConnectionAcknowledgeEventWithOptions(m->p->IOPMConnection, m->p->SleepCookie, opts);
	else
#endif
	if (result == kIOReturnSuccess) IOAllowPowerChange (m->p->PowerConnection, m->p->SleepCookie);
	else                            IOCancelPowerChange(m->p->PowerConnection, m->p->SleepCookie);

	if (opts) CFRelease(opts);
	return(mDNStrue);
	}

mDNSlocal void KQWokenFlushBytes(int fd, __unused short filter, __unused void *context)
	{
	// Read all of the bytes so we won't wake again.
	char    buffer[100];
	while (recv(fd, buffer, sizeof(buffer), MSG_DONTWAIT) > 0) continue;
	}

mDNSlocal void * KQueueLoop(void *m_param)
	{
	mDNS            *m = m_param;
	int             numevents = 0;

#if USE_SELECT_WITH_KQUEUEFD
	fd_set          readfds;
	FD_ZERO(&readfds);
	const int multiplier = 1000000    / mDNSPlatformOneSecond;
#else
	const int multiplier = 1000000000 / mDNSPlatformOneSecond;
#endif
	
	pthread_mutex_lock(&PlatformStorage.BigMutex);
	LogInfo("Starting time value 0x%08lX (%ld)", (mDNSu32)mDNSStorage.timenow_last, mDNSStorage.timenow_last);
	
	// This is the main work loop:
	// (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time
	// (2) Then we make sure we've delivered all waiting browse messages to our clients
	// (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner
	// (4) On wakeup we first process *all* events
	// (5) then when no more events remain, we go back to (1) to finish off any deferred work and do it all again
	for ( ; ; )
		{
		#define kEventsToReadAtOnce 1
		struct kevent new_events[kEventsToReadAtOnce];

		// Run mDNS_Execute to find out the time we next need to wake up
		mDNSs32 start          = mDNSPlatformRawTime();
		mDNSs32 nextTimerEvent = udsserver_idle(mDNSDaemonIdle(m));
		mDNSs32 end            = mDNSPlatformRawTime();
		if (end - start >= WatchDogReportingThreshold)
			LogInfo("WARNING: Idle task took %dms to complete", end - start);

		mDNSs32 now = mDNS_TimeNow(m);

		if (m->ShutdownTime)
			{
			if (mDNSStorage.ResourceRecords)
				{
				LogInfo("Cannot exit yet; Resource Record still exists: %s", ARDisplayString(m, mDNSStorage.ResourceRecords));
				if (mDNS_LoggingEnabled) usleep(10000);		// Sleep 10ms so that we don't flood syslog with too many messages
				}
			if (mDNSStorage.ServiceRegistrations)
				LogInfo("Cannot exit yet; ServiceRegistrations still exists: %s", ARDisplayString(m, &mDNSStorage.ServiceRegistrations->RR_SRV));
			if (mDNS_ExitNow(m, now))
				{
				if (!mDNSStorage.ResourceRecords && !mDNSStorage.ServiceRegistrations)
					safe_vproc_transaction_end();
				LogInfo("mDNS_FinalExit");
				mDNS_FinalExit(&mDNSStorage);
				usleep(1000);		// Little 1ms pause before exiting, so we don't lose our final syslog messages
				exit(0);
				}
			if (nextTimerEvent - m->ShutdownTime >= 0)
				nextTimerEvent = m->ShutdownTime;
			}

		if (m->SleepLimit)
			if (!AllowSleepNow(m, now))
				if (nextTimerEvent - m->SleepLimit >= 0)
					nextTimerEvent = m->SleepLimit;

		// Convert absolute wakeup time to a relative time from now
		mDNSs32 ticks = nextTimerEvent - now;
		if (ticks < 1) ticks = 1;
		
		static mDNSs32 RepeatedBusy = 0;	// Debugging sanity check, to guard against CPU spins
		if (ticks > 1)
			RepeatedBusy = 0;
		else
			{
			ticks = 1;
			if (++RepeatedBusy >= mDNSPlatformOneSecond) { ShowTaskSchedulingError(&mDNSStorage); RepeatedBusy = 0; }
			}

		verbosedebugf("KQueueLoop: Handled %d events; now sleeping for %d ticks", numevents, ticks);
		numevents = 0;

		// Release the lock, and sleep until:
		// 1. Something interesting happens like a packet arriving, or
		// 2. The other thread writes a byte to WakeKQueueLoopFD to poke us and make us wake up, or
		// 3. The timeout expires
		pthread_mutex_unlock(&PlatformStorage.BigMutex);

#if USE_SELECT_WITH_KQUEUEFD
		struct timeval timeout;
		timeout.tv_sec = ticks / mDNSPlatformOneSecond;
		timeout.tv_usec = (ticks % mDNSPlatformOneSecond) * multiplier;
		FD_SET(KQueueFD, &readfds);
		if (select(KQueueFD+1, &readfds, NULL, NULL, &timeout) < 0)
			{ LogMsg("select(%d) failed errno %d (%s)", KQueueFD, errno, strerror(errno)); sleep(1); }
#else
		struct timespec timeout;
		timeout.tv_sec = ticks / mDNSPlatformOneSecond;
		timeout.tv_nsec = (ticks % mDNSPlatformOneSecond) * multiplier;
		// In my opinion, you ought to be able to call kevent() with nevents set to zero,
		// and have it work similarly to the way it does with nevents non-zero --
		// i.e. it waits until either an event happens or the timeout expires, and then wakes up.
		// In fact, what happens if you do this is that it just returns immediately. So, we have
		// to pass nevents set to one, and then we just ignore the event it gives back to us. -- SC
		if (kevent(KQueueFD, NULL, 0, new_events, 1, &timeout) < 0)
			{ LogMsg("kevent(%d) failed errno %d (%s)", KQueueFD, errno, strerror(errno)); sleep(1); }
#endif

		pthread_mutex_lock(&PlatformStorage.BigMutex);
		// We have to ignore the event we may have been told about above, because that
		// was done without holding the lock, and between the time we woke up and the
		// time we reclaimed the lock the other thread could have done something that
		// makes the event no longer valid. Now we have the lock, we call kevent again
		// and this time we can safely process the events it tells us about.

		static const struct timespec zero_timeout = { 0, 0 };
		int events_found;
		while ((events_found = kevent(KQueueFD, NULL, 0, new_events, kEventsToReadAtOnce, &zero_timeout)) != 0)
			{
			if (events_found > kEventsToReadAtOnce || (events_found < 0 && errno != EINTR))
				{
				// Not sure what to do here, our kqueue has failed us - this isn't ideal
				LogMsg("ERROR: KQueueLoop - kevent failed errno %d (%s)", errno, strerror(errno));
				exit(errno);
				}

			numevents += events_found;

			int i;
			for (i = 0; i < events_found; i++)
				{
				const KQueueEntry *const kqentry = new_events[i].udata;
				mDNSs32 stime = mDNSPlatformRawTime();
				const char *const KQtask = kqentry->KQtask;	// Grab a copy in case KQcallback deletes the task
				kqentry->KQcallback(new_events[i].ident, new_events[i].filter, kqentry->KQcontext);
				mDNSs32 etime = mDNSPlatformRawTime();
				if (etime - stime >= WatchDogReportingThreshold)
					LogInfo("WARNING: %s took %dms to complete", KQtask, etime - stime);
				}
			}
		}
	
	return NULL;
	}

mDNSlocal void LaunchdCheckin(void)
	{
	launch_data_t msg  = launch_data_new_string(LAUNCH_KEY_CHECKIN);
	launch_data_t resp = launch_msg(msg);
	launch_data_free(msg);
	if (!resp) { LogMsg("launch_msg returned NULL"); return; }

	if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO)
		{
		int err = launch_data_get_errno(resp);
		// When running on Tiger with "ServiceIPC = false", we get "err == EACCES" to tell us there's no launchdata to fetch
		if (err != EACCES) LogMsg("launch_msg returned %d", err);
		else LogInfo("Launchd provided no launchdata; will open Mach port and Unix Domain Socket explicitly...", err);
		}
	else
		{
		launch_data_t skts = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_SOCKETS);
		if (!skts) LogMsg("launch_data_dict_lookup LAUNCH_JOBKEY_SOCKETS returned NULL");
		else
			{
			launch_data_t skt = launch_data_dict_lookup(skts, "Listeners");
			if (!skt) LogMsg("launch_data_dict_lookup Listeners returned NULL");
			else
				{
				launchd_fds_count = launch_data_array_get_count(skt);
				if (launchd_fds_count == 0) LogMsg("launch_data_array_get_count(skt) returned 0");
				else
					{
					launchd_fds = mallocL("LaunchdCheckin", sizeof(dnssd_sock_t) * launchd_fds_count);
					if (!launchd_fds) LogMsg("LaunchdCheckin: malloc failed");
					else
						{
						size_t i;
						for(i = 0; i < launchd_fds_count; i++)
							{						
							launch_data_t s = launch_data_array_get_index(skt, i);
							if (!s)
								{
								launchd_fds[i] = dnssd_InvalidSocket;
								LogMsg("launch_data_array_get_index(skt, %d) returned NULL", i);
								}
							else
								{
								launchd_fds[i] = launch_data_get_fd(s);
								LogInfo("Launchd Unix Domain Socket [%d]: %d", i, launchd_fds[i]);
								}
							}
						}
					// In some early versions of 10.4.x, the permissions on the UDS were not set correctly, so we fix them here
					chmod(MDNS_UDS_SERVERPATH, S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP | S_IROTH|S_IWOTH);
					}
				}
			}

		launch_data_t ports = launch_data_dict_lookup(resp, "MachServices");
		if (!ports) LogMsg("launch_data_dict_lookup MachServices returned NULL");
		else
			{
			launch_data_t p = launch_data_dict_lookup(ports, "com.apple.mDNSResponder");
			if (!p) LogInfo("launch_data_dict_lookup(ports, \"com.apple.mDNSResponder\") returned NULL");
			else
				{
				m_port = launch_data_get_fd(p);
				LogInfo("Launchd Mach Port: %d", m_port);
				if (m_port == ~0U) m_port = MACH_PORT_NULL;
				}
			}
		}
	launch_data_free(resp);
	}

mDNSlocal void DropPrivileges(void)
	{
	static const char login[] = "_mdnsresponder";
	struct passwd *pwd = getpwnam(login);
	if (NULL == pwd)
		LogMsg("Could not find account name \"%s\". Running as root.", login);
	else
		{
		uid_t uid = pwd->pw_uid;
		gid_t gid = pwd->pw_gid;

		LogMsg("Started as root. Switching to userid \"%s\".", login);

		if (unlink(MDNS_UDS_SERVERPATH) < 0 && errno != ENOENT) LogMsg("DropPrivileges: Could not unlink \"%s\": (%d) %s", MDNS_UDS_SERVERPATH, errno, strerror(errno));
		else
			{
			static char path[] = "/var/run/mdns/mDNSResponder";
			char *p = strrchr(path, '/');
			*p = '\0';
			if (mkdir(path, 0755) < 0 && errno != EEXIST) LogMsg("DropPrivileges: Could not create directory \"%s\": (%d) %s", path, errno, strerror(errno));
			else if (chown(path, uid, gid) < 0) LogMsg("DropPrivileges: Could not chown directory \"%s\": (%d) %s", path, errno, strerror(errno));
			else
				{
				*p = '/';
				if (unlink(path) < 0 && errno != ENOENT) LogMsg("DropPrivileges: Could not unlink \"%s\": (%d) %s", path, errno, strerror(errno));
				else if (symlink(path, MDNS_UDS_SERVERPATH) < 0) LogMsg("DropPrivileges: Could not symlink \"%s\" -> \"%s\": (%d) %s", MDNS_UDS_SERVERPATH, path, errno, strerror(errno));
				else LogInfo("DropPrivileges: Created subdirectory and symlink");
				}
			}

		if (0 != initgroups(login, gid)) LogMsg("initgroups(\"%s\", %lu) failed.  Continuing.", login,        (unsigned long)gid);
		if (0 != setgid(gid))            LogMsg("setgid(%lu) failed.  Continuing with group %lu privileges.", (unsigned long)getegid());
		if (0 != setuid(uid))            LogMsg("setuid(%lu) failed. Continuing as root after all.",          (unsigned long)uid);
		}
	}

extern int sandbox_init(const char *profile, uint64_t flags, char **errorbuf) __attribute__((weak_import));

mDNSexport int main(int argc, char **argv)
	{
	int i;
	kern_return_t status;
	pthread_t KQueueThread;

	LogMsg("%s starting", mDNSResponderVersionString);
	
#if 0
	LogMsg("CacheRecord %d", sizeof(CacheRecord));
	LogMsg("CacheGroup  %d", sizeof(CacheGroup));
	LogMsg("ResourceRecord  %d", sizeof(ResourceRecord));
	LogMsg("RData_small     %d", sizeof(RData_small));

	LogMsg("sizeof(CacheEntity) %d", sizeof(CacheEntity));
	LogMsg("RR_CACHE_SIZE       %d", RR_CACHE_SIZE);
	LogMsg("block usage         %d", sizeof(CacheEntity) * RR_CACHE_SIZE);
	LogMsg("block wastage       %d", 16*1024 - sizeof(CacheEntity) * RR_CACHE_SIZE);
#endif

	safe_vproc_transaction_begin();

	if (0 == geteuid()) DropPrivileges();

	for (i=1; i<argc; i++)
		{
		if (!strcasecmp(argv[i], "-d"                        )) mDNS_DebugMode            = mDNStrue;
		if (!strcasecmp(argv[i], "-launchd"                  )) started_via_launchdaemon  = mDNStrue;
		if (!strcasecmp(argv[i], "-launchdaemon"             )) started_via_launchdaemon  = mDNStrue;
		if (!strcasecmp(argv[i], "-NoMulticastAdvertisements")) advertise                 = mDNS_Init_DontAdvertiseLocalAddresses;
		if (!strcasecmp(argv[i], "-DebugLogging"             )) mDNS_LoggingEnabled       = mDNStrue;
		if (!strcasecmp(argv[i], "-UnicastPacketLogging"     )) mDNS_PacketLoggingEnabled = mDNStrue;
		if (!strcasecmp(argv[i], "-OfferSleepProxyService"   ))
			OfferSleepProxyService = (i+1<argc && mDNSIsDigit(argv[i+1][0]) && mDNSIsDigit(argv[i+1][1]) && argv[i+1][2]==0) ? atoi(argv[++i]) : 80;
		}
	
	// Note that mDNSPlatformInit will set DivertMulticastAdvertisements in the mDNS structure
	if (!advertise) LogMsg("Administratively prohibiting multicast advertisements");

	OSXVers = mDNSMacOSXSystemBuildNumber(NULL);

	signal(SIGHUP,  HandleSIG);		// (Debugging) Purge the cache to check for cache handling bugs
	signal(SIGINT,  HandleSIG);		// Ctrl-C: Detach from Mach BootstrapService and exit cleanly
	// On 10.5 and later, the default action for SIGABRT is to generate a crash report, so we only need our CatchABRT handler on 10.4
	if (OSXVers <= OSXVers_10_4_Tiger)
		{
		LogInfo("Adding SIGABRT handler");
		signal(SIGABRT, CatchABRT); // For debugging -- SIGABRT should never happen
		}
	signal(SIGPIPE, SIG_IGN  );		// Don't want SIGPIPE signals -- we'll handle EPIPE errors directly
	signal(SIGTERM, HandleSIG);		// Machine shutting down: Detach from and exit cleanly like Ctrl-C
	signal(SIGINFO, HandleSIG);		// (Debugging) Write state snapshot to syslog
	signal(SIGUSR1, HandleSIG);		// (Debugging) Enable Logging
	signal(SIGUSR2, HandleSIG);		// (Debugging) Enable Packet Logging

	mDNSStorage.p = &PlatformStorage;	// Make sure mDNSStorage.p is set up, because validatelists uses it
	LaunchdCheckin();

	// Register the server with mach_init for automatic restart only during normal (non-debug) mode
    if (!mDNS_DebugMode && !started_via_launchdaemon)
    	{
    	registerBootstrapService();
    	if (!restarting_via_mach_init) exit(0); // mach_init will restart us immediately as a daemon
		int fd = open(_PATH_DEVNULL, O_RDWR, 0);
		if (fd < 0) LogMsg("open(_PATH_DEVNULL, O_RDWR, 0) failed errno %d (%s)", errno, strerror(errno));
		else
			{
			// Avoid unnecessarily duplicating a file descriptor to itself
			if (fd != STDIN_FILENO)  if (dup2(fd, STDIN_FILENO)  < 0) LogMsg("dup2(fd, STDIN_FILENO)  failed errno %d (%s)", errno, strerror(errno));
			if (fd != STDOUT_FILENO) if (dup2(fd, STDOUT_FILENO) < 0) LogMsg("dup2(fd, STDOUT_FILENO) failed errno %d (%s)", errno, strerror(errno));
			if (fd != STDERR_FILENO) if (dup2(fd, STDERR_FILENO) < 0) LogMsg("dup2(fd, STDERR_FILENO) failed errno %d (%s)", errno, strerror(errno));
			if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO) (void)close(fd);
			}
		}

	// Create the kqueue, mutex and thread to support KQSockets
	KQueueFD = kqueue();
	if (KQueueFD == -1) { LogMsg("kqueue() failed errno %d (%s)", errno, strerror(errno)); status = errno; goto exit; }
	
	i = pthread_mutex_init(&PlatformStorage.BigMutex, NULL);
	if (i == -1) { LogMsg("pthread_mutex_init() failed errno %d (%s)", errno, strerror(errno)); status = errno; goto exit; }
	
	int fdpair[2] = {0, 0};
	i = socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair);
	if (i == -1) { LogMsg("socketpair() failed errno %d (%s)", errno, strerror(errno)); status = errno; goto exit; }

	// Socket pair returned us two identical sockets connected to each other
	// We will use the first socket to send the second socket. The second socket
	// will be added to the kqueue so it will wake when data is sent.
	static const KQueueEntry wakeKQEntry = { KQWokenFlushBytes, NULL, "kqueue wakeup after CFRunLoop event" };
	PlatformStorage.WakeKQueueLoopFD = fdpair[0];
	KQueueSet(fdpair[1], EV_ADD, EVFILT_READ, &wakeKQEntry);
	
	// Invoke sandbox profile /usr/share/sandbox/mDNSResponder.sb
#if MDNS_NO_SANDBOX
	LogMsg("Note: Compiled without Apple Sandbox support");
#else
	if (!sandbox_init)
		LogMsg("Note: Running without Apple Sandbox support (not available on this OS)");
	else
		{
		char *sandbox_msg;
		int sandbox_err = sandbox_init("mDNSResponder", SANDBOX_NAMED, &sandbox_msg);
		if (sandbox_err) { LogMsg("WARNING: sandbox_init error %s", sandbox_msg); sandbox_free_error(sandbox_msg); }
		else LogInfo("Now running under Apple Sandbox restrictions");
		}
#endif

	status = mDNSDaemonInitialize();
	if (status) { LogMsg("Daemon start: mDNSDaemonInitialize failed"); goto exit; }

	status = udsserver_init(launchd_fds, launchd_fds_count);
	if (status) { LogMsg("Daemon start: udsserver_init failed"); goto exit; }

	mDNSMacOSXNetworkChanged(&mDNSStorage);

	// Start the kqueue thread
	i = pthread_create(&KQueueThread, NULL, KQueueLoop, &mDNSStorage);
	if (i == -1) { LogMsg("pthread_create() failed errno %d (%s)", errno, strerror(errno)); status = errno; goto exit; }

	if (status == 0)
		{
		CFRunLoopRun();
		LogMsg("ERROR: CFRunLoopRun Exiting.");
		mDNS_Close(&mDNSStorage);
		}

	LogMsg("%s exiting", mDNSResponderVersionString);

exit:
	if (!mDNS_DebugMode && !started_via_launchdaemon) destroyBootstrapService();
	return(status);
	}

// uds_daemon.c support routines /////////////////////////////////////////////

// Arrange things so that when data appears on fd, callback is called with context
mDNSexport mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *context)
	{
	KQSocketEventSource **p = &gEventSources;
	while (*p && (*p)->fd != fd) p = &(*p)->next;
	if (*p) { LogMsg("udsSupportAddFDToEventLoop: ERROR fd %d already has EventLoop source entry", fd); return mStatus_AlreadyRegistered; }

	KQSocketEventSource *newSource = (KQSocketEventSource*) mallocL("KQSocketEventSource", sizeof *newSource);
	if (!newSource) return mStatus_NoMemoryErr;

	newSource->next           = mDNSNULL;
	newSource->fd             = fd;
	newSource->kqs.KQcallback = callback;
	newSource->kqs.KQcontext  = context;
	newSource->kqs.KQtask     = "UDS client";

	if (KQueueSet(fd, EV_ADD, EVFILT_READ, &newSource->kqs) == 0)
		{
		*p = newSource;
		return mStatus_NoError;
		}

	LogMsg("KQueueSet failed for fd %d errno %d (%s)", fd, errno, strerror(errno));
	freeL("KQSocketEventSource", newSource);
	return mStatus_BadParamErr;
	}

mDNSexport mStatus udsSupportRemoveFDFromEventLoop(int fd)		// Note: This also CLOSES the file descriptor
	{
	KQSocketEventSource **p = &gEventSources;
	while (*p && (*p)->fd != fd) p = &(*p)->next;
	if (*p)
		{
		KQSocketEventSource *s = *p;
		*p = (*p)->next;
		// We don't have to explicitly do a kqueue EV_DELETE here because closing the fd
		// causes the kernel to automatically remove any associated kevents
		close(s->fd);
		freeL("KQSocketEventSource", s);
		return mStatus_NoError;
		}
	LogMsg("udsSupportRemoveFDFromEventLoop: ERROR fd %d not found in EventLoop source list", fd);
	return mStatus_NoSuchNameErr;
	}

#if _BUILDING_XCODE_PROJECT_
// If mDNSResponder crashes, then this string will be magically included in the automatically-generated crash log
const char *__crashreporter_info__ = mDNSResponderVersionString;
asm(".desc ___crashreporter_info__, 0x10");
#endif

// For convenience when using the "strings" command, this is the last thing in the file
// The "@(#) " pattern is a special prefix the "what" command looks for
mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";