include /Developer/Makefiles/pb_makefiles/platform.make
-MVERS = "mDNSResponder-58.1"
+MVERS = "mDNSResponder-58.3"
install:
cd "$(SRCROOT)/mDNSMacOSX"; pbxbuild install OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS)
*
* @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
Change History (most recent first):
$Log: mDNS.c,v $
+Revision 1.307.2.2 2003/12/20 01:51:40 cheshire
+<rdar://problem/3515876>: Error putting additional records into packets
+Another fix from Rampi: responseptr needs to be updated inside the "for" loop,
+after every record, not once at the end.
+
Revision 1.307.2.1 2003/12/03 11:20:27 cheshire
<rdar://problem/3457718>: Stop and start of a service uses old ip address (with old port number)
rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it
}
newptr = PutResourceRecord(&response, newptr, &response.h.numAdditionals, &rr->resrec);
+ if (newptr) responseptr = newptr;
rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state
}
}
- if (newptr) responseptr = newptr;
if (response.h.numAnswers > 0) // We *never* send a packet with only additionals in it
{
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
Change History (most recent first):
$Log: daemon.c,v $
+Revision 1.134.2.3 2003/12/12 01:21:30 cheshire
+<rdar://problem/3491108> mDNSResponder should not run as root
+
Revision 1.134.2.2 2003/12/05 00:03:35 cheshire
<rdar://problem/3487869> Use buffer size MAX_ESCAPED_DOMAIN_NAME instead of 256
#include <unistd.h>
#include <paths.h>
#include <fcntl.h>
+#include <pwd.h>
#include "DNSServiceDiscoveryRequestServer.h"
#include "DNSServiceDiscoveryReply.h"
LogMsg("%s starting", mDNSResponderVersionString);
status = mDNSDaemonInitialize();
+ // Now that we're finished with anything privileged, switch over to running as "nobody"
+ const struct passwd *pw = getpwnam( "nobody");
+ if ( pw != NULL)
+ setuid( pw->pw_uid);
+ else
+ status = mStatus_Incompatible; // refuse to run as root
+
if (status == 0)
{
int numevents = 0;
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
--- /dev/null
+/*
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * 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
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+
+ Change History (most recent first):
+
+$Log: dns_sd.h,v $
+Revision 1.3 2003/08/12 19:51:51 cheshire
+Update to APSL 2.0
+
+
+ */
+
+#ifndef _DNS_SD_H
+#define _DNS_SD_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdint.h>
+#include <netinet/in.h>
+
+
+/* DNSServiceRef, DNSRecordRef
+ *
+ * Opaque internal data types.
+ * Note: client is responsible for serializing access to these structures if
+ * they are shared between concurrent threads.
+ */
+
+typedef struct _DNSServiceRef_t *DNSServiceRef;
+typedef struct _DNSRecordRef_t *DNSRecordRef;
+
+/* General flags used in functions defined below */
+enum
+ {
+ kDNSServiceFlagsMoreComing = 1,
+ kDNSServiceFlagsFinished = 0, /* i.e. bit not set */
+ /* MoreComing indicates to a Browse callback that another result is
+ * queued. Applications should not update their UI to display browse
+ * results when the MoreComing flag is set, instead deferring the update
+ * until the callback's flag is Finished. */
+
+ kDNSServiceFlagsAdd = 2,
+ kDNSServiceFlagsDefault = 4,
+ kDNSServiceFlagsRemove = 0, /* i.e. bit not set */
+ /* Flags for domain enumeration and browse reply callbacks.
+ * "Default" applies only to enumeration and is only valid in
+ * conjuction with "Add"
+ */
+
+ kDNSServiceFlagsNoAutoRename = 8,
+ kDNSServiceFlagsAutoRename = 0, /* i.e. bit not set */
+ /* Flag for specifying renaming behavior on name conflict when registering
+ * non-shared records. NoAutorename is only valid if a name is explicitly
+ * specified when registering a service (ie the default name is not used.)
+ */
+
+
+ kDNSServiceFlagsShared = 16,
+ kDNSServiceFlagsUnique = 32,
+ /* Flag for registering individual records on a connected
+ * DNSServiceRef. Shared indicates that there may be multiple records
+ * with this name on the network (e.g. PTR records). Unique indicates that the
+ * record's name is to be unique on the network (e.g. SRV records).
+ */
+
+ kDNSServiceFlagsBrowseDomains = 64,
+ kDNSServiceFlagsRegistrationDomains = 128
+ /* Flags for specifying domain enumeration type in DNSServiceEnumerateDomains.
+ * BrowseDomains enumerates domains recommended for browsing, RegistrationDomains
+ * enumerates domains recommended for registration.
+ */
+ };
+
+/* possible error code values */
+enum
+ {
+ kDNSServiceErr_NoError = 0,
+ kDNSServiceErr_Unknown = -65537, /* 0xFFFE FFFF */
+ kDNSServiceErr_NoSuchName = -65538,
+ kDNSServiceErr_NoMemory = -65539,
+ kDNSServiceErr_BadParam = -65540,
+ kDNSServiceErr_BadReference = -65541,
+ kDNSServiceErr_BadState = -65542,
+ kDNSServiceErr_BadFlags = -65543,
+ kDNSServiceErr_Unsupported = -65544,
+ kDNSServiceErr_NotInitialized = -65545,
+ kDNSServiceErr_AlreadyRegistered = -65547,
+ kDNSServiceErr_NameConflict = -65548,
+ kDNSServiceErr_Invalid = -65549,
+ kDNSServiceErr_Incompatible = -65551, /* client library incompatible with daemon */
+ kDNSServiceErr_BadinterfaceIndex = -65552
+ /* mDNS Error codes are in the range
+ * FFFE FF00 (-65792) to FFFE FFFF (-65537) */
+ };
+
+
+/* Maximum length, in bytes, of a domain name represented as an escaped C-String */
+#define kDNSServiceMaxDomainName 1005
+
+
+typedef uint32_t DNSServiceFlags;
+typedef int32_t DNSServiceErrorType;
+
+
+/*********************************************************************************************
+ *
+ * Unix Domain Socket access, DNSServiceRef deallocation, and data processing functions
+ *
+ *********************************************************************************************/
+
+
+/* DNSServiceRefSockFD()
+ *
+ * Access underlying Unix domain socket for an initialized DNSServiceRef.
+ * The DNS Service Discovery implmementation uses this socket to communicate between
+ * the client and the mDNSResponder daemon. The application MUST NOT directly read from
+ * or write to this socket. Access to the socket is provided so that it can be used as a
+ * run loop source, or in a select() loop: when data is available for reading on the socket,
+ * DNSServiceProcessResult() should be called, which will extract the daemon's reply from
+ * the socket, and pass it to the appropriate application callback. By using a run loop or
+ * select(), results from the daemon can be processed asynchronously. Without using these
+ * constructs, DNSServiceProcessResult() will block until the response from the daemon arrives.
+ * The client is responsible for ensuring that the data on the socket is processed in a timely
+ * fashion - the daemon may terminate its connection with a client that does not clear its
+ * socket buffer.
+ *
+ * sdRef: A DNSServiceRef initialized by any of the DNSService calls.
+ *
+ * return value: The DNSServiceRef's underlying socket descriptor, or -1 on
+ * error.
+ */
+
+int DNSServiceRefSockFD(DNSServiceRef sdRef);
+
+/* DNSServiceProcessResult()
+ *
+ * Read a reply from the daemon, calling the appropriate application callback. This call will
+ * block until the daemon's response is received. Use DNSServiceRefSockFD() in
+ * conjunction with a run loop or select() to determine the presence of a response from the
+ * server before calling this function to process the reply without blocking. Call this function
+ * at any point if it is acceptable to block until the daemon's response arrives. Note that the
+ * client is responsible for ensuring that DNSServiceProcessResult() is called whenever there is
+ * a reply from the daemon - the daemon may terminate its connection with a client that does not
+ * process the daemon's responses.
+ *
+ * sdRef: A DNSServiceRef initialized by any of the DNSService calls
+ * that take a callback parameter.
+ *
+ * return value: Returns kDNSServiceErr_NoError on success, otherwise returns
+ * an error code indicating the specific failure that occurred.
+ */
+
+DNSServiceErrorType DNSServiceProcessResult(DNSServiceRef sdRef);
+
+/* DNSServiceRefDeallocate()
+ *
+ * Terminate a connection with the daemon and free memory associated with the DNSServiceRef.
+ * Any services or records registered with this DNSServiceRef will be deregistered. Any
+ * Browse, Resolve, or Query operations called with this reference will be terminated. If the
+ * reference's underlying socket is used in a run loop or select() call, it should be removed BEFORE
+ * DNSServiceRefDeallocate() is called, as this function closes the reference's socket.
+ *
+ * Note: This call is to be used only with the DNSServiceRef defined by this API. It is
+ * not compatible with dns_service_discovery_ref objects defined in the legacy Mach-based
+ * DNSServiceDiscovery.h API.
+ *
+ * sdRef: A DNSServiceRef initialized by any of the DNSService calls.
+ *
+ */
+
+void DNSServiceRefDeallocate(DNSServiceRef sdRef);
+
+
+/*********************************************************************************************
+ *
+ * Domain Enumeration
+ *
+ *********************************************************************************************/
+
+/* DNSServiceEnumerateDomains()
+ *
+ * Asynchronously enumerate domains available for browsing and registration.
+ * Currently, the only domain returned is "local.", but other domains will be returned in future.
+ *
+ * The enumeration MUST be cancelled via DNSServiceRefDeallocate() when no more domains
+ * are to be found.
+ *
+ *
+ * DNSServiceDomainEnumReply Callback Parameters:
+ *
+ * sdRef: The DNSServiceRef initialized by DNSServiceEnumerateDomains().
+ *
+ * flags: Possible values are:
+ * 1 (MoreComing)
+ * 2 (Add/Remove)
+ * 4 (Add Default)
+ *
+ * interfaceIndex: Specifies the interface on which the domain exists. (The index for a given
+ * interface is determined via the if_nametoindex() family of calls.)
+ *
+ * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise indicates
+ * the failure that occurred (other parameters are undefined if errorCode is nonzero).
+ *
+ * replyDomain: The name of the domain.
+ *
+ * context: The context pointer passed to DNSServiceEnumerateDomains.
+ *
+ */
+
+typedef void (*DNSServiceDomainEnumReply)
+ (
+ DNSServiceRef sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char *replyDomain,
+ void *context
+ );
+
+/* DNSServiceEnumerateDomains() Parameters:
+ *
+ *
+ * sdRef: A pointer to an uninitialized sdRef. May be passed to
+ * DNSServiceRefDeallocate() to cancel the enumeration.
+ *
+ * flags: Possible values are:
+ * 0 (BrowseDomains) to enumerate domains recommended for browsing.
+ * 32 (RegistrationDomains) to enumerate domains recommended for registration.
+ *
+ * interfaceIndex: If non-zero, specifies the interface on which to look for domains.
+ * (the index for a given interface is determined via the if_nametoindex()
+ * family of calls.) Most applications will pass 0 to enumerate domains on
+ * all interfaces.
+ *
+ * callBack: The function to be called when a domain is found or the call asynchronously
+ * fails.
+ *
+ * context: An application context pointer which is passed to the callback function
+ * (may be NULL).
+ *
+ * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous
+ * errors are delivered to the callback), otherwise returns an error code indicating
+ * the error that occurred (the callback is not invoked and the DNSServiceRef
+ * is not initialized.)
+ */
+
+DNSServiceErrorType DNSServiceEnumerateDomains
+ (
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceDomainEnumReply callBack,
+ void *context /* may be NULL */
+ );
+
+/*********************************************************************************************
+ *
+ * Service Registration
+ *
+ *********************************************************************************************/
+
+/* Register a service that is discovered via Browse() and Resolve() calls.
+ *
+ *
+ * DNSServiceRegisterReply() Callback Parameters:
+ *
+ * sdRef: The DNSServiceRef initialized by DNSServiceRegister().
+ *
+ * flags: Currently unused, reserved for future use.
+ *
+ * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will
+ * indicate the failure that occurred (including name conflicts, if the
+ * kDNSServiceFlagsNoAutoRenameOnConflict flag was passed to the
+ * callout.) Other parameters are undefined if errorCode is nonzero.
+ *
+ * name: The service name registered (if the application did not specify a name in
+ * DNSServiceRegister(), this indicates what name was automatically chosen).
+ *
+ * regtype: The type of service registered, as it was passed to the callout.
+ *
+ * domain: The domain on which the service was registered (if the application did not
+ * specify a domain in DNSServiceRegister(), this indicates the default domain
+ * on which the service was registered).
+ *
+ * context: The context pointer that was passed to the callout.
+ *
+ */
+
+typedef void (*DNSServiceRegisterReply)
+ (
+ DNSServiceRef sdRef,
+ DNSServiceFlags flags,
+ DNSServiceErrorType errorCode,
+ const char *name,
+ const char *regtype,
+ const char *domain,
+ void *context
+ );
+
+/* DNSServiceRegister() Parameters:
+ *
+ * sdRef: A pointer to an uninitialized sdRef. If this call succeeds, the reference
+ * may be passed to
+ * DNSServiceRefDeallocate() to deregister the service.
+ *
+ * interfaceIndex: If non-zero, specifies the interface on which to register the service
+ * (the index for a given interface is determined via the if_nametoindex()
+ * family of calls.) Most applications will pass 0 to register on all
+ * available interfaces. Pass -1 to register a service only on the local
+ * machine (service will not be visible to remote hosts.)
+ *
+ * flags: Indicates the renaming behavior on name conflict (most applications
+ * will pass 0). See flag definitions above for details.
+ *
+ * name: If non-NULL, specifies the service name to be registered.
+ * Most applications will not specify a name, in which case the
+ * computer name is used (this name is communicated to the client via
+ * the callback).
+ *
+ * regtype: The service type followed by the protocol, separated by a dot
+ * (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
+ *
+ * domain: If non-NULL, specifies the domain on which to advertise the service.
+ * Most applications will not specify a domain, instead automatically
+ * registering in the default domain(s).
+ *
+ * host: If non-NULL, specifies the SRV target host name. Most applications
+ * will not specify a host, instead automatically using the machine's
+ * default host name(s). Note that specifying a non-NULL host does NOT
+ * create an address record for that host - the application is responsible
+ * for ensuring that the appropriate address record exists, or creating it
+ * via DNSServiceRegisterRecord().
+ *
+ * port: The port on which the service accepts connections. Pass 0 for a
+ * "placeholder" service (i.e. a service that will not be discovered by
+ * browsing, but will cause a name conflict if another client tries to
+ * register that same name.) Most clients will not use placeholder services.
+ *
+ * txtLen: The length of the txtRecord, in bytes. Must be zero if the txtRecord is NULL.
+ *
+ * txtRecord: The txt record rdata. May be NULL. Note that a non-NULL txtRecord
+ * MUST be a properly formatted DNS TXT record, i.e. <length byte> <data>
+ * <length byte> <data> ...
+ *
+ * callBack: The function to be called when the registration completes or asynchronously
+ * fails. The client MAY pass NULL for the callback - The client will NOT be notified
+ * of the default values picked on its behalf, and the client will NOT be notified of any
+ * asynchronous errors (e.g. out of memory errors, etc.) that may prevent the registration
+ * of the service. The client may NOT pass the NoAutoRename flag if the callback is NULL.
+ * The client may still deregister the service at any time via DNSServiceRefDeallocate().
+ *
+ * context: An application context pointer which is passed to the callback function
+ * (may be NULL).
+ *
+ * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous
+ * errors are delivered to the callback), otherwise returns an error code indicating
+ * the error that occurred (the callback is never invoked and the DNSServiceRef
+ * is not initialized.)
+ *
+ */
+
+DNSServiceErrorType DNSServiceRegister
+ (
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ const char *name, /* may be NULL */
+ const char *regtype,
+ const char *domain, /* may be NULL */
+ const char *host, /* may be NULL */
+ uint16_t port,
+ uint16_t txtLen,
+ const void *txtRecord, /* may be NULL */
+ DNSServiceRegisterReply callBack, /* may be NULL */
+ void *context /* may be NULL */
+ );
+
+/* DNSServiceAddRecord()
+ *
+ * Add a record to a registered service. The name of the record will be the same as the
+ * registered service's name.
+ * The record can later be updated or deregistered by passing the RecordRef initialized
+ * by this function to DNSServiceUpdateRecord() or DNSServiceRemoveRecord().
+ *
+ *
+ * Parameters;
+ *
+ * sdRef: A DNSServiceRef initialized by DNSServiceRegister().
+ *
+ * RecordRef: A pointer to an uninitialized DNSRecordRef. Upon succesfull completion of this
+ * call, this ref may be passed to DNSServiceUpdateRecord() or DNSServiceRemoveRecord().
+ *
+ * flags: Currently ignored, reserved for future use.
+ *
+ * rrtype: The type of the record (e.g. TXT, SRV, etc), as defined in nameser.h.
+ *
+ * rdlen: The length, in bytes, of the rdata.
+ *
+ * rdata: The raw rdata to be contained in the added resource record.
+ *
+ * ttl: The time to live of the resource record, in seconds.
+ *
+ * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an
+ * error code indicating the error that occurred (the RecordRef is not initialized).
+ */
+
+DNSServiceErrorType DNSServiceAddRecord
+ (
+ DNSServiceRef sdRef,
+ DNSRecordRef *RecordRef,
+ DNSServiceFlags flags,
+ uint16_t rrtype,
+ uint16_t rdlen,
+ const void *rdata,
+ uint32_t ttl
+ );
+
+/* DNSServiceUpdateRecord
+ *
+ * Update a registered resource record. The record must either be:
+ * - The primary txt record of a service registered via DNSServiceRegister()
+ * - A record added to a registered service via DNSServiceAddRecord()
+ * - An individual record registered by DNSServiceRegisterRecord()
+ *
+ *
+ * Parameters:
+ *
+ * sdRef: A DNSServiceRef that was initialized by DNSServiceRegister()
+ * or DNSServiceCreateConnection().
+ *
+ * RecordRef: A DNSRecordRef initialized by DNSServiceAddRecord, or NULL to update the
+ * service's primary txt record.
+ *
+ * flags: Currently ignored, reserved for future use.
+ *
+ * rdlen: The length, in bytes, of the new rdata.
+ *
+ * rdata: The new rdata to be contained in the updated resource record.
+ *
+ * ttl: The time to live of the updated resource record, in seconds.
+ *
+ * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an
+ * error code indicating the error that occurred.
+ */
+
+DNSServiceErrorType DNSServiceUpdateRecord
+ (
+ DNSServiceRef sdRef,
+ DNSRecordRef RecordRef, /* may be NULL */
+ DNSServiceFlags flags,
+ uint16_t rdlen,
+ const void *rdata,
+ uint32_t ttl
+ );
+
+/* DNSServiceRemoveRecord
+ *
+ * Remove a record previously added to a service record set via DNSServiceAddRecord(), or deregister
+ * an record registered individually via DNSServiceRegisterRecord().
+ *
+ * Parameters:
+ *
+ * sdRef: A DNSServiceRef initialized by DNSServiceRegister() (if the
+ * record being removed was registered via DNSServiceAddRecord()) or by
+ * DNSServiceCreateConnection() (if the record being removed was registered via
+ * DNSServiceRegisterRecord()).
+ *
+ * recordRef: A DNSRecordRef initialized by a successful call to DNSServiceAddRecord()
+ * or DNSServiceRegisterRecord().
+ *
+ * flags: Currently ignored, reserved for future use.
+ *
+ * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an
+ * error code indicating the error that occurred.
+ */
+
+DNSServiceErrorType DNSServiceRemoveRecord
+ (
+ DNSServiceRef sdRef,
+ DNSRecordRef RecordRef,
+ DNSServiceFlags flags
+ );
+
+
+/*********************************************************************************************
+ *
+ * Service Discovery
+ *
+ *********************************************************************************************/
+
+
+/* Browse for instances of a service.
+ *
+ *
+ * DNSServiceBrowseReply() Parameters:
+ *
+ * sdRef: The DNSServiceRef initialized by DNSServiceBrowse().
+ *
+ * flags: Possible values are MoreComing and Add/Remove. See flag definitions
+ * for details.
+ *
+ * interfaceIndex: The interface on which the service is advertised. This index should
+ * be passed to DNSServiceResolve() when resolving the service.
+ *
+ * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise will
+ * indicate the failure that occurred. Other parameters are undefined if
+ * the errorCode is nonzero.
+ *
+ * serviceName: The service name discovered.
+ *
+ * regtype: The service type, as passed in to DNSServiceBrowse().
+ *
+ * domain: The domain on which the service was discovered (if the application did not
+ * specify a domain in DNSServicBrowse(), this indicates the domain on which the
+ * service was discovered.)
+ *
+ * context: The context pointer that was passed to the callout.
+ *
+ */
+
+typedef void (*DNSServiceBrowseReply)
+ (
+ DNSServiceRef sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char *serviceName,
+ const char *regtype,
+ const char *replyDomain,
+ void *context
+ );
+
+/* DNSServiceBrowse() Parameters:
+ *
+ * sdRef: A pointer to an uninitialized sdRef. May be passed to
+ * DNSServiceRefDeallocate() to terminate the browse.
+ *
+ * flags: Currently ignored, reserved for future use.
+ *
+ * interfaceIndex: If non-zero, specifies the interface on which to browse for services
+ * (the index for a given interface is determined via the if_nametoindex()
+ * family of calls.) Most applications will pass 0 to browse on all available
+ * interfaces. Pass -1 to only browse for services provided on the local host.
+ *
+ * regtype: The service type being browsed for followed by the protocol, separated by a
+ * dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
+ *
+ * domain: If non-NULL, specifies the domain on which to browse for services.
+ * Most applications will not specify a domain, instead browsing on the
+ * default domain(s).
+ *
+ * callBack: The function to be called when an instance of the service being browsed for
+ * is found, or if the call asynchronously fails.
+ *
+ * context: An application context pointer which is passed to the callback function
+ * (may be NULL).
+ *
+ * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous
+ * errors are delivered to the callback), otherwise returns an error code indicating
+ * the error that occurred (the callback is not invoked and the DNSServiceRef
+ * is not initialized.)
+ */
+
+DNSServiceErrorType DNSServiceBrowse
+ (
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ const char *regtype,
+ const char *domain, /* may be NULL */
+ DNSServiceBrowseReply callBack,
+ void *context /* may be NULL */
+ );
+
+/* DNSServiceResolve()
+ *
+ * Resolve a service name discovered via DNSServiceBrowse() to a target host name, port number, and
+ * txt record.
+ *
+ * Note: Applications should NOT use DNSServiceResolve() solely for txt record monitoring - use
+ * DNSServiceQueryRecord() instead, as it is more efficient for this task.
+ *
+ * Note: When the desired results have been returned, the client MUST terminate the resolve by calling
+ * DNSServiceRefDeallocate().
+ *
+ * Note: DNSServiceResolve() behaves correctly for typical services that have a single SRV record and
+ * a single TXT record (the TXT record may be empty.) To resolve non-standard services with multiple
+ * SRV or TXT records, DNSServiceQueryRecord() should be used.
+ *
+ * DNSServiceResolveReply Callback Parameters:
+ *
+ * sdRef: The DNSServiceRef initialized by DNSServiceResolve().
+ *
+ * flags: Possible values are MoreComing and Add/Remove. See flag definitions
+ * for details.
+ *
+ * interfaceIndex: The interface on which the service was resolved.
+ *
+ * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise will
+ * indicate the failure that occurred. Other parameters are undefined if
+ * the errorCode is nonzero.
+ *
+ * fullname: The full service domain name, in the form <servicename>.<protocol>.<domain>.
+ * (Any literal dots (".") are escaped with a backslash ("\."), and literal
+ * backslashes are escaped with a second backslash ("\\"), e.g. a web server
+ * named "Dr. Pepper" would have the fullname "Dr\.\032Pepper._http._tcp.local.").
+ * This is the appropriate format to pass to standard system DNS APIs such as
+ * res_query(), or to the special-purpose functions included in this API that
+ * take fullname parameters.
+ *
+ * hosttarget: The target hostname of the machine providing the service. This name can
+ * be passed to functions like gethostbyname() to identify the host's IP address.
+ *
+ * port: The port number on which connections are accepted for this service.
+ *
+ * txtLen: The length of the txt record, in bytes.
+ *
+ * txtRecord: The service's primary txt record, in standard txt record format.
+ *
+
+ * context: The context pointer that was passed to the callout.
+ *
+ */
+
+typedef void (*DNSServiceResolveReply)
+ (
+ DNSServiceRef sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char *fullname,
+ const char *hosttarget,
+ uint16_t port,
+ uint16_t txtLen,
+ const char *txtRecord,
+ void *context
+ );
+
+/* DNSServiceResolve() Parameters
+ *
+ * sdRef: A pointer to an uninitialized sdRef. May be passed to
+ * DNSServiceRefDeallocate() to terminate the resolve.
+ *
+ * flags: Currently ignored, reserved for future use.
+ *
+ * interfaceIndex: The interface on which to resolve the service. The client should
+ * pass the interface on which the servicename was discovered, i.e.
+ * the interfaceIndex passed to the DNSServiceBrowseReply callback,
+ * or 0 to resolve the named service on all available interfaces.
+ *
+ * name: The servicename to be resolved.
+ *
+ * regtype: The service type being resolved followed by the protocol, separated by a
+ * dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
+ *
+ * domain: The domain on which the service is registered, i.e. the domain passed
+ * to the DNSServiceBrowseReply callback.
+ *
+ * callBack: The function to be called when a result is found, or if the call
+ * asynchronously fails.
+ *
+ * context: An application context pointer which is passed to the callback function
+ * (may be NULL).
+ *
+ * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous
+ * errors are delivered to the callback), otherwise returns an error code indicating
+ * the error that occurred (the callback is never invoked and the DNSServiceRef
+ * is not initialized.)
+ */
+
+
+DNSServiceErrorType DNSServiceResolve
+ (
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ const char *name,
+ const char *regtype,
+ const char *domain,
+ DNSServiceResolveReply callBack,
+ void *context /* may be NULL */
+ );
+
+
+/*********************************************************************************************
+ *
+ * Special Purpose Calls (most applications will not use these)
+ *
+ *********************************************************************************************/
+
+/* DNS Naming Conventions:
+ *
+ * The following functions refer to resource records by their full domain name, unlike the above
+ * functions which divide the name into servicename/regtype/domain fields. In the above functions,
+ * a dot (".") is considered to be a literal dot in the servicename field (e.g. "Dr. Pepper") and
+ * a label separator in the regtype ("_ftp._tcp") or domain ("apple.com") fields. Literal dots in
+ * the domain field would be escaped with a backslash, and literal backslashes would be escaped with
+ * a second backslash (this is generally not an issue, as domain names on the Internet today almost
+ * never use characters other than letters, digits, or hyphens, and the dots are label separators.)
+ * Furthermore, this is transparent to the caller, so long as the fields are passed between functions
+ * without manipulation. However, the following, special-purpose calls use a single, full domain name.
+ * As such, all dots are considered to be label separators, unless escaped, and all backslashes are
+ * considered to be escape characters, unless preceded by a second backslash. For example, the name
+ * "Dr. Smith \ Dr. Johnson" could be passed literally as a service name parameter in the above calls,
+ * but in the special purpose call, the dots and backslash would have to be escaped
+ * (e.g. "Dr\. Smith \\ Dr\. Johnson._ftp._tcp.apple.com" for an ftp service on the apple.com domain.)
+ */
+
+/* DNSServiceConstructFullName()
+ *
+ * Concatenate a three-part domain name (as returned by the above callbacks) into a properly-escaped
+ * full domain name. Note that callbacks in the above functions ALREADY ESCAPE strings where necessary.
+ *
+ * Parameters:
+ *
+ * fullName: A pointer to a buffer that where the resulting full domain name is to be written.
+ * The buffer must be kDNSServiceDiscoveryMaxDomainName (1005) bytes in length to
+ * accommodate the longest legal domain name without buffer overrun.
+ *
+ * service: The service name - any dots or slashes must NOT be escaped.
+ * May be NULL (to construct a PTR record name, e.g.
+ * "_ftp._tcp.apple.com").
+ *
+ * regtype: The service type followed by the protocol, separated by a dot
+ * (e.g. "_ftp._tcp").
+ *
+ * domain: The domain name, e.g. "apple.com". Any literal dots or backslashes
+ * must be escaped.
+ *
+ * return value: Returns 0 on success, -1 on error.
+ *
+ */
+
+int DNSServiceConstructFullName
+ (
+ char *fullName,
+ const char *service, /* may be NULL */
+ const char *regtype,
+ const char *domain
+ );
+
+/* DNSServiceCreateConnection()
+ *
+ * Create a connection to the daemon allowing efficient registration of
+ * multiple individual records.
+ *
+ *
+ * Parameters:
+ *
+ * sdRef: A pointer to an uninitialized DNSServiceRef. Deallocating
+ * the reference (via DNSServiceRefDeallocate()) severs the
+ * connection and deregisters all records registered on this connection.
+ *
+ * return value: Returns kDNSServiceErr_NoError on success, otherwise returns
+ * an error code indicating the specific failure that occurred (in which
+ * case the DNSServiceRef is not initialized).
+ */
+
+DNSServiceErrorType DNSServiceCreateConnection(DNSServiceRef *sdRef);
+
+
+/* DNSServiceRegisterRecord
+ *
+ * Register an individual resource record on a connected DNSServiceRef.
+ *
+ * Note that name conflicts occurring for records registered via this call must be handled
+ * by the client in the callback.
+ *
+ *
+ * DNSServiceRegisterRecordReply() parameters:
+ *
+ * sdRef: The connected DNSServiceRef initialized by
+ * DNSServiceDiscoveryConnect().
+ *
+ * RecordRef: The DNSRecordRef initialized by DNSServiceRegisterRecord().
+ *
+ * flags: Currently unused, reserved for future use.
+ *
+ * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will
+ * indicate the failure that occurred (including name conflicts.)
+ * Other parameters are undefined if errorCode is nonzero.
+ *
+ * context: The context pointer that was passed to the callout.
+ *
+ */
+
+ typedef void (*DNSServiceRegisterRecordReply)
+ (
+ DNSServiceRef sdRef,
+ DNSRecordRef RecordRef,
+ DNSServiceFlags flags,
+ DNSServiceErrorType errorCode,
+ void *context
+ );
+
+
+/* DNSServiceRegisterRecord() Parameters:
+ *
+ * sdRef: A DNSServiceRef initialized by DNSServiceCreateConnection().
+ *
+ * RecordRef: A pointer to an uninitialized DNSRecordRef. Upon succesfull completion of this
+ * call, this ref may be passed to DNSServiceUpdateRecord() or DNSServiceRemoveRecord().
+ * (To deregister ALL records registered on a single connected DNSServiceRef
+ * and deallocate each of their corresponding DNSServiceRecordRefs, call
+ * DNSServiceRefDealloocate()).
+ *
+ * flags: Possible values are Shared/Unique (see flag type definitions for details).
+ *
+ * interfaceIndex: If non-zero, specifies the interface on which to register the record
+ * (the index for a given interface is determined via the if_nametoindex()
+ * family of calls.) Passing 0 causes the record to be registered on all interfaces.
+ * Passing -1 causes the record to only be visible on the local host.
+ *
+ * fullname: The full domain name of the resource record.
+ *
+ * rrtype: The numerical type of the resource record (e.g. PTR, SRV, etc), as defined
+ * in nameser.h.
+ *
+ * rrclass: The class of the resource record, as defined in nameser.h (usually 1 for the
+ * Internet class).
+ *
+ * rdlen: Length, in bytes, of the rdata.
+ *
+ * rdata: A pointer to the raw rdata, as it is to appear in the DNS record.
+ *
+ * ttl: The time to live of the resource record, in seconds.
+ *
+ * callBack: The function to be called when a result is found, or if the call
+ * asynchronously fails (e.g. because of a name conflict.)
+ *
+ * context: An application context pointer which is passed to the callback function
+ * (may be NULL).
+ *
+ * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous
+ * errors are delivered to the callback), otherwise returns an error code indicating
+ * the error that occurred (the callback is never invoked and the DNSRecordRef is
+ * not initialized.)
+ */
+
+
+DNSServiceErrorType DNSServiceRegisterRecord
+ (
+ DNSServiceRef sdRef,
+ DNSRecordRef *RecordRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ const char *fullname,
+ uint16_t rrtype,
+ uint16_t rrclass,
+ uint16_t rdlen,
+ const void *rdata,
+ uint32_t ttl,
+ DNSServiceRegisterRecordReply callBack,
+ void *context /* may be NULL */
+ );
+
+
+/* DNSServiceQueryRecord
+ *
+ * Query for an arbitrary DNS record.
+ *
+ *
+ * DNSServiceQueryRecordReply() Callback Parameters:
+ *
+ * sdRef: The DNSServiceRef initialized by DNSServiceQueryRecord().
+ *
+ * flags: Possible values are Finished/MoreComing.
+ *
+ * interfaceIndex: The interface on which the query was resolved (the index for a given
+ * interface is determined via the if_nametoindex() family of calls).
+ *
+ * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will
+ * indicate the failure that occurred. Other parameters are undefined if
+ * errorCode is nonzero.
+ *
+ * fullname: The resource record's full domain name.
+ *
+ * rrtype: The resource record's type (e.g. PTR, SRV, etc) as defined in nameser.h.
+ *
+ * rrclass: The class of the resource record, as defined in nameser.h (usually 1).
+ *
+ * rdlen: The length, in bytes, of the resource record rdata.
+ *
+ * rdata: The raw rdata of the resource record.
+ *
+ * ttl: The resource record's time to live, in seconds.
+ *
+ * context: The context pointer that was passed to the callout.
+ *
+ */
+
+typedef void (*DNSServiceQueryRecordReply)
+ (
+ DNSServiceRef DNSServiceRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char *fullname,
+ uint16_t rrtype,
+ uint16_t rrclass,
+ uint16_t rdlen,
+ const void *rdata,
+ uint32_t ttl,
+ void *context
+ );
+
+/* DNSServiceQueryRecord() Parameters:
+ *
+ * sdRef: A pointer to an uninitialized DNSServiceRef.
+ *
+ * flags: Currently unused, reserved for future use.
+ *
+ * interfaceIndex: If non-zero, specifies the interface on which to issue the query
+ * (the index for a given interface is determined via the if_nametoindex()
+ * family of calls.) Passing 0 causes the name to be queried for on all
+ * interfaces. Passing -1 causes the name to be queried for only on the
+ * local host.
+ *
+ * fullname: The full domain name of the resource record to be queried for.
+ *
+ * rrtype: The numerical type of the resource record to be queried for (e.g. PTR, SRV, etc)
+ * as defined in nameser.h.
+ *
+ * rrclass: The class of the resource record, as defined in nameser.h
+ * (usually 1 for the Internet class).
+ *
+ * callBack: The function to be called when a result is found, or if the call
+ * asynchronously fails.
+ *
+ * context: An application context pointer which is passed to the callback function
+ * (may be NULL).
+ *
+ * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous
+ * errors are delivered to the callback), otherwise returns an error code indicating
+ * the error that occurred (the callback is never invoked and the DNSServiceRef
+ * is not initialized.)
+ */
+
+DNSServiceErrorType DNSServiceQueryRecord
+ (
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ const char *fullname,
+ uint16_t rrtype,
+ uint16_t rrclass,
+ DNSServiceQueryRecordReply callBack,
+ void *context /* may be NULL */
+ );
+
+/* DNSServiceReconfirmRecord
+ *
+ * Instruct the daemon to verify the validity of a resource record that appears to
+ * be out of date (e.g. because tcp connection to a service's target failed.)
+ * Causes the record to be flushed from the daemon's cache (as well as all other
+ * daemons' caches on the network) if the record is determined to be invalid.
+ *
+ * Parameters:
+ *
+ * flags: Currently unused, reserved for future use.
+ *
+ * fullname: The resource record's full domain name.
+ *
+ * rrtype: The resource record's type (e.g. PTR, SRV, etc) as defined in nameser.h.
+ *
+ * rrclass: The class of the resource record, as defined in nameser.h (usually 1).
+ *
+ * rdlen: The length, in bytes, of the resource record rdata.
+ *
+ * rdata: The raw rdata of the resource record.
+ *
+ */
+
+void DNSServiceReconfirmRecord
+ (
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ const char *fullname,
+ uint16_t rrtype,
+ uint16_t rrclass,
+ uint16_t rdlen,
+ const void *rdata
+ );
+
+
+#endif // _DNS_SD_H
+
--- /dev/null
+/*
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * 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
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+
+ Change History (most recent first):
+
+$Log: dnssd_clientstub.c,v $
+Revision 1.9 2003/08/15 21:30:39 cheshire
+Bring up to date with LibInfo version
+
+Revision 1.8 2003/08/13 23:54:52 ksekar
+Bringing dnssd_clientstub.c up to date with Libinfo, per radar 3376640
+
+Revision 1.7 2003/08/12 19:56:25 cheshire
+Update to APSL 2.0
+
+ */
+
+#include "dnssd_ipc.h"
+
+#define CTL_PATH_PREFIX "/tmp/dnssd_clippath."
+// error socket (if needed) is named "dnssd_clipath.[pid].xxx:n" where xxx are the
+// last 3 digits of the time (in seconds) and n is the 6-digit microsecond time
+
+// general utility functions
+static DNSServiceRef connect_to_server(void);
+DNSServiceErrorType deliver_request(void *msg, DNSServiceRef sdr, int reuse_sd);
+static ipc_msg_hdr *create_hdr(int op, int *len, char **data_start, int reuse_socket);
+static int my_read(int sd, char *buf, int len);
+static int my_write(int sd, char *buf, int len);
+static int domain_ends_in_dot(const char *dom);
+// server response handlers
+static void handle_query_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *msg);
+static void handle_browse_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data);
+static void handle_regservice_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data);
+static void handle_regrecord_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data);
+static void handle_enumeration_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data);
+static void handle_resolve_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data);
+
+typedef struct _DNSServiceRef_t
+ {
+ int sockfd; // connected socket between client and daemon
+ int op; // request/reply_op_t
+ process_reply_callback process_reply;
+ void *app_callback;
+ void *app_context;
+ uint32_t max_index; //largest assigned record index - 0 if no additl. recs registered
+ } _DNSServiceRef_t;
+
+typedef struct _DNSRecordRef_t
+ {
+ void *app_context;
+ DNSServiceRegisterRecordReply app_callback;
+ DNSRecordRef recref;
+ int record_index; // index is unique to the ServiceDiscoveryRef
+ DNSServiceRef sdr;
+ } _DNSRecordRef_t;
+
+
+// exported functions
+
+int DNSServiceRefSockFD(DNSServiceRef sdRef)
+ {
+ if (!sdRef) return -1;
+ return sdRef->sockfd;
+ }
+
+// handle reply from server, calling application client callback. If there is no reply
+// from the daemon on the socket contained in sdRef, the call will block.
+DNSServiceErrorType DNSServiceProcessResult(DNSServiceRef sdRef)
+ {
+ ipc_msg_hdr hdr;
+ char *data;
+
+ if (!sdRef || sdRef->sockfd < 0 || !sdRef->process_reply)
+ return kDNSServiceErr_BadReference;
+
+ if (my_read(sdRef->sockfd, (void *)&hdr, sizeof(hdr)) < 0)
+ return kDNSServiceErr_Unknown;
+ if (hdr.version != VERSION)
+ return kDNSServiceErr_Incompatible;
+ data = malloc(hdr.datalen);
+ if (!data) return kDNSServiceErr_NoMemory;
+ if (my_read(sdRef->sockfd, data, hdr.datalen) < 0)
+ return kDNSServiceErr_Unknown;
+ sdRef->process_reply(sdRef, &hdr, data);
+ return kDNSServiceErr_Unknown;
+ }
+
+
+void DNSServiceRefDeallocate(DNSServiceRef sdRef)
+ {
+ if (!sdRef) return;
+ if (sdRef->sockfd > 0) close(sdRef->sockfd);
+ free(sdRef);
+ }
+
+
+DNSServiceErrorType DNSServiceResolve
+ (
+ DNSServiceRef *sdRef,
+ const DNSServiceFlags flags,
+ const uint32_t interfaceIndex,
+ const char *name,
+ const char *regtype,
+ const char *domain,
+ const DNSServiceResolveReply callBack,
+ void *context
+ )
+ {
+ char *msg = NULL, *ptr;
+ int len;
+ ipc_msg_hdr *hdr;
+ DNSServiceRef sdr;
+ DNSServiceErrorType err;
+
+ if (!sdRef) return kDNSServiceErr_BadParam;
+ *sdRef = NULL;
+
+ // calculate total message length
+ len = sizeof(flags);
+ len += sizeof(interfaceIndex);
+ len += strlen(name) + 1;
+ len += strlen(regtype) + 1;
+ len += strlen(domain) + 1;
+
+ hdr = create_hdr(resolve_request, &len, &ptr, 1);
+ if (!hdr) goto error;
+ msg = (void *)hdr;
+
+ put_flags(flags, &ptr);
+ put_long(interfaceIndex, &ptr);
+ put_string(name, &ptr);
+ put_string(regtype, &ptr);
+ put_string(domain, &ptr);
+
+ sdr = connect_to_server();
+ if (!sdr) goto error;
+ err = deliver_request(msg, sdr, 1);
+ if (err)
+ {
+ DNSServiceRefDeallocate(sdr);
+ return err;
+ }
+ sdr->op = resolve_request;
+ sdr->process_reply = handle_resolve_response;
+ sdr->app_callback = callBack;
+ sdr->app_context = context;
+ *sdRef = sdr;
+
+ return err;
+
+error:
+ if (msg) free(msg);
+ if (*sdRef) { free(*sdRef); *sdRef = NULL; }
+ return kDNSServiceErr_Unknown;
+ }
+
+
+static void handle_resolve_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data)
+ {
+ DNSServiceFlags flags;
+ char fullname[kDNSServiceMaxDomainName];
+ char target[kDNSServiceMaxDomainName];
+ uint16_t port, txtlen;
+ uint32_t ifi;
+ DNSServiceErrorType err;
+ char *txtrecord;
+
+ (void)hdr; //unused
+
+ flags = get_flags(&data);
+ ifi = get_long(&data);
+ err = get_error_code(&data);
+ get_string(&data, fullname, kDNSServiceMaxDomainName);
+ get_string(&data, target, kDNSServiceMaxDomainName);
+ port = get_short(&data);
+ txtlen = get_short(&data);
+ txtrecord = get_rdata(&data, txtlen);
+
+ ((DNSServiceResolveReply)sdr->app_callback)(sdr, flags, ifi, err, fullname, target, port, txtlen, txtrecord, sdr->app_context);
+ }
+
+
+
+
+DNSServiceErrorType DNSServiceQueryRecord
+(
+ DNSServiceRef *sdRef,
+ const DNSServiceFlags flags,
+ const uint32_t interfaceIndex,
+ const char *name,
+ const uint16_t rrtype,
+ const uint16_t rrclass,
+ const DNSServiceQueryRecordReply callBack,
+ void *context
+ )
+ {
+ char *msg = NULL, *ptr;
+ int len;
+ ipc_msg_hdr *hdr;
+ DNSServiceRef sdr;
+ DNSServiceErrorType err;
+
+ if (!sdRef) return kDNSServiceErr_BadParam;
+ *sdRef = NULL;
+
+ if (!name) name = "\0";
+
+ // calculate total message length
+ len = sizeof(flags);
+ len += sizeof(uint32_t); //interfaceIndex
+ len += strlen(name) + 1;
+ len += 2 * sizeof(uint16_t); // rrtype, rrclass
+
+ hdr = create_hdr(query_request, &len, &ptr, 1);
+ if (!hdr) goto error;
+ msg = (void *)hdr;
+
+ put_flags(flags, &ptr);
+ put_long(interfaceIndex, &ptr);
+ put_string(name, &ptr);
+ put_short(rrtype, &ptr);
+ put_short(rrclass, &ptr);
+
+ sdr = connect_to_server();
+ if (!sdr) goto error;
+ err = deliver_request(msg, sdr, 1);
+ if (err)
+ {
+ DNSServiceRefDeallocate(sdr);
+ return err;
+ }
+
+ sdr->op = query_request;
+ sdr->process_reply = handle_query_response;
+ sdr->app_callback = callBack;
+ sdr->app_context = context;
+ *sdRef = sdr;
+ return err;
+
+error:
+ if (msg) free(msg);
+ if (*sdRef) { free(*sdRef); *sdRef = NULL; }
+ return kDNSServiceErr_Unknown;
+ }
+
+
+static void handle_query_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data)
+ {
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex, ttl;
+ DNSServiceErrorType errorCode;
+ char name[256];
+ uint16_t rrtype, rrclass, rdlen;
+ char *rdata;
+ (void)hdr;//Unused
+
+ flags = get_flags(&data);
+ interfaceIndex = get_long(&data);
+ errorCode = get_error_code(&data);
+ (get_string(&data, name, 256) < 0);
+ rrtype = get_short(&data);
+ rrclass = get_short(&data);
+ rdlen = get_short(&data);
+ rdata = get_rdata(&data, rdlen);
+ ttl = get_long(&data);
+ if (!rdata) return;
+ ((DNSServiceQueryRecordReply)sdr->app_callback)(sdr, flags, interfaceIndex, errorCode, name, rrtype, rrclass,
+ rdlen, rdata, ttl, sdr->app_context);
+ return;
+ }
+
+DNSServiceErrorType DNSServiceBrowse
+(
+ DNSServiceRef *sdRef,
+ const DNSServiceFlags flags,
+ const uint32_t interfaceIndex,
+ const char *regtype,
+ const char *domain,
+ const DNSServiceBrowseReply callBack,
+ void *context
+ )
+ {
+ char *msg = NULL, *ptr;
+ int len;
+ ipc_msg_hdr *hdr;
+ DNSServiceRef sdr;
+ DNSServiceErrorType err;
+
+ if (!sdRef) return kDNSServiceErr_BadParam;
+ *sdRef = NULL;
+
+ if (!domain) domain = "";
+
+ len = sizeof(flags);
+ len += sizeof(interfaceIndex);
+ len += strlen(regtype) + 1;
+ len += strlen(domain) + 1;
+
+ hdr = create_hdr(browse_request, &len, &ptr, 1);
+ if (!hdr) goto error;
+ msg = (char *)hdr;
+ put_flags(flags, &ptr);
+ put_long(interfaceIndex, &ptr);
+ put_string(regtype, &ptr);
+ put_string(domain, &ptr);
+
+ sdr = connect_to_server();
+ if (!sdr) goto error;
+ err = deliver_request(msg, sdr, 1);
+ if (err)
+ {
+ DNSServiceRefDeallocate(sdr);
+ return err;
+ }
+ sdr->op = browse_request;
+ sdr->process_reply = handle_browse_response;
+ sdr->app_callback = callBack;
+ sdr->app_context = context;
+ *sdRef = sdr;
+ return err;
+
+error:
+ if (msg) free(msg);
+ if (*sdRef) { free(*sdRef); *sdRef = NULL; }
+ return kDNSServiceErr_Unknown;
+ }
+
+
+
+
+static void handle_browse_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data)
+ {
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex;
+ DNSServiceErrorType errorCode;
+ char replyName[256], replyType[256], replyDomain[256];
+ (void)hdr;//Unused
+
+ flags = get_flags(&data);
+ interfaceIndex = get_long(&data);
+ errorCode = get_error_code(&data);
+ get_string(&data, replyName, 256);
+ get_string(&data, replyType, 256);
+ get_string(&data, replyDomain, 256);
+ ((DNSServiceBrowseReply)sdr->app_callback)(sdr, flags, interfaceIndex, errorCode, replyName, replyType, replyDomain, sdr->app_context);
+ }
+
+
+DNSServiceErrorType DNSServiceRegister
+ (
+ DNSServiceRef *sdRef,
+ const DNSServiceFlags flags,
+ const uint32_t interfaceIndex,
+ const char *name,
+ const char *regtype,
+ const char *domain,
+ const char *host,
+ const uint16_t port,
+ const uint16_t txtLen,
+ const void *txtRecord,
+ const DNSServiceRegisterReply callBack,
+ void *context
+ )
+ {
+ char *msg = NULL, *ptr;
+ int len;
+ ipc_msg_hdr *hdr;
+ DNSServiceRef sdr;
+ DNSServiceErrorType err;
+
+ if (!sdRef) return kDNSServiceErr_BadParam;
+ *sdRef = NULL;
+
+ if (!name) name = "";
+ if (!regtype) return kDNSServiceErr_BadParam;
+ if (!domain) domain = "";
+ if (!host) host = "";
+ if (!txtRecord) (char *)txtRecord = "";
+
+ // auto-name must also have auto-rename
+ if (!name[0] && (flags & kDNSServiceFlagsNoAutoRename))
+ return kDNSServiceErr_BadParam;
+
+ // no callback must have auto-name
+ if (!callBack && name[0]) return kDNSServiceErr_BadParam;
+
+ len = sizeof(DNSServiceFlags);
+ len += sizeof(uint32_t); // interfaceIndex
+ len += strlen(name) + strlen(regtype) + strlen(domain) + strlen(host) + 4;
+ len += 2 * sizeof(uint16_t); // port, txtLen
+ len += txtLen;
+
+ hdr = create_hdr(reg_service_request, &len, &ptr, 1);
+ if (!hdr) goto error;
+ if (!callBack) hdr->flags |= IPC_FLAGS_NOREPLY;
+ msg = (char *)hdr;
+ put_flags(flags, &ptr);
+ put_long(interfaceIndex, &ptr);
+ put_string(name, &ptr);
+ put_string(regtype, &ptr);
+ put_string(domain, &ptr);
+ put_string(host, &ptr);
+ put_short(port, &ptr);
+ put_short(txtLen, &ptr);
+ put_rdata(txtLen, txtRecord, &ptr);
+
+ sdr = connect_to_server();
+ if (!sdr) goto error;
+ err = deliver_request(msg, sdr, 1);
+ if (err)
+ {
+ DNSServiceRefDeallocate(sdr);
+ return err;
+ }
+
+ sdr->op = reg_service_request;
+ sdr->process_reply = callBack ? handle_regservice_response : NULL;
+ sdr->app_callback = callBack;
+ sdr->app_context = context;
+ *sdRef = sdr;
+
+ return err;
+
+error:
+ if (msg) free(msg);
+ if (*sdRef) { free(*sdRef); *sdRef = NULL; }
+ return kDNSServiceErr_Unknown;
+ }
+
+
+static void handle_regservice_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data)
+ {
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex;
+ DNSServiceErrorType errorCode;
+ char name[256], regtype[256], domain[256];
+ (void)hdr;//Unused
+
+ flags = get_flags(&data);
+ interfaceIndex = get_long(&data);
+ errorCode = get_error_code(&data);
+ get_string(&data, name, 256);
+ get_string(&data, regtype, 256);
+ get_string(&data, domain, 256);
+ ((DNSServiceRegisterReply)sdr->app_callback)(sdr, flags, errorCode, name, regtype, domain, sdr->app_context);
+ }
+
+DNSServiceErrorType DNSServiceEnumerateDomains
+(
+ DNSServiceRef *sdRef,
+ const DNSServiceFlags flags,
+ const uint32_t interfaceIndex,
+ const DNSServiceDomainEnumReply callBack,
+ void *context
+ )
+ {
+ char *msg = NULL, *ptr;
+ int len;
+ ipc_msg_hdr *hdr;
+ DNSServiceRef sdr;
+ DNSServiceErrorType err;
+
+
+ if (!sdRef) return kDNSServiceErr_BadParam;
+ *sdRef = NULL;
+
+ len = sizeof(DNSServiceFlags);
+ len += sizeof(uint32_t);
+
+ hdr = create_hdr(enumeration_request, &len, &ptr, 1);
+ if (!hdr) goto error;
+ msg = (void *)hdr;
+
+ put_flags(flags, &ptr);
+ put_long(interfaceIndex, &ptr);
+
+ sdr = connect_to_server();
+ if (!sdr) goto error;
+ err = deliver_request(msg, sdr, 1);
+ if (err)
+ {
+ DNSServiceRefDeallocate(sdr);
+ return err;
+ }
+
+ sdr->op = enumeration_request;
+ sdr->process_reply = handle_enumeration_response;
+ sdr->app_callback = callBack;
+ sdr->app_context = context;
+ *sdRef = sdr;
+ return err;
+
+error:
+ if (msg) free(msg);
+ if (*sdRef) { free(*sdRef); *sdRef = NULL; }
+ return kDNSServiceErr_Unknown;
+ }
+
+
+static void handle_enumeration_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data)
+ {
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex;
+ DNSServiceErrorType err;
+ char domain[256];
+ (void)hdr;//Unused
+
+ flags = get_flags(&data);
+ interfaceIndex = get_long(&data);
+ err = get_error_code(&data);
+ get_string(&data, domain, 256);
+ ((DNSServiceDomainEnumReply)sdr->app_callback)(sdr, flags, interfaceIndex, err, domain, sdr->app_context);
+ }
+
+
+DNSServiceErrorType DNSServiceCreateConnection(DNSServiceRef *sdRef)
+ {
+ if (!sdRef) return kDNSServiceErr_BadParam;
+ *sdRef = connect_to_server();
+ if (!*sdRef)
+ return kDNSServiceErr_Unknown;
+ (*sdRef)->op = connection;
+ (*sdRef)->process_reply = handle_regrecord_response;
+ return 0;
+ }
+
+
+
+static void handle_regrecord_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data)
+ {
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex;
+ DNSServiceErrorType errorCode;
+ DNSRecordRef rref = hdr->client_context.context;
+
+ if (sdr->op != connection)
+ {
+ rref->app_callback(rref->sdr, rref, 0, kDNSServiceErr_Unknown, rref->app_context);
+ return;
+ }
+ flags = get_flags(&data);
+ interfaceIndex = get_long(&data);
+ errorCode = get_error_code(&data);
+
+ rref->app_callback(rref->sdr, rref, flags, errorCode, rref->app_context);
+ }
+
+DNSServiceErrorType DNSServiceRegisterRecord
+(
+ const DNSServiceRef sdRef,
+ DNSRecordRef *RecordRef,
+ const DNSServiceFlags flags,
+ const uint32_t interfaceIndex,
+ const char *fullname,
+ const uint16_t rrtype,
+ const uint16_t rrclass,
+ const uint16_t rdlen,
+ const void *rdata,
+ const uint32_t ttl,
+ const DNSServiceRegisterRecordReply callBack,
+ void *context
+ )
+ {
+ char *msg = NULL, *ptr;
+ int len;
+ ipc_msg_hdr *hdr = NULL;
+ DNSServiceRef tmp = NULL;
+ DNSRecordRef rref = NULL;
+
+ if (!sdRef || sdRef->op != connection || sdRef->sockfd < 0)
+ return kDNSServiceErr_BadReference;
+ *RecordRef = NULL;
+
+ len = sizeof(DNSServiceFlags);
+ len += 2 * sizeof(uint32_t); // interfaceIndex, ttl
+ len += 3 * sizeof(uint16_t); // rrtype, rrclass, rdlen
+ len += strlen(fullname) + 1;
+ len += rdlen;
+
+ hdr = create_hdr(reg_record_request, &len, &ptr, 0);
+ if (!hdr) goto error;
+ msg = (char *)hdr;
+ put_flags(flags, &ptr);
+ put_long(interfaceIndex, &ptr);
+ put_string(fullname, &ptr);
+ put_short(rrtype, &ptr);
+ put_short(rrclass, &ptr);
+ put_short(rdlen, &ptr);
+ put_rdata(rdlen, rdata, &ptr);
+ put_long(ttl, &ptr);
+
+ rref = malloc(sizeof(_DNSRecordRef_t));
+ if (!rref) goto error;
+ rref->app_context = context;
+ rref->app_callback = callBack;
+ rref->record_index = sdRef->max_index++;
+ rref->sdr = sdRef;
+ *RecordRef = rref;
+ hdr->client_context.context = rref;
+ hdr->reg_index = rref->record_index;
+
+ return deliver_request(msg, sdRef, 0);
+
+error:
+ if (rref) free(rref);
+ if (tmp) free(tmp);
+ if (hdr) free(hdr);
+ return kDNSServiceErr_Unknown;
+ }
+
+//sdRef returned by DNSServiceRegister()
+DNSServiceErrorType DNSServiceAddRecord
+ (
+ const DNSServiceRef sdRef,
+ DNSRecordRef *RecordRef,
+ const DNSServiceFlags flags,
+ const uint16_t rrtype,
+ const uint16_t rdlen,
+ const void *rdata,
+ const uint32_t ttl
+ )
+ {
+ ipc_msg_hdr *hdr;
+ int len = 0;
+ char *ptr;
+ DNSRecordRef rref;
+
+ if (!sdRef || (sdRef->op != reg_service_request) || !RecordRef)
+ return kDNSServiceErr_BadReference;
+ *RecordRef = NULL;
+
+ len += 2 * sizeof(uint16_t); //rrtype, rdlen
+ len += rdlen;
+ len += sizeof(uint32_t);
+ len += sizeof(DNSServiceFlags);
+
+ hdr = create_hdr(add_record_request, &len, &ptr, 0);
+ if (!hdr) return kDNSServiceErr_Unknown;
+ put_flags(flags, &ptr);
+ put_short(rrtype, &ptr);
+ put_short(rdlen, &ptr);
+ put_rdata(rdlen, rdata, &ptr);
+ put_long(ttl, &ptr);
+
+ rref = malloc(sizeof(_DNSRecordRef_t));
+ if (!rref) goto error;
+ rref->app_context = NULL;
+ rref->app_callback = NULL;
+ rref->record_index = sdRef->max_index++;
+ rref->sdr = sdRef;
+ *RecordRef = rref;
+ hdr->client_context.context = rref;
+ hdr->reg_index = rref->record_index;
+ return deliver_request((char *)hdr, sdRef, 0);
+
+error:
+ if (hdr) free(hdr);
+ if (rref) free(rref);
+ if (*RecordRef) *RecordRef = NULL;
+ return kDNSServiceErr_Unknown;
+}
+
+
+//DNSRecordRef returned by DNSServiceRegisterRecord or DNSServiceAddRecord
+DNSServiceErrorType DNSServiceUpdateRecord
+ (
+ const DNSServiceRef sdRef,
+ DNSRecordRef RecordRef,
+ const DNSServiceFlags flags,
+ const uint16_t rdlen,
+ const void *rdata,
+ const uint32_t ttl
+ )
+ {
+ ipc_msg_hdr *hdr;
+ int len = 0;
+ char *ptr;
+
+ if (!sdRef || !RecordRef || !sdRef->max_index)
+ return kDNSServiceErr_BadReference;
+
+ len += sizeof(uint16_t);
+ len += rdlen;
+ len += sizeof(uint32_t);
+ len += sizeof(DNSServiceFlags);
+
+ hdr = create_hdr(update_record_request, &len, &ptr, 0);
+ if (!hdr) return kDNSServiceErr_Unknown;
+ hdr->reg_index = RecordRef ? RecordRef->record_index : TXT_RECORD_INDEX;
+ put_flags(flags, &ptr);
+ put_short(rdlen, &ptr);
+ put_rdata(rdlen, rdata, &ptr);
+ put_long(ttl, &ptr);
+ return deliver_request((char *)hdr, sdRef, 0);
+ }
+
+
+
+DNSServiceErrorType DNSServiceRemoveRecord
+(
+ const DNSServiceRef sdRef,
+ const DNSRecordRef RecordRef,
+ const DNSServiceFlags flags
+ )
+ {
+ ipc_msg_hdr *hdr;
+ int len = 0;
+ char *ptr;
+ DNSServiceErrorType err;
+
+ if (!sdRef || !RecordRef || !sdRef->max_index)
+ return kDNSServiceErr_BadReference;
+
+ len += sizeof(flags);
+ hdr = create_hdr(remove_record_request, &len, &ptr, 0);
+ if (!hdr) return kDNSServiceErr_Unknown;
+ hdr->reg_index = RecordRef->record_index;
+ put_flags(flags, &ptr);
+ err = deliver_request((char *)hdr, sdRef, 0);
+ if (!err) free(RecordRef);
+ return err;
+ }
+
+
+void DNSServiceReconfirmRecord
+(
+ const DNSServiceFlags flags,
+ const uint32_t interfaceIndex,
+ const char *fullname,
+ const uint16_t rrtype,
+ const uint16_t rrclass,
+ const uint16_t rdlen,
+ const void *rdata
+ )
+ {
+ char *ptr;
+ int len;
+ ipc_msg_hdr *hdr;
+ DNSServiceRef tmp;
+
+ len = sizeof(DNSServiceFlags);
+ len += sizeof(uint32_t);
+ len += strlen(fullname) + 1;
+ len += 3 * sizeof(uint16_t);
+ len += rdlen;
+ tmp = connect_to_server();
+ if (!tmp) return;
+ hdr = create_hdr(reconfirm_record_request, &len, &ptr, 1);
+ if (!hdr) return;
+
+ put_flags(flags, &ptr);
+ put_long(interfaceIndex, &ptr);
+ put_string(fullname, &ptr);
+ put_short(rrtype, &ptr);
+ put_short(rrclass, &ptr);
+ put_short(rdlen, &ptr);
+ put_rdata(rdlen, rdata, &ptr);
+ my_write(tmp->sockfd, (char *)hdr, len);
+ DNSServiceRefDeallocate(tmp);
+ }
+
+
+int DNSServiceConstructFullName
+ (
+ char *fullName,
+ const char *service, /* may be NULL */
+ const char *regtype,
+ const char *domain
+ )
+ {
+ int len;
+ u_char c;
+ char *fn = fullName;
+ const char *s = service;
+ const char *r = regtype;
+ const char *d = domain;
+
+ if (service)
+ {
+ while(*s)
+ {
+ c = *s++;
+ if (c == '.' || (c == '\\')) *fn++ = '\\'; // escape dot and backslash literals
+ else if (c <= ' ') // escape non-printable characters
+ {
+ *fn++ = '\\';
+ *fn++ = (char) ('0' + (c / 100));
+ *fn++ = (char) ('0' + (c / 10) % 10);
+ c = (u_char)('0' + (c % 10));
+ }
+ *fn++ = c;
+ }
+ *fn++ = '.';
+ }
+
+ if (!regtype) return -1;
+ len = strlen(regtype);
+ if (domain_ends_in_dot(regtype)) len--;
+ if (len < 4) return -1; // regtype must end in _udp or _tcp
+ if (strncmp((regtype + len - 4), "_tcp", 4) && strncmp((regtype + len - 4), "_udp", 4)) return -1;
+ while(*r)
+ *fn++ = *r++;
+ if (!domain_ends_in_dot(regtype)) *fn++ = '.';
+
+ if (!domain) return -1;
+ len = strlen(domain);
+ if (!len) return -1;
+ while(*d)
+ *fn++ = *d++;
+ if (!domain_ends_in_dot(domain)) *fn++ = '.';
+ *fn = '\0';
+ return 0;
+ }
+
+static int domain_ends_in_dot(const char *dom)
+ {
+ while(*dom && *(dom + 1))
+ {
+ if (*dom == '\\') // advance past escaped byte sequence
+ {
+ if (*(dom + 1) >= '0' && *(dom + 1) <= '9') dom += 4;
+ else dom += 2;
+ }
+ else dom++; // else read one character
+ }
+ return (*dom == '.');
+ }
+
+
+
+ // return a connected service ref (deallocate with DNSServiceRefDeallocate)
+static DNSServiceRef connect_to_server(void)
+ {
+ struct sockaddr_un saddr;
+ DNSServiceRef sdr;
+
+ sdr = malloc(sizeof(_DNSServiceRef_t));
+ if (!sdr) return NULL;
+
+ if ((sdr->sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
+ {
+ free(sdr);
+ return NULL;
+ }
+
+ saddr.sun_family = AF_LOCAL;
+ strcpy(saddr.sun_path, MDNS_UDS_SERVERPATH);
+ if (connect(sdr->sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
+ {
+ free(sdr);
+ return NULL;
+ }
+ return sdr;
+ }
+
+
+
+
+int my_write(int sd, char *buf, int len)
+ {
+ if (send(sd, buf, len, MSG_WAITALL) != len) return -1;
+ return 0;
+ }
+
+
+// read len bytes. return 0 on success, -1 on error
+int my_read(int sd, char *buf, int len)
+ {
+ if (recv(sd, buf, len, MSG_WAITALL) != len) return -1;
+ return 0;
+ }
+
+
+DNSServiceErrorType deliver_request(void *msg, DNSServiceRef sdr, int reuse_sd)
+ {
+ ipc_msg_hdr *hdr = msg;
+ mode_t mask;
+ struct sockaddr_un caddr, daddr; // (client and daemon address structs)
+ char *path = NULL;
+ int listenfd = -1, errsd = -1, len;
+ DNSServiceErrorType err = kDNSServiceErr_Unknown;
+
+ if (!hdr || sdr->sockfd < 0) return kDNSServiceErr_Unknown;
+
+ if (!reuse_sd)
+ {
+ // setup temporary error socket
+ if ((listenfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
+ goto cleanup;
+
+ bzero(&caddr, sizeof(caddr));
+ caddr.sun_family = AF_LOCAL;
+ caddr.sun_len = sizeof(struct sockaddr_un);
+ path = (char *)msg + sizeof(ipc_msg_hdr);
+ strcpy(caddr.sun_path, path);
+ mask = umask(0);
+ if (bind(listenfd, (struct sockaddr *)&caddr, sizeof(caddr)) < 0)
+ {
+ umask(mask);
+ goto cleanup;
+ }
+ umask(mask);
+ listen(listenfd, 1);
+ }
+
+ if (my_write(sdr->sockfd, msg, hdr->datalen + sizeof(ipc_msg_hdr)) < 0)
+ goto cleanup;
+ free(msg);
+ msg = NULL;
+
+ if (reuse_sd) errsd = sdr->sockfd;
+ else
+ {
+ len = sizeof(daddr);
+ errsd = accept(listenfd, (struct sockaddr *)&daddr, &len);
+ if (errsd < 0) goto cleanup;
+ }
+
+ len = recv(errsd, &err, sizeof(err), MSG_WAITALL);
+ if (len != sizeof(err))
+ {
+ err = kDNSServiceErr_Unknown;
+ }
+cleanup:
+ if (!reuse_sd && listenfd > 0) close(listenfd);
+ if (!reuse_sd && errsd > 0) close(errsd);
+ if (!reuse_sd && path) unlink(path);
+ if (msg) free(msg);
+ return err;
+ }
+
+
+
+/* create_hdr
+ *
+ * allocate and initialize an ipc message header. value of len should initially be the
+ * length of the data, and is set to the value of the data plus the header. data_start
+ * is set to point to the beginning of the data section. reuse_socket should be non-zero
+ * for calls that can receive an immediate error return value on their primary socket.
+ * if zero, the path to a control socket is appended at the beginning of the message buffer.
+ * data_start is set past this string.
+ */
+
+static ipc_msg_hdr *create_hdr(int op, int *len, char **data_start, int reuse_socket)
+ {
+ char *msg = NULL;
+ ipc_msg_hdr *hdr;
+ int datalen;
+ char ctrl_path[256];
+ struct timeval time;
+
+ if (!reuse_socket)
+ {
+ if (gettimeofday(&time, NULL) < 0) return NULL;
+ sprintf(ctrl_path, "%s%d-%.3x-%.6u", CTL_PATH_PREFIX, (int)getpid(),
+ time.tv_sec & 0xFFF, time.tv_usec);
+
+ *len += strlen(ctrl_path) + 1;
+ }
+
+
+ datalen = *len;
+ *len += sizeof(ipc_msg_hdr);
+
+ // write message to buffer
+ msg = malloc(*len);
+ if (!msg) return NULL;
+
+ bzero(msg, *len);
+ hdr = (void *)msg;
+ hdr->datalen = datalen;
+ hdr->version = VERSION;
+ hdr->op.request_op = op;
+ if (reuse_socket) hdr->flags |= IPC_FLAGS_REUSE_SOCKET;
+ *data_start = msg + sizeof(ipc_msg_hdr);
+ if (!reuse_socket) put_string(ctrl_path, data_start);
+ return hdr;
+ }
--- /dev/null
+/*
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * 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
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+
+ Change History (most recent first):
+
+$Log: dnssd_ipc.c,v $
+Revision 1.7 2003/08/12 19:56:25 cheshire
+Update to APSL 2.0
+
+ */
+
+#include "dnssd_ipc.h"
+
+void put_flags(const DNSServiceFlags flags, char **ptr)
+ {
+ memcpy(*ptr, &flags, sizeof(DNSServiceFlags));
+ *ptr += sizeof(flags);
+ }
+
+DNSServiceFlags get_flags(char **ptr)
+ {
+ DNSServiceFlags flags;
+
+ flags = *(DNSServiceFlags *)*ptr;
+ *ptr += sizeof(DNSServiceFlags);
+ return flags;
+ }
+
+void put_long(const uint32_t l, char **ptr)
+ {
+
+ *(uint32_t *)(*ptr) = l;
+ *ptr += sizeof(uint32_t);
+ }
+
+uint32_t get_long(char **ptr)
+ {
+ uint32_t l;
+
+ l = *(uint32_t *)(*ptr);
+ *ptr += sizeof(uint32_t);
+ return l;
+ }
+
+void put_error_code(const DNSServiceErrorType error, char **ptr)
+ {
+ memcpy(*ptr, &error, sizeof(error));
+ *ptr += sizeof(DNSServiceErrorType);
+ }
+
+DNSServiceErrorType get_error_code(char **ptr)
+ {
+ DNSServiceErrorType error;
+
+ error = *(DNSServiceErrorType *)(*ptr);
+ *ptr += sizeof(DNSServiceErrorType);
+ return error;
+ }
+
+void put_short(const uint16_t s, char **ptr)
+ {
+ *(uint16_t *)(*ptr) = s;
+ *ptr += sizeof(uint16_t);
+ }
+
+uint16_t get_short(char **ptr)
+ {
+ uint16_t s;
+
+ s = *(uint16_t *)(*ptr);
+ *ptr += sizeof(uint16_t);
+ return s;
+ }
+
+
+int put_string(const char *str, char **ptr)
+ {
+ if (!str) str = "";
+ strcpy(*ptr, str);
+ *ptr += strlen(str) + 1;
+ return 0;
+ }
+
+// !!!KRS we don't properly handle the case where the string is longer than the buffer!!!
+int get_string(char **ptr, char *buffer, int buflen)
+ {
+ int overrun;
+
+ overrun = (int)strlen(*ptr) < buflen ? 0 : -1;
+ strncpy(buffer, *ptr, buflen - 1);
+ buffer[buflen - 1] = '\0';
+ *ptr += strlen(buffer) + 1;
+ return overrun;
+ }
+
+void put_rdata(const int rdlen, const char *rdata, char **ptr)
+ {
+ memcpy(*ptr, rdata, rdlen);
+ *ptr += rdlen;
+ }
+
+char *get_rdata(char **ptr, int rdlen)
+ {
+ char *rd;
+
+ rd = *ptr;
+ *ptr += rdlen;
+ return rd;
+ }
+
+
+
+
+
+
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * 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
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+
+ Change History (most recent first):
+
+$Log: dnssd_ipc.h,v $
+Revision 1.6 2003/08/12 19:56:25 cheshire
+Update to APSL 2.0
+
+ */
+
+#ifndef DNSSD_IPC_H
+#define DNSSD_IPC_H
+
+#include "dns_sd.h"
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/un.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+//#define UDSDEBUG // verbose debug output
+
+// General UDS constants
+#define MDNS_UDS_SERVERPATH "/var/run/mDNSResponder"
+#define LISTENQ 100
+#define TXT_RECORD_INDEX -1 // record index for default text record
+#define MAX_CTLPATH 256 // longest legal control path length
+
+// IPC data encoding constants and types
+#define VERSION 1
+#define IPC_FLAGS_NOREPLY 1 // set flag if no asynchronous replies are to be sent to client
+#define IPC_FLAGS_REUSE_SOCKET 2 // set flag if synchronous errors are to be sent via the primary socket
+ // (if not set, first string in message buffer must be path to error socket
+
+
+typedef enum
+ {
+ connection = 1, // connected socket via DNSServiceConnect()
+ reg_record_request, // reg/remove record only valid for connected sockets
+ remove_record_request,
+ enumeration_request,
+ reg_service_request,
+ browse_request,
+ resolve_request,
+ query_request,
+ reconfirm_record_request,
+ add_record_request,
+ update_record_request
+ } request_op_t;
+
+typedef enum
+ {
+ enumeration_reply = 64,
+ reg_service_reply,
+ browse_reply,
+ resolve_reply,
+ query_reply,
+ reg_record_reply
+ } reply_op_t;
+
+
+typedef struct ipc_msg_hdr_struct ipc_msg_hdr;
+
+
+// client stub callback to process message from server and deliver results to
+// client application
+
+typedef void (*process_reply_callback)
+ (
+ DNSServiceRef sdr,
+ ipc_msg_hdr *hdr,
+ char *msg
+ );
+
+// allow 64-bit client to interoperate w/ 32-bit daemon
+typedef union
+ {
+ void *context;
+ uint32_t ptr64[2];
+ } client_context_t;
+
+
+typedef struct ipc_msg_hdr_struct
+ {
+ uint32_t version;
+ uint32_t datalen;
+ uint32_t flags;
+ union
+ {
+ request_op_t request_op;
+ reply_op_t reply_op;
+ } op;
+ client_context_t client_context; // context passed from client, returned by server in corresponding reply
+ int reg_index; // identifier for a record registered via DNSServiceRegisterRecord() on a
+ // socket connected by DNSServiceConnect(). Must be unique in the scope of the connection, such that and
+ // index/socket pair uniquely identifies a record. (Used to select records for removal by DNSServiceRemoveRecord())
+ } ipc_msg_hdr_struct;
+
+
+
+
+// routines to write to and extract data from message buffers.
+// caller responsible for bounds checking.
+// ptr is the address of the pointer to the start of the field.
+// it is advanced to point to the next field, or the end of the message
+
+
+void put_flags(const DNSServiceFlags flags, char **ptr);
+DNSServiceFlags get_flags(char **ptr);
+
+void put_long(const uint32_t l, char **ptr);
+uint32_t get_long(char **ptr);
+
+void put_error_code(const DNSServiceErrorType, char **ptr);
+DNSServiceErrorType get_error_code(char **ptr);
+
+int put_string(const char *str, char **ptr);
+int get_string(char **ptr, char *buffer, int buflen);
+
+void put_rdata(const int rdlen, const char *rdata, char **ptr);
+char *get_rdata(char **ptr, int rdlen); // return value is rdata pointed to by *ptr -
+ // rdata is not copied from buffer.
+
+void put_short(uint16_t s, char **ptr);
+uint16_t get_short(char **ptr);
+
+
+
+#endif // DNSSD_IPC_H
+
+
+
+
+
+
+
+
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * 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
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+
+ Change History (most recent first):
+
+$Log: uds_daemon.c,v $
+Revision 1.22.2.1 2003/12/05 00:03:35 cheshire
+<rdar://problem/3487869> Use buffer size MAX_ESCAPED_DOMAIN_NAME instead of 256
+
+Revision 1.22 2003/08/19 16:03:55 ksekar
+Bug #: <rdar://problem/3380097>: ER: SIGINFO dump should include resolves started by DNSServiceQueryRecord
+Check termination_context for NULL before dereferencing.
+
+Revision 1.21 2003/08/19 05:39:43 cheshire
+<rdar://problem/3380097> SIGINFO dump should include resolves started by DNSServiceQueryRecord
+
+Revision 1.20 2003/08/16 03:39:01 cheshire
+<rdar://problem/3338440> InterfaceID -1 indicates "local only"
+
+Revision 1.19 2003/08/15 20:16:03 cheshire
+<rdar://problem/3366590> 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.18 2003/08/15 00:38:00 ksekar
+Bug #: <rdar://problem/3377005>: Bug: buffer overrun when reading long rdata from client
+
+Revision 1.17 2003/08/14 02:18:21 cheshire
+<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
+
+Revision 1.16 2003/08/13 23:58:52 ksekar
+Bug #: <rdar://problem/3374911>: Bug: UDS Sub-type browsing works, but not sub-type registration
+Fixed pointer increment error, moved subtype reading for-loop for easier error bailout.
+
+Revision 1.15 2003/08/13 17:30:33 ksekar
+Bug #: <rdar://problem/3374671>: DNSServiceAddRecord doesn't work
+Fixed various problems with handling the AddRecord request and freeing the ExtraResourceRecords.
+
+Revision 1.14 2003/08/12 19:56:25 cheshire
+Update to APSL 2.0
+
+ */
+
+#include "mDNSClientAPI.h"
+#include "mDNSMacOSX.h"
+#include "dns_sd.h"
+#include "dnssd_ipc.h"
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+// Types and Data Structures
+// ----------------------------------------------------------------------
+
+typedef enum
+ {
+ t_uninitialized,
+ t_morecoming,
+ t_complete,
+ t_error,
+ t_terminated
+ } transfer_state;
+
+typedef void (*req_termination_fn)(void *);
+
+
+typedef struct registered_record_entry
+ {
+ int key;
+ AuthRecord *rr;
+ struct registered_record_entry *next;
+ } registered_record_entry;
+
+typedef struct registered_service
+ {
+ //struct registered_service *next;
+ int autoname;
+ int renameonconflict;
+ int rename_on_memfree; // set flag on config change when we deregister original name
+ domainlabel name;
+ ServiceRecordSet *srs;
+ struct request_state *request;
+ AuthRecord *subtypes;
+ } registered_service;
+
+typedef struct
+ {
+ mStatus err;
+ int nwritten;
+ int sd;
+ } undelivered_error_t;
+
+typedef struct request_state
+ {
+ // connection structures
+ CFRunLoopSourceRef rls;
+ CFSocketRef sr;
+ int sd;
+ int errfd;
+
+ // state of read (in case message is read over several recv() calls)
+ transfer_state ts;
+ uint32_t hdr_bytes; // bytes of header already read
+ ipc_msg_hdr hdr;
+ uint32_t data_bytes; // bytes of message data already read
+ char *msgbuf; // pointer to data storage to pass to free()
+ char *msgdata; // pointer to data to be read from (may be modified)
+ int bufsize; // size of data storage
+
+ // reply, termination, error, and client context info
+ int no_reply; // don't send asynchronous replies to client
+ void *client_context; // don't touch this - pointer only valid in client's addr space
+ struct reply_state *replies; // corresponding (active) reply list
+ undelivered_error_t *u_err;
+ void *termination_context;
+ req_termination_fn terminate;
+
+ //!!!KRS toss these pointers in a union
+ // registration context associated with this request (null if not applicable)
+ registered_record_entry *reg_recs; // muliple registrations for a connection-oriented request
+ registered_service *service; // service record set and flags
+ struct resolve_result_t *resolve_results;
+
+ struct request_state *next;
+ } request_state;
+
+// struct physically sits between ipc message header and call-specific fields in the message buffer
+typedef struct
+ {
+ DNSServiceFlags flags;
+ uint32_t ifi;
+ DNSServiceErrorType error;
+ } reply_hdr;
+
+
+typedef struct reply_state
+ {
+ // state of the transmission
+ int sd;
+ transfer_state ts;
+ uint32_t nwriten;
+ uint32_t len;
+ // context of the reply
+ struct request_state *request; // the request that this answers
+ struct reply_state *next; // if there are multiple unsent replies
+ // pointer into message buffer - allows fields to be changed after message is formatted
+ ipc_msg_hdr *mhdr;
+ reply_hdr *rhdr;
+ char *sdata; // pointer to start of call-specific data
+ // pointer to malloc'd buffer
+ char *msgbuf;
+ } reply_state;
+
+
+// domain enumeration and resolv calls require 2 mDNSCore calls, so we need separate interconnected
+// structures to handle callbacks
+typedef struct
+ {
+ DNSQuestion question;
+ mDNS_DomainType type;
+ request_state *rstate;
+ } domain_enum_t;
+
+typedef struct
+ {
+ domain_enum_t *all;
+ domain_enum_t *def;
+ request_state *rstate;
+ } enum_termination_t;
+
+typedef struct
+ {
+ DNSQuestion question;
+ uint16_t qtype;
+ request_state *rstate;
+ } resolve_t;
+
+typedef struct
+ {
+ resolve_t *txt;
+ resolve_t *srv;
+ request_state *rstate;
+ } resolve_termination_t;
+
+typedef struct resolve_result_t
+ {
+ const ResourceRecord *txt;
+ const ResourceRecord *srv;
+ } resolve_result_t;
+
+typedef struct
+ {
+ request_state *rstate;
+ client_context_t client_context;
+ } regrecord_callback_context;
+
+
+
+
+// globals
+static int listenfd = -1;
+static request_state *all_requests = NULL;
+//!!!KRS we should keep a separate list containing only the requests that need to be examined
+//in the idle() routine.
+
+
+#define MAX_OPENFILES 1024
+
+
+// private function prototypes
+static void connect_callback(CFSocketRef sr, CFSocketCallBackType t, CFDataRef dr, const void *c, void *i);
+static int read_msg(request_state *rs);
+static int send_msg(reply_state *rs);
+static void abort_request(request_state *rs);
+static void request_callback(CFSocketRef sr, CFSocketCallBackType t, CFDataRef dr, const void *c, void *i);
+static void handle_resolve_request(request_state *rstate);
+static void question_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord);
+static void question_termination_callback(void *context);
+static void handle_browse_request(request_state *request);
+static void browse_termination_callback(void *context);
+static void browse_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord);
+static void handle_regservice_request(request_state *request);
+static void regservice_termination_callback(void *context);
+static void process_service_registration(ServiceRecordSet *const srs);
+static void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result);
+static void handle_add_request(request_state *rstate);
+static void handle_update_request(request_state *rstate);
+static mStatus gen_rr_response(domainname *servicename, mDNSInterfaceID id, request_state *request, reply_state **rep);
+static void append_reply(request_state *req, reply_state *rep);
+static int build_domainname_from_strings(domainname *srv, char *name, char *regtype, char *domain);
+static void enum_termination_callback(void *context);
+static void enum_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord);
+static void handle_query_request(request_state *rstate);
+static mStatus do_question(request_state *rstate, domainname *name, uint32_t ifi, uint16_t rrtype, int16_t rrclass);
+static reply_state *format_enumeration_reply(request_state *rstate, char *domain, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err);
+static void handle_enum_request(request_state *rstate);
+static void handle_regrecord_request(request_state *rstate);
+static void regrecord_callback(mDNS *const m, AuthRecord *const rr, mStatus result);
+static void connected_registration_termination(void *context);
+static void handle_reconfirm_request(request_state *rstate);
+static AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int ttl);
+static void handle_removerecord_request(request_state *rstate);
+static void reset_connected_rstate(request_state *rstate);
+static int deliver_error(request_state *rstate, mStatus err);
+static int deliver_async_error(request_state *rs, reply_op_t op, mStatus err);
+static transfer_state send_undelivered_error(request_state *rs);
+static reply_state *create_reply(reply_op_t op, int datalen, request_state *request);
+static void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd);
+static void my_perror(char *errmsg);
+static void unlink_request(request_state *rs);
+static void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord);
+static void resolve_termination_callback(void *context);
+
+// initialization, setup/teardown functions
+
+int udsserver_init(void)
+ {
+ mode_t mask;
+ struct sockaddr_un laddr;
+ struct rlimit maxfds;
+
+ if ((listenfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
+ goto error;
+ unlink(MDNS_UDS_SERVERPATH); //OK if this fails
+ bzero(&laddr, sizeof(laddr));
+ laddr.sun_family = AF_LOCAL;
+ laddr.sun_len = sizeof(struct sockaddr_un);
+ strcpy(laddr.sun_path, MDNS_UDS_SERVERPATH);
+ mask = umask(0);
+ if (bind(listenfd, (struct sockaddr *)&laddr, sizeof(laddr)) < 0)
+ goto error;
+ umask(mask);
+
+ if (fcntl(listenfd, F_SETFL, O_NONBLOCK) < 0)
+ {
+ my_perror("ERROR: could not set listen socket to non-blocking mode");
+ goto error;
+ }
+ listen(listenfd, LISTENQ);
+
+
+ // set maximum file descriptor to 1024
+ if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0)
+ {
+ my_perror("ERROR: Unable to get file descriptor limit");
+ return 0;
+ }
+ if (maxfds.rlim_max >= MAX_OPENFILES && maxfds.rlim_cur == maxfds.rlim_max)
+ {
+ // proper values already set
+ return 0;
+ }
+ maxfds.rlim_max = MAX_OPENFILES;
+ maxfds.rlim_cur = MAX_OPENFILES;
+ if (setrlimit(RLIMIT_NOFILE, &maxfds) < 0)
+ my_perror("ERROR: Unable to set maximum file descriptor limit");
+ return 0;
+
+error:
+ my_perror("ERROR: udsserver_init");
+ return -1;
+ }
+
+int udsserver_exit(void)
+ {
+ close(listenfd);
+ unlink(MDNS_UDS_SERVERPATH);
+ return 0;
+ }
+
+
+// add the named socket as a runloop source
+int udsserver_add_rl_source(void)
+ {
+ CFSocketContext context = { 0, NULL, NULL, NULL, NULL };
+ CFSocketRef sr = CFSocketCreateWithNative(kCFAllocatorDefault, listenfd, kCFSocketReadCallBack, connect_callback, &context);
+ if (!sr)
+ {
+ debugf("ERROR: udsserver_add_rl_source - CFSocketCreateWithNative");
+ return -1;
+ }
+ CFRunLoopSourceRef rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, sr, 0);
+
+ if (!rls)
+ {
+ debugf("ERROR: udsserver_add_rl_source - CFSocketCreateRunLoopSource");
+ return -1;
+ }
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+ return 0;
+ }
+
+mDNSs32 udsserver_idle(mDNSs32 nextevent)
+ {
+ request_state *req = all_requests, *tmp, *prev = NULL;
+ reply_state *fptr;
+ transfer_state result;
+ mDNSs32 now = mDNSPlatformTimeNow();
+
+
+ while(req)
+ {
+ result = t_uninitialized;
+ if (req->u_err)
+ result = send_undelivered_error(req);
+ if (result != t_error && result != t_morecoming && // don't try to send msg if send_error failed
+ (req->ts == t_complete || req->ts == t_morecoming))
+ {
+ while(req->replies)
+ {
+ if (req->replies->next) req->replies->rhdr->flags |= kDNSServiceFlagsMoreComing;
+ else req->replies->rhdr->flags |= kDNSServiceFlagsFinished;
+ result = send_msg(req->replies);
+ if (result == t_complete)
+ {
+ fptr = req->replies;
+ req->replies = req->replies->next;
+ freeL("udsserver_idle", fptr);
+ }
+ else if (result == t_terminated || result == t_error)
+ {
+ abort_request(req);
+ break;
+ }
+ else if (result == t_morecoming) // client's queues are full, move to next
+ {
+ if (nextevent - now > mDNSPlatformOneSecond)
+ nextevent = now + mDNSPlatformOneSecond;
+ break; // start where we left off in a second
+ }
+ }
+ }
+ if (result == t_terminated || result == t_error)
+ //since we're already doing a list traversal, we unlink the request manunally instead of calling unlink_request()
+ {
+ tmp = req;
+ if (prev) prev->next = req->next;
+ if (req == all_requests) all_requests = all_requests->next;
+ req = req->next;
+ freeL("udsserver_idle", tmp);
+ }
+ else
+ {
+ prev = req;
+ req = req->next;
+ }
+ }
+ return nextevent;
+ }
+
+void udsserver_info(void)
+ {
+ request_state *req;
+ for (req = all_requests; req; req=req->next)
+ {
+ void *t = req->termination_context;
+ if (!t) continue;
+ if (req->terminate == regservice_termination_callback)
+ LogMsg("DNSServiceRegister %##s", ((registered_service *) t)->srs->RR_SRV.resrec.name.c);
+ else if (req->terminate == browse_termination_callback)
+ LogMsg("DNSServiceBrowse %##s", ((DNSQuestion *) t)->qname.c);
+ else if (req->terminate == resolve_termination_callback)
+ LogMsg("DNSServiceResolve %##s", ((resolve_termination_t *)t)->srv->question.qname.c);
+ else if (req->terminate == question_termination_callback)
+ LogMsg("DNSServiceQueryRecord %##s", ((DNSQuestion *) t)->qname.c);
+ else if (req->terminate == enum_termination_callback)
+ LogMsg("DNSServiceEnumerateDomains %##s", ((enum_termination_t *) t)->all->question.qname.c);
+ }
+ }
+
+void udsserver_handle_configchange(void)
+ {
+ registered_service *srv;
+ request_state *req;
+ mStatus err;
+
+ for (req = all_requests; req; req = req->next)
+ {
+ srv = req->service;
+ if (srv->autoname && !SameDomainLabel(srv->name.c, mDNSStorage.nicelabel.c))
+ {
+ srv->rename_on_memfree = 1;
+ err = mDNS_DeregisterService(&mDNSStorage, srv->srs);
+ if (err) LogMsg("ERROR: udsserver_handle_configchange: DeregisterService returned error %d. Continuing.", err);
+ // error should never occur - safest to log and continue
+ }
+ }
+ }
+
+
+
+
+// accept a connection on the named socket, adding the new descriptor to the runloop and passing the error
+// descriptor to the client
+static void connect_callback(CFSocketRef s, CFSocketCallBackType t, CFDataRef dr, const void *c, void *i)
+ {
+ int sd, clilen, optval;
+ struct sockaddr_un cliaddr;
+ CFSocketContext context = { 0, NULL, NULL, NULL, NULL };
+ request_state *rstate;
+// int errpipe[2];
+
+ #pragma unused(s, t, dr, c, i)
+
+ clilen = sizeof(cliaddr);
+ sd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
+
+ if (sd < 0)
+ {
+ if (errno == EWOULDBLOCK) return;
+ my_perror("ERROR: accept");
+ return;
+ }
+ optval = 1;
+ if (setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0)
+ {
+ my_perror("ERROR: setsockopt - SOL_NOSIGPIPE - aborting client");
+ close(sd);
+ return;
+ }
+
+ if (fcntl(sd, F_SETFL, O_NONBLOCK) < 0)
+ {
+ my_perror("ERROR: could not set connected socket to non-blocking mode - aborting client");
+ close(sd);
+ return;
+ }
+
+/*
+ // open a pipe to deliver error messages, pass descriptor to client
+ if (pipe(errpipe) < 0)
+ {
+ my_perror("ERROR: could not create pipe");
+ exit(1);
+ }
+
+ if (ioctl(sd, I_SENDFD, errpipe[0]) < 0)
+ {
+ my_perror("ERROR: could not pass pipe descriptor to client. Aborting client.\n");
+ close(sd);
+ return;
+ }
+ if (fcntl(errpipe[1], F_SETFL, O_NONBLOCK) < 0)
+ {
+ my_perror("ERROR: could not set error pipe to non-blocking mode - aborting client");
+ close(sd);
+ close(errpipe[1]);
+ return;
+ }
+ */
+
+ // allocate a request_state struct that will live with the socket
+ rstate = mallocL("connect_callback", sizeof(request_state));
+ if (!rstate)
+ {
+ my_perror("ERROR: malloc");
+ exit(1);
+ }
+ bzero(rstate, sizeof(request_state));
+ rstate->ts = t_morecoming;
+ rstate->sd = sd;
+ //rstate->errfd = errpipe[1];
+
+ //now create CFSocket wrapper and add to run loop
+ context.info = rstate;
+ rstate->sr = CFSocketCreateWithNative(kCFAllocatorDefault, sd, kCFSocketReadCallBack, request_callback, &context);
+ if (!rstate->sr)
+ {
+ debugf("ERROR: connect_callback - CFSocketCreateWithNative");
+ freeL("connect_callback", rstate);
+ close(sd);
+ return;
+ }
+ rstate->rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, rstate->sr, 0);
+ if (!rstate->rls)
+ {
+ debugf("ERROR: connect_callback - CFSocketCreateRunLoopSource");
+ CFSocketInvalidate(rstate->sr); // automatically closes socket
+ CFRelease(rstate->sr);
+ freeL("connect_callback", rstate);
+ return;
+ }
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rstate->rls, kCFRunLoopDefaultMode);
+ if (!CFRunLoopContainsSource(CFRunLoopGetCurrent(), rstate->rls, kCFRunLoopDefaultMode))
+ {
+ LogMsg("ERROR: connect_callback, CFRunLoopAddSource");
+ abort_request(rstate);
+ return;
+ }
+ rstate->next = all_requests;
+ all_requests = rstate;
+ }
+
+
+// main client request handling routine. reads request and calls the appropriate request-specific
+// handler
+static void request_callback(CFSocketRef sr, CFSocketCallBackType t, CFDataRef dr, const void *context, void *info)
+ {
+ request_state *rstate = info;
+ transfer_state result;
+ struct sockaddr_un cliaddr;
+ char ctrl_path[MAX_CTLPATH];
+
+ #pragma unused(sr, t, dr, context)
+
+ int native = CFSocketGetNative(sr);
+ if (native != rstate->sd)
+ {
+ LogMsg("ERROR: request_callback - CFSocket's native descriptor does not match rstate member descriptor.");
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+
+ result = read_msg(rstate);
+ if (result == t_morecoming)
+ {
+ return;
+ }
+ if (result == t_terminated)
+ {
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+ if (result == t_error)
+ {
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+
+ if (rstate->hdr.version != VERSION)
+ {
+ LogMsg("ERROR: client incompatible with daemon (client version = %d, "
+ "daemon version = %d)\n", rstate->hdr.version, VERSION);
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+
+ // check if client wants silent operation
+ if (rstate->hdr.flags & IPC_FLAGS_NOREPLY) rstate->no_reply = 1;
+
+ // check if primary socket is to be used for synchronous errors, else open new socket
+ if (rstate->hdr.flags & IPC_FLAGS_REUSE_SOCKET)
+ rstate->errfd = rstate->sd;
+ else
+ {
+ if ((rstate->errfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
+ {
+ my_perror("ERROR: socket");
+ exit(1);
+ }
+ if (fcntl(rstate->errfd, F_SETFL, O_NONBLOCK) < 0)
+ {
+ my_perror("ERROR: could not set control socket to non-blocking mode");
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+ get_string(&rstate->msgdata, ctrl_path, 256); // path is first element in message buffer
+ bzero(&cliaddr, sizeof(cliaddr));
+ cliaddr.sun_family = AF_LOCAL;
+ strcpy(cliaddr.sun_path, ctrl_path);
+ if (connect(rstate->errfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)) < 0)
+ {
+ my_perror("ERROR: connect");
+ abort_request(rstate);
+ unlink_request(rstate);
+ }
+ }
+
+
+
+
+ switch(rstate->hdr.op.request_op)
+ {
+ case resolve_request: handle_resolve_request(rstate); break;
+ case query_request: handle_query_request(rstate); break;
+ case browse_request: handle_browse_request(rstate); break;
+ case reg_service_request: handle_regservice_request(rstate); break;
+ case enumeration_request: handle_enum_request(rstate); break;
+ case reg_record_request: handle_regrecord_request(rstate); break;
+ case add_record_request: handle_add_request(rstate); break;
+ case update_record_request: handle_update_request(rstate); break;
+ case remove_record_request: handle_removerecord_request(rstate); break;
+ case reconfirm_record_request: handle_reconfirm_request(rstate); break;
+ default:
+ debugf("ERROR: udsserver_recv_request - unsupported request type: %d", rstate->hdr.op.request_op);
+ }
+ }
+
+// mDNS operation functions. Each operation has 3 associated functions - a request handler that parses
+// the client's request and makes the appropriate mDNSCore call, a result handler (passed as a callback
+// to the mDNSCore routine) that sends results back to the client, and a termination routine that aborts
+// the mDNSCore operation if the client dies or closes its socket.
+
+
+// query and resolve calls have separate request handlers that parse the arguments from the client and
+// massage the name parameters appropriately, but the rest of the operations (making the query call,
+// delivering the result to the client, and termination) are identical.
+
+static void handle_query_request(request_state *rstate)
+ {
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex;
+ char name[256];
+ uint16_t rrtype, rrclass;
+ char *ptr;
+ domainname dname;
+ mStatus result;
+
+ if (rstate->ts != t_complete)
+ {
+ LogMsg("ERROR: handle_query_request - transfer state != t_complete");
+ goto error;
+ }
+ ptr = rstate->msgdata;
+ if (!ptr)
+ {
+ LogMsg("ERROR: handle_query_request - NULL msgdata");
+ goto error;
+ }
+ flags = get_flags(&ptr);
+ interfaceIndex = get_long(&ptr);
+ if (get_string(&ptr, name, 256) < 0) goto bad_param;
+ rrtype = get_short(&ptr);
+ rrclass = get_short(&ptr);
+ if (!MakeDomainNameFromDNSNameString(&dname, name)) goto bad_param;
+ result = do_question(rstate, &dname, interfaceIndex, rrtype, rrclass);
+ if (result) rstate->terminate = NULL;
+ if (deliver_error(rstate, result) < 0) goto error;
+ return;
+
+bad_param:
+ deliver_error(rstate, mStatus_BadParamErr);
+ rstate->terminate = NULL; // don't try to terminate insuccessful Core calls
+error:
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+
+static void handle_resolve_request(request_state *rstate)
+ {
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex;
+ char name[256], regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME];
+ char *ptr; // message data pointer
+ domainname fqdn;
+ resolve_t *srv, *txt;
+ resolve_termination_t *term;
+ mStatus err;
+
+ if (rstate->ts != t_complete)
+ {
+ LogMsg("ERROR: handle_resolve_request - transfer state != t_complete");
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+
+ // extract the data from the message
+ ptr = rstate->msgdata;
+ if (!ptr)
+ {
+ LogMsg("ERROR: handle_resolve_request - NULL msgdata");
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+ flags = get_flags(&ptr);
+ interfaceIndex = get_long(&ptr);
+ mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+ if (interfaceIndex && !InterfaceID) goto bad_param;
+ if (get_string(&ptr, name, 256) < 0 ||
+ get_string(&ptr, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+ get_string(&ptr, domain, MAX_ESCAPED_DOMAIN_NAME) < 0)
+ goto bad_param;
+
+ // free memory in rstate since we don't need it anymore
+ freeL("handle_resolve_request", rstate->msgbuf);
+ rstate->msgbuf = NULL;
+
+ if (build_domainname_from_strings(&fqdn, name, regtype, domain) < 0)
+ goto bad_param;
+
+ // allocate question wrapper structs
+ srv = mallocL("handle_resolve_request", sizeof(resolve_t));
+ txt = mallocL("handle_resolve_request", sizeof(resolve_t));
+ if (!srv || !txt) goto malloc_error;
+ srv->qtype = kDNSType_SRV;
+ txt->qtype = kDNSType_TXT;
+ srv->rstate = rstate;
+ txt->rstate = rstate;
+
+ // format questions
+ srv->question.QuestionContext = rstate;
+ srv->question.QuestionCallback = resolve_result_callback;
+ memcpy(&srv->question.qname, &fqdn, MAX_DOMAIN_NAME);
+ srv->question.qtype = kDNSType_SRV;
+ srv->question.qclass = kDNSClass_IN;
+ srv->question.InterfaceID = InterfaceID;
+
+ txt->question.QuestionContext = rstate;
+ txt->question.QuestionCallback = resolve_result_callback;
+ memcpy(&txt->question.qname, &fqdn, MAX_DOMAIN_NAME);
+ txt->question.qtype = kDNSType_TXT;
+ txt->question.qclass = kDNSClass_IN;
+ txt->question.InterfaceID = InterfaceID;
+
+ // set up termination info
+ term = mallocL("handle_resolve_request", sizeof(resolve_termination_t));
+ if (!term) goto malloc_error;
+ term->srv = srv;
+ term->txt = txt;
+ term->rstate = rstate;
+ rstate->termination_context = term;
+ rstate->terminate = resolve_termination_callback;
+
+ // set up reply wrapper struct (since answer will come via 2 callbacks)
+ rstate->resolve_results = mallocL("handle_resolve_response", sizeof(resolve_result_t));
+ if (!rstate->resolve_results) goto malloc_error;
+ bzero(rstate->resolve_results, sizeof(resolve_result_t));
+
+ // ask the questions
+ err = mDNS_StartQuery(&mDNSStorage, &srv->question);
+ if (!err) err = mDNS_StartQuery(&mDNSStorage, &txt->question);
+
+ if (err)
+ {
+ freeL("handle_resolve_request", txt);
+ freeL("handle_resolve_request", srv);
+ freeL("handle_resolve_request", term);
+ freeL("handle_resolve_request", rstate->resolve_results);
+ rstate->terminate = NULL; // prevent abort_request() from invoking termination callback
+ }
+ if (deliver_error(rstate, err) < 0 || err)
+ {
+ abort_request(rstate);
+ unlink_request(rstate);
+ }
+ return;
+
+bad_param:
+ deliver_error(rstate, mStatus_BadParamErr);
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+
+malloc_error:
+ my_perror("ERROR: malloc");
+ exit(1);
+ }
+
+static void resolve_termination_callback(void *context)
+ {
+ resolve_termination_t *term = context;
+ request_state *rs;
+
+ if (!term)
+ {
+ LogMsg("ERROR: resolve_termination_callback: double termination");
+ return;
+ }
+ rs = term->rstate;
+
+ mDNS_StopQuery(&mDNSStorage, &term->txt->question);
+ mDNS_StopQuery(&mDNSStorage, &term->srv->question);
+
+ freeL("resolve_termination_callback", term->txt);
+ freeL("resolve_termination_callback", term->srv);
+ freeL("resolve_termination_callback", term);
+ rs->termination_context = NULL;
+ freeL("resolve_termination_callback", rs->resolve_results);
+ rs->resolve_results = NULL;
+ }
+
+
+
+static void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
+{
+ int len = 0;
+ char fullname[MAX_ESCAPED_DOMAIN_NAME], target[MAX_ESCAPED_DOMAIN_NAME];
+ char *data;
+ transfer_state result;
+ reply_state *rep;
+ request_state *rs = question->QuestionContext;
+ resolve_result_t *res = rs->resolve_results;
+ #pragma unused(m)
+
+ if (!AddRecord)
+ {
+ if (answer->rrtype == kDNSType_TXT && res->txt == answer) res->txt = mDNSNULL;
+ if (answer->rrtype == kDNSType_SRV && res->srv == answer) res->srv = mDNSNULL;
+ return;
+ }
+
+ if (answer->rrtype == kDNSType_TXT) res->txt = answer;
+ if (answer->rrtype == kDNSType_SRV) res->srv = answer;
+
+ if (!res->txt || !res->srv) return; // only deliver result to client if we have both answers
+
+ ConvertDomainNameToCString(&answer->name, fullname);
+ ConvertDomainNameToCString(&res->srv->rdata->u.srv.target, target);
+
+ // calculate reply length
+ len += sizeof(DNSServiceFlags);
+ len += sizeof(uint32_t); // interface index
+ len += sizeof(DNSServiceErrorType);
+ len += strlen(fullname) + 1;
+ len += strlen(target) + 1;
+ len += 2 * sizeof(uint16_t); // port, txtLen
+ len += res->txt->rdlength;
+
+ // allocate/init reply header
+ rep = create_reply(resolve_reply, len, rs);
+ rep->rhdr->flags = 0;
+ rep->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, answer->InterfaceID);
+ rep->rhdr->error = kDNSServiceErr_NoError;
+ data = rep->sdata;
+
+ // write reply data to message
+ put_string(fullname, &data);
+ put_string(target, &data);
+ put_short(res->srv->rdata->u.srv.port.NotAnInteger, &data);
+ put_short(res->txt->rdlength, &data);
+ put_rdata(res->txt->rdlength, res->txt->rdata->u.txt.c, &data);
+
+ result = send_msg(rep);
+ if (result == t_error || result == t_terminated)
+ {
+ abort_request(rs);
+ unlink_request(rs);
+ freeL("resolve_result_callback", rep);
+ }
+ else if (result == t_complete) freeL("resolve_result_callback", rep);
+ else append_reply(rs, rep);
+ }
+
+
+
+
+// common query issuing routine for resolve and query requests
+static mStatus do_question(request_state *rstate, domainname *name, uint32_t ifi, uint16_t rrtype, int16_t rrclass)
+ {
+ DNSQuestion *q;
+ mStatus result;
+ mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, ifi);
+ if (ifi && !InterfaceID) return(mStatus_BadParamErr);
+
+ q = mallocL("do_question", sizeof(DNSQuestion));
+ if (!q)
+ {
+ my_perror("ERROR: do_question - malloc");
+ exit(1);
+ }
+ bzero(q, sizeof(DNSQuestion));
+
+ q->QuestionContext = rstate;
+ q->QuestionCallback = question_result_callback;
+ memcpy(&q->qname, name, MAX_DOMAIN_NAME);
+ q->qtype = rrtype;
+ q->qclass = rrclass;
+ q->InterfaceID = InterfaceID;
+
+
+ rstate->termination_context = q;
+ rstate->terminate = question_termination_callback;
+
+ result = mDNS_StartQuery(&mDNSStorage, q);
+ if (result != mStatus_NoError) LogMsg("ERROR: mDNS_StartQuery: %d", (int)result);
+ return result;
+ }
+
+// what gets called when a resolve is completed and we need to send the data back to the client
+static void question_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
+ {
+ char *data;
+ char name[MAX_ESCAPED_DOMAIN_NAME];
+ request_state *req;
+ reply_state *rep;
+ int len;
+
+ #pragma unused(m)
+ //mDNS_StopQuery(m, question);
+ req = question->QuestionContext;
+
+ // calculate reply data length
+ len = sizeof(DNSServiceFlags);
+ len += 2 * sizeof(uint32_t); // if index + ttl
+ len += sizeof(DNSServiceErrorType);
+ len += 3 * sizeof(uint16_t); // type, class, rdlen
+ len += answer->rdlength;
+ ConvertDomainNameToCString(&answer->name, name);
+ len += strlen(name) + 1;
+
+ rep = create_reply(query_reply, len, req);
+ rep->rhdr->flags = AddRecord ? kDNSServiceFlagsAdd : kDNSServiceFlagsRemove;
+ rep->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, answer->InterfaceID);
+ rep->rhdr->error = kDNSServiceErr_NoError;
+ data = rep->sdata;
+
+ put_string(name, &data);
+ put_short(answer->rrtype, &data);
+ put_short(answer->rrclass, &data);
+ put_short(answer->rdlength, &data);
+ put_rdata(answer->rdlength, (char *)&answer->rdata->u, &data);
+ put_long(AddRecord ? answer->rroriginalttl : 0, &data);
+
+ append_reply(req, rep);
+ return;
+ }
+
+static void question_termination_callback(void *context)
+ {
+ DNSQuestion *q = context;
+
+
+ mDNS_StopQuery(&mDNSStorage, q); // no need to error check
+ freeL("question_termination_callback", q);
+ }
+
+
+static void handle_browse_request(request_state *request)
+ {
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex;
+ char regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME];
+ DNSQuestion *q;
+ domainname typedn, domdn;
+ char *ptr;
+ mStatus result;
+
+ if (request->ts != t_complete)
+ {
+ LogMsg("ERROR: handle_browse_request - transfer state != t_complete");
+ abort_request(request);
+ unlink_request(request);
+ return;
+ }
+ q = mallocL("handle_browse_request", sizeof(DNSQuestion));
+ if (!q)
+ {
+ my_perror("ERROR: handle_browse_request - malloc");
+ exit(1);
+ }
+ bzero(q, sizeof(DNSQuestion));
+
+ // extract data from message
+ ptr = request->msgdata;
+ flags = get_flags(&ptr);
+ interfaceIndex = get_long(&ptr);
+ if (get_string(&ptr, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+ get_string(&ptr, domain, MAX_ESCAPED_DOMAIN_NAME) < 0)
+ goto bad_param;
+
+ freeL("handle_browse_request", request->msgbuf);
+ request->msgbuf = NULL;
+
+ mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+ if (interfaceIndex && !InterfaceID) goto bad_param;
+ q->QuestionContext = request;
+ q->QuestionCallback = browse_result_callback;
+ if (!MakeDomainNameFromDNSNameString(&typedn, regtype) ||
+ !MakeDomainNameFromDNSNameString(&domdn, domain[0] ? domain : "local."))
+ goto bad_param;
+ request->termination_context = q;
+ request->terminate = browse_termination_callback;
+ result = mDNS_StartBrowse(&mDNSStorage, q, &typedn, &domdn, InterfaceID, browse_result_callback, request);
+ deliver_error(request, result);
+ return;
+
+bad_param:
+ deliver_error(request, mStatus_BadParamErr);
+ abort_request(request);
+ unlink_request(request);
+ }
+
+static void browse_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
+ {
+ request_state *req;
+ reply_state *rep;
+ mStatus err;
+
+ #pragma unused(m)
+ req = question->QuestionContext;
+
+ err = gen_rr_response(&answer->rdata->u.name, answer->InterfaceID, req, &rep);
+ if (err)
+ {
+ if (deliver_async_error(req, browse_reply, err) < 0)
+ {
+ abort_request(req);
+ unlink_request(req);
+ }
+ return;
+ }
+ if (AddRecord) rep->rhdr->flags |= kDNSServiceFlagsAdd; // non-zero TTL indicates add
+ append_reply(req, rep);
+ return;
+ }
+
+static void browse_termination_callback(void *context)
+ {
+ DNSQuestion *q = context;
+
+ mDNS_StopBrowse(&mDNSStorage, q); // no need to error-check result
+ freeL("browse_termination_callback", q);
+ }
+
+// service registration
+static void handle_regservice_request(request_state *request)
+ {
+ DNSServiceFlags flags;
+ uint32_t ifi;
+ char name[256], regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME], host[MAX_ESCAPED_DOMAIN_NAME];
+ uint16_t txtlen;
+ mDNSIPPort port;
+ void *txtdata;
+ char *ptr;
+ domainlabel n;
+ domainname t, d, h, srv;
+ registered_service *r_srv;
+ int srs_size;
+ mStatus result;
+
+ char *sub, *rtype_ptr;
+ int i, num_subtypes;
+
+
+ if (request->ts != t_complete)
+ {
+ LogMsg("ERROR: handle_regservice_request - transfer state != t_complete");
+ abort_request(request);
+ unlink_request(request);
+ return;
+ }
+
+ // extract data from message
+ ptr = request->msgdata;
+ flags = get_flags(&ptr);
+ ifi = get_long(&ptr);
+ mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, ifi);
+ if (ifi && !InterfaceID) goto bad_param;
+ if (get_string(&ptr, name, 256) < 0 ||
+ get_string(&ptr, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+ get_string(&ptr, domain, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+ get_string(&ptr, host, MAX_ESCAPED_DOMAIN_NAME) < 0)
+ goto bad_param;
+
+ port.NotAnInteger = get_short(&ptr);
+ txtlen = get_short(&ptr);
+ txtdata = get_rdata(&ptr, txtlen);
+
+ // count subtypes, replacing commas w/ whitespace
+ rtype_ptr = regtype;
+ num_subtypes = -1;
+ while((sub = strsep(&rtype_ptr, ",")))
+ if (*sub) num_subtypes++;
+
+ if (!name[0]) n = (&mDNSStorage)->nicelabel;
+ else if (!MakeDomainLabelFromLiteralString(&n, name))
+ goto bad_param;
+ if ((!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) ||
+ (!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) ||
+ (!ConstructServiceName(&srv, &n, &t, &d)))
+ goto bad_param;
+ if (host[0] && !MakeDomainNameFromDNSNameString(&h, host)) goto bad_param;
+
+ r_srv = mallocL("handle_regservice_request", sizeof(registered_service));
+ if (!r_srv) goto malloc_error;
+ srs_size = sizeof(ServiceRecordSet) + (sizeof(RDataBody) > txtlen ? 0 : txtlen - sizeof(RDataBody));
+ r_srv->srs = mallocL("handle_regservice_request", srs_size);
+ if (!r_srv->srs) goto malloc_error;
+ if (num_subtypes > 0)
+ {
+ r_srv->subtypes = mallocL("handle_regservice_request", num_subtypes * sizeof(AuthRecord));
+ if (!r_srv->subtypes) goto malloc_error;
+ sub = regtype + strlen(regtype) + 1;
+ for (i = 0; i < num_subtypes; i++)
+ {
+ if (!MakeDomainNameFromDNSNameString(&(r_srv->subtypes + i)->resrec.name, sub))
+ {
+ freeL("handle_regservice_request", r_srv->subtypes);
+ freeL("handle_regservice_request", r_srv);
+ r_srv = NULL;
+ goto bad_param;
+ }
+ sub += strlen(sub) + 1;
+ }
+ }
+ else r_srv->subtypes = NULL;
+ r_srv->request = request;
+
+ r_srv->autoname = (!name[0]);
+ r_srv->rename_on_memfree = 0;
+ r_srv->renameonconflict = !(flags & kDNSServiceFlagsNoAutoRename);
+ r_srv->name = n;
+ request->termination_context = r_srv;
+ request->terminate = regservice_termination_callback;
+ request->service = r_srv;
+
+ result = mDNS_RegisterService(&mDNSStorage, r_srv->srs, &n, &t, &d, host[0] ? &h : NULL, port,
+ txtdata, txtlen, r_srv->subtypes, num_subtypes, InterfaceID, regservice_callback, r_srv);
+ deliver_error(request, result);
+ if (result != mStatus_NoError)
+ {
+ abort_request(request);
+ unlink_request(request);
+ }
+ else
+ {
+ reset_connected_rstate(request); // reset to receive add/remove messages
+ }
+ return;
+
+bad_param:
+ deliver_error(request, mStatus_BadParamErr);
+ abort_request(request);
+ unlink_request(request);
+return;
+
+malloc_error:
+ my_perror("ERROR: malloc");
+ exit(1);
+ }
+
+// service registration callback performs three duties - frees memory for deregistered services,
+// handles name conflicts, and delivers completed registration information to the client (via
+// process_service_registraion())
+
+static void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result)
+ {
+ mStatus err;
+ ExtraResourceRecord *extra;
+ registered_service *r_srv = srs->ServiceContext;
+ request_state *rs = r_srv->request;
+
+ #pragma unused(m)
+
+ if (!rs && (result != mStatus_MemFree && !r_srv->rename_on_memfree))
+ {
+ // error should never happen - safest to log and continue
+ LogMsg("ERROR: regservice_callback: received result %d with a NULL request pointer\n");
+ return;
+ }
+
+ if (result == mStatus_NoError)
+ return process_service_registration(srs);
+ else if (result == mStatus_MemFree)
+ {
+ if (r_srv->rename_on_memfree)
+ {
+ r_srv->rename_on_memfree = 0;
+ r_srv->name = mDNSStorage.nicelabel;
+ err = mDNS_RenameAndReregisterService(&mDNSStorage, srs, &r_srv->name);
+ if (err) LogMsg("ERROR: regservice_callback - RenameAndReregisterService returned %d", err);
+ // error should never happen - safest to log and continue
+ }
+ else
+ {
+ while (r_srv->srs->Extras)
+ {
+ extra = r_srv->srs->Extras;
+ r_srv->srs->Extras = r_srv->srs->Extras->next;
+ freeL("regservice_callback", extra);
+ }
+ freeL("regservice_callback", r_srv->srs);
+ if (r_srv->subtypes) freeL("regservice_callback", r_srv->subtypes);
+ if (r_srv->request) r_srv->request->service = NULL;
+ freeL("regservice_callback", r_srv);
+ return;
+ }
+ }
+ else if (result == mStatus_NameConflict)
+ {
+ if (r_srv->autoname || r_srv->renameonconflict)
+ {
+ mDNS_RenameAndReregisterService(&mDNSStorage, srs, mDNSNULL);
+ return;
+ }
+ else
+ {
+ freeL("regservice_callback", r_srv);
+ freeL("regservice_callback", r_srv->srs);
+ if (r_srv->subtypes) freeL("regservice_callback", r_srv->subtypes);
+ if (r_srv->request) r_srv->request->service = NULL;
+ freeL("regservice_callback", r_srv);
+ if (deliver_async_error(rs, reg_service_reply, result) < 0)
+ {
+ abort_request(rs);
+ unlink_request(rs);
+ }
+ return;
+ }
+ }
+ else
+ {
+ LogMsg("ERROR: unknown result in regservice_callback");
+ if (deliver_async_error(rs, reg_service_reply, result) < 0)
+ {
+ abort_request(rs);
+ unlink_request(rs);
+ }
+ return;
+ }
+ }
+
+static void handle_add_request(request_state *rstate)
+ {
+ registered_record_entry *re;
+ ExtraResourceRecord *extra;
+ uint32_t size, ttl;
+ uint16_t rrtype, rdlen;
+ char *ptr, *rdata;
+ mStatus result;
+ DNSServiceFlags flags;
+ ServiceRecordSet *srs = rstate->service->srs;
+
+ if (!srs)
+ {
+ LogMsg("ERROR: handle_add_request - no service record set in request state");
+ deliver_error(rstate, mStatus_UnknownErr);
+ return;
+ }
+
+ ptr = rstate->msgdata;
+ flags = get_flags(&ptr);
+ rrtype = get_short(&ptr);
+ rdlen = get_short(&ptr);
+ rdata = get_rdata(&ptr, rdlen);
+ ttl = get_long(&ptr);
+
+ if (rdlen > sizeof(RDataBody)) size = rdlen;
+ else size = sizeof(RDataBody);
+
+ extra = mallocL("hanle_add_request", sizeof(ExtraResourceRecord) - sizeof(RDataBody) + size);
+ if (!extra)
+ {
+ my_perror("ERROR: malloc");
+ exit(1);
+ }
+
+ bzero(extra, sizeof(ExtraResourceRecord)); // OK if oversized rdata not zero'd
+ extra->r.resrec.rrtype = rrtype;
+ extra->r.rdatastorage.MaxRDLength = size;
+ extra->r.resrec.rdlength = rdlen;
+ memcpy(&extra->r.rdatastorage.u.data, rdata, rdlen);
+ result = mDNS_AddRecordToService(&mDNSStorage, srs , extra, &extra->r.rdatastorage, ttl);
+ deliver_error(rstate, result);
+ reset_connected_rstate(rstate);
+ if (result)
+ {
+ freeL("handle_add_request", rstate->msgbuf);
+ rstate->msgbuf = NULL;
+ freeL("handle_add_request", extra);
+ return;
+ }
+ re = mallocL("handle_add_request", sizeof(registered_record_entry));
+ if (!re)
+ {
+ my_perror("ERROR: malloc");
+ exit(1);
+ }
+ re->key = rstate->hdr.reg_index;
+ re->rr = &extra->r;
+ re->next = rstate->reg_recs;
+ rstate->reg_recs = re;
+ }
+
+static void handle_update_request(request_state *rstate)
+ {
+ registered_record_entry *reptr;
+ AuthRecord *rr;
+ RData *newrd;
+ uint16_t rdlen, rdsize;
+ char *ptr, *rdata;
+ uint32_t ttl;
+ mStatus result;
+
+ if (rstate->hdr.reg_index == TXT_RECORD_INDEX)
+ {
+ if (!rstate->service)
+ {
+ deliver_error(rstate, mStatus_BadParamErr);
+ return;
+ }
+ rr = &rstate->service->srs->RR_TXT;
+ }
+ else
+ {
+ reptr = rstate->reg_recs;
+ while(reptr && reptr->key != rstate->hdr.reg_index) reptr = reptr->next;
+ if (!reptr) deliver_error(rstate, mStatus_BadReferenceErr);
+ rr = reptr->rr;
+ }
+
+ ptr = rstate->msgdata;
+ get_flags(&ptr); // flags unused
+ rdlen = get_short(&ptr);
+ rdata = get_rdata(&ptr, rdlen);
+ ttl = get_long(&ptr);
+
+ if (rdlen > sizeof(RDataBody)) rdsize = rdlen;
+ else rdsize = sizeof(RDataBody);
+ newrd = mallocL("handle_update_request", sizeof(RData) - sizeof(RDataBody) + rdsize);
+ if (!newrd)
+ {
+ my_perror("ERROR: malloc");
+ exit(1);
+ }
+ newrd->MaxRDLength = rdsize;
+ memcpy(&newrd->u, rdata, rdlen);
+ result = mDNS_Update(&mDNSStorage, rr, ttl, rdlen, newrd, update_callback);
+ deliver_error(rstate, result);
+ reset_connected_rstate(rstate);
+ }
+
+static void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd)
+ {
+ #pragma unused(m)
+
+ if (oldrd != &rr->rdatastorage) freeL("update_callback", oldrd);
+ }
+
+static void process_service_registration(ServiceRecordSet *const srs)
+ {
+ reply_state *rep;
+ transfer_state send_result;
+ mStatus err;
+ registered_service *r_srv = srs->ServiceContext;
+ request_state *req = r_srv->request;
+
+
+ err = gen_rr_response(&srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, req, &rep);
+ if (err)
+ {
+ if (deliver_async_error(req, reg_service_reply, err) < 0)
+ {
+ abort_request(req);
+ unlink_request(req);
+ }
+ return;
+ }
+ send_result = send_msg(rep);
+ if (send_result == t_error || send_result == t_terminated)
+ {
+ abort_request(req);
+ unlink_request(req);
+ freeL("process_service_registration", rep);
+ }
+ else if (send_result == t_complete) freeL("process_service_registration", rep);
+ else append_reply(req, rep);
+ }
+
+static void regservice_termination_callback(void *context)
+ {
+ registered_service *srv = context;
+
+ // only safe to free memory if registration is not valid, ie deregister fails
+ if (mDNS_DeregisterService(&mDNSStorage, srv->srs) != mStatus_NoError)
+ {
+ freeL("regservice_callback", srv->srs);
+ if (srv->subtypes) freeL("regservice_callback", srv->subtypes);
+ freeL("regservice_callback", srv);
+ freeL("regservice_termination_callback", srv);
+ }
+ }
+
+
+static void handle_regrecord_request(request_state *rstate)
+ {
+ AuthRecord *rr;
+ regrecord_callback_context *rcc;
+ registered_record_entry *re;
+ mStatus result;
+
+ if (rstate->ts != t_complete)
+ {
+ LogMsg("ERROR: handle_regrecord_request - transfer state != t_complete");
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+
+ rr = read_rr_from_ipc_msg(rstate->msgdata, 1);
+ if (!rr)
+ {
+ deliver_error(rstate, mStatus_BadParamErr);
+ return;
+ }
+
+ rcc = mallocL("hanlde_regrecord_request", sizeof(regrecord_callback_context));
+ if (!rcc) goto malloc_error;
+ rcc->rstate = rstate;
+ rcc->client_context = rstate->hdr.client_context;
+ rr->RecordContext = rcc;
+ rr->RecordCallback = regrecord_callback;
+
+ // allocate registration entry, link into list
+ re = mallocL("hanlde_regrecord_request", sizeof(registered_record_entry));
+ if (!re) goto malloc_error;
+ re->key = rstate->hdr.reg_index;
+ re->rr = rr;
+ re->next = rstate->reg_recs;
+ rstate->reg_recs = re;
+
+ if (!rstate->terminate)
+ {
+ rstate->terminate = connected_registration_termination;
+ rstate->termination_context = rstate;
+ }
+
+ result = mDNS_Register(&mDNSStorage, rr);
+ deliver_error(rstate, result);
+ reset_connected_rstate(rstate);
+ return;
+
+malloc_error:
+ my_perror("ERROR: malloc");
+ return;
+ }
+
+static void regrecord_callback(mDNS *const m, AuthRecord *const rr, mStatus result)
+ {
+ regrecord_callback_context *rcc;
+ int len;
+ reply_state *reply;
+ transfer_state ts;
+
+ #pragma unused(m)
+
+ if (result == mStatus_MemFree) { freeL("regrecord_callback", rr); return; }
+ rcc = rr->RecordContext;
+
+ // format result, add to the list for the request, including the client context in the header
+ len = sizeof(DNSServiceFlags);
+ len += sizeof(uint32_t); //interfaceIndex
+ len += sizeof(DNSServiceErrorType);
+
+ reply = create_reply(reg_record_reply, len, rcc->rstate);
+ reply->mhdr->client_context = rcc->client_context;
+ reply->rhdr->flags = 0;
+ reply->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, rr->resrec.InterfaceID);
+ reply->rhdr->error = result;
+
+ ts = send_msg(reply);
+ if (ts == t_error || ts == t_terminated)
+ {
+ abort_request(rcc->rstate);
+ unlink_request(rcc->rstate);
+ }
+ else if (ts == t_complete) freeL("regrecord_callback", reply);
+ else if (ts == t_morecoming) append_reply(rcc->rstate, reply); // client is blocked, link reply into list
+ }
+
+static void connected_registration_termination(void *context)
+ {
+ registered_record_entry *fptr, *ptr = ((request_state *)context)->reg_recs;
+ while(ptr)
+ {
+ mDNS_Deregister(&mDNSStorage, ptr->rr);
+ fptr = ptr;
+ ptr = ptr->next;
+ freeL("connected_registration_termination", fptr);
+ }
+ }
+
+
+
+static void handle_removerecord_request(request_state *rstate)
+ {
+ registered_record_entry *reptr, *prev = NULL;
+ mStatus err = mStatus_UnknownErr;
+ char *ptr;
+ reptr = rstate->reg_recs;
+
+ ptr = rstate->msgdata;
+ get_flags(&ptr); // flags unused
+
+ while(reptr)
+ {
+ if (reptr->key == rstate->hdr.reg_index) // found match
+ {
+ if (prev) prev->next = reptr->next;
+ else rstate->reg_recs = reptr->next;
+ err = mDNS_Deregister(&mDNSStorage, reptr->rr);
+ freeL("handle_removerecord_request", reptr); //rr gets freed by callback
+ break;
+ }
+ prev = reptr;
+ reptr = reptr->next;
+ }
+ reset_connected_rstate(rstate);
+ if (deliver_error(rstate, err) < 0)
+ {
+ abort_request(rstate);
+ unlink_request(rstate);
+ }
+ }
+
+
+// domain enumeration
+static void handle_enum_request(request_state *rstate)
+ {
+ DNSServiceFlags flags, add_default;
+ uint32_t ifi;
+ char *ptr = rstate->msgdata;
+ domain_enum_t *def, *all;
+ enum_termination_t *term;
+ reply_state *reply; // initial default reply
+ transfer_state tr;
+ mStatus err;
+ int result;
+
+ if (rstate->ts != t_complete)
+ {
+ LogMsg("ERROR: handle_enum_request - transfer state != t_complete");
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+
+ flags = get_flags(&ptr);
+ ifi = get_long(&ptr);
+ mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, ifi);
+ if (ifi && !InterfaceID)
+ {
+ deliver_error(rstate, mStatus_BadParamErr);
+ abort_request(rstate);
+ unlink_request(rstate);
+ }
+
+ // allocate context structures
+ def = mallocL("hanlde_enum_request", sizeof(domain_enum_t));
+ all = mallocL("handle_enum_request", sizeof(domain_enum_t));
+ term = mallocL("handle_enum_request", sizeof(enum_termination_t));
+ if (!def || !all || !term)
+ {
+ my_perror("ERROR: malloc");
+ exit(1);
+ }
+
+ // enumeration requires multiple questions, so we must link all the context pointers so that
+ // necessary context can be reached from the callbacks
+ def->rstate = rstate;
+ all->rstate = rstate;
+ term->def = def;
+ term->all = all;
+ term->rstate = rstate;
+ rstate->termination_context = term;
+ rstate->terminate = enum_termination_callback;
+ def->question.QuestionContext = def;
+ def->type = (flags & kDNSServiceFlagsRegistrationDomains) ?
+ mDNS_DomainTypeRegistrationDefault: mDNS_DomainTypeBrowseDefault;
+ all->question.QuestionContext = all;
+ all->type = (flags & kDNSServiceFlagsRegistrationDomains) ?
+ mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse;
+
+ // make the calls
+ err = mDNS_GetDomains(&mDNSStorage, &all->question, all->type, InterfaceID, enum_result_callback, all);
+ if (err == mStatus_NoError)
+ err = mDNS_GetDomains(&mDNSStorage, &def->question, def->type, InterfaceID, enum_result_callback, def);
+ result = deliver_error(rstate, err); // send error *before* returning local domain
+
+ if (result < 0 || err)
+ {
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+
+ // provide local. as the first domain automatically
+ add_default = kDNSServiceFlagsDefault | kDNSServiceFlagsAdd | kDNSServiceFlagsFinished;
+ reply = format_enumeration_reply(rstate, "local.", add_default, ifi, 0);
+ tr = send_msg(reply);
+ if (tr == t_error || tr == t_terminated)
+ {
+ freeL("handle_enum_request", def);
+ freeL("handle_enum_request", all);
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+ if (tr == t_complete) freeL("handle_enum_request", reply);
+ if (tr == t_morecoming) append_reply(rstate, reply); // couldn't send whole reply because client is blocked - link into list
+ }
+
+static void enum_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
+ {
+ char domain[MAX_ESCAPED_DOMAIN_NAME];
+ domain_enum_t *de = question->QuestionContext;
+ DNSServiceFlags flags = 0;
+ reply_state *reply;
+
+ #pragma unused(m)
+ if (answer->rrtype != kDNSType_PTR) return;
+ if (AddRecord)
+ {
+ flags |= kDNSServiceFlagsAdd;
+ if (de->type == mDNS_DomainTypeRegistrationDefault || de->type == mDNS_DomainTypeBrowseDefault)
+ flags |= kDNSServiceFlagsDefault;
+ }
+ else
+ {
+ flags |= kDNSServiceFlagsRemove;
+ }
+ ConvertDomainNameToCString(&answer->rdata->u.name, domain);
+ reply = format_enumeration_reply(de->rstate, domain, flags, mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, answer->InterfaceID), kDNSServiceErr_NoError);
+ if (!reply)
+ {
+ LogMsg("ERROR: enum_result_callback, format_enumeration_reply");
+ return;
+ }
+ reply->next = NULL;
+ append_reply(de->rstate, reply);
+ return;
+ }
+
+static reply_state *format_enumeration_reply(request_state *rstate, char *domain, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err)
+ {
+ int len;
+ reply_state *reply;
+ char *data;
+
+
+ len = sizeof(DNSServiceFlags);
+ len += sizeof(uint32_t);
+ len += sizeof(DNSServiceErrorType);
+ len += strlen(domain) + 1;
+
+ reply = create_reply(enumeration_reply, len, rstate);
+ reply->rhdr->flags = flags;
+ reply->rhdr->ifi = ifi;
+ reply->rhdr->error = err;
+ data = reply->sdata;
+ put_string(domain, &data);
+ return reply;
+ }
+
+static void enum_termination_callback(void *context)
+ {
+ enum_termination_t *t = context;
+ mDNS *coredata = &mDNSStorage;
+
+ mDNS_StopGetDomains(coredata, &t->all->question);
+ mDNS_StopGetDomains(coredata, &t->def->question);
+ freeL("enum_termination_callback", t->all);
+ freeL("enum_termination_callback", t->def);
+ t->rstate->termination_context = NULL;
+ freeL("enum_termination_callback", t);
+ }
+
+static void handle_reconfirm_request(request_state *rstate)
+ {
+ AuthRecord *rr;
+
+ rr = read_rr_from_ipc_msg(rstate->msgdata, 0);
+ if (!rr) return;
+ mDNS_ReconfirmByValue(&mDNSStorage, &rr->resrec);
+ abort_request(rstate);
+ unlink_request(rstate);
+ freeL("handle_reconfirm_request", rr);
+ }
+
+
+// setup rstate to accept new reg/dereg requests
+static void reset_connected_rstate(request_state *rstate)
+ {
+ rstate->ts = t_morecoming;
+ rstate->hdr_bytes = 0;
+ rstate->data_bytes = 0;
+ if (rstate->msgbuf) freeL("reset_connected_rstate", rstate->msgbuf);
+ rstate->msgbuf = NULL;
+ rstate->bufsize = 0;
+ }
+
+
+
+// returns a resource record (allocated w/ malloc) containing the data found in an IPC message
+// data must be in format flags, interfaceIndex, name, rrtype, rrclass, rdlen, rdata, (optional)ttl
+// (ttl only extracted/set if ttl argument is non-zero). returns NULL for a bad-parameter error
+static AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int ttl)
+ {
+ char *rdata, name[256];
+ AuthRecord *rr;
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex;
+ uint16_t type, class, rdlen;
+ int storage_size;
+
+ flags = get_flags(&msgbuf);
+ interfaceIndex = get_long(&msgbuf);
+ if (get_string(&msgbuf, name, 256) < 0)
+ {
+ LogMsg("ERROR: read_rr_from_ipc_msg - get_string");
+ return NULL;
+ }
+ type = get_short(&msgbuf);
+ class = get_short(&msgbuf);
+ rdlen = get_short(&msgbuf);
+
+ if (rdlen > sizeof(RDataBody)) storage_size = rdlen;
+ else storage_size = sizeof(RDataBody);
+
+ rr = mallocL("read_rr_from_ipc_msg", sizeof(AuthRecord) - sizeof(RDataBody) + storage_size);
+ if (!rr)
+ {
+ my_perror("ERROR: malloc");
+ exit(1);
+ }
+ bzero(rr, sizeof(AuthRecord)); // ok if oversized rdata not zero'd
+ rr->resrec.rdata = &rr->rdatastorage;
+ rr->resrec.InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+ if (!MakeDomainNameFromDNSNameString(&rr->resrec.name, name))
+ {
+ LogMsg("ERROR: bad name: %s", name);
+ freeL("read_rr_from_ipc_msg", rr);
+ return NULL;
+ }
+ rr->resrec.rrtype = type;
+ if ((flags & kDNSServiceFlagsShared) == kDNSServiceFlagsShared)
+ rr->resrec.RecordType = kDNSRecordTypeShared;
+ if ((flags & kDNSServiceFlagsUnique) == kDNSServiceFlagsUnique)
+ rr->resrec.RecordType = kDNSRecordTypeUnique;
+ rr->resrec.rrclass = class;
+ rr->resrec.rdlength = rdlen;
+ rr->resrec.rdata->MaxRDLength = rdlen;
+ rdata = get_rdata(&msgbuf, rdlen);
+ memcpy(rr->resrec.rdata->u.data, rdata, rdlen);
+ if (ttl)
+ {
+ rr->resrec.rroriginalttl = get_long(&msgbuf);
+ }
+ return rr;
+ }
+
+
+// generate a response message for a browse result, service registration result, or any other call with the
+// identical callback signature. on successful completion rep is set to point to a malloc'd reply_state struct,
+// and mStatus_NoError is returned. otherwise the appropriate error is returned.
+
+static mStatus gen_rr_response(domainname *servicename, mDNSInterfaceID id, request_state *request, reply_state **rep)
+ {
+ char *data;
+ int len;
+ domainlabel name;
+ domainname type, dom;
+ char namestr[MAX_DOMAIN_LABEL+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL.
+ char typestr[MAX_ESCAPED_DOMAIN_NAME];
+ char domstr [MAX_ESCAPED_DOMAIN_NAME];
+
+ *rep = NULL;
+
+ if (!DeconstructServiceName(servicename, &name, &type, &dom))
+ return kDNSServiceErr_Unknown;
+
+ ConvertDomainLabelToCString_unescaped(&name, namestr);
+ ConvertDomainNameToCString(&type, typestr);
+ ConvertDomainNameToCString(&dom, domstr);
+
+ // calculate reply data length
+ len = sizeof(DNSServiceFlags);
+ len += sizeof(uint32_t); // if index
+ len += sizeof(DNSServiceErrorType);
+ len += strlen(namestr) + 1;
+ len += strlen(typestr) + 1;
+ len += strlen(domstr) + 1;
+
+ *rep = create_reply(query_reply, len, request);
+ (*rep)->rhdr->flags = 0;
+ (*rep)->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id);
+ (*rep)->rhdr->error = kDNSServiceErr_NoError;
+ data = (*rep)->sdata;
+
+ put_string(namestr, &data);
+ put_string(typestr, &data);
+ put_string(domstr, &data);
+ return mStatus_NoError;
+ }
+
+
+static int build_domainname_from_strings(domainname *srv, char *name, char *regtype, char *domain)
+ {
+ domainlabel n;
+ domainname d, t;
+
+ if (!MakeDomainLabelFromLiteralString(&n, name)) return -1;
+ if (!MakeDomainNameFromDNSNameString(&t, regtype)) return -1;
+ if (!MakeDomainNameFromDNSNameString(&d, domain)) return -1;
+ if (!ConstructServiceName(srv, &n, &t, &d)) return -1;
+ return 0;
+ }
+
+
+// append a reply to the list in a request object
+static void append_reply(request_state *req, reply_state *rep)
+ {
+ reply_state *ptr;
+
+ if (!req->replies) req->replies = rep;
+ else
+ {
+ ptr = req->replies;
+ while (ptr->next) ptr = ptr->next;
+ ptr->next = rep;
+ }
+ rep->next = NULL;
+ }
+
+
+// read_msg may be called any time when the transfer state (rs->ts) is t_morecoming.
+// returns the current state of the request (morecoming, error, complete, terminated.)
+// if there is no data on the socket, the socket will be closed and t_terminated will be returned
+static int read_msg(request_state *rs)
+ {
+ uint32_t nleft;
+ int nread;
+ char buf[4]; // dummy for death notification
+
+ if (rs->ts == t_terminated || rs->ts == t_error)
+ {
+ LogMsg("ERROR: read_msg called with transfer state terminated or error");
+ rs->ts = t_error;
+ return t_error;
+ }
+
+ if (rs->ts == t_complete)
+ { // this must be death or something is wrong
+ nread = recv(rs->sd, buf, 4, 0);
+ if (!nread) { rs->ts = t_terminated; return t_terminated; }
+ if (nread < 0) goto rerror;
+ LogMsg("ERROR: read data from a completed request.");
+ rs->ts = t_error;
+ return t_error;
+ }
+
+ if (rs->ts != t_morecoming)
+ {
+ LogMsg("ERROR: read_msg called with invalid transfer state (%d)", rs->ts);
+ rs->ts = t_error;
+ return t_error;
+ }
+
+ if (rs->hdr_bytes < sizeof(ipc_msg_hdr))
+ {
+ nleft = sizeof(ipc_msg_hdr) - rs->hdr_bytes;
+ nread = recv(rs->sd, (char *)&rs->hdr + rs->hdr_bytes, nleft, 0);
+ if (nread == 0) { rs->ts = t_terminated; return t_terminated; }
+ if (nread < 0) goto rerror;
+ rs->hdr_bytes += nread;
+ if (rs->hdr_bytes > sizeof(ipc_msg_hdr))
+ {
+ LogMsg("ERROR: read_msg - read too many header bytes");
+ rs->ts = t_error;
+ return t_error;
+ }
+ }
+
+ // only read data if header is complete
+ if (rs->hdr_bytes == sizeof(ipc_msg_hdr))
+ {
+ if (rs->hdr.datalen == 0) // ok in removerecord requests
+ {
+ rs->ts = t_complete;
+ rs->msgbuf = NULL;
+ return t_complete;
+ }
+
+ if (!rs->msgbuf) // allocate the buffer first time through
+ {
+ rs->msgbuf = mallocL("read_msg", rs->hdr.datalen);
+ if (!rs->msgbuf)
+ {
+ my_perror("ERROR: malloc");
+ rs->ts = t_error;
+ return t_error;
+ }
+ rs->msgdata = rs->msgbuf;
+ }
+ nleft = rs->hdr.datalen - rs->data_bytes;
+ nread = recv(rs->sd, rs->msgbuf + rs->data_bytes, nleft, 0);
+ if (nread == 0) { rs->ts = t_terminated; return t_terminated; }
+ if (nread < 0) goto rerror;
+ rs->data_bytes += nread;
+ if (rs->data_bytes > rs->hdr.datalen)
+ {
+ LogMsg("ERROR: read_msg - read too many data bytes");
+ rs->ts = t_error;
+ return t_error;
+ }
+ }
+
+ if (rs->hdr_bytes == sizeof(ipc_msg_hdr) && rs->data_bytes == rs->hdr.datalen)
+ rs->ts = t_complete;
+ else rs->ts = t_morecoming;
+
+ return rs->ts;
+
+rerror:
+ if (errno == EAGAIN || errno == EINTR) return rs->ts;
+ my_perror("ERROR: read_msg");
+ rs->ts = t_error;
+ return t_error;
+ }
+
+
+static int send_msg(reply_state *rs)
+ {
+ ssize_t nwriten;
+
+ if (!rs->msgbuf)
+ {
+ LogMsg("ERROR: send_msg called with NULL message buffer");
+ return t_error;
+ }
+
+ if (rs->request->no_reply) //!!!KRS this behavior should be optimized if it becomes more common
+ {
+ rs->ts = t_complete;
+ freeL("send_msg", rs->msgbuf);
+ return t_complete;
+ }
+
+ nwriten = send(rs->sd, rs->msgbuf + rs->nwriten, rs->len - rs->nwriten, 0);
+ if (nwriten < 0)
+ {
+ if (errno == EINTR || errno == EAGAIN) nwriten = 0;
+ else
+ {
+ if (errno == EPIPE)
+ {
+ LogMsg("broken pipe - cleanup should be handled by run-loop read wakeup");
+ rs->ts = t_terminated;
+ rs->request->ts = t_terminated;
+ return t_terminated;
+ }
+ else
+ {
+ my_perror("ERROR: send\n");
+ rs->ts = t_error;
+ return t_error;
+ }
+ }
+ }
+ rs->nwriten += nwriten;
+
+ if (rs->nwriten == rs->len)
+ {
+ rs->ts = t_complete;
+ freeL("send_msg", rs->msgbuf);
+ }
+ return rs->ts;
+ }
+
+
+
+static reply_state *create_reply(reply_op_t op, int datalen, request_state *request)
+{
+ reply_state *reply;
+ int totallen;
+
+
+ if ((unsigned)datalen < sizeof(reply_hdr))
+ {
+ LogMsg("ERROR: create_reply - data length less than lenght of required fields");
+ return NULL;
+ }
+
+ totallen = datalen + sizeof(ipc_msg_hdr);
+ reply = mallocL("create_reply", sizeof(reply_state));
+ if (!reply)
+ {
+ my_perror("ERROR: malloc");
+ exit(1);
+ }
+ bzero(reply, sizeof(reply_state));
+ reply->ts = t_morecoming;
+ reply->sd = request->sd;
+ reply->request = request;
+ reply->len = totallen;
+ reply->msgbuf = mallocL("create_reply", totallen);
+ if (!reply->msgbuf)
+ {
+ my_perror("ERROR: malloc");
+ exit(1);
+ }
+ bzero(reply->msgbuf, totallen);
+ reply->mhdr = (ipc_msg_hdr *)reply->msgbuf;
+ reply->rhdr = (reply_hdr *)(reply->msgbuf + sizeof(ipc_msg_hdr));
+ reply->sdata = reply->msgbuf + sizeof(ipc_msg_hdr) + sizeof(reply_hdr);
+ reply->mhdr->version = VERSION;
+ reply->mhdr->op.reply_op = op;
+ reply->mhdr->datalen = totallen - sizeof(ipc_msg_hdr);
+ return reply;
+ }
+
+
+static int deliver_error(request_state *rstate, mStatus err)
+ {
+ int nwritten = -1;
+ undelivered_error_t *undeliv;
+
+ nwritten = send(rstate->errfd, &err, sizeof(mStatus), 0);
+ if (nwritten < (int)sizeof(mStatus))
+ {
+ if (errno == EINTR || errno == EAGAIN)
+ nwritten = 0;
+ if (nwritten < 0)
+ {
+ my_perror("ERROR: send - unable to deliver error to client");
+ goto error;
+ }
+ //client blocked - store result and come backr
+ undeliv = mallocL("deliver_error", sizeof(undelivered_error_t));
+ if (!undeliv)
+ {
+ my_perror("ERROR: malloc");
+ exit(1);
+ }
+ undeliv->err = err;
+ undeliv->nwritten = nwritten;
+ undeliv->sd = rstate->errfd;
+ rstate->u_err = undeliv;
+ return 0;
+ }
+ if (rstate->errfd != rstate->sd) close(rstate->errfd);
+ return 0;
+
+error:
+ if (rstate->errfd != rstate->sd) close(rstate->errfd);
+ return -1;
+
+ }
+
+
+// returns 0 on success, -1 if send is incomplete, or on terminal failre (request is aborted)
+static transfer_state send_undelivered_error(request_state *rs)
+ {
+ int nwritten;
+
+ nwritten = send(rs->u_err->sd, (char *)(&rs->u_err) + rs->u_err->nwritten, sizeof(mStatus) - rs->u_err->nwritten, 0);
+ if (nwritten < 0)
+ {
+ if (errno == EINTR || errno == EAGAIN)
+ nwritten = 0;
+ else
+ {
+ my_perror("ERROR: send - unable to deliver error to client\n");
+ if (rs->u_err->sd == rs->sd) close (rs->u_err->sd);
+ return t_error;
+ }
+ }
+ if (nwritten + rs->u_err->nwritten == sizeof(mStatus))
+ {
+ if (rs->u_err->sd == rs->sd) close(rs->u_err->sd);
+ freeL("send_undelivered_error", rs->u_err);
+ rs->u_err = NULL;
+ return t_complete;
+ }
+ rs->u_err->nwritten += nwritten;
+ return t_morecoming;
+ }
+
+
+// send bogus data along with an error code to the app callback
+// returns 0 on success (linking reply into list of not fully delivered),
+// -1 on failure (request should be aborted)
+static int deliver_async_error(request_state *rs, reply_op_t op, mStatus err)
+ {
+ int len;
+ reply_state *reply;
+ transfer_state ts;
+
+ if (rs->no_reply) return 0;
+ len = 256; // long enough for any reply handler to read all args w/o buffer overrun
+ reply = create_reply(op, len, rs);
+ reply->rhdr->error = err;
+ ts = send_msg(reply);
+ if (ts == t_error || ts == t_terminated)
+ {
+ freeL("deliver_async_error", reply);
+ return -1;
+ }
+ else if (ts == t_complete) freeL("deliver_async_error", reply);
+ else if (ts == t_morecoming) append_reply(rs, reply); // client is blocked, link reply into list
+ return 0;
+ }
+
+
+static void abort_request(request_state *rs)
+ {
+ reply_state *rep, *ptr;
+
+ if (rs->terminate) rs->terminate(rs->termination_context); // terminate field may not be set yet
+ if (rs->msgbuf) freeL("abort_request", rs->msgbuf);
+ CFRunLoopRemoveSource(CFRunLoopGetCurrent(), rs->rls, kCFRunLoopDefaultMode);
+ CFRunLoopSourceInvalidate(rs->rls);
+ CFRelease(rs->rls);
+ CFSocketInvalidate(rs->sr);
+ CFRelease(rs->sr);
+ rs->sd = -1;
+ if (rs->errfd >= 0) close(rs->errfd);
+ rs->errfd = -1;
+
+ // free pending replies
+ rep = rs->replies;
+ while(rep)
+ {
+ if (rep->msgbuf) freeL("abort_request", rep->msgbuf);
+ ptr = rep;
+ rep = rep->next;
+ freeL("abort_request", ptr);
+ }
+
+ if (rs->u_err)
+ {
+ freeL("abort_request", rs->u_err);
+ rs->u_err = NULL;
+ }
+ }
+
+
+static void unlink_request(request_state *rs)
+ {
+ request_state *ptr;
+
+ if (rs == all_requests)
+ {
+ all_requests = all_requests->next;
+ freeL("unlink_request", rs);
+ return;
+ }
+ for(ptr = all_requests; ptr->next; ptr = ptr->next)
+ if (ptr->next == rs)
+ {
+ ptr->next = rs->next;
+ freeL("unlink_request", rs);
+ return;
+ }
+ }
+
+
+
+//hack to search-replace perror's to LogMsg's
+static void my_perror(char *errmsg)
+ {
+ LogMsg("%s: %s", errmsg, strerror(errno));
+ }
+
+
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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
*
* @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