X-Git-Url: https://git.saurik.com/apple/mdnsresponder.git/blobdiff_plain/8e92c31c9a45a66732f5bc7afbc9f5596c17e91d..b8d5688b3ede9438aa8ad9679403f1d4e3faddb5:/mDNSMacOSX/daemon.c diff --git a/mDNSMacOSX/daemon.c b/mDNSMacOSX/daemon.c index 9cb6a47..1b257ea 100644 --- a/mDNSMacOSX/daemon.c +++ b/mDNSMacOSX/daemon.c @@ -1,381 +1,91 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. +/* -*- Mode: C; tab-width: 4 -*- * - * @APPLE_LICENSE_HEADER_START@ - * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and + * Copyright (c) 2002-2011 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. - * - * @APPLE_LICENSE_HEADER_END@ * - * 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 , - * 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.175 2004/06/10 20:23:21 cheshire -Also list interfaces in SIGINFO output - -Revision 1.174 2004/06/08 18:54:48 ksekar -: mDNSResponder leaks after exploring in Printer Setup Utility - -Revision 1.173 2004/06/08 17:35:12 cheshire - Detect and report if mDNSResponder uses too much CPU - -Revision 1.172 2004/06/05 00:04:26 cheshire -: wide-area domains should be returned in reg. domain enumeration - -Revision 1.171 2004/06/04 08:58:30 ksekar -: Keychain integration for secure dynamic update - -Revision 1.170 2004/05/30 20:01:50 ksekar -: wide-area default registrations should be in -.local too - fixed service registration when clients pass an explicit -domain (broken by previous checkin) - -Revision 1.169 2004/05/30 01:30:16 ksekar -: wide-area default registrations should be in -.local too - -Revision 1.168 2004/05/18 23:51:26 cheshire -Tidy up all checkin comments to use consistent "" format for bug numbers - -Revision 1.167 2004/05/14 16:39:47 ksekar -Browse for iChat locally for now. - -Revision 1.166 2004/05/13 21:33:52 ksekar -Clean up non-local registration control via config file. Force iChat -registrations to be local for now. - -Revision 1.165 2004/05/13 04:54:20 ksekar -Unified list copy/free code. Added symetric list for - -Revision 1.164 2004/05/12 22:03:08 ksekar -Made GetSearchDomainList a true platform-layer call (declaration moved -from mDNSMacOSX.h to mDNSClientAPI.h), impelemted to return "local" -only on non-OSX platforms. Changed call to return a copy of the list -to avoid shared memory issues. Added a routine to free the list. - -Revision 1.163 2004/05/12 02:03:25 ksekar -Non-local domains will only be browsed by default, and show up in -_browse domain enumeration, if they contain an _browse._dns-sd ptr record. - -Revision 1.162 2004/04/14 23:09:29 ksekar -Support for TSIG signed dynamic updates. - -Revision 1.161 2004/04/07 01:20:04 cheshire -Hash slot value should be unsigned - -Revision 1.160 2004/04/06 19:51:24 cheshire - mDNSResponder will not launch if "nobody" user doesn't exist. -After more discussion, we've decided to use userid -2 if "nobody" user doesn't exist. - -Revision 1.159 2004/04/03 01:36:55 cheshire - mDNSResponder will not launch if "nobody" user doesn't exist. -If "nobody" user doesn't exist, log a message and continue as "root" - -Revision 1.158 2004/04/02 21:39:05 cheshire -Fix errors in comments - -Revision 1.157 2004/03/19 18:49:10 ksekar -Increased size check in freeL() to account for LargeCacheRecord -structs larger than 8k - -Revision 1.156 2004/03/19 18:19:19 ksekar -Fixed daemon.c to compile with malloc debugging turned on. - -Revision 1.155 2004/03/13 01:57:34 ksekar -: DynDNS: Dynamic update of service records - -Revision 1.154 2004/03/12 08:42:47 cheshire -: Should not allow empty string for resolve domain - -Revision 1.153 2004/03/12 08:08:51 cheshire -Update comments - -Revision 1.152 2004/02/05 19:39:29 cheshire -Move creation of /var/run/mDNSResponder.pid to uds_daemon.c, -so that all platforms get this functionality - -Revision 1.151 2004/02/03 22:35:34 cheshire -: Should not allow empty string for resolve domain - -Revision 1.150 2004/01/28 21:14:23 cheshire -Reconcile debug_mode and gDebugLogging into a single flag (mDNS_DebugMode) - -Revision 1.149 2004/01/28 02:30:08 ksekar -Added default Search Domains to unicast browsing, controlled via -Networking sharing prefs pane. Stopped sending unicast messages on -every interface. Fixed unicast resolving via mach-port API. - -Revision 1.148 2004/01/25 00:03:20 cheshire -Change to use mDNSVal16() instead of private PORT_AS_NUM() macro - -Revision 1.147 2004/01/19 19:51:46 cheshire -Fix compiler error (mixed declarations and code) on some versions of Linux - -Revision 1.146 2003/12/08 21:00:46 rpantos -Changes to support mDNSResponder on Linux. - -Revision 1.145 2003/12/05 22:08:07 cheshire -Update version string to "mDNSResponder-61", including new mechanism to allow dots (e.g. 58.1) - -Revision 1.144 2003/11/19 23:21:08 ksekar -: config change handler not called for dns-sd services - -Revision 1.143 2003/11/14 21:18:32 cheshire -: Security: Crashing bug in mDNSResponder -Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers. - -Revision 1.142 2003/11/08 22:18:29 cheshire -: Don't need to show process ID in *every* mDNSResponder syslog message - -Revision 1.141 2003/11/07 02:30:57 cheshire -Also check per-slot cache use counts in SIGINFO state log - -Revision 1.140 2003/10/21 19:58:26 cheshire - Syslog messages should show TTL as signed (for overdue records) - -Revision 1.139 2003/10/21 00:10:18 rpantos -: mDNSResponder should not run as root - -Revision 1.138 2003/10/07 20:16:58 cheshire -Shorten syslog message a bit - -Revision 1.137 2003/09/23 02:12:43 cheshire -Also include port number in list of services registered via new UDS API - -Revision 1.136 2003/09/23 02:07:25 cheshire -Include port number in DNSServiceRegistration START/STOP messages - -Revision 1.135 2003/09/23 01:34:02 cheshire -In SIGINFO state log, show remaining TTL on cache records, and port number on ServiceRegistrations - -Revision 1.134 2003/08/21 20:01:37 cheshire - Traffic reduction: Detect long-lived Resolve() calls, and report them in syslog - -Revision 1.133 2003/08/20 23:39:31 cheshire - Review syslog messages, and remove as appropriate - -Revision 1.132 2003/08/20 01:44:56 cheshire -Fix errors in LogOperation() calls (only used for debugging) - -Revision 1.131 2003/08/19 05:39:43 cheshire - SIGINFO dump should include resolves started by DNSServiceQueryRecord - -Revision 1.130 2003/08/16 03:39:01 cheshire - InterfaceID -1 indicates "local only" - -Revision 1.129 2003/08/15 20:16:03 cheshire - mDNSResponder takes too much RPRVT -We want to avoid touching the rdata pages, so we don't page them in. -1. RDLength was stored with the rdata, which meant touching the page just to find the length. - Moved this from the RData to the ResourceRecord object. -2. To avoid unnecessarily touching the rdata just to compare it, - compute a hash of the rdata and store the hash in the ResourceRecord object. - -Revision 1.128 2003/08/14 19:30:36 cheshire - Include list of cache records in SIGINFO output - -Revision 1.127 2003/08/14 02:18:21 cheshire - Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord - -Revision 1.126 2003/08/12 19:56:25 cheshire -Update to APSL 2.0 - -Revision 1.125 2003/08/08 18:36:04 cheshire - Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug - -Revision 1.124 2003/07/25 18:28:23 cheshire -Minor fix to error messages in syslog: Display string parameters with quotes - -Revision 1.123 2003/07/23 17:45:28 cheshire - mDNSResponder leaks a bit -Don't allocate memory for the reply until after we've verified that the reply is valid - -Revision 1.122 2003/07/23 00:00:04 cheshire -Add comments - -Revision 1.121 2003/07/20 03:38:51 ksekar - Completed support for Unix-domain socket based API. - -Revision 1.120 2003/07/18 00:30:00 cheshire - Remove mDNSResponder version from packet header and use HINFO record instead - -Revision 1.119 2003/07/17 19:08:58 cheshire - Remove calls to enable obsolete UDS code - -Revision 1.118 2003/07/15 21:12:28 cheshire -Added extra debugging checks in validatelists() (not used in final shipping version) - -Revision 1.117 2003/07/15 01:55:15 cheshire - Need to implement service registration with subtypes - -Revision 1.116 2003/07/02 21:19:51 cheshire - Update copyright notices, etc., in source code comments - -Revision 1.115 2003/07/02 02:41:24 cheshire - mDNSResponder needs to start with a smaller cache and then grow it as needed - -Revision 1.114 2003/07/01 21:10:20 cheshire -Reinstate checkin 1.111, inadvertently overwritten by checkin 1.112 - -Revision 1.113 2003/06/28 17:27:43 vlubet - Redirect standard input, standard output, and -standard error file descriptors to /dev/null just like any other -well behaved daemon - -Revision 1.112 2003/06/25 23:42:19 ksekar -: Feature: New Rendezvous APIs (#7875) -Reviewed by: Stuart Cheshire -Added files necessary to implement Unix domain sockets based enhanced -Rendezvous APIs, and integrated with existing Mach-port based daemon. - -Revision 1.111 2003/06/11 01:02:43 cheshire - mDNSResponder binary compatibility -Make single binary that can run on both Jaguar and Panther. - -Revision 1.110 2003/06/10 01:14:11 cheshire - New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call - -Revision 1.109 2003/06/06 19:53:43 cheshire -For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass -(Global search-and-replace; no functional change to code execution.) - -Revision 1.108 2003/06/06 14:08:06 cheshire -For clarity, pull body of main while() loop out into a separate function called mDNSDaemonIdle() - -Revision 1.107 2003/05/29 05:44:55 cheshire -Minor fixes to log messages - -Revision 1.106 2003/05/27 18:30:55 cheshire - Need a way to easily examine current mDNSResponder state -Dean Reece suggested SIGINFO is more appropriate than SIGHUP - -Revision 1.105 2003/05/26 03:21:29 cheshire -Tidy up address structure naming: -mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) -mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 -mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 - -Revision 1.104 2003/05/26 00:42:06 cheshire - Temporarily include mDNSResponder version in packets - -Revision 1.103 2003/05/23 23:07:44 cheshire - Must not write to stderr when running as daemon - -Revision 1.102 2003/05/22 01:32:31 cheshire -Fix typo in Log message format string - -Revision 1.101 2003/05/22 00:26:55 cheshire - DNSServiceRegistrationCreate() should return error on dup -Modify error message to explain that this is technically legal, but may indicate a bug. - -Revision 1.100 2003/05/21 21:02:24 ksekar -: Service should be prefixed -Changed kmDNSBootstrapName to "com.apple.mDNSResponderRestart" since we're changing the main -Mach message port to "com.apple.mDNSResponder. - -Revision 1.99 2003/05/21 17:33:49 cheshire -Fix warnings (mainly printf format string warnings, like using "%d" where it should say "%lu", etc.) - -Revision 1.98 2003/05/20 00:33:07 cheshire - Need a way to easily examine current mDNSResponder state -SIGHUP now writes state summary to syslog - -Revision 1.97 2003/05/08 00:19:08 cheshire - Forgot to set "err = mStatus_BadParamErr" in a couple of places - -Revision 1.96 2003/05/07 22:10:46 cheshire - Add a few more error logging messages - -Revision 1.95 2003/05/07 19:20:17 cheshire - Add version number to mDNSResponder builds - -Revision 1.94 2003/05/07 00:28:18 cheshire - Need to make mDNSResponder more defensive against bad clients - -Revision 1.93 2003/05/06 00:00:49 cheshire - Rationalize naming of domainname manipulation functions - -Revision 1.92 2003/04/04 20:38:57 cheshire -Add $Log header - */ #include #include -#include #include +#include +#include #include #include #include +#include +#include // for launch_socket_service_check_in() +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include // for bootstrap_check_in() #include "DNSServiceDiscoveryRequestServer.h" #include "DNSServiceDiscoveryReply.h" -#include "mDNSClientAPI.h" // Defines the interface to the client layer above -#include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform +#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 "uds_daemon.h" // Interface to the server side implementation of dns_sd.h +#include "xpc_services.h" // Interface to XPC services -#include "GenLinkedList.h" +#include "../mDNSMacOSX/DNSServiceDiscovery.h" +#include "helper.h" -#include +static aslclient log_client = NULL; +static aslmsg log_msg = NULL; -//************************************************************************************************************* -// Macros - -// Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion -// e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4" -// To expand "version" to its value before making the string, use STRINGIFY(version) instead -#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s -#define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) - -// convenience definition -#define _UNUSED __attribute__ ((unused)) +// Used on Embedded Side for Reading mDNSResponder Managed Preferences Profile +#if TARGET_OS_EMBEDDED +#define kmDNSEnableLoggingStr CFSTR("EnableLogging") +#define kmDNSResponderPrefIDStr "com.apple.mDNSResponder.plist" +#define kmDNSResponderPrefID CFSTR(kmDNSResponderPrefIDStr) +#endif //************************************************************************************************************* -// Globals +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Globals +#endif -#define LOCAL_DEFAULT_REG 1 // empty string means register in the local domain -#define DEFAULT_REG_DOMAIN "apple.com." // used if the above flag is turned off -mDNSexport mDNS mDNSStorage; static mDNS_PlatformSupport PlatformStorage; -#define RR_CACHE_SIZE 64 -static CacheRecord rrcachestorage[RR_CACHE_SIZE]; -static const char kmDNSBootstrapName[] = "com.apple.mDNSResponderRestart"; +// 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 mach_port_t m_port = MACH_PORT_NULL; + +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM +mDNSlocal void PrepareForIdle(void *m_param); +#else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM static mach_port_t client_death_port = MACH_PORT_NULL; -static mach_port_t exit_m_port = MACH_PORT_NULL; -static mach_port_t info_m_port = MACH_PORT_NULL; -static mach_port_t server_priv_port = MACH_PORT_NULL; +static mach_port_t signal_port = MACH_PORT_NULL; +#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + +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 @@ -385,1265 +95,1520 @@ static mach_port_t server_priv_port = MACH_PORT_NULL; // even extra-slow clients a fair chance before we cut them off. #define MDNS_MM_TIMEOUT 250 -static int restarting_via_mach_init = 0; +static mDNSBool advertise = mDNS_Init_AdvertiseLocalAddresses; // By default, advertise addresses (& other records) via multicast + +extern mDNSBool StrictUnicastOrdering; +extern mDNSBool AlwaysAppendSearchDomains; //************************************************************************************************************* -// Active client list structures +#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 - }; +{ + 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; - }; +{ + DNSServiceBrowserResult *next; + int resultType; + domainname result; +}; typedef struct DNSServiceBrowser_struct DNSServiceBrowser; typedef struct DNSServiceBrowserQuestion - { +{ struct DNSServiceBrowserQuestion *next; DNSQuestion q; - } DNSServiceBrowserQuestion; + domainname domain; +} DNSServiceBrowserQuestion; struct DNSServiceBrowser_struct - { - DNSServiceBrowser *next; - mach_port_t ClientMachPort; +{ + DNSServiceBrowser *next; + mach_port_t ClientMachPort; DNSServiceBrowserQuestion *qlist; - DNSServiceBrowserResult *results; - mDNSs32 lastsuccess; - }; + 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; - }; - - -typedef struct ExtraRecordRef - { - ExtraResourceRecord *localRef; // extra added to .local service - ExtraResourceRecord *globalRef; // extra added to default global service (may be NULL) - struct ExtraRecordRef *next; - } ExtraRecordRef; - -typedef struct DNSServiceRegistration_struct DNSServiceRegistration; -struct DNSServiceRegistration_struct - { - DNSServiceRegistration *next; - mach_port_t ClientMachPort; - mDNSBool autoname; - mDNSBool autorenameLS; - mDNSBool autorenameGS; - mDNSBool deallocate; // gs and ls (below) will receive separate MemFree callbacks, - // the latter of which must deallocate the wrapper structure. - // ls MemFree callback: if (!gs) free wrapper; else set deallocate flag - // gs callback: if (deallocate) free wrapper; else free (gs), gs = NULL - domainlabel name; - ExtraRecordRef *ExtraRefList; - ServiceRecordSet *gs; // default "global" (wide area) service (may be NULL) - ServiceRecordSet ls; // .local service (also used if client passes an explicit domain) - // Don't add any fields after ServiceRecordSet. - // This is where the implicit extra space goes if we allocate an oversized ServiceRecordSet object - }; +{ + 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; + //************************************************************************************************************* -// General Utility Functions +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - General Utility Functions +#endif -#if MACOSX_MDNS_MALLOC_DEBUGGING +#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING char _malloc_options[] = "AXZ"; -static void validatelists(mDNS *const m) - { - DNSServiceDomainEnumeration *e; - DNSServiceBrowser *b; - DNSServiceResolver *l; - DNSServiceRegistration *r; - AuthRecord *rr; - CacheRecord *cr; - DNSQuestion *q; - mDNSu32 slot; - - for (e = DNSServiceDomainEnumerationList; e; e=e->next) - if (e->ClientMachPort == 0 || e->ClientMachPort == (mach_port_t)~0) - LogMsg("!!!! DNSServiceDomainEnumerationList: %p is garbage (%X) !!!!", e, e->ClientMachPort); - - for (b = DNSServiceBrowserList; b; b=b->next) - if (b->ClientMachPort == 0 || b->ClientMachPort == (mach_port_t)~0) - LogMsg("!!!! DNSServiceBrowserList: %p is garbage (%X) !!!!", b, b->ClientMachPort); - - for (l = DNSServiceResolverList; l; l=l->next) - if (l->ClientMachPort == 0 || l->ClientMachPort == (mach_port_t)~0) - LogMsg("!!!! DNSServiceResolverList: %p is garbage (%X) !!!!", l, l->ClientMachPort); - - for (r = DNSServiceRegistrationList; r; r=r->next) - if (r->ClientMachPort == 0 || r->ClientMachPort == (mach_port_t)~0) - LogMsg("!!!! DNSServiceRegistrationList: %p is garbage (%X) !!!!", r, r->ClientMachPort); - - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF) - LogMsg("!!!! ResourceRecords list: %p is garbage (%X) !!!!", rr, rr->resrec.RecordType); - - for (rr = m->DuplicateRecords; rr; rr=rr->next) - if (rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF) - LogMsg("!!!! DuplicateRecords list: %p is garbage (%X) !!!!", rr, rr->resrec.RecordType); - - for (q = m->Questions; q; q=q->next) - if (q->ThisQInterval == (mDNSs32)~0) - LogMsg("!!!! Questions list: %p is garbage (%lX) !!!!", q, q->ThisQInterval); - - for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) - for (cr = mDNSStorage.rrcache_hash[slot]; cr; cr=cr->next) - if (cr->resrec.RecordType == 0 || cr->resrec.RecordType == 0xFF) - LogMsg("!!!! Cache slot %lu: %p is garbage (%X) !!!!", slot, rr, rr->resrec.RecordType); - } - -void *mallocL(char *msg, unsigned int size) - { - unsigned long *mem = malloc(size+8); - if (!mem) - { - LogMsg("malloc( %s : %d ) failed", msg, size); - return(NULL); - } - else - { - LogMalloc("malloc( %s : %lu ) = %p", msg, size, &mem[2]); - mem[0] = 0xDEAD1234; - mem[1] = size; - //bzero(&mem[2], size); - memset(&mem[2], 0xFF, size); - validatelists(&mDNSStorage); - return(&mem[2]); - } - } - -void freeL(char *msg, void *x) - { - if (!x) - LogMsg("free( %s @ NULL )!", msg); - else - { - unsigned long *mem = ((unsigned long *)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) too big!", msg, mem[1], &mem[2]); return; } - LogMalloc("free( %s : %ld @ %p)", msg, mem[1], &mem[2]); - //bzero(mem, mem[1]+8); - memset(mem, 0xFF, mem[1]+8); - validatelists(&mDNSStorage); - free(mem); - } - } +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 -mDNSlocal void FreeSRS(ServiceRecordSet *s) - { - while (s->Extras) - { - ExtraResourceRecord *extras = s->Extras; - s->Extras = s->Extras->next; - if (extras->r.resrec.rdata != &extras->r.rdatastorage) - freeL("Extra RData", extras->r.resrec.rdata); - freeL("ExtraResourceRecord", extras); - } - - 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); - } - -mDNSlocal void FreeDNSServiceRegistration(ServiceRecordSet *srs) - { - DNSServiceRegistration *x = srs->ServiceContext; - ExtraRecordRef *ref, *fptr; - - FreeSRS(srs); - if (srs == x->gs) - { - freeL("DNSServiceRegistration GlobalService", srs); - x->gs = NULL; - } - else x->deallocate = mDNStrue; - - if (x->deallocate && !x->gs) - { - ref = x->ExtraRefList; - while (ref) - { fptr = ref; ref = ref->next; freeL("ExtraRecordRef", fptr); } - freeL("DNSServiceRegistration", x); - } - } +// 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: DNSServiceBrowser(%##s) STOP; WARNING m %p != x %p", ClientMachPort, qptr->q.qname.c, m, x); - else LogOperation("%5d: DNSServiceBrowser(%##s) STOP", ClientMachPort, qptr->q.qname.c); - mDNS_StopBrowse(&mDNSStorage, &qptr->q); - freePtr = qptr; - qptr = qptr->next; - freeL("DNSServiceBrowserQuestion", freePtr); - } - while (x->results) - { - DNSServiceBrowserResult *r = x->results; - x->results = x->results->next; - freeL("DNSServiceBrowserResult", r); - } - 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: DNSServiceResolver(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->i.name.c, m, x); - else LogOperation("%5d: DNSServiceResolver(%##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) - { - DNSServiceRegistration *x = *r; - *r = (*r)->next; - x->autorenameLS = mDNSfalse; - x->autorenameGS = mDNSfalse; - if (m && m != x) - { - LogMsg("%5d: DNSServiceRegistration(%##s, %u) STOP; WARNING m %p != x %p", ClientMachPort, x->ls.RR_SRV.resrec.name.c, SRS_PORT(&x->ls), m, x); - if (x->gs) LogMsg("%5d: DNSServiceRegistration(%##s, %u) STOP; WARNING m %p != x %p", ClientMachPort, x->gs->RR_SRV.resrec.name.c, SRS_PORT(x->gs), m, x); - } - else - { - LogOperation("%5d: DNSServiceRegistration(%##s, %u) STOP", ClientMachPort, x->ls.RR_SRV.resrec.name.c, SRS_PORT(&x->ls)); - if (x->gs) LogOperation("%5d: DNSServiceRegistration(%##s, %u) STOP", ClientMachPort, x->gs->RR_SRV.resrec.name.c, SRS_PORT(x->gs)); - } - // 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 (x->gs && mDNS_DeregisterService(&mDNSStorage, x->gs)) - { - // Deregister returned an error, so we free immediately - FreeSRS(x->gs); - x->gs = NULL; - } - if (mDNS_DeregisterService(&mDNSStorage, &x->ls)) - FreeDNSServiceRegistration(&x->ls); - return; - } - - LogMsg("%5d: died or deallocated, but no record of client can be found!", ClientMachPort); - } +{ + 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) - { - LogMsg("%5d: Registration(%##s) %s%s", c, r->ls.RR_SRV.resrec.name.c, reason, msg); - if (r->gs) LogMsg("%5d: Registration(%##s) %s%s", c, r->gs->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); - } +{ + 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->ls.RR_SRV.resrec.name.c); - return(e || b || l || r); - } +{ + 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); +} + +#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM mDNSlocal void ClientDeathCallback(CFMachPortRef unusedport, void *voidmsg, CFIndex size, void *info) - { - 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); - } - } +{ + 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"); +} + +#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM 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); - } +{ +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + dispatch_source_t mach_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND, ClientMachPort, 0, dispatch_get_main_queue()); + if (mach_source == mDNSNULL) + { + AbortClientWithLogMessage(ClientMachPort, "died/deallocated before we could enable death notification", "", m); + return; + } + dispatch_source_set_event_handler(mach_source, ^{ + mach_port_destroy(mach_task_self(), ClientMachPort); + }); + dispatch_resume(mach_source); +#else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + 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); +#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM +} //************************************************************************************************************* // Domain Enumeration -mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) - { - kern_return_t status; - #pragma unused(m) - char buffer[MAX_ESCAPED_DOMAIN_NAME]; - DNSServiceDomainEnumerationReplyResultType rt; - DNSServiceDomainEnumeration *x = (DNSServiceDomainEnumeration *)question->QuestionContext; - - debugf("FoundDomain: %##s PTR %##s", answer->name.c, answer->rdata->u.name.c); - if (answer->rrtype != kDNSType_PTR) return; - if (!x) { debugf("FoundDomain: 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); - } +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; - const DNSServiceDomainEnumerationReplyResultType rt = DNSServiceDomainEnumerationReplyAddDomainDefault; - - // 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; - - // Generate initial response - verbosedebugf("%5d: Enumerate %s Domains", client, regDom ? "Registration" : "Browsing"); - // We always give local. as the initial default browse domain, and then look for more - kern_return_t status = DNSServiceDomainEnumerationReply_rpc(x->ClientMachPort, rt, "local.", 0, MDNS_MM_TIMEOUT); - if (status == MACH_SEND_TIMED_OUT) - { AbortBlockedClient(x->ClientMachPort, "local enumeration", x); return(mStatus_UnknownErr); } - - // Do the operation - err = mDNS_GetDomains(&mDNSStorage, &x->dom, dt1, NULL, mDNSInterface_LocalOnly, FoundDomain, x); - if (!err) err = mDNS_GetDomains(&mDNSStorage, &x->def, dt2, NULL, mDNSInterface_LocalOnly, FoundDomain, 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); + 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 (%ld)", client, regDom, errormsg, err); - return(err); - } + 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, mDNSBool 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; - } +mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) +{ + (void)m; // Unused -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"; - DNameListElem *SearchDomains = NULL, *sdPtr; - DNSServiceBrowserQuestion *qptr; - - 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; } + if (answer->rrtype != kDNSType_PTR) + { LogMsg("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer->rrtype); return; } - // Check other parameters - domainname t, d; - if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Illegal regtype"; goto badparam; } + 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, mDNSNULL, mDNSInterface_Any, 0, mDNSfalse, 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); + } + } + } +} - // 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 - x->ClientMachPort = client; - x->results = NULL; - x->lastsuccess = 0; - x->qlist = NULL; - x->next = DNSServiceBrowserList; - DNSServiceBrowserList = x; - - //!!!KRS browse locally for ichat - if (!domain[0] && (!strcmp(regtype, "_ichat._tcp.") || !strcmp(regtype, "_presence._tcp."))) - domain = "local."; - - if (domain[0]) - { - // Start browser for an explicit domain - x->qlist = mallocL("DNSServiceBrowserQuestion", sizeof(DNSServiceBrowserQuestion)); - x->qlist->next = NULL; - if (!x->qlist) { err = mStatus_UnknownErr; AbortClient(client, x); errormsg = "malloc"; goto fail; } - - if (!MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Illegal domain"; goto badparam; } - LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", client, t.c, d.c); - err = mDNS_StartBrowse(&mDNSStorage, &x->qlist->q, &t, &d, mDNSInterface_Any, FoundInstance, x); - if (err) { AbortClient(client, x); errormsg = "mDNS_StartBrowse"; goto fail; } - } - else - { - // Start browser on all domains - SearchDomains = mDNSPlatformGetSearchDomainList(); - if (!SearchDomains) { AbortClient(client, x); errormsg = "GetSearchDomainList"; goto fail; } - for (sdPtr = SearchDomains; sdPtr; sdPtr = sdPtr->next) - { - qptr = mallocL("DNSServiceBrowserQuestion", sizeof(DNSServiceBrowserQuestion)); - if (!qptr) { err = mStatus_UnknownErr; AbortClient(client, x); errormsg = "malloc"; goto fail; } - qptr->next = x->qlist; - x->qlist = qptr; - LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", client, t.c, sdPtr->name.c); - err = mDNS_StartBrowse(&mDNSStorage, &qptr->q, &t, &sdPtr->name, mDNSInterface_Any, FoundInstance, x); - if (err) { AbortClient(client, x); errormsg = "mDNS_StartBrowse"; goto fail; } - } - } - // Succeeded: Wrap up and return - EnableDeathNotificationForClient(client, x); - mDNS_FreeDNameList(SearchDomains); - return(mStatus_NoError); - - badparam: - err = mStatus_BadParamErr; +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, mDNSNULL); // 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 (%ld)", client, regtype, domain, errormsg, err); - if (SearchDomains) mDNS_FreeDNameList(SearchDomains); - return(err); - } + 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 == (mDNSInterfaceID)~0) 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; - - bzero(&interface, sizeof(interface)); - bzero(&address, sizeof(address)); - - if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv4) - { - struct sockaddr_in *sin = (struct sockaddr_in*)&interface; - sin->sin_len = sizeof(*sin); - sin->sin_family = AF_INET; - sin->sin_port = 0; - sin->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 *sin = (struct sockaddr_in*)&address; - sin->sin_len = sizeof(*sin); - sin->sin_family = AF_INET; - sin->sin_port = query->info->port.NotAnInteger; - sin->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; iinfo->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); - } +{ + kern_return_t status; + DNSServiceResolver *x = (DNSServiceResolver *)query->ServiceInfoQueryContext; + NetworkInterfaceInfoOSX *ifx = IfindexToInterfaceInfoOSX(m, query->info->InterfaceID); + if (query->info->InterfaceID == mDNSInterface_LocalOnly || query->info->InterfaceID == mDNSInterface_P2P) 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; iinfo->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 = (mDNSPlatformTimeNow() + 130 * mDNSPlatformOneSecond) | 1; - // Don't report errors for old iChat ("_ichat._tcp") service. - // New iChat ("_presence._tcp") uses DNSServiceQueryRecord() (from /usr/include/dns_sd.h) instead, - // and so should other applications that have valid reasons to be doing ongoing record monitoring. - if (SameDomainLabel(t.c, (mDNSu8*)"\x6_ichat")) x->ReportTime = 0; - x->next = DNSServiceResolverList; - DNSServiceResolverList = x; - - // Do the operation - LogOperation("%5d: DNSServiceResolver(%##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; + 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 (%ld)", client, name, regtype, domain, errormsg, err); - return(err); - } + LogMsg("%5d: DNSServiceResolve(\"%s\", \"%s\", \"%s\") failed: %s (%d)", client, name, regtype, domain, errormsg, err); + return(err); +} //************************************************************************************************************* // Registration -mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result) - { - DNSServiceRegistration *x = (DNSServiceRegistration*)sr->ServiceContext; - - if (result == mStatus_NoError) - { - kern_return_t status; - LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Registered", x->ClientMachPort, sr->RR_SRV.resrec.name.c, SRS_PORT(sr)); - status = DNSServiceRegistrationReply_rpc(x->ClientMachPort, result, MDNS_MM_TIMEOUT); - if (status == MACH_SEND_TIMED_OUT) - AbortBlockedClient(x->ClientMachPort, "registration success", x); - } - - else if (result == mStatus_NameConflict) - { - LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Conflict", x->ClientMachPort, sr->RR_SRV.resrec.name.c, SRS_PORT(sr)); - // 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 (x->autoname) - mDNS_RenameAndReregisterService(m, sr, mDNSNULL); - 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(x->ClientMachPort, result, MDNS_MM_TIMEOUT); - if (status == MACH_SEND_TIMED_OUT) - AbortBlockedClient(x->ClientMachPort, "registration conflict", x); - } - } - - else if (result == mStatus_MemFree) - { - mDNSBool *autorename = (sr == &x->ls ? &x->autorenameLS : &x->autorenameGS); - if (*autorename) - { - debugf("RegCallback renaming %#s to %#s", x->name.c, mDNSStorage.nicelabel.c); - *autorename = mDNSfalse; - x->name = mDNSStorage.nicelabel; - mDNS_RenameAndReregisterService(m, sr, &x->name); - } - else - { - // SANITY CHECK: Should only get mStatus_MemFree as a result of calling mDNS_DeregisterService() - // and should only get it with x->autorename false if we've already removed the record from our - // list, but this check is just to make sure... - DNSServiceRegistration **r = &DNSServiceRegistrationList; - while (*r && *r != x) r = &(*r)->next; - if (*r) - { - LogMsg("RegCallback: %##s Still in DNSServiceRegistration list; removing now", sr->RR_SRV.resrec.name.c); - *r = (*r)->next; - } - // END SANITY CHECK - LogOperation("%5d: DNSServiceRegistration(%##s, %u) Memory Free", x->ClientMachPort, sr->RR_SRV.resrec.name.c, SRS_PORT(sr)); - FreeDNSServiceRegistration(sr); - } - } - - else - { - LogMsg("%5d: DNSServiceRegistration(%##s, %u) Unknown Result %ld", - x->ClientMachPort, sr->RR_SRV.resrec.name.c, SRS_PORT(sr), result); - if (sr == x->gs) { freeL("RegCallback - ServiceRecordSet", x->gs); x->gs = NULL; } - } - } - -mDNSlocal void CheckForDuplicateRegistrations(DNSServiceRegistration *x, domainname *srv, mDNSIPPort port) - { - int count = 1; // Start with the one we're planning to register, then see if there are any more - AuthRecord *rr; - for (rr = mDNSStorage.ResourceRecords; rr; rr=rr->next) - if (rr->resrec.rrtype == kDNSType_SRV && - rr->resrec.rdata->u.srv.port.NotAnInteger == port.NotAnInteger && - SameDomainName(&rr->resrec.name, srv)) - count++; - - if (count > 1) - LogMsg("%5d: Client application registered %d identical instances of service %##s port %u.", - x->ClientMachPort, count, srv->c, mDNSVal16(port)); - } - -// Pass NULL for x to allocate the structure (for local service). Call again w/ initialized x to add a global service. -mDNSlocal DNSServiceRegistration *RegisterService(mach_port_t client, DNSCString name, DNSCString regtype, DNSCString domain, - int notAnIntPort, DNSCString txtRecord, DNSServiceRegistration *x) - { - ServiceRecordSet *srs = NULL; // record set to use in registration operation - mStatus err = mStatus_NoError; - const char *errormsg = "Unknown"; - +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, mDNSNULL); + 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; + si->srs.AnonData = mDNSNULL; + + 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, 0); + 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 = ®->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 - AuthRecord *SubTypes = mDNSNULL; - mDNSu32 i, NumSubTypes = 0; - char *comma = regtype; - while (*comma && *comma != ',') comma++; - if (*comma) // If we found a comma... - { - *comma = 0; // Overwrite the first comma with a nul - char *p = comma + 1; // Start scanning from the next character - while (*p) - { - if ( !(*p && *p != ',')) { errormsg = "Bad Service SubType"; goto badparam; } - while (*p && *p != ',') p++; - if (*p) *p++ = 0; - NumSubTypes++; - } - } - - // 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)) { errormsg = "Bad Domain"; goto badparam; } - if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; } - - mDNSIPPort port; - port.NotAnInteger = notAnIntPort; - - 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; - - // Allocate memory, and handle failure - if (!x) - { - x = mallocL("DNSServiceRegistration", sizeof(*x) - sizeof(RDataBody) + size); - if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } - bzero(x, sizeof(*x) - sizeof(RDataBody) + size); - // Set up object, and link into list - x->ClientMachPort = client; - x->autoname = (!name[0]); - x->name = n; - x->next = DNSServiceRegistrationList; - DNSServiceRegistrationList = x; - srs = &x->ls; - } - else - { - x->gs = mallocL("DNSServiceRegistration GlobalService", sizeof(ServiceRecordSet) - sizeof(RDataBody) + size); - if (!x->gs) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } - srs = x->gs; - bzero(srs, sizeof(ServiceRecordSet) - sizeof(RDataBody) + size); - } - - if (NumSubTypes) - { - SubTypes = mallocL("ServiceSubTypes", NumSubTypes * sizeof(AuthRecord)); - if (!SubTypes) { freeL("DNSServiceRegistration", x); err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } - for (i = 0; i < NumSubTypes; i++) - { - comma++; // Advance over the nul character - MakeDomainNameFromDNSNameString(&SubTypes[i].resrec.name, comma); - while (*comma) comma++; // Advance comma to point to the next terminating nul - } - } - - // Do the operation - LogOperation("%5d: DNSServiceRegistration(\"%s\", \"%s\", \"%s\", %u) START", - x->ClientMachPort, name, regtype, domain, mDNSVal16(port)); - // 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 (port.NotAnInteger) CheckForDuplicateRegistrations(x, &srv, port); - - err = mDNS_RegisterService(&mDNSStorage, srs, - &x->name, &t, &d, // Name, type, domain - mDNSNULL, port, // Host and port - txtinfo, data_len, // TXT data, length - SubTypes, NumSubTypes, // Subtypes - mDNSInterface_Any, // Interface ID - RegCallback, x); // Callback and context - - if (err) - { - if (srs == &x->ls) AbortClient(client, x); // don't abort client for global service - else FreeDNSServiceRegistration(x->gs); - errormsg = "mDNS_RegisterService"; - goto fail; - } - return x; - + size_t reglen = strlen(regtype) + 1; + if (reglen > MAX_ESCAPED_DOMAIN_NAME) { errormsg = "reglen too long"; goto badparam; } + mDNSs32 NumSubTypes = ChopSubTypes(regtype, mDNSNULL); // 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); + LogMsg("%5d: TXT record: %.100s...", client, txtRecord); badparam: - err = mStatus_BadParamErr; + err = mStatus_BadParamErr; fail: - LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%ld)", - client, name, regtype, domain, notAnIntPort, errormsg, err); - return NULL; - } - -mDNSexport kern_return_t provide_DNSServiceRegistrationCreate_rpc(mach_port_t unusedserver, mach_port_t client, - DNSCString name, DNSCString regtype, DNSCString domain, int notAnIntPort, DNSCString txtRecord) - { - // Check client parameter - (void)unusedserver; // Unused - mStatus err = mStatus_NoError; - const char *errormsg = "Unknown"; - DNSServiceRegistration *x = NULL; - 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; } - - x = RegisterService(client, name, regtype, *domain ? domain : "local.", notAnIntPort, txtRecord, NULL); - if (!x) { err = mStatus_UnknownErr; goto fail; } - - //!!!KRS if we got a dynamic reg domain from the config file, use it for default (except for iChat) - if (!*domain && mDNSStorage.uDNS_info.ServiceRegDomain[0] && strcmp(regtype, "_presence._tcp.") && strcmp(regtype, "_ichat._tcp.")) - x = RegisterService(client, name, regtype, mDNSStorage.uDNS_info.ServiceRegDomain, notAnIntPort, txtRecord, x); - - // Succeeded: Wrap up and return - EnableDeathNotificationForClient(client, x); - return(mStatus_NoError); + LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%d)", + client, name, regtype, domain, mDNSVal16(port), errormsg, err); + return(err); +} + +mDNSlocal void mDNSPreferencesSetNames(mDNS *const m, int key, domainlabel *old, domainlabel *new) +{ + domainlabel *prevold, *prevnew; + switch (key) + { + case kmDNSComputerName: + case kmDNSLocalHostName: + if (key == kmDNSComputerName) + { + prevold = &m->p->prevoldnicelabel; + prevnew = &m->p->prevnewnicelabel; + } + else + { + prevold = &m->p->prevoldhostlabel; + prevnew = &m->p->prevnewhostlabel; + } + // There are a few cases where we need to invoke the helper. + // + // 1. If the "old" label and "new" label are not same, it means there is a conflict. We need + // to invoke the helper so that it pops up a dialogue to inform the user about the + // conflict + // + // 2. If the "old" label and "new" label are same, it means the user has set the host/nice label + // through the preferences pane. We may have to inform the helper as it may have popped up + // a dialogue previously (due to a conflict) and it needs to suppress it now. We can avoid invoking + // the helper in this case if the previous values (old and new) that we told helper last time + // are same. If the previous old and new values are same, helper does not care. + // + // Note: "new" can be NULL when we have repeated conflicts and we are asking helper to give up. "old" + // is not called with NULL today, but this makes it future proof. + if (!old || !new || !SameDomainLabelCS(old->c, new->c) || + !SameDomainLabelCS(old->c, prevold->c) || + !SameDomainLabelCS(new->c, prevnew->c)) + { + if (old) + *prevold = *old; + else + prevold->c[0] = 0; + if (new) + *prevnew = *new; + else + prevnew->c[0] = 0; + mDNSPreferencesSetName(key, old, new); + } + else + { + LogInfo("mDNSPreferencesSetNames not invoking helper %s %#s, %s %#s, old %#s, new %#s", + (key == kmDNSComputerName ? "prevoldnicelabel" : "prevoldhostlabel"), prevold->c, + (key == kmDNSComputerName ? "prevnewnicelabel" : "prevnewhostlabel"), prevnew->c, + old->c, new->c); + } + break; + default: + LogMsg("mDNSPreferencesSetNames: unrecognized key: %d", key); + return; + } +} -fail: - LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%ld)", - client, name, regtype, domain, notAnIntPort, errormsg, err); - return mStatus_UnknownErr; - } - mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result) - { - (void)m; // Unused - if (result == mStatus_ConfigChanged) - { - DNSServiceRegistration *r; - for (r = DNSServiceRegistrationList; r; r=r->next) - if (r->autoname && !SameDomainLabel(r->name.c, mDNSStorage.nicelabel.c)) - { - debugf("NetworkChanged renaming %#s to %#s", r->name.c, mDNSStorage.nicelabel.c); - r->autorenameLS = mDNStrue; - mDNS_DeregisterService(&mDNSStorage, &r->ls); - if (r->gs) { mDNS_DeregisterService(&mDNSStorage, r->gs); r->autorenameGS = mDNStrue; } - } - udsserver_handle_configchange(); - } - else if (result == mStatus_GrowCache) - { - // If we've run out of cache space, then double the total cache size and give the memory to mDNSCore - mDNSu32 numrecords = m->rrcache_size; - CacheRecord *storage = mallocL("mStatus_GrowCache", sizeof(CacheRecord) * numrecords); - if (storage) mDNS_GrowCache(&mDNSStorage, storage, numrecords); - } - } +{ + (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 + mDNSPreferencesSetNames(m, 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. + mDNSPreferencesSetNames(m, kmDNSComputerName, &m->p->usernicelabel, &m->nicelabel); + mDNSPreferencesSetNames(m, 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_drt(m, &si->srs, mDNS_Dereg_rapid)) + RegCallback(m, &si->srs, mStatus_MemFree); // If service deregistered already, we can re-register immediately + } + } + } + + // 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 - -mDNSlocal ExtraResourceRecord *AddExtraRecord(DNSServiceRegistration *x, ServiceRecordSet *srs, mach_port_t client, - int type, const char *data, mach_msg_type_number_t data_len, uint32_t ttl) - { - mStatus err = mStatus_NoError; - const char *errormsg = "Unknown"; - domainname *name = (domainname *)""; - name = &srs->RR_SRV.resrec.name; - - (void)x; // unused - - unsigned int size = sizeof(RDataBody); - if (size < data_len) - size = data_len; - - // 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, srs->RR_SRV.resrec.name.c, type, data_len, extra); - err = mDNS_AddRecordToService(&mDNSStorage, srs, extra, &extra->r.rdatastorage, ttl); - - if (err) - { - freeL("Extra Resource Record", extra); - errormsg = "mDNS_AddRecordToService"; - goto fail; - } - - return extra; - -fail: - LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%ld)", client, name->c, type, data_len, errormsg, err); - return NULL; - } - - 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 - (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; - 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; } - - ExtraRecordRef *ref = mallocL("ExtraRecordRef", sizeof(ExtraRecordRef)); - if (!ref) { LogMsg("ERROR: malloc"); return mStatus_NoMemoryErr; } - - ref->localRef = AddExtraRecord(x, &x->ls, client, type, data, data_len, ttl); - if (!ref->localRef) { freeL("ExtraRecordRef", ref); *reference = (natural_t)NULL; return mStatus_UnknownErr; } - - if (x->gs) ref->globalRef = AddExtraRecord(x, x->gs, client, type, data, data_len, ttl); // return success even if global case fails - else ref->globalRef = NULL; - - // Succeeded: Wrap up and return - ref->next = x->ExtraRefList; - x->ExtraRefList = ref; - *reference = (natural_t)ref; - return mStatus_NoError; + 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, 0); + + 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 (%ld)", client, x->name.c, 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); - } + 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, mDNSu16 OldRDLen) +{ + (void)m; // Unused + (void)OldRDLen; // 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"; - domainname *name = (domainname *)""; + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + const domainname *name = (const domainname *)""; - name = &srs->RR_SRV.resrec.name; + name = srs->RR_SRV.resrec.name; - unsigned int size = sizeof(RDataBody); + 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); - - // 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); + 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 s", not "Zero or more 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 (%ld)", client, name->c, data_len, errormsg, err); - return(err); - } - + 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) - { + 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"; - domainname *name = (domainname *)""; - AuthRecord *gRR, *lRR; + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + const domainname *name = (const domainname *)""; + ServiceInstance *si; - (void)unusedserver; // unused + (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; } - - // Find the record we're updating. NULL reference means update the primary TXT record - if (!reference) - { - lRR = &x->ls.RR_TXT; - gRR = x->gs ? &x->gs->RR_TXT : NULL; - } - else - { - ExtraRecordRef *ref; - for (ref = x->ExtraRefList; ref; ref= ref->next) - if (ref == (ExtraRecordRef *)reference) break; - if (!ref) { err = mStatus_BadReferenceErr; errormsg = "No such record"; goto fail; } - lRR = &ref->localRef->r; - gRR = ref->globalRef ? &ref->globalRef->r : NULL; - } - - err = UpdateRecord(&x->ls, client, lRR, data, data_len, ttl); - if (err) goto fail; - - if (gRR) UpdateRecord(x->gs, client, gRR, data, data_len, ttl); // don't return error if global fails - return mStatus_NoError; - + 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 (%ld)", client, name->c, reference, data_len, errormsg, err); - return(err); - } + 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) - { - domainname *name = &srs->RR_SRV.resrec.name; - mStatus err = mStatus_NoError; - const char *errormsg = "Unknown"; - - // Do the operation - LogOperation("%5d: DNSServiceRegistrationRemoveRecord(%##s)", client, srs->RR_SRV.resrec.name.c); +{ + const domainname *const name = srs->RR_SRV.resrec.name; + mStatus err = mStatus_NoError; - err = mDNS_RemoveRecordFromService(&mDNSStorage, srs, extra); - if (err) { errormsg = "mDNS_RemoveRecordFromService (No such record)"; goto fail; } + // Do the operation + LogOperation("%5d: DNSServiceRegistrationRemoveRecord(%##s)", client, srs->RR_SRV.resrec.name->c); - // Succeeded: Wrap up and return - if (extra->r.resrec.rdata != &extra->r.rdatastorage) - freeL("Extra RData", extra->r.resrec.rdata); - freeL("ExtraResourceRecord", extra); - return(mStatus_NoError); + err = mDNS_RemoveRecordFromService(&mDNSStorage, srs, extra, FreeExtraRR, extra); + if (err) LogMsg("%5d: DNSServiceRegistrationRemoveRecord (%##s) failed: %d", client, name->c, err); -fail: - LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%##s, %X) failed: %s", client, name->c, errormsg, err); - return(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"; - ExtraRecordRef *ref, *prev = NULL; - 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; } - - ref = x->ExtraRefList; - while (ref) - { - if (ref == (ExtraRecordRef *)ref) break; - prev = ref; - ref = ref->next; - } - - if (!ref) { err = mStatus_BadReferenceErr; errormsg = "No such reference"; goto fail; } - err = RemoveRecord(&x->ls, ref->localRef, client); - if (x->gs && ref->globalRef) RemoveRecord(x->gs, ref->globalRef, client); // don't return error if this fails - - // delete the ref struct - if (prev) prev->next = ref->next; - else x->ExtraRefList = ref->next; - ref->next = NULL; - freeL("ExtraRecordRef", ref); - return err; + 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 (%ld)", client, reference, errormsg, err); - return(err); - } + LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%X) failed: %s (%d)", client, reference, errormsg, err); + return(err); +} //************************************************************************************************************* -// Support Code +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Startup, shutdown, and supporting code +#endif + +mDNSlocal void ExitCallback(int sig) +{ + (void)sig; // Unused + LogMsg("%s stopping", mDNSResponderVersionString); + + 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); +} + +#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM 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 - - /* 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)) - { +{ + 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 @@ -1651,8 +1616,8 @@ mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, vo * user or the remote one, we pretend it's ok. */ CFAllocatorDeallocate(NULL, reply); - return; - } + goto done; + } /* * destroy any out-of-line data in the request buffer but don't destroy @@ -1660,16 +1625,16 @@ mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, vo */ 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); - return; - } + goto done; + } /* * send reply. @@ -1687,537 +1652,1422 @@ mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, vo 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 */ + 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; - } + { + 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); - } - -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(CFMachPortRef port, void *msg, CFIndex size, void *info) - { - (void)port; // Unused - (void)msg; // Unused - (void)size; // Unused - (void)info; // Unused -/* - CacheRecord *rr; - int rrcache_active = 0; - for (rr = mDNSStorage.rrcache; rr; rr=rr->next) if (CacheRRActive(&mDNSStorage, rr)) rrcache_active++; - debugf("ExitCallback: RR Cache now using %d records, %d active", mDNSStorage.rrcache_used, rrcache_active); -*/ - - LogMsgIdent(mDNSResponderVersionString, "stopping"); - - debugf("ExitCallback: destroyBootstrapService"); - if (!mDNS_DebugMode) - 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); - - debugf("ExitCallback: mDNS_Close"); - mDNS_Close(&mDNSStorage); - if (udsserver_exit() < 0) LogMsg("ExitCallback: udsserver_exit failed"); - exit(0); - } + +done: + KQueueUnlock(&mDNSStorage, "Mach client event"); +} // Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit -mDNSlocal void HandleSIGTERM(int signal) - { - (void)signal; // Unused - debugf(" "); - debugf("SIGINT/SIGTERM"); - mach_msg_header_t header; - header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); - header.msgh_remote_port = exit_m_port; - header.msgh_local_port = MACH_PORT_NULL; - header.msgh_size = sizeof(header); - header.msgh_id = 0; - if (mach_msg_send(&header) != MACH_MSG_SUCCESS) - { LogMsg("HandleSIGTERM: mach_msg_send failed; Exiting immediately."); exit(-1); } - } - -mDNSlocal void INFOCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) - { - (void)port; // Unused - (void)msg; // Unused - (void)size; // Unused - (void)info; // Unused - DNSServiceDomainEnumeration *e; - DNSServiceBrowser *b; - DNSServiceResolver *l; - DNSServiceRegistration *r; - NetworkInterfaceInfoOSX *i; - mDNSu32 slot; - CacheRecord *rr; - mDNSu32 CacheUsed = 0, CacheActive = 0; - mDNSs32 now = mDNSPlatformTimeNow(); - - LogMsgIdent(mDNSResponderVersionString, "---- BEGIN STATE LOG ----"); - - for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) - { - mDNSu32 SlotUsed = 0; - for (rr = mDNSStorage.rrcache_hash[slot]; rr; rr=rr->next) - { - mDNSs32 remain = rr->resrec.rroriginalttl - (now - rr->TimeRcvd) / mDNSPlatformOneSecond; - CacheUsed++; - SlotUsed++; - if (rr->CRActiveQuestion) CacheActive++; - LogMsgNoIdent("%s%6ld %-6s%-6s%s", rr->CRActiveQuestion ? "*" : " ", remain, DNSTypeName(rr->resrec.rrtype), - ((NetworkInterfaceInfoOSX *)rr->resrec.InterfaceID)->ifa_name, GetRRDisplayString(&mDNSStorage, rr)); - usleep(1000); // Limit rate a little so we don't flood syslog too fast - } - if (mDNSStorage.rrcache_used[slot] != SlotUsed) - LogMsgNoIdent("Cache use mismatch: rrcache_used[slot] is %lu, true count %lu", mDNSStorage.rrcache_used[slot], SlotUsed); - } - if (mDNSStorage.rrcache_totalused != CacheUsed) - LogMsgNoIdent("Cache use mismatch: rrcache_totalused is %lu, true count %lu", mDNSStorage.rrcache_totalused, CacheUsed); - if (mDNSStorage.rrcache_active != CacheActive) - LogMsgNoIdent("Cache use mismatch: rrcache_active is %lu, true count %lu", mDNSStorage.rrcache_active, CacheActive); - LogMsgNoIdent("Cache currently contains %lu records; %lu referenced by active questions", CacheUsed, CacheActive); - - for (e = DNSServiceDomainEnumerationList; e; e=e->next) - LogMsgNoIdent("%5d: 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: ServiceBrowse %##s", b->ClientMachPort, qptr->q.qname.c); - } - for (l = DNSServiceResolverList; l; l=l->next) - LogMsgNoIdent("%5d: ServiceResolve %##s", l->ClientMachPort, l->i.name.c); - - for (r = DNSServiceRegistrationList; r; r=r->next) - { - LogMsgNoIdent("%5d: ServiceRegistration %##s %u", r->ClientMachPort, r->ls.RR_SRV.resrec.name.c, mDNSVal16(r->ls.RR_SRV.resrec.rdata->u.srv.port)); - if (r->gs) LogMsgNoIdent("%5d: ServiceRegistration %##s %u", r->ClientMachPort, r->gs->RR_SRV.resrec.name.c, mDNSVal16(r->gs->RR_SRV.resrec.rdata->u.srv.port)); - } - - udsserver_info(); - - for (i = mDNSStorage.p->InterfaceList; i; i = i->next) - { - if (!i->Exists) - LogMsgNoIdent("Interface: %s %5s(%lu) DORMANT", - i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifa_name, i->scope_id); - else - LogMsgNoIdent("Interface: %s %5s(%lu) %s %s %2d %s %2d InterfaceID %p %s %s %#a", - i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifa_name, i->scope_id, - i->ifinfo.InterfaceActive ? "Active" : " ", - i->ifinfo.IPv4Available ? "v4" : " ", i->ss.sktv4, - i->ifinfo.IPv6Available ? "v6" : " ", i->ss.sktv6, - i->ifinfo.InterfaceID, - i->ifinfo.Advertise ? "Adv" : " ", - i->ifinfo.McastTxRx ? "TxRx" : " ", - &i->ifinfo.ip); - } - - LogMsgIdent(mDNSResponderVersionString, "---- END STATE LOG ----"); - } - -mDNSlocal void HandleSIGINFO(int signal) - { - (void)signal; // Unused - mach_msg_header_t header; - header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); - header.msgh_remote_port = info_m_port; - header.msgh_local_port = MACH_PORT_NULL; - header.msgh_size = sizeof(header); - header.msgh_id = 0; - if (mach_msg_send(&header) != MACH_MSG_SUCCESS) - LogMsg("HandleSIGINFO: mach_msg_send failed; No state log will be generated."); - } +mDNSlocal void HandleSIG(int sig) +{ + kern_return_t status; + mach_msg_header_t header; + + // WARNING: can't call syslog or fprintf from signal handler + 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; + + status = mach_msg(&header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, header.msgh_size, + 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); + + if (status != MACH_MSG_SUCCESS) + { + if (status == MACH_SEND_TIMED_OUT) mach_msg_destroy(&header); + if (sig == SIGTERM || sig == SIGINT) exit(-1); + } +} + +#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + +mDNSlocal void INFOCallback(void) +{ + mDNSs32 utc = mDNSPlatformUTC(); + NetworkInterfaceInfoOSX *i; + DNSServer *s; + McastResolver *mr; + + // Create LoggerID(Key)->com.apple.networking.mDNSResponder(Value) pair when SIGINFO is received. + // This key-value pair is used as a condition by syslogd to Log to com.apple.networking.mDNSResponder.log file + // present in /etc/asl/com.apple.networking.mDNSResponder. + asl_set(log_msg, "LoggerID", "com.apple.networking.mDNSResponder"); + + LogMsg("---- BEGIN STATE LOG ---- %s %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers); + + udsserver_info(&mDNSStorage); + xpcserver_info(&mDNSStorage); + + LogMsgNoIdent("----- KQSocketEventSources -----"); + if (!gEventSources) LogMsgNoIdent(""); + else + { + KQSocketEventSource *k; + for (k = gEventSources; k; k=k->next) + LogMsgNoIdent("%3d %s %s", k->fd, k->kqs.KQtask, k->fd == mDNSStorage.uds_listener_skt ? "Listener for incoming UDS clients" : " "); + } + + LogMsgNoIdent("------ Network Interfaces ------"); + if (!mDNSStorage.p->InterfaceList) LogMsgNoIdent(""); + 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 %2ld, Registered %p, %s %-6s(%lu) %.6a %.6a %#-14a dormant for %d seconds", + i, i->ifinfo.InterfaceID, i->Registered, + 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 %2ld, Registered %p, %s %-6s(%lu) %.6a %.6a %s %s %-15.4a %s %s %s %s %#a", + i, i->ifinfo.InterfaceID, i->Registered, + 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 ? "A" : " ", + i->ifinfo.McastTxRx ? "M" : " ", + !(i->ifinfo.InterfaceActive && i->ifinfo.NetWake) ? " " : !sps[0] ? "p" : "P", + &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(%d) ----------", NumUnicastDNSServers); + if (!mDNSStorage.DNSServers) LogMsgNoIdent(""); + else + { + for (s = mDNSStorage.DNSServers; s; s = s->next) + { + NetworkInterfaceInfoOSX *ifx = IfindexToInterfaceInfoOSX(&mDNSStorage, s->interface); + LogMsgNoIdent("DNS Server %##s %s%s%#a:%d %d %s %d %d %s %s %s %s %s", + s->domain.c, ifx ? ifx->ifinfo.ifname : "", ifx ? " " : "", &s->addr, mDNSVal16(s->port), + s->penaltyTime ? s->penaltyTime - mDNS_TimeNow(&mDNSStorage) : 0, DNSScopeToString(s->scoped), + s->timeout, s->resGroupID, + s->teststate == DNSServer_Untested ? "(Untested)" : + s->teststate == DNSServer_Passed ? "" : + s->teststate == DNSServer_Failed ? "(Failed)" : + s->teststate == DNSServer_Disabled ? "(Disabled)" : "(Unknown state)", + s->req_A ? "v4" : "!v4", + s->req_AAAA ? "v6" : "!v6", + s->cellIntf ? "cell" : "!cell", + s->DNSSECAware ? "DNSSECAware" : "!DNSSECAware"); + } + } + mDNSs32 now = mDNS_TimeNow(&mDNSStorage); + LogMsgNoIdent("v4answers %d", mDNSStorage.p->v4answers); + LogMsgNoIdent("v6answers %d", mDNSStorage.p->v6answers); + LogMsgNoIdent("Last DNS Trigger: %d ms ago", (now - mDNSStorage.p->DNSTrigger)); + + LogMsgNoIdent("--------- Mcast Resolvers ----------"); + if (!mDNSStorage.McastResolvers) LogMsgNoIdent(""); + else + { + for (mr = mDNSStorage.McastResolvers; mr; mr = mr->next) + LogMsgNoIdent("Mcast Resolver %##s timeout %u", mr->domain.c, mr->timeout); + } + + LogMsgNoIdent("Timenow 0x%08lX (%d)", (mDNSu32)now, now); + LogMsg("---- END STATE LOG ---- %s %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers); + + // If logging is disabled, only then clear the key we set at the top of this func + if (!mDNS_LoggingEnabled) + asl_unset(log_msg, "LoggerID"); +} + +mDNSlocal void DebugSetFilter() +{ + if (!log_client) + return; + // When USR1 is turned on, we log only the LOG_WARNING and LOG_NOTICE messages by default. + // The user has to manually do "syslog -c mDNSResponder -i" to get the LOG_INFO messages + // also to be logged. Most of the times, we need the INFO level messages for debugging. + // Hence, we set the filter to INFO level when USR1 logging is turned on to avoid + // having the user to do this extra step manually. + + if (mDNS_LoggingEnabled) + { + asl_set_filter(log_client, ASL_FILTER_MASK_UPTO(ASL_LEVEL_INFO)); + asl_set(log_msg, "LoggerID", "com.apple.networking.mDNSResponder"); + // Create LoggerID(Key)->com.apple.networking.mDNSResponder(Value) pair when USR1 Logging is Enabled. + // This key-value pair is used as a condition by syslogd to Log to com.apple.networking.mDNSResponder.log file + // present in /etc/asl/com.apple.networking.mDNSResponder. + } + else + { + asl_set_filter(log_client, ASL_FILTER_MASK_UPTO(ASL_LEVEL_ERR)); + asl_unset(log_msg, "LoggerID"); + // Clear the key-value pair when USR1 Logging is Disabled, as we do not want to log to + // com.apple.networking.mDNSResponder.log file in this case. + } +} + +mDNSexport void mDNSPlatformLogToFile(int log_level, const char *buffer) +{ + int asl_level = ASL_LEVEL_ERR; + + if (!log_client) + { + syslog(log_level, "%s", buffer); + return; + } + switch (log_level) + { + case LOG_ERR: + asl_level = ASL_LEVEL_ERR; + break; + case LOG_WARNING: + asl_level = ASL_LEVEL_WARNING; + break; + case LOG_NOTICE: + asl_level = ASL_LEVEL_NOTICE; + break; + case LOG_INFO: + asl_level = ASL_LEVEL_INFO; + break; + case LOG_DEBUG: + asl_level = ASL_LEVEL_DEBUG; + break; + default: + break; + } + asl_log(log_client, log_msg, asl_level, "%s", buffer); +} + +// Writes the state out to the dynamic store and also affects the ASL filter level +mDNSexport void UpdateDebugState() +{ + mDNSu32 one = 1; + mDNSu32 zero = 0; + + CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (!dict) + { + LogMsg("UpdateDebugState: Could not create dict"); + return; + } + + CFNumberRef numOne = CFNumberCreate(NULL, kCFNumberSInt32Type, &one); + if (!numOne) + { + LogMsg("UpdateDebugState: Could not create CFNumber one"); + return; + } + CFNumberRef numZero = CFNumberCreate(NULL, kCFNumberSInt32Type, &zero); + if (!numZero) + { + LogMsg("UpdateDebugState: Could not create CFNumber zero"); + CFRelease(numOne); + return; + } + + if (mDNS_LoggingEnabled) + CFDictionarySetValue(dict, CFSTR("VerboseLogging"), numOne); + else + CFDictionarySetValue(dict, CFSTR("VerboseLogging"), numZero); + + if (mDNS_PacketLoggingEnabled) + CFDictionarySetValue(dict, CFSTR("PacketLogging"), numOne); + else + CFDictionarySetValue(dict, CFSTR("PacketLogging"), numZero); + + if (mDNS_McastLoggingEnabled) + CFDictionarySetValue(dict, CFSTR("McastLogging"), numOne); + else + CFDictionarySetValue(dict, CFSTR("McastLogging"), numZero); + + if (mDNS_McastTracingEnabled) + CFDictionarySetValue(dict, CFSTR("McastTracing"), numOne); + else + CFDictionarySetValue(dict, CFSTR("McastTracing"), numZero); + + CFRelease(numOne); + CFRelease(numZero); + mDNSDynamicStoreSetConfig(kmDNSDebugState, mDNSNULL, dict); + CFRelease(dict); + // If we turned off USR1 logging, we need to reset the filter + DebugSetFilter(); +} + +#if TARGET_OS_EMBEDDED +mDNSlocal void Prefschanged() +{ + mDNSBool mDNSProf_installed; + LogMsg("Prefschanged: mDNSResponder Managed Preferences have changed"); + mDNSProf_installed = GetmDNSManagedPref(kmDNSEnableLoggingStr); + dispatch_async(dispatch_get_main_queue(), + ^{ + if (mDNSProf_installed) + { + mDNS_LoggingEnabled = mDNS_PacketLoggingEnabled = 1; + } + else + { + LogMsg("Prefschanged: mDNSDebugProfile is uninstalled -> Turning OFF USR1/USR2 Logging with SIGINFO o/p"); + INFOCallback(); + mDNS_LoggingEnabled = mDNS_PacketLoggingEnabled = 0; + } + UpdateDebugState(); + // If Logging Enabled: Start Logging to com.apple.networking.mDNSResponder.log (has to be LogInfo) + LogInfo("Prefschanged: mDNSDebugProfile is installed -> Turned ON USR1/USR2 Logging"); + }); + return; +} +#endif //TARGET_OS_EMBEDDED + +#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + +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 SIGURG: + m->mDNSOppCaching = m->mDNSOppCaching ? mDNSfalse : mDNStrue; + LogMsg("SIGURG: Opportunistic Caching %s", m->mDNSOppCaching ? "Enabled" : "Disabled"); + // FALL THROUGH to purge the cache so that we re-do the caching based on the new setting + 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; + UpdateDebugState(); + // If Logging Enabled: Start Logging to com.apple.networking.mDNSResponder.log + LogInfo("USR1 Logging Enabled: Start Logging to mDNSResponder Log file"); + break; + case SIGUSR2: mDNS_PacketLoggingEnabled = mDNS_PacketLoggingEnabled ? 0 : 1; + LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled"); + mDNS_McastTracingEnabled = (mDNS_PacketLoggingEnabled && mDNS_McastLoggingEnabled) ? mDNStrue : mDNSfalse; + LogInfo("SIGUSR2: Multicast Tracing is %s", mDNS_McastTracingEnabled ? "Enabled" : "Disabled"); + UpdateDebugState(); + break; + case SIGPROF: mDNS_McastLoggingEnabled = mDNS_McastLoggingEnabled ? mDNSfalse : mDNStrue; + LogMsg("SIGPROF: Multicast Logging %s", mDNS_McastLoggingEnabled ? "Enabled" : "Disabled"); + LogMcastStateInfo(m, mDNSfalse, mDNStrue, mDNStrue); + mDNS_McastTracingEnabled = (mDNS_PacketLoggingEnabled && mDNS_McastLoggingEnabled) ? mDNStrue : mDNSfalse; + LogMsg("SIGPROF: Multicast Tracing is %s", mDNS_McastTracingEnabled ? "Enabled" : "Disabled"); + UpdateDebugState(); + break; + case SIGTSTP: mDNS_LoggingEnabled = mDNS_PacketLoggingEnabled = mDNS_McastLoggingEnabled = mDNS_McastTracingEnabled = mDNSfalse; + LogMsg("All mDNSResponder Debug Logging/Tracing Disabled (USR1/USR2/PROF)"); + UpdateDebugState(); + break; + + default: LogMsg("SignalCallback: Unknown signal %d", msg_header->msgh_id); break; + } + KQueueUnlock(m, "Unix Signal"); +} + +// MachServerName is com.apple.mDNSResponder (Supported only till 10.9.x) mDNSlocal kern_return_t mDNSDaemonInitialize(void) - { - mStatus err; - CFMachPortRef d_port = CFMachPortCreate(NULL, ClientDeathCallback, NULL, NULL); - CFMachPortRef s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL); - CFMachPortRef e_port = CFMachPortCreate(NULL, ExitCallback, NULL, NULL); - CFMachPortRef i_port = CFMachPortCreate(NULL, INFOCallback, NULL, NULL); - mach_port_t m_port = CFMachPortGetPort(s_port); - char *MachServerName = mDNSMacOSXSystemBuildNumber(NULL) < 7 ? "DNSServiceDiscoveryServer" : "com.apple.mDNSResponder"; - kern_return_t status = bootstrap_register(bootstrap_port, MachServerName, m_port); - CFRunLoopSourceRef d_rls = CFMachPortCreateRunLoopSource(NULL, d_port, 0); - CFRunLoopSourceRef s_rls = CFMachPortCreateRunLoopSource(NULL, s_port, 0); - CFRunLoopSourceRef e_rls = CFMachPortCreateRunLoopSource(NULL, e_port, 0); - CFRunLoopSourceRef i_rls = CFMachPortCreateRunLoopSource(NULL, i_port, 0); - - 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); - } - - err = mDNS_Init(&mDNSStorage, &PlatformStorage, - rrcachestorage, RR_CACHE_SIZE, - mDNS_Init_AdvertiseLocalAddresses, - mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext); +{ + mStatus err; + CFMachPortRef s_port; + CFRunLoopSourceRef s_rls; + CFRunLoopSourceRef d_rls; + + s_port = CFMachPortCreateWithPort(NULL, m_port, DNSserverCallback, NULL, NULL); + CFMachPortRef d_port = CFMachPortCreate(NULL, ClientDeathCallback, NULL, NULL); + + 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); - if (err) { LogMsg("Daemon start: mDNS_Init failed %ld", err); return(err); } - - client_death_port = CFMachPortGetPort(d_port); - exit_m_port = CFMachPortGetPort(e_port); - info_m_port = CFMachPortGetPort(i_port); - - CFRunLoopAddSource(CFRunLoopGetCurrent(), d_rls, kCFRunLoopDefaultMode); - CFRunLoopAddSource(CFRunLoopGetCurrent(), s_rls, kCFRunLoopDefaultMode); - CFRunLoopAddSource(CFRunLoopGetCurrent(), e_rls, kCFRunLoopDefaultMode); - CFRunLoopAddSource(CFRunLoopGetCurrent(), i_rls, kCFRunLoopDefaultMode); - CFRelease(d_rls); - CFRelease(s_rls); - CFRelease(e_rls); - CFRelease(i_rls); - if (mDNS_DebugMode) printf("Service registered with Mach Port %d\n", m_port); - err = udsserver_init(&mDNSStorage); - if (err) { LogMsg("Daemon start: udsserver_init failed"); return err; } - return(err); - } - -mDNSlocal mDNSs32 mDNSDaemonIdle(void) - { - // 1. Call mDNS_Execute() to let mDNSCore do what it needs to do - mDNSs32 nextevent = mDNS_Execute(&mDNSStorage); - - mDNSs32 now = mDNSPlatformTimeNow(); - - // 2. 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; - LogMsg("%5d: DNSServiceResolver(%##s) active for over two minutes. " - "This places considerable burden on the network.", l->ClientMachPort, l->i.name.c); - } - - return(nextevent); - } + s_rls = CFMachPortCreateRunLoopSource(NULL, s_port, 0); + CFRunLoopAddSource(PlatformStorage.CFRunLoop, s_rls, kCFRunLoopDefaultMode); + CFRelease(s_rls); + + d_rls = CFMachPortCreateRunLoopSource(NULL, d_port, 0); + CFRunLoopAddSource(PlatformStorage.CFRunLoop, d_rls, kCFRunLoopDefaultMode); + CFRelease(d_rls); + + CFMachPortRef i_port = CFMachPortCreate(NULL, SignalCallback, NULL, NULL); + CFRunLoopSourceRef i_rls = CFMachPortCreateRunLoopSource(NULL, i_port, 0); + signal_port = CFMachPortGetPort(i_port); + CFRunLoopAddSource(PlatformStorage.CFRunLoop, i_rls, kCFRunLoopDefaultMode); + CFRelease(i_rls); + + if (mDNS_DebugMode) printf("Service registered with Mach Port %d\n", m_port); + return(err); +} + +#else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + +// SignalDispatch is mostly just a copy/paste of entire code block from SignalCallback above. +// The common code should be a subroutine, or we end up having to fix bugs in two places all the time. +// The same applies to mDNSDaemonInitialize, much of which is just a copy/paste of chunks +// of code from above. Alternatively we could remove the duplicated source code by having +// single routines, with the few differing parts bracketed with "#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM" + +mDNSlocal void SignalDispatch(dispatch_source_t source) +{ + int sig = (int)dispatch_source_get_handle(source); + mDNS *const m = &mDNSStorage; + KQueueLock(m); + switch(sig) + { + 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(sig); 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; + UpdateDebugState(); + break; + case SIGUSR2: mDNS_PacketLoggingEnabled = mDNS_PacketLoggingEnabled ? 0 : 1; + LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled"); + UpdateDebugState(); + break; + default: LogMsg("SignalCallback: Unknown signal %d", sig); break; + } + KQueueUnlock(m, "Unix Signal"); +} + +mDNSlocal void mDNSSetupSignal(dispatch_queue_t queue, int sig) +{ + signal(sig, SIG_IGN); + dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, sig, 0, queue); + + if (source) + { + dispatch_source_set_event_handler(source, ^{SignalDispatch(source);}); + // Start processing signals + dispatch_resume(source); + } + else + { + LogMsg("mDNSSetupSignal: Cannot setup signal %d", sig); + } +} + +// 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; + dispatch_source_t mach_source; + dispatch_queue_t queue = dispatch_get_main_queue(); + + 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); } + + mach_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, m_port, 0, queue); + if (mach_source == mDNSNULL) {LogMsg("mDNSDaemonInitialize: Error creating source for m_port"); return -1;} + dispatch_source_set_event_handler(mach_source, ^{ + dispatch_mig_server(mach_source, sizeof(union __RequestUnion__DNSServiceDiscoveryReply_subsystem), + DNSServiceDiscoveryRequest_server); + }); + dispatch_resume(mach_source); + + mDNSSetupSignal(queue, SIGHUP); + mDNSSetupSignal(queue, SIGINT); + mDNSSetupSignal(queue, SIGTERM); + mDNSSetupSignal(queue, SIGINFO); + mDNSSetupSignal(queue, SIGUSR1); + mDNSSetupSignal(queue, SIGUSR2); + mDNSSetupSignal(queue, SIGURG); + + // Create a custom handler for doing the housekeeping work. This is either triggered + // by the timer or an event source + PlatformStorage.custom = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, queue); + if (PlatformStorage.custom == mDNSNULL) {LogMsg("mDNSDaemonInitialize: Error creating custom source"); return -1;} + dispatch_source_set_event_handler(PlatformStorage.custom, ^{PrepareForIdle(&mDNSStorage);}); + dispatch_resume(PlatformStorage.custom); + + // Create a timer source to trigger housekeeping work. The houskeeping work itself + // is done in the custom handler that we set below. + + PlatformStorage.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); + if (PlatformStorage.timer == mDNSNULL) {LogMsg("mDNSDaemonInitialize: Error creating timer source"); return -1;} + + // As the API does not support one shot timers, we pass zero for the interval. In the custom handler, we + // always reset the time to the new time computed. In effect, we ignore the interval + dispatch_source_set_timer(PlatformStorage.timer, DISPATCH_TIME_NOW, 1000ull * 1000000000, 0); + dispatch_source_set_event_handler(PlatformStorage.timer, ^{ + dispatch_source_merge_data(PlatformStorage.custom, 1); + }); + dispatch_resume(PlatformStorage.timer); + + LogMsg("DaemonIntialize done successfully"); + + if (mDNS_DebugMode) printf("Service registered with Mach Port %d\n", m_port); + return(err); +} + +#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + +mDNSlocal mDNSs32 mDNSDaemonIdle(mDNS *const m) +{ + mDNSs32 now = mDNS_TimeNow(m); + + // 1. If we need to set domain secrets, do so before handling the network change + // Detailed reason: + // BTMM domains listed in DynStore Setup:/Network/BackToMyMac are added to the registration domains list, + // and we need to setup the associated AutoTunnel DomainAuthInfo entries before that happens. + if (m->p->KeyChainTimer && now - m->p->KeyChainTimer >= 0) + { + m->p->KeyChainTimer = 0; + mDNS_Lock(m); + SetDomainSecrets(m); + mDNS_Unlock(m); + } + + // 2. If we have network change events to handle, do them 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); } + + // 3. 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->KeyChainTimer) + if (nextevent - m->p->KeyChainTimer > 0) + nextevent = m->p->KeyChainTimer; + + if (m->p->RequestReSleep) + if (nextevent - m->p->RequestReSleep > 0) + nextevent = m->p->RequestReSleep; + + // 4. 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); + mDNSPreferencesSetNames(m, 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); + mDNSPreferencesSetNames(m, 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 + { + int ic, j; + + const void *pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetDHCP); + if (!pattern) + { + LogMsg("DHCPWakeTime: SCDynamicStoreKeyCreateNetworkServiceEntity failed\n"); + return e; + } + CFArrayRef dhcpinfo = CFArrayCreate(NULL, (const void **)&pattern, 1, &kCFTypeArrayCallBacks); + CFRelease(pattern); + if (dhcpinfo) + { + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("DHCP-LEASES"), NULL, NULL); + if (store) + { + CFDictionaryRef dict = SCDynamicStoreCopyMultiple(store, NULL, dhcpinfo); + if (dict) + { + ic = CFDictionaryGetCount(dict); + const void *vals[ic]; + CFDictionaryGetKeysAndValues(dict, NULL, vals); + + for (j = 0; j < ic; j++) + { + const CFDictionaryRef dhcp = (CFDictionaryRef)vals[j]; + 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", + j, start, lease, lease ? CFDataGetLength(lease) : 0); + else + { + const UInt8 *d = CFDataGetBytePtr(lease); + if (!d) LogMsg("DHCPWakeTime: CFDataGetBytePtr %d failed", j); + 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(dict); + } + CFRelease(store); + } + CFRelease(dhcpinfo); + } + } + 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, now); + 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) + { + int r; + LogMsg("AllowSleepNow: 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. + // We preserve the value of "result" because if our original power request was 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); + r = mDNSPowerRequest(1, interval); + } + while (r == kIOReturnNotReady); + if (r) LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful: %d %X", interval, r, r); + else LogSPS("AllowSleepNow: Requested later wakeup in %d seconds; will also attempt IOCancelPowerChange", interval); + } + else + { + 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; + } + } + + m->SleepState = SleepState_Sleeping; + // 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 + mDNSMacOSXNetworkChanged(m); + } + + LogSPS("AllowSleepNow: %s(%lX) %s at %ld (%d ticks remaining)", +#if !TARGET_OS_EMBEDDED && defined(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 + m->TimeSlept = mDNSPlatformUTC(); + + // accumulate total time awake for this statistics gathering interval + if (m->StatStartTime) + { + m->ActiveStatTime += (m->TimeSlept - m->StatStartTime); + + // indicate this value is invalid until reinitialzed on wakeup + m->StatStartTime = 0; + } + +#if !TARGET_OS_EMBEDDED && defined(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); +} + +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + +mDNSexport void TriggerEventCompletion() +{ + debugf("TriggerEventCompletion: Merge data"); + dispatch_source_merge_data(PlatformStorage.custom, 1); +} + +mDNSlocal void PrepareForIdle(void *m_param) +{ + mDNS *m = m_param; + int64_t time_offset; + dispatch_time_t dtime; + + const int multiplier = 1000000000 / mDNSPlatformOneSecond; + + // 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 + + debugf("PrepareForIdle: called"); + // 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("CustomSourceHandler: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 (mDNS_ExitNow(m, now)) + { + LogInfo("IdleLoop: 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; } + } + + time_offset = ((mDNSu32)ticks / mDNSPlatformOneSecond) * 1000000000 + (ticks % mDNSPlatformOneSecond) * multiplier; + dtime = dispatch_time(DISPATCH_TIME_NOW, time_offset); + dispatch_source_set_timer(PlatformStorage.timer, dtime, 1000ull*1000000000, 0); + debugf("PrepareForIdle: scheduling timer with ticks %d", ticks); + return; +} + +#else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + +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 SetLowWater(const KQSocketSet *const k, const int r) +{ + if (setsockopt(k->sktv4, SOL_SOCKET, SO_RCVLOWAT, &r, sizeof(r)) < 0) + LogMsg("SO_RCVLOWAT IPv4 %d error %d errno %d (%s)", k->sktv4, r, errno, strerror(errno)); + if (setsockopt(k->sktv6, SOL_SOCKET, SO_RCVLOWAT, &r, sizeof(r)) < 0) + LogMsg("SO_RCVLOWAT IPv6 %d error %d errno %d (%s)", k->sktv6, r, errno, strerror(errno)); +} + +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) + { + AuthRecord *rr; + for (rr = mDNSStorage.ResourceRecords; rr; rr=rr->next) + { + LogInfo("Cannot exit yet; Resource Record still exists: %s", ARDisplayString(m, rr)); + if (mDNS_LoggingEnabled) usleep(10000); // Sleep 10ms so that we don't flood syslog with too many messages + } + } + if (mDNS_ExitNow(m, now)) + { + 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 we woke up to receive a multicast, set low-water mark to dampen excessive wakeup rate + if (m->p->num_mcasts) + { + SetLowWater(&m->p->permanentsockets, 0x10000); + if (ticks > mDNSPlatformOneSecond / 8) ticks = mDNSPlatformOneSecond / 8; + } + +#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. + + // If we changed UDP socket low-water mark, restore it, so we will be told about every packet + if (m->p->num_mcasts) + { + SetLowWater(&m->p->permanentsockets, 1); + m->p->num_mcasts = 0; + } + + 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; +} + +#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + +mDNSlocal void LaunchdCheckin(void) +{ + // Ask launchd for our socket + launch_data_t resp_sd = launch_socket_service_check_in(); + if (!resp_sd) + { + LogMsg("launch_socket_service_check_in returned NULL"); + return; + } + else + { + launch_data_t skts = launch_data_dict_lookup(resp_sd, 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_free(resp_sd); +} + +static mach_port_t RegisterMachService(const char *service_name) +{ + mach_port_t port = MACH_PORT_NULL; + kern_return_t kr; + + if (KERN_SUCCESS != (kr = bootstrap_check_in(bootstrap_port, (char *)service_name, &port))) + { + LogMsg("RegisterMachService: %d %X %s", kr, kr, mach_error_string(kr)); + return MACH_PORT_NULL; + } + + if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND))) + { + LogMsg("RegisterMachService: %d %X %s", kr, kr, mach_error_string(kr)); + mach_port_deallocate(mach_task_self(), port); + return MACH_PORT_NULL; + } + + return port; +} + +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; - - for (i=1; ipw_uid); - else - setuid(-2); // User "nobody" is -2; use that value if "nobody" does not appear in the password database - - if (status == 0) - { - int numevents = 0; - int RunLoopStatus = kCFRunLoopRunTimedOut; - - // 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 - while (RunLoopStatus == kCFRunLoopRunTimedOut) - { - // 1. Before going into a blocking wait call and letting our process to go sleep, - // call mDNSDaemonIdle to allow any deferred work to be completed. - mDNSs32 nextevent = mDNSDaemonIdle(); - nextevent = udsserver_idle(nextevent); - - // 2. Work out how long we expect to sleep before the next scheduled task - mDNSs32 ticks = nextevent - mDNSPlatformTimeNow(); - static mDNSs32 RepeatedBusy = 0; // Debugging sanity check, to guard against CPU spins - if (ticks > 1) - RepeatedBusy = 0; - else - { - ticks = 1; - if (++RepeatedBusy >= mDNSPlatformOneSecond * 10) - { LogMsg("Task Scheduling Error: Continuously busy for the last ten seconds"); RepeatedBusy = 0; } - } - CFAbsoluteTime interval = (CFAbsoluteTime)ticks / (CFAbsoluteTime)mDNSPlatformOneSecond; - - // 3. Now do a blocking "CFRunLoopRunInMode" call so we sleep until - // (a) our next wakeup time, or (b) an event occurs. - // The 'true' parameter makes it return after handling any event that occurs - // This gives us chance to regain control so we can call mDNS_Execute() before sleeping again - verbosedebugf("main: Handled %d events; now sleeping for %d ticks", numevents, ticks); - numevents = 0; - RunLoopStatus = CFRunLoopRunInMode(kCFRunLoopDefaultMode, interval, true); - - // 4. Time to do some work? Handle all remaining events as quickly as we can, before returning to mDNSDaemonIdle() - while (RunLoopStatus == kCFRunLoopRunHandledSource) - { - numevents++; - RunLoopStatus = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, true); - } - } - - LogMsg("ERROR: CFRunLoopRun Exiting."); - mDNS_Close(&mDNSStorage); - } - - if (!mDNS_DebugMode) destroyBootstrapService(); - - return(status); - } - -// uds_daemon.c support routines ///////////////////////////////////////////// - -// We keep a list of client-supplied event sources in PosixEventSource records -struct CFSocketEventSource - { - udsEventCallback Callback; - void *Context; - int fd; - struct CFSocketEventSource *Next; - CFSocketRef SocketRef; - CFRunLoopSourceRef RLS; - }; -typedef struct CFSocketEventSource CFSocketEventSource; - -static GenLinkedList gEventSources; // linked list of CFSocketEventSource's - -static void cf_callback(CFSocketRef s _UNUSED, CFSocketCallBackType t _UNUSED, CFDataRef dr _UNUSED, const void *c _UNUSED, void *i) - // Called by CFSocket when data appears on socket - { - CFSocketEventSource *source = (CFSocketEventSource*) i; - source->Callback(source->Context); - } - -mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *context) - // Arrange things so that callback is called with context when data appears on fd - { - CFSocketEventSource *newSource; - CFSocketContext cfContext = { 0, NULL, NULL, NULL, NULL }; +{ + int i; + kern_return_t status; + + mDNSMacOSXSystemBuildNumber(NULL); + LogMsg("%s starting %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers); + +#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 + + if (0 == geteuid()) + { + LogMsg("mDNSResponder cannot be run as root !! Exiting.."); + return -1; + } + + for (i=1; i= FD_SETSIZE || fd < 0) - return mStatus_UnsupportedErr; - if (callback == NULL) - return mStatus_BadParamErr; - - newSource = (CFSocketEventSource*) calloc(1, sizeof *newSource); - if (NULL == newSource) - return mStatus_NoMemoryErr; - - newSource->Callback = callback; - newSource->Context = context; - newSource->fd = fd; - - cfContext.info = newSource; - if ( NULL != (newSource->SocketRef = CFSocketCreateWithNative(kCFAllocatorDefault, fd, kCFSocketReadCallBack, - cf_callback, &cfContext)) && - NULL != (newSource->RLS = CFSocketCreateRunLoopSource(kCFAllocatorDefault, newSource->SocketRef, 0))) - { - CFRunLoopAddSource(CFRunLoopGetCurrent(), newSource->RLS, kCFRunLoopDefaultMode); - AddToTail(&gEventSources, newSource); - } - else - { - if (newSource->SocketRef) - { - CFSocketInvalidate(newSource->SocketRef); // automatically closes socket - CFRelease(newSource->SocketRef); - } - return mStatus_NoMemoryErr; - } - - return mStatus_NoError; - } - -mStatus udsSupportRemoveFDFromEventLoop(int fd) - // Reverse what was done in udsSupportAddFDToEventLoop(). - { - CFSocketEventSource *iSource; - - for (iSource=(CFSocketEventSource*)gEventSources.Head; iSource; iSource = iSource->Next) - { - if (fd == iSource->fd) - { - RemoveFromList(&gEventSources, iSource); - CFRunLoopRemoveSource(CFRunLoopGetCurrent(), iSource->RLS, kCFRunLoopDefaultMode); - CFRunLoopSourceInvalidate(iSource->RLS); - CFRelease(iSource->RLS); - CFSocketInvalidate(iSource->SocketRef); - CFRelease(iSource->SocketRef); - free(iSource); - return mStatus_NoError; - } - } - return mStatus_NoSuchNameErr; - } +#if TARGET_OS_EMBEDDED + _scprefs_observer_watch(scprefs_observer_type_global, kmDNSResponderPrefIDStr, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), + ^{ + Prefschanged(); + }); +#endif + + mDNSMacOSXNetworkChanged(&mDNSStorage); + UpdateDebugState(); + +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + LogInfo("Daemon Start: Using LibDispatch"); + // CFRunLoopRun runs both CFRunLoop sources and dispatch sources + CFRunLoopRun(); +#else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + // Start the kqueue thread + pthread_t KQueueThread; + 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); + } +#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + + LogMsg("%s exiting", mDNSResponderVersionString); + +exit: + 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, void **platform_data) +{ + KQSocketEventSource **p = &gEventSources; + (void) platform_data; + 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"; +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + newSource->kqs.readSource = mDNSNULL; + newSource->kqs.writeSource = mDNSNULL; + newSource->kqs.fdClosed = mDNSfalse; +#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + + 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; +} + +int udsSupportReadFD(dnssd_sock_t fd, char *buf, int len, int flags, void *platform_data) +{ + (void) platform_data; + return recv(fd, buf, len, flags); +} + +mDNSexport mStatus udsSupportRemoveFDFromEventLoop(int fd, void *platform_data) // Note: This also CLOSES the file descriptor +{ + KQSocketEventSource **p = &gEventSources; + (void) platform_data; + 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 + mDNSPlatformCloseFD(&s->kqs, 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 -mDNSexport const char mDNSResponderVersionString[] = STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; +// The "@(#) " pattern is a special prefix the "what" command looks for +mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";