summaryrefslogtreecommitdiffstats
path: root/mDNSResponder/mDNSShared
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2014-01-30 13:52:13 +0100
committerSebastian Huber <sebastian.huber@embedded-brains.de>2014-01-30 16:23:04 +0100
commit9449f151d0ccf3ac755d5f2bd9b4057ae2b03157 (patch)
treeada21dc6aa0b146c62a7561a08fb51fe4a8922ee /mDNSResponder/mDNSShared
parentDHCPCD(8): Add MASTER_ONLY option (diff)
downloadrtems-libbsd-9449f151d0ccf3ac755d5f2bd9b4057ae2b03157.tar.bz2
mDNS: Import
The sources can be obtained via: http://opensource.apple.com/tarballs/mDNSResponder/mDNSResponder-544.tar.gz
Diffstat (limited to 'mDNSResponder/mDNSShared')
-rw-r--r--mDNSResponder/mDNSShared/CommonServices.h1537
-rw-r--r--mDNSResponder/mDNSShared/DebugServices.c3075
-rw-r--r--mDNSResponder/mDNSShared/DebugServices.h1607
-rw-r--r--mDNSResponder/mDNSShared/GenLinkedList.c319
-rw-r--r--mDNSResponder/mDNSShared/GenLinkedList.h90
-rw-r--r--mDNSResponder/mDNSShared/Java/BaseListener.java36
-rw-r--r--mDNSResponder/mDNSShared/Java/BrowseListener.java73
-rw-r--r--mDNSResponder/mDNSShared/Java/DNSRecord.java52
-rw-r--r--mDNSResponder/mDNSShared/Java/DNSSD.java860
-rw-r--r--mDNSResponder/mDNSShared/Java/DNSSDException.java64
-rw-r--r--mDNSResponder/mDNSShared/Java/DNSSDRecordRegistrar.java64
-rw-r--r--mDNSResponder/mDNSShared/Java/DNSSDRegistration.java60
-rw-r--r--mDNSResponder/mDNSShared/Java/DNSSDService.java37
-rw-r--r--mDNSResponder/mDNSShared/Java/DomainListener.java60
-rw-r--r--mDNSResponder/mDNSShared/Java/JNISupport.c1072
-rw-r--r--mDNSResponder/mDNSShared/Java/QueryListener.java59
-rw-r--r--mDNSResponder/mDNSShared/Java/RegisterListener.java49
-rw-r--r--mDNSResponder/mDNSShared/Java/RegisterRecordListener.java37
-rw-r--r--mDNSResponder/mDNSShared/Java/ResolveListener.java56
-rw-r--r--mDNSResponder/mDNSShared/Java/TXTRecord.java290
-rw-r--r--mDNSResponder/mDNSShared/PlatformCommon.c199
-rw-r--r--mDNSResponder/mDNSShared/PlatformCommon.h18
-rw-r--r--mDNSResponder/mDNSShared/dns-sd.1266
-rw-r--r--mDNSResponder/mDNSShared/dns_sd.h2657
-rw-r--r--mDNSResponder/mDNSShared/dnsextd.869
-rw-r--r--mDNSResponder/mDNSShared/dnsextd.c3150
-rw-r--r--mDNSResponder/mDNSShared/dnsextd.conf60
-rw-r--r--mDNSResponder/mDNSShared/dnsextd.h163
-rw-r--r--mDNSResponder/mDNSShared/dnsextd_lexer.l84
-rw-r--r--mDNSResponder/mDNSShared/dnsextd_parser.y585
-rw-r--r--mDNSResponder/mDNSShared/dnssd_clientlib.c366
-rw-r--r--mDNSResponder/mDNSShared/dnssd_clientshim.c811
-rw-r--r--mDNSResponder/mDNSShared/dnssd_clientstub.c2363
-rw-r--r--mDNSResponder/mDNSShared/dnssd_ipc.c161
-rw-r--r--mDNSResponder/mDNSShared/dnssd_ipc.h221
-rw-r--r--mDNSResponder/mDNSShared/mDNSDebug.c95
-rw-r--r--mDNSResponder/mDNSShared/mDNSResponder.8116
-rw-r--r--mDNSResponder/mDNSShared/uds_daemon.c6103
-rw-r--r--mDNSResponder/mDNSShared/uds_daemon.h90
39 files changed, 27074 insertions, 0 deletions
diff --git a/mDNSResponder/mDNSShared/CommonServices.h b/mDNSResponder/mDNSShared/CommonServices.h
new file mode 100644
index 00000000..342479b9
--- /dev/null
+++ b/mDNSResponder/mDNSShared/CommonServices.h
@@ -0,0 +1,1537 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @header CommonServices
+
+ Common Services for Mac OS X, Linux, Palm, VxWorks, Windows, and Windows CE.
+ */
+
+#ifndef __COMMON_SERVICES__
+#define __COMMON_SERVICES__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if 0
+#pragma mark == Target ==
+#endif
+
+//===========================================================================================================================
+// Target
+//===========================================================================================================================
+
+// Macintosh
+
+#if ( !defined( TARGET_OS_MAC ) )
+ #if ( ( macintosh || __MACH__ ) && !KERNEL )
+// ConditionalMacros.h in CoreServices will define this TARGET_* flag.
+ #else
+ #define TARGET_OS_MAC 0
+ #endif
+#endif
+
+#if ( !defined( TARGET_API_MAC_OSX_KERNEL ) )
+ #if ( __MACH__ && KERNEL )
+ #define TARGET_API_MAC_OSX_KERNEL 1
+ #else
+ #define TARGET_API_MAC_OSX_KERNEL 0
+ #endif
+#endif
+
+// FreeBSD
+
+#if ( !defined( TARGET_OS_FREEBSD ) )
+ #if ( defined( __FreeBSD__ ) )
+ #define TARGET_OS_FREEBSD 1
+ #else
+ #define TARGET_OS_FREEBSD 0
+ #endif
+#endif
+
+// Linux
+
+#if ( !defined( TARGET_OS_LINUX ) )
+ #if ( defined( __linux__ ) )
+ #define TARGET_OS_LINUX 1
+ #else
+ #define TARGET_OS_LINUX 0
+ #endif
+#endif
+
+// Solaris
+
+#if ( !defined( TARGET_OS_SOLARIS ) )
+ #if ( defined(solaris) || (defined(__SVR4) && defined(sun)) )
+ #define TARGET_OS_SOLARIS 1
+ #else
+ #define TARGET_OS_SOLARIS 0
+ #endif
+#endif
+
+// Palm
+
+#if ( !defined( TARGET_OS_PALM ) )
+ #if ( defined( __PALMOS_TRAPS__ ) || defined( __PALMOS_ARMLET__ ) )
+ #define TARGET_OS_PALM 1
+ #else
+ #define TARGET_OS_PALM 0
+ #endif
+#endif
+
+// VxWorks
+
+#if ( !defined( TARGET_OS_VXWORKS ) )
+
+// No predefined macro for VxWorks so just assume VxWorks if nothing else is set.
+
+ #if ( !macintosh && !__MACH__ && !defined( __FreeBSD__ ) && !defined( __linux__ ) && !defined ( __SVR4 ) && !defined ( __sun ) && !defined( __PALMOS_TRAPS__ ) && !defined( __PALMOS_ARMLET__ ) && !defined( _WIN32 ) )
+ #define TARGET_OS_VXWORKS 1
+ #else
+ #define TARGET_OS_VXWORKS 0
+ #endif
+#endif
+
+// Windows
+
+#if ( !defined( TARGET_OS_WIN32 ) )
+ #if ( macintosh || __MACH__ )
+// ConditionalMacros.h in CoreServices will define this TARGET_* flag.
+ #else
+ #if ( defined( _WIN32 ) )
+ #define TARGET_OS_WIN32 1
+ #else
+ #define TARGET_OS_WIN32 0
+ #endif
+ #endif
+#endif
+
+// Windows CE
+
+#if ( !defined( TARGET_OS_WINDOWS_CE ) )
+ #if ( defined( _WIN32_WCE ) )
+ #define TARGET_OS_WINDOWS_CE 1
+ #else
+ #define TARGET_OS_WINDOWS_CE 0
+ #endif
+#endif
+
+#if 0
+#pragma mark == Includes ==
+#endif
+
+//===========================================================================================================================
+// Includes
+//===========================================================================================================================
+
+#if ( !KERNEL )
+ #if defined(WIN32) && !defined(_WSPIAPI_COUNTOF)
+ #define _WSPIAPI_COUNTOF(_Array) (sizeof(_Array) / sizeof(_Array[0]))
+ #endif
+ #include <stddef.h>
+#endif
+
+#if ( ( macintosh || __MACH__ ) && !KERNEL )
+
+ #if ( defined( __MWERKS__ ) )
+ #if ( __option( c9x ) )
+ #include <stdbool.h>
+ #endif
+ #else
+ #include <stdbool.h>
+ #endif
+
+ #include <stdint.h>
+
+ #if ( __MACH__ )
+
+// Mac OS X
+
+ #include <sys/types.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <fcntl.h>
+ #include <pthread.h>
+ #include <sys/ioctl.h>
+ #include <sys/socket.h>
+ #include <unistd.h>
+
+ #else
+
+// Classic Mac OS
+
+ #include <ConditionalMacros.h>
+ #include <MacTypes.h>
+
+ #endif
+
+#elif ( KERNEL )
+
+// Mac OS X Kernel
+
+ #include <stdint.h>
+
+ #include <libkern/OSTypes.h>
+ #include <sys/types.h>
+
+#elif ( TARGET_OS_FREEBSD )
+
+// FreeBSD
+ #include <stdint.h>
+ #include <pthread.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <sys/socket.h>
+
+#elif ( TARGET_OS_LINUX )
+
+// Linux
+
+ #include <stdint.h>
+ #include <arpa/inet.h>
+
+#elif ( TARGET_OS_SOLARIS )
+
+// Solaris
+
+ #include <stdint.h>
+
+ #include <arpa/inet.h>
+ #include <arpa/nameser.h>
+
+ #if ( defined( BYTE_ORDER ) && defined( LITTLE_ENDIAN ) && ( BYTE_ORDER == LITTLE_ENDIAN ) )
+ #define TARGET_RT_LITTLE_ENDIAN 1
+ #endif
+ #if ( defined( BYTE_ORDER ) && defined( BIG_ENDIAN ) && ( BYTE_ORDER == BIG_ENDIAN ) )
+ #define TARGET_RT_BIG_ENDIAN 1
+ #endif
+
+#elif ( TARGET_OS_PALM )
+
+// Palm (no special includes yet).
+
+#elif ( TARGET_OS_VXWORKS )
+
+// VxWorks
+
+ #include "vxWorks.h"
+
+#elif ( TARGET_OS_WIN32 )
+
+// Windows
+
+ #if ( !defined( WIN32_WINDOWS ) )
+ #define WIN32_WINDOWS 0x0401
+ #endif
+
+ #if ( !defined( _WIN32_WINDOWS ) )
+ #define _WIN32_WINDOWS 0x0401
+ #endif
+
+ #if ( !defined( WIN32_LEAN_AND_MEAN ) )
+ #define WIN32_LEAN_AND_MEAN // Needed to avoid redefinitions by Windows interfaces.
+ #endif
+
+ #if ( defined( __MWERKS__ ) )
+
+ #if ( __option( c9x ) )
+ #include <stdbool.h>
+ #endif
+
+ #include <stdint.h>
+
+ #elif ( defined( _MSC_VER ) )
+
+ #pragma warning( disable:4127 ) // Disable "conditional expression is constant" warning for debug macros.
+ #pragma warning( disable:4706 ) // Disable "assignment within conditional expression" for Microsoft headers.
+
+ #endif
+
+ #include <windows.h>
+ #include <winsock2.h>
+ #include <Ws2tcpip.h>
+
+ #if ( defined( _MSC_VER ) )
+ #pragma warning( default:4706 )
+ #endif
+
+#else
+ #error unknown OS - update this file to support your OS
+#endif
+
+#if ( !defined( TARGET_BUILD_MAIN ) )
+ #if ( !TARGET_OS_VXWORKS )
+ #define TARGET_BUILD_MAIN 1
+ #endif
+#endif
+
+#if ( __GNUC__ || !TARGET_OS_VXWORKS )
+ #define TARGET_LANGUAGE_C_LIKE 1
+#else
+ #define TARGET_LANGUAGE_C_LIKE 0
+#endif
+
+#if 0
+#pragma mark == CPU ==
+#endif
+
+//===========================================================================================================================
+// CPU
+//===========================================================================================================================
+
+// PowerPC
+
+#if ( !defined( TARGET_CPU_PPC ) )
+ #if ( defined( __ppc__ ) || defined( __PPC__ ) || defined( powerpc ) || defined( ppc ) || defined( _M_MPPC ) )
+ #define TARGET_CPU_PPC 1
+ #else
+ #define TARGET_CPU_PPC 0
+ #endif
+#endif
+
+// x86
+
+#if ( !defined( TARGET_CPU_X86 ) )
+ #if ( __INTEL__ || defined( __i386__ ) || defined( i386 ) || defined( intel ) || defined( _M_IX86 ) )
+ #define TARGET_CPU_X86 1
+ #else
+ #define TARGET_CPU_X86 0
+ #endif
+#endif
+
+// MIPS
+
+#if ( !defined( TARGET_CPU_MIPS ) )
+ #if ( __MIPS__ || defined( MIPS32 ) || defined( R3000 ) || defined( R4000 ) || defined( R4650 ) || defined( _M_MRX000 ) )
+ #define TARGET_CPU_MIPS 1
+ #else
+ #define TARGET_CPU_MIPS 0
+ #endif
+#endif
+
+#if ( !defined( TARGET_CPU_PPC ) && !defined( TARGET_CPU_X86 ) && !defined( TARGET_CPU_MIPS ) )
+ #error unknown CPU - update this file to support your CPU
+#endif
+
+#if 0
+#pragma mark == Byte Order ==
+#endif
+
+//===========================================================================================================================
+// Byte Order
+//===========================================================================================================================
+
+// TARGET_RT_LITTLE_ENDIAN
+
+#if ( !defined( TARGET_RT_LITTLE_ENDIAN ) )
+ #if ( MIPSEL || IL_LITTLE_ENDIAN || defined( __LITTLE_ENDIAN__ ) || \
+ ( defined( BYTE_ORDER ) && defined( LITTLE_ENDIAN ) && ( BYTE_ORDER == LITTLE_ENDIAN ) ) || \
+ ( defined( _BYTE_ORDER ) && defined( _LITTLE_ENDIAN ) && ( _BYTE_ORDER == _LITTLE_ENDIAN ) ) || \
+ ( defined( __BYTE_ORDER ) && defined( __LITTLE_ENDIAN ) && ( __BYTE_ORDER == __LITTLE_ENDIAN ) ) || \
+ TARGET_CPU_X86 || ( defined( TARGET_RT_BIG_ENDIAN ) && !TARGET_RT_BIG_ENDIAN ) )
+ #define TARGET_RT_LITTLE_ENDIAN 1
+ #else
+ #define TARGET_RT_LITTLE_ENDIAN 0
+ #endif
+#endif
+
+// TARGET_RT_BIG_ENDIAN
+
+#if ( !defined( TARGET_RT_BIG_ENDIAN ) )
+ #if ( MIPSEB || IL_BIG_ENDIAN || defined( __BIG_ENDIAN__ ) || \
+ ( defined( BYTE_ORDER ) && defined( BIG_ENDIAN ) && ( BYTE_ORDER == BIG_ENDIAN ) ) || \
+ ( defined( _BYTE_ORDER ) && defined( _BIG_ENDIAN ) && ( _BYTE_ORDER == _BIG_ENDIAN ) ) || \
+ ( defined( __BYTE_ORDER ) && defined( __BIG_ENDIAN ) && ( __BYTE_ORDER == __BIG_ENDIAN ) ) || \
+ ( defined( TARGET_RT_LITTLE_ENDIAN ) && !TARGET_RT_LITTLE_ENDIAN ) )
+ #define TARGET_RT_BIG_ENDIAN 1
+ #else
+ #define TARGET_RT_BIG_ENDIAN 0
+ #endif
+#endif
+
+#if ( defined( TARGET_RT_LITTLE_ENDIAN ) && !defined( TARGET_RT_BIG_ENDIAN ) )
+ #if ( TARGET_RT_LITTLE_ENDIAN )
+ #define TARGET_RT_BIG_ENDIAN 0
+ #else
+ #define TARGET_RT_BIG_ENDIAN 1
+ #endif
+#endif
+
+#if ( defined( TARGET_RT_BIG_ENDIAN ) && !defined( TARGET_RT_LITTLE_ENDIAN ) )
+ #if ( TARGET_RT_BIG_ENDIAN )
+ #define TARGET_RT_LITTLE_ENDIAN 0
+ #else
+ #define TARGET_RT_LITTLE_ENDIAN 1
+ #endif
+#endif
+
+#if ( !defined( TARGET_RT_LITTLE_ENDIAN ) || !defined( TARGET_RT_BIG_ENDIAN ) )
+ #error unknown byte order - update this file to support your byte order
+#endif
+
+// TARGET_RT_BYTE_ORDER
+
+#if ( !defined( TARGET_RT_BYTE_ORDER_BIG_ENDIAN ) )
+ #define TARGET_RT_BYTE_ORDER_BIG_ENDIAN 1234
+#endif
+
+#if ( !defined( TARGET_RT_BYTE_ORDER_LITTLE_ENDIAN ) )
+ #define TARGET_RT_BYTE_ORDER_LITTLE_ENDIAN 4321
+#endif
+
+#if ( !defined( TARGET_RT_BYTE_ORDER ) )
+ #if ( TARGET_RT_LITTLE_ENDIAN )
+ #define TARGET_RT_BYTE_ORDER TARGET_RT_BYTE_ORDER_LITTLE_ENDIAN
+ #else
+ #define TARGET_RT_BYTE_ORDER TARGET_RT_BYTE_ORDER_BIG_ENDIAN
+ #endif
+#endif
+
+#if 0
+#pragma mark == Constants ==
+#endif
+
+//===========================================================================================================================
+// Constants
+//===========================================================================================================================
+
+#if ( !TARGET_OS_MAC )
+ #define CR '\r'
+#endif
+
+#define LF '\n'
+#define CRSTR "\r"
+#define LFSTR "\n"
+#define CRLF "\r\n"
+#define CRCR "\r\r"
+
+#if 0
+#pragma mark == Compatibility ==
+#endif
+
+//===========================================================================================================================
+// Compatibility
+//===========================================================================================================================
+
+// Macros to allow the same code to work on Windows and other sockets API-compatible platforms.
+
+#if ( TARGET_OS_WIN32 )
+ #define close_compat( X ) closesocket( X )
+ #define errno_compat() (int) GetLastError()
+ #define set_errno_compat( X ) SetLastError( X )
+ #define EWOULDBLOCK_compat WSAEWOULDBLOCK
+ #define ETIMEDOUT_compat WSAETIMEDOUT
+ #define ENOTCONN_compat WSAENOTCONN
+ #define IsValidSocket( X ) ( ( X ) != INVALID_SOCKET )
+ #define kInvalidSocketRef INVALID_SOCKET
+ #if ( TARGET_LANGUAGE_C_LIKE )
+typedef SOCKET SocketRef;
+ #endif
+#else
+ #define close_compat( X ) close( X )
+ #define errno_compat() errno
+ #define set_errno_compat( X ) do { errno = ( X ); } while( 0 )
+ #define EWOULDBLOCK_compat EWOULDBLOCK
+ #define ETIMEDOUT_compat ETIMEDOUT
+ #define ENOTCONN_compat ENOTCONN
+ #define IsValidSocket( X ) ( ( X ) >= 0 )
+ #define kInvalidSocketRef -1
+ #if ( TARGET_LANGUAGE_C_LIKE )
+typedef int SocketRef;
+ #endif
+#endif
+
+// socklen_t is not defined on the following platforms so emulate it if not defined:
+//
+// - Pre-Panther Mac OS X. Panther defines SO_NOADDRERR so trigger off that.
+// - Windows SDK prior to 2003. 2003+ SDK's define EAI_AGAIN so trigger off that.
+// - VxWorks
+
+#if ( TARGET_LANGUAGE_C_LIKE )
+ #if ( ( TARGET_OS_MAC && !defined( SO_NOADDRERR ) ) || ( TARGET_OS_WIN32 && !defined( EAI_AGAIN ) ) || TARGET_OS_VXWORKS )
+typedef int socklen_t;
+ #endif
+#endif
+
+// ssize_t is not defined on the following platforms so emulate it if not defined:
+//
+// - Mac OS X when not building with BSD headers
+// - Windows
+
+#if ( TARGET_LANGUAGE_C_LIKE )
+ #if ( !defined(_SSIZE_T) && ( TARGET_OS_WIN32 || !defined( _BSD_SSIZE_T_DEFINED_ ) ) && !TARGET_OS_FREEBSD && !TARGET_OS_LINUX && !TARGET_OS_VXWORKS && !TARGET_OS_MAC)
+typedef int ssize_t;
+ #endif
+#endif
+
+// sockaddr_storage is not supported on non-IPv6 machines so alias it to an IPv4-compatible structure.
+
+#if ( TARGET_LANGUAGE_C_LIKE )
+ #if ( !defined( AF_INET6 ) )
+ #define sockaddr_storage sockaddr_in
+ #define ss_family sin_family
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined SOCKADDR_IS_IP_LOOPBACK
+
+ @abstract Determines if a sockaddr is an IPv4 or IPv6 loopback address (if IPv6 is supported).
+ */
+
+#if ( defined( AF_INET6 ) )
+ #define SOCKADDR_IS_IP_LOOPBACK( SA ) \
+ ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \
+ ? ( ( (const struct sockaddr_in *)( SA ) )->sin_addr.s_addr == htonl( INADDR_LOOPBACK ) ) \
+ : ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET6 ) \
+ ? IN6_IS_ADDR_LOOPBACK( &( (const struct sockaddr_in6 *)( SA ) )->sin6_addr ) \
+ : 0
+#else
+ #define SOCKADDR_IS_IP_LOOPBACK( SA ) \
+ ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \
+ ? ( ( (const struct sockaddr_in *)( SA ) )->sin_addr.s_addr == htonl( INADDR_LOOPBACK ) ) \
+ : 0
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined SOCKADDR_IS_IP_LINK_LOCAL
+
+ @abstract Determines if a sockaddr is an IPv4 or IPv6 link-local address (if IPv6 is supported).
+ */
+
+#if ( defined( AF_INET6 ) )
+ #define SOCKADDR_IS_IP_LINK_LOCAL( SA ) \
+ ( ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \
+ ? ( ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 0 ] == 169 ) && \
+ ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 1 ] == 254 ) ) \
+ : IN6_IS_ADDR_LOOPBACK( &( (const struct sockaddr_in6 *)( SA ) )->sin6_addr ) )
+#else
+ #define SOCKADDR_IS_IP_LINK_LOCAL( SA ) \
+ ( ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \
+ ? ( ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 0 ] == 169 ) && \
+ ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 1 ] == 254 ) ) \
+ : 0 )
+#endif
+
+// _beginthreadex and _endthreadex are not supported on Windows CE 2.1 or later (the C runtime issues with leaking
+// resources have apparently been resolved and they seem to have just ripped out support for the API) so map it to
+// CreateThread on Windows CE.
+
+#if ( TARGET_OS_WINDOWS_CE )
+ #define _beginthreadex_compat( SECURITY_PTR, STACK_SIZE, START_ADDRESS, ARG_LIST, FLAGS, THREAD_ID_PTR ) \
+ (uintptr_t) CreateThread( SECURITY_PTR, STACK_SIZE, (LPTHREAD_START_ROUTINE) START_ADDRESS, ARG_LIST, FLAGS, \
+ (LPDWORD) THREAD_ID_PTR )
+
+ #define _endthreadex_compat( RESULT ) ExitThread( (DWORD) RESULT )
+#elif ( TARGET_OS_WIN32 )
+ #define _beginthreadex_compat _beginthreadex
+ #define _endthreadex_compat _endthreadex
+#endif
+
+// The C99 "inline" keyword is not supported by Microsoft compilers, but they do support __inline so map it when needed.
+
+#if ( defined( _MSC_VER ) )
+ #define inline_compat __inline
+#else
+ #define inline_compat inline
+#endif
+
+// Calling conventions
+
+#if ( !defined( CALLBACK_COMPAT ) )
+ #if ( TARGET_OS_WIN32 || TARGET_OS_WINDOWS_CE )
+ #define CALLBACK_COMPAT CALLBACK
+ #else
+ #define CALLBACK_COMPAT
+ #endif
+#endif
+
+#if 0
+#pragma mark == Macros ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined kSizeCString
+
+ @abstract A meta-value to pass to supported routines to indicate the size should be calculated with strlen.
+ */
+
+#define kSizeCString ( (size_t) -1 )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined sizeof_array
+
+ @abstract Determines the number of elements in an array.
+ */
+
+#define sizeof_array( X ) ( sizeof( X ) / sizeof( X[ 0 ] ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined sizeof_element
+
+ @abstract Determines the size of an array element.
+ */
+
+#define sizeof_element( X ) sizeof( X[ 0 ] )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined sizeof_string
+
+ @abstract Determines the size of a constant C string, excluding the null terminator.
+ */
+
+#define sizeof_string( X ) ( sizeof( ( X ) ) - 1 )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined sizeof_field
+
+ @abstract Determines the size of a field of a type.
+ */
+
+#define sizeof_field( TYPE, FIELD ) sizeof( ( ( (TYPE *) 0 )->FIELD ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function RoundUp
+
+ @abstract Rounds X up to a multiple of Y.
+ */
+
+#define RoundUp( X, Y ) ( ( X ) + ( ( Y ) -( ( X ) % ( Y ) ) ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function IsAligned
+
+ @abstract Returns non-zero if X is aligned to a Y byte boundary and 0 if not. Y must be a power of 2.
+ */
+
+#define IsAligned( X, Y ) ( ( ( X ) &( ( Y ) -1 ) ) == 0 )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function IsFieldAligned
+
+ @abstract Returns non-zero if FIELD of type TYPE is aligned to a Y byte boundary and 0 if not. Y must be a power of 2.
+ */
+
+#define IsFieldAligned( X, TYPE, FIELD, Y ) IsAligned( ( (uintptr_t)( X ) ) + offsetof( TYPE, FIELD ), ( Y ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function AlignDown
+
+ @abstract Aligns X down to a Y byte boundary. Y must be a power of 2.
+ */
+
+#define AlignDown( X, Y ) ( ( X ) &~( ( Y ) -1 ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function AlignUp
+
+ @abstract Aligns X up to a Y byte boundary. Y must be a power of 2.
+ */
+
+#define AlignUp( X, Y ) ( ( ( X ) + ( ( Y ) -1 ) ) & ~( ( Y ) -1 ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function Min
+
+ @abstract Returns the lesser of X and Y.
+ */
+
+#if ( !defined( Min ) )
+ #define Min( X, Y ) ( ( ( X ) < ( Y ) ) ? ( X ) : ( Y ) )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function Max
+
+ @abstract Returns the greater of X and Y.
+ */
+
+#if ( !defined( Max ) )
+ #define Max( X, Y ) ( ( ( X ) > ( Y ) ) ? ( X ) : ( Y ) )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function InsertBits
+
+ @abstract Inserts BITS (both 0 and 1 bits) into X, controlled by MASK and SHIFT, and returns the result.
+
+ @discussion
+
+ MASK is the bitmask of the bits in the final position.
+ SHIFT is the number of bits to shift left for 1 to reach the first bit position of MASK.
+
+ For example, if you wanted to insert 0x3 into the leftmost 4 bits of a 32-bit value:
+
+ InsertBits( 0, 0x3, 0xF0000000U, 28 ) == 0x30000000
+ */
+
+#define InsertBits( X, BITS, MASK, SHIFT ) ( ( ( X ) &~( MASK ) ) | ( ( ( BITS ) << ( SHIFT ) ) & ( MASK ) ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function ExtractBits
+
+ @abstract Extracts bits from X, controlled by MASK and SHIFT, and returns the result.
+
+ @discussion
+
+ MASK is the bitmask of the bits in the final position.
+ SHIFT is the number of bits to shift right to right justify MASK.
+
+ For example, if you had a 32-bit value (e.g. 0x30000000) wanted the left-most 4 bits (e.g. 3 in this example):
+
+ ExtractBits( 0x30000000U, 0xF0000000U, 28 ) == 0x3
+ */
+
+#define ExtractBits( X, MASK, SHIFT ) ( ( ( X ) >> ( SHIFT ) ) & ( ( MASK ) >> ( SHIFT ) ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function Stringify
+
+ @abstract Stringify's an expression.
+
+ @discussion
+
+ Stringify macros to process raw text passed via -D options to C string constants. The double-wrapping is necessary
+ because the C preprocessor doesn't perform its normal argument expansion pre-scan with stringified macros so the
+ -D macro needs to be expanded once via the wrapper macro then stringified so the raw text is stringified. Otherwise,
+ the replacement value would be used instead of the symbolic name (only for preprocessor symbols like #defines).
+
+ For example:
+
+ #define kMyConstant 1
+
+ printf( "%s", Stringify( kMyConstant ) ); // Prints "kMyConstant"
+ printf( "%s", StringifyExpansion( kMyConstant ) ); // Prints "1"
+
+ Non-preprocessor symbols do not have this issue. For example:
+
+ enum
+ {
+ kMyConstant = 1
+ };
+
+ printf( "%s", Stringify( kMyConstant ) ); // Prints "kMyConstant"
+ printf( "%s", StringifyExpansion( kMyConstant ) ); // Prints "kMyConstant"
+
+ See <http://gcc.gnu.org/onlinedocs/cpp/Argument-Prescan.html> for more info on C preprocessor pre-scanning.
+ */
+
+#define Stringify( X ) # X
+#define StringifyExpansion( X ) Stringify( X )
+
+#if 0
+#pragma mark == Types ==
+#endif
+
+#if ( TARGET_LANGUAGE_C_LIKE )
+//===========================================================================================================================
+// Standard Types
+//===========================================================================================================================
+
+#if ( !defined( INT8_MIN ) )
+
+ #define INT8_MIN SCHAR_MIN
+
+ #if ( defined( _MSC_VER ) )
+
+// C99 stdint.h not supported in VC++/VS.NET yet.
+
+typedef INT8 int8_t;
+typedef UINT8 uint8_t;
+typedef INT16 int16_t;
+typedef UINT16 uint16_t;
+typedef INT32 int32_t;
+typedef UINT32 uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+
+ #elif ( TARGET_OS_VXWORKS && ( TORNADO_VERSION < 220 ) )
+typedef long long int64_t;
+typedef unsigned long long uint64_t;
+ #endif
+
+typedef int8_t int_least8_t;
+typedef int16_t int_least16_t;
+typedef int32_t int_least32_t;
+typedef int64_t int_least64_t;
+
+typedef uint8_t uint_least8_t;
+typedef uint16_t uint_least16_t;
+typedef uint32_t uint_least32_t;
+typedef uint64_t uint_least64_t;
+
+typedef int8_t int_fast8_t;
+typedef int16_t int_fast16_t;
+typedef int32_t int_fast32_t;
+typedef int64_t int_fast64_t;
+
+typedef uint8_t uint_fast8_t;
+typedef uint16_t uint_fast16_t;
+typedef uint32_t uint_fast32_t;
+typedef uint64_t uint_fast64_t;
+
+ #if ( !defined( _MSC_VER ) || TARGET_OS_WINDOWS_CE )
+typedef long int intptr_t;
+typedef unsigned long int uintptr_t;
+ #endif
+
+#endif
+
+// Macros for minimum-width integer constants
+
+#if ( !defined( INT8_C ) )
+ #define INT8_C( value ) value
+#endif
+
+#if ( !defined( INT16_C ) )
+ #define INT16_C( value ) value
+#endif
+
+#if ( !defined( INT32_C ) )
+ #define INT32_C( value ) value ## L
+#endif
+
+#if ( !defined( INT64_C ) )
+ #if ( defined( _MSC_VER ) )
+ #define INT64_C( value ) value ## i64
+ #else
+ #define INT64_C( value ) value ## LL
+ #endif
+#endif
+
+#if ( !defined( UINT8_C ) )
+ #define UINT8_C( value ) value ## U
+#endif
+
+#if ( !defined( UINT16_C ) )
+ #define UINT16_C( value ) value ## U
+#endif
+
+#if ( !defined( UINT32_C ) )
+ #define UINT32_C( value ) value ## UL
+#endif
+
+#if ( !defined( UINT64_C ) )
+ #if ( defined( _MSC_VER ) )
+ #define UINT64_C( value ) value ## UI64
+ #else
+ #define UINT64_C( value ) value ## ULL
+ #endif
+#endif
+
+#if 0
+#pragma mark == bool ==
+#endif
+
+//===========================================================================================================================
+// Boolean Constants and Types
+//===========================================================================================================================
+
+// C++ defines bool, true, and false. Metrowerks allows this to be controlled by the "bool" option though.
+// C99 defines __bool_true_false_are_defined when bool, true, and false are defined.
+// MacTypes.h defines true and false (Mac builds only).
+//
+// Note: The Metrowerks has to be in its own block because Microsoft Visual Studio .NET does not completely
+// short-circuit and gets confused by the option( bool ) portion of the conditional.
+
+#if ( defined( __MWERKS__ ) )
+
+// Note: The following test is done on separate lines because CodeWarrior doesn't like it all on one line.
+
+ #if ( !__bool_true_false_are_defined && ( !defined( __cplusplus ) || !__option( bool ) ) )
+ #define COMMON_SERVICES_NEEDS_BOOL 1
+ #else
+ #define COMMON_SERVICES_NEEDS_BOOL 0
+ #endif
+
+// Workaround when building with CodeWarrior, but using the Apple stdbool.h header, which uses _Bool.
+
+ #if ( __bool_true_false_are_defined && !defined( __cplusplus ) && !__option( c9x ) )
+ #define _Bool int
+ #endif
+
+// Workaround when building with CodeWarrior for C++ with bool disabled and using the Apple stdbool.h header,
+// which defines true and false to map to C++ true and false (which are not enabled). Serenity Now!
+
+ #if ( __bool_true_false_are_defined && defined( __cplusplus ) && !__option( bool ) )
+ #define true 1
+ #define false 0
+ #endif
+#else
+ #define COMMON_SERVICES_NEEDS_BOOL ( !defined( __cplusplus ) && !__bool_true_false_are_defined )
+#endif
+
+#if ( COMMON_SERVICES_NEEDS_BOOL )
+
+typedef int bool;
+
+ #define bool bool
+
+ #if ( !defined( __MACTYPES__ ) && !defined( true ) && !defined( false ) )
+ #define true 1
+ #define false 0
+ #endif
+
+ #define __bool_true_false_are_defined 1
+#endif
+
+// IOKit IOTypes.h typedef's bool if TYPE_BOOL is not defined so define it here to prevent redefinition by IOTypes.h.
+
+#if ( TARGET_API_MAC_OSX_KERNEL )
+ #define TYPE_BOOL 1
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef CStr255
+
+ @abstract 255 character null-terminated (C-style) string.
+ */
+
+#if ( TARGET_LANGUAGE_C_LIKE )
+typedef char CStr255[ 256 ];
+#endif
+
+#endif // TARGET_LANGUAGE_C_LIKE
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined TYPE_LONGLONG_NATIVE
+
+ @abstract Defines whether long long (or its equivalent) is natively supported or requires special libraries.
+ */
+
+#if ( !defined( TYPE_LONGLONG_NATIVE ) )
+ #if ( !TARGET_OS_VXWORKS )
+ #define TYPE_LONGLONG_NATIVE 1
+ #else
+ #define TYPE_LONGLONG_NATIVE 0
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined long_long_compat
+
+ @abstract Compatibility type to map to the closest thing to long long and unsigned long long.
+
+ @discussion
+
+ Neither long long nor unsigned long long are supported by Microsoft compilers, but they do support proprietary
+ "__int64" and "unsigned __int64" equivalents so map to those types if the real long long is not supported.
+ */
+
+#if ( TARGET_LANGUAGE_C_LIKE )
+ #if ( TARGET_OS_WIN32 )
+typedef __int64 long_long_compat;
+typedef unsigned __int64 unsigned_long_long_compat;
+ #else
+typedef signed long long long_long_compat;
+typedef unsigned long long unsigned_long_long_compat;
+ #endif
+#endif
+
+#if 0
+#pragma mark == Errors ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @enum OSStatus
+
+ @abstract Status Code
+
+ @constant kNoErr 0 No error occurred.
+ @constant kInProgressErr 1 Operation in progress.
+ @constant kUnknownErr -6700 Unknown error occurred.
+ @constant kOptionErr -6701 Option was not acceptable.
+ @constant kSelectorErr -6702 Selector passed in is invalid or unknown.
+ @constant kExecutionStateErr -6703 Call made in the wrong execution state (e.g. called at interrupt time).
+ @constant kPathErr -6704 Path is invalid, too long, or otherwise not usable.
+ @constant kParamErr -6705 Parameter is incorrect, missing, or not appropriate.
+ @constant kParamCountErr -6706 Incorrect or unsupported number of parameters.
+ @constant kCommandErr -6707 Command invalid or not supported.
+ @constant kIDErr -6708 Unknown, invalid, or inappropriate identifier.
+ @constant kStateErr -6709 Not in appropriate state to perform operation.
+ @constant kRangeErr -6710 Index is out of range or not valid.
+ @constant kRequestErr -6711 Request was improperly formed or not appropriate.
+ @constant kResponseErr -6712 Response was incorrect or out of sequence.
+ @constant kChecksumErr -6713 Checksum does not match the actual data.
+ @constant kNotHandledErr -6714 Operation was not handled (or not handled completely).
+ @constant kVersionErr -6715 Version is not incorrect or not compatibile.
+ @constant kSignatureErr -6716 Signature did not match what was expected.
+ @constant kFormatErr -6717 Unknown, invalid, or inappropriate file/data format.
+ @constant kNotInitializedErr -6718 Action request before needed services were initialized.
+ @constant kAlreadyInitializedErr -6719 Attempt made to initialize when already initialized.
+ @constant kNotInUseErr -6720 Object not in use (e.g. cannot abort if not already in use).
+ @constant kInUseErr -6721 Object is in use (e.g. cannot reuse active param blocks).
+ @constant kTimeoutErr -6722 Timeout occurred.
+ @constant kCanceledErr -6723 Operation canceled (successful cancel).
+ @constant kAlreadyCanceledErr -6724 Operation has already been canceled.
+ @constant kCannotCancelErr -6725 Operation could not be canceled (maybe already done or invalid).
+ @constant kDeletedErr -6726 Object has already been deleted.
+ @constant kNotFoundErr -6727 Something was not found.
+ @constant kNoMemoryErr -6728 Not enough memory was available to perform the operation.
+ @constant kNoResourcesErr -6729 Resources unavailable to perform the operation.
+ @constant kDuplicateErr -6730 Duplicate found or something is a duplicate.
+ @constant kImmutableErr -6731 Entity is not changeable.
+ @constant kUnsupportedDataErr -6732 Data is unknown or not supported.
+ @constant kIntegrityErr -6733 Data is corrupt.
+ @constant kIncompatibleErr -6734 Data is not compatible or it is in an incompatible format.
+ @constant kUnsupportedErr -6735 Feature or option is not supported.
+ @constant kUnexpectedErr -6736 Error occurred that was not expected.
+ @constant kValueErr -6737 Value is not appropriate.
+ @constant kNotReadableErr -6738 Could not read or reading is not allowed.
+ @constant kNotWritableErr -6739 Could not write or writing is not allowed.
+ @constant kBadReferenceErr -6740 An invalid or inappropriate reference was specified.
+ @constant kFlagErr -6741 An invalid, inappropriate, or unsupported flag was specified.
+ @constant kMalformedErr -6742 Something was not formed correctly.
+ @constant kSizeErr -6743 Size was too big, too small, or not appropriate.
+ @constant kNameErr -6744 Name was not correct, allowed, or appropriate.
+ @constant kNotReadyErr -6745 Device or service is not ready.
+ @constant kReadErr -6746 Could not read.
+ @constant kWriteErr -6747 Could not write.
+ @constant kMismatchErr -6748 Something does not match.
+ @constant kDateErr -6749 Date is invalid or out-of-range.
+ @constant kUnderrunErr -6750 Less data than expected.
+ @constant kOverrunErr -6751 More data than expected.
+ @constant kEndingErr -6752 Connection, session, or something is ending.
+ @constant kConnectionErr -6753 Connection failed or could not be established.
+ @constant kAuthenticationErr -6754 Authentication failed or is not supported.
+ @constant kOpenErr -6755 Could not open file, pipe, device, etc.
+ @constant kTypeErr -6756 Incorrect or incompatible type (e.g. file, data, etc.).
+ @constant kSkipErr -6757 Items should be or was skipped.
+ @constant kNoAckErr -6758 No acknowledge.
+ @constant kCollisionErr -6759 Collision occurred (e.g. two on bus at same time).
+ @constant kBackoffErr -6760 Backoff in progress and operation intentionally failed.
+ @constant kNoAddressAckErr -6761 No acknowledge of address.
+ @constant kBusyErr -6762 Cannot perform because something is busy.
+ @constant kNoSpaceErr -6763 Not enough space to perform operation.
+ */
+
+#if ( TARGET_LANGUAGE_C_LIKE )
+ #if ( !TARGET_OS_MAC && !TARGET_API_MAC_OSX_KERNEL )
+typedef int32_t OSStatus;
+ #endif
+#endif
+
+#define kNoErr 0
+#define kInProgressErr 1
+
+// Generic error codes are in the range -6700 to -6779.
+
+#define kGenericErrorBase -6700 // Starting error code for all generic errors.
+
+#define kUnknownErr -6700
+#define kOptionErr -6701
+#define kSelectorErr -6702
+#define kExecutionStateErr -6703
+#define kPathErr -6704
+#define kParamErr -6705
+#define kParamCountErr -6706
+#define kCommandErr -6707
+#define kIDErr -6708
+#define kStateErr -6709
+#define kRangeErr -6710
+#define kRequestErr -6711
+#define kResponseErr -6712
+#define kChecksumErr -6713
+#define kNotHandledErr -6714
+#define kVersionErr -6715
+#define kSignatureErr -6716
+#define kFormatErr -6717
+#define kNotInitializedErr -6718
+#define kAlreadyInitializedErr -6719
+#define kNotInUseErr -6720
+#define kInUseErr -6721
+#define kTimeoutErr -6722
+#define kCanceledErr -6723
+#define kAlreadyCanceledErr -6724
+#define kCannotCancelErr -6725
+#define kDeletedErr -6726
+#define kNotFoundErr -6727
+#define kNoMemoryErr -6728
+#define kNoResourcesErr -6729
+#define kDuplicateErr -6730
+#define kImmutableErr -6731
+#define kUnsupportedDataErr -6732
+#define kIntegrityErr -6733
+#define kIncompatibleErr -6734
+#define kUnsupportedErr -6735
+#define kUnexpectedErr -6736
+#define kValueErr -6737
+#define kNotReadableErr -6738
+#define kNotWritableErr -6739
+#define kBadReferenceErr -6740
+#define kFlagErr -6741
+#define kMalformedErr -6742
+#define kSizeErr -6743
+#define kNameErr -6744
+#define kNotReadyErr -6745
+#define kReadErr -6746
+#define kWriteErr -6747
+#define kMismatchErr -6748
+#define kDateErr -6749
+#define kUnderrunErr -6750
+#define kOverrunErr -6751
+#define kEndingErr -6752
+#define kConnectionErr -6753
+#define kAuthenticationErr -6754
+#define kOpenErr -6755
+#define kTypeErr -6756
+#define kSkipErr -6757
+#define kNoAckErr -6758
+#define kCollisionErr -6759
+#define kBackoffErr -6760
+#define kNoAddressAckErr -6761
+#define kBusyErr -6762
+#define kNoSpaceErr -6763
+
+#define kGenericErrorEnd -6779 // Last generic error code (inclusive)
+
+#if 0
+#pragma mark == Mac Compatibility ==
+#endif
+
+//===========================================================================================================================
+// Mac Compatibility
+//===========================================================================================================================
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @enum Duration
+
+ @abstract Type used to specify a duration of time.
+
+ @constant kDurationImmediate Indicates no delay/wait time.
+ @constant kDurationMicrosecond Microsecond units.
+ @constant kDurationMillisecond Millisecond units.
+ @constant kDurationSecond Second units.
+ @constant kDurationMinute Minute units.
+ @constant kDurationHour Hour units.
+ @constant kDurationDay Day units.
+ @constant kDurationForever Infinite period of time (no timeout).
+
+ @discussion
+
+ Duration values are intended to be multiplied by the specific interval to achieve an actual duration. For example,
+ to wait for 5 seconds you would use "5 * kDurationSecond".
+ */
+
+#if ( TARGET_LANGUAGE_C_LIKE )
+ #if ( !TARGET_OS_MAC )
+typedef int32_t Duration;
+ #endif
+#endif
+
+#define kDurationImmediate 0L
+#define kDurationMicrosecond -1L
+#define kDurationMillisecond 1L
+#define kDurationSecond ( 1000L * kDurationMillisecond )
+#define kDurationMinute ( 60L * kDurationSecond )
+#define kDurationHour ( 60L * kDurationMinute )
+#define kDurationDay ( 24L * kDurationHour )
+#define kDurationForever 0x7FFFFFFFL
+
+// Seconds <-> Minutes <-> Hours <-> Days <-> Weeks <-> Months <-> Years conversions
+
+#define kNanosecondsPerMicrosecond 1000
+#define kNanosecondsPerMillisecond 1000000
+#define kNanosecondsPerSecond 1000000000
+#define kMicrosecondsPerSecond 1000000
+#define kMicrosecondsPerMillisecond 1000
+#define kMillisecondsPerSecond 1000
+#define kSecondsPerMinute 60
+#define kSecondsPerHour ( 60 * 60 ) // 3600
+#define kSecondsPerDay ( 60 * 60 * 24 ) // 86400
+#define kSecondsPerWeek ( 60 * 60 * 24 * 7 ) // 604800
+#define kMinutesPerHour 60
+#define kMinutesPerDay ( 60 * 24 ) // 1440
+#define kHoursPerDay 24
+#define kDaysPerWeek 7
+#define kWeeksPerYear 52
+#define kMonthsPerYear 12
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined VersionStages
+
+ @abstract NumVersion-style version stages.
+ */
+
+#define kVersionStageDevelopment 0x20
+#define kVersionStageAlpha 0x40
+#define kVersionStageBeta 0x60
+#define kVersionStageFinal 0x80
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function NumVersionBuild
+
+ @abstract Builds a 32-bit Mac-style NumVersion value (e.g. NumVersionBuild( 1, 2, 3, kVersionStageBeta, 4 ) -> 1.2.3b4).
+ */
+
+#define NumVersionBuild( MAJOR, MINOR, BUGFIX, STAGE, REV ) \
+ ( ( ( ( MAJOR ) & 0xFF ) << 24 ) | \
+ ( ( ( MINOR ) & 0x0F ) << 20 ) | \
+ ( ( ( BUGFIX ) & 0x0F ) << 16 ) | \
+ ( ( ( STAGE ) & 0xFF ) << 8 ) | \
+ ( ( ( REV ) & 0xFF ) ) )
+
+#define NumVersionExtractMajor( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 24 ) & 0xFF ) )
+#define NumVersionExtractMinorAndBugFix( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 16 ) & 0xFF ) )
+#define NumVersionExtractMinor( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 20 ) & 0x0F ) )
+#define NumVersionExtractBugFix( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 16 ) & 0x0F ) )
+#define NumVersionExtractStage( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 8 ) & 0xFF ) )
+#define NumVersionExtractRevision( VERSION ) ( (uint8_t)( ( VERSION ) & 0xFF ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function NumVersionCompare
+
+ @abstract Compares two NumVersion values and returns the following values:
+
+ left < right -> -1
+ left > right -> 1
+ left = right -> 0
+ */
+
+#if ( TARGET_LANGUAGE_C_LIKE )
+int NumVersionCompare( uint32_t inLeft, uint32_t inRight );
+#endif
+
+#if 0
+#pragma mark == Binary Constants ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined binary_4
+
+ @abstract Macro to generate an 4-bit constant using binary notation (e.g. binary_4( 1010 ) == 0xA).
+ */
+
+#define binary_4( a ) binary_4_hex_wrap( hex_digit4( a ) )
+#define binary_4_hex_wrap( a ) binary_4_hex( a )
+#define binary_4_hex( a ) ( 0x ## a )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined binary_8
+
+ @abstract Macro to generate an 8-bit constant using binary notation (e.g. binary_8( 01111011 ) == 0x7B).
+ */
+
+#define binary_8( a ) binary_8_hex_wrap( hex_digit8( a ) )
+#define binary_8_hex_wrap( a ) binary_8_hex( a )
+#define binary_8_hex( a ) ( 0x ## a )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined binary_16
+
+ @abstract Macro to generate an 16-bit constant using binary notation (e.g. binary_16( 01111011, 01111011 ) == 0x7B7B).
+ */
+
+#define binary_16( a, b ) binary_16_hex_wrap( hex_digit8( a ), hex_digit8( b ) )
+#define binary_16_hex_wrap( a, b ) binary_16_hex( a, b )
+#define binary_16_hex( a, b ) ( 0x ## a ## b )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined binary_32
+
+ @abstract Macro to generate an 32-bit constant using binary notation
+ (e.g. binary_32( 01111011, 01111011, 01111011, 01111011 ) == 0x7B7B7B7B).
+ */
+
+#define binary_32( a, b, c, d ) binary_32_hex_wrap( hex_digit8( a ), hex_digit8( b ), hex_digit8( c ), hex_digit8( d ) )
+#define binary_32_hex_wrap( a, b, c, d ) binary_32_hex( a, b, c, d )
+#define binary_32_hex( a, b, c, d ) ( 0x ## a ## b ## c ## d )
+
+// Binary Constant Helpers
+
+#define hex_digit8( a ) HEX_DIGIT_ ## a
+#define hex_digit4( a ) HEX_DIGIT_ ## 0000 ## a
+
+#define HEX_DIGIT_00000000 00
+#define HEX_DIGIT_00000001 01
+#define HEX_DIGIT_00000010 02
+#define HEX_DIGIT_00000011 03
+#define HEX_DIGIT_00000100 04
+#define HEX_DIGIT_00000101 05
+#define HEX_DIGIT_00000110 06
+#define HEX_DIGIT_00000111 07
+#define HEX_DIGIT_00001000 08
+#define HEX_DIGIT_00001001 09
+#define HEX_DIGIT_00001010 0A
+#define HEX_DIGIT_00001011 0B
+#define HEX_DIGIT_00001100 0C
+#define HEX_DIGIT_00001101 0D
+#define HEX_DIGIT_00001110 0E
+#define HEX_DIGIT_00001111 0F
+#define HEX_DIGIT_00010000 10
+#define HEX_DIGIT_00010001 11
+#define HEX_DIGIT_00010010 12
+#define HEX_DIGIT_00010011 13
+#define HEX_DIGIT_00010100 14
+#define HEX_DIGIT_00010101 15
+#define HEX_DIGIT_00010110 16
+#define HEX_DIGIT_00010111 17
+#define HEX_DIGIT_00011000 18
+#define HEX_DIGIT_00011001 19
+#define HEX_DIGIT_00011010 1A
+#define HEX_DIGIT_00011011 1B
+#define HEX_DIGIT_00011100 1C
+#define HEX_DIGIT_00011101 1D
+#define HEX_DIGIT_00011110 1E
+#define HEX_DIGIT_00011111 1F
+#define HEX_DIGIT_00100000 20
+#define HEX_DIGIT_00100001 21
+#define HEX_DIGIT_00100010 22
+#define HEX_DIGIT_00100011 23
+#define HEX_DIGIT_00100100 24
+#define HEX_DIGIT_00100101 25
+#define HEX_DIGIT_00100110 26
+#define HEX_DIGIT_00100111 27
+#define HEX_DIGIT_00101000 28
+#define HEX_DIGIT_00101001 29
+#define HEX_DIGIT_00101010 2A
+#define HEX_DIGIT_00101011 2B
+#define HEX_DIGIT_00101100 2C
+#define HEX_DIGIT_00101101 2D
+#define HEX_DIGIT_00101110 2E
+#define HEX_DIGIT_00101111 2F
+#define HEX_DIGIT_00110000 30
+#define HEX_DIGIT_00110001 31
+#define HEX_DIGIT_00110010 32
+#define HEX_DIGIT_00110011 33
+#define HEX_DIGIT_00110100 34
+#define HEX_DIGIT_00110101 35
+#define HEX_DIGIT_00110110 36
+#define HEX_DIGIT_00110111 37
+#define HEX_DIGIT_00111000 38
+#define HEX_DIGIT_00111001 39
+#define HEX_DIGIT_00111010 3A
+#define HEX_DIGIT_00111011 3B
+#define HEX_DIGIT_00111100 3C
+#define HEX_DIGIT_00111101 3D
+#define HEX_DIGIT_00111110 3E
+#define HEX_DIGIT_00111111 3F
+#define HEX_DIGIT_01000000 40
+#define HEX_DIGIT_01000001 41
+#define HEX_DIGIT_01000010 42
+#define HEX_DIGIT_01000011 43
+#define HEX_DIGIT_01000100 44
+#define HEX_DIGIT_01000101 45
+#define HEX_DIGIT_01000110 46
+#define HEX_DIGIT_01000111 47
+#define HEX_DIGIT_01001000 48
+#define HEX_DIGIT_01001001 49
+#define HEX_DIGIT_01001010 4A
+#define HEX_DIGIT_01001011 4B
+#define HEX_DIGIT_01001100 4C
+#define HEX_DIGIT_01001101 4D
+#define HEX_DIGIT_01001110 4E
+#define HEX_DIGIT_01001111 4F
+#define HEX_DIGIT_01010000 50
+#define HEX_DIGIT_01010001 51
+#define HEX_DIGIT_01010010 52
+#define HEX_DIGIT_01010011 53
+#define HEX_DIGIT_01010100 54
+#define HEX_DIGIT_01010101 55
+#define HEX_DIGIT_01010110 56
+#define HEX_DIGIT_01010111 57
+#define HEX_DIGIT_01011000 58
+#define HEX_DIGIT_01011001 59
+#define HEX_DIGIT_01011010 5A
+#define HEX_DIGIT_01011011 5B
+#define HEX_DIGIT_01011100 5C
+#define HEX_DIGIT_01011101 5D
+#define HEX_DIGIT_01011110 5E
+#define HEX_DIGIT_01011111 5F
+#define HEX_DIGIT_01100000 60
+#define HEX_DIGIT_01100001 61
+#define HEX_DIGIT_01100010 62
+#define HEX_DIGIT_01100011 63
+#define HEX_DIGIT_01100100 64
+#define HEX_DIGIT_01100101 65
+#define HEX_DIGIT_01100110 66
+#define HEX_DIGIT_01100111 67
+#define HEX_DIGIT_01101000 68
+#define HEX_DIGIT_01101001 69
+#define HEX_DIGIT_01101010 6A
+#define HEX_DIGIT_01101011 6B
+#define HEX_DIGIT_01101100 6C
+#define HEX_DIGIT_01101101 6D
+#define HEX_DIGIT_01101110 6E
+#define HEX_DIGIT_01101111 6F
+#define HEX_DIGIT_01110000 70
+#define HEX_DIGIT_01110001 71
+#define HEX_DIGIT_01110010 72
+#define HEX_DIGIT_01110011 73
+#define HEX_DIGIT_01110100 74
+#define HEX_DIGIT_01110101 75
+#define HEX_DIGIT_01110110 76
+#define HEX_DIGIT_01110111 77
+#define HEX_DIGIT_01111000 78
+#define HEX_DIGIT_01111001 79
+#define HEX_DIGIT_01111010 7A
+#define HEX_DIGIT_01111011 7B
+#define HEX_DIGIT_01111100 7C
+#define HEX_DIGIT_01111101 7D
+#define HEX_DIGIT_01111110 7E
+#define HEX_DIGIT_01111111 7F
+#define HEX_DIGIT_10000000 80
+#define HEX_DIGIT_10000001 81
+#define HEX_DIGIT_10000010 82
+#define HEX_DIGIT_10000011 83
+#define HEX_DIGIT_10000100 84
+#define HEX_DIGIT_10000101 85
+#define HEX_DIGIT_10000110 86
+#define HEX_DIGIT_10000111 87
+#define HEX_DIGIT_10001000 88
+#define HEX_DIGIT_10001001 89
+#define HEX_DIGIT_10001010 8A
+#define HEX_DIGIT_10001011 8B
+#define HEX_DIGIT_10001100 8C
+#define HEX_DIGIT_10001101 8D
+#define HEX_DIGIT_10001110 8E
+#define HEX_DIGIT_10001111 8F
+#define HEX_DIGIT_10010000 90
+#define HEX_DIGIT_10010001 91
+#define HEX_DIGIT_10010010 92
+#define HEX_DIGIT_10010011 93
+#define HEX_DIGIT_10010100 94
+#define HEX_DIGIT_10010101 95
+#define HEX_DIGIT_10010110 96
+#define HEX_DIGIT_10010111 97
+#define HEX_DIGIT_10011000 98
+#define HEX_DIGIT_10011001 99
+#define HEX_DIGIT_10011010 9A
+#define HEX_DIGIT_10011011 9B
+#define HEX_DIGIT_10011100 9C
+#define HEX_DIGIT_10011101 9D
+#define HEX_DIGIT_10011110 9E
+#define HEX_DIGIT_10011111 9F
+#define HEX_DIGIT_10100000 A0
+#define HEX_DIGIT_10100001 A1
+#define HEX_DIGIT_10100010 A2
+#define HEX_DIGIT_10100011 A3
+#define HEX_DIGIT_10100100 A4
+#define HEX_DIGIT_10100101 A5
+#define HEX_DIGIT_10100110 A6
+#define HEX_DIGIT_10100111 A7
+#define HEX_DIGIT_10101000 A8
+#define HEX_DIGIT_10101001 A9
+#define HEX_DIGIT_10101010 AA
+#define HEX_DIGIT_10101011 AB
+#define HEX_DIGIT_10101100 AC
+#define HEX_DIGIT_10101101 AD
+#define HEX_DIGIT_10101110 AE
+#define HEX_DIGIT_10101111 AF
+#define HEX_DIGIT_10110000 B0
+#define HEX_DIGIT_10110001 B1
+#define HEX_DIGIT_10110010 B2
+#define HEX_DIGIT_10110011 B3
+#define HEX_DIGIT_10110100 B4
+#define HEX_DIGIT_10110101 B5
+#define HEX_DIGIT_10110110 B6
+#define HEX_DIGIT_10110111 B7
+#define HEX_DIGIT_10111000 B8
+#define HEX_DIGIT_10111001 B9
+#define HEX_DIGIT_10111010 BA
+#define HEX_DIGIT_10111011 BB
+#define HEX_DIGIT_10111100 BC
+#define HEX_DIGIT_10111101 BD
+#define HEX_DIGIT_10111110 BE
+#define HEX_DIGIT_10111111 BF
+#define HEX_DIGIT_11000000 C0
+#define HEX_DIGIT_11000001 C1
+#define HEX_DIGIT_11000010 C2
+#define HEX_DIGIT_11000011 C3
+#define HEX_DIGIT_11000100 C4
+#define HEX_DIGIT_11000101 C5
+#define HEX_DIGIT_11000110 C6
+#define HEX_DIGIT_11000111 C7
+#define HEX_DIGIT_11001000 C8
+#define HEX_DIGIT_11001001 C9
+#define HEX_DIGIT_11001010 CA
+#define HEX_DIGIT_11001011 CB
+#define HEX_DIGIT_11001100 CC
+#define HEX_DIGIT_11001101 CD
+#define HEX_DIGIT_11001110 CE
+#define HEX_DIGIT_11001111 CF
+#define HEX_DIGIT_11010000 D0
+#define HEX_DIGIT_11010001 D1
+#define HEX_DIGIT_11010010 D2
+#define HEX_DIGIT_11010011 D3
+#define HEX_DIGIT_11010100 D4
+#define HEX_DIGIT_11010101 D5
+#define HEX_DIGIT_11010110 D6
+#define HEX_DIGIT_11010111 D7
+#define HEX_DIGIT_11011000 D8
+#define HEX_DIGIT_11011001 D9
+#define HEX_DIGIT_11011010 DA
+#define HEX_DIGIT_11011011 DB
+#define HEX_DIGIT_11011100 DC
+#define HEX_DIGIT_11011101 DD
+#define HEX_DIGIT_11011110 DE
+#define HEX_DIGIT_11011111 DF
+#define HEX_DIGIT_11100000 E0
+#define HEX_DIGIT_11100001 E1
+#define HEX_DIGIT_11100010 E2
+#define HEX_DIGIT_11100011 E3
+#define HEX_DIGIT_11100100 E4
+#define HEX_DIGIT_11100101 E5
+#define HEX_DIGIT_11100110 E6
+#define HEX_DIGIT_11100111 E7
+#define HEX_DIGIT_11101000 E8
+#define HEX_DIGIT_11101001 E9
+#define HEX_DIGIT_11101010 EA
+#define HEX_DIGIT_11101011 EB
+#define HEX_DIGIT_11101100 EC
+#define HEX_DIGIT_11101101 ED
+#define HEX_DIGIT_11101110 EE
+#define HEX_DIGIT_11101111 EF
+#define HEX_DIGIT_11110000 F0
+#define HEX_DIGIT_11110001 F1
+#define HEX_DIGIT_11110010 F2
+#define HEX_DIGIT_11110011 F3
+#define HEX_DIGIT_11110100 F4
+#define HEX_DIGIT_11110101 F5
+#define HEX_DIGIT_11110110 F6
+#define HEX_DIGIT_11110111 F7
+#define HEX_DIGIT_11111000 F8
+#define HEX_DIGIT_11111001 F9
+#define HEX_DIGIT_11111010 FA
+#define HEX_DIGIT_11111011 FB
+#define HEX_DIGIT_11111100 FC
+#define HEX_DIGIT_11111101 FD
+#define HEX_DIGIT_11111110 FE
+#define HEX_DIGIT_11111111 FF
+
+#if 0
+#pragma mark == Debugging ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function CommonServicesTest
+
+ @abstract Unit test.
+ */
+
+#if ( DEBUG )
+ #if ( TARGET_LANGUAGE_C_LIKE )
+OSStatus CommonServicesTest( void );
+ #endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __COMMON_SERVICES__
diff --git a/mDNSResponder/mDNSShared/DebugServices.c b/mDNSResponder/mDNSShared/DebugServices.c
new file mode 100644
index 00000000..98f876a4
--- /dev/null
+++ b/mDNSResponder/mDNSShared/DebugServices.c
@@ -0,0 +1,3075 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+
+ To Do:
+
+ - Use StackWalk on Windows to optionally print stack frames.
+ */
+
+#if 0
+#pragma mark == Includes ==
+#endif
+
+//===========================================================================================================================
+// Includes
+//===========================================================================================================================
+
+#if ( !KERNEL )
+ #include <ctype.h>
+ #include <stdio.h>
+ #include <string.h>
+#endif
+
+#include "CommonServices.h"
+
+#include "DebugServices.h"
+
+#if ( DEBUG )
+
+#if ( TARGET_OS_VXWORKS )
+ #include "intLib.h"
+#endif
+
+#if ( TARGET_OS_WIN32 )
+ #include <time.h>
+
+ #if ( !TARGET_OS_WINDOWS_CE )
+ #include <fcntl.h>
+ #include <io.h>
+ #endif
+#endif
+
+#if ( DEBUG_IDEBUG_ENABLED && TARGET_API_MAC_OSX_KERNEL )
+ #include <IOKit/IOLib.h>
+#endif
+
+// If MDNS_DEBUGMSGS is defined (even if defined 0), it is aware of mDNS and it is probably safe to include mDNSEmbeddedAPI.h.
+
+#if ( defined( MDNS_DEBUGMSGS ) )
+ #include "mDNSEmbeddedAPI.h"
+#endif
+
+#if 0
+#pragma mark == Macros ==
+#endif
+
+//===========================================================================================================================
+// Macros
+//===========================================================================================================================
+
+#define DebugIsPrint( C ) ( ( ( C ) >= 0x20 ) && ( ( C ) <= 0x7E ) )
+
+#if 0
+#pragma mark == Prototypes ==
+#endif
+
+//===========================================================================================================================
+// Prototypes
+//===========================================================================================================================
+
+static OSStatus DebugPrint( DebugLevel inLevel, char *inData, size_t inSize );
+
+// fprintf
+
+#if ( DEBUG_FPRINTF_ENABLED )
+static OSStatus DebugFPrintFInit( DebugOutputTypeFlags inFlags, const char *inFilename );
+static void DebugFPrintFPrint( char *inData, size_t inSize );
+#endif
+
+// iDebug (Mac OS X user and kernel)
+
+#if ( DEBUG_IDEBUG_ENABLED )
+static OSStatus DebugiDebugInit( void );
+static void DebugiDebugPrint( char *inData, size_t inSize );
+#endif
+
+// kprintf (Mac OS X Kernel)
+
+#if ( DEBUG_KPRINTF_ENABLED )
+static void DebugKPrintFPrint( char *inData, size_t inSize );
+#endif
+
+// Mac OS X IOLog (Mac OS X Kernel)
+
+#if ( DEBUG_MAC_OS_X_IOLOG_ENABLED )
+static void DebugMacOSXIOLogPrint( char *inData, size_t inSize );
+#endif
+
+// Mac OS X Log
+
+#if ( TARGET_OS_MAC )
+static OSStatus DebugMacOSXLogInit( void );
+static void DebugMacOSXLogPrint( char *inData, size_t inSize );
+#endif
+
+// Windows Debugger
+
+#if ( TARGET_OS_WIN32 )
+static void DebugWindowsDebuggerPrint( char *inData, size_t inSize );
+#endif
+
+// Windows Event Log
+
+#if ( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+static OSStatus DebugWindowsEventLogInit( const char *inName, HMODULE inModule );
+static void DebugWindowsEventLogPrint( DebugLevel inLevel, char *inData, size_t inSize );
+#endif
+
+// DebugLib support
+
+#if ( DEBUG_CORE_SERVICE_ASSERTS_ENABLED )
+static pascal void
+DebugAssertOutputHandler(
+ OSType inComponentSignature,
+ UInt32 inOptions,
+ const char * inAssertionString,
+ const char * inExceptionString,
+ const char * inErrorString,
+ const char * inFileName,
+ long inLineNumber,
+ void * inValue,
+ ConstStr255Param inOutputMsg );
+#endif
+
+// Utilities
+
+static char * DebugNumVersionToString( uint32_t inVersion, char *inString );
+
+#if ( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+static void DebugWinEnableConsole( void );
+#endif
+
+#if ( TARGET_OS_WIN32 )
+static TCHAR *
+DebugWinCharToTCharString(
+ const char * inCharString,
+ size_t inCharCount,
+ TCHAR * outTCharString,
+ size_t inTCharCountMax,
+ size_t * outTCharCount );
+#endif
+
+#if 0
+#pragma mark == Globals ==
+#endif
+
+//===========================================================================================================================
+// Private Globals
+//===========================================================================================================================
+
+#if ( TARGET_OS_VXWORKS )
+// TCP States for inetstatShow.
+
+extern char ** pTcpstates; // defined in tcpLib.c
+
+const char * kDebugTCPStates[] =
+{
+ "(0) TCPS_CLOSED",
+ "(1) TCPS_LISTEN",
+ "(2) TCPS_SYN_SENT",
+ "(3) TCPS_SYN_RECEIVED",
+ "(4) TCPS_ESTABLISHED",
+ "(5) TCPS_CLOSE_WAIT",
+ "(6) TCPS_FIN_WAIT_1",
+ "(7) TCPS_CLOSING",
+ "(8) TCPS_LAST_ACK",
+ "(9) TCPS_FIN_WAIT_2",
+ "(10) TCPS_TIME_WAIT",
+};
+#endif
+
+// General
+
+static bool gDebugInitialized = false;
+static DebugOutputType gDebugOutputType = kDebugOutputTypeNone;
+static DebugLevel gDebugPrintLevelMin = kDebugLevelInfo;
+static DebugLevel gDebugPrintLevelMax = kDebugLevelMax;
+static DebugLevel gDebugBreakLevel = kDebugLevelAssert;
+#if ( DEBUG_CORE_SERVICE_ASSERTS_ENABLED )
+static DebugAssertOutputHandlerUPP gDebugAssertOutputHandlerUPP = NULL;
+#endif
+
+// Custom
+
+static DebugOutputFunctionPtr gDebugCustomOutputFunction = NULL;
+static void * gDebugCustomOutputContext = NULL;
+
+// fprintf
+
+#if ( DEBUG_FPRINTF_ENABLED )
+static FILE * gDebugFPrintFFile = NULL;
+#endif
+
+// MacOSXLog
+
+#if ( TARGET_OS_MAC )
+typedef int ( *DebugMacOSXLogFunctionPtr )( const char *inFormat, ... );
+
+static DebugMacOSXLogFunctionPtr gDebugMacOSXLogFunction = NULL;
+#endif
+
+// WindowsEventLog
+
+
+#if ( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+static HANDLE gDebugWindowsEventLogEventSource = NULL;
+#endif
+
+#if 0
+#pragma mark -
+#pragma mark == General ==
+#endif
+
+//===========================================================================================================================
+// DebugInitialize
+//===========================================================================================================================
+
+DEBUG_EXPORT OSStatus DebugInitialize( DebugOutputType inType, ... )
+{
+ OSStatus err;
+ DebugOutputType type;
+ va_list args;
+
+ va_start( args, inType );
+
+#if ( TARGET_OS_VXWORKS )
+ // Set up the TCP state strings if they are not already set up by VxWorks (normally not set up for some reason).
+
+ if( !pTcpstates )
+ {
+ pTcpstates = (char **) kDebugTCPStates;
+ }
+#endif
+
+ // Set up DebugLib stuff (if building with Debugging.h).
+
+#if ( DEBUG_CORE_SERVICE_ASSERTS_ENABLED )
+ if( !gDebugAssertOutputHandlerUPP )
+ {
+ gDebugAssertOutputHandlerUPP = NewDebugAssertOutputHandlerUPP( DebugAssertOutputHandler );
+ check( gDebugAssertOutputHandlerUPP );
+ if( gDebugAssertOutputHandlerUPP )
+ {
+ InstallDebugAssertOutputHandler( gDebugAssertOutputHandlerUPP );
+ }
+ }
+#endif
+
+ // Pre-process meta-output kind to pick an appropriate output kind for the platform.
+
+ type = inType;
+ if( type == kDebugOutputTypeMetaConsole )
+ {
+ #if ( TARGET_OS_MAC )
+ type = kDebugOutputTypeMacOSXLog;
+ #elif ( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+ #if ( DEBUG_FPRINTF_ENABLED )
+ type = kDebugOutputTypeFPrintF;
+ #else
+ type = kDebugOutputTypeWindowsDebugger;
+ #endif
+ #elif ( TARGET_API_MAC_OSX_KERNEL )
+ #if ( DEBUG_MAC_OS_X_IOLOG_ENABLED )
+ type = kDebugOutputTypeMacOSXIOLog;
+ #elif ( DEBUG_IDEBUG_ENABLED )
+ type = kDebugOutputTypeiDebug;
+ #elif ( DEBUG_KPRINTF_ENABLED )
+ type = kDebugOutputTypeKPrintF;
+ #endif
+ #elif ( TARGET_OS_VXWORKS )
+ #if ( DEBUG_FPRINTF_ENABLED )
+ type = kDebugOutputTypeFPrintF;
+ #else
+ #error target is VxWorks, but fprintf output is disabled
+ #endif
+ #else
+ #if ( DEBUG_FPRINTF_ENABLED )
+ type = kDebugOutputTypeFPrintF;
+ #endif
+ #endif
+ }
+
+ // Process output kind.
+
+ gDebugOutputType = type;
+ switch( type )
+ {
+ case kDebugOutputTypeNone:
+ err = kNoErr;
+ break;
+
+ case kDebugOutputTypeCustom:
+ gDebugCustomOutputFunction = va_arg( args, DebugOutputFunctionPtr );
+ gDebugCustomOutputContext = va_arg( args, void * );
+ err = kNoErr;
+ break;
+
+#if ( DEBUG_FPRINTF_ENABLED )
+ case kDebugOutputTypeFPrintF:
+ if( inType == kDebugOutputTypeMetaConsole )
+ {
+ err = DebugFPrintFInit( kDebugOutputTypeFlagsStdErr, NULL );
+ }
+ else
+ {
+ DebugOutputTypeFlags flags;
+ const char * filename;
+
+ flags = (DebugOutputTypeFlags) va_arg( args, unsigned int );
+ if( ( flags & kDebugOutputTypeFlagsTypeMask ) == kDebugOutputTypeFlagsFile )
+ {
+ filename = va_arg( args, const char * );
+ }
+ else
+ {
+ filename = NULL;
+ }
+ err = DebugFPrintFInit( flags, filename );
+ }
+ break;
+#endif
+
+#if ( DEBUG_IDEBUG_ENABLED )
+ case kDebugOutputTypeiDebug:
+ err = DebugiDebugInit();
+ break;
+#endif
+
+#if ( DEBUG_KPRINTF_ENABLED )
+ case kDebugOutputTypeKPrintF:
+ err = kNoErr;
+ break;
+#endif
+
+#if ( DEBUG_MAC_OS_X_IOLOG_ENABLED )
+ case kDebugOutputTypeMacOSXIOLog:
+ err = kNoErr;
+ break;
+#endif
+
+#if ( TARGET_OS_MAC )
+ case kDebugOutputTypeMacOSXLog:
+ err = DebugMacOSXLogInit();
+ break;
+#endif
+
+#if ( TARGET_OS_WIN32 )
+ case kDebugOutputTypeWindowsDebugger:
+ err = kNoErr;
+ break;
+#endif
+
+#if ( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+ case kDebugOutputTypeWindowsEventLog:
+ {
+ const char * name;
+ HMODULE module;
+
+ name = va_arg( args, const char * );
+ module = va_arg( args, HMODULE );
+ err = DebugWindowsEventLogInit( name, module );
+ }
+ break;
+#endif
+
+ default:
+ err = kParamErr;
+ goto exit;
+ }
+ gDebugInitialized = true;
+
+exit:
+ va_end( args );
+ return( err );
+}
+
+//===========================================================================================================================
+// DebugFinalize
+//===========================================================================================================================
+
+DEBUG_EXPORT void DebugFinalize( void )
+{
+#if ( DEBUG_CORE_SERVICE_ASSERTS_ENABLED )
+ check( gDebugAssertOutputHandlerUPP );
+ if( gDebugAssertOutputHandlerUPP )
+ {
+ InstallDebugAssertOutputHandler( NULL );
+ DisposeDebugAssertOutputHandlerUPP( gDebugAssertOutputHandlerUPP );
+ gDebugAssertOutputHandlerUPP = NULL;
+ }
+#endif
+}
+
+//===========================================================================================================================
+// DebugGetProperty
+//===========================================================================================================================
+
+DEBUG_EXPORT OSStatus DebugGetProperty( DebugPropertyTag inTag, ... )
+{
+ OSStatus err;
+ va_list args;
+ DebugLevel * level;
+
+ va_start( args, inTag );
+ switch( inTag )
+ {
+ case kDebugPropertyTagPrintLevelMin:
+ level = va_arg( args, DebugLevel * );
+ *level = gDebugPrintLevelMin;
+ err = kNoErr;
+ break;
+
+ case kDebugPropertyTagPrintLevelMax:
+ level = va_arg( args, DebugLevel * );
+ *level = gDebugPrintLevelMax;
+ err = kNoErr;
+ break;
+
+ case kDebugPropertyTagBreakLevel:
+ level = va_arg( args, DebugLevel * );
+ *level = gDebugBreakLevel;
+ err = kNoErr;
+ break;
+
+ default:
+ err = kUnsupportedErr;
+ break;
+ }
+ va_end( args );
+ return( err );
+}
+
+//===========================================================================================================================
+// DebugSetProperty
+//===========================================================================================================================
+
+DEBUG_EXPORT OSStatus DebugSetProperty( DebugPropertyTag inTag, ... )
+{
+ OSStatus err;
+ va_list args;
+ DebugLevel level;
+
+ va_start( args, inTag );
+ switch( inTag )
+ {
+ case kDebugPropertyTagPrintLevelMin:
+ level = va_arg( args, DebugLevel );
+ gDebugPrintLevelMin = level;
+ err = kNoErr;
+ break;
+
+ case kDebugPropertyTagPrintLevelMax:
+ level = va_arg( args, DebugLevel );
+ gDebugPrintLevelMax = level;
+ err = kNoErr;
+ break;
+
+ case kDebugPropertyTagBreakLevel:
+ level = va_arg( args, DebugLevel );
+ gDebugBreakLevel = level;
+ err = kNoErr;
+ break;
+
+ default:
+ err = kUnsupportedErr;
+ break;
+ }
+ va_end( args );
+ return( err );
+}
+
+#if 0
+#pragma mark -
+#pragma mark == Output ==
+#endif
+
+//===========================================================================================================================
+// DebugPrintF
+//===========================================================================================================================
+
+DEBUG_EXPORT size_t DebugPrintF( DebugLevel inLevel, const char *inFormat, ... )
+{
+ va_list args;
+ size_t n;
+
+ // Skip if the level is not in the enabled range..
+
+ if( ( inLevel < gDebugPrintLevelMin ) || ( inLevel > gDebugPrintLevelMax ) )
+ {
+ n = 0;
+ goto exit;
+ }
+
+ va_start( args, inFormat );
+ n = DebugPrintFVAList( inLevel, inFormat, args );
+ va_end( args );
+
+exit:
+ return( n );
+}
+
+//===========================================================================================================================
+// DebugPrintFVAList
+//===========================================================================================================================
+
+DEBUG_EXPORT size_t DebugPrintFVAList( DebugLevel inLevel, const char *inFormat, va_list inArgs )
+{
+ size_t n;
+ char buffer[ 512 ];
+
+ // Skip if the level is not in the enabled range..
+
+ if( ( inLevel < gDebugPrintLevelMin ) || ( inLevel > gDebugPrintLevelMax ) )
+ {
+ n = 0;
+ goto exit;
+ }
+
+ n = DebugSNPrintFVAList( buffer, sizeof( buffer ), inFormat, inArgs );
+ DebugPrint( inLevel, buffer, (size_t) n );
+
+exit:
+ return( n );
+}
+
+//===========================================================================================================================
+// DebugPrint
+//===========================================================================================================================
+
+static OSStatus DebugPrint( DebugLevel inLevel, char *inData, size_t inSize )
+{
+ OSStatus err;
+
+ // Skip if the level is not in the enabled range..
+
+ if( ( inLevel < gDebugPrintLevelMin ) || ( inLevel > gDebugPrintLevelMax ) )
+ {
+ err = kRangeErr;
+ goto exit;
+ }
+
+ // Printing is not safe at interrupt time so check for this and warn with an interrupt safe mechanism (if available).
+
+ if( DebugTaskLevel() & kDebugInterruptLevelMask )
+ {
+ #if ( TARGET_OS_VXWORKS )
+ logMsg( "\ncannot print at interrupt time\n\n", 1, 2, 3, 4, 5, 6 );
+ #endif
+
+ err = kExecutionStateErr;
+ goto exit;
+ }
+
+ // Initialize the debugging library if it hasn't already been initialized (allows for zero-config usage).
+
+ if( !gDebugInitialized )
+ {
+ debug_initialize( kDebugOutputTypeMetaConsole );
+ }
+
+ // Print based on the current output type.
+
+ switch( gDebugOutputType )
+ {
+ case kDebugOutputTypeNone:
+ break;
+
+ case kDebugOutputTypeCustom:
+ if( gDebugCustomOutputFunction )
+ {
+ gDebugCustomOutputFunction( inData, inSize, gDebugCustomOutputContext );
+ }
+ break;
+
+#if ( DEBUG_FPRINTF_ENABLED )
+ case kDebugOutputTypeFPrintF:
+ DebugFPrintFPrint( inData, inSize );
+ break;
+#endif
+
+#if ( DEBUG_IDEBUG_ENABLED )
+ case kDebugOutputTypeiDebug:
+ DebugiDebugPrint( inData, inSize );
+ break;
+#endif
+
+#if ( DEBUG_KPRINTF_ENABLED )
+ case kDebugOutputTypeKPrintF:
+ DebugKPrintFPrint( inData, inSize );
+ break;
+#endif
+
+#if ( DEBUG_MAC_OS_X_IOLOG_ENABLED )
+ case kDebugOutputTypeMacOSXIOLog:
+ DebugMacOSXIOLogPrint( inData, inSize );
+ break;
+#endif
+
+#if ( TARGET_OS_MAC )
+ case kDebugOutputTypeMacOSXLog:
+ DebugMacOSXLogPrint( inData, inSize );
+ break;
+#endif
+
+#if ( TARGET_OS_WIN32 )
+ case kDebugOutputTypeWindowsDebugger:
+ DebugWindowsDebuggerPrint( inData, inSize );
+ break;
+#endif
+
+#if ( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+ case kDebugOutputTypeWindowsEventLog:
+ DebugWindowsEventLogPrint( inLevel, inData, inSize );
+ break;
+#endif
+
+ default:
+ break;
+ }
+ err = kNoErr;
+
+exit:
+ return( err );
+}
+
+//===========================================================================================================================
+// DebugPrintAssert
+//
+// Warning: This routine relies on several of the strings being string constants that will exist forever because the
+// underlying logMsg API that does the printing is asynchronous so it cannot use temporary/stack-based
+// pointer variables (e.g. local strings). The debug macros that invoke this function only use constant
+// constant strings, but if this function is invoked directly from other places, it must use constant strings.
+//===========================================================================================================================
+
+DEBUG_EXPORT void
+DebugPrintAssert(
+ int_least32_t inErrorCode,
+ const char * inAssertString,
+ const char * inMessage,
+ const char * inFilename,
+ int_least32_t inLineNumber,
+ const char * inFunction )
+{
+ // Skip if the level is not in the enabled range..
+
+ if( ( kDebugLevelAssert < gDebugPrintLevelMin ) || ( kDebugLevelAssert > gDebugPrintLevelMax ) )
+ {
+ return;
+ }
+
+ if( inErrorCode != 0 )
+ {
+ DebugPrintF(
+ kDebugLevelAssert,
+ "\n"
+ "[ASSERT] error: %ld (%m)\n"
+ "[ASSERT] where: \"%s\", line %ld, \"%s\"\n"
+ "\n",
+ inErrorCode, inErrorCode,
+ inFilename ? inFilename : "",
+ inLineNumber,
+ inFunction ? inFunction : "" );
+ }
+ else
+ {
+ DebugPrintF(
+ kDebugLevelAssert,
+ "\n"
+ "[ASSERT] assert: \"%s\" %s\n"
+ "[ASSERT] where: \"%s\", line %ld, \"%s\"\n"
+ "\n",
+ inAssertString ? inAssertString : "",
+ inMessage ? inMessage : "",
+ inFilename ? inFilename : "",
+ inLineNumber,
+ inFunction ? inFunction : "" );
+ }
+
+ // Break into the debugger if enabled.
+
+ #if ( TARGET_OS_WIN32 )
+ if( gDebugBreakLevel <= kDebugLevelAssert )
+ {
+ if( IsDebuggerPresent() )
+ {
+ DebugBreak();
+ }
+ }
+ #endif
+}
+
+#if 0
+#pragma mark -
+#endif
+
+#if ( DEBUG_FPRINTF_ENABLED )
+//===========================================================================================================================
+// DebugFPrintFInit
+//===========================================================================================================================
+
+static OSStatus DebugFPrintFInit( DebugOutputTypeFlags inFlags, const char *inFilename )
+{
+ OSStatus err;
+ DebugOutputTypeFlags typeFlags;
+
+ typeFlags = inFlags & kDebugOutputTypeFlagsTypeMask;
+ if( typeFlags == kDebugOutputTypeFlagsStdOut )
+ {
+ #if ( TARGET_OS_WIN32 )
+ DebugWinEnableConsole();
+ #endif
+
+ gDebugFPrintFFile = stdout;
+ }
+ else if( typeFlags == kDebugOutputTypeFlagsStdErr )
+ {
+ #if ( TARGET_OS_WIN32 )
+ DebugWinEnableConsole();
+ #endif
+
+ gDebugFPrintFFile = stdout;
+ }
+ else if( typeFlags == kDebugOutputTypeFlagsFile )
+ {
+ require_action_quiet( inFilename && ( *inFilename != '\0' ), exit, err = kOpenErr );
+
+ gDebugFPrintFFile = fopen( inFilename, "a" );
+ require_action_quiet( gDebugFPrintFFile, exit, err = kOpenErr );
+ }
+ else
+ {
+ err = kParamErr;
+ goto exit;
+ }
+ err = kNoErr;
+
+exit:
+ return( err );
+}
+
+//===========================================================================================================================
+// DebugFPrintFPrint
+//===========================================================================================================================
+
+static void DebugFPrintFPrint( char *inData, size_t inSize )
+{
+ char * p;
+ char * q;
+
+ // Convert \r to \n. fprintf will interpret \n and convert to whatever is appropriate for the platform.
+
+ p = inData;
+ q = p + inSize;
+ while( p < q )
+ {
+ if( *p == '\r' )
+ {
+ *p = '\n';
+ }
+ ++p;
+ }
+
+ // Write the data and flush.
+
+ if( gDebugFPrintFFile )
+ {
+ fprintf( gDebugFPrintFFile, "%.*s", (int) inSize, inData );
+ fflush( gDebugFPrintFFile );
+ }
+}
+#endif // DEBUG_FPRINTF_ENABLED
+
+#if ( DEBUG_IDEBUG_ENABLED )
+//===========================================================================================================================
+// DebugiDebugInit
+//===========================================================================================================================
+
+static OSStatus DebugiDebugInit( void )
+{
+ OSStatus err;
+
+ #if ( TARGET_API_MAC_OSX_KERNEL )
+
+ extern uint32_t * _giDebugReserved1;
+
+ // Emulate the iDebugSetOutputType macro in iDebugServices.h.
+ // Note: This is not thread safe, but neither is iDebugServices.h nor iDebugKext.
+
+ if( !_giDebugReserved1 )
+ {
+ _giDebugReserved1 = (uint32_t *) IOMalloc( sizeof( uint32_t ) );
+ require_action_quiet( _giDebugReserved1, exit, err = kNoMemoryErr );
+ }
+ *_giDebugReserved1 = 0x00010000U;
+ err = kNoErr;
+exit:
+ #else
+
+ __private_extern__ void iDebugSetOutputTypeInternal( uint32_t inType );
+
+ iDebugSetOutputTypeInternal( 0x00010000U );
+ err = kNoErr;
+
+ #endif
+
+ return( err );
+}
+
+//===========================================================================================================================
+// DebugiDebugPrint
+//===========================================================================================================================
+
+static void DebugiDebugPrint( char *inData, size_t inSize )
+{
+ #if ( TARGET_API_MAC_OSX_KERNEL )
+
+ // Locally declared here so we do not need to include iDebugKext.h.
+ // Note: IOKit uses a global namespace for all code and only a partial link occurs at build time. When the
+ // KEXT is loaded, the runtime linker will link in this extern'd symbol (assuming iDebug is present).
+ // _giDebugLogInternal is actually part of IOKit proper so this should link even if iDebug is not present.
+
+ typedef void ( *iDebugLogFunctionPtr )( uint32_t inLevel, uint32_t inTag, const char *inFormat, ... );
+
+ extern iDebugLogFunctionPtr _giDebugLogInternal;
+
+ if( _giDebugLogInternal )
+ {
+ _giDebugLogInternal( 0, 0, "%.*s", (int) inSize, inData );
+ }
+
+ #else
+
+ __private_extern__ void iDebugLogInternal( uint32_t inLevel, uint32_t inTag, const char *inFormat, ... );
+
+ iDebugLogInternal( 0, 0, "%.*s", (int) inSize, inData );
+
+ #endif
+}
+#endif
+
+#if ( DEBUG_KPRINTF_ENABLED )
+//===========================================================================================================================
+// DebugKPrintFPrint
+//===========================================================================================================================
+
+static void DebugKPrintFPrint( char *inData, size_t inSize )
+{
+ extern void kprintf( const char *inFormat, ... );
+
+ kprintf( "%.*s", (int) inSize, inData );
+}
+#endif
+
+#if ( DEBUG_MAC_OS_X_IOLOG_ENABLED )
+//===========================================================================================================================
+// DebugMacOSXIOLogPrint
+//===========================================================================================================================
+
+static void DebugMacOSXIOLogPrint( char *inData, size_t inSize )
+{
+ extern void IOLog( const char *inFormat, ... );
+
+ IOLog( "%.*s", (int) inSize, inData );
+}
+#endif
+
+#if ( TARGET_OS_MAC )
+//===========================================================================================================================
+// DebugMacOSXLogInit
+//===========================================================================================================================
+
+static OSStatus DebugMacOSXLogInit( void )
+{
+ OSStatus err;
+ CFStringRef path;
+ CFURLRef url;
+ CFBundleRef bundle;
+ CFStringRef functionName;
+ void * functionPtr;
+
+ bundle = NULL;
+
+ // Create a bundle reference for System.framework.
+
+ path = CFSTR( "/System/Library/Frameworks/System.framework" );
+ url = CFURLCreateWithFileSystemPath( NULL, path, kCFURLPOSIXPathStyle, true );
+ require_action_quiet( url, exit, err = memFullErr );
+
+ bundle = CFBundleCreate( NULL, url );
+ CFRelease( url );
+ require_action_quiet( bundle, exit, err = memFullErr );
+
+ // Get a ptr to the system's "printf" function from System.framework.
+
+ functionName = CFSTR( "printf" );
+ functionPtr = CFBundleGetFunctionPointerForName( bundle, functionName );
+ require_action_quiet( functionPtr, exit, err = memFullErr );
+
+ // Success! Note: The bundle cannot be released because it would invalidate the function ptr.
+
+ gDebugMacOSXLogFunction = (DebugMacOSXLogFunctionPtr) functionPtr;
+ bundle = NULL;
+ err = noErr;
+
+exit:
+ if( bundle )
+ {
+ CFRelease( bundle );
+ }
+ return( err );
+}
+
+//===========================================================================================================================
+// DebugMacOSXLogPrint
+//===========================================================================================================================
+
+static void DebugMacOSXLogPrint( char *inData, size_t inSize )
+{
+ if( gDebugMacOSXLogFunction )
+ {
+ gDebugMacOSXLogFunction( "%.*s", (int) inSize, inData );
+ }
+}
+#endif
+
+#if ( TARGET_OS_WIN32 )
+//===========================================================================================================================
+// DebugWindowsDebuggerPrint
+//===========================================================================================================================
+
+void DebugWindowsDebuggerPrint( char *inData, size_t inSize )
+{
+ TCHAR buffer[ 512 ];
+ const char * src;
+ const char * end;
+ TCHAR * dst;
+ char c;
+
+ // Copy locally and null terminate the string. This also converts from char to TCHAR in case we are
+ // building with UNICODE enabled since the input is always char. Also convert \r to \n in the process.
+
+ src = inData;
+ if( inSize >= sizeof_array( buffer ) )
+ {
+ inSize = sizeof_array( buffer ) - 1;
+ }
+ end = src + inSize;
+ dst = buffer;
+ while( src < end )
+ {
+ c = *src++;
+ if( c == '\r' )
+ {
+ c = '\n';
+ }
+ *dst++ = (TCHAR) c;
+ }
+ *dst = 0;
+
+ // Print out the string to the debugger.
+
+ OutputDebugString( buffer );
+}
+#endif
+
+#if ( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+//===========================================================================================================================
+// DebugWindowsEventLogInit
+//===========================================================================================================================
+
+static OSStatus DebugWindowsEventLogInit( const char *inName, HMODULE inModule )
+{
+ OSStatus err;
+ HKEY key;
+ TCHAR name[ 128 ];
+ const char * src;
+ TCHAR path[ MAX_PATH ];
+ size_t size;
+ DWORD typesSupported;
+ DWORD n;
+
+ key = NULL;
+
+ // Use a default name if needed then convert the name to TCHARs so it works on ANSI or Unicode builds.
+
+ if( !inName || ( *inName == '\0' ) )
+ {
+ inName = "DefaultApp";
+ }
+ DebugWinCharToTCharString( inName, kSizeCString, name, sizeof( name ), NULL );
+
+ // Build the path string using the fixed registry path and app name.
+
+ src = "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\";
+ DebugWinCharToTCharString( src, kSizeCString, path, sizeof_array( path ), &size );
+ DebugWinCharToTCharString( inName, kSizeCString, path + size, sizeof_array( path ) - size, NULL );
+
+ // Add/Open the source name as a sub-key under the Application key in the EventLog registry key.
+
+ err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, path, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, NULL );
+ require_noerr_quiet( err, exit );
+
+ // Set the path in the EventMessageFile subkey. Add 1 to the TCHAR count to include the null terminator.
+
+ n = GetModuleFileName( inModule, path, sizeof_array( path ) );
+ err = translate_errno( n > 0, (OSStatus) GetLastError(), kParamErr );
+ require_noerr_quiet( err, exit );
+ n += 1;
+ n *= sizeof( TCHAR );
+
+ err = RegSetValueEx( key, TEXT( "EventMessageFile" ), 0, REG_EXPAND_SZ, (const LPBYTE) path, n );
+ require_noerr_quiet( err, exit );
+
+ // Set the supported event types in the TypesSupported subkey.
+
+ typesSupported = EVENTLOG_SUCCESS | EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE |
+ EVENTLOG_AUDIT_SUCCESS | EVENTLOG_AUDIT_FAILURE;
+ err = RegSetValueEx( key, TEXT( "TypesSupported" ), 0, REG_DWORD, (const LPBYTE) &typesSupported, sizeof( DWORD ) );
+ require_noerr_quiet( err, exit );
+
+ // Set up the event source.
+
+ gDebugWindowsEventLogEventSource = RegisterEventSource( NULL, name );
+ err = translate_errno( gDebugWindowsEventLogEventSource, (OSStatus) GetLastError(), kParamErr );
+ require_noerr_quiet( err, exit );
+
+exit:
+ if( key )
+ {
+ RegCloseKey( key );
+ }
+ return( err );
+}
+
+//===========================================================================================================================
+// DebugWindowsEventLogPrint
+//===========================================================================================================================
+
+static void DebugWindowsEventLogPrint( DebugLevel inLevel, char *inData, size_t inSize )
+{
+ WORD type;
+ TCHAR buffer[ 512 ];
+ const char * src;
+ const char * end;
+ TCHAR * dst;
+ char c;
+ const TCHAR * array[ 1 ];
+
+ // Map the debug level to a Windows EventLog type.
+
+ if( inLevel <= kDebugLevelNotice )
+ {
+ type = EVENTLOG_INFORMATION_TYPE;
+ }
+ else if( inLevel <= kDebugLevelWarning )
+ {
+ type = EVENTLOG_WARNING_TYPE;
+ }
+ else
+ {
+ type = EVENTLOG_ERROR_TYPE;
+ }
+
+ // Copy locally and null terminate the string. This also converts from char to TCHAR in case we are
+ // building with UNICODE enabled since the input is always char. Also convert \r to \n in the process.
+
+ src = inData;
+ if( inSize >= sizeof_array( buffer ) )
+ {
+ inSize = sizeof_array( buffer ) - 1;
+ }
+ end = src + inSize;
+ dst = buffer;
+ while( src < end )
+ {
+ c = *src++;
+ if( c == '\r' )
+ {
+ c = '\n';
+ }
+ *dst++ = (TCHAR) c;
+ }
+ *dst = 0;
+
+ // Add the the string to the event log.
+
+ array[ 0 ] = buffer;
+ if( gDebugWindowsEventLogEventSource )
+ {
+ ReportEvent( gDebugWindowsEventLogEventSource, type, 0, 0x20000001L, NULL, 1, 0, array, NULL );
+ }
+}
+#endif // TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE
+
+#if ( DEBUG_CORE_SERVICE_ASSERTS_ENABLED )
+//===========================================================================================================================
+// DebugAssertOutputHandler
+//===========================================================================================================================
+
+static pascal void
+DebugAssertOutputHandler(
+ OSType inComponentSignature,
+ UInt32 inOptions,
+ const char * inAssertString,
+ const char * inExceptionString,
+ const char * inErrorString,
+ const char * inFileName,
+ long inLineNumber,
+ void * inValue,
+ ConstStr255Param inOutputMsg )
+{
+ DEBUG_UNUSED( inComponentSignature );
+ DEBUG_UNUSED( inOptions );
+ DEBUG_UNUSED( inExceptionString );
+ DEBUG_UNUSED( inValue );
+ DEBUG_UNUSED( inOutputMsg );
+
+ DebugPrintAssert( 0, inAssertString, inErrorString, inFileName, (int_least32_t) inLineNumber, "" );
+}
+#endif
+
+#if 0
+#pragma mark -
+#pragma mark == Utilities ==
+#endif
+
+//===========================================================================================================================
+// DebugSNPrintF
+//
+// Stolen from mDNS.c's mDNS_snprintf/mDNS_vsnprintf with the following changes:
+//
+// Changed names to avoid name collisions with the mDNS versions.
+// Changed types to standard C types since mDNSEmbeddedAPI.h may not be available.
+// Conditionalized mDNS stuff so it can be used with or with mDNSEmbeddedAPI.h.
+// Added 64-bit support for %d (%lld), %i (%lli), %u (%llu), %o (%llo), %x (%llx), and %b (%llb).
+// Added %@ - Cocoa/CoreFoundation object. Param is the object. Strings are used directly. Others use CFCopyDescription.
+// Added %.8a - FIbre Channel address. Arg=ptr to address.
+// Added %##a - IPv4 (if AF_INET defined) or IPv6 (if AF_INET6 defined) sockaddr. Arg=ptr to sockaddr.
+// Added %b - Binary representation of integer (e.g. 01101011). Modifiers and arg=the same as %d, %x, etc.
+// Added %C - Mac-style FourCharCode (e.g. 'APPL'). Arg=32-bit value to print as a Mac-style FourCharCode.
+// Added %H - Hex Dump (e.g. "\x6b\xa7" -> "6B A7"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size.
+// Added %#H - Hex Dump & ASCII (e.g. "\x41\x62" -> "6B A7 'Ab'"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size.
+// Added %m - Error Message (e.g. 0 -> "kNoErr"). Modifiers and error code args are the same as %d, %x, etc.
+// Added %S - UTF-16 string. Host order if no BOM. Precision is UTF-16 char count. BOM counts in any precision. Arg=ptr.
+// Added %#S - Big Endian UTF-16 string (unless BOM overrides). Otherwise the same as %S.
+// Added %##S - Little Endian UTF-16 string (unless BOM overrides). Otherwise the same as %S.
+// Added %U - Universally Unique Identifier (UUID) (e.g. 6ba7b810-9dad-11d1-80b4-00c04fd430c8). Arg=ptr to 16-byte UUID.
+//===========================================================================================================================
+
+DEBUG_EXPORT size_t DebugSNPrintF(char *sbuffer, size_t buflen, const char *fmt, ...)
+{
+ size_t length;
+
+ va_list ptr;
+ va_start(ptr,fmt);
+ length = DebugSNPrintFVAList(sbuffer, buflen, fmt, ptr);
+ va_end(ptr);
+
+ return(length);
+}
+
+//===========================================================================================================================
+// DebugSNPrintFVAList - va_list version of DebugSNPrintF. See DebugSNPrintF for more info.
+//===========================================================================================================================
+
+DEBUG_EXPORT size_t DebugSNPrintFVAList(char *sbuffer, size_t buflen, const char *fmt, va_list arg)
+{
+ static const struct DebugSNPrintF_format
+ {
+ unsigned leftJustify : 1;
+ unsigned forceSign : 1;
+ unsigned zeroPad : 1;
+ unsigned havePrecision : 1;
+ unsigned hSize : 1;
+ char lSize;
+ char altForm;
+ char sign; // +, - or space
+ unsigned int fieldWidth;
+ unsigned int precision;
+ } DebugSNPrintF_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ size_t nwritten = 0;
+ int c;
+ if (buflen == 0) return(0);
+ buflen--; // Pre-reserve one space in the buffer for the terminating nul
+ if (buflen == 0) goto exit;
+
+ for (c = *fmt; c != 0; c = *++fmt)
+ {
+ if (c != '%')
+ {
+ *sbuffer++ = (char)c;
+ if (++nwritten >= buflen) goto exit;
+ }
+ else
+ {
+ size_t i=0, j;
+ // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for
+ // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc.
+ // The size needs to be enough for a 256-byte domain name plus some error text.
+ #define mDNS_VACB_Size 300
+ char mDNS_VACB[mDNS_VACB_Size];
+ #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size])
+ #define mDNS_VACB_Remain(s) ((size_t)(mDNS_VACB_Lim - s))
+ char *s = mDNS_VACB_Lim;
+ const char *digits = "0123456789ABCDEF";
+ struct DebugSNPrintF_format F = DebugSNPrintF_format_default;
+
+ for(;;) // decode flags
+ {
+ c = *++fmt;
+ if (c == '-') F.leftJustify = 1;
+ else if (c == '+') F.forceSign = 1;
+ else if (c == ' ') F.sign = ' ';
+ else if (c == '#') F.altForm++;
+ else if (c == '0') F.zeroPad = 1;
+ else break;
+ }
+
+ if (c == '*') // decode field width
+ {
+ int f = va_arg(arg, int);
+ if (f < 0) { f = -f; F.leftJustify = 1; }
+ F.fieldWidth = (unsigned int)f;
+ c = *++fmt;
+ }
+ else
+ {
+ for (; c >= '0' && c <= '9'; c = *++fmt)
+ F.fieldWidth = (10 * F.fieldWidth) + (c - '0');
+ }
+
+ if (c == '.') // decode precision
+ {
+ if ((c = *++fmt) == '*')
+ { F.precision = va_arg(arg, unsigned int); c = *++fmt; }
+ else for (; c >= '0' && c <= '9'; c = *++fmt)
+ F.precision = (10 * F.precision) + (c - '0');
+ F.havePrecision = 1;
+ }
+
+ if (F.leftJustify) F.zeroPad = 0;
+
+conv:
+ switch (c) // perform appropriate conversion
+ {
+ #if TYPE_LONGLONG_NATIVE
+ unsigned_long_long_compat n;
+ unsigned_long_long_compat base;
+ #else
+ unsigned long n;
+ unsigned long base;
+ #endif
+ case 'h': F.hSize = 1; c = *++fmt; goto conv;
+ case 'l': // fall through
+ case 'L': F.lSize++; c = *++fmt; goto conv;
+ case 'd':
+ case 'i': base = 10;
+ goto canBeSigned;
+ case 'u': base = 10;
+ goto notSigned;
+ case 'o': base = 8;
+ goto notSigned;
+ case 'b': base = 2;
+ goto notSigned;
+ case 'p': n = va_arg(arg, uintptr_t);
+ F.havePrecision = 1;
+ F.precision = (sizeof(uintptr_t) == 4) ? 8 : 16;
+ F.sign = 0;
+ base = 16;
+ c = 'x';
+ goto number;
+ case 'x': digits = "0123456789abcdef";
+ case 'X': base = 16;
+ goto notSigned;
+canBeSigned:
+ #if TYPE_LONGLONG_NATIVE
+ if (F.lSize == 1) n = (unsigned_long_long_compat)va_arg(arg, long);
+ else if (F.lSize == 2) n = (unsigned_long_long_compat)va_arg(arg, long_long_compat);
+ else n = (unsigned_long_long_compat)va_arg(arg, int);
+ #else
+ if (F.lSize == 1) n = (unsigned long)va_arg(arg, long);
+ else if (F.lSize == 2) goto exit;
+ else n = (unsigned long)va_arg(arg, int);
+ #endif
+ if (F.hSize) n = (short) n;
+ #if TYPE_LONGLONG_NATIVE
+ if ((long_long_compat) n < 0) { n = (unsigned_long_long_compat)-(long_long_compat)n; F.sign = '-'; }
+ #else
+ if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; }
+ #endif
+ else if (F.forceSign) F.sign = '+';
+ goto number;
+
+notSigned: if (F.lSize == 1) n = va_arg(arg, unsigned long);
+ else if (F.lSize == 2)
+ {
+ #if TYPE_LONGLONG_NATIVE
+ n = va_arg(arg, unsigned_long_long_compat);
+ #else
+ goto exit;
+ #endif
+ }
+ else n = va_arg(arg, unsigned int);
+ if (F.hSize) n = (unsigned short) n;
+ F.sign = 0;
+ goto number;
+
+number: if (!F.havePrecision)
+ {
+ if (F.zeroPad)
+ {
+ F.precision = F.fieldWidth;
+ if (F.altForm) F.precision -= 2;
+ if (F.sign) --F.precision;
+ }
+ if (F.precision < 1) F.precision = 1;
+ }
+ if (F.precision > mDNS_VACB_Size - 1)
+ F.precision = mDNS_VACB_Size - 1;
+ for (i = 0; n; n /= base, i++) *--s = (char)(digits[n % base]);
+ for (; i < F.precision; i++) *--s = '0';
+ if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; }
+ if (F.sign) { *--s = F.sign; i++; }
+ break;
+
+ case 'a': {
+ unsigned char *a = va_arg(arg, unsigned char *);
+ char pre[4] = "";
+ char post[32] = "";
+ if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
+ else
+ {
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ if (F.altForm == 1)
+ {
+ #if (defined(MDNS_DEBUGMSGS))
+ mDNSAddr *ip = (mDNSAddr*)a;
+ switch (ip->type)
+ {
+ case mDNSAddrType_IPv4: F.precision = 4; a = (unsigned char *)&ip->ip.v4; break;
+ case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break;
+ default: F.precision = 0; break;
+ }
+ #else
+ F.precision = 0; // mDNSEmbeddedAPI.h not included so no mDNSAddr support
+ #endif
+ }
+ else if (F.altForm == 2)
+ {
+ #ifdef AF_INET
+ const struct sockaddr *sa;
+ unsigned char *port;
+ sa = (const struct sockaddr*)a;
+ switch (sa->sa_family)
+ {
+ case AF_INET: F.precision = 4; a = (unsigned char*)&((const struct sockaddr_in *)a)->sin_addr;
+ port = (unsigned char*)&((const struct sockaddr_in *)sa)->sin_port;
+ DebugSNPrintF(post, sizeof(post), ":%d", (port[0] << 8) | port[1]); break;
+ #ifdef AF_INET6
+ case AF_INET6: F.precision = 16; a = (unsigned char*)&((const struct sockaddr_in6 *)a)->sin6_addr;
+ pre[0] = '['; pre[1] = '\0';
+ port = (unsigned char*)&((const struct sockaddr_in6 *)sa)->sin6_port;
+ DebugSNPrintF(post, sizeof(post), "%%%d]:%d",
+ (int)((const struct sockaddr_in6 *)sa)->sin6_scope_id,
+ (port[0] << 8) | port[1]); break;
+ #endif
+ default: F.precision = 0; break;
+ }
+ #else
+ F.precision = 0; // socket interfaces not included so no sockaddr support
+ #endif
+ }
+ switch (F.precision)
+ {
+ case 4: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d%s",
+ a[0], a[1], a[2], a[3], post); break;
+ case 6: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X",
+ a[0], a[1], a[2], a[3], a[4], a[5]); break;
+ case 8: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",
+ a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]); break;
+ case 16: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB),
+ "%s%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X%s",
+ pre, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8],
+ a[9], a[10], a[11], a[12], a[13], a[14], a[15], post); break;
+ default: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify address size "
+ "(i.e. %.4a=IPv4, %.6a=Ethernet, %.8a=Fibre Channel %.16a=IPv6) >>"); break;
+ }
+ }
+ }
+ break;
+
+ case 'U': {
+ unsigned char *a = va_arg(arg, unsigned char *);
+ if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
+ else
+ {
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ *((uint32_t*) &a[0]), *((uint16_t*) &a[4]), *((uint16_t*) &a[6]),
+ a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]); break;
+ }
+ }
+ break;
+
+ case 'c': *--s = (char)va_arg(arg, int); i = 1; break;
+
+ case 'C': if (F.lSize) n = va_arg(arg, unsigned long);
+ else n = va_arg(arg, unsigned int);
+ if (F.hSize) n = (unsigned short) n;
+ c = (int)( n & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^');
+ c = (int)((n >> 8) & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^');
+ c = (int)((n >> 16) & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^');
+ c = (int)((n >> 24) & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^');
+ i = 4;
+ break;
+
+ case 's': s = va_arg(arg, char *);
+ if (!s) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
+ else switch (F.altForm)
+ {
+ case 0: i=0;
+ if (F.havePrecision) // C string
+ {
+ while((i < F.precision) && s[i]) i++;
+ // Make sure we don't truncate in the middle of a UTF-8 character.
+ // If the last character is part of a multi-byte UTF-8 character, back up to the start of it.
+ j=0;
+ while((i > 0) && ((c = s[i-1]) & 0x80)) { j++; i--; if((c & 0xC0) != 0x80) break;}
+ // If the actual count of UTF-8 characters matches the encoded UTF-8 count, add it back.
+ if((j > 1) && (j <= 6))
+ {
+ int test = (0xFF << (8-j)) & 0xFF;
+ int mask = test | (1 << ((8-j)-1));
+ if((c & mask) == test) i += j;
+ }
+ }
+ else
+ while(s[i]) i++;
+ break;
+ case 1: i = (unsigned char) *s++; break; // Pascal string
+ case 2: { // DNS label-sequence name
+ unsigned char *a = (unsigned char *)s;
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ if (*a == 0) *s++ = '.'; // Special case for root DNS name
+ while (*a)
+ {
+ if (*a > 63) { s += DebugSNPrintF(s, mDNS_VACB_Remain(s), "<<INVALID LABEL LENGTH %u>>", *a); break; }
+ if (s + *a >= &mDNS_VACB[254]) { s += DebugSNPrintF(s, mDNS_VACB_Remain(s), "<<NAME TOO LONG>>"); break; }
+ s += DebugSNPrintF(s, mDNS_VACB_Remain(s), "%#s.", a);
+ a += 1 + *a;
+ }
+ i = (size_t)(s - mDNS_VACB);
+ s = mDNS_VACB; // Reset s back to the start of the buffer
+ break;
+ }
+ }
+ if (F.havePrecision && i > F.precision) // Make sure we don't truncate in the middle of a UTF-8 character
+ { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--;}
+ break;
+
+ case 'S': { // UTF-16 string
+ unsigned char *a = va_arg(arg, unsigned char *);
+ uint16_t *u = (uint16_t*)a;
+ if (!u) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
+ if ((!F.havePrecision || F.precision))
+ {
+ if ((a[0] == 0xFE) && (a[1] == 0xFF)) { F.altForm = 1; u += 1; a += 2; F.precision--; } // Big Endian
+ else if ((a[0] == 0xFF) && (a[1] == 0xFE)) { F.altForm = 2; u += 1; a += 2; F.precision--; } // Little Endian
+ }
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ switch (F.altForm)
+ {
+ case 0: while ((!F.havePrecision || (i < F.precision)) && u[i] && mDNS_VACB_Remain(s)) // Host Endian
+ { c = u[i]; *s++ = (char)(DebugIsPrint(c) ? c : '^'); i++; }
+ break;
+ case 1: while ((!F.havePrecision || (i < F.precision)) && u[i] && mDNS_VACB_Remain(s)) // Big Endian
+ { c = ((a[0] << 8) | a[1]) & 0xFF; *s++ = (char)(DebugIsPrint(c) ? c : '^'); i++; a += 2; }
+ break;
+ case 2: while ((!F.havePrecision || (i < F.precision)) && u[i] && mDNS_VACB_Remain(s)) // Little Endian
+ { c = ((a[1] << 8) | a[0]) & 0xFF; *s++ = (char)(DebugIsPrint(c) ? c : '^'); i++; a += 2; }
+ break;
+ }
+ }
+ s = mDNS_VACB; // Reset s back to the start of the buffer
+ break;
+
+ #if TARGET_OS_MAC
+ case '@': { // Cocoa/CoreFoundation object
+ CFTypeRef cfObj;
+ CFStringRef cfStr;
+ cfObj = (CFTypeRef) va_arg(arg, void *);
+ cfStr = (CFGetTypeID(cfObj) == CFStringGetTypeID()) ? (CFStringRef)CFRetain(cfObj) : CFCopyDescription(cfObj);
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ if (cfStr)
+ {
+ CFRange range;
+ CFIndex m;
+ range = CFRangeMake(0, CFStringGetLength(cfStr));
+ m = 0;
+ CFStringGetBytes(cfStr, range, kCFStringEncodingUTF8, '^', false, (UInt8*)mDNS_VACB, (CFIndex)sizeof(mDNS_VACB), &m);
+ CFRelease(cfStr);
+ i = (size_t) m;
+ }
+ else
+ {
+ i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%s", "ERROR: <invalid CF object>" );
+ }
+ }
+ if (F.havePrecision && i > F.precision) // Make sure we don't truncate in the middle of a UTF-8 character
+ { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--;}
+ break;
+ #endif
+
+ case 'm': { // Error Message
+ long err;
+ if (F.lSize) err = va_arg(arg, long);
+ else err = va_arg(arg, int);
+ if (F.hSize) err = (short)err;
+ DebugGetErrorString(err, mDNS_VACB, sizeof(mDNS_VACB));
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ for(i=0; s[i]; i++) {}
+ }
+ break;
+
+ case 'H': { // Hex Dump
+ void *a = va_arg(arg, void *);
+ size_t size = (size_t)va_arg(arg, int);
+ size_t max = (size_t)va_arg(arg, int);
+ DebugFlags flags =
+ kDebugFlagsNoAddress | kDebugFlagsNoOffset | kDebugFlagsNoNewLine |
+ kDebugFlags8BitSeparator | kDebugFlagsNo32BitSeparator |
+ kDebugFlagsNo16ByteHexPad | kDebugFlagsNoByteCount;
+ if (F.altForm == 0) flags |= kDebugFlagsNoASCII;
+ size = (max < size) ? max : size;
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ i = DebugHexDump(kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, a, a, size, flags, mDNS_VACB, sizeof(mDNS_VACB));
+ }
+ break;
+
+ case 'v': { // Version
+ uint32_t version;
+ version = va_arg(arg, unsigned int);
+ DebugNumVersionToString(version, mDNS_VACB);
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ for(i=0; s[i]; i++) {}
+ }
+ break;
+
+ case 'n': s = va_arg(arg, char *);
+ if (F.hSize) *(short *) s = (short)nwritten;
+ else if (F.lSize) *(long *) s = (long)nwritten;
+ else *(int *) s = (int)nwritten;
+ continue;
+
+ default: s = mDNS_VACB;
+ i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c);
+
+ case '%': *sbuffer++ = (char)c;
+ if (++nwritten >= buflen) goto exit;
+ break;
+ }
+
+ if (i < F.fieldWidth && !F.leftJustify) // Pad on the left
+ do {
+ *sbuffer++ = ' ';
+ if (++nwritten >= buflen) goto exit;
+ } while (i < --F.fieldWidth);
+
+ if (i > buflen - nwritten) // Make sure we don't truncate in the middle of a UTF-8 character
+ { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--;}
+ for (j=0; j<i; j++) *sbuffer++ = *s++; // Write the converted result
+ nwritten += i;
+ if (nwritten >= buflen) goto exit;
+
+ for (; i < F.fieldWidth; i++) // Pad on the right
+ {
+ *sbuffer++ = ' ';
+ if (++nwritten >= buflen) goto exit;
+ }
+ }
+ }
+exit:
+ *sbuffer++ = 0;
+ return(nwritten);
+}
+
+//===========================================================================================================================
+// DebugGetErrorString
+//===========================================================================================================================
+
+DEBUG_EXPORT const char * DebugGetErrorString( int_least32_t inErrorCode, char *inBuffer, size_t inBufferSize )
+{
+ const char * s;
+ char * dst;
+ char * end;
+#if ( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+ char buffer[ 256 ];
+#endif
+
+ switch( inErrorCode )
+ {
+ #define CaseErrorString( X, STR ) case X: s = STR; break
+ #define CaseErrorStringify( X ) case X: s = # X; break
+ #define CaseErrorStringifyHardCode( VALUE, X ) case VALUE: s = # X; break
+
+ // General Errors
+
+ CaseErrorString( 0, "no error" );
+ CaseErrorString( 1, "in-progress/waiting" );
+ CaseErrorString( -1, "catch-all unknown error" );
+
+ // ACP Errors
+
+ CaseErrorStringifyHardCode( -2, kACPBadRequestErr );
+ CaseErrorStringifyHardCode( -3, kACPNoMemoryErr );
+ CaseErrorStringifyHardCode( -4, kACPBadParamErr );
+ CaseErrorStringifyHardCode( -5, kACPNotFoundErr );
+ CaseErrorStringifyHardCode( -6, kACPBadChecksumErr );
+ CaseErrorStringifyHardCode( -7, kACPCommandNotHandledErr );
+ CaseErrorStringifyHardCode( -8, kACPNetworkErr );
+ CaseErrorStringifyHardCode( -9, kACPDuplicateCommandHandlerErr );
+ CaseErrorStringifyHardCode( -10, kACPUnknownPropertyErr );
+ CaseErrorStringifyHardCode( -11, kACPImmutablePropertyErr );
+ CaseErrorStringifyHardCode( -12, kACPBadPropertyValueErr );
+ CaseErrorStringifyHardCode( -13, kACPNoResourcesErr );
+ CaseErrorStringifyHardCode( -14, kACPBadOptionErr );
+ CaseErrorStringifyHardCode( -15, kACPBadSizeErr );
+ CaseErrorStringifyHardCode( -16, kACPBadPasswordErr );
+ CaseErrorStringifyHardCode( -17, kACPNotInitializedErr );
+ CaseErrorStringifyHardCode( -18, kACPNonReadablePropertyErr );
+ CaseErrorStringifyHardCode( -19, kACPBadVersionErr );
+ CaseErrorStringifyHardCode( -20, kACPBadSignatureErr );
+ CaseErrorStringifyHardCode( -21, kACPBadIndexErr );
+ CaseErrorStringifyHardCode( -22, kACPUnsupportedErr );
+ CaseErrorStringifyHardCode( -23, kACPInUseErr );
+ CaseErrorStringifyHardCode( -24, kACPParamCountErr );
+ CaseErrorStringifyHardCode( -25, kACPIDErr );
+ CaseErrorStringifyHardCode( -26, kACPFormatErr );
+ CaseErrorStringifyHardCode( -27, kACPUnknownUserErr );
+ CaseErrorStringifyHardCode( -28, kACPAccessDeniedErr );
+ CaseErrorStringifyHardCode( -29, kACPIncorrectFWErr );
+
+ // Common Services Errors
+
+ CaseErrorStringify( kUnknownErr );
+ CaseErrorStringify( kOptionErr );
+ CaseErrorStringify( kSelectorErr );
+ CaseErrorStringify( kExecutionStateErr );
+ CaseErrorStringify( kPathErr );
+ CaseErrorStringify( kParamErr );
+ CaseErrorStringify( kParamCountErr );
+ CaseErrorStringify( kCommandErr );
+ CaseErrorStringify( kIDErr );
+ CaseErrorStringify( kStateErr );
+ CaseErrorStringify( kRangeErr );
+ CaseErrorStringify( kRequestErr );
+ CaseErrorStringify( kResponseErr );
+ CaseErrorStringify( kChecksumErr );
+ CaseErrorStringify( kNotHandledErr );
+ CaseErrorStringify( kVersionErr );
+ CaseErrorStringify( kSignatureErr );
+ CaseErrorStringify( kFormatErr );
+ CaseErrorStringify( kNotInitializedErr );
+ CaseErrorStringify( kAlreadyInitializedErr );
+ CaseErrorStringify( kNotInUseErr );
+ CaseErrorStringify( kInUseErr );
+ CaseErrorStringify( kTimeoutErr );
+ CaseErrorStringify( kCanceledErr );
+ CaseErrorStringify( kAlreadyCanceledErr );
+ CaseErrorStringify( kCannotCancelErr );
+ CaseErrorStringify( kDeletedErr );
+ CaseErrorStringify( kNotFoundErr );
+ CaseErrorStringify( kNoMemoryErr );
+ CaseErrorStringify( kNoResourcesErr );
+ CaseErrorStringify( kDuplicateErr );
+ CaseErrorStringify( kImmutableErr );
+ CaseErrorStringify( kUnsupportedDataErr );
+ CaseErrorStringify( kIntegrityErr );
+ CaseErrorStringify( kIncompatibleErr );
+ CaseErrorStringify( kUnsupportedErr );
+ CaseErrorStringify( kUnexpectedErr );
+ CaseErrorStringify( kValueErr );
+ CaseErrorStringify( kNotReadableErr );
+ CaseErrorStringify( kNotWritableErr );
+ CaseErrorStringify( kBadReferenceErr );
+ CaseErrorStringify( kFlagErr );
+ CaseErrorStringify( kMalformedErr );
+ CaseErrorStringify( kSizeErr );
+ CaseErrorStringify( kNameErr );
+ CaseErrorStringify( kNotReadyErr );
+ CaseErrorStringify( kReadErr );
+ CaseErrorStringify( kWriteErr );
+ CaseErrorStringify( kMismatchErr );
+ CaseErrorStringify( kDateErr );
+ CaseErrorStringify( kUnderrunErr );
+ CaseErrorStringify( kOverrunErr );
+ CaseErrorStringify( kEndingErr );
+ CaseErrorStringify( kConnectionErr );
+ CaseErrorStringify( kAuthenticationErr );
+ CaseErrorStringify( kOpenErr );
+ CaseErrorStringify( kTypeErr );
+ CaseErrorStringify( kSkipErr );
+ CaseErrorStringify( kNoAckErr );
+ CaseErrorStringify( kCollisionErr );
+ CaseErrorStringify( kBackoffErr );
+ CaseErrorStringify( kNoAddressAckErr );
+ CaseErrorStringify( kBusyErr );
+ CaseErrorStringify( kNoSpaceErr );
+
+ // mDNS/DNS-SD Errors
+
+ CaseErrorStringifyHardCode( -65537, mStatus_UnknownErr );
+ CaseErrorStringifyHardCode( -65538, mStatus_NoSuchNameErr );
+ CaseErrorStringifyHardCode( -65539, mStatus_NoMemoryErr );
+ CaseErrorStringifyHardCode( -65540, mStatus_BadParamErr );
+ CaseErrorStringifyHardCode( -65541, mStatus_BadReferenceErr );
+ CaseErrorStringifyHardCode( -65542, mStatus_BadStateErr );
+ CaseErrorStringifyHardCode( -65543, mStatus_BadFlagsErr );
+ CaseErrorStringifyHardCode( -65544, mStatus_UnsupportedErr );
+ CaseErrorStringifyHardCode( -65545, mStatus_NotInitializedErr );
+ CaseErrorStringifyHardCode( -65546, mStatus_NoCache );
+ CaseErrorStringifyHardCode( -65547, mStatus_AlreadyRegistered );
+ CaseErrorStringifyHardCode( -65548, mStatus_NameConflict );
+ CaseErrorStringifyHardCode( -65549, mStatus_Invalid );
+ CaseErrorStringifyHardCode( -65550, mStatus_GrowCache );
+ CaseErrorStringifyHardCode( -65551, mStatus_BadInterfaceErr );
+ CaseErrorStringifyHardCode( -65552, mStatus_Incompatible );
+ CaseErrorStringifyHardCode( -65791, mStatus_ConfigChanged );
+ CaseErrorStringifyHardCode( -65792, mStatus_MemFree );
+
+ // RSP Errors
+
+ CaseErrorStringifyHardCode( -400000, kRSPUnknownErr );
+ CaseErrorStringifyHardCode( -400050, kRSPParamErr );
+ CaseErrorStringifyHardCode( -400108, kRSPNoMemoryErr );
+ CaseErrorStringifyHardCode( -405246, kRSPRangeErr );
+ CaseErrorStringifyHardCode( -409057, kRSPSizeErr );
+ CaseErrorStringifyHardCode( -400200, kRSPHardwareErr );
+ CaseErrorStringifyHardCode( -401712, kRSPTimeoutErr );
+ CaseErrorStringifyHardCode( -402053, kRSPUnsupportedErr );
+ CaseErrorStringifyHardCode( -402419, kRSPIDErr );
+ CaseErrorStringifyHardCode( -403165, kRSPFlagErr );
+ CaseErrorString( -200000, "kRSPControllerStatusBase - 0x50" );
+ CaseErrorString( -200080, "kRSPCommandSucceededErr - 0x50" );
+ CaseErrorString( -200001, "kRSPCommandFailedErr - 0x01" );
+ CaseErrorString( -200051, "kRSPChecksumErr - 0x33" );
+ CaseErrorString( -200132, "kRSPCommandTimeoutErr - 0x84" );
+ CaseErrorString( -200034, "kRSPPasswordRequiredErr - 0x22 OBSOLETE" );
+ CaseErrorString( -200128, "kRSPCanceledErr - 0x02 Async" );
+
+ // XML Errors
+
+ CaseErrorStringifyHardCode( -100043, kXMLNotFoundErr );
+ CaseErrorStringifyHardCode( -100050, kXMLParamErr );
+ CaseErrorStringifyHardCode( -100108, kXMLNoMemoryErr );
+ CaseErrorStringifyHardCode( -100206, kXMLFormatErr );
+ CaseErrorStringifyHardCode( -100586, kXMLNoRootElementErr );
+ CaseErrorStringifyHardCode( -101703, kXMLWrongDataTypeErr );
+ CaseErrorStringifyHardCode( -101726, kXMLKeyErr );
+ CaseErrorStringifyHardCode( -102053, kXMLUnsupportedErr );
+ CaseErrorStringifyHardCode( -102063, kXMLMissingElementErr );
+ CaseErrorStringifyHardCode( -103026, kXMLParseErr );
+ CaseErrorStringifyHardCode( -103159, kXMLBadDataErr );
+ CaseErrorStringifyHardCode( -103170, kXMLBadNameErr );
+ CaseErrorStringifyHardCode( -105246, kXMLRangeErr );
+ CaseErrorStringifyHardCode( -105251, kXMLUnknownElementErr );
+ CaseErrorStringifyHardCode( -108739, kXMLMalformedInputErr );
+ CaseErrorStringifyHardCode( -109057, kXMLBadSizeErr );
+ CaseErrorStringifyHardCode( -101730, kXMLMissingChildElementErr );
+ CaseErrorStringifyHardCode( -102107, kXMLMissingParentElementErr );
+ CaseErrorStringifyHardCode( -130587, kXMLNonRootElementErr );
+ CaseErrorStringifyHardCode( -102015, kXMLDateErr );
+
+ #if ( __MACH__ )
+
+ // Mach Errors
+
+ CaseErrorStringifyHardCode( 0x00002000, MACH_MSG_IPC_SPACE );
+ CaseErrorStringifyHardCode( 0x00001000, MACH_MSG_VM_SPACE );
+ CaseErrorStringifyHardCode( 0x00000800, MACH_MSG_IPC_KERNEL );
+ CaseErrorStringifyHardCode( 0x00000400, MACH_MSG_VM_KERNEL );
+ CaseErrorStringifyHardCode( 0x10000001, MACH_SEND_IN_PROGRESS );
+ CaseErrorStringifyHardCode( 0x10000002, MACH_SEND_INVALID_DATA );
+ CaseErrorStringifyHardCode( 0x10000003, MACH_SEND_INVALID_DEST );
+ CaseErrorStringifyHardCode( 0x10000004, MACH_SEND_TIMED_OUT );
+ CaseErrorStringifyHardCode( 0x10000007, MACH_SEND_INTERRUPTED );
+ CaseErrorStringifyHardCode( 0x10000008, MACH_SEND_MSG_TOO_SMALL );
+ CaseErrorStringifyHardCode( 0x10000009, MACH_SEND_INVALID_REPLY );
+ CaseErrorStringifyHardCode( 0x1000000A, MACH_SEND_INVALID_RIGHT );
+ CaseErrorStringifyHardCode( 0x1000000B, MACH_SEND_INVALID_NOTIFY );
+ CaseErrorStringifyHardCode( 0x1000000C, MACH_SEND_INVALID_MEMORY );
+ CaseErrorStringifyHardCode( 0x1000000D, MACH_SEND_NO_BUFFER );
+ CaseErrorStringifyHardCode( 0x1000000E, MACH_SEND_TOO_LARGE );
+ CaseErrorStringifyHardCode( 0x1000000F, MACH_SEND_INVALID_TYPE );
+ CaseErrorStringifyHardCode( 0x10000010, MACH_SEND_INVALID_HEADER );
+ CaseErrorStringifyHardCode( 0x10000011, MACH_SEND_INVALID_TRAILER );
+ CaseErrorStringifyHardCode( 0x10000015, MACH_SEND_INVALID_RT_OOL_SIZE );
+ CaseErrorStringifyHardCode( 0x10004001, MACH_RCV_IN_PROGRESS );
+ CaseErrorStringifyHardCode( 0x10004002, MACH_RCV_INVALID_NAME );
+ CaseErrorStringifyHardCode( 0x10004003, MACH_RCV_TIMED_OUT );
+ CaseErrorStringifyHardCode( 0x10004004, MACH_RCV_TOO_LARGE );
+ CaseErrorStringifyHardCode( 0x10004005, MACH_RCV_INTERRUPTED );
+ CaseErrorStringifyHardCode( 0x10004006, MACH_RCV_PORT_CHANGED );
+ CaseErrorStringifyHardCode( 0x10004007, MACH_RCV_INVALID_NOTIFY );
+ CaseErrorStringifyHardCode( 0x10004008, MACH_RCV_INVALID_DATA );
+ CaseErrorStringifyHardCode( 0x10004009, MACH_RCV_PORT_DIED );
+ CaseErrorStringifyHardCode( 0x1000400A, MACH_RCV_IN_SET );
+ CaseErrorStringifyHardCode( 0x1000400B, MACH_RCV_HEADER_ERROR );
+ CaseErrorStringifyHardCode( 0x1000400C, MACH_RCV_BODY_ERROR );
+ CaseErrorStringifyHardCode( 0x1000400D, MACH_RCV_INVALID_TYPE );
+ CaseErrorStringifyHardCode( 0x1000400E, MACH_RCV_SCATTER_SMALL );
+ CaseErrorStringifyHardCode( 0x1000400F, MACH_RCV_INVALID_TRAILER );
+ CaseErrorStringifyHardCode( 0x10004011, MACH_RCV_IN_PROGRESS_TIMED );
+
+ // Mach OSReturn Errors
+
+ CaseErrorStringifyHardCode( 0xDC000001, kOSReturnError );
+ CaseErrorStringifyHardCode( 0xDC004001, kOSMetaClassInternal );
+ CaseErrorStringifyHardCode( 0xDC004002, kOSMetaClassHasInstances );
+ CaseErrorStringifyHardCode( 0xDC004003, kOSMetaClassNoInit );
+ CaseErrorStringifyHardCode( 0xDC004004, kOSMetaClassNoTempData );
+ CaseErrorStringifyHardCode( 0xDC004005, kOSMetaClassNoDicts );
+ CaseErrorStringifyHardCode( 0xDC004006, kOSMetaClassNoKModSet );
+ CaseErrorStringifyHardCode( 0xDC004007, kOSMetaClassNoInsKModSet );
+ CaseErrorStringifyHardCode( 0xDC004008, kOSMetaClassNoSuper );
+ CaseErrorStringifyHardCode( 0xDC004009, kOSMetaClassInstNoSuper );
+ CaseErrorStringifyHardCode( 0xDC00400A, kOSMetaClassDuplicateClass );
+
+ // IOKit Errors
+
+ CaseErrorStringifyHardCode( 0xE00002BC, kIOReturnError );
+ CaseErrorStringifyHardCode( 0xE00002BD, kIOReturnNoMemory );
+ CaseErrorStringifyHardCode( 0xE00002BE, kIOReturnNoResources );
+ CaseErrorStringifyHardCode( 0xE00002BF, kIOReturnIPCError );
+ CaseErrorStringifyHardCode( 0xE00002C0, kIOReturnNoDevice );
+ CaseErrorStringifyHardCode( 0xE00002C1, kIOReturnNotPrivileged );
+ CaseErrorStringifyHardCode( 0xE00002C2, kIOReturnBadArgument );
+ CaseErrorStringifyHardCode( 0xE00002C3, kIOReturnLockedRead );
+ CaseErrorStringifyHardCode( 0xE00002C4, kIOReturnLockedWrite );
+ CaseErrorStringifyHardCode( 0xE00002C5, kIOReturnExclusiveAccess );
+ CaseErrorStringifyHardCode( 0xE00002C6, kIOReturnBadMessageID );
+ CaseErrorStringifyHardCode( 0xE00002C7, kIOReturnUnsupported );
+ CaseErrorStringifyHardCode( 0xE00002C8, kIOReturnVMError );
+ CaseErrorStringifyHardCode( 0xE00002C9, kIOReturnInternalError );
+ CaseErrorStringifyHardCode( 0xE00002CA, kIOReturnIOError );
+ CaseErrorStringifyHardCode( 0xE00002CC, kIOReturnCannotLock );
+ CaseErrorStringifyHardCode( 0xE00002CD, kIOReturnNotOpen );
+ CaseErrorStringifyHardCode( 0xE00002CE, kIOReturnNotReadable );
+ CaseErrorStringifyHardCode( 0xE00002CF, kIOReturnNotWritable );
+ CaseErrorStringifyHardCode( 0xE00002D0, kIOReturnNotAligned );
+ CaseErrorStringifyHardCode( 0xE00002D1, kIOReturnBadMedia );
+ CaseErrorStringifyHardCode( 0xE00002D2, kIOReturnStillOpen );
+ CaseErrorStringifyHardCode( 0xE00002D3, kIOReturnRLDError );
+ CaseErrorStringifyHardCode( 0xE00002D4, kIOReturnDMAError );
+ CaseErrorStringifyHardCode( 0xE00002D5, kIOReturnBusy );
+ CaseErrorStringifyHardCode( 0xE00002D6, kIOReturnTimeout );
+ CaseErrorStringifyHardCode( 0xE00002D7, kIOReturnOffline );
+ CaseErrorStringifyHardCode( 0xE00002D8, kIOReturnNotReady );
+ CaseErrorStringifyHardCode( 0xE00002D9, kIOReturnNotAttached );
+ CaseErrorStringifyHardCode( 0xE00002DA, kIOReturnNoChannels );
+ CaseErrorStringifyHardCode( 0xE00002DB, kIOReturnNoSpace );
+ CaseErrorStringifyHardCode( 0xE00002DD, kIOReturnPortExists );
+ CaseErrorStringifyHardCode( 0xE00002DE, kIOReturnCannotWire );
+ CaseErrorStringifyHardCode( 0xE00002DF, kIOReturnNoInterrupt );
+ CaseErrorStringifyHardCode( 0xE00002E0, kIOReturnNoFrames );
+ CaseErrorStringifyHardCode( 0xE00002E1, kIOReturnMessageTooLarge );
+ CaseErrorStringifyHardCode( 0xE00002E2, kIOReturnNotPermitted );
+ CaseErrorStringifyHardCode( 0xE00002E3, kIOReturnNoPower );
+ CaseErrorStringifyHardCode( 0xE00002E4, kIOReturnNoMedia );
+ CaseErrorStringifyHardCode( 0xE00002E5, kIOReturnUnformattedMedia );
+ CaseErrorStringifyHardCode( 0xE00002E6, kIOReturnUnsupportedMode );
+ CaseErrorStringifyHardCode( 0xE00002E7, kIOReturnUnderrun );
+ CaseErrorStringifyHardCode( 0xE00002E8, kIOReturnOverrun );
+ CaseErrorStringifyHardCode( 0xE00002E9, kIOReturnDeviceError );
+ CaseErrorStringifyHardCode( 0xE00002EA, kIOReturnNoCompletion );
+ CaseErrorStringifyHardCode( 0xE00002EB, kIOReturnAborted );
+ CaseErrorStringifyHardCode( 0xE00002EC, kIOReturnNoBandwidth );
+ CaseErrorStringifyHardCode( 0xE00002ED, kIOReturnNotResponding );
+ CaseErrorStringifyHardCode( 0xE00002EE, kIOReturnIsoTooOld );
+ CaseErrorStringifyHardCode( 0xE00002EF, kIOReturnIsoTooNew );
+ CaseErrorStringifyHardCode( 0xE00002F0, kIOReturnNotFound );
+ CaseErrorStringifyHardCode( 0xE0000001, kIOReturnInvalid );
+
+ // IOKit FireWire Errors
+
+ CaseErrorStringifyHardCode( 0xE0008010, kIOFireWireResponseBase );
+ CaseErrorStringifyHardCode( 0xE0008020, kIOFireWireBusReset );
+ CaseErrorStringifyHardCode( 0xE0008001, kIOConfigNoEntry );
+ CaseErrorStringifyHardCode( 0xE0008002, kIOFireWirePending );
+ CaseErrorStringifyHardCode( 0xE0008003, kIOFireWireLastDCLToken );
+ CaseErrorStringifyHardCode( 0xE0008004, kIOFireWireConfigROMInvalid );
+ CaseErrorStringifyHardCode( 0xE0008005, kIOFireWireAlreadyRegistered );
+ CaseErrorStringifyHardCode( 0xE0008006, kIOFireWireMultipleTalkers );
+ CaseErrorStringifyHardCode( 0xE0008007, kIOFireWireChannelActive );
+ CaseErrorStringifyHardCode( 0xE0008008, kIOFireWireNoListenerOrTalker );
+ CaseErrorStringifyHardCode( 0xE0008009, kIOFireWireNoChannels );
+ CaseErrorStringifyHardCode( 0xE000800A, kIOFireWireChannelNotAvailable );
+ CaseErrorStringifyHardCode( 0xE000800B, kIOFireWireSeparateBus );
+ CaseErrorStringifyHardCode( 0xE000800C, kIOFireWireBadSelfIDs );
+ CaseErrorStringifyHardCode( 0xE000800D, kIOFireWireLowCableVoltage );
+ CaseErrorStringifyHardCode( 0xE000800E, kIOFireWireInsufficientPower );
+ CaseErrorStringifyHardCode( 0xE000800F, kIOFireWireOutOfTLabels );
+ CaseErrorStringifyHardCode( 0xE0008101, kIOFireWireBogusDCLProgram );
+ CaseErrorStringifyHardCode( 0xE0008102, kIOFireWireTalkingAndListening );
+ CaseErrorStringifyHardCode( 0xE0008103, kIOFireWireHardwareSlept );
+ CaseErrorStringifyHardCode( 0xE00087D0, kIOFWMessageServiceIsRequestingClose );
+ CaseErrorStringifyHardCode( 0xE00087D1, kIOFWMessagePowerStateChanged );
+ CaseErrorStringifyHardCode( 0xE00087D2, kIOFWMessageTopologyChanged );
+
+ // IOKit USB Errors
+
+ CaseErrorStringifyHardCode( 0xE0004061, kIOUSBUnknownPipeErr );
+ CaseErrorStringifyHardCode( 0xE0004060, kIOUSBTooManyPipesErr );
+ CaseErrorStringifyHardCode( 0xE000405F, kIOUSBNoAsyncPortErr );
+ CaseErrorStringifyHardCode( 0xE000405E, kIOUSBNotEnoughPipesErr );
+ CaseErrorStringifyHardCode( 0xE000405D, kIOUSBNotEnoughPowerErr );
+ CaseErrorStringifyHardCode( 0xE0004057, kIOUSBEndpointNotFound );
+ CaseErrorStringifyHardCode( 0xE0004056, kIOUSBConfigNotFound );
+ CaseErrorStringifyHardCode( 0xE0004051, kIOUSBTransactionTimeout );
+ CaseErrorStringifyHardCode( 0xE0004050, kIOUSBTransactionReturned );
+ CaseErrorStringifyHardCode( 0xE000404F, kIOUSBPipeStalled );
+ CaseErrorStringifyHardCode( 0xE000404E, kIOUSBInterfaceNotFound );
+ CaseErrorStringifyHardCode( 0xE000404D, kIOUSBLowLatencyBufferNotPreviouslyAllocated );
+ CaseErrorStringifyHardCode( 0xE000404C, kIOUSBLowLatencyFrameListNotPreviouslyAllocated );
+ CaseErrorStringifyHardCode( 0xE000404B, kIOUSBHighSpeedSplitError );
+ CaseErrorStringifyHardCode( 0xE0004010, kIOUSBLinkErr );
+ CaseErrorStringifyHardCode( 0xE000400F, kIOUSBNotSent2Err );
+ CaseErrorStringifyHardCode( 0xE000400E, kIOUSBNotSent1Err );
+ CaseErrorStringifyHardCode( 0xE000400D, kIOUSBBufferUnderrunErr );
+ CaseErrorStringifyHardCode( 0xE000400C, kIOUSBBufferOverrunErr );
+ CaseErrorStringifyHardCode( 0xE000400B, kIOUSBReserved2Err );
+ CaseErrorStringifyHardCode( 0xE000400A, kIOUSBReserved1Err );
+ CaseErrorStringifyHardCode( 0xE0004007, kIOUSBWrongPIDErr );
+ CaseErrorStringifyHardCode( 0xE0004006, kIOUSBPIDCheckErr );
+ CaseErrorStringifyHardCode( 0xE0004003, kIOUSBDataToggleErr );
+ CaseErrorStringifyHardCode( 0xE0004002, kIOUSBBitstufErr );
+ CaseErrorStringifyHardCode( 0xE0004001, kIOUSBCRCErr );
+
+ #endif // __MACH__
+
+ // Other Errors
+
+ default:
+ s = NULL;
+ #if ( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+ if( inBuffer && ( inBufferSize > 0 ) )
+ {
+ DWORD n;
+
+ n = FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, (DWORD) inErrorCode,
+ MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), buffer, sizeof( buffer ), NULL );
+ if( n > 0 )
+ {
+ // Remove any trailing CR's or LF's since some messages have them.
+
+ while( ( n > 0 ) && isspace( ( (unsigned char *) buffer )[ n - 1 ] ) )
+ {
+ buffer[ --n ] = '\0';
+ }
+ s = buffer;
+ }
+ }
+ #endif
+
+ if( !s )
+ {
+ #if ( !TARGET_API_MAC_OSX_KERNEL && !TARGET_OS_WINDOWS_CE )
+ s = strerror( inErrorCode );
+ #endif
+ if( !s )
+ {
+ s = "<unknown error code>";
+ }
+ }
+ break;
+ }
+
+ // Copy the string to the output buffer. If no buffer is supplied or it is empty, return an empty string.
+
+ if( inBuffer && ( inBufferSize > 0 ) )
+ {
+ dst = inBuffer;
+ end = dst + ( inBufferSize - 1 );
+ while( ( ( end - dst ) > 0 ) && ( *s != '\0' ) )
+ {
+ *dst++ = *s++;
+ }
+ *dst = '\0';
+ s = inBuffer;
+ }
+ return( s );
+}
+
+//===========================================================================================================================
+// DebugHexDump
+//===========================================================================================================================
+
+DEBUG_EXPORT size_t
+DebugHexDump(
+ DebugLevel inLevel,
+ int inIndent,
+ const char * inLabel,
+ size_t inLabelSize,
+ int inLabelMinWidth,
+ const char * inType,
+ size_t inTypeSize,
+ const void * inDataStart,
+ const void * inData,
+ size_t inDataSize,
+ DebugFlags inFlags,
+ char * outBuffer,
+ size_t inBufferSize )
+{
+ static const char kHexChars[] = "0123456789ABCDEF";
+ const uint8_t * start;
+ const uint8_t * src;
+ char * dst;
+ char * end;
+ size_t n;
+ int offset;
+ int width;
+ const char * newline;
+ char separator[ 8 ];
+ char * s;
+
+ DEBUG_UNUSED( inType );
+ DEBUG_UNUSED( inTypeSize );
+
+ // Set up the function-wide variables.
+
+ if( inLabelSize == kSizeCString )
+ {
+ inLabelSize = strlen( inLabel );
+ }
+ start = (const uint8_t *) inData;
+ src = start;
+ dst = outBuffer;
+ end = dst + inBufferSize;
+ offset = (int)( (intptr_t) inData - (intptr_t) inDataStart );
+ width = ( (int) inLabelSize > inLabelMinWidth ) ? (int) inLabelSize : inLabelMinWidth;
+ newline = ( inFlags & kDebugFlagsNoNewLine ) ? "" : "\n";
+
+ // Set up the separator string. This is used to insert spaces on subsequent "lines" when not using newlines.
+
+ s = separator;
+ if( inFlags & kDebugFlagsNoNewLine )
+ {
+ if( inFlags & kDebugFlags8BitSeparator )
+ {
+ *s++ = ' ';
+ }
+ if( inFlags & kDebugFlags16BitSeparator )
+ {
+ *s++ = ' ';
+ }
+ if( !( inFlags & kDebugFlagsNo32BitSeparator ) )
+ {
+ *s++ = ' ';
+ }
+ check( ( (size_t)( s - separator ) ) < sizeof( separator ) );
+ }
+ *s = '\0';
+
+ for( ;; )
+ {
+ char prefixString[ 32 ];
+ char hexString[ 64 ];
+ char asciiString[ 32 ];
+ char byteCountString[ 32 ];
+ int c;
+ size_t chunkSize;
+ size_t i;
+
+ // If this is a label-only item (i.e. no data), print the label (accounting for prefix string spacing) and exit.
+
+ if( inDataSize == 0 )
+ {
+ if( inLabel && ( inLabelSize > 0 ) )
+ {
+ width = 0;
+ if( !( inFlags & kDebugFlagsNoAddress ) )
+ {
+ width += 8; // "00000000"
+ if( !( inFlags & kDebugFlagsNoOffset ) )
+ {
+ width += 1; // "+"
+ }
+ }
+ if( inFlags & kDebugFlags32BitOffset )
+ {
+ width += 8; // "00000000"
+ }
+ else if( !( inFlags & kDebugFlagsNoOffset ) )
+ {
+ width += 4; // "0000"
+ }
+
+ if( outBuffer )
+ {
+ dst += DebugSNPrintF( dst, (size_t)( end - dst ), "%*s" "%-*.*s" "%.*s" "%s",
+ width, "",
+ ( width > 0 ) ? ": " : "",
+ width, (int) inLabelSize, inLabel,
+ newline );
+ }
+ else
+ {
+ dst += DebugPrintF( inLevel, "%*s" "%-*.*s" "%.*s" "%s",
+ width, "",
+ ( width > 0 ) ? ": " : "",
+ width, (int) inLabelSize, inLabel,
+ newline );
+ }
+ }
+ break;
+ }
+
+ // Build the prefix string. It will be in one of the following formats:
+ //
+ // 1) "00000000+0000[0000]" (address and offset)
+ // 2) "00000000" (address only)
+ // 3) "0000[0000]" (offset only)
+ // 4) "" (no address or offset)
+ //
+ // Note: If we're printing multiple "lines", but not printing newlines, a space is used to separate.
+
+ s = prefixString;
+ if( !( inFlags & kDebugFlagsNoAddress ) )
+ {
+ *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 28 ) & 0xF ];
+ *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 24 ) & 0xF ];
+ *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 20 ) & 0xF ];
+ *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 16 ) & 0xF ];
+ *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 12 ) & 0xF ];
+ *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 8 ) & 0xF ];
+ *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 4 ) & 0xF ];
+ *s++ = kHexChars[ ( (uintptr_t) src ) & 0xF ];
+
+ if( !( inFlags & kDebugFlagsNoOffset ) )
+ {
+ *s++ = '+';
+ }
+ }
+ if( !( inFlags & kDebugFlagsNoOffset ) )
+ {
+ if( inFlags & kDebugFlags32BitOffset )
+ {
+ *s++ = kHexChars[ ( offset >> 28 ) & 0xF ];
+ *s++ = kHexChars[ ( offset >> 24 ) & 0xF ];
+ *s++ = kHexChars[ ( offset >> 20 ) & 0xF ];
+ *s++ = kHexChars[ ( offset >> 16 ) & 0xF ];
+ }
+ *s++ = kHexChars[ ( offset >> 12 ) & 0xF ];
+ *s++ = kHexChars[ ( offset >> 8 ) & 0xF ];
+ *s++ = kHexChars[ ( offset >> 4 ) & 0xF ];
+ *s++ = kHexChars[ offset & 0xF ];
+ }
+ if( s != prefixString )
+ {
+ *s++ = ':';
+ *s++ = ' ';
+ }
+ check( ( (size_t)( s - prefixString ) ) < sizeof( prefixString ) );
+ *s = '\0';
+
+ // Build a hex string with a optional spaces after every 1, 2, and/or 4 bytes to make it easier to read.
+ // Optionally pads the hex string with space to fill the full 16 byte range (so it lines up).
+
+ s = hexString;
+ chunkSize = ( inDataSize < 16 ) ? inDataSize : 16;
+ n = ( inFlags & kDebugFlagsNo16ByteHexPad ) ? chunkSize : 16;
+ for( i = 0; i < n; ++i )
+ {
+ if( ( inFlags & kDebugFlags8BitSeparator ) && ( i > 0 ) )
+ {
+ *s++ = ' ';
+ }
+ if( ( inFlags & kDebugFlags16BitSeparator ) && ( i > 0 ) && ( ( i % 2 ) == 0 ) )
+ {
+ *s++ = ' ';
+ }
+ if( !( inFlags & kDebugFlagsNo32BitSeparator ) && ( i > 0 ) && ( ( i % 4 ) == 0 ) )
+ {
+ *s++ = ' ';
+ }
+ if( i < chunkSize )
+ {
+ *s++ = kHexChars[ src[ i ] >> 4 ];
+ *s++ = kHexChars[ src[ i ] & 0xF ];
+ }
+ else
+ {
+ *s++ = ' ';
+ *s++ = ' ';
+ }
+ }
+ check( ( (size_t)( s - hexString ) ) < sizeof( hexString ) );
+ *s = '\0';
+
+ // Build a string with the ASCII version of the data (replaces non-printable characters with '^').
+ // Optionally pads the string with '`' to fill the full 16 byte range (so it lines up).
+
+ s = asciiString;
+ if( !( inFlags & kDebugFlagsNoASCII ) )
+ {
+ *s++ = ' ';
+ *s++ = '|';
+ for( i = 0; i < n; ++i )
+ {
+ if( i < chunkSize )
+ {
+ c = src[ i ];
+ if( !DebugIsPrint( c ) )
+ {
+ c = '^';
+ }
+ }
+ else
+ {
+ c = '`';
+ }
+ *s++ = (char) c;
+ }
+ *s++ = '|';
+ check( ( (size_t)( s - asciiString ) ) < sizeof( asciiString ) );
+ }
+ *s = '\0';
+
+ // Build a string indicating how bytes are in the hex dump. Only printed on the first line.
+
+ s = byteCountString;
+ if( !( inFlags & kDebugFlagsNoByteCount ) )
+ {
+ if( src == start )
+ {
+ s += DebugSNPrintF( s, sizeof( byteCountString ), " (%d bytes)", (int) inDataSize );
+ }
+ }
+ check( ( (size_t)( s - byteCountString ) ) < sizeof( byteCountString ) );
+ *s = '\0';
+
+ // Build the entire line from all the pieces we've previously built.
+
+ if( outBuffer )
+ {
+ if( src == start )
+ {
+ dst += DebugSNPrintF( dst, (size_t)( end - dst ),
+ "%*s" // Indention
+ "%s" // Separator (only if needed)
+ "%s" // Prefix
+ "%-*.*s" // Label
+ "%s" // Separator
+ "%s" // Hex
+ "%s" // ASCII
+ "%s" // Byte Count
+ "%s", // Newline
+ inIndent, "",
+ ( src != start ) ? separator : "",
+ prefixString,
+ width, (int) inLabelSize, inLabel ? inLabel : "",
+ ( width > 0 ) ? " " : "",
+ hexString,
+ asciiString,
+ byteCountString,
+ newline );
+ }
+ else
+ {
+ dst += DebugSNPrintF( dst, (size_t)( end - dst ),
+ "%*s" // Indention
+ "%s" // Separator (only if needed)
+ "%s" // Prefix
+ "%*s" // Label Spacing
+ "%s" // Separator
+ "%s" // Hex
+ "%s" // ASCII
+ "%s" // Byte Count
+ "%s", // Newline
+ inIndent, "",
+ ( src != start ) ? separator : "",
+ prefixString,
+ width, "",
+ ( width > 0 ) ? " " : "",
+ hexString,
+ asciiString,
+ byteCountString,
+ newline );
+ }
+ }
+ else
+ {
+ if( src == start )
+ {
+ dst += DebugPrintF( inLevel,
+ "%*s" // Indention
+ "%s" // Separator (only if needed)
+ "%s" // Prefix
+ "%-*.*s" // Label
+ "%s" // Separator
+ "%s" // Hex
+ "%s" // ASCII
+ "%s" // Byte Count
+ "%s", // Newline
+ inIndent, "",
+ ( src != start ) ? separator : "",
+ prefixString,
+ width, (int) inLabelSize, inLabel,
+ ( width > 0 ) ? " " : "",
+ hexString,
+ asciiString,
+ byteCountString,
+ newline );
+ }
+ else
+ {
+ dst += DebugPrintF( inLevel,
+ "%*s" // Indention
+ "%s" // Separator (only if needed)
+ "%s" // Prefix
+ "%*s" // Label Spacing
+ "%s" // Separator
+ "%s" // Hex
+ "%s" // ASCII
+ "%s" // Byte Count
+ "%s", // Newline
+ inIndent, "",
+ ( src != start ) ? separator : "",
+ prefixString,
+ width, "",
+ ( width > 0 ) ? " " : "",
+ hexString,
+ asciiString,
+ byteCountString,
+ newline );
+ }
+ }
+
+ // Move to the next chunk. Exit if there is no more data.
+
+ offset += (int) chunkSize;
+ src += chunkSize;
+ inDataSize -= chunkSize;
+ if( inDataSize == 0 )
+ {
+ break;
+ }
+ }
+
+ // Note: The "dst - outBuffer" size calculation works even if "outBuffer" is NULL because it's all relative.
+
+ return( (size_t)( dst - outBuffer ) );
+}
+
+//===========================================================================================================================
+// DebugNumVersionToString
+//===========================================================================================================================
+
+static char * DebugNumVersionToString( uint32_t inVersion, char *inString )
+{
+ char * s;
+ uint8_t majorRev;
+ uint8_t minor;
+ uint8_t bugFix;
+ uint8_t stage;
+ uint8_t revision;
+
+ check( inString );
+
+ majorRev = (uint8_t)( ( inVersion >> 24 ) & 0xFF );
+ minor = (uint8_t)( ( inVersion >> 20 ) & 0x0F );
+ bugFix = (uint8_t)( ( inVersion >> 16 ) & 0x0F );
+ stage = (uint8_t)( ( inVersion >> 8 ) & 0xFF );
+ revision = (uint8_t)( inVersion & 0xFF );
+
+ // Convert the major, minor, and bugfix numbers.
+
+ s = inString;
+ s += sprintf( s, "%u", majorRev );
+ s += sprintf( s, ".%u", minor );
+ if( bugFix != 0 )
+ {
+ s += sprintf( s, ".%u", bugFix );
+ }
+
+ // Convert the version stage and non-release revision number.
+
+ switch( stage )
+ {
+ case kVersionStageDevelopment:
+ s += sprintf( s, "d%u", revision );
+ break;
+
+ case kVersionStageAlpha:
+ s += sprintf( s, "a%u", revision );
+ break;
+
+ case kVersionStageBeta:
+ s += sprintf( s, "b%u", revision );
+ break;
+
+ case kVersionStageFinal:
+
+ // A non-release revision of zero is a special case indicating the software is GM (at the golden master
+ // stage) and therefore, the non-release revision should not be added to the string.
+
+ if( revision != 0 )
+ {
+ s += sprintf( s, "f%u", revision );
+ }
+ break;
+
+ default:
+ dlog( kDebugLevelError, "invalid NumVersion stage (0x%02X)\n", stage );
+ break;
+ }
+ return( inString );
+}
+
+//===========================================================================================================================
+// DebugTaskLevel
+//===========================================================================================================================
+
+DEBUG_EXPORT uint32_t DebugTaskLevel( void )
+{
+ uint32_t level;
+
+ level = 0;
+
+#if ( TARGET_OS_VXWORKS )
+ if( intContext() )
+ {
+ level |= ( ( 1 << kDebugInterruptLevelShift ) & kDebugInterruptLevelMask );
+ }
+#endif
+
+ return( level );
+}
+
+#if ( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+//===========================================================================================================================
+// DebugWinEnableConsole
+//===========================================================================================================================
+
+#pragma warning( disable:4311 )
+
+static void DebugWinEnableConsole( void )
+{
+ static bool sConsoleEnabled = false;
+ BOOL result;
+ int fileHandle;
+ FILE * file;
+ int err;
+
+ if( sConsoleEnabled )
+ {
+ goto exit;
+ }
+
+ // Create console window.
+
+ result = AllocConsole();
+ require_quiet( result, exit );
+
+ // Redirect stdin to the console stdin.
+
+ fileHandle = _open_osfhandle( (long) GetStdHandle( STD_INPUT_HANDLE ), _O_TEXT );
+
+ #if ( defined( __MWERKS__ ) )
+ file = __handle_reopen( (unsigned long) fileHandle, "r", stdin );
+ require_quiet( file, exit );
+ #else
+ file = _fdopen( fileHandle, "r" );
+ require_quiet( file, exit );
+
+ *stdin = *file;
+ #endif
+
+ err = setvbuf( stdin, NULL, _IONBF, 0 );
+ require_noerr_quiet( err, exit );
+
+ // Redirect stdout to the console stdout.
+
+ fileHandle = _open_osfhandle( (long) GetStdHandle( STD_OUTPUT_HANDLE ), _O_TEXT );
+
+ #if ( defined( __MWERKS__ ) )
+ file = __handle_reopen( (unsigned long) fileHandle, "w", stdout );
+ require_quiet( file, exit );
+ #else
+ file = _fdopen( fileHandle, "w" );
+ require_quiet( file, exit );
+
+ *stdout = *file;
+ #endif
+
+ err = setvbuf( stdout, NULL, _IONBF, 0 );
+ require_noerr_quiet( err, exit );
+
+ // Redirect stderr to the console stdout.
+
+ fileHandle = _open_osfhandle( (long) GetStdHandle( STD_OUTPUT_HANDLE ), _O_TEXT );
+
+ #if ( defined( __MWERKS__ ) )
+ file = __handle_reopen( (unsigned long) fileHandle, "w", stderr );
+ require_quiet( file, exit );
+ #else
+ file = _fdopen( fileHandle, "w" );
+ require_quiet( file, exit );
+
+ *stderr = *file;
+ #endif
+
+ err = setvbuf( stderr, NULL, _IONBF, 0 );
+ require_noerr_quiet( err, exit );
+
+ sConsoleEnabled = true;
+
+exit:
+ return;
+}
+
+#pragma warning( default:4311 )
+
+#endif // TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE
+
+#if ( TARGET_OS_WIN32 )
+//===========================================================================================================================
+// DebugWinCharToTCharString
+//===========================================================================================================================
+
+static TCHAR *
+DebugWinCharToTCharString(
+ const char * inCharString,
+ size_t inCharCount,
+ TCHAR * outTCharString,
+ size_t inTCharCountMax,
+ size_t * outTCharCount )
+{
+ const char * src;
+ TCHAR * dst;
+ TCHAR * end;
+
+ if( inCharCount == kSizeCString )
+ {
+ inCharCount = strlen( inCharString );
+ }
+ src = inCharString;
+ dst = outTCharString;
+ if( inTCharCountMax > 0 )
+ {
+ inTCharCountMax -= 1;
+ if( inTCharCountMax > inCharCount )
+ {
+ inTCharCountMax = inCharCount;
+ }
+
+ end = dst + inTCharCountMax;
+ while( dst < end )
+ {
+ *dst++ = (TCHAR) *src++;
+ }
+ *dst = 0;
+ }
+ if( outTCharCount )
+ {
+ *outTCharCount = (size_t)( dst - outTCharString );
+ }
+ return( outTCharString );
+}
+#endif
+
+#if 0
+#pragma mark -
+#pragma mark == Debugging ==
+#endif
+
+//===========================================================================================================================
+// DebugServicesTest
+//===========================================================================================================================
+
+DEBUG_EXPORT OSStatus DebugServicesTest( void )
+{
+ OSStatus err;
+ char s[ 512 ];
+ uint8_t * p;
+ uint8_t data[] =
+ {
+ 0x11, 0x22, 0x33, 0x44,
+ 0x55, 0x66,
+ 0x77, 0x88, 0x99, 0xAA,
+ 0xBB, 0xCC, 0xDD,
+ 0xEE,
+ 0xFF,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
+ 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0,
+ 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, 0x81, 0x91, 0xA1
+ };
+
+ debug_initialize( kDebugOutputTypeMetaConsole );
+
+ // check's
+
+ check( 0 && "SHOULD SEE: check" );
+ check( 1 && "SHOULD *NOT* SEE: check (valid)" );
+ check_string( 0, "SHOULD SEE: check_string" );
+ check_string( 1, "SHOULD *NOT* SEE: check_string (valid)" );
+ check_noerr( -123 );
+ check_noerr( 10038 );
+ check_noerr( 22 );
+ check_noerr( 0 );
+ check_noerr_string( -6712, "SHOULD SEE: check_noerr_string" );
+ check_noerr_string( 0, "SHOULD *NOT* SEE: check_noerr_string (valid)" );
+ check_translated_errno( 0 >= 0 && "SHOULD *NOT* SEE", -384, -999 );
+ check_translated_errno( -1 >= 0 && "SHOULD SEE", -384, -999 );
+ check_translated_errno( -1 >= 0 && "SHOULD SEE", 0, -999 );
+ check_ptr_overlap( "SHOULD *NOT* SEE" ? 10 : 0, 10, 22, 10 );
+ check_ptr_overlap( "SHOULD SEE" ? 10 : 0, 10, 5, 10 );
+ check_ptr_overlap( "SHOULD SEE" ? 10 : 0, 10, 12, 6 );
+ check_ptr_overlap( "SHOULD SEE" ? 12 : 0, 6, 10, 10 );
+ check_ptr_overlap( "SHOULD SEE" ? 12 : 0, 10, 10, 10 );
+ check_ptr_overlap( "SHOULD *NOT* SEE" ? 22 : 0, 10, 10, 10 );
+ check_ptr_overlap( "SHOULD *NOT* SEE" ? 10 : 0, 10, 20, 10 );
+ check_ptr_overlap( "SHOULD *NOT* SEE" ? 20 : 0, 10, 10, 10 );
+
+ // require's
+
+ require( 0 && "SHOULD SEE", require1 );
+ { err = kResponseErr; goto exit; }
+require1:
+ require( 1 && "SHOULD *NOT* SEE", require2 );
+ goto require2Good;
+require2:
+ { err = kResponseErr; goto exit; }
+require2Good:
+ require_string( 0 && "SHOULD SEE", require3, "SHOULD SEE: require_string" );
+ { err = kResponseErr; goto exit; }
+require3:
+ require_string( 1 && "SHOULD *NOT* SEE", require4, "SHOULD *NOT* SEE: require_string (valid)" );
+ goto require4Good;
+require4:
+ { err = kResponseErr; goto exit; }
+require4Good:
+ require_quiet( 0 && "SHOULD SEE", require5 );
+ { err = kResponseErr; goto exit; }
+require5:
+ require_quiet( 1 && "SHOULD *NOT* SEE", require6 );
+ goto require6Good;
+require6:
+ { err = kResponseErr; goto exit; }
+require6Good:
+ require_noerr( -1, require7 );
+ { err = kResponseErr; goto exit; }
+require7:
+ require_noerr( 0, require8 );
+ goto require8Good;
+require8:
+ { err = kResponseErr; goto exit; }
+require8Good:
+ require_noerr_string( -2, require9, "SHOULD SEE: require_noerr_string");
+ { err = kResponseErr; goto exit; }
+require9:
+ require_noerr_string( 0, require10, "SHOULD *NOT* SEE: require_noerr_string (valid)" );
+ goto require10Good;
+require10:
+ { err = kResponseErr; goto exit; }
+require10Good:
+ require_noerr_action_string( -3, require11, dlog( kDebugLevelMax, "action 1 (expected)\n" ), "require_noerr_action_string" );
+ { err = kResponseErr; goto exit; }
+require11:
+ require_noerr_action_string( 0, require12, dlog( kDebugLevelMax, "action 2\n" ), "require_noerr_action_string (valid)" );
+ goto require12Good;
+require12:
+ { err = kResponseErr; goto exit; }
+require12Good:
+ require_noerr_quiet( -4, require13 );
+ { err = kResponseErr; goto exit; }
+require13:
+ require_noerr_quiet( 0, require14 );
+ goto require14Good;
+require14:
+ { err = kResponseErr; goto exit; }
+require14Good:
+ require_noerr_action( -5, require15, dlog( kDebugLevelMax, "SHOULD SEE: action 3 (expected)\n" ) );
+ { err = kResponseErr; goto exit; }
+require15:
+ require_noerr_action( 0, require16, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 4\n" ) );
+ goto require16Good;
+require16:
+ { err = kResponseErr; goto exit; }
+require16Good:
+ require_noerr_action_quiet( -4, require17, dlog( kDebugLevelMax, "SHOULD SEE: action 5 (expected)\n" ) );
+ { err = kResponseErr; goto exit; }
+require17:
+ require_noerr_action_quiet( 0, require18, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 6\n" ) );
+ goto require18Good;
+require18:
+ { err = kResponseErr; goto exit; }
+require18Good:
+ require_action( 0 && "SHOULD SEE", require19, dlog( kDebugLevelMax, "SHOULD SEE: action 7 (expected)\n" ) );
+ { err = kResponseErr; goto exit; }
+require19:
+ require_action( 1 && "SHOULD *NOT* SEE", require20, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 8\n" ) );
+ goto require20Good;
+require20:
+ { err = kResponseErr; goto exit; }
+require20Good:
+ require_action_quiet( 0, require21, dlog( kDebugLevelMax, "SHOULD SEE: action 9 (expected)\n" ) );
+ { err = kResponseErr; goto exit; }
+require21:
+ require_action_quiet( 1, require22, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 10\n" ) );
+ goto require22Good;
+require22:
+ { err = kResponseErr; goto exit; }
+require22Good:
+ require_action_string( 0, require23, dlog( kDebugLevelMax, "SHOULD SEE: action 11 (expected)\n" ), "SHOULD SEE: require_action_string" );
+ { err = kResponseErr; goto exit; }
+require23:
+ require_action_string( 1, require24, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 12\n" ), "SHOULD *NOT* SEE: require_action_string" );
+ goto require24Good;
+require24:
+ { err = kResponseErr; goto exit; }
+require24Good:
+
+#if ( defined( __MWERKS__ ) )
+ #if ( defined( __cplusplus ) && __option( exceptions ) )
+ #define COMPILER_HAS_EXCEPTIONS 1
+ #else
+ #define COMPILER_HAS_EXCEPTIONS 0
+ #endif
+#else
+ #if ( defined( __cplusplus ) )
+ #define COMPILER_HAS_EXCEPTIONS 1
+ #else
+ #define COMPILER_HAS_EXCEPTIONS 0
+ #endif
+#endif
+
+#if ( COMPILER_HAS_EXCEPTIONS )
+ try
+ {
+ require_throw( 1 && "SHOULD *NOT* SEE" );
+ require_throw( 0 && "SHOULD SEE" );
+ }
+ catch(... )
+ {
+ goto require26Good;
+ }
+ { err = kResponseErr; goto exit; }
+require26Good:
+#endif
+
+ // translate_errno
+
+ err = translate_errno( 1 != -1, -123, -567 );
+ require( ( err == 0 ) && "SHOULD *NOT* SEE", exit );
+
+ err = translate_errno( -1 != -1, -123, -567 );
+ require( ( err == -123 ) && "SHOULD *NOT* SEE", exit );
+
+ err = translate_errno( -1 != -1, 0, -567 );
+ require( ( err == -567 ) && "SHOULD *NOT* SEE", exit );
+
+ // debug_string
+
+ debug_string( "debug_string" );
+
+ // DebugSNPrintF
+
+ DebugSNPrintF( s, sizeof( s ), "%d", 1234 );
+ require_action( strcmp( s, "1234" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%X", 0x2345 );
+ require_action( strcmp( s, "2345" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%#s", "\05test" );
+ require_action( strcmp( s, "test" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%##s", "\03www\05apple\03com" );
+ require_action( strcmp( s, "www.apple.com." ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%ld", (long) INT32_C( 2147483647 ) );
+ require_action( strcmp( s, "2147483647" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%lu", (unsigned long) UINT32_C( 4294967295 ) );
+ require_action( strcmp( s, "4294967295" ) == 0, exit, err = -1 );
+
+ #if ( TYPE_LONGLONG_NATIVE )
+ DebugSNPrintF( s, sizeof( s ), "%lld", (long_long_compat) INT64_C( 9223372036854775807 ) );
+ require_action( strcmp( s, "9223372036854775807" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%lld", (long_long_compat) INT64_C( -9223372036854775807 ) );
+ require_action( strcmp( s, "-9223372036854775807" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%llu", (unsigned_long_long_compat) UINT64_C( 18446744073709551615 ) );
+ require_action( strcmp( s, "18446744073709551615" ) == 0, exit, err = -1 );
+ #endif
+
+ DebugSNPrintF( s, sizeof( s ), "%lb", (unsigned long) binary_32( 01111011, 01111011, 01111011, 01111011 ) );
+ require_action( strcmp( s, "1111011011110110111101101111011" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%C", 0x41624364 ); // 'AbCd'
+ require_action( strcmp( s, "AbCd" ) == 0, exit, err = -1 );
+
+ #if ( defined( MDNS_DEBUGMSGS ) )
+ {
+ mDNSAddr maddr;
+
+ memset( &maddr, 0, sizeof( maddr ) );
+ maddr.type = mDNSAddrType_IPv4;
+ maddr.ip.v4.b[ 0 ] = 127;
+ maddr.ip.v4.b[ 1 ] = 0;
+ maddr.ip.v4.b[ 2 ] = 0;
+ maddr.ip.v4.b[ 3 ] = 1;
+ DebugSNPrintF( s, sizeof( s ), "%#a", &maddr );
+ require_action( strcmp( s, "127.0.0.1" ) == 0, exit, err = -1 );
+
+ memset( &maddr, 0, sizeof( maddr ) );
+ maddr.type = mDNSAddrType_IPv6;
+ maddr.ip.v6.b[ 0 ] = 0xFE;
+ maddr.ip.v6.b[ 1 ] = 0x80;
+ maddr.ip.v6.b[ 15 ] = 0x01;
+ DebugSNPrintF( s, sizeof( s ), "%#a", &maddr );
+ require_action( strcmp( s, "FE80:0000:0000:0000:0000:0000:0000:0001" ) == 0, exit, err = -1 );
+ }
+ #endif
+
+ #if ( AF_INET )
+ {
+ struct sockaddr_in sa4;
+
+ memset( &sa4, 0, sizeof( sa4 ) );
+ sa4.sin_family = AF_INET;
+ p = (uint8_t *) &sa4.sin_port;
+ p[ 0 ] = (uint8_t)( ( 80 >> 8 ) & 0xFF );
+ p[ 1 ] = (uint8_t)( 80 & 0xFF );
+ p = (uint8_t *) &sa4.sin_addr.s_addr;
+ p[ 0 ] = (uint8_t)( ( INADDR_LOOPBACK >> 24 ) & 0xFF );
+ p[ 1 ] = (uint8_t)( ( INADDR_LOOPBACK >> 16 ) & 0xFF );
+ p[ 2 ] = (uint8_t)( ( INADDR_LOOPBACK >> 8 ) & 0xFF );
+ p[ 3 ] = (uint8_t)( INADDR_LOOPBACK & 0xFF );
+ DebugSNPrintF( s, sizeof( s ), "%##a", &sa4 );
+ require_action( strcmp( s, "127.0.0.1:80" ) == 0, exit, err = -1 );
+ }
+ #endif
+
+ #if ( AF_INET6 )
+ {
+ struct sockaddr_in6 sa6;
+
+ memset( &sa6, 0, sizeof( sa6 ) );
+ sa6.sin6_family = AF_INET6;
+ p = (uint8_t *) &sa6.sin6_port;
+ p[ 0 ] = (uint8_t)( ( 80 >> 8 ) & 0xFF );
+ p[ 1 ] = (uint8_t)( 80 & 0xFF );
+ sa6.sin6_addr.s6_addr[ 0 ] = 0xFE;
+ sa6.sin6_addr.s6_addr[ 1 ] = 0x80;
+ sa6.sin6_addr.s6_addr[ 15 ] = 0x01;
+ sa6.sin6_scope_id = 2;
+ DebugSNPrintF( s, sizeof( s ), "%##a", &sa6 );
+ require_action( strcmp( s, "[FE80:0000:0000:0000:0000:0000:0000:0001%2]:80" ) == 0, exit, err = -1 );
+ }
+ #endif
+
+ // Unicode
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "tes" );
+ require_action( strcmp( s, "tes" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "test" );
+ require_action( strcmp( s, "test" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "testing" );
+ require_action( strcmp( s, "test" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xC3\xA9" );
+ require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xC3\xA9ing" );
+ require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "tes\xC3\xA9ing" );
+ require_action( strcmp( s, "tes" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "t\xed\x9f\xbf" );
+ require_action( strcmp( s, "t\xed\x9f\xbf" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "t\xed\x9f\xbfing" );
+ require_action( strcmp( s, "t\xed\x9f\xbf" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xed\x9f\xbf" );
+ require_action( strcmp( s, "te" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xed\x9f\xbfing" );
+ require_action( strcmp( s, "te" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 7, "te\xC3\xA9\xed\x9f\xbfing" );
+ require_action( strcmp( s, "te\xC3\xA9\xed\x9f\xbf" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 6, "te\xC3\xA9\xed\x9f\xbfing" );
+ require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr );
+
+ DebugSNPrintF(s, sizeof(s), "%.*s", 5, "te\xC3\xA9\xed\x9f\xbfing" );
+ require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr );
+
+ #if ( TARGET_RT_BIG_ENDIAN )
+ DebugSNPrintF( s, sizeof( s ), "%S", "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" );
+ require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
+ #else
+ DebugSNPrintF( s, sizeof( s ), "%S", "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" "\x00" );
+ require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
+ #endif
+
+ DebugSNPrintF( s, sizeof( s ), "%S",
+ "\xFE\xFF" "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" ); // Big Endian BOM
+ require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%S",
+ "\xFF\xFE" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" "\x00" ); // Little Endian BOM
+ require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%#S", "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" ); // Big Endian
+ require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%##S", "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" "\x00" ); // Little Endian
+ require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%.*S",
+ 4, "\xFE\xFF" "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" ); // Big Endian BOM
+ require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%.*S",
+ 4, "\xFF\xFE" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" ); // Little Endian BOM
+ require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
+
+ #if ( TARGET_RT_BIG_ENDIAN )
+ DebugSNPrintF( s, sizeof( s ), "%.*S", 3, "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" );
+ require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
+ #else
+ DebugSNPrintF( s, sizeof( s ), "%.*S", 3, "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" );
+ require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
+ #endif
+
+ DebugSNPrintF( s, sizeof( s ), "%#.*S", 3, "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" ); // Big Endian
+ require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%##.*S", 3, "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" ); // Little Endian
+ require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
+
+ // Misc
+
+ DebugSNPrintF( s, sizeof( s ), "%U", "\x10\xb8\xa7\x6b" "\xad\x9d" "\xd1\x11" "\x80\xb4" "\x00\xc0\x4f\xd4\x30\xc8" );
+ require_action( strcmp( s, "6ba7b810-9dad-11d1-80b4-00c04fd430c8" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%m", 0 );
+ require_action( strcmp( s, "no error" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "%lm", (long) 0 );
+ require_action( strcmp( s, "no error" ) == 0, exit, err = -1 );
+
+ DebugSNPrintF( s, sizeof( s ), "\"%H\"", "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8", 16, 16 );
+ DebugPrintF( kDebugLevelMax, "%s\n\n", s );
+
+ DebugSNPrintF( s, sizeof( s ), "\"%H\"",
+ "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8"
+ "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8",
+ 32, 32 );
+ DebugPrintF( kDebugLevelMax, "%s\n\n", s );
+
+ DebugSNPrintF( s, sizeof( s ), "\"%H\"", "\x6b\xa7", 2, 2 );
+ DebugPrintF( kDebugLevelMax, "%s\n\n", s );
+
+ // Hex Dumps
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, "My Label", kSizeCString, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNone, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNoAddress | kDebugFlagsNoOffset, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, "My Label", kSizeCString, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNoAddress | kDebugFlagsNoOffset, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, "My Label", kSizeCString, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNoAddress, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNoOffset, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNoAddress, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNoOffset, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNoByteCount, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, "\x41\x62\x43\x64", "\x41\x62\x43\x64", 4, // 'AbCd'
+ kDebugFlagsNoAddress | kDebugFlagsNoOffset | kDebugFlagsNoNewLine |
+ kDebugFlagsNo32BitSeparator | kDebugFlagsNo16ByteHexPad | kDebugFlagsNoByteCount,
+ s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ),
+ kDebugFlagsNoAddress | kDebugFlagsNoOffset | kDebugFlagsNoASCII | kDebugFlagsNoNewLine |
+ kDebugFlags16BitSeparator | kDebugFlagsNo32BitSeparator |
+ kDebugFlagsNo16ByteHexPad | kDebugFlagsNoByteCount, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ s[ 0 ] = '\0';
+ DebugHexDump( kDebugLevelMax, 8, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), kDebugFlagsNone, s, sizeof( s ) );
+ DebugPrintF( kDebugLevelMax, "%s\n", s );
+
+ // dlog's
+
+ dlog( kDebugLevelNotice, "dlog\n" );
+ dlog( kDebugLevelNotice, "dlog integer: %d\n", 123 );
+ dlog( kDebugLevelNotice, "dlog string: \"%s\"\n", "test string" );
+ dlogmem( kDebugLevelNotice, data, sizeof( data ) );
+
+ // Done
+
+ DebugPrintF( kDebugLevelMax, "\n\nALL TESTS DONE\n\n" );
+ err = kNoErr;
+
+exit:
+ if( err )
+ {
+ DebugPrintF( kDebugLevelMax, "\n\n### TEST FAILED ###\n\n" );
+ }
+ return( err );
+}
+
+#endif // DEBUG
diff --git a/mDNSResponder/mDNSShared/DebugServices.h b/mDNSResponder/mDNSShared/DebugServices.h
new file mode 100644
index 00000000..108f7f5f
--- /dev/null
+++ b/mDNSResponder/mDNSShared/DebugServices.h
@@ -0,0 +1,1607 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @header DebugServices
+
+ Debugging Library
+ */
+
+#ifndef __DEBUG_SERVICES__
+#define __DEBUG_SERVICES__
+
+#include <stdarg.h>
+
+#include "CommonServices.h"
+
+#if ( TARGET_OS_VXWORKS )
+ #include "logLib.h"
+#endif
+
+#if 0
+#pragma mark == Settings ==
+#endif
+
+//===========================================================================================================================
+// Settings
+//===========================================================================================================================
+
+// General
+
+#if ( !defined( DEBUG ) )
+ #define DEBUG 0
+#endif
+
+#if ( defined( NDEBUG ) && DEBUG )
+ #error NDEBUG defined and DEBUG is also enabled...they need to be in-sync
+#endif
+
+// AssertMacros.h/Debugging.h overrides.
+
+#if ( !defined( DEBUG_OVERRIDE_APPLE_MACROS ) )
+ #define DEBUG_OVERRIDE_APPLE_MACROS 1
+#endif
+
+// Routine name. Uses ISO __func__ where possible. Otherwise, uses the best thing that is available (if anything).
+
+#if ( defined( __MWERKS__ ) || ( __GNUC__ > 2 ) || ( ( __GNUC__ == 2 ) && ( __GNUC_MINOR__ >= 9 ) ) )
+ #define __ROUTINE__ __func__
+#elif ( defined( __GNUC__ ) )
+ #define __ROUTINE__ __PRETTY_FUNCTION__
+#elif ( defined( _MSC_VER ) && !defined( _WIN32_WCE ) )
+ #define __ROUTINE__ __FUNCTION__
+#else
+ #define __ROUTINE__ ""
+#endif
+
+// Variable argument macro support. Use ANSI C99 __VA_ARGS__ where possible. Otherwise, use the next best thing.
+
+#if ( defined( __GNUC__ ) )
+ #if ( ( __GNUC__ > 3 ) || ( ( __GNUC__ == 3 ) && ( __GNUC_MINOR__ >= 3) ) )
+ #define DEBUG_C99_VA_ARGS 1
+ #define DEBUG_GNU_VA_ARGS 0
+ #else
+ #define DEBUG_C99_VA_ARGS 0
+ #define DEBUG_GNU_VA_ARGS 1
+ #endif
+#elif ( defined( __MWERKS__ ) )
+ #define DEBUG_C99_VA_ARGS 1
+ #define DEBUG_GNU_VA_ARGS 0
+#else
+ #define DEBUG_C99_VA_ARGS 0
+ #define DEBUG_GNU_VA_ARGS 0
+#endif
+
+#if 0
+#pragma mark == Output ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DEBUG_FPRINTF_ENABLED
+
+ @abstract Enables ANSI C fprintf output.
+ */
+
+#if ( !defined( DEBUG_FPRINTF_ENABLED ) )
+ #if ( !TARGET_API_MAC_OSX_KERNEL && !TARGET_OS_WINDOWS_CE )
+ #define DEBUG_FPRINTF_ENABLED 1
+ #else
+ #define DEBUG_FPRINTF_ENABLED 0
+ #endif
+#else
+ #if ( TARGET_API_MAC_OSX_KERNEL || TARGET_OS_WINDOWS_CE )
+ #error fprintf enabled, but not supported on Mac OS X kernel or Windows CE
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DEBUG_MAC_OS_X_IOLOG_ENABLED
+
+ @abstract Enables IOLog (Mac OS X Kernel) output.
+ */
+
+#if ( !defined( DEBUG_MAC_OS_X_IOLOG_ENABLED ) )
+ #define DEBUG_MAC_OS_X_IOLOG_ENABLED TARGET_API_MAC_OSX_KERNEL
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DEBUG_KPRINTF_ENABLED
+
+ @abstract Enables kprintf (Mac OS X Kernel) output.
+ */
+
+#if ( !defined( DEBUG_KPRINTF_ENABLED ) )
+ #define DEBUG_KPRINTF_ENABLED TARGET_API_MAC_OSX_KERNEL
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DEBUG_IDEBUG_ENABLED
+
+ @abstract Enables iDebug (Mac OS X user and Kernel) output.
+
+ @discussion
+
+ For Mac OS X kernel development, iDebug is enabled by default because we can dynamically check for the presence
+ of iDebug via some exported IOKit symbols. Mac OS X app usage doesn't allow dynamic detection because it relies
+ on statically linking to the iDebugServices.cp file so for Mac OS X app usage, you have to manually enable iDebug.
+ */
+
+#if ( !defined( DEBUG_IDEBUG_ENABLED ) )
+ #define DEBUG_IDEBUG_ENABLED TARGET_API_MAC_OSX_KERNEL
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DEBUG_CORE_SERVICE_ASSERTS_ENABLED
+
+ @abstract Controls whether Core Services assert handling is enabled. Enabling requires CoreServices framework.
+ */
+
+#if ( !defined( DEBUG_CORE_SERVICE_ASSERTS_ENABLED ) )
+ #if ( defined( __DEBUGGING__ ) )
+ #define DEBUG_CORE_SERVICE_ASSERTS_ENABLED 1
+ #else
+ #define DEBUG_CORE_SERVICE_ASSERTS_ENABLED 0
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DebugOutputType
+
+ @abstract Type of debug output (i.e. where the output goes).
+ */
+
+typedef uint32_t DebugOutputType;
+
+#define kDebugOutputTypeNone 0x6E6F6E65U // 'none' - no params
+#define kDebugOutputTypeCustom 0x63757374U // 'cust' - 1st param = function ptr, 2nd param = context
+#define kDebugOutputTypeFPrintF 0x66707269U // 'fpri' - 1st param = DebugOutputTypeFlags [, 2nd param = filename]
+#define kDebugOutputTypeiDebug 0x69646267U // 'idbg' - no params
+#define kDebugOutputTypeKPrintF 0x6B707266U // 'kprf' - no params
+#define kDebugOutputTypeMacOSXIOLog 0x696C6F67U // 'ilog' - no params
+#define kDebugOutputTypeMacOSXLog 0x786C6F67U // 'xlog' - no params
+#define kDebugOutputTypeWindowsDebugger 0x77696E64U // 'wind' - no params
+#define kDebugOutputTypeWindowsEventLog 0x7765766CU // 'wevl' - 1st param = C-string name, 2nd param = HMODULE or NULL.
+
+// Console meta output kind - Any kind of Console output (in horizontal order of preference):
+//
+// Mac OS X = ANSI printf (viewable in Console.app)
+// Mac OS X Kernel = IOLog (/var/log/system.log) or kprintf (serial).
+// Windows = ANSI printf (Console window) or OutputDebugString (debugger).
+// Other = ANSI printf (viewer varies).
+
+#define kDebugOutputTypeMetaConsole 0x434F4E53U // 'CONS' - no params
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DebugOutputTypeFlags
+
+ @abstract Flags controlling how the output type is configured.
+
+ @constant kDebugOutputTypeFlagsTypeMask Bit mask for the output type (e.g. stdout, stderr, file, etc.).
+ @constant kDebugOutputTypeFlagsStdOut fprintf should go to stdout.
+ @constant kDebugOutputTypeFlagsStdErr fprintf should go to stderr.
+ @constant kDebugOutputTypeFlagsFile fprintf should go to a specific file (filename passed as va_arg).
+ */
+
+typedef unsigned int DebugOutputTypeFlags;
+
+#define kDebugOutputTypeFlagsTypeMask 0xF
+#define kDebugOutputTypeFlagsStdOut 1
+#define kDebugOutputTypeFlagsStdErr 2
+#define kDebugOutputTypeFlagsFile 10
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DebugOutputFunctionPtr
+
+ @abstract Function ptr for a custom callback to print debug output.
+ */
+
+typedef void ( *DebugOutputFunctionPtr )( char *inData, size_t inSize, void *inContext );
+
+//===========================================================================================================================
+// Constants
+//===========================================================================================================================
+
+#if 0
+#pragma mark == Flags ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DebugFlags
+
+ @abstract Flags controlling how output is printed.
+ */
+
+typedef uint32_t DebugFlags;
+
+#define kDebugFlagsNone 0
+#define kDebugFlagsNoAddress ( 1 << 0 )
+#define kDebugFlagsNoOffset ( 1 << 1 )
+#define kDebugFlags32BitOffset ( 1 << 2 )
+#define kDebugFlagsNoASCII ( 1 << 3 )
+#define kDebugFlagsNoNewLine ( 1 << 4 )
+#define kDebugFlags8BitSeparator ( 1 << 5 )
+#define kDebugFlags16BitSeparator ( 1 << 6 )
+#define kDebugFlagsNo32BitSeparator ( 1 << 7 )
+#define kDebugFlagsNo16ByteHexPad ( 1 << 8 )
+#define kDebugFlagsNoByteCount ( 1 << 9 )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @enum DebugTaskLevelFlags
+
+ @abstract Flags indicating the task level.
+ */
+
+enum
+{
+ kDebugInterruptLevelShift = 0,
+ kDebugInterruptLevelMask = 0x00000007,
+ kDebugInVBLTaskMask = 0x00000010,
+ kDebugInDeferredTaskMask = 0x00000020,
+ kDebugInSecondaryInterruptHandlerMask = 0x00000040,
+ kDebugPageFaultFatalMask = 0x00000100, // There should be a "kPageFaultFatalMask" in Debugging.h.
+ kDebugMPTaskLevelMask = 0x00000200, // There should be a "kMPTaskLevelMask" in Debugging.h.
+ kDebugInterruptDepthShift = 16,
+ kDebugInterruptDepthMask = 0x00FF0000
+};
+
+#define DebugExtractTaskLevelInterruptLevel( LEVEL ) \
+ ( ( ( LEVEL ) &kDebugInterruptLevelMask ) >> kDebugInterruptLevelShift )
+
+#define DebugExtractTaskLevelInterruptDepth( LEVEL ) \
+ ( ( ( LEVEL ) &kDebugInterruptDepthMask ) >> kDebugInterruptDepthShift )
+
+#if 0
+#pragma mark == Levels ==
+#endif
+
+//===========================================================================================================================
+// Constants & Types - Levels
+//===========================================================================================================================
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DebugLevel
+
+ @abstract Level used to control debug logging.
+ */
+
+typedef int32_t DebugLevel;
+
+// Levels
+
+#define kDebugLevelMask 0x0000FFFF
+#define kDebugLevelChatty 100
+#define kDebugLevelVerbose 500
+#define kDebugLevelTrace 800
+#define kDebugLevelInfo 1000
+#define kDebugLevelNotice 3000
+#define kDebugLevelWarning 5000
+#define kDebugLevelAssert 6000
+#define kDebugLevelRequire 7000
+#define kDebugLevelError 8000
+#define kDebugLevelCritical 9000
+#define kDebugLevelAlert 10000
+#define kDebugLevelEmergency 11000
+#define kDebugLevelTragic 12000
+#define kDebugLevelMax 0x0000FFFF
+
+// Level Flags
+
+#define kDebugLevelFlagMask 0xFFFF0000
+#define kDebugLevelFlagStackTrace 0x00010000
+#define kDebugLevelFlagDebugBreak 0x00020000
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef LogLevel
+
+ @abstract Level used to control which events are logged.
+ */
+
+typedef int32_t LogLevel;
+
+#define kLogLevelUninitialized -1L
+#define kLogLevelAll 0L
+#define kLogLevelChatty 100L
+#define kLogLevelVerbose 500L
+#define kLogLevelTrace 800L
+#define kLogLevelInfo 1000L
+#define kLogLevelNotice 3000L
+#define kLogLevelWarning 4000L
+#define kLogLevelAssert 6000L
+#define kLogLevelRequire 7000L
+#define kLogLevelError 8000L
+#define kLogLevelCritical 9000L
+#define kLogLevelAlert 10000L
+#define kLogLevelEmergency 11000L
+#define kLogLevelTragic 12000L
+#define kLogLevelOff 0x0000FFFEL
+
+#if 0
+#pragma mark == Properties ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DebugPropertyTag
+
+ @abstract Tag for properties.
+ */
+
+typedef uint32_t DebugPropertyTag;
+
+#define kDebugPropertyTagPrintLevelMin 0x6D696E70U // 'minp' Get: 1st param = DebugLevel *
+ // Set: 1st param = DebugLevel
+
+#define kDebugPropertyTagPrintLevel kDebugPropertyTagPrintLevelMin
+
+#define kDebugPropertyTagPrintLevelMax 0x706D786CU // 'maxp' Get: 1st param = DebugLevel *
+ // Set: 1st param = DebugLevel
+
+#define kDebugPropertyTagBreakLevel 0x62726B6CU // 'brkl' Get: 1st param = DebugLevel *
+ // Set: 1st param = DebugLevel
+#if 0
+#pragma mark == General macros ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DEBUG_UNUSED
+
+ @abstract Macro to mark a paramter as unused to avoid unused parameter warnings.
+
+ @discussion
+
+ There is no universally supported pragma/attribute for indicating a variable is unused. DEBUG_UNUSED lets us
+ indicate a variable is unused in a manner that is supported by most compilers.
+ */
+
+#define DEBUG_UNUSED( X ) (void)( X )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DEBUG_USE_ONLY
+
+ @abstract Macro to mark a variable as used only when debugging is enabled.
+
+ @discussion
+
+ Variables are sometimes needed only for debugging. When debugging is turned off, these debug-only variables generate
+ compiler warnings about unused variables. To eliminate these warnings, use these macros to indicate variables that
+ are only used for debugging.
+ */
+
+#if ( DEBUG )
+ #define DEBUG_USE_ONLY( X )
+#else
+ #define DEBUG_USE_ONLY( X ) (void)( X )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DEBUG_LOCAL
+
+ @abstract Macros to make variables and functions static when debugging is off, but extern when debugging is on.
+
+ @discussion
+
+ Rather than using "static" directly, using this macros allows you to access these variables external while
+ debugging without being penalized for production builds.
+ */
+
+#if ( DEBUG )
+ #define DEBUG_LOCAL
+#else
+ #define DEBUG_LOCAL static
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DEBUG_STATIC
+
+ @abstract Macros to make variables and functions static when debugging is off, but extern when debugging is on.
+
+ @discussion
+
+ Rather than using "static" directly, using this macros allows you to access these variables external while
+ debugging without being penalized for production builds.
+ */
+
+#if ( DEBUG )
+ #define DEBUG_STATIC
+#else
+ #define DEBUG_STATIC static
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DEBUG_EXPORT
+
+ @abstract Macros to export variables.
+
+ @discussion
+
+ "__private_extern__" is a hack for IOKit to allow symbols to be exported from compilation units, but
+ // not exported outside a driver (IOKit uses a lame global namespace for symbols). This still does not
+ // solve the problem of multiple drivers in the same dependency chain since they share symbols.
+ */
+
+#if ( TARGET_API_MAC_OSX_KERNEL )
+ #define DEBUG_EXPORT __private_extern__
+#else
+ #define DEBUG_EXPORT extern
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined debug_add
+
+ @abstract Macro to add (or subtract if negative) a value when debugging is on. Does nothing if debugging is off.
+ */
+
+#if ( DEBUG )
+ #define debug_add( A, B ) ( A ) += ( B )
+#else
+ #define debug_add( A, B )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined debug_perform
+
+ @abstract Macro to perform something in debug-only builds.
+ */
+
+#if ( DEBUG )
+ #define debug_perform( X ) do { X; } while( 0 )
+#else
+ #define debug_perform( X )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function translate_errno
+
+ @abstract Returns 0 if the test success. If the test fails, returns errno if non-zero and othewise the alternate error.
+ */
+
+#define translate_errno( TEST, ERRNO, ALTERNATE_ERROR ) ( ( TEST ) ? 0 : ( ERRNO ) ? ( ERRNO ) : ( ALTERNATE_ERROR ) )
+
+#if 0
+#pragma mark == Compile Time macros ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined check_compile_time
+
+ @abstract Performs a compile-time check of something such as the size of an int.
+
+ @discussion
+
+ This declares an array with a size that is determined by a compile-time expression. If the expression evaluates
+ to 0, the array has a size of -1, which is illegal and generates a compile-time error.
+
+ For example:
+
+ check_compile_time( sizeof( int ) == 4 );
+
+ Note: This only works with compile-time expressions.
+ Note: This only works in places where extern declarations are allowed (e.g. global scope).
+
+ References:
+
+ <http://www.jaggersoft.com/pubs/CVu11_3.html>
+ <http://www.jaggersoft.com/pubs/CVu11_5.html>
+
+ Note: The following macros differ from the macros on the www.jaggersoft.com web site because those versions do not
+ work with GCC due to GCC allow a zero-length array. Using a -1 condition turned out to be more portable.
+ */
+
+#define check_compile_time( X ) extern int debug_compile_time_name[ ( X ) ? 1 : -1 ]
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined check_compile_time_code
+
+ @abstract Perform a compile-time check, suitable for placement in code, of something such as the size of an int.
+
+ @discussion
+
+ This creates a switch statement with an existing case for 0 and an additional case using the result of a
+ compile-time expression. A switch statement cannot have two case labels with the same constant so if the
+ compile-time expression evaluates to 0, it is illegal and generates a compile-time error. If the compile-time
+ expression does not evaluate to 0, the resulting value is used as the case label and it compiles without error.
+
+ For example:
+
+ check_compile_time_code( sizeof( int ) == 4 );
+
+ Note: This only works with compile-time expressions.
+ Note: This does not work in a global scope so it must be inside a function.
+
+ References:
+
+ <http://www.jaggersoft.com/pubs/CVu11_3.html>
+ <http://www.jaggersoft.com/pubs/CVu11_5.html>
+ */
+
+#define check_compile_time_code( X ) switch( 0 ) { case 0: case X:; }
+
+#if 0
+#pragma mark == check macros ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined check
+
+ @abstract Check that an expression is true (non-zero).
+
+ @discussion
+
+ If expression evalulates to false, this prints debugging information (actual expression string, file, line number,
+ function name, etc.) using the default debugging output method.
+
+ Code inside check() statements is not compiled into production builds.
+ */
+
+#if ( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef check
+#endif
+#if ( !defined( check ) )
+ #if ( DEBUG )
+ #define check( X ) \
+ do \
+ { \
+ if( !( X ) ) \
+ { \
+ debug_print_assert( 0, # X, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
+ } \
+ } while( 0 )
+ #else
+ #define check( X )
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined check_string
+
+ @abstract Check that an expression is true (non-zero) with an explanation.
+
+ @discussion
+
+ If expression evalulates to false, this prints debugging information (actual expression string, file, line number,
+ function name, etc.) and a custom explanation string using the default debugging output method.
+
+ Code inside check_string() statements is not compiled into production builds.
+ */
+
+#if ( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef check_string
+#endif
+#if ( !defined( check_string ) )
+ #if ( DEBUG )
+ #define check_string( X, STR ) \
+ do \
+ { \
+ if( !( X ) ) \
+ { \
+ debug_print_assert( 0, # X, STR, __FILE__, __LINE__, __ROUTINE__ ); \
+ } \
+ \
+ } while( 0 )
+ #else
+ #define check_string( X, STR )
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined check_noerr
+
+ @abstract Check that an error code is noErr (0).
+
+ @discussion
+
+ If the error code is non-0, this prints debugging information (actual expression string, file, line number,
+ function name, etc.) using the default debugging output method.
+
+ Code inside check_noerr() statements is not compiled into production builds.
+ */
+
+#if ( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef check_noerr
+#endif
+#if ( !defined( check_noerr ) )
+ #if ( DEBUG )
+ #define check_noerr( ERR ) \
+ do \
+ { \
+ int_least32_t localErr; \
+ \
+ localErr = (int_least32_t)( ERR ); \
+ if( localErr != 0 ) \
+ { \
+ debug_print_assert( localErr, NULL, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
+ } \
+ \
+ } while( 0 )
+ #else
+ #define check_noerr( ERR )
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined check_noerr_string
+
+ @abstract Check that an error code is noErr (0) with an explanation.
+
+ @discussion
+
+ If the error code is non-0, this prints debugging information (actual expression string, file, line number,
+ function name, etc.) and a custom explanation string using the default debugging output method.
+
+ Code inside check_noerr_string() statements is not compiled into production builds.
+ */
+
+#if ( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef check_noerr_string
+#endif
+#if ( !defined( check_noerr_string ) )
+ #if ( DEBUG )
+ #define check_noerr_string( ERR, STR ) \
+ do \
+ { \
+ int_least32_t localErr; \
+ \
+ localErr = (int_least32_t)( ERR ); \
+ if( localErr != 0 ) \
+ { \
+ debug_print_assert( localErr, NULL, STR, __FILE__, __LINE__, __ROUTINE__ ); \
+ } \
+ \
+ } while( 0 )
+ #else
+ #define check_noerr_string( ERR, STR )
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined check_translated_errno
+
+ @abstract Check a condition and prints errno (if non-zero) to the log.
+
+ @discussion
+
+ Code inside check_translated_errno() statements is not compiled into production builds.
+ */
+
+#if ( !defined( check_translated_errno ) )
+ #if ( DEBUG )
+ #define check_translated_errno( TEST, ERRNO, ALTERNATE_ERROR ) \
+ do \
+ { \
+ if( !( TEST ) ) \
+ { \
+ int_least32_t localErr; \
+ \
+ localErr = (int_least32_t)( ERRNO ); \
+ localErr = ( localErr != 0 ) ? localErr : (int_least32_t)( ALTERNATE_ERROR ); \
+ debug_print_assert( localErr, # TEST, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
+ } \
+ \
+ } while( 0 )
+ #else
+ #define check_translated_errno( TEST, ERRNO, ALTERNATE_ERROR )
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined check_ptr_overlap
+
+ @abstract Checks that two ptrs do not overlap.
+ */
+
+#define check_ptr_overlap( P1, P1_SIZE, P2, P2_SIZE ) \
+ do \
+ { \
+ check( !( ( (uintptr_t)( P1 ) >= (uintptr_t)( P2 ) ) && \
+ ( (uintptr_t)( P1 ) < ( ( (uintptr_t)( P2 ) ) + ( P2_SIZE ) ) ) ) ); \
+ check( !( ( (uintptr_t)( P2 ) >= (uintptr_t)( P1 ) ) && \
+ ( (uintptr_t)( P2 ) < ( ( (uintptr_t)( P1 ) ) + ( P1_SIZE ) ) ) ) ); \
+ \
+ } while( 0 )
+
+#if 0
+#pragma mark == require macros ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require
+
+ @abstract Requires that an expression evaluate to true.
+
+ @discussion
+
+ If expression evalulates to false, this prints debugging information (actual expression string, file, line number,
+ function name, etc.) using the default debugging output method then jumps to a label.
+ */
+
+#if ( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef require
+#endif
+#if ( !defined( require ) )
+ #define require( X, LABEL ) \
+ do \
+ { \
+ if( !( X ) ) \
+ { \
+ debug_print_assert( 0, # X, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
+ goto LABEL; \
+ } \
+ \
+ } while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require_string
+
+ @abstract Requires that an expression evaluate to true with an explanation.
+
+ @discussion
+
+ If expression evalulates to false, this prints debugging information (actual expression string, file, line number,
+ function name, etc.) and a custom explanation string using the default debugging output method then jumps to a label.
+ */
+
+#if ( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef require_string
+#endif
+#if ( !defined( require_string ) )
+ #define require_string( X, LABEL, STR ) \
+ do \
+ { \
+ if( !( X ) ) \
+ { \
+ debug_print_assert( 0, # X, STR, __FILE__, __LINE__, __ROUTINE__ ); \
+ goto LABEL; \
+ } \
+ \
+ } while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require_quiet
+
+ @abstract Requires that an expression evaluate to true.
+
+ @discussion
+
+ If expression evalulates to false, this jumps to a label. No debugging information is printed.
+ */
+
+#if ( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef require_quiet
+#endif
+#if ( !defined( require_quiet ) )
+ #define require_quiet( X, LABEL ) \
+ do \
+ { \
+ if( !( X ) ) \
+ { \
+ goto LABEL; \
+ } \
+ \
+ } while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require_noerr
+
+ @abstract Require that an error code is noErr (0).
+
+ @discussion
+
+ If the error code is non-0, this prints debugging information (actual expression string, file, line number,
+ function name, etc.) using the default debugging output method then jumps to a label.
+ */
+
+#if ( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef require_noerr
+#endif
+#if ( !defined( require_noerr ) )
+ #define require_noerr( ERR, LABEL ) \
+ do \
+ { \
+ int_least32_t localErr; \
+ \
+ localErr = (int_least32_t)( ERR ); \
+ if( localErr != 0 ) \
+ { \
+ debug_print_assert( localErr, NULL, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
+ goto LABEL; \
+ } \
+ \
+ } while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require_noerr_string
+
+ @abstract Require that an error code is noErr (0).
+
+ @discussion
+
+ If the error code is non-0, this prints debugging information (actual expression string, file, line number,
+ function name, etc.), and a custom explanation string using the default debugging output method using the
+ default debugging output method then jumps to a label.
+ */
+
+#if ( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef require_noerr_string
+#endif
+#if ( !defined( require_noerr_string ) )
+ #define require_noerr_string( ERR, LABEL, STR ) \
+ do \
+ { \
+ int_least32_t localErr; \
+ \
+ localErr = (int_least32_t)( ERR ); \
+ if( localErr != 0 ) \
+ { \
+ debug_print_assert( localErr, NULL, STR, __FILE__, __LINE__, __ROUTINE__ ); \
+ goto LABEL; \
+ } \
+ \
+ } while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require_noerr_action_string
+
+ @abstract Require that an error code is noErr (0).
+
+ @discussion
+
+ If the error code is non-0, this prints debugging information (actual expression string, file, line number,
+ function name, etc.), and a custom explanation string using the default debugging output method using the
+ default debugging output method then executes an action and jumps to a label.
+ */
+
+#if ( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef require_noerr_action_string
+#endif
+#if ( !defined( require_noerr_action_string ) )
+ #define require_noerr_action_string( ERR, LABEL, ACTION, STR ) \
+ do \
+ { \
+ int_least32_t localErr; \
+ \
+ localErr = (int_least32_t)( ERR ); \
+ if( localErr != 0 ) \
+ { \
+ debug_print_assert( localErr, NULL, STR, __FILE__, __LINE__, __ROUTINE__ ); \
+ { ACTION; } \
+ goto LABEL; \
+ } \
+ \
+ } while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require_noerr_quiet
+
+ @abstract Require that an error code is noErr (0).
+
+ @discussion
+
+ If the error code is non-0, this jumps to a label. No debugging information is printed.
+ */
+
+#if ( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef require_noerr_quiet
+#endif
+#if ( !defined( require_noerr_quiet ) )
+ #define require_noerr_quiet( ERR, LABEL ) \
+ do \
+ { \
+ if( ( ERR ) != 0 ) \
+ { \
+ goto LABEL; \
+ } \
+ \
+ } while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require_noerr_action
+
+ @abstract Require that an error code is noErr (0) with an action to execute otherwise.
+
+ @discussion
+
+ If the error code is non-0, this prints debugging information (actual expression string, file, line number,
+ function name, etc.) using the default debugging output method then executes an action and jumps to a label.
+ */
+
+#if ( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef require_noerr_action
+#endif
+#if ( !defined( require_noerr_action ) )
+ #define require_noerr_action( ERR, LABEL, ACTION ) \
+ do \
+ { \
+ int_least32_t localErr; \
+ \
+ localErr = (int_least32_t)( ERR ); \
+ if( localErr != 0 ) \
+ { \
+ debug_print_assert( localErr, NULL, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
+ { ACTION; } \
+ goto LABEL; \
+ } \
+ \
+ } while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require_noerr_action_quiet
+
+ @abstract Require that an error code is noErr (0) with an action to execute otherwise.
+
+ @discussion
+
+ If the error code is non-0, this executes an action and jumps to a label. No debugging information is printed.
+ */
+
+#if ( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef require_noerr_action_quiet
+#endif
+#if ( !defined( require_noerr_action_quiet ) )
+ #define require_noerr_action_quiet( ERR, LABEL, ACTION ) \
+ do \
+ { \
+ if( ( ERR ) != 0 ) \
+ { \
+ { ACTION; } \
+ goto LABEL; \
+ } \
+ \
+ } while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require_action
+
+ @abstract Requires that an expression evaluate to true with an action to execute otherwise.
+
+ @discussion
+
+ If expression evalulates to false, this prints debugging information (actual expression string, file, line number,
+ function name, etc.) using the default debugging output method then executes an action and jumps to a label.
+ */
+
+#if ( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef require_action
+#endif
+#if ( !defined( require_action ) )
+ #define require_action( X, LABEL, ACTION ) \
+ do \
+ { \
+ if( !( X ) ) \
+ { \
+ debug_print_assert( 0, # X, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
+ { ACTION; } \
+ goto LABEL; \
+ } \
+ \
+ } while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require_action_quiet
+
+ @abstract Requires that an expression evaluate to true with an action to execute otherwise.
+
+ @discussion
+
+ If expression evalulates to false, this executes an action and jumps to a label. No debugging information is printed.
+ */
+
+#if ( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef require_action_quiet
+#endif
+#if ( !defined( require_action_quiet ) )
+ #define require_action_quiet( X, LABEL, ACTION ) \
+ do \
+ { \
+ if( !( X ) ) \
+ { \
+ { ACTION; } \
+ goto LABEL; \
+ } \
+ \
+ } while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require_action_string
+
+ @abstract Requires that an expression evaluate to true with an explanation and action to execute otherwise.
+
+ @discussion
+
+ If expression evalulates to false, this prints debugging information (actual expression string, file, line number,
+ function name, etc.) and a custom explanation string using the default debugging output method then executes an
+ action and jumps to a label.
+ */
+
+#if ( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef require_action_string
+#endif
+#if ( !defined( require_action_string ) )
+ #define require_action_string( X, LABEL, ACTION, STR ) \
+ do \
+ { \
+ if( !( X ) ) \
+ { \
+ debug_print_assert( 0, # X, STR, __FILE__, __LINE__, __ROUTINE__ ); \
+ { ACTION; } \
+ goto LABEL; \
+ } \
+ \
+ } while( 0 )
+
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined require_throw
+
+ @abstract Requires that an expression evaluates to true or an exception is thrown.
+
+ @discussion
+
+ If the expression evaluates to false, this prints debugging information (actual expression string, file,
+ line number, function name, etc.) using the default debugging output method then throws an exception.
+ */
+
+#if ( defined( __cplusplus ) )
+ #define require_throw( X ) \
+ do \
+ { \
+ if( !( X ) ) \
+ { \
+ debug_print_assert( 0, # X, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
+ throw kUnknownErr; \
+ } \
+ \
+ } while( 0 )
+#endif
+
+#if 0
+#pragma mark == Design-By-Contract macros ==
+#endif
+
+//===========================================================================================================================
+// Design-By-Contract macros
+//===========================================================================================================================
+
+#define ensure( X ) check( X )
+#define ensure_string( X, STR ) check_string( X, STR )
+#define ensure_noerr( ERR ) check_noerr( ERR )
+#define ensure_noerr_string( ERR, STR ) check_noerr_string( ERR, STR )
+#define ensure_translated_errno( TEST, ERRNO, ALTERNATE_ERROR ) check_translated_errno( TEST, ERRNO, ALTERNATE_ERROR )
+
+// Note: Design-By-Contract "require" macros are already defined elsewhere.
+
+#if 0
+#pragma mark == Expect macros ==
+#endif
+
+//===========================================================================================================================
+// Expect macros
+//===========================================================================================================================
+
+// Expect macros allow code to include runtime checking of things that should not happen in shipping code (e.g. internal
+// programmer errors, such as a NULL parameter where it is not allowed). Once the code has been verified to work correctly
+// without asserting, the DEBUG_EXPECT_VERIFIED conditional can be set to eliminate the error checking entirely. It can
+// also be useful to measure the cost of error checking code by profiling with it enable and with it disabled.
+
+#if ( DEBUG_EXPECT_VERIFIED )
+ #define require_expect
+ #define require_string_expect
+ #define require_quiet_expect
+ #define require_noerr_expect
+ #define require_noerr_string_expect
+ #define require_noerr_action_string_expect
+ #define require_noerr_quiet_expect
+ #define require_noerr_action_expect
+ #define require_noerr_action_quiet_expect
+ #define require_action_expect
+ #define require_action_quiet_expect
+ #define require_action_string_expect
+#else
+ #define require_expect require
+ #define require_string_expect require_string
+ #define require_quiet_expect require_quiet
+ #define require_noerr_expect require_noerr
+ #define require_noerr_string_expect require_noerr_string
+ #define require_noerr_action_string_expect require_noerr_action_string
+ #define require_noerr_quiet_expect require_noerr_quiet
+ #define require_noerr_action_expect require_noerr_action
+ #define require_noerr_action_quiet_expect require_noerr_action_quiet
+ #define require_action_expect require_action
+ #define require_action_quiet_expect require_action_quiet
+ #define require_action_string_expect require_action_string
+#endif
+
+#if 0
+#pragma mark == Output macros ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined debug_string
+
+ @abstract Prints a debugging C string.
+ */
+
+#if ( DEBUG_OVERRIDE_APPLE_MACROS )
+ #undef debug_string
+#endif
+#if ( !defined( debug_string ) )
+ #if ( DEBUG )
+ #define debug_string( STR ) \
+ do \
+ { \
+ debug_print_assert( 0, NULL, STR, __FILE__, __LINE__, __ROUTINE__ ); \
+ \
+ } while( 0 )
+ #else
+ #define debug_string( STR )
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined debug_print_assert
+
+ @abstract Prints an assertion.
+ */
+
+#if ( DEBUG )
+ #define debug_print_assert( ERROR_CODE, ASSERT_STRING, MESSAGE, FILENAME, LINE_NUMBER, FUNCTION ) \
+ DebugPrintAssert( ERROR_CODE, ASSERT_STRING, MESSAGE, FILENAME, LINE_NUMBER, FUNCTION )
+#else
+ #define debug_print_assert( ERROR_CODE, ASSERT_STRING, MESSAGE, FILENAME, LINE_NUMBER, FUNCTION )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined dlog
+
+ @abstract Prints a debug-only message.
+ */
+
+#if ( DEBUG )
+ #if ( DEBUG_C99_VA_ARGS )
+ #define dlog(... ) DebugPrintF( __VA_ARGS__ )
+ #elif ( DEBUG_GNU_VA_ARGS )
+ #define dlog( ARGS... ) DebugPrintF( ## ARGS )
+ #else
+ #define dlog DebugPrintF
+ #endif
+#else
+ #if ( DEBUG_C99_VA_ARGS )
+ #define dlog(... )
+ #elif ( DEBUG_GNU_VA_ARGS )
+ #define dlog( ARGS... )
+ #else
+ #define dlog while( 0 )
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined dlogv
+
+ @abstract Prints a debug-only message.
+ */
+
+#if ( DEBUG )
+ #define dlogv( LEVEL, FORMAT, LIST ) DebugPrintFVAList( ( LEVEL ), ( FORMAT ), ( LIST ) )
+#else
+ #define dlogv( LEVEL, FORMAT, LIST )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined dlogmem
+
+ @abstract Prints a debug-only dump of memory.
+ */
+
+#if ( DEBUG )
+ #define dlogmem( LEVEL, PTR, SIZE ) \
+ DebugHexDump( ( LEVEL ), 0, NULL, 0, 0, NULL, 0, ( PTR ), ( PTR ), ( SIZE ), kDebugFlagsNone, NULL, 0 )
+#else
+ #define dlogmem( LEVEL, PTR, SIZE )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DebugNSLog
+
+ @abstract Debug-only macro for the Cocoa NSLog function.
+ */
+
+#if ( DEBUG )
+ #if ( DEBUG_C99_VA_ARGS )
+ #define DebugNSLog(... ) NSLog( __VA_ARGS__ )
+ #elif ( DEBUG_GNU_VA_ARGS )
+ #define DebugNSLog( ARGS... ) NSLog( ## ARGS )
+ #else
+ #define DebugNSLog NSLog
+ #endif
+#else
+ #if ( DEBUG_C99_VA_ARGS )
+ #define DebugNSLog(... )
+ #elif ( DEBUG_GNU_VA_ARGS )
+ #define DebugNSLog( ARGS... )
+ #else
+ #define DebugNSLog while( 0 )
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DebugLogMsg
+
+ @abstract Debug-only macro for the VxWorks logMsg function.
+ */
+
+#if ( TARGET_OS_VXWORKS )
+ #if ( DEBUG )
+ #define DebugLogMsg( LEVEL, FORMAT, P1, P2, P3, P4, P5, P6 ) \
+ do \
+ { \
+ if( ( inLevel >= gDebugPrintLevelMin ) || ( inLevel <= gDebugPrintLevelMax ) ) \
+ { \
+ logMsg( ( FORMAT ), ( P1 ), ( P2 ), ( P3 ), ( P4 ), ( P5 ), ( P6 ) ); \
+ } \
+ \
+ } while( 0 )
+ #else
+ #define DebugLogMsg( LEVEL, FORMAT, P1, P2, P3, P4, P5, P6 )
+ #endif
+#else
+ #define DebugLogMsg dlog
+#endif
+
+#if 0
+#pragma mark == Routines - General ==
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugInitialize
+
+ @abstract Initializes the debugging library for a specific kind of output.
+
+ @param inType
+ @param varArg Variable number parameters, controlled by the "inType" parameter.
+ */
+
+#if ( DEBUG )
+DEBUG_EXPORT OSStatus DebugInitialize( DebugOutputType inType, ... );
+#endif
+
+#if ( DEBUG )
+ #if ( DEBUG_C99_VA_ARGS )
+ #define debug_initialize(... ) DebugInitialize( __VA_ARGS__ )
+ #elif ( DEBUG_GNU_VA_ARGS )
+ #define debug_initialize( ARGS... ) DebugInitialize( ## ARGS )
+ #else
+ #define debug_initialize DebugInitialize
+ #endif
+#else
+ #if ( DEBUG_C99_VA_ARGS )
+ #define debug_initialize(... )
+ #elif ( DEBUG_GNU_VA_ARGS )
+ #define debug_initialize( ARGS... )
+ #else
+ #define debug_initialize while( 0 )
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugFinalize
+
+ @abstract Releases any resources used by the debugging library
+ */
+
+#if ( DEBUG )
+DEBUG_EXPORT void DebugFinalize( void );
+#endif
+
+#if ( DEBUG )
+ #define debug_terminate() DebugFinalize()
+#else
+ #define debug_terminate()
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugGetProperty
+
+ @abstract Gets the specified property from the debugging library.
+ */
+
+#if ( DEBUG )
+DEBUG_EXPORT OSStatus DebugGetProperty( DebugPropertyTag inTag, ... );
+#endif
+
+#if ( DEBUG )
+ #if ( DEBUG_C99_VA_ARGS )
+ #define debug_get_property(... ) DebugGetProperty( __VA_ARGS__ )
+ #elif ( DEBUG_GNU_VA_ARGS )
+ #define debug_get_property( ARGS... ) DebugGetProperty( ## ARGS )
+ #else
+ #define debug_get_property DebugGetProperty
+ #endif
+#else
+ #if ( DEBUG_C99_VA_ARGS )
+ #define debug_get_property(... )
+ #elif ( DEBUG_GNU_VA_ARGS )
+ #define debug_get_property( ARGS... )
+ #else
+ #define debug_get_property while( 0 )
+ #endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugSetProperty
+
+ @abstract Sets the specified property from the debugging library.
+ */
+
+#if ( DEBUG )
+DEBUG_EXPORT OSStatus DebugSetProperty( DebugPropertyTag inTag, ... );
+#endif
+
+#if ( DEBUG )
+ #if ( DEBUG_C99_VA_ARGS )
+ #define debug_set_property(... ) DebugSetProperty( __VA_ARGS__ )
+ #elif ( DEBUG_GNU_VA_ARGS )
+ #define debug_set_property( ARGS... ) DebugSetProperty( ## ARGS )
+ #else
+ #define debug_set_property DebugSetProperty
+ #endif
+#else
+ #if ( DEBUG_C99_VA_ARGS )
+ #define debug_set_property(... )
+ #elif ( DEBUG_GNU_VA_ARGS )
+ #define debug_set_property( ARGS... )
+ #else
+ #define debug_set_property while( 0 )
+ #endif
+#endif
+
+#if 0
+#pragma mark == Routines - Debugging Output ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugPrintF
+
+ @abstract Prints a debug message with printf-style formatting.
+
+ @param inLevel Error that generated this assert or noErr.
+
+ @param inFormatString
+ C string containing assertion text.
+
+ @param VAR_ARG
+ Variable number of arguments depending on the format string.
+
+ @result Number of bytes printed or -1 on error.
+ */
+
+#if ( DEBUG )
+DEBUG_EXPORT size_t DebugPrintF( DebugLevel inLevel, const char *inFormat, ... );
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugPrintFVAList
+
+ @abstract va_list version of DebugPrintF. See DebugPrintF for more info.
+ */
+
+#if ( DEBUG )
+DEBUG_EXPORT size_t DebugPrintFVAList( DebugLevel inLevel, const char *inFormat, va_list inArgs );
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugPrintAssert
+
+ @abstract Prints a message describing the reason the (e.g. an assert failed), an optional error message,
+ an optional source filename, an optional source line number.
+
+ @param inErrorCode Error that generated this assert or noErr.
+ @param inAssertString C string containing assertion text.
+ @param inMessage C string containing a message about the assert.
+ @param inFileName C string containing path of file where the error occurred.
+ @param inLineNumber Line number in source file where the error occurred.
+ @param inFunction C string containing name of function where assert occurred.
+
+ @discussion
+
+ Example output:
+
+ [ASSERT] assert: "dataPtr != NULL" allocate memory for object failed
+ [ASSERT] where: "MyFile.c", line 123, ("MyFunction")
+
+ OR
+
+ [ASSERT] error: -6728 (kNoMemoryErr)
+ [ASSERT] where: "MyFile.c", line 123, ("MyFunction")
+ */
+
+#if ( DEBUG )
+DEBUG_EXPORT void
+DebugPrintAssert(
+ int_least32_t inErrorCode,
+ const char * inAssertString,
+ const char * inMessage,
+ const char * inFilename,
+ int_least32_t inLineNumber,
+ const char * inFunction );
+#endif
+
+#if 0
+#pragma mark == Routines - Utilities ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugSNPrintF
+
+ @abstract Debugging versions of standard C snprintf with extra features.
+
+ @param sbuffer Buffer to receive result. Null terminated unless the buffer size is 0.
+ @param buflen Size of the buffer including space for the null terminator.
+ @param fmt printf-style format string.
+ @param VAR_ARG Variable number of arguments depending on the format string.
+
+ @result Number of characters written (minus the null terminator).
+
+ @discussion
+
+ Extra features over the standard C snprintf:
+ <pre>
+ 64-bit support for %d (%lld), %i (%lli), %u (%llu), %o (%llo), %x (%llx), and %b (%llb).
+ %@ - Cocoa/CoreFoundation object. Param is the object. Strings are used directly. Others use CFCopyDescription.
+ %a - Network Address: %.4a=IPv4, %.6a=Ethernet, %.8a Fibre Channel, %.16a=IPv6. Arg=ptr to network address.
+ %#a - IPv4 or IPv6 mDNSAddr. Arg=ptr to mDNSAddr.
+ %##a - IPv4 (if AF_INET defined) or IPv6 (if AF_INET6 defined) sockaddr. Arg=ptr to sockaddr.
+ %b - Binary representation of integer (e.g. 01101011). Modifiers and arg=the same as %d, %x, etc.
+ %C - Mac-style FourCharCode (e.g. 'APPL'). Arg=32-bit value to print as a Mac-style FourCharCode.
+ %H - Hex Dump (e.g. "\x6b\xa7" -> "6B A7"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size.
+ %#H - Hex Dump & ASCII (e.g. "\x41\x62" -> "6B A7 'Ab'"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size.
+ %m - Error Message (e.g. 0 -> "kNoErr"). Modifiers and error code arg=the same as %d, %x, etc.
+ %#s - Pascal-style length-prefixed string. Arg=ptr to string.
+ %##s - DNS label-sequence name. Arg=ptr to name.
+ %S - UTF-16 string, 0x0000 terminated. Host order if no BOM. Precision is UTF-16 count. Precision includes BOM.
+ %#S - Big Endian UTF-16 string (unless BOM overrides). Otherwise, the same as %S.
+ %##S - Little Endian UTF-16 string (unless BOM overrides). Otherwise, the same as %S.
+ %U - Universally Unique Identifier (UUID) (e.g. 6ba7b810-9dad-11d1-80b4-00c04fd430c8). Arg=ptr to 16-byte UUID.
+ </pre>
+ */
+
+#if ( DEBUG )
+DEBUG_EXPORT size_t DebugSNPrintF(char *sbuffer, size_t buflen, const char *fmt, ...);
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugSNPrintFVAList
+
+ @abstract va_list version of DebugSNPrintF. See DebugSNPrintF for more info.
+ */
+
+#if ( DEBUG )
+DEBUG_EXPORT size_t DebugSNPrintFVAList(char *sbuffer, size_t buflen, const char *fmt, va_list arg);
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugGetErrorString
+
+ @abstract Gets an error string from an error code.
+
+ @param inStatus Error code to get the string for.
+ @param inBuffer Optional buffer to copy the string to for non-static strings. May be null.
+ @param inBufferSize Size of optional buffer. May be 0.
+
+ @result C string containing error string for the error code. Guaranteed to be a valid, static string. If a
+ buffer is supplied, the return value will always be a pointer to the supplied buffer, which will
+ contain the best available description of the error code. If a buffer is not supplied, the return
+ value will be the best available description of the error code that can be represented as a static
+ string. This allows code that cannot use a temporary buffer to hold the result to still get a useful
+ error string in most cases, but also allows code that can use a temporary buffer to get the best
+ available description.
+ */
+
+#if ( DEBUG )
+DEBUG_EXPORT const char * DebugGetErrorString( int_least32_t inErrorCode, char *inBuffer, size_t inBufferSize );
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugHexDump
+
+ @abstract Hex dumps data to a string or to the output device.
+ */
+
+#if ( DEBUG )
+DEBUG_EXPORT size_t
+DebugHexDump(
+ DebugLevel inLevel,
+ int inIndent,
+ const char * inLabel,
+ size_t inLabelSize,
+ int inLabelMinWidth,
+ const char * inType,
+ size_t inTypeSize,
+ const void * inDataStart,
+ const void * inData,
+ size_t inDataSize,
+ DebugFlags inFlags,
+ char * outBuffer,
+ size_t inBufferSize );
+#endif
+
+#if ( DEBUG )
+ #define dloghex( LEVEL, INDENT, LABEL, LABEL_SIZE, LABEL_MIN_SIZE, TYPE, TYPE_SIZE, DATA_START, DATA, DATA_SIZE, FLAGS, BUFFER, BUFFER_SIZE ) \
+ DebugHexDump( ( LEVEL ), (INDENT), ( LABEL ), ( LABEL_SIZE ), ( LABEL_MIN_SIZE ), ( TYPE ), ( TYPE_SIZE ), \
+ ( DATA_START ), ( DATA ), ( DATA_SIZE ), ( FLAGS ), ( BUFFER ), ( BUFFER_SIZE ) )
+#else
+ #define dloghex( LEVEL, INDENT, LABEL, LABEL_SIZE, LABEL_MIN_SIZE, TYPE, TYPE_SIZE, DATA_START, DATA, DATA_SIZE, FLAGS, BUFFER, BUFFER_SIZE )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugTaskLevel
+
+ @abstract Returns the current task level.
+
+ @result Current task level
+
+ @discussion
+
+ Bit masks to isolate portions of the result (note that some masks may also need bit shifts to right justify):
+ <pre>
+ kDebugInterruptLevelMask - Indicates the current interrupt level (> 0 means interrupt time).
+ kDebugInVBLTaskMask - Indicates if a VBL task is currently being executed.
+ kDebugInDeferredTaskMask - Indicates if a Deferred Task is currently being executed.
+ kDebugInSecondaryInterruptHandlerMask - Indicates if a Secondary Interrupt Handler is currently being executed.
+ kDebugPageFaultFatalMask - Indicates if it is unsafe to cause a page fault (worse than interrupt time).
+ kDebugMPTaskLevelMask - Indicates if being called from an MP task.
+ kDebugInterruptDepthMask - 0 means task level, 1 means in interrupt, > 1 means in nested interrupt.
+ </pre>
+
+ Helpers:
+ <pre>
+ DebugExtractTaskLevelInterruptDepth() - Macro to extract interrupt depth from task level value.
+ </pre>
+ */
+
+#if ( DEBUG )
+DEBUG_EXPORT uint32_t DebugTaskLevel( void );
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DebugServicesTest
+
+ @abstract Unit test.
+ */
+
+#if ( DEBUG )
+DEBUG_EXPORT OSStatus DebugServicesTest( void );
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __DEBUG_SERVICES__
diff --git a/mDNSResponder/mDNSShared/GenLinkedList.c b/mDNSResponder/mDNSShared/GenLinkedList.c
new file mode 100644
index 00000000..45dbb7cb
--- /dev/null
+++ b/mDNSResponder/mDNSShared/GenLinkedList.c
@@ -0,0 +1,319 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+
+ File: GenLinkedList.c
+
+ Contains: implementation of generic linked lists.
+
+ Version: 1.0
+ Tabs: 4 spaces
+ */
+
+#include "GenLinkedList.h"
+
+
+// Return the link pointer contained within element e at offset o.
+#define GETLINK( e, o) ( *(void**)((char*) (e) + (o)) )
+
+// Assign the link pointer l to element e at offset o.
+#define ASSIGNLINK( e, l, o) ( *((void**)((char*) (e) + (o))) = (l))
+
+
+// GenLinkedList /////////////////////////////////////////////////////////////
+
+void InitLinkedList( GenLinkedList *pList, size_t linkOffset)
+/* Initialize the block of memory pointed to by pList as a linked list. */
+{
+ pList->Head = NULL;
+ pList->Tail = NULL;
+ pList->LinkOffset = linkOffset;
+}
+
+
+void AddToTail( GenLinkedList *pList, void *elem)
+/* Add a linked list element to the tail of the list. */
+{
+ if ( pList->Tail) {
+ ASSIGNLINK( pList->Tail, elem, pList->LinkOffset);
+ } else
+ pList->Head = elem;
+ ASSIGNLINK( elem, NULL, pList->LinkOffset);
+
+ pList->Tail = elem;
+}
+
+
+void AddToHead( GenLinkedList *pList, void *elem)
+/* Add a linked list element to the head of the list. */
+{
+ ASSIGNLINK( elem, pList->Head, pList->LinkOffset);
+ if ( pList->Tail == NULL)
+ pList->Tail = elem;
+
+ pList->Head = elem;
+}
+
+
+int RemoveFromList( GenLinkedList *pList, void *elem)
+/* Remove a linked list element from the list. Return 0 if it was not found. */
+/* If the element is removed, its link will be set to NULL. */
+{
+ void *iElem, *lastElem;
+
+ for ( iElem = pList->Head, lastElem = NULL; iElem; iElem = GETLINK( iElem, pList->LinkOffset)) {
+ if ( iElem == elem) {
+ if ( lastElem) { // somewhere past the head
+ ASSIGNLINK( lastElem, GETLINK( elem, pList->LinkOffset), pList->LinkOffset);
+ } else { // at the head
+ pList->Head = GETLINK( elem, pList->LinkOffset);
+ }
+ if ( pList->Tail == elem)
+ pList->Tail = lastElem ? lastElem : NULL;
+ ASSIGNLINK( elem, NULL, pList->LinkOffset); // maybe catch a stale reference bug.
+ return 1;
+ }
+ lastElem = iElem;
+ }
+
+ return 0;
+}
+
+
+int ReplaceElem( GenLinkedList *pList, void *elemInList, void *newElem)
+/* Replace an element in the list with a new element, in the same position. */
+{
+ void *iElem, *lastElem;
+
+ if ( elemInList == NULL || newElem == NULL)
+ return 0;
+
+ for ( iElem = pList->Head, lastElem = NULL; iElem; iElem = GETLINK( iElem, pList->LinkOffset))
+ {
+ if ( iElem == elemInList)
+ {
+ ASSIGNLINK( newElem, GETLINK( elemInList, pList->LinkOffset), pList->LinkOffset);
+ if ( lastElem) // somewhere past the head
+ {
+ ASSIGNLINK( lastElem, newElem, pList->LinkOffset);
+ }
+ else // at the head
+ {
+ pList->Head = newElem;
+ }
+ if ( pList->Tail == elemInList)
+ pList->Tail = newElem;
+ return 1;
+ }
+ lastElem = iElem;
+ }
+
+ return 0;
+}
+
+
+// GenDoubleLinkedList /////////////////////////////////////////////////////////
+
+void InitDoubleLinkedList( GenDoubleLinkedList *pList, size_t fwdLinkOffset,
+ size_t backLinkOffset)
+/* Initialize the block of memory pointed to by pList as a double linked list. */
+{
+ pList->Head = NULL;
+ pList->Tail = NULL;
+ pList->FwdLinkOffset = fwdLinkOffset;
+ pList->BackLinkOffset = backLinkOffset;
+}
+
+
+void DLLAddToHead( GenDoubleLinkedList *pList, void *elem)
+/* Add a linked list element to the head of the list. */
+{
+ void *pNext;
+
+ pNext = pList->Head;
+
+ // fix up the forward links
+ ASSIGNLINK( elem, pList->Head, pList->FwdLinkOffset);
+ pList->Head = elem;
+
+ // fix up the backward links
+ if ( pNext) {
+ ASSIGNLINK( pNext, elem, pList->BackLinkOffset);
+ } else
+ pList->Tail = elem;
+ ASSIGNLINK( elem, NULL, pList->BackLinkOffset);
+}
+
+
+void DLLRemoveFromList( GenDoubleLinkedList *pList, void *elem)
+/* Remove a linked list element from the list. */
+/* When the element is removed, its link will be set to NULL. */
+{
+ void *pNext, *pPrev;
+
+ pNext = GETLINK( elem, pList->FwdLinkOffset);
+ pPrev = GETLINK( elem, pList->BackLinkOffset);
+
+ // fix up the forward links
+ if ( pPrev)
+ ASSIGNLINK( pPrev, pNext, pList->FwdLinkOffset);
+ else
+ pList->Head = pNext;
+
+ // fix up the backward links
+ if ( pNext)
+ ASSIGNLINK( pNext, pPrev, pList->BackLinkOffset);
+ else
+ pList->Tail = pPrev;
+
+ ASSIGNLINK( elem, NULL, pList->FwdLinkOffset);
+ ASSIGNLINK( elem, NULL, pList->BackLinkOffset);
+}
+
+
+// GenLinkedOffsetList /////////////////////////////////////////////////////
+
+// Extract the Next offset from element
+#define GETOFFSET( e, o) ( *(size_t*)((char*) (e) + (o)) )
+
+static void AssignOffsetLink( void *elem, void *link, size_t linkOffset);
+
+
+static void AssignOffsetLink( void *elem, void *link, size_t linkOffset)
+// Assign link to elem as an offset from elem. Assign 0 to elem if link is NULL.
+{
+ GETOFFSET( elem, linkOffset) = link ? (size_t) link - (size_t) elem : 0;
+}
+
+
+void *GetHeadPtr( GenLinkedOffsetList *pList)
+/* Return a pointer to the head element of a list, or NULL if none. */
+{
+ return pList->Head ? ( (char*) (pList) + pList->Head) : NULL;
+}
+
+
+void *GetTailPtr( GenLinkedOffsetList *pList)
+/* Return a pointer to the tail element of a list, or NULL if none. */
+{
+ return pList->Tail ? ( (char*) (pList) + pList->Tail) : NULL;
+}
+
+
+void *GetOffsetLink( GenLinkedOffsetList *pList, void *elem)
+/* Return the link pointer contained within element e for pList, or NULL if it is 0. */
+{
+ size_t nextOffset;
+
+ nextOffset = GETOFFSET( elem, pList->LinkOffset);
+
+ return nextOffset ? (char*) elem + nextOffset : NULL;
+}
+
+
+void InitLinkedOffsetList( GenLinkedOffsetList *pList, size_t linkOffset)
+/* Initialize the block of memory pointed to by pList as a linked list. */
+{
+ pList->Head = 0;
+ pList->Tail = 0;
+ pList->LinkOffset = linkOffset;
+}
+
+
+void OffsetAddToTail( GenLinkedOffsetList *pList, void *elem)
+/* Add a linked list element to the tail of the list. */
+{
+ if ( pList->Tail) {
+ AssignOffsetLink( GetTailPtr( pList), elem, pList->LinkOffset);
+ } else
+ pList->Head = (size_t) elem - (size_t) pList;
+ AssignOffsetLink( elem, NULL, pList->LinkOffset);
+
+ pList->Tail = (size_t) elem - (size_t) pList;
+}
+
+
+void OffsetAddToHead( GenLinkedOffsetList *pList, void *elem)
+/* Add a linked list element to the head of the list. */
+{
+ AssignOffsetLink( elem, GetHeadPtr( pList), pList->LinkOffset);
+ if ( pList->Tail == 0)
+ pList->Tail = (size_t) elem - (size_t) pList;
+
+ pList->Head = (size_t) elem - (size_t) pList;
+}
+
+
+int OffsetRemoveFromList( GenLinkedOffsetList *pList, void *elem)
+/* Remove a linked list element from the list. Return 0 if it was not found. */
+/* If the element is removed, its link will be set to NULL. */
+{
+ void *iElem, *lastElem;
+
+ for ( iElem = GetHeadPtr( pList), lastElem = NULL; iElem;
+ iElem = GetOffsetLink( pList, iElem))
+ {
+ if ( iElem == elem) {
+ if ( lastElem) { // somewhere past the head
+ AssignOffsetLink( lastElem, GetOffsetLink( pList, elem), pList->LinkOffset);
+ } else { // at the head
+ iElem = GetOffsetLink( pList, elem);
+ pList->Head = iElem ? (size_t) iElem - (size_t) pList : 0;
+ }
+ if ( GetTailPtr( pList) == elem)
+ pList->Tail = lastElem ? (size_t) lastElem - (size_t) pList : 0;
+ AssignOffsetLink( elem, NULL, pList->LinkOffset); // maybe catch a stale reference bug.
+ return 1;
+ }
+ lastElem = iElem;
+ }
+
+ return 0;
+}
+
+
+int OffsetReplaceElem( GenLinkedOffsetList *pList, void *elemInList, void *newElem)
+/* Replace an element in the list with a new element, in the same position. */
+{
+ void *iElem, *lastElem;
+
+ if ( elemInList == NULL || newElem == NULL)
+ return 0;
+
+ for ( iElem = GetHeadPtr( pList), lastElem = NULL; iElem;
+ iElem = GetOffsetLink( pList, iElem))
+ {
+ if ( iElem == elemInList)
+ {
+ AssignOffsetLink( newElem, GetOffsetLink( pList, elemInList), pList->LinkOffset);
+ if ( lastElem) // somewhere past the head
+ {
+ AssignOffsetLink( lastElem, newElem, pList->LinkOffset);
+ }
+ else // at the head
+ {
+ pList->Head = (size_t) newElem - (size_t) pList;
+ }
+ if ( GetTailPtr( pList) == elemInList)
+ pList->Tail = (size_t) newElem - (size_t) pList;
+ return 1;
+ }
+ lastElem = iElem;
+ }
+
+ return 0;
+}
+
+
diff --git a/mDNSResponder/mDNSShared/GenLinkedList.h b/mDNSResponder/mDNSShared/GenLinkedList.h
new file mode 100644
index 00000000..2d0ada6d
--- /dev/null
+++ b/mDNSResponder/mDNSShared/GenLinkedList.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __GenLinkedList__
+#define __GenLinkedList__
+
+
+#include <stddef.h>
+
+
+struct GenLinkedList
+{
+ void *Head,
+ *Tail;
+ size_t LinkOffset;
+};
+typedef struct GenLinkedList GenLinkedList;
+
+
+void InitLinkedList( GenLinkedList *pList, size_t linkOffset);
+
+void AddToHead( GenLinkedList *pList, void *elem);
+void AddToTail( GenLinkedList *pList, void *elem);
+
+int RemoveFromList( GenLinkedList *pList, void *elem);
+
+int ReplaceElem( GenLinkedList *pList, void *elemInList, void *newElem);
+
+
+
+struct GenDoubleLinkedList
+{
+ void *Head,
+ *Tail;
+ size_t FwdLinkOffset,
+ BackLinkOffset;
+};
+typedef struct GenDoubleLinkedList GenDoubleLinkedList;
+
+
+void InitDoubleLinkedList( GenDoubleLinkedList *pList, size_t fwdLinkOffset,
+ size_t backLinkOffset);
+
+void DLLAddToHead( GenDoubleLinkedList *pList, void *elem);
+
+void DLLRemoveFromList( GenDoubleLinkedList *pList, void *elem);
+
+
+
+/* A GenLinkedOffsetList is like a GenLinkedList that stores the *Next field as a signed */
+/* offset from the address of the beginning of the element, rather than as a pointer. */
+
+struct GenLinkedOffsetList
+{
+ size_t Head,
+ Tail;
+ size_t LinkOffset;
+};
+typedef struct GenLinkedOffsetList GenLinkedOffsetList;
+
+
+void InitLinkedOffsetList( GenLinkedOffsetList *pList, size_t linkOffset);
+
+void *GetHeadPtr( GenLinkedOffsetList *pList);
+void *GetTailPtr( GenLinkedOffsetList *pList);
+void *GetOffsetLink( GenLinkedOffsetList *pList, void *elem);
+
+void OffsetAddToHead( GenLinkedOffsetList *pList, void *elem);
+void OffsetAddToTail( GenLinkedOffsetList *pList, void *elem);
+
+int OffsetRemoveFromList( GenLinkedOffsetList *pList, void *elem);
+
+int OffsetReplaceElem( GenLinkedOffsetList *pList, void *elemInList, void *newElem);
+
+
+#endif // __GenLinkedList__
diff --git a/mDNSResponder/mDNSShared/Java/BaseListener.java b/mDNSResponder/mDNSShared/Java/BaseListener.java
new file mode 100644
index 00000000..b99d341c
--- /dev/null
+++ b/mDNSResponder/mDNSShared/Java/BaseListener.java
@@ -0,0 +1,36 @@
+/* -*- Mode: Java; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.apple.dnssd;
+
+
+/** A base class for DNSSD listeners. */
+
+public interface BaseListener
+{
+ /** Called to report DNSSD operation failures.<P>
+
+ @param service
+ The service that encountered the failure.
+ <P>
+ @param errorCode
+ Indicates the failure that occurred. See {@link DNSSDException} for error codes.
+ */
+ void operationFailed( DNSSDService service, int errorCode);
+}
+
diff --git a/mDNSResponder/mDNSShared/Java/BrowseListener.java b/mDNSResponder/mDNSShared/Java/BrowseListener.java
new file mode 100644
index 00000000..b92b9dc5
--- /dev/null
+++ b/mDNSResponder/mDNSShared/Java/BrowseListener.java
@@ -0,0 +1,73 @@
+/* -*- Mode: Java; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.apple.dnssd;
+
+
+/** A listener that receives results from {@link DNSSD#browse}. */
+
+public interface BrowseListener extends BaseListener
+{
+ /** Called to report discovered services.<P>
+
+ @param browser
+ The active browse service.
+ <P>
+ @param flags
+ Possible values are DNSSD.MORE_COMING.
+ <P>
+ @param ifIndex
+ The interface on which the service is advertised. This index should be passed
+ to {@link DNSSD#resolve} when resolving the service.
+ <P>
+ @param serviceName
+ The service name discovered.
+ <P>
+ @param regType
+ The registration type, as passed in to DNSSD.browse().
+ <P>
+ @param domain
+ The domain in which the service was discovered.
+ */
+ void serviceFound( DNSSDService browser, int flags, int ifIndex,
+ String serviceName, String regType, String domain);
+
+ /** Called to report services which have been deregistered.<P>
+
+ @param browser
+ The active browse service.
+ <P>
+ @param flags
+ Possible values are DNSSD.MORE_COMING.
+ <P>
+ @param ifIndex
+ The interface on which the service is advertised.
+ <P>
+ @param serviceName
+ The service name which has deregistered.
+ <P>
+ @param regType
+ The registration type, as passed in to DNSSD.browse().
+ <P>
+ @param domain
+ The domain in which the service was discovered.
+ */
+ void serviceLost( DNSSDService browser, int flags, int ifIndex,
+ String serviceName, String regType, String domain);
+}
+
diff --git a/mDNSResponder/mDNSShared/Java/DNSRecord.java b/mDNSResponder/mDNSShared/Java/DNSRecord.java
new file mode 100644
index 00000000..a853d091
--- /dev/null
+++ b/mDNSResponder/mDNSShared/Java/DNSRecord.java
@@ -0,0 +1,52 @@
+/* -*- Mode: Java; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.apple.dnssd;
+
+
+/**
+ Reference to a record returned by {@link DNSSDRegistration#addRecord}.<P>
+
+ Note: client is responsible for serializing access to these objects if
+ they are shared between concurrent threads.
+*/
+
+public interface DNSRecord
+{
+ /** Update a registered resource record.<P>
+ The record must either be the primary txt record of a service registered via DNSSD.register(),
+ or a record added to a registered service via addRecord().<P>
+
+ @param flags
+ Currently unused, reserved for future use.
+ <P>
+ @param rData
+ The new rdata to be contained in the updated resource record.
+ <P>
+ @param ttl
+ The time to live of the updated resource record, in seconds.
+ */
+ void update( int flags, byte[] rData, int ttl)
+ throws DNSSDException;
+
+ /** Remove a registered resource record.<P>
+ */
+ void remove()
+ throws DNSSDException;
+}
+
diff --git a/mDNSResponder/mDNSShared/Java/DNSSD.java b/mDNSResponder/mDNSShared/Java/DNSSD.java
new file mode 100644
index 00000000..f749a88e
--- /dev/null
+++ b/mDNSResponder/mDNSShared/Java/DNSSD.java
@@ -0,0 +1,860 @@
+/* -*- Mode: Java; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+
+ This file declares and implements DNSSD, the central Java factory class
+ for doing DNS Service Discovery. It includes the mostly-abstract public
+ interface, as well as the Apple* implementation subclasses.
+ */
+
+
+package com.apple.dnssd;
+
+
+/**
+ DNSSD provides access to DNS Service Discovery features of ZeroConf networking.<P>
+
+ It is a factory class that is used to invoke registration and discovery-related
+ operations. Most operations are non-blocking; clients are called back through an interface
+ with the result of an operation. Callbacks are made from a separate worker thread.<P>
+
+ For example, in this program<P>
+ <PRE><CODE>
+ class MyClient implements BrowseListener {
+ void lookForWebServers() {
+ myBrowser = DNSSD.browse("_http._tcp", this);
+ }
+
+ public void serviceFound(DNSSDService browser, int flags, int ifIndex,
+ String serviceName, String regType, String domain) {}
+ ...
+ }</CODE></PRE>
+ <CODE>MyClient.serviceFound()</CODE> would be called for every HTTP server discovered in the
+ default browse domain(s).
+*/
+
+abstract public class DNSSD
+{
+ /** Flag indicates to a {@link BrowseListener} that another result is
+ queued. Applications should not update their UI to display browse
+ results if the MORE_COMING flag is set; they will be called at least once
+ more with the flag clear.
+ */
+ public static final int MORE_COMING = ( 1 << 0 );
+
+ /** If flag is set in a {@link DomainListener} callback, indicates that the result is the default domain. */
+ public static final int DEFAULT = ( 1 << 2 );
+
+ /** If flag is set, a name conflict will trigger an exception when registering non-shared records.<P>
+ A name must be explicitly specified when registering a service if this bit is set
+ (i.e. the default name may not not be used).
+ */
+ public static final int NO_AUTO_RENAME = ( 1 << 3 );
+
+ /** If flag is set, allow multiple records with this name on the network (e.g. PTR records)
+ when registering individual records on a {@link DNSSDRegistration}.
+ */
+ public static final int SHARED = ( 1 << 4 );
+
+ /** If flag is set, records with this name must be unique on the network (e.g. SRV records). */
+ public static final int UNIQUE = ( 1 << 5 );
+
+ /** Set flag when calling enumerateDomains() to restrict results to domains recommended for browsing. */
+ public static final int BROWSE_DOMAINS = ( 1 << 6 );
+ /** Set flag when calling enumerateDomains() to restrict results to domains recommended for registration. */
+ public static final int REGISTRATION_DOMAINS = ( 1 << 7 );
+
+ /** Maximum length, in bytes, of a domain name represented as an escaped C-String. */
+ public static final int MAX_DOMAIN_NAME = 1009;
+
+ /** Pass for ifIndex to specify all available interfaces. */
+ public static final int ALL_INTERFACES = 0;
+
+ /** Pass for ifIndex to specify the localhost interface. */
+ public static final int LOCALHOST_ONLY = -1;
+
+ /** Browse for instances of a service.<P>
+
+ Note: browsing consumes network bandwidth. Call {@link DNSSDService#stop} when you have finished browsing.<P>
+
+ @param flags
+ Currently ignored, reserved for future use.
+ <P>
+ @param ifIndex
+ 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.
+ <P>
+ @param regType
+ The registration type being browsed for followed by the protocol, separated by a
+ dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
+ <P>
+ @param 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).
+ <P>
+ @param listener
+ This object will get called when instances of the service are discovered (or disappear).
+ <P>
+ @return A {@link DNSSDService} that represents the active browse operation.
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public static DNSSDService browse( int flags, int ifIndex, String regType, String domain, BrowseListener listener)
+ throws DNSSDException
+ { return getInstance()._makeBrowser( flags, ifIndex, regType, domain, listener); }
+
+ /** Browse for instances of a service. Use default flags, ifIndex and domain.<P>
+
+ @param regType
+ The registration type being browsed for followed by the protocol, separated by a
+ dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
+ <P>
+ @param listener
+ This object will get called when instances of the service are discovered (or disappear).
+ <P>
+ @return A {@link DNSSDService} that represents the active browse operation.
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public static DNSSDService browse( String regType, BrowseListener listener)
+ throws DNSSDException
+ { return browse( 0, 0, regType, "", listener); }
+
+ /** Resolve a service name discovered via browse() to a target host name, port number, and txt record.<P>
+
+ Note: Applications should NOT use resolve() solely for txt record monitoring - use
+ queryRecord() instead, as it is more efficient for this task.<P>
+
+ Note: When the desired results have been returned, the client MUST terminate the resolve by
+ calling {@link DNSSDService#stop}.<P>
+
+ Note: resolve() 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, use queryRecord().<P>
+
+ @param flags
+ Currently ignored, reserved for future use.
+ <P>
+ @param ifIndex
+ The interface on which to resolve the service. The client should
+ pass the interface on which the serviceName was discovered (i.e.
+ the ifIndex passed to the serviceFound() callback)
+ or 0 to resolve the named service on all available interfaces.
+ <P>
+ @param serviceName
+ The servicename to be resolved.
+ <P>
+ @param regType
+ The registration type being resolved followed by the protocol, separated by a
+ dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
+ <P>
+ @param domain
+ The domain on which the service is registered, i.e. the domain passed
+ to the serviceFound() callback.
+ <P>
+ @param listener
+ This object will get called when the service is resolved.
+ <P>
+ @return A {@link DNSSDService} that represents the active resolve operation.
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public static DNSSDService resolve( int flags, int ifIndex, String serviceName, String regType,
+ String domain, ResolveListener listener)
+ throws DNSSDException
+ { return getInstance()._resolve( flags, ifIndex, serviceName, regType, domain, listener); }
+
+ /** Register a service, to be discovered via browse() and resolve() calls.<P>
+ @param flags
+ Possible values are: NO_AUTO_RENAME.
+ <P>
+ @param ifIndex
+ 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).
+ <P>
+ @param serviceName
+ If non-null, specifies the service name to be registered.
+ Applications need not specify a name, in which case the
+ computer name is used (this name is communicated to the client via
+ the serviceRegistered() callback).
+ <P>
+ @param regType
+ The registration type being registered followed by the protocol, separated by a
+ dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
+ <P>
+ @param 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).
+ <P>
+ @param 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 {@link DNSSDRegistration#addRecord}.
+ <P>
+ @param 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.
+ <P>
+ @param txtRecord
+ The txt record rdata. May be null. Note that a non-null txtRecord
+ MUST be a properly formatted DNS TXT record, i.e. &lt;length byte&gt; &lt;data&gt;
+ &lt;length byte&gt; &lt;data&gt; ...
+ <P>
+ @param listener
+ This object will get called when the service is registered.
+ <P>
+ @return A {@link DNSSDRegistration} that controls the active registration.
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public static DNSSDRegistration register( int flags, int ifIndex, String serviceName, String regType,
+ String domain, String host, int port, TXTRecord txtRecord, RegisterListener listener)
+ throws DNSSDException
+ { return getInstance()._register( flags, ifIndex, serviceName, regType, domain, host, port, txtRecord, listener); }
+
+ /** Register a service, to be discovered via browse() and resolve() calls. Use default flags, ifIndex, domain, host and txtRecord.<P>
+ @param serviceName
+ If non-null, specifies the service name to be registered.
+ Applications need not specify a name, in which case the
+ computer name is used (this name is communicated to the client via
+ the serviceRegistered() callback).
+ <P>
+ @param regType
+ The registration type being registered followed by the protocol, separated by a
+ dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
+ <P>
+ @param 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.
+ <P>
+ @param listener
+ This object will get called when the service is registered.
+ <P>
+ @return A {@link DNSSDRegistration} that controls the active registration.
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public static DNSSDRegistration register( String serviceName, String regType, int port, RegisterListener listener)
+ throws DNSSDException
+ { return register( 0, 0, serviceName, regType, null, null, port, null, listener); }
+
+ /** Create a {@link DNSSDRecordRegistrar} allowing efficient registration of
+ multiple individual records.<P>
+ <P>
+ @return A {@link DNSSDRecordRegistrar} that can be used to register records.
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public static DNSSDRecordRegistrar createRecordRegistrar( RegisterRecordListener listener)
+ throws DNSSDException
+ { return getInstance()._createRecordRegistrar( listener); }
+
+ /** Query for an arbitrary DNS record.<P>
+ @param flags
+ Possible values are: MORE_COMING.
+ <P>
+ @param ifIndex
+ 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.
+ <P>
+ @param serviceName
+ The full domain name of the resource record to be queried for.
+ <P>
+ @param rrtype
+ The numerical type of the resource record to be queried for (e.g. PTR, SRV, etc)
+ as defined in nameser.h.
+ <P>
+ @param rrclass
+ The class of the resource record, as defined in nameser.h
+ (usually 1 for the Internet class).
+ <P>
+ @param listener
+ This object will get called when the query completes.
+ <P>
+ @return A {@link DNSSDService} that controls the active query.
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public static DNSSDService queryRecord( int flags, int ifIndex, String serviceName, int rrtype,
+ int rrclass, QueryListener listener)
+ throws DNSSDException
+ { return getInstance()._queryRecord( flags, ifIndex, serviceName, rrtype, rrclass, listener); }
+
+ /** Asynchronously enumerate domains available for browsing and registration.<P>
+
+ Currently, the only domain returned is "local.", but other domains will be returned in future.<P>
+
+ The enumeration MUST be cancelled by calling {@link DNSSDService#stop} when no more domains
+ are to be found.<P>
+ @param flags
+ Possible values are: BROWSE_DOMAINS, REGISTRATION_DOMAINS.
+ <P>
+ @param ifIndex
+ 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.
+ <P>
+ @param listener
+ This object will get called when domains are found.
+ <P>
+ @return A {@link DNSSDService} that controls the active enumeration.
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public static DNSSDService enumerateDomains( int flags, int ifIndex, DomainListener listener)
+ throws DNSSDException
+ { return getInstance()._enumerateDomains( flags, ifIndex, listener); }
+
+ /** Concatenate a three-part domain name (as provided to the listeners) into a
+ properly-escaped full domain name. Note that strings passed to listeners are
+ ALREADY ESCAPED where necessary.<P>
+ @param serviceName
+ 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").
+ <P>
+ @param regType
+ The registration type followed by the protocol, separated by a dot (e.g. "_ftp._tcp").
+ <P>
+ @param domain
+ The domain name, e.g. "apple.com". Any literal dots or backslashes must be escaped.
+ <P>
+ @return The full domain name.
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public static String constructFullName( String serviceName, String regType, String domain)
+ throws DNSSDException
+ { return getInstance()._constructFullName( serviceName, regType, domain); }
+
+ /** 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.) <P>
+
+ 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.<P>
+ @param flags
+ Currently unused, reserved for future use.
+ <P>
+ @param ifIndex
+ If non-zero, specifies the interface on which to reconfirm the record
+ (the index for a given interface is determined via the if_nametoindex()
+ family of calls.) Passing 0 causes the name to be reconfirmed on all
+ interfaces. Passing -1 causes the name to be reconfirmed only on the
+ local host.
+ <P>
+ @param fullName
+ The resource record's full domain name.
+ <P>
+ @param rrtype
+ The resource record's type (e.g. PTR, SRV, etc) as defined in nameser.h.
+ <P>
+ @param rrclass
+ The class of the resource record, as defined in nameser.h (usually 1).
+ <P>
+ @param rdata
+ The raw rdata of the resource record.
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public static void reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype,
+ int rrclass, byte[] rdata)
+ { getInstance()._reconfirmRecord( flags, ifIndex, fullName, rrtype, rrclass, rdata); }
+
+ /** Return the canonical name of a particular interface index.<P>
+ @param ifIndex
+ A valid interface index. Must not be ALL_INTERFACES.
+ <P>
+ @return The name of the interface, which should match java.net.NetworkInterface.getName().
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public static String getNameForIfIndex( int ifIndex)
+ { return getInstance()._getNameForIfIndex( ifIndex); }
+
+ /** Return the index of a named interface.<P>
+ @param ifName
+ A valid interface name. An example is java.net.NetworkInterface.getName().
+ <P>
+ @return The interface index.
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public static int getIfIndexForName( String ifName)
+ { return getInstance()._getIfIndexForName( ifName); }
+
+ protected DNSSD() {} // prevent direct instantiation
+
+ /** Return the single instance of DNSSD. */
+ static protected final DNSSD getInstance()
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkPermission( new RuntimePermission( "getDNSSDInstance"));
+ return fInstance;
+ }
+
+ abstract protected DNSSDService _makeBrowser( int flags, int ifIndex, String regType, String domain, BrowseListener listener)
+ throws DNSSDException;
+
+ abstract protected DNSSDService _resolve( int flags, int ifIndex, String serviceName, String regType,
+ String domain, ResolveListener listener)
+ throws DNSSDException;
+
+ abstract protected DNSSDRegistration _register( int flags, int ifIndex, String serviceName, String regType,
+ String domain, String host, int port, TXTRecord txtRecord, RegisterListener listener)
+ throws DNSSDException;
+
+ abstract protected DNSSDRecordRegistrar _createRecordRegistrar( RegisterRecordListener listener)
+ throws DNSSDException;
+
+ abstract protected DNSSDService _queryRecord( int flags, int ifIndex, String serviceName, int rrtype,
+ int rrclass, QueryListener listener)
+ throws DNSSDException;
+
+ abstract protected DNSSDService _enumerateDomains( int flags, int ifIndex, DomainListener listener)
+ throws DNSSDException;
+
+ abstract protected String _constructFullName( String serviceName, String regType, String domain)
+ throws DNSSDException;
+
+ abstract protected void _reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype,
+ int rrclass, byte[] rdata);
+
+ abstract protected String _getNameForIfIndex( int ifIndex);
+
+ abstract protected int _getIfIndexForName( String ifName);
+
+ protected static DNSSD fInstance;
+
+ static
+ {
+ try
+ {
+ String name = System.getProperty( "com.apple.dnssd.DNSSD" );
+ if (name == null)
+ name = "com.apple.dnssd.AppleDNSSD"; // Fall back to Apple-provided class.
+ fInstance = (DNSSD) Class.forName(name).newInstance();
+ }
+ catch( Exception e )
+ {
+ throw new InternalError( "cannot instantiate DNSSD" + e );
+ }
+ }
+}
+
+
+// Concrete implementation of DNSSDException
+class AppleDNSSDException extends DNSSDException
+{
+ public AppleDNSSDException( int errorCode) { fErrorCode = errorCode; }
+
+ public int getErrorCode() { return fErrorCode; }
+
+ public String getMessage()
+ {
+ final String kMessages[] = { // should probably be put into a resource or something
+ "UNKNOWN",
+ "NO_SUCH_NAME",
+ "NO_MEMORY",
+ "BAD_PARAM",
+ "BAD_REFERENCE",
+ "BAD_STATE",
+ "BAD_FLAGS",
+ "UNSUPPORTED",
+ "NOT_INITIALIZED",
+ "NO_CACHE",
+ "ALREADY_REGISTERED",
+ "NAME_CONFLICT",
+ "INVALID",
+ "FIREWALL",
+ "INCOMPATIBLE",
+ "BAD_INTERFACE_INDEX",
+ "REFUSED",
+ "NOSUCHRECORD",
+ "NOAUTH",
+ "NOSUCHKEY",
+ "NATTRAVERSAL",
+ "DOUBLENAT",
+ "BADTIME",
+ "BADSIG",
+ "BADKEY",
+ "TRANSIENT",
+ "SERVICENOTRUNNING",
+ "NATPORTMAPPINGUNSUPPORTED",
+ "NATPORTMAPPINGDISABLED"
+ };
+
+ if (fErrorCode <= UNKNOWN && fErrorCode > ( UNKNOWN - kMessages.length))
+ {
+ return "DNS-SD Error " + String.valueOf( fErrorCode) + ": " + kMessages[ UNKNOWN - fErrorCode];
+ }
+ else
+ return super.getMessage() + "(" + String.valueOf( fErrorCode) + ")";
+ }
+
+ protected int fErrorCode;
+}
+
+// The concrete, default implementation.
+class AppleDNSSD extends DNSSD
+{
+ static
+ {
+ System.loadLibrary( "jdns_sd");
+
+ int libInitResult = InitLibrary( 2); // Current version number (must be sync'd with jnilib version)
+
+ if (libInitResult != DNSSDException.NO_ERROR)
+ throw new InternalError( "cannot instantiate DNSSD: " + new AppleDNSSDException( libInitResult).getMessage());
+ }
+
+ static public boolean hasAutoCallbacks; // Set by InitLibrary() to value of AUTO_CALLBACKS
+
+ protected DNSSDService _makeBrowser( int flags, int ifIndex, String regType, String domain, BrowseListener client)
+ throws DNSSDException
+ {
+ return new AppleBrowser( flags, ifIndex, regType, domain, client);
+ }
+
+ protected DNSSDService _resolve( int flags, int ifIndex, String serviceName, String regType,
+ String domain, ResolveListener client)
+ throws DNSSDException
+ {
+ return new AppleResolver( flags, ifIndex, serviceName, regType, domain, client);
+ }
+
+ protected DNSSDRegistration _register( int flags, int ifIndex, String serviceName, String regType,
+ String domain, String host, int port, TXTRecord txtRecord, RegisterListener client)
+ throws DNSSDException
+ {
+ return new AppleRegistration( flags, ifIndex, serviceName, regType, domain, host, port,
+ ( txtRecord != null) ? txtRecord.getRawBytes() : null, client);
+ }
+
+ protected DNSSDRecordRegistrar _createRecordRegistrar( RegisterRecordListener listener)
+ throws DNSSDException
+ {
+ return new AppleRecordRegistrar( listener);
+ }
+
+ protected DNSSDService _queryRecord( int flags, int ifIndex, String serviceName, int rrtype,
+ int rrclass, QueryListener client)
+ throws DNSSDException
+ {
+ return new AppleQuery( flags, ifIndex, serviceName, rrtype, rrclass, client);
+ }
+
+ protected DNSSDService _enumerateDomains( int flags, int ifIndex, DomainListener listener)
+ throws DNSSDException
+ {
+ return new AppleDomainEnum( flags, ifIndex, listener);
+ }
+
+ protected String _constructFullName( String serviceName, String regType, String domain)
+ throws DNSSDException
+ {
+ String[] responseHolder = new String[1]; // lame maneuver to get around Java's lack of reference parameters
+
+ int rc = ConstructName( serviceName, regType, domain, responseHolder);
+ if (rc != 0)
+ throw new AppleDNSSDException( rc);
+
+ return responseHolder[0];
+ }
+
+ protected void _reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype,
+ int rrclass, byte[] rdata)
+ {
+ ReconfirmRecord( flags, ifIndex, fullName, rrtype, rrclass, rdata);
+ }
+
+ protected String _getNameForIfIndex( int ifIndex)
+ {
+ return GetNameForIfIndex( ifIndex);
+ }
+
+ protected int _getIfIndexForName( String ifName)
+ {
+ return GetIfIndexForName( ifName);
+ }
+
+
+ protected native int ConstructName( String serviceName, String regType, String domain, String[] pOut);
+
+ protected native void ReconfirmRecord( int flags, int ifIndex, String fullName, int rrtype,
+ int rrclass, byte[] rdata);
+
+ protected native String GetNameForIfIndex( int ifIndex);
+
+ protected native int GetIfIndexForName( String ifName);
+
+ protected static native int InitLibrary( int callerVersion);
+}
+
+class AppleService implements DNSSDService, Runnable
+{
+ public AppleService(BaseListener listener) { fNativeContext = 0; fListener = listener; }
+
+ public void stop() { this.HaltOperation(); }
+
+ /* Block until data arrives, or one second passes. Returns 1 if data present, 0 otherwise. */
+ protected native int BlockForData();
+
+ /* Call ProcessResults when data appears on socket descriptor. */
+ protected native int ProcessResults();
+
+ protected synchronized native void HaltOperation();
+
+ protected void ThrowOnErr( int rc) throws DNSSDException
+ {
+ if (rc != 0)
+ throw new AppleDNSSDException( rc);
+ }
+
+ protected long /* warning */ fNativeContext; // Private storage for native side
+
+ public void run()
+ {
+ while ( true )
+ {
+ // Note: We want to allow our DNS-SD operation to be stopped from other threads, so we have to
+ // block waiting for data *outside* the synchronized section. Because we're doing this unsynchronized
+ // we have to write some careful code. Suppose our DNS-SD operation is stopped from some other thread,
+ // and then immediately afterwards that thread (or some third, unrelated thread) starts a new DNS-SD
+ // operation. The Unix kernel always allocates the lowest available file descriptor to a new socket,
+ // so the same file descriptor is highly likely to be reused for the new operation, and if our old
+ // stale ServiceThread accidentally consumes bytes off that new socket we'll get really messed up.
+ // To guard against that, before calling ProcessResults we check to ensure that our
+ // fNativeContext has not been deleted, which is a telltale sign that our operation was stopped.
+ // After calling ProcessResults we check again, because it's extremely common for callback
+ // functions to stop their own operation and start others. For example, a resolveListener callback
+ // may well stop the resolve and then start a QueryRecord call to monitor the TXT record.
+ //
+ // The remaining risk is that between our checking fNativeContext and calling ProcessResults(),
+ // some other thread could stop the operation and start a new one using same file descriptor, and
+ // we wouldn't know. To prevent this, the AppleService object's HaltOperation() routine is declared
+ // synchronized and we perform our checks synchronized on the AppleService object, which ensures
+ // that HaltOperation() can't execute while we're doing it. Because Java locks are re-entrant this
+ // locking DOESN'T prevent the callback routine from stopping its own operation, but DOES prevent
+ // any other thread from stopping it until after the callback has completed and returned to us here.
+
+ int result = BlockForData();
+ synchronized (this)
+ {
+ if (fNativeContext == 0) break; // Some other thread stopped our DNSSD operation; time to terminate this thread
+ if (result == 0) continue; // If BlockForData() said there was no data, go back and block again
+ result = ProcessResults();
+ if (fNativeContext == 0) break; // Event listener stopped its own DNSSD operation; terminate this thread
+ if (result != 0) { fListener.operationFailed(this, result); break; } // If error, notify listener
+ }
+ }
+ }
+
+ protected BaseListener fListener;
+}
+
+
+class AppleBrowser extends AppleService
+{
+ public AppleBrowser( int flags, int ifIndex, String regType, String domain, BrowseListener client)
+ throws DNSSDException
+ {
+ super(client);
+ this.ThrowOnErr( this.CreateBrowser( flags, ifIndex, regType, domain));
+ if (!AppleDNSSD.hasAutoCallbacks)
+ new Thread(this).start();
+ }
+
+ // Sets fNativeContext. Returns non-zero on error.
+ protected native int CreateBrowser( int flags, int ifIndex, String regType, String domain);
+}
+
+class AppleResolver extends AppleService
+{
+ public AppleResolver( int flags, int ifIndex, String serviceName, String regType,
+ String domain, ResolveListener client)
+ throws DNSSDException
+ {
+ super(client);
+ this.ThrowOnErr( this.CreateResolver( flags, ifIndex, serviceName, regType, domain));
+ if (!AppleDNSSD.hasAutoCallbacks)
+ new Thread(this).start();
+ }
+
+ // Sets fNativeContext. Returns non-zero on error.
+ protected native int CreateResolver( int flags, int ifIndex, String serviceName, String regType,
+ String domain);
+}
+
+// An AppleDNSRecord is a simple wrapper around a dns_sd DNSRecord.
+class AppleDNSRecord implements DNSRecord
+{
+ public AppleDNSRecord( AppleService owner)
+ {
+ fOwner = owner;
+ fRecord = 0; // record always starts out empty
+ }
+
+ public void update( int flags, byte[] rData, int ttl)
+ throws DNSSDException
+ {
+ this.ThrowOnErr( this.Update( flags, rData, ttl));
+ }
+
+ public void remove()
+ throws DNSSDException
+ {
+ this.ThrowOnErr( this.Remove());
+ }
+
+ protected long fRecord; // Really a DNSRecord; sizeof(long) == sizeof(void*) ?
+ protected AppleService fOwner;
+
+ protected void ThrowOnErr( int rc) throws DNSSDException
+ {
+ if (rc != 0)
+ throw new AppleDNSSDException( rc);
+ }
+
+ protected native int Update( int flags, byte[] rData, int ttl);
+
+ protected native int Remove();
+}
+
+class AppleRegistration extends AppleService implements DNSSDRegistration
+{
+ public AppleRegistration( int flags, int ifIndex, String serviceName, String regType, String domain,
+ String host, int port, byte[] txtRecord, RegisterListener client)
+ throws DNSSDException
+ {
+ super(client);
+ this.ThrowOnErr( this.BeginRegister( ifIndex, flags, serviceName, regType, domain, host, port, txtRecord));
+ if (!AppleDNSSD.hasAutoCallbacks)
+ new Thread(this).start();
+ }
+
+ public DNSRecord addRecord( int flags, int rrType, byte[] rData, int ttl)
+ throws DNSSDException
+ {
+ AppleDNSRecord newRecord = new AppleDNSRecord( this);
+
+ this.ThrowOnErr( this.AddRecord( flags, rrType, rData, ttl, newRecord));
+ return newRecord;
+ }
+
+ public DNSRecord getTXTRecord()
+ throws DNSSDException
+ {
+ return new AppleDNSRecord( this); // A record with ref 0 is understood to be primary TXT record
+ }
+
+ // Sets fNativeContext. Returns non-zero on error.
+ protected native int BeginRegister( int ifIndex, int flags, String serviceName, String regType,
+ String domain, String host, int port, byte[] txtRecord);
+
+ // Sets fNativeContext. Returns non-zero on error.
+ protected native int AddRecord( int flags, int rrType, byte[] rData, int ttl, AppleDNSRecord destObj);
+}
+
+class AppleRecordRegistrar extends AppleService implements DNSSDRecordRegistrar
+{
+ public AppleRecordRegistrar( RegisterRecordListener listener)
+ throws DNSSDException
+ {
+ super(listener);
+ this.ThrowOnErr( this.CreateConnection());
+ if (!AppleDNSSD.hasAutoCallbacks)
+ new Thread(this).start();
+ }
+
+ public DNSRecord registerRecord( int flags, int ifIndex, String fullname, int rrtype,
+ int rrclass, byte[] rdata, int ttl)
+ throws DNSSDException
+ {
+ AppleDNSRecord newRecord = new AppleDNSRecord( this);
+
+ this.ThrowOnErr( this.RegisterRecord( flags, ifIndex, fullname, rrtype, rrclass, rdata, ttl, newRecord));
+ return newRecord;
+ }
+
+ // Sets fNativeContext. Returns non-zero on error.
+ protected native int CreateConnection();
+
+ // Sets fNativeContext. Returns non-zero on error.
+ protected native int RegisterRecord( int flags, int ifIndex, String fullname, int rrtype,
+ int rrclass, byte[] rdata, int ttl, AppleDNSRecord destObj);
+}
+
+class AppleQuery extends AppleService
+{
+ public AppleQuery( int flags, int ifIndex, String serviceName, int rrtype,
+ int rrclass, QueryListener client)
+ throws DNSSDException
+ {
+ super(client);
+ this.ThrowOnErr( this.CreateQuery( flags, ifIndex, serviceName, rrtype, rrclass));
+ if (!AppleDNSSD.hasAutoCallbacks)
+ new Thread(this).start();
+ }
+
+ // Sets fNativeContext. Returns non-zero on error.
+ protected native int CreateQuery( int flags, int ifIndex, String serviceName, int rrtype, int rrclass);
+}
+
+class AppleDomainEnum extends AppleService
+{
+ public AppleDomainEnum( int flags, int ifIndex, DomainListener client)
+ throws DNSSDException
+ {
+ super(client);
+ this.ThrowOnErr( this.BeginEnum( flags, ifIndex));
+ if (!AppleDNSSD.hasAutoCallbacks)
+ new Thread(this).start();
+ }
+
+ // Sets fNativeContext. Returns non-zero on error.
+ protected native int BeginEnum( int flags, int ifIndex);
+}
+
+
diff --git a/mDNSResponder/mDNSShared/Java/DNSSDException.java b/mDNSResponder/mDNSShared/Java/DNSSDException.java
new file mode 100644
index 00000000..99549b5d
--- /dev/null
+++ b/mDNSResponder/mDNSShared/Java/DNSSDException.java
@@ -0,0 +1,64 @@
+/* -*- Mode: Java; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.apple.dnssd;
+
+
+/**
+ Used to report various DNS-SD-related error conditions.
+*/
+
+abstract public class DNSSDException extends Exception
+{
+ public static final int NO_ERROR = 0;
+ public static final int UNKNOWN = -65537;
+ public static final int NO_SUCH_NAME = -65538;
+ public static final int NO_MEMORY = -65539;
+ public static final int BAD_PARAM = -65540;
+ public static final int BAD_REFERENCE = -65541;
+ public static final int BAD_STATE = -65542;
+ public static final int BAD_FLAGS = -65543;
+ public static final int UNSUPPORTED = -65544;
+ public static final int NOT_INITIALIZED = -65545;
+ public static final int NO_CACHE = -65546;
+ public static final int ALREADY_REGISTERED = -65547;
+ public static final int NAME_CONFLICT = -65548;
+ public static final int INVALID = -65549;
+ public static final int FIREWALL = -65550;
+ public static final int INCOMPATIBLE = -65551;
+ public static final int BAD_INTERFACE_INDEX = -65552;
+ public static final int REFUSED = -65553;
+ public static final int NOSUCHRECORD = -65554;
+ public static final int NOAUTH = -65555;
+ public static final int NOSUCHKEY = -65556;
+ public static final int NATTRAVERSAL = -65557;
+ public static final int DOUBLENAT = -65558;
+ public static final int BADTIME = -65559;
+ public static final int BADSIG = -65560;
+ public static final int BADKEY = -65561;
+ public static final int TRANSIENT = -65562;
+ public static final int SERVICENOTRUNNING = -65563;
+ public static final int NATPORTMAPPINGUNSUPPORTED = -65564;
+ public static final int NATPORTMAPPINGDISABLED = -65565;
+
+ // Note: When adding new error values here, remember also
+ // to update the corresponding kMessages array in AppleDNSSDException (DNSSD.java)
+
+ /** Returns the sub-code that identifies the particular error. */
+ abstract public int getErrorCode();
+}
+
diff --git a/mDNSResponder/mDNSShared/Java/DNSSDRecordRegistrar.java b/mDNSResponder/mDNSShared/Java/DNSSDRecordRegistrar.java
new file mode 100644
index 00000000..3baac677
--- /dev/null
+++ b/mDNSResponder/mDNSShared/Java/DNSSDRecordRegistrar.java
@@ -0,0 +1,64 @@
+/* -*- Mode: Java; tab-width: 4 -*-
+ *
+ * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.apple.dnssd;
+
+
+/** An object for registering records, created by {@link DNSSD#createRecordRegistrar}. */
+
+public interface DNSSDRecordRegistrar extends DNSSDService
+{
+ /** Register an independent {@link DNSRecord}.<P>
+ @param flags
+ Possible values are SHARED or UNIQUE (see flag type definitions for details).
+ <P>
+ @param ifIndex
+ 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.
+ <P>
+ @param fullname
+ The full domain name of the resource record.
+ <P>
+ @param rrtype
+ The numerical type of the resource record to be queried for (e.g. PTR, SRV, etc)
+ as defined in nameser.h.
+ <P>
+ @param rrclass
+ The class of the resource record, as defined in nameser.h
+ (usually 1 for the Internet class).
+ <P>
+ @param rData
+ The new rdata as it is to appear in the DNS record.
+ <P>
+ @param ttl
+ The time to live of the resource record, in seconds. Pass 0 to use a default value.
+ <P>
+ @param listener
+ This object will get called when the service is registered.
+ <P>
+ @return A {@link DNSSDService} that can be used to abort the record registration.
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public DNSRecord registerRecord( int flags, int ifIndex, String fullname, int rrtype,
+ int rrclass, byte[] rdata, int ttl)
+ throws DNSSDException;
+}
+
diff --git a/mDNSResponder/mDNSShared/Java/DNSSDRegistration.java b/mDNSResponder/mDNSShared/Java/DNSSDRegistration.java
new file mode 100644
index 00000000..720df0b8
--- /dev/null
+++ b/mDNSResponder/mDNSShared/Java/DNSSDRegistration.java
@@ -0,0 +1,60 @@
+/* -*- Mode: Java; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.apple.dnssd;
+
+
+/** A tracking object for a registration created by {@link DNSSD#register}. */
+
+public interface DNSSDRegistration extends DNSSDService
+{
+ /** Get a reference to the primary TXT record of a registered service.<P>
+ The record can be updated by sending it an update() message.<P>
+
+ <P>
+ @return A {@link DNSRecord}.
+ If {@link DNSSDRegistration#stop} is called, the DNSRecord is also
+ invalidated and may not be used further.
+ */
+ DNSRecord getTXTRecord()
+ throws DNSSDException;
+
+ /** Add a record to a registered service.<P>
+ The name of the record will be the same as the registered service's name.<P>
+ The record can be updated or deregistered by sending it an update() or remove() message.<P>
+
+ @param flags
+ Currently unused, reserved for future use.
+ <P>
+ @param rrType
+ The type of the record (e.g. TXT, SRV, etc), as defined in nameser.h.
+ <P>
+ @param rData
+ The raw rdata to be contained in the added resource record.
+ <P>
+ @param ttl
+ The time to live of the resource record, in seconds.
+ <P>
+ @return A {@link DNSRecord} that may be passed to updateRecord() or removeRecord().
+ If {@link DNSSDRegistration#stop} is called, the DNSRecord is also
+ invalidated and may not be used further.
+ */
+ DNSRecord addRecord( int flags, int rrType, byte[] rData, int ttl)
+ throws DNSSDException;
+}
+
diff --git a/mDNSResponder/mDNSShared/Java/DNSSDService.java b/mDNSResponder/mDNSShared/Java/DNSSDService.java
new file mode 100644
index 00000000..10f74021
--- /dev/null
+++ b/mDNSResponder/mDNSShared/Java/DNSSDService.java
@@ -0,0 +1,37 @@
+/* -*- Mode: Java; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.apple.dnssd;
+
+/** A tracking object for a service created by {@link DNSSD}. */
+
+public interface DNSSDService
+{
+ /**
+ Halt the active operation and free resources associated with the DNSSDService.<P>
+
+ Any services or records registered with this DNSSDService will be deregistered. Any
+ Browse, Resolve, or Query operations associated with this reference will be terminated.<P>
+
+ Note: if the service was initialized with DNSSD.register(), and an extra resource record was
+ added to the service via {@link DNSSDRegistration#addRecord}, the DNSRecord so created
+ is invalidated when this method is called - the DNSRecord may not be used afterward.
+ */
+ void stop();
+}
+
diff --git a/mDNSResponder/mDNSShared/Java/DomainListener.java b/mDNSResponder/mDNSShared/Java/DomainListener.java
new file mode 100644
index 00000000..852f6430
--- /dev/null
+++ b/mDNSResponder/mDNSShared/Java/DomainListener.java
@@ -0,0 +1,60 @@
+/* -*- Mode: Java; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.apple.dnssd;
+
+
+/**
+ A listener that receives results from {@link DNSSD#enumerateDomains}.
+*/
+
+public interface DomainListener extends BaseListener
+{
+ /** Called to report discovered domains.<P>
+
+ @param domainEnum
+ The active domain enumerator.
+ @param flags
+ Possible values are: DNSSD.MORE_COMING, DNSSD.DEFAULT
+ <P>
+ @param ifIndex
+ Specifies the interface on which the domain exists. (The index for a given
+ interface is determined via the if_nametoindex() family of calls.)
+ <P>
+ @param domain
+ The name of the domain.
+ */
+ void domainFound( DNSSDService domainEnum, int flags, int ifIndex, String domain);
+
+ /** Called to report that a domain has disappeared.<P>
+
+ @param domainEnum
+ The active domain enumerator.
+ @param flags
+ Possible values are: DNSSD.MORE_COMING, DNSSD.DEFAULT
+ <P>
+ @param ifIndex
+ Specifies the interface on which the domain exists. (The index for a given
+ interface is determined via the if_nametoindex() family of calls.)
+ <P>
+ @param domain
+ The name of the domain.
+ */
+ void domainLost( DNSSDService domainEnum, int flags, int ifIndex, String domain);
+}
+
diff --git a/mDNSResponder/mDNSShared/Java/JNISupport.c b/mDNSResponder/mDNSShared/Java/JNISupport.c
new file mode 100644
index 00000000..22b40930
--- /dev/null
+++ b/mDNSResponder/mDNSShared/Java/JNISupport.c
@@ -0,0 +1,1072 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+
+ This file contains the platform support for DNSSD and related Java classes.
+ It is used to shim through to the underlying <dns_sd.h> API.
+ */
+
+// AUTO_CALLBACKS should be set to 1 if the underlying mDNS implementation fires response
+// callbacks automatically (as in the early Windows prototypes).
+// AUTO_CALLBACKS should be set to 0 if the client must call DNSServiceProcessResult() to
+// invoke response callbacks (as is true on Mac OS X, Posix, Windows, etc.).
+// (Invoking callbacks automatically on a different thread sounds attractive, but while
+// the client gains by not needing to add an event source to its main event loop, it loses
+// by being forced to deal with concurrency and locking, which can be a bigger burden.)
+#ifndef AUTO_CALLBACKS
+#define AUTO_CALLBACKS 0
+#endif
+
+#if !AUTO_CALLBACKS
+#ifdef _WIN32
+#include <winsock2.h>
+#else //_WIN32
+#include <sys/types.h>
+#include <sys/select.h>
+#endif // _WIN32
+#endif // AUTO_CALLBACKS
+
+#include <dns_sd.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef _WIN32
+#include <winsock2.h>
+#include <iphlpapi.h>
+static char * win32_if_indextoname( DWORD ifIndex, char * nameBuff);
+static DWORD win32_if_nametoindex( const char * nameStr );
+#define if_indextoname win32_if_indextoname
+#define if_nametoindex win32_if_nametoindex
+#define IF_NAMESIZE MAX_ADAPTER_NAME_LENGTH
+#else // _WIN32
+#include <sys/socket.h>
+#include <net/if.h>
+#endif // _WIN32
+
+// When compiling with "-Wshadow" set, including jni.h produces the following error:
+// /System/Library/Frameworks/JavaVM.framework/Versions/A/Headers/jni.h:609: warning: declaration of 'index' shadows a global declaration
+// To work around this, we use the preprocessor to map the identifier 'index', which appears harmlessly in function prototype declarations,
+// to something 'jni_index', which doesn't conflict
+#define index jni_index
+#include "DNSSD.java.h"
+#undef index
+
+//#include <syslog.h>
+
+// convenience definition
+#ifdef __GNUC__
+#define _UNUSED __attribute__ ((unused))
+#else
+#define _UNUSED
+#endif
+
+enum {
+ kInterfaceVersionOne = 1,
+ kInterfaceVersionCurrent // Must match version in .jar file
+};
+
+typedef struct OpContext OpContext;
+
+struct OpContext
+{
+ DNSServiceRef ServiceRef;
+ JNIEnv *Env;
+ jobject JavaObj;
+ jobject ClientObj;
+ jmethodID Callback;
+ jmethodID Callback2;
+};
+
+// For AUTO_CALLBACKS, we must attach the callback thread to the Java VM prior to upcall.
+#if AUTO_CALLBACKS
+JavaVM *gJavaVM = NULL;
+#endif
+
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_InitLibrary( JNIEnv *pEnv, jclass cls,
+ jint callerVersion)
+{
+ /* Ensure that caller & interface versions match. */
+ if ( callerVersion != kInterfaceVersionCurrent)
+ return kDNSServiceErr_Incompatible;
+
+#if AUTO_CALLBACKS
+ {
+ jsize numVMs;
+
+ if ( 0 != JNI_GetCreatedJavaVMs( &gJavaVM, 1, &numVMs))
+ return kDNSServiceErr_BadState;
+ }
+#endif
+
+ // Set AppleDNSSD.hasAutoCallbacks
+ {
+#if AUTO_CALLBACKS
+ jboolean hasAutoC = JNI_TRUE;
+#else
+ jboolean hasAutoC = JNI_FALSE;
+#endif
+ jfieldID hasAutoCField = (*pEnv)->GetStaticFieldID( pEnv, cls, "hasAutoCallbacks", "Z");
+ (*pEnv)->SetStaticBooleanField( pEnv, cls, hasAutoCField, hasAutoC);
+ }
+
+ return kDNSServiceErr_NoError;
+}
+
+
+static const char* SafeGetUTFChars( JNIEnv *pEnv, jstring str)
+// Wrapper for JNI GetStringUTFChars() that returns NULL for null str.
+{
+ return str != NULL ? (*pEnv)->GetStringUTFChars( pEnv, str, 0) : NULL;
+}
+
+static void SafeReleaseUTFChars( JNIEnv *pEnv, jstring str, const char *buff)
+// Wrapper for JNI GetStringUTFChars() that handles null str.
+{
+ if ( str != NULL)
+ (*pEnv)->ReleaseStringUTFChars( pEnv, str, buff);
+}
+
+
+#if AUTO_CALLBACKS
+static void SetupCallbackState( JNIEnv **ppEnv)
+{
+ (*gJavaVM)->AttachCurrentThread( gJavaVM, (void**) ppEnv, NULL);
+}
+
+static void TeardownCallbackState( void )
+{
+ (*gJavaVM)->DetachCurrentThread( gJavaVM);
+}
+
+#else // AUTO_CALLBACKS
+
+static void SetupCallbackState( JNIEnv **ppEnv _UNUSED)
+{
+ // No setup necessary if ProcessResults() has been called
+}
+
+static void TeardownCallbackState( void )
+{
+ // No teardown necessary if ProcessResults() has been called
+}
+#endif // AUTO_CALLBACKS
+
+
+static OpContext *NewContext( JNIEnv *pEnv, jobject owner,
+ const char *callbackName, const char *callbackSig)
+// Create and initialize a new OpContext.
+{
+ OpContext *pContext = (OpContext*) malloc( sizeof *pContext);
+
+ if ( pContext != NULL)
+ {
+ jfieldID clientField = (*pEnv)->GetFieldID( pEnv, (*pEnv)->GetObjectClass( pEnv, owner),
+ "fListener", "Lcom/apple/dnssd/BaseListener;");
+
+ pContext->JavaObj = (*pEnv)->NewWeakGlobalRef( pEnv, owner); // must convert local ref to global to cache;
+ pContext->ClientObj = (*pEnv)->GetObjectField( pEnv, owner, clientField);
+ pContext->ClientObj = (*pEnv)->NewWeakGlobalRef( pEnv, pContext->ClientObj); // must convert local ref to global to cache
+ pContext->Callback = (*pEnv)->GetMethodID( pEnv,
+ (*pEnv)->GetObjectClass( pEnv, pContext->ClientObj),
+ callbackName, callbackSig);
+ pContext->Callback2 = NULL; // not always used
+ }
+
+ return pContext;
+}
+
+
+static void ReportError( JNIEnv *pEnv, jobject target, jobject service, DNSServiceErrorType err)
+// Invoke operationFailed() method on target with err.
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, target);
+ jmethodID opFailed = (*pEnv)->GetMethodID( pEnv, cls, "operationFailed",
+ "(Lcom/apple/dnssd/DNSSDService;I)V");
+
+ (*pEnv)->CallVoidMethod( pEnv, target, opFailed, service, err);
+}
+
+JNIEXPORT void JNICALL Java_com_apple_dnssd_AppleService_HaltOperation( JNIEnv *pEnv, jobject pThis)
+/* Deallocate the dns_sd service browser and set the Java object's fNativeContext field to 0. */
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+
+ if ( contextField != 0)
+ {
+ OpContext *pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, pThis, contextField);
+ if ( pContext != NULL)
+ {
+ // MUST clear fNativeContext first, BEFORE calling DNSServiceRefDeallocate()
+ (*pEnv)->SetLongField(pEnv, pThis, contextField, 0);
+ if ( pContext->ServiceRef != NULL)
+ DNSServiceRefDeallocate( pContext->ServiceRef);
+
+ (*pEnv)->DeleteWeakGlobalRef( pEnv, pContext->JavaObj);
+ (*pEnv)->DeleteWeakGlobalRef( pEnv, pContext->ClientObj);
+ free( pContext);
+ }
+ }
+}
+
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleService_BlockForData( JNIEnv *pEnv, jobject pThis)
+/* Block until data arrives, or one second passes. Returns 1 if data present, 0 otherwise. */
+{
+// BlockForData() not supported with AUTO_CALLBACKS
+#if !AUTO_CALLBACKS
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+
+ if ( contextField != 0)
+ {
+ OpContext *pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, pThis, contextField);
+ if ( pContext != NULL)
+ {
+ fd_set readFDs;
+ int sd = DNSServiceRefSockFD( pContext->ServiceRef);
+ struct timeval timeout = { 1, 0 };
+ FD_ZERO( &readFDs);
+ FD_SET( sd, &readFDs);
+
+ // Q: Why do we poll here?
+ // A: Because there's no other thread-safe way to do it.
+ // Mac OS X terminates a select() call if you close one of the sockets it's listening on, but Linux does not,
+ // and arguably Linux is correct (See <http://www.ussg.iu.edu/hypermail/linux/kernel/0405.1/0418.html>)
+ // The problem is that the Mac OS X behaviour assumes that it's okay for one thread to close a socket while
+ // some other thread is monitoring that socket in select(), but the difficulty is that there's no general way
+ // to make that thread-safe, because there's no atomic way to enter select() and release a lock simultaneously.
+ // If we try to do this without holding any lock, then right as we jump to the select() routine,
+ // some other thread could stop our operation (thereby closing the socket),
+ // and then that thread (or even some third, unrelated thread)
+ // could do some other DNS-SD operation (or some other operation that opens a new file descriptor)
+ // and then we'd blindly resume our fall into the select() call, now blocking on a file descriptor
+ // that may coincidentally have the same numerical value, but is semantically unrelated
+ // to the true file descriptor we thought we were blocking on.
+ // We can't stop this race condition from happening, but at least if we wake up once a second we can detect
+ // when fNativeContext has gone to zero, and thereby discover that we were blocking on the wrong fd.
+
+ if (select( sd + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &timeout) == 1) return(1);
+ }
+ }
+#endif // !AUTO_CALLBACKS
+ return(0);
+}
+
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleService_ProcessResults( JNIEnv *pEnv, jobject pThis)
+/* Call through to DNSServiceProcessResult() while data remains on socket. */
+{
+#if !AUTO_CALLBACKS // ProcessResults() not supported with AUTO_CALLBACKS
+
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ OpContext *pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, pThis, contextField);
+ DNSServiceErrorType err = kDNSServiceErr_BadState;
+
+ if ( pContext != NULL)
+ {
+ int sd = DNSServiceRefSockFD( pContext->ServiceRef);
+ fd_set readFDs;
+ struct timeval zeroTimeout = { 0, 0 };
+
+ pContext->Env = pEnv;
+
+ FD_ZERO( &readFDs);
+ FD_SET( sd, &readFDs);
+
+ err = kDNSServiceErr_NoError;
+ if (0 < select(sd + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &zeroTimeout))
+ {
+ err = DNSServiceProcessResult(pContext->ServiceRef);
+ // Use caution here!
+ // We cannot touch any data structures associated with this operation!
+ // The DNSServiceProcessResult() routine should have invoked our callback,
+ // and our callback could have terminated the operation with op.stop();
+ // and that means HaltOperation() will have been called, which frees pContext.
+ // Basically, from here we just have to get out without touching any stale
+ // data structures that could blow up on us! Particularly, any attempt
+ // to loop here reading more results from the file descriptor is unsafe.
+ }
+ }
+ return err;
+#endif // AUTO_CALLBACKS
+}
+
+
+static void DNSSD_API ServiceBrowseReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode, const char *serviceName, const char *regtype,
+ const char *replyDomain, void *context)
+{
+ OpContext *pContext = (OpContext*) context;
+
+ SetupCallbackState( &pContext->Env);
+
+ if ( pContext->ClientObj != NULL && pContext->Callback != NULL)
+ {
+ if ( errorCode == kDNSServiceErr_NoError)
+ {
+ (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj,
+ ( flags & kDNSServiceFlagsAdd) != 0 ? pContext->Callback : pContext->Callback2,
+ pContext->JavaObj, flags, interfaceIndex,
+ (*pContext->Env)->NewStringUTF( pContext->Env, serviceName),
+ (*pContext->Env)->NewStringUTF( pContext->Env, regtype),
+ (*pContext->Env)->NewStringUTF( pContext->Env, replyDomain));
+ }
+ else
+ ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode);
+ }
+
+ TeardownCallbackState();
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleBrowser_CreateBrowser( JNIEnv *pEnv, jobject pThis,
+ jint flags, jint ifIndex, jstring regType, jstring domain)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+
+ if ( contextField != 0)
+ pContext = NewContext( pEnv, pThis, "serviceFound",
+ "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
+ else
+ err = kDNSServiceErr_BadParam;
+
+ if ( pContext != NULL)
+ {
+ const char *regStr = SafeGetUTFChars( pEnv, regType);
+ const char *domainStr = SafeGetUTFChars( pEnv, domain);
+
+ pContext->Callback2 = (*pEnv)->GetMethodID( pEnv,
+ (*pEnv)->GetObjectClass( pEnv, pContext->ClientObj),
+ "serviceLost", "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
+
+ err = DNSServiceBrowse( &pContext->ServiceRef, flags, ifIndex, regStr, domainStr, ServiceBrowseReply, pContext);
+ if ( err == kDNSServiceErr_NoError)
+ {
+ (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext);
+ }
+
+ SafeReleaseUTFChars( pEnv, regType, regStr);
+ SafeReleaseUTFChars( pEnv, domain, domainStr);
+ }
+ else
+ err = kDNSServiceErr_NoMemory;
+
+ return err;
+}
+
+
+static void DNSSD_API ServiceResolveReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget,
+ uint16_t port, uint16_t txtLen, const unsigned char *txtRecord, void *context)
+{
+ OpContext *pContext = (OpContext*) context;
+ jclass txtCls;
+ jmethodID txtCtor;
+ jbyteArray txtBytes;
+ jobject txtObj;
+ jbyte *pBytes;
+
+ SetupCallbackState( &pContext->Env);
+
+ txtCls = (*pContext->Env)->FindClass( pContext->Env, "com/apple/dnssd/TXTRecord");
+ txtCtor = (*pContext->Env)->GetMethodID( pContext->Env, txtCls, "<init>", "([B)V");
+
+ if ( pContext->ClientObj != NULL && pContext->Callback != NULL && txtCtor != NULL &&
+ NULL != ( txtBytes = (*pContext->Env)->NewByteArray( pContext->Env, txtLen)))
+ {
+ if ( errorCode == kDNSServiceErr_NoError)
+ {
+ // Since Java ints are defined to be big-endian, we canonicalize 'port' from a 16-bit
+ // pattern into a number here.
+ port = ( ((unsigned char*) &port)[0] << 8) | ((unsigned char*) &port)[1];
+
+ // Initialize txtBytes with contents of txtRecord
+ pBytes = (*pContext->Env)->GetByteArrayElements( pContext->Env, txtBytes, NULL);
+ memcpy( pBytes, txtRecord, txtLen);
+ (*pContext->Env)->ReleaseByteArrayElements( pContext->Env, txtBytes, pBytes, JNI_COMMIT);
+
+ // Construct txtObj with txtBytes
+ txtObj = (*pContext->Env)->NewObject( pContext->Env, txtCls, txtCtor, txtBytes);
+ (*pContext->Env)->DeleteLocalRef( pContext->Env, txtBytes);
+
+ (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback,
+ pContext->JavaObj, flags, interfaceIndex,
+ (*pContext->Env)->NewStringUTF( pContext->Env, fullname),
+ (*pContext->Env)->NewStringUTF( pContext->Env, hosttarget),
+ port, txtObj);
+ }
+ else
+ ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode);
+ }
+
+ TeardownCallbackState();
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleResolver_CreateResolver( JNIEnv *pEnv, jobject pThis,
+ jint flags, jint ifIndex, jstring serviceName, jstring regType, jstring domain)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+
+ if ( contextField != 0)
+ pContext = NewContext( pEnv, pThis, "serviceResolved",
+ "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;ILcom/apple/dnssd/TXTRecord;)V");
+ else
+ err = kDNSServiceErr_BadParam;
+
+ if ( pContext != NULL)
+ {
+ const char *servStr = SafeGetUTFChars( pEnv, serviceName);
+ const char *regStr = SafeGetUTFChars( pEnv, regType);
+ const char *domainStr = SafeGetUTFChars( pEnv, domain);
+
+ err = DNSServiceResolve( &pContext->ServiceRef, flags, ifIndex,
+ servStr, regStr, domainStr, ServiceResolveReply, pContext);
+ if ( err == kDNSServiceErr_NoError)
+ {
+ (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext);
+ }
+
+ SafeReleaseUTFChars( pEnv, serviceName, servStr);
+ SafeReleaseUTFChars( pEnv, regType, regStr);
+ SafeReleaseUTFChars( pEnv, domain, domainStr);
+ }
+ else
+ err = kDNSServiceErr_NoMemory;
+
+ return err;
+}
+
+
+static void DNSSD_API ServiceRegisterReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags,
+ DNSServiceErrorType errorCode, const char *serviceName,
+ const char *regType, const char *domain, void *context)
+{
+ OpContext *pContext = (OpContext*) context;
+
+ SetupCallbackState( &pContext->Env);
+
+ if ( pContext->ClientObj != NULL && pContext->Callback != NULL)
+ {
+ if ( errorCode == kDNSServiceErr_NoError)
+ {
+ (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback,
+ pContext->JavaObj, flags,
+ (*pContext->Env)->NewStringUTF( pContext->Env, serviceName),
+ (*pContext->Env)->NewStringUTF( pContext->Env, regType),
+ (*pContext->Env)->NewStringUTF( pContext->Env, domain));
+ }
+ else
+ ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode);
+ }
+ TeardownCallbackState();
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRegistration_BeginRegister( JNIEnv *pEnv, jobject pThis,
+ jint ifIndex, jint flags, jstring serviceName, jstring regType,
+ jstring domain, jstring host, jint port, jbyteArray txtRecord)
+{
+ //syslog(LOG_ERR, "BR");
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+ jbyte *pBytes;
+ jsize numBytes;
+
+ //syslog(LOG_ERR, "BR: contextField %d", contextField);
+
+ if ( contextField != 0)
+ pContext = NewContext( pEnv, pThis, "serviceRegistered",
+ "(Lcom/apple/dnssd/DNSSDRegistration;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
+ else
+ err = kDNSServiceErr_BadParam;
+
+ if ( pContext != NULL)
+ {
+ const char *servStr = SafeGetUTFChars( pEnv, serviceName);
+ const char *regStr = SafeGetUTFChars( pEnv, regType);
+ const char *domainStr = SafeGetUTFChars( pEnv, domain);
+ const char *hostStr = SafeGetUTFChars( pEnv, host);
+
+ //syslog(LOG_ERR, "BR: regStr %s", regStr);
+
+ // Since Java ints are defined to be big-endian, we de-canonicalize 'port' from a
+ // big-endian number into a 16-bit pattern here.
+ uint16_t portBits = port;
+ portBits = ( ((unsigned char*) &portBits)[0] << 8) | ((unsigned char*) &portBits)[1];
+
+ pBytes = txtRecord ? (*pEnv)->GetByteArrayElements( pEnv, txtRecord, NULL) : NULL;
+ numBytes = txtRecord ? (*pEnv)->GetArrayLength( pEnv, txtRecord) : 0;
+
+ err = DNSServiceRegister( &pContext->ServiceRef, flags, ifIndex, servStr, regStr,
+ domainStr, hostStr, portBits,
+ numBytes, pBytes, ServiceRegisterReply, pContext);
+ if ( err == kDNSServiceErr_NoError)
+ {
+ (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext);
+ }
+
+ if ( pBytes != NULL)
+ (*pEnv)->ReleaseByteArrayElements( pEnv, txtRecord, pBytes, 0);
+
+ SafeReleaseUTFChars( pEnv, serviceName, servStr);
+ SafeReleaseUTFChars( pEnv, regType, regStr);
+ SafeReleaseUTFChars( pEnv, domain, domainStr);
+ SafeReleaseUTFChars( pEnv, host, hostStr);
+ }
+ else
+ err = kDNSServiceErr_NoMemory;
+
+ return err;
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRegistration_AddRecord( JNIEnv *pEnv, jobject pThis,
+ jint flags, jint rrType, jbyteArray rData, jint ttl, jobject destObj)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ jclass destCls = (*pEnv)->GetObjectClass( pEnv, destObj);
+ jfieldID recField = (*pEnv)->GetFieldID( pEnv, destCls, "fRecord", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+ jbyte *pBytes;
+ jsize numBytes;
+ DNSRecordRef recRef;
+
+ if ( contextField != 0)
+ pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, pThis, contextField);
+ if ( pContext == NULL || pContext->ServiceRef == NULL)
+ return kDNSServiceErr_BadParam;
+
+ pBytes = (*pEnv)->GetByteArrayElements( pEnv, rData, NULL);
+ numBytes = (*pEnv)->GetArrayLength( pEnv, rData);
+
+ err = DNSServiceAddRecord( pContext->ServiceRef, &recRef, flags, rrType, numBytes, pBytes, ttl);
+ if ( err == kDNSServiceErr_NoError)
+ {
+ (*pEnv)->SetLongField(pEnv, destObj, recField, (long) recRef);
+ }
+
+ if ( pBytes != NULL)
+ (*pEnv)->ReleaseByteArrayElements( pEnv, rData, pBytes, 0);
+
+ return err;
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSRecord_Update( JNIEnv *pEnv, jobject pThis,
+ jint flags, jbyteArray rData, jint ttl)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID ownerField = (*pEnv)->GetFieldID( pEnv, cls, "fOwner", "Lcom/apple/dnssd/AppleService;");
+ jfieldID recField = (*pEnv)->GetFieldID( pEnv, cls, "fRecord", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+ jbyte *pBytes;
+ jsize numBytes;
+ DNSRecordRef recRef = NULL;
+
+ if ( ownerField != 0)
+ {
+ jobject ownerObj = (*pEnv)->GetObjectField( pEnv, pThis, ownerField);
+ jclass ownerClass = (*pEnv)->GetObjectClass( pEnv, ownerObj);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, ownerClass, "fNativeContext", "J");
+ if ( contextField != 0)
+ pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, ownerObj, contextField);
+ }
+ if ( recField != 0)
+ recRef = (DNSRecordRef) (long) (*pEnv)->GetLongField(pEnv, pThis, recField);
+ if ( pContext == NULL || pContext->ServiceRef == NULL)
+ return kDNSServiceErr_BadParam;
+
+ pBytes = (*pEnv)->GetByteArrayElements( pEnv, rData, NULL);
+ numBytes = (*pEnv)->GetArrayLength( pEnv, rData);
+
+ err = DNSServiceUpdateRecord( pContext->ServiceRef, recRef, flags, numBytes, pBytes, ttl);
+
+ if ( pBytes != NULL)
+ (*pEnv)->ReleaseByteArrayElements( pEnv, rData, pBytes, 0);
+
+ return err;
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSRecord_Remove( JNIEnv *pEnv, jobject pThis)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID ownerField = (*pEnv)->GetFieldID( pEnv, cls, "fOwner", "Lcom/apple/dnssd/AppleService;");
+ jfieldID recField = (*pEnv)->GetFieldID( pEnv, cls, "fRecord", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+ DNSRecordRef recRef = NULL;
+
+ if ( ownerField != 0)
+ {
+ jobject ownerObj = (*pEnv)->GetObjectField( pEnv, pThis, ownerField);
+ jclass ownerClass = (*pEnv)->GetObjectClass( pEnv, ownerObj);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, ownerClass, "fNativeContext", "J");
+ if ( contextField != 0)
+ pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, ownerObj, contextField);
+ }
+ if ( recField != 0)
+ recRef = (DNSRecordRef) (long) (*pEnv)->GetLongField(pEnv, pThis, recField);
+ if ( pContext == NULL || pContext->ServiceRef == NULL)
+ return kDNSServiceErr_BadParam;
+
+ err = DNSServiceRemoveRecord( pContext->ServiceRef, recRef, 0);
+
+ return err;
+}
+
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRecordRegistrar_CreateConnection( JNIEnv *pEnv, jobject pThis)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+
+ if ( contextField != 0)
+ pContext = NewContext( pEnv, pThis, "recordRegistered", "(Lcom/apple/dnssd/DNSRecord;I)V");
+ else
+ err = kDNSServiceErr_BadParam;
+
+ if ( pContext != NULL)
+ {
+ err = DNSServiceCreateConnection( &pContext->ServiceRef);
+ if ( err == kDNSServiceErr_NoError)
+ {
+ (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext);
+ }
+ }
+ else
+ err = kDNSServiceErr_NoMemory;
+
+ return err;
+}
+
+struct RecordRegistrationRef
+{
+ OpContext *Context;
+ jobject RecordObj;
+};
+typedef struct RecordRegistrationRef RecordRegistrationRef;
+
+static void DNSSD_API RegisterRecordReply( DNSServiceRef sdRef _UNUSED,
+ DNSRecordRef recordRef _UNUSED, DNSServiceFlags flags,
+ DNSServiceErrorType errorCode, void *context)
+{
+ RecordRegistrationRef *regEnvelope = (RecordRegistrationRef*) context;
+ OpContext *pContext = regEnvelope->Context;
+
+ SetupCallbackState( &pContext->Env);
+
+ if ( pContext->ClientObj != NULL && pContext->Callback != NULL)
+ {
+ if ( errorCode == kDNSServiceErr_NoError)
+ {
+ (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback,
+ regEnvelope->RecordObj, flags);
+ }
+ else
+ ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode);
+ }
+
+ (*pContext->Env)->DeleteWeakGlobalRef( pContext->Env, regEnvelope->RecordObj);
+ free( regEnvelope);
+
+ TeardownCallbackState();
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRecordRegistrar_RegisterRecord( JNIEnv *pEnv, jobject pThis,
+ jint flags, jint ifIndex, jstring fullname, jint rrType, jint rrClass,
+ jbyteArray rData, jint ttl, jobject destObj)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ jclass destCls = (*pEnv)->GetObjectClass( pEnv, destObj);
+ jfieldID recField = (*pEnv)->GetFieldID( pEnv, destCls, "fRecord", "J");
+ const char *nameStr = SafeGetUTFChars( pEnv, fullname);
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+ jbyte *pBytes;
+ jsize numBytes;
+ DNSRecordRef recRef;
+ RecordRegistrationRef *regEnvelope;
+
+ if ( contextField != 0)
+ pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, pThis, contextField);
+ if ( pContext == NULL || pContext->ServiceRef == NULL || nameStr == NULL)
+ return kDNSServiceErr_BadParam;
+
+ regEnvelope = calloc( 1, sizeof *regEnvelope);
+ if ( regEnvelope == NULL)
+ return kDNSServiceErr_NoMemory;
+ regEnvelope->Context = pContext;
+ regEnvelope->RecordObj = (*pEnv)->NewWeakGlobalRef( pEnv, destObj); // must convert local ref to global to cache
+
+ pBytes = (*pEnv)->GetByteArrayElements( pEnv, rData, NULL);
+ numBytes = (*pEnv)->GetArrayLength( pEnv, rData);
+
+ err = DNSServiceRegisterRecord( pContext->ServiceRef, &recRef, flags, ifIndex,
+ nameStr, rrType, rrClass, numBytes, pBytes, ttl,
+ RegisterRecordReply, regEnvelope);
+
+ if ( err == kDNSServiceErr_NoError)
+ {
+ (*pEnv)->SetLongField(pEnv, destObj, recField, (long) recRef);
+ }
+ else
+ {
+ if ( regEnvelope->RecordObj != NULL)
+ (*pEnv)->DeleteWeakGlobalRef( pEnv, regEnvelope->RecordObj);
+ free( regEnvelope);
+ }
+
+ if ( pBytes != NULL)
+ (*pEnv)->ReleaseByteArrayElements( pEnv, rData, pBytes, 0);
+
+ SafeReleaseUTFChars( pEnv, fullname, nameStr);
+
+ return err;
+}
+
+
+static void DNSSD_API ServiceQueryReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode, const char *serviceName,
+ uint16_t rrtype, uint16_t rrclass, uint16_t rdlen,
+ const void *rdata, uint32_t ttl, void *context)
+{
+ OpContext *pContext = (OpContext*) context;
+ jbyteArray rDataObj;
+ jbyte *pBytes;
+
+ SetupCallbackState( &pContext->Env);
+
+ if ( pContext->ClientObj != NULL && pContext->Callback != NULL &&
+ NULL != ( rDataObj = (*pContext->Env)->NewByteArray( pContext->Env, rdlen)))
+ {
+ if ( errorCode == kDNSServiceErr_NoError)
+ {
+ // Initialize rDataObj with contents of rdata
+ pBytes = (*pContext->Env)->GetByteArrayElements( pContext->Env, rDataObj, NULL);
+ memcpy( pBytes, rdata, rdlen);
+ (*pContext->Env)->ReleaseByteArrayElements( pContext->Env, rDataObj, pBytes, JNI_COMMIT);
+
+ (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback,
+ pContext->JavaObj, flags, interfaceIndex,
+ (*pContext->Env)->NewStringUTF( pContext->Env, serviceName),
+ rrtype, rrclass, rDataObj, ttl);
+ }
+ else
+ ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode);
+ }
+ TeardownCallbackState();
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleQuery_CreateQuery( JNIEnv *pEnv, jobject pThis,
+ jint flags, jint ifIndex, jstring serviceName, jint rrtype, jint rrclass)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+
+ if ( contextField != 0)
+ pContext = NewContext( pEnv, pThis, "queryAnswered",
+ "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;II[BI)V");
+ else
+ err = kDNSServiceErr_BadParam;
+
+ if ( pContext != NULL)
+ {
+ const char *servStr = SafeGetUTFChars( pEnv, serviceName);
+
+ err = DNSServiceQueryRecord( &pContext->ServiceRef, flags, ifIndex, servStr,
+ rrtype, rrclass, ServiceQueryReply, pContext);
+ if ( err == kDNSServiceErr_NoError)
+ {
+ (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext);
+ }
+
+ SafeReleaseUTFChars( pEnv, serviceName, servStr);
+ }
+ else
+ err = kDNSServiceErr_NoMemory;
+
+ return err;
+}
+
+
+static void DNSSD_API DomainEnumReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode, const char *replyDomain, void *context)
+{
+ OpContext *pContext = (OpContext*) context;
+
+ SetupCallbackState( &pContext->Env);
+
+ if ( pContext->ClientObj != NULL && pContext->Callback != NULL)
+ {
+ if ( errorCode == kDNSServiceErr_NoError)
+ {
+ (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj,
+ ( flags & kDNSServiceFlagsAdd) != 0 ? pContext->Callback : pContext->Callback2,
+ pContext->JavaObj, flags, interfaceIndex,
+ (*pContext->Env)->NewStringUTF( pContext->Env, replyDomain));
+ }
+ else
+ ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode);
+ }
+ TeardownCallbackState();
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDomainEnum_BeginEnum( JNIEnv *pEnv, jobject pThis,
+ jint flags, jint ifIndex)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+
+ if ( contextField != 0)
+ pContext = NewContext( pEnv, pThis, "domainFound",
+ "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;)V");
+ else
+ err = kDNSServiceErr_BadParam;
+
+ if ( pContext != NULL)
+ {
+ pContext->Callback2 = (*pEnv)->GetMethodID( pEnv,
+ (*pEnv)->GetObjectClass( pEnv, pContext->ClientObj),
+ "domainLost", "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;)V");
+
+ err = DNSServiceEnumerateDomains( &pContext->ServiceRef, flags, ifIndex,
+ DomainEnumReply, pContext);
+ if ( err == kDNSServiceErr_NoError)
+ {
+ (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext);
+ }
+ }
+ else
+ err = kDNSServiceErr_NoMemory;
+
+ return err;
+}
+
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_ConstructName( JNIEnv *pEnv, jobject pThis _UNUSED,
+ jstring serviceName, jstring regtype, jstring domain, jobjectArray pOut)
+{
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+ const char *nameStr = SafeGetUTFChars( pEnv, serviceName);
+ const char *regStr = SafeGetUTFChars( pEnv, regtype);
+ const char *domStr = SafeGetUTFChars( pEnv, domain);
+ char buff[ kDNSServiceMaxDomainName + 1];
+
+ err = DNSServiceConstructFullName( buff, nameStr, regStr, domStr);
+
+ if ( err == kDNSServiceErr_NoError)
+ {
+ // pOut is expected to be a String[1] array.
+ (*pEnv)->SetObjectArrayElement( pEnv, pOut, 0, (*pEnv)->NewStringUTF( pEnv, buff));
+ }
+
+ SafeReleaseUTFChars( pEnv, serviceName, nameStr);
+ SafeReleaseUTFChars( pEnv, regtype, regStr);
+ SafeReleaseUTFChars( pEnv, domain, domStr);
+
+ return err;
+}
+
+JNIEXPORT void JNICALL Java_com_apple_dnssd_AppleDNSSD_ReconfirmRecord( JNIEnv *pEnv, jobject pThis _UNUSED,
+ jint flags, jint ifIndex, jstring fullName,
+ jint rrtype, jint rrclass, jbyteArray rdata)
+{
+ jbyte *pBytes;
+ jsize numBytes;
+ const char *nameStr = SafeGetUTFChars( pEnv, fullName);
+
+ pBytes = (*pEnv)->GetByteArrayElements( pEnv, rdata, NULL);
+ numBytes = (*pEnv)->GetArrayLength( pEnv, rdata);
+
+ DNSServiceReconfirmRecord( flags, ifIndex, nameStr, rrtype, rrclass, numBytes, pBytes);
+
+ if ( pBytes != NULL)
+ (*pEnv)->ReleaseByteArrayElements( pEnv, rdata, pBytes, 0);
+
+ SafeReleaseUTFChars( pEnv, fullName, nameStr);
+}
+
+#define LOCAL_ONLY_NAME "loo"
+#define P2P_NAME "p2p"
+
+JNIEXPORT jstring JNICALL Java_com_apple_dnssd_AppleDNSSD_GetNameForIfIndex( JNIEnv *pEnv, jobject pThis _UNUSED,
+ jint ifIndex)
+{
+ char *p = LOCAL_ONLY_NAME, nameBuff[IF_NAMESIZE];
+
+ if (ifIndex == (jint) kDNSServiceInterfaceIndexP2P)
+ p = P2P_NAME;
+ else if (ifIndex != (jint) kDNSServiceInterfaceIndexLocalOnly)
+ p = if_indextoname( ifIndex, nameBuff );
+
+ return (*pEnv)->NewStringUTF( pEnv, p);
+}
+
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_GetIfIndexForName( JNIEnv *pEnv, jobject pThis _UNUSED,
+ jstring ifName)
+{
+ uint32_t ifIndex = kDNSServiceInterfaceIndexLocalOnly;
+ const char *nameStr = SafeGetUTFChars( pEnv, ifName);
+
+ if (strcmp(nameStr, P2P_NAME) == 0)
+ ifIndex = kDNSServiceInterfaceIndexP2P;
+ else if (strcmp(nameStr, LOCAL_ONLY_NAME))
+ ifIndex = if_nametoindex( nameStr);
+
+ SafeReleaseUTFChars( pEnv, ifName, nameStr);
+
+ return ifIndex;
+}
+
+
+#if defined(_WIN32)
+static char*
+win32_if_indextoname( DWORD ifIndex, char * nameBuff)
+{
+ PIP_ADAPTER_INFO pAdapterInfo = NULL;
+ PIP_ADAPTER_INFO pAdapter = NULL;
+ DWORD dwRetVal = 0;
+ char * ifName = NULL;
+ ULONG ulOutBufLen = 0;
+
+ if (GetAdaptersInfo( NULL, &ulOutBufLen) != ERROR_BUFFER_OVERFLOW)
+ {
+ goto exit;
+ }
+
+ pAdapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen);
+
+ if (pAdapterInfo == NULL)
+ {
+ goto exit;
+ }
+
+ dwRetVal = GetAdaptersInfo( pAdapterInfo, &ulOutBufLen );
+
+ if (dwRetVal != NO_ERROR)
+ {
+ goto exit;
+ }
+
+ pAdapter = pAdapterInfo;
+ while (pAdapter)
+ {
+ if (pAdapter->Index == ifIndex)
+ {
+ // It would be better if we passed in the length of nameBuff to this
+ // function, so we would have absolute certainty that no buffer
+ // overflows would occur. Buffer overflows *shouldn't* occur because
+ // nameBuff is of size MAX_ADAPTER_NAME_LENGTH.
+ strcpy( nameBuff, pAdapter->AdapterName );
+ ifName = nameBuff;
+ break;
+ }
+
+ pAdapter = pAdapter->Next;
+ }
+
+exit:
+
+ if (pAdapterInfo != NULL)
+ {
+ free( pAdapterInfo );
+ pAdapterInfo = NULL;
+ }
+
+ return ifName;
+}
+
+
+static DWORD
+win32_if_nametoindex( const char * nameStr )
+{
+ PIP_ADAPTER_INFO pAdapterInfo = NULL;
+ PIP_ADAPTER_INFO pAdapter = NULL;
+ DWORD dwRetVal = 0;
+ DWORD ifIndex = 0;
+ ULONG ulOutBufLen = 0;
+
+ if (GetAdaptersInfo( NULL, &ulOutBufLen) != ERROR_BUFFER_OVERFLOW)
+ {
+ goto exit;
+ }
+
+ pAdapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen);
+
+ if (pAdapterInfo == NULL)
+ {
+ goto exit;
+ }
+
+ dwRetVal = GetAdaptersInfo( pAdapterInfo, &ulOutBufLen );
+
+ if (dwRetVal != NO_ERROR)
+ {
+ goto exit;
+ }
+
+ pAdapter = pAdapterInfo;
+ while (pAdapter)
+ {
+ if (strcmp(pAdapter->AdapterName, nameStr) == 0)
+ {
+ ifIndex = pAdapter->Index;
+ break;
+ }
+
+ pAdapter = pAdapter->Next;
+ }
+
+exit:
+
+ if (pAdapterInfo != NULL)
+ {
+ free( pAdapterInfo );
+ pAdapterInfo = NULL;
+ }
+
+ return ifIndex;
+}
+#endif
+
+
+// Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
+// e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
+// To expand "version" to its value before making the string, use STRINGIFY(version) instead
+#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) # s
+#define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
+
+// NOT static -- otherwise the compiler may optimize it out
+// The "@(#) " pattern is a special prefix the "what" command looks for
+const char VersionString_SCCS[] = "@(#) libjdns_sd " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
diff --git a/mDNSResponder/mDNSShared/Java/QueryListener.java b/mDNSResponder/mDNSShared/Java/QueryListener.java
new file mode 100644
index 00000000..0decb7fc
--- /dev/null
+++ b/mDNSResponder/mDNSShared/Java/QueryListener.java
@@ -0,0 +1,59 @@
+/* -*- Mode: Java; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.apple.dnssd;
+
+
+/** A listener that receives results from {@link DNSSD#queryRecord}. */
+
+public interface QueryListener extends BaseListener
+{
+ /** Called when a record query has been completed. Inspect flags
+ parameter to determine nature of query event.<P>
+
+ @param query
+ The active query object.
+ <P>
+ @param flags
+ If kDNSServiceFlagsAdd bit is set, this is a newly discovered answer;
+ otherwise this is a previously discovered answer which has expired.
+ Other possible values are DNSSD.MORE_COMING.
+ <P>
+ @param ifIndex
+ The interface on which the query was resolved. (The index for a given
+ interface is determined via the if_nametoindex() family of calls.)
+ <P>
+ @param fullName
+ The resource record's full domain name.
+ <P>
+ @param rrtype
+ The resource record's type (e.g. PTR, SRV, etc) as defined by RFC 1035 and its updates.
+ <P>
+ @param rrclass
+ The class of the resource record, as defined by RFC 1035 and its updates.
+ <P>
+ @param rdata
+ The raw rdata of the resource record.
+ <P>
+ @param ttl
+ The resource record's time to live, in seconds.
+ */
+ void queryAnswered( DNSSDService query, int flags, int ifIndex, String fullName,
+ int rrtype, int rrclass, byte[] rdata, int ttl);
+}
+
diff --git a/mDNSResponder/mDNSShared/Java/RegisterListener.java b/mDNSResponder/mDNSShared/Java/RegisterListener.java
new file mode 100644
index 00000000..00fa1a63
--- /dev/null
+++ b/mDNSResponder/mDNSShared/Java/RegisterListener.java
@@ -0,0 +1,49 @@
+/* -*- Mode: Java; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.apple.dnssd;
+
+
+/** A listener that receives results from {@link DNSSD#register}. */
+
+public interface RegisterListener extends BaseListener
+{
+ /** Called when a registration has been completed.<P>
+
+ @param registration
+ The active registration.
+ <P>
+ @param flags
+ Currently unused, reserved for future use.
+ <P>
+ @param serviceName
+ The service name registered (if the application did not specify a name in
+ DNSSD.register(), this indicates what name was automatically chosen).
+ <P>
+ @param regType
+ The type of service registered, as it was passed to DNSSD.register().
+ <P>
+ @param domain
+ The domain on which the service was registered. If the application did not
+ specify a domain in DNSSD.register(), this is the default domain
+ on which the service was registered.
+ */
+ void serviceRegistered( DNSSDRegistration registration, int flags, String serviceName,
+ String regType, String domain);
+}
+
diff --git a/mDNSResponder/mDNSShared/Java/RegisterRecordListener.java b/mDNSResponder/mDNSShared/Java/RegisterRecordListener.java
new file mode 100644
index 00000000..6fecf8d8
--- /dev/null
+++ b/mDNSResponder/mDNSShared/Java/RegisterRecordListener.java
@@ -0,0 +1,37 @@
+/* -*- Mode: Java; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.apple.dnssd;
+
+
+/** A listener that receives results from {@link DNSSDRecordRegistrar#registerRecord}. */
+
+public interface RegisterRecordListener extends BaseListener
+{
+ /** Called when a record registration succeeds.<P>
+
+ @param record
+ A {@link DNSRecord}.
+ <P>
+ @param flags
+ Currently ignored, reserved for future use.
+ <P>
+ */
+ void recordRegistered( DNSRecord record, int flags);
+}
+
diff --git a/mDNSResponder/mDNSShared/Java/ResolveListener.java b/mDNSResponder/mDNSShared/Java/ResolveListener.java
new file mode 100644
index 00000000..33dafa38
--- /dev/null
+++ b/mDNSResponder/mDNSShared/Java/ResolveListener.java
@@ -0,0 +1,56 @@
+/* -*- Mode: Java; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.apple.dnssd;
+
+
+/** A listener that receives results from {@link DNSSD#resolve}. */
+
+public interface ResolveListener extends BaseListener
+{
+ /** Called when a service has been resolved.<P>
+
+ @param resolver
+ The active resolver object.
+ <P>
+ @param flags
+ Currently unused, reserved for future use.
+ <P>
+ @param fullName
+ The full service domain name, in the form &lt;servicename&gt;.&lt;protocol&gt;.&lt;domain&gt;.
+ (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.
+ <P>
+ @param hostName
+ The target hostname of the machine providing the service. This name can
+ be passed to functions like queryRecord() to look up the host's IP address.
+ <P>
+ @param port
+ The port number on which connections are accepted for this service.
+ <P>
+ @param txtRecord
+ The service's primary txt record.
+ */
+ void serviceResolved( DNSSDService resolver, int flags, int ifIndex, String fullName,
+ String hostName, int port, TXTRecord txtRecord);
+}
+
diff --git a/mDNSResponder/mDNSShared/Java/TXTRecord.java b/mDNSResponder/mDNSShared/Java/TXTRecord.java
new file mode 100644
index 00000000..8d9df7a1
--- /dev/null
+++ b/mDNSResponder/mDNSShared/Java/TXTRecord.java
@@ -0,0 +1,290 @@
+/* -*- Mode: Java; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+
+ To do:
+ - implement remove()
+ - fix set() to replace existing values
+ */
+
+
+package com.apple.dnssd;
+
+
+/**
+ Object used to construct and parse DNS-SD format TXT records.
+ For more info see <a href="http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt">DNS-Based Service Discovery</a>, section 6.
+*/
+
+public class TXTRecord
+{
+ /*
+ DNS-SD specifies that a TXT record corresponding to an SRV record consist of
+ a packed array of bytes, each preceded by a length byte. Each string
+ is an attribute-value pair.
+
+ The TXTRecord object stores the entire TXT data as a single byte array, traversing it
+ as need be to implement its various methods.
+ */
+
+ static final protected byte kAttrSep = '=';
+
+ protected byte[] fBytes;
+
+ /** Constructs a new, empty TXT record. */
+ public TXTRecord()
+ { fBytes = new byte[0]; }
+
+ /** Constructs a new TXT record from a byte array in the standard format. */
+ public TXTRecord( byte[] initBytes)
+ { fBytes = (byte[]) initBytes.clone(); }
+
+ /** Set a key/value pair in the TXT record. Setting an existing key will replace its value.<P>
+ @param key
+ The key name. Must be ASCII, with no '=' characters.
+ <P>
+ @param value
+ Value to be encoded into bytes using the default platform character set.
+ */
+ public void set( String key, String value)
+ {
+ byte[] valBytes = (value != null) ? value.getBytes() : null;
+ this.set( key, valBytes);
+ }
+
+ /** Set a key/value pair in the TXT record. Setting an existing key will replace its value.<P>
+ @param key
+ The key name. Must be ASCII, with no '=' characters.
+ <P>
+ @param value
+ Binary representation of the value.
+ */
+ public void set( String key, byte[] value)
+ {
+ byte[] keyBytes;
+ int valLen = (value != null) ? value.length : 0;
+
+ try {
+ keyBytes = key.getBytes( "US-ASCII");
+ }
+ catch ( java.io.UnsupportedEncodingException uee) {
+ throw new IllegalArgumentException();
+ }
+
+ for ( int i=0; i < keyBytes.length; i++)
+ if ( keyBytes[i] == '=')
+ throw new IllegalArgumentException();
+
+ if ( keyBytes.length + valLen >= 255)
+ throw new ArrayIndexOutOfBoundsException();
+
+ int prevLoc = this.remove( key);
+ if ( prevLoc == -1)
+ prevLoc = this.size();
+
+ this.insert( keyBytes, value, prevLoc);
+ }
+
+ protected void insert( byte[] keyBytes, byte[] value, int index)
+ // Insert a key-value pair at index
+ {
+ byte[] oldBytes = fBytes;
+ int valLen = (value != null) ? value.length : 0;
+ int insertion = 0;
+ int newLen, avLen;
+
+ // locate the insertion point
+ for ( int i=0; i < index && insertion < fBytes.length; i++)
+ insertion += (0xFF & (fBytes[ insertion] + 1));
+
+ avLen = keyBytes.length + valLen + (value != null ? 1 : 0);
+ newLen = avLen + oldBytes.length + 1;
+
+ fBytes = new byte[ newLen];
+ System.arraycopy( oldBytes, 0, fBytes, 0, insertion);
+ int secondHalfLen = oldBytes.length - insertion;
+ System.arraycopy( oldBytes, insertion, fBytes, newLen - secondHalfLen, secondHalfLen);
+ fBytes[ insertion] = ( byte) avLen;
+ System.arraycopy( keyBytes, 0, fBytes, insertion + 1, keyBytes.length);
+ if ( value != null)
+ {
+ fBytes[ insertion + 1 + keyBytes.length] = kAttrSep;
+ System.arraycopy( value, 0, fBytes, insertion + keyBytes.length + 2, valLen);
+ }
+ }
+
+ /** Remove a key/value pair from the TXT record. Returns index it was at, or -1 if not found. */
+ public int remove( String key)
+ {
+ int avStart = 0;
+
+ for ( int i=0; avStart < fBytes.length; i++)
+ {
+ int avLen = fBytes[ avStart];
+ if ( key.length() <= avLen &&
+ ( key.length() == avLen || fBytes[ avStart + key.length() + 1] == kAttrSep))
+ {
+ String s = new String( fBytes, avStart + 1, key.length());
+ if ( 0 == key.compareToIgnoreCase( s))
+ {
+ byte[] oldBytes = fBytes;
+ fBytes = new byte[ oldBytes.length - avLen - 1];
+ System.arraycopy( oldBytes, 0, fBytes, 0, avStart);
+ System.arraycopy( oldBytes, avStart + avLen + 1, fBytes, avStart, oldBytes.length - avStart - avLen - 1);
+ return i;
+ }
+ }
+ avStart += (0xFF & (avLen + 1));
+ }
+ return -1;
+ }
+
+ /** Return the number of keys in the TXT record. */
+ public int size()
+ {
+ int i, avStart;
+
+ for ( i=0, avStart=0; avStart < fBytes.length; i++)
+ avStart += (0xFF & (fBytes[ avStart] + 1));
+ return i;
+ }
+
+ /** Return true if key is present in the TXT record, false if not. */
+ public boolean contains( String key)
+ {
+ String s = null;
+
+ for ( int i=0; null != ( s = this.getKey( i)); i++)
+ if ( 0 == key.compareToIgnoreCase( s))
+ return true;
+ return false;
+ }
+
+ /** Return a key in the TXT record by zero-based index. Returns null if index exceeds the total number of keys. */
+ public String getKey( int index)
+ {
+ int avStart = 0;
+
+ for ( int i=0; i < index && avStart < fBytes.length; i++)
+ avStart += fBytes[ avStart] + 1;
+
+ if ( avStart < fBytes.length)
+ {
+ int avLen = fBytes[ avStart];
+ int aLen = 0;
+
+ for ( aLen=0; aLen < avLen; aLen++)
+ if ( fBytes[ avStart + aLen + 1] == kAttrSep)
+ break;
+ return new String( fBytes, avStart + 1, aLen);
+ }
+ return null;
+ }
+
+ /**
+ Look up a key in the TXT record by zero-based index and return its value. <P>
+ Returns null if index exceeds the total number of keys.
+ Returns null if the key is present with no value.
+ */
+ public byte[] getValue( int index)
+ {
+ int avStart = 0;
+ byte[] value = null;
+
+ for ( int i=0; i < index && avStart < fBytes.length; i++)
+ avStart += fBytes[ avStart] + 1;
+
+ if ( avStart < fBytes.length)
+ {
+ int avLen = fBytes[ avStart];
+ int aLen = 0;
+
+ for ( aLen=0; aLen < avLen; aLen++)
+ {
+ if ( fBytes[ avStart + aLen + 1] == kAttrSep)
+ {
+ value = new byte[ avLen - aLen - 1];
+ System.arraycopy( fBytes, avStart + aLen + 2, value, 0, avLen - aLen - 1);
+ break;
+ }
+ }
+ }
+ return value;
+ }
+
+ /** Converts the result of getValue() to a string in the platform default character set. */
+ public String getValueAsString( int index)
+ {
+ byte[] value = this.getValue( index);
+ return value != null ? new String( value) : null;
+ }
+
+ /** Get the value associated with a key. Will be null if the key is not defined.
+ Array will have length 0 if the key is defined with an = but no value.<P>
+
+ @param forKey
+ The left-hand side of the key-value pair.
+ <P>
+ @return The binary representation of the value.
+ */
+ public byte[] getValue( String forKey)
+ {
+ String s = null;
+ int i;
+
+ for ( i=0; null != ( s = this.getKey( i)); i++)
+ if ( 0 == forKey.compareToIgnoreCase( s))
+ return this.getValue( i);
+ return null;
+ }
+
+ /** Converts the result of getValue() to a string in the platform default character set.<P>
+
+ @param forKey
+ The left-hand side of the key-value pair.
+ <P>
+ @return The value represented in the default platform character set.
+ */
+ public String getValueAsString( String forKey)
+ {
+ byte[] val = this.getValue( forKey);
+ return val != null ? new String( val) : null;
+ }
+
+ /** Return the contents of the TXT record as raw bytes. */
+ public byte[] getRawBytes() { return (byte[]) fBytes.clone(); }
+
+ /** Return a string representation of the object. */
+ public String toString()
+ {
+ String a, result = null;
+
+ for ( int i=0; null != ( a = this.getKey( i)); i++)
+ {
+ String av = String.valueOf( i) + "={" + a;
+ String val = this.getValueAsString( i);
+ if ( val != null)
+ av += "=" + val + "}";
+ else
+ av += "}";
+ if ( result == null)
+ result = av;
+ else
+ result = result + ", " + av;
+ }
+ return result != null ? result : "";
+ }
+}
+
diff --git a/mDNSResponder/mDNSShared/PlatformCommon.c b/mDNSResponder/mDNSShared/PlatformCommon.c
new file mode 100644
index 00000000..d86a755a
--- /dev/null
+++ b/mDNSResponder/mDNSShared/PlatformCommon.c
@@ -0,0 +1,199 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h> // Needed for fopen() etc.
+#include <unistd.h> // Needed for close()
+#include <string.h> // Needed for strlen() etc.
+#include <errno.h> // Needed for errno etc.
+#include <sys/socket.h> // Needed for socket() etc.
+#include <netinet/in.h> // Needed for sockaddr_in
+#include <syslog.h>
+
+#include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above
+#include "DNSCommon.h"
+#include "PlatformCommon.h"
+
+#ifdef NOT_HAVE_SOCKLEN_T
+typedef unsigned int socklen_t;
+#endif
+
+// Bind a UDP socket to find the source address to a destination
+mDNSexport void mDNSPlatformSourceAddrForDest(mDNSAddr *const src, const mDNSAddr *const dst)
+{
+ union { struct sockaddr s; struct sockaddr_in a4; struct sockaddr_in6 a6; } addr;
+ socklen_t len = sizeof(addr);
+ socklen_t inner_len = 0;
+ int sock = socket(AF_INET, SOCK_DGRAM, 0);
+ src->type = mDNSAddrType_None;
+ if (sock == -1) return;
+ if (dst->type == mDNSAddrType_IPv4)
+ {
+ inner_len = sizeof(addr.a4);
+ #ifndef NOT_HAVE_SA_LEN
+ addr.a4.sin_len = inner_len;
+ #endif
+ addr.a4.sin_family = AF_INET;
+ addr.a4.sin_port = 1; // Not important, any port will do
+ addr.a4.sin_addr.s_addr = dst->ip.v4.NotAnInteger;
+ }
+ else if (dst->type == mDNSAddrType_IPv6)
+ {
+ inner_len = sizeof(addr.a6);
+ #ifndef NOT_HAVE_SA_LEN
+ addr.a6.sin6_len = inner_len;
+ #endif
+ addr.a6.sin6_family = AF_INET6;
+ addr.a6.sin6_flowinfo = 0;
+ addr.a6.sin6_port = 1; // Not important, any port will do
+ addr.a6.sin6_addr = *(struct in6_addr*)&dst->ip.v6;
+ addr.a6.sin6_scope_id = 0;
+ }
+ else return;
+
+ if ((connect(sock, &addr.s, inner_len)) < 0)
+ { LogMsg("mDNSPlatformSourceAddrForDest: connect %#a failed errno %d (%s)", dst, errno, strerror(errno)); goto exit; }
+
+ if ((getsockname(sock, &addr.s, &len)) < 0)
+ { LogMsg("mDNSPlatformSourceAddrForDest: getsockname failed errno %d (%s)", errno, strerror(errno)); goto exit; }
+
+ src->type = dst->type;
+ if (dst->type == mDNSAddrType_IPv4) src->ip.v4.NotAnInteger = addr.a4.sin_addr.s_addr;
+ else src->ip.v6 = *(mDNSv6Addr*)&addr.a6.sin6_addr;
+exit:
+ close(sock);
+}
+
+// dst must be at least MAX_ESCAPED_DOMAIN_NAME bytes, and option must be less than 32 bytes in length
+mDNSlocal mDNSBool GetConfigOption(char *dst, const char *option, FILE *f)
+{
+ char buf[32+1+MAX_ESCAPED_DOMAIN_NAME]; // Option name, one space, option value
+ unsigned int len = strlen(option);
+ if (len + 1 + MAX_ESCAPED_DOMAIN_NAME > sizeof(buf)-1) { LogMsg("GetConfigOption: option %s too long", option); return mDNSfalse; }
+ fseek(f, 0, SEEK_SET); // set position to beginning of stream
+ while (fgets(buf, sizeof(buf), f)) // Read at most sizeof(buf)-1 bytes from file, and append '\0' C-string terminator
+ {
+ if (!strncmp(buf, option, len))
+ {
+ strncpy(dst, buf + len + 1, MAX_ESCAPED_DOMAIN_NAME-1);
+ if (dst[MAX_ESCAPED_DOMAIN_NAME-1]) dst[MAX_ESCAPED_DOMAIN_NAME-1] = '\0';
+ len = strlen(dst);
+ if (len && dst[len-1] == '\n') dst[len-1] = '\0'; // chop newline
+ return mDNStrue;
+ }
+ }
+ debugf("Option %s not set", option);
+ return mDNSfalse;
+}
+
+mDNSexport void ReadDDNSSettingsFromConfFile(mDNS *const m, const char *const filename, domainname *const hostname, domainname *const domain, mDNSBool *DomainDiscoveryDisabled)
+{
+ char buf[MAX_ESCAPED_DOMAIN_NAME] = "";
+ mStatus err;
+ FILE *f = fopen(filename, "r");
+
+ if (hostname) hostname->c[0] = 0;
+ if (domain) domain->c[0] = 0;
+ if (DomainDiscoveryDisabled) *DomainDiscoveryDisabled = mDNSfalse;
+
+ if (f)
+ {
+ if (DomainDiscoveryDisabled && GetConfigOption(buf, "DomainDiscoveryDisabled", f) && !strcasecmp(buf, "true")) *DomainDiscoveryDisabled = mDNStrue;
+ if (hostname && GetConfigOption(buf, "hostname", f) && !MakeDomainNameFromDNSNameString(hostname, buf)) goto badf;
+ if (domain && GetConfigOption(buf, "zone", f) && !MakeDomainNameFromDNSNameString(domain, buf)) goto badf;
+ buf[0] = 0;
+ GetConfigOption(buf, "secret-64", f); // failure means no authentication
+ fclose(f);
+ f = NULL;
+ }
+ else
+ {
+ if (errno != ENOENT) LogMsg("ERROR: Config file exists, but cannot be opened.");
+ return;
+ }
+
+ if (domain && domain->c[0] && buf[0])
+ {
+ DomainAuthInfo *info = (DomainAuthInfo*)mDNSPlatformMemAllocate(sizeof(*info));
+ // for now we assume keyname = service reg domain and we use same key for service and hostname registration
+ err = mDNS_SetSecretForDomain(m, info, domain, domain, buf, NULL, 0, mDNSfalse);
+ if (err) LogMsg("ERROR: mDNS_SetSecretForDomain returned %d for domain %##s", err, domain->c);
+ }
+
+ return;
+
+badf:
+ LogMsg("ERROR: malformatted config file");
+ if (f) fclose(f);
+}
+
+#if MDNS_DEBUGMSGS
+mDNSexport void mDNSPlatformWriteDebugMsg(const char *msg)
+{
+ fprintf(stderr,"%s\n", msg);
+ fflush(stderr);
+}
+#endif
+
+mDNSexport void mDNSPlatformWriteLogMsg(const char *ident, const char *buffer, mDNSLogLevel_t loglevel)
+{
+#if APPLE_OSX_mDNSResponder && LogTimeStamps
+ extern mDNS mDNSStorage;
+ extern mDNSu32 mDNSPlatformClockDivisor;
+ mDNSs32 t = mDNSStorage.timenow ? mDNSStorage.timenow : mDNSPlatformClockDivisor ? mDNS_TimeNow_NoLock(&mDNSStorage) : 0;
+ int ms = ((t < 0) ? -t : t) % 1000;
+#endif
+
+ if (mDNS_DebugMode) // In debug mode we write to stderr
+ {
+#if APPLE_OSX_mDNSResponder && LogTimeStamps
+ if (ident && ident[0] && mDNSPlatformClockDivisor)
+ fprintf(stderr,"%8d.%03d: %s\n", (int)(t/1000), ms, buffer);
+ else
+#endif
+ fprintf(stderr,"%s\n", buffer);
+ fflush(stderr);
+ }
+ else // else, in production mode, we write to syslog
+ {
+ static int log_inited = 0;
+
+ int syslog_level = LOG_ERR;
+ switch (loglevel)
+ {
+ case MDNS_LOG_MSG: syslog_level = LOG_ERR; break;
+ case MDNS_LOG_OPERATION: syslog_level = LOG_WARNING; break;
+ case MDNS_LOG_SPS: syslog_level = LOG_NOTICE; break;
+ case MDNS_LOG_INFO: syslog_level = LOG_INFO; break;
+ case MDNS_LOG_DEBUG: syslog_level = LOG_DEBUG; break;
+ default:
+ fprintf(stderr, "Unknown loglevel %d, assuming LOG_ERR\n", loglevel);
+ fflush(stderr);
+ }
+
+ if (!log_inited) { openlog(ident, LOG_CONS, LOG_DAEMON); log_inited++; }
+
+#if APPLE_OSX_mDNSResponder && LogTimeStamps
+ if (ident && ident[0] && mDNSPlatformClockDivisor)
+ syslog(syslog_level, "%8d.%03d: %s", (int)(t/1000), ms, buffer);
+ else
+#elif APPLE_OSX_mDNSResponder
+ mDNSPlatformLogToFile(syslog_level, buffer);
+#else
+ syslog(syslog_level, "%s", buffer);
+#endif
+ }
+}
diff --git a/mDNSResponder/mDNSShared/PlatformCommon.h b/mDNSResponder/mDNSShared/PlatformCommon.h
new file mode 100644
index 00000000..2a068711
--- /dev/null
+++ b/mDNSResponder/mDNSShared/PlatformCommon.h
@@ -0,0 +1,18 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+extern void ReadDDNSSettingsFromConfFile(mDNS *const m, const char *const filename, domainname *const hostname, domainname *const domain, mDNSBool *DomainDiscoveryDisabled);
diff --git a/mDNSResponder/mDNSShared/dns-sd.1 b/mDNSResponder/mDNSShared/dns-sd.1
new file mode 100644
index 00000000..9d8323b9
--- /dev/null
+++ b/mDNSResponder/mDNSShared/dns-sd.1
@@ -0,0 +1,266 @@
+.\" -*- tab-width: 4 -*-
+.\"
+.\" Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved.
+.\"
+.\" Licensed under the Apache License, Version 2.0 (the "License");
+.\" you may not use this file except in compliance with the License.
+.\" You may obtain a copy of the License at
+.\"
+.\" http://www.apache.org/licenses/LICENSE-2.0
+.\"
+.\" Unless required by applicable law or agreed to in writing, software
+.\" distributed under the License is distributed on an "AS IS" BASIS,
+.\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+.\" See the License for the specific language governing permissions and
+.\" limitations under the License.
+.\"
+.Dd April 2004 \" Date
+.Dt dns-sd 1 \" Document Title
+.Os Darwin \" Operating System
+.\"
+.Sh NAME
+.Nm dns-sd
+.Nd Multicast DNS (mDNS) & DNS Service Discovery (DNS-SD) Test Tool \" For whatis
+.\"
+.Sh SYNOPSIS
+.Nm Fl E
+.Pp
+.Nm Fl F
+.Pp
+.Nm Fl R Ar name type domain port Op Ar key=value ...
+.Pp
+.Nm Fl B Ar type domain
+.Pp
+.Nm Fl L Ar name type domain
+.Pp
+.Nm Fl P Ar name type domain port host IP Op Ar key=value ...
+.Pp
+.Nm Fl q Ar name rrtype rrclass
+.Pp
+.Nm Fl Z Ar type domain
+.Pp
+.Nm Fl G Ns \ v4/v6/v4v6 Ar name
+.Pp
+.Nm Fl V
+.\"
+.Sh DESCRIPTION
+The
+.Nm
+command is a network diagnostic tool, much like
+.Xr ping 8
+or
+.Xr traceroute 8 .
+However, unlike those tools, most of its functionality is not implemented in the
+.Nm
+executable itself, but in library code that is available to any application.
+The library API that
+.Nm
+uses is documented in
+.Pa /usr/include/dns_sd.h .
+The
+.Nm
+command replaces the older
+mDNS
+command.
+.Pp
+The
+.Nm
+command is primarily intended for interactive use.
+Because its command-line arguments and output format are subject to change,
+invoking it from a shell script will generally be fragile. Additionally,
+the asynchronous nature of DNS Service Discovery does
+not lend itself easily to script-oriented programming. For example,
+calls like "browse" never complete; the action of performing a "browse"
+sets in motion machinery to notify the client whenever instances of
+that service type appear or disappear from the network. These
+notifications continue to be delivered indefinitely, for minutes,
+hours, or even days, as services come and go, until the client
+explicitly terminates the call. This style of asynchronous interaction
+works best with applications that are either multi-threaded, or use a
+main event-handling loop to receive keystrokes, network data, and other
+asynchronous event notifications as they happen.
+.br
+If you wish to perform DNS Service Discovery operations from a
+scripting language, then the best way to do this is not to execute the
+.Nm
+command and then attempt to decipher the textual output, but instead to
+directly call the DNS-SD APIs using a binding for your chosen language.
+.br
+For example, if you are programming in Ruby, then you can
+directly call DNS-SD APIs using the dnssd package documented at
+.Pa <http://rubyforge.org/projects/dnssd/> .
+.br
+Similar bindings for other languages are also in development.
+.Pp
+.Bl -tag -width E
+.It Nm Fl E
+return a list of domains recommended for registering(advertising) services.
+.Pp
+.It Nm Fl F
+return a list of domains recommended for browsing services.
+.Pp
+Normally, on your home network, the only domain you are likely to see is "local".
+However if your network administrator has created Domain Enumeration records,
+then you may also see other recommended domains for registering and browsing.
+.Pp
+.It Nm Fl R Ar name type domain port Op Ar key=value ...
+register (advertise) a service in the specified
+.Ar domain
+with the given
+.Ar name
+and
+.Ar type
+as listening (on the current machine) on
+.Ar port.
+.Pp
+.Ar name
+can be arbitrary unicode text, containing any legal unicode characters
+(including dots, spaces, slashes, colons, etc. without restriction),
+up to 63 UTF-8 bytes long.
+.Ar type
+must be of the form "_app-proto._tcp" or "_app-proto._udp", where
+"app-proto" is an application protocol name registered at
+.Pa http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml .
+.Pp
+.Ar domain
+is the domain in which to register the service.
+In current implementations, only the local multicast domain "local" is
+supported. In the future, registering will be supported in any arbitrary
+domain that has a working DNS Update server [RFC 2136]. The
+.Ar domain
+"." is a synonym for "pick a sensible default" which today
+means "local".
+.Pp
+.Ar port
+is a number from 0 to 65535, and is the TCP or UDP port number upon
+which the service is listening.
+.Pp
+Additional attributes of the service may optionally be described by
+key/value pairs, which are stored in the advertised service's DNS TXT
+record. Allowable keys and values are listed with the service
+registration at
+.Pa http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml .
+.It Nm Fl B Ar type domain
+browse for instances of service
+.Ar type
+in
+.Ar domain .
+.Pp
+For valid
+.Ar type Ns s
+see
+.Pa http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml .
+as described above. Omitting the
+.Ar domain
+or using "." means "pick a sensible default."
+.It Nm Fl L Ar name type domain
+look up and display the information necessary to contact and use the
+named service: the hostname of the machine where that service is
+available, the port number on which the service is listening, and (if
+present) TXT record attributes describing properties of the service.
+.Pp
+Note that in a typical application, browsing may only happen rarely, while lookup
+(or "resolving") happens every time the service is used. For example, a
+user browses the network to pick a default printer fairly rarely, but once
+a default printer has been picked, that named service is resolved to its
+current IP address and port number every time the user presses Cmd-P to
+print.
+.Pp
+.It Nm Fl P Ar name type domain port host IP Op Ar key=value ...
+create a proxy advertisement for a service running on(offered by) some other machine.
+The two new options are Host, a name for the device and IP, the address of it.
+.Pp
+The service for which you create a proxy advertisement does not necessarily have to be on your local network.
+You can set up a local proxy for a website on the Internet.
+.Pp
+.It Nm Fl q Ar name rrtype rrclass
+look up any DNS name, resource record type, and resource record class,
+not necessarily DNS-SD names and record types.
+If rrtype is not specified, it queries for the IPv4 address of the name,
+if rrclass is not specified, IN class is assumed. If the name is not a fully
+qualified domain name, then search domains may be appended.
+.Pp
+.It Nm Fl Z Ar type domain
+browse for service instances and display output in zone file format.
+.Pp
+.It Nm Fl G Ns \ v4/v6/v4v6 Ar name
+look up the IP address information of the name.
+If v4 is specified, the IPv4 address of the name is looked up,
+if v6 is specified the IPv6 address is looked up. If v4v6 is specified both the IPv4 and IPv6
+address is looked up. If the name is not a fully qualified domain name,
+then search domains may be appended.
+.Pp
+.It Nm Fl V
+return the version of the currently running daemon/system service.
+.El
+.Sh EXAMPLES
+.Pp
+To advertise the existence of LPR printing service on port 515 on this
+machine, such that it will be discovered by the Mac OS X printing software
+and other DNS-SD compatible printing clients, use:
+.Pp
+.Dl Nm Fl R Ns \ \&"My Test\&" _printer._tcp. \&. 515 pdl=application/postscript
+.Pp
+For this registration to be useful, you need to actually have LPR service
+available on port 515. Advertising a service that does not exist is not
+very useful, and will be confusing and annoying to other people on the
+network.
+.Pp
+Similarly, to advertise a web page being served by an HTTP
+server on port 80 on this machine, such that it will show up in the
+Bonjour list in Safari and other DNS-SD compatible Web clients, use:
+.Pp
+.Dl Nm Fl R Ns \ \&"My Test\&" _http._tcp \&. 80 path=/path-to-page.html
+.Pp
+To find the advertised web pages on the local network (the same list that
+Safari shows), use:
+.Pp
+.Dl Nm Fl B Ns \ _http._tcp
+.Pp
+While that command is running, in another window, try the
+.Nm Fl R
+example given above to advertise a web page, and you should see the
+"Add" event reported to the
+.Nm Fl B
+window. Now press Ctrl-C in the
+.Nm Fl R
+window and you should see the "Remove" event reported to the
+.Nm Fl B
+window.
+.Pp
+In the example below, the www.apple.com web page is advertised as a service called "apple",
+running on a target host called apple.local, which resolves to 17.149.160.49.
+.Pp
+.Dl Nm Fl P Ns \ apple _http._tcp \&"\&"\& 80 apple.local 17.149.160.49
+.Pp
+The Bonjour menu in the Safari web browser will now show "apple".
+The same IP address can be reached by entering apple.local in the web browser.
+In either case, the request will be resolved to the IP address and browser will show
+contents associated with www.apple.com.
+.Pp
+If a client wants to be notified of changes in server state, it can
+initiate a query for the service's particular record and leave it running.
+For example, to monitor the status of an iChat user you can use:
+.Pp
+.Dl Nm Fl q Ns \ someone@ex1._presence._tcp.local txt
+.Pp
+Everytime status of that user(someone) changes, you will see a new TXT record result reported.
+.Pp
+You can also query for a unicast name like www.apple.com and monitor its status.
+.Pp
+.Dl Nm Fl q Ns \ www.apple.com
+.Pp
+.Sh FILES
+.Pa /usr/bin/dns-sd \" Pathname
+.\"
+.Sh SEE ALSO
+.Xr mDNSResponder 8
+.\"
+.Sh BUGS
+.Nm
+bugs are tracked in Apple Radar component "mDNSResponder".
+.\"
+.Sh HISTORY
+The
+.Nm
+command first appeared in Mac OS X 10.4 (Tiger).
diff --git a/mDNSResponder/mDNSShared/dns_sd.h b/mDNSResponder/mDNSShared/dns_sd.h
new file mode 100644
index 00000000..99373de6
--- /dev/null
+++ b/mDNSResponder/mDNSShared/dns_sd.h
@@ -0,0 +1,2657 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2013 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*! @header DNS Service Discovery
+ *
+ * @discussion This section describes the functions, callbacks, and data structures
+ * that make up the DNS Service Discovery API.
+ *
+ * The DNS Service Discovery API is part of Bonjour, Apple's implementation
+ * of zero-configuration networking (ZEROCONF).
+ *
+ * Bonjour allows you to register a network service, such as a
+ * printer or file server, so that it can be found by name or browsed
+ * for by service type and domain. Using Bonjour, applications can
+ * discover what services are available on the network, along with
+ * all the information -- such as name, IP address, and port --
+ * necessary to access a particular service.
+ *
+ * In effect, Bonjour combines the functions of a local DNS server and
+ * AppleTalk. Bonjour allows applications to provide user-friendly printer
+ * and server browsing, among other things, over standard IP networks.
+ * This behavior is a result of combining protocols such as multicast and
+ * DNS to add new functionality to the network (such as multicast DNS).
+ *
+ * Bonjour gives applications easy access to services over local IP
+ * networks without requiring the service or the application to support
+ * an AppleTalk or a Netbeui stack, and without requiring a DNS server
+ * for the local network.
+ */
+
+
+/* _DNS_SD_H contains the mDNSResponder version number for this header file, formatted as follows:
+ * Major part of the build number * 10000 +
+ * minor part of the build number * 100
+ * For example, Mac OS X 10.4.9 has mDNSResponder-108.4, which would be represented as
+ * version 1080400. This allows C code to do simple greater-than and less-than comparisons:
+ * e.g. an application that requires the DNSServiceGetProperty() call (new in mDNSResponder-126) can check:
+ *
+ * #if _DNS_SD_H+0 >= 1260000
+ * ... some C code that calls DNSServiceGetProperty() ...
+ * #endif
+ *
+ * The version defined in this header file symbol allows for compile-time
+ * checking, so that C code building with earlier versions of the header file
+ * can avoid compile errors trying to use functions that aren't even defined
+ * in those earlier versions. Similar checks may also be performed at run-time:
+ * => weak linking -- to avoid link failures if run with an earlier
+ * version of the library that's missing some desired symbol, or
+ * => DNSServiceGetProperty(DaemonVersion) -- to verify whether the running daemon
+ * ("system service" on Windows) meets some required minimum functionality level.
+ */
+
+#ifndef _DNS_SD_H
+#define _DNS_SD_H 5440000
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Set to 1 if libdispatch is supported
+ * Note: May also be set by project and/or Makefile
+ */
+#ifndef _DNS_SD_LIBDISPATCH
+#define _DNS_SD_LIBDISPATCH 0
+#endif /* ndef _DNS_SD_LIBDISPATCH */
+
+/* standard calling convention under Win32 is __stdcall */
+/* Note: When compiling Intel EFI (Extensible Firmware Interface) under MS Visual Studio, the */
+/* _WIN32 symbol is defined by the compiler even though it's NOT compiling code for Windows32 */
+#if defined(_WIN32) && !defined(EFI32) && !defined(EFI64)
+#define DNSSD_API __stdcall
+#else
+#define DNSSD_API
+#endif
+
+/* stdint.h does not exist on FreeBSD 4.x; its types are defined in sys/types.h instead */
+#if defined(__FreeBSD__) && (__FreeBSD__ < 5)
+#include <sys/types.h>
+
+/* Likewise, on Sun, standard integer types are in sys/types.h */
+#elif defined(__sun__)
+#include <sys/types.h>
+
+/* EFI does not have stdint.h, or anything else equivalent */
+#elif defined(EFI32) || defined(EFI64) || defined(EFIX64)
+#include "Tiano.h"
+#if !defined(_STDINT_H_)
+typedef UINT8 uint8_t;
+typedef INT8 int8_t;
+typedef UINT16 uint16_t;
+typedef INT16 int16_t;
+typedef UINT32 uint32_t;
+typedef INT32 int32_t;
+#endif
+/* Windows has its own differences */
+#elif defined(_WIN32)
+#include <windows.h>
+#define _UNUSED
+#ifndef _MSL_STDINT_H
+typedef UINT8 uint8_t;
+typedef INT8 int8_t;
+typedef UINT16 uint16_t;
+typedef INT16 int16_t;
+typedef UINT32 uint32_t;
+typedef INT32 int32_t;
+#endif
+
+/* All other Posix platforms use stdint.h */
+#else
+#include <stdint.h>
+#endif
+
+#if _DNS_SD_LIBDISPATCH
+#include <dispatch/dispatch.h>
+#endif
+
+/* 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;
+
+struct sockaddr;
+
+/*! @enum General flags
+ * Most DNS-SD API functions and callbacks include a DNSServiceFlags parameter.
+ * As a general rule, any given bit in the 32-bit flags field has a specific fixed meaning,
+ * regardless of the function or callback being used. For any given function or callback,
+ * typically only a subset of the possible flags are meaningful, and all others should be zero.
+ * The discussion section for each API call describes which flags are valid for that call
+ * and callback. In some cases, for a particular call, it may be that no flags are currently
+ * defined, in which case the DNSServiceFlags parameter exists purely to allow future expansion.
+ * In all cases, developers should expect that in future releases, it is possible that new flag
+ * values will be defined, and write code with this in mind. For example, code that tests
+ * if (flags == kDNSServiceFlagsAdd) ...
+ * will fail if, in a future release, another bit in the 32-bit flags field is also set.
+ * The reliable way to test whether a particular bit is set is not with an equality test,
+ * but with a bitwise mask:
+ * if (flags & kDNSServiceFlagsAdd) ...
+ * With the exception of kDNSServiceFlagsValidate, each flag can be valid(be set)
+ * EITHER only as an input to one of the DNSService*() APIs OR only as an output
+ * (provide status) through any of the callbacks used. For example, kDNSServiceFlagsAdd
+ * can be set only as an output in the callback, whereas the kDNSServiceFlagsIncludeP2P
+ * can be set only as an input to the DNSService*() APIs. See comments on kDNSServiceFlagsValidate
+ * defined in enum below.
+ */
+enum
+{
+ kDNSServiceFlagsMoreComing = 0x1,
+ /* MoreComing indicates to a callback that at least one more result is
+ * queued and will be delivered following immediately after this one.
+ * When the MoreComing flag is set, applications should not immediately
+ * update their UI, because this can result in a great deal of ugly flickering
+ * on the screen, and can waste a great deal of CPU time repeatedly updating
+ * the screen with content that is then immediately erased, over and over.
+ * Applications should wait until MoreComing is not set, and then
+ * update their UI when no more changes are imminent.
+ * When MoreComing is not set, that doesn't mean there will be no more
+ * answers EVER, just that there are no more answers immediately
+ * available right now at this instant. If more answers become available
+ * in the future they will be delivered as usual.
+ */
+
+ kDNSServiceFlagsAdd = 0x2,
+ kDNSServiceFlagsDefault = 0x4,
+ /* Flags for domain enumeration and browse/query reply callbacks.
+ * "Default" applies only to enumeration and is only valid in
+ * conjunction with "Add". An enumeration callback with the "Add"
+ * flag NOT set indicates a "Remove", i.e. the domain is no longer
+ * valid.
+ */
+
+ kDNSServiceFlagsNoAutoRename = 0x8,
+ /* Flag for specifying renaming behavior on name conflict when registering
+ * non-shared records. By default, name conflicts are automatically handled
+ * by renaming the service. NoAutoRename overrides this behavior - with this
+ * flag set, name conflicts will result in a callback. The NoAutorename flag
+ * is only valid if a name is explicitly specified when registering a service
+ * (i.e. the default name is not used.)
+ */
+
+ kDNSServiceFlagsShared = 0x10,
+ kDNSServiceFlagsUnique = 0x20,
+ /* 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 = 0x40,
+ kDNSServiceFlagsRegistrationDomains = 0x80,
+ /* Flags for specifying domain enumeration type in DNSServiceEnumerateDomains.
+ * BrowseDomains enumerates domains recommended for browsing, RegistrationDomains
+ * enumerates domains recommended for registration.
+ */
+
+ kDNSServiceFlagsLongLivedQuery = 0x100,
+ /* Flag for creating a long-lived unicast query for the DNSServiceQueryRecord call. */
+
+ kDNSServiceFlagsAllowRemoteQuery = 0x200,
+ /* Flag for creating a record for which we will answer remote queries
+ * (queries from hosts more than one hop away; hosts not directly connected to the local link).
+ */
+
+ kDNSServiceFlagsForceMulticast = 0x400,
+ /* Flag for signifying that a query or registration should be performed exclusively via multicast
+ * DNS, even for a name in a domain (e.g. foo.apple.com.) that would normally imply unicast DNS.
+ */
+
+ kDNSServiceFlagsForce = 0x800, // This flag is deprecated.
+
+ kDNSServiceFlagsKnownUnique = 0x800,
+ /*
+ * Client guarantees that record names are unique, so we can skip sending out initial
+ * probe messages. Standard name conflict resolution is still done if a conflict is discovered.
+ * Currently only valid for a DNSServiceRegister call.
+ */
+
+ kDNSServiceFlagsReturnIntermediates = 0x1000,
+ /* Flag for returning intermediate results.
+ * For example, if a query results in an authoritative NXDomain (name does not exist)
+ * then that result is returned to the client. However the query is not implicitly
+ * cancelled -- it remains active and if the answer subsequently changes
+ * (e.g. because a VPN tunnel is subsequently established) then that positive
+ * result will still be returned to the client.
+ * Similarly, if a query results in a CNAME record, then in addition to following
+ * the CNAME referral, the intermediate CNAME result is also returned to the client.
+ * When this flag is not set, NXDomain errors are not returned, and CNAME records
+ * are followed silently without informing the client of the intermediate steps.
+ * (In earlier builds this flag was briefly calledkDNSServiceFlagsReturnCNAME)
+ */
+
+ kDNSServiceFlagsNonBrowsable = 0x2000,
+ /* A service registered with the NonBrowsable flag set can be resolved using
+ * DNSServiceResolve(), but will not be discoverable using DNSServiceBrowse().
+ * This is for cases where the name is actually a GUID; it is found by other means;
+ * there is no end-user benefit to browsing to find a long list of opaque GUIDs.
+ * Using the NonBrowsable flag creates SRV+TXT without the cost of also advertising
+ * an associated PTR record.
+ */
+
+ kDNSServiceFlagsShareConnection = 0x4000,
+ /* For efficiency, clients that perform many concurrent operations may want to use a
+ * single Unix Domain Socket connection with the background daemon, instead of having a
+ * separate connection for each independent operation. To use this mode, clients first
+ * call DNSServiceCreateConnection(&MainRef) to initialize the main DNSServiceRef.
+ * For each subsequent operation that is to share that same connection, the client copies
+ * the MainRef, and then passes the address of that copy, setting the ShareConnection flag
+ * to tell the library that this DNSServiceRef is not a typical uninitialized DNSServiceRef;
+ * it's a copy of an existing DNSServiceRef whose connection information should be reused.
+ *
+ * For example:
+ *
+ * DNSServiceErrorType error;
+ * DNSServiceRef MainRef;
+ * error = DNSServiceCreateConnection(&MainRef);
+ * if (error) ...
+ * DNSServiceRef BrowseRef = MainRef; // Important: COPY the primary DNSServiceRef first...
+ * error = DNSServiceBrowse(&BrowseRef, kDNSServiceFlagsShareConnection, ...); // then use the copy
+ * if (error) ...
+ * ...
+ * DNSServiceRefDeallocate(BrowseRef); // Terminate the browse operation
+ * DNSServiceRefDeallocate(MainRef); // Terminate the shared connection
+ *
+ * Notes:
+ *
+ * 1. Collective kDNSServiceFlagsMoreComing flag
+ * When callbacks are invoked using a shared DNSServiceRef, the
+ * kDNSServiceFlagsMoreComing flag applies collectively to *all* active
+ * operations sharing the same parent DNSServiceRef. If the MoreComing flag is
+ * set it means that there are more results queued on this parent DNSServiceRef,
+ * but not necessarily more results for this particular callback function.
+ * The implication of this for client programmers is that when a callback
+ * is invoked with the MoreComing flag set, the code should update its
+ * internal data structures with the new result, and set a variable indicating
+ * that its UI needs to be updated. Then, later when a callback is eventually
+ * invoked with the MoreComing flag not set, the code should update *all*
+ * stale UI elements related to that shared parent DNSServiceRef that need
+ * updating, not just the UI elements related to the particular callback
+ * that happened to be the last one to be invoked.
+ *
+ * 2. Canceling operations and kDNSServiceFlagsMoreComing
+ * Whenever you cancel any operation for which you had deferred UI updates
+ * waiting because of a kDNSServiceFlagsMoreComing flag, you should perform
+ * those deferred UI updates. This is because, after cancelling the operation,
+ * you can no longer wait for a callback *without* MoreComing set, to tell
+ * you do perform your deferred UI updates (the operation has been canceled,
+ * so there will be no more callbacks). An implication of the collective
+ * kDNSServiceFlagsMoreComing flag for shared connections is that this
+ * guideline applies more broadly -- any time you cancel an operation on
+ * a shared connection, you should perform all deferred UI updates for all
+ * operations sharing that connection. This is because the MoreComing flag
+ * might have been referring to events coming for the operation you canceled,
+ * which will now not be coming because the operation has been canceled.
+ *
+ * 3. Only share DNSServiceRef's created with DNSServiceCreateConnection
+ * Calling DNSServiceCreateConnection(&ref) creates a special shareable DNSServiceRef.
+ * DNSServiceRef's created by other calls like DNSServiceBrowse() or DNSServiceResolve()
+ * cannot be shared by copying them and using kDNSServiceFlagsShareConnection.
+ *
+ * 4. Don't Double-Deallocate
+ * Calling DNSServiceRefDeallocate(ref) for a particular operation's DNSServiceRef terminates
+ * just that operation. Calling DNSServiceRefDeallocate(ref) for the main shared DNSServiceRef
+ * (the parent DNSServiceRef, originally created by DNSServiceCreateConnection(&ref))
+ * automatically terminates the shared connection and all operations that were still using it.
+ * After doing this, DO NOT then attempt to deallocate any remaining subordinate DNSServiceRef's.
+ * The memory used by those subordinate DNSServiceRef's has already been freed, so any attempt
+ * to do a DNSServiceRefDeallocate (or any other operation) on them will result in accesses
+ * to freed memory, leading to crashes or other equally undesirable results.
+ *
+ * 5. Thread Safety
+ * The dns_sd.h API does not presuppose any particular threading model, and consequently
+ * does no locking of its own (which would require linking some specific threading library).
+ * If client code calls API routines on the same DNSServiceRef concurrently
+ * from multiple threads, it is the client's responsibility to use a mutext
+ * lock or take similar appropriate precautions to serialize those calls.
+ */
+
+ kDNSServiceFlagsSuppressUnusable = 0x8000,
+ /*
+ * This flag is meaningful only in DNSServiceQueryRecord which suppresses unusable queries on the
+ * wire. If "hostname" is a wide-area unicast DNS hostname (i.e. not a ".local." name)
+ * but this host has no routable IPv6 address, then the call will not try to look up IPv6 addresses
+ * for "hostname", since any addresses it found would be unlikely to be of any use anyway. Similarly,
+ * if this host has no routable IPv4 address, the call will not try to look up IPv4 addresses for
+ * "hostname".
+ */
+
+ kDNSServiceFlagsTimeout = 0x10000,
+ /*
+ * When kDNServiceFlagsTimeout is passed to DNSServiceQueryRecord or DNSServiceGetAddrInfo, the query is
+ * stopped after a certain number of seconds have elapsed. The time at which the query will be stopped
+ * is determined by the system and cannot be configured by the user. The query will be stopped irrespective
+ * of whether a response was given earlier or not. When the query is stopped, the callback will be called
+ * with an error code of kDNSServiceErr_Timeout and a NULL sockaddr will be returned for DNSServiceGetAddrInfo
+ * and zero length rdata will be returned for DNSServiceQueryRecord.
+ */
+
+ kDNSServiceFlagsIncludeP2P = 0x20000,
+ /*
+ * Include P2P interfaces when kDNSServiceInterfaceIndexAny is specified.
+ * By default, specifying kDNSServiceInterfaceIndexAny does not include P2P interfaces.
+ */
+
+ kDNSServiceFlagsWakeOnResolve = 0x40000,
+ /*
+ * This flag is meaningful only in DNSServiceResolve. When set, it tries to send a magic packet
+ * to wake up the client.
+ */
+
+ kDNSServiceFlagsBackgroundTrafficClass = 0x80000,
+ /*
+ * This flag is meaningful in DNSServiceBrowse, DNSServiceGetAddrInfo, DNSServiceQueryRecord,
+ * and DNSServiceResolve. When set, it uses the background traffic
+ * class for packets that service the request.
+ */
+
+ kDNSServiceFlagsIncludeAWDL = 0x100000,
+ /*
+ * Include AWDL interface when kDNSServiceInterfaceIndexAny is specified.
+ */
+
+ kDNSServiceFlagsValidate = 0x200000,
+ /*
+ * This flag is meaningful in DNSServiceGetAddrInfo and DNSServiceQueryRecord. This is the ONLY flag to be valid
+ * as an input to the APIs and also an output through the callbacks in the APIs.
+ *
+ * When this flag is passed to DNSServiceQueryRecord and DNSServiceGetAddrInfo to resolve unicast names,
+ * the response will be validated using DNSSEC. The validation results are delivered using the flags field in
+ * the callback and kDNSServiceFlagsValidate is marked in the flags to indicate that DNSSEC status is also available.
+ * When the callback is called to deliver the query results, the validation results may or may not be available.
+ * If it is not delivered along with the results, the validation status is delivered when the validation completes.
+ *
+ * When the validation results are delivered in the callback, it is indicated by marking the flags with
+ * kDNSServiceFlagsValidate and kDNSServiceFlagsAdd along with the DNSSEC status flags (described below) and a NULL
+ * sockaddr will be returned for DNSServiceGetAddrInfo and zero length rdata will be returned for DNSServiceQueryRecord.
+ * DNSSEC validation results are for the whole RRSet and not just individual records delivered in the callback. When
+ * kDNSServiceFlagsAdd is not set in the flags, applications should implicitly assume that the DNSSEC status of the
+ * RRSet that has been delivered up until that point is not valid anymore, till another callback is called with
+ * kDNSServiceFlagsAdd and kDNSServiceFlagsValidate.
+ *
+ * The following four flags indicate the status of the DNSSEC validation and marked in the flags field of the callback.
+ * When any of the four flags is set, kDNSServiceFlagsValidate will also be set. To check the validation status, the
+ * other applicable output flags should be masked. See kDNSServiceOutputFlags below.
+ */
+
+ kDNSServiceFlagsSecure = 0x200010,
+ /*
+ * The response has been validated by verifying all the signaures in the response and was able to
+ * build a successful authentication chain starting from a known trust anchor.
+ */
+
+ kDNSServiceFlagsInsecure = 0x200020,
+ /*
+ * A chain of trust cannot be built starting from a known trust anchor to the response.
+ */
+
+ kDNSServiceFlagsBogus = 0x200040,
+ /*
+ * If the response cannot be verified to be secure due to expired signatures, missing signatures etc.,
+ * then the results are considered to be bogus.
+ */
+
+ kDNSServiceFlagsIndeterminate = 0x200080,
+ /*
+ * There is no valid trust anchor that can be used to determine whether a response is secure or not.
+ */
+
+ kDNSServiceFlagsUnicastResponse = 0x400000,
+ /*
+ * Request unicast response to query.
+ */
+ kDNSServiceFlagsValidateOptional = 0x800000,
+
+ /*
+ * This flag is identical to kDNSServiceFlagsValidate except for the case where the response
+ * cannot be validated. If this flag is set in DNSServiceQueryRecord or DNSServiceGetAddrInfo,
+ * the DNSSEC records will be requested for validation. If they cannot be received for some reason
+ * during the validation (e.g., zone is not signed, zone is signed but cannot be traced back to
+ * root, recursive server does not understand DNSSEC etc.), then this will fallback to the default
+ * behavior where the validation will not be performed and no DNSSEC results will be provided.
+ *
+ * If the zone is signed and there is a valid path to a known trust anchor configured in the system
+ * and the application requires DNSSEC validation irrespective of the DNSSEC awareness in the current
+ * network, then this option MUST not be used. This is only intended to be used during the transition
+ * period where the different nodes participating in the DNS resolution may not understand DNSSEC or
+ * managed properly (e.g. missing DS record) but still want to be able to resolve DNS successfully.
+ */
+
+ kDNSServiceFlagsWakeOnlyService = 0x1000000,
+ /*
+ * This flag is meaningful only in DNSServiceRegister. When set, the service will not be registered
+ * with sleep proxy server during sleep.
+ */
+
+ kDNSServiceFlagsThresholdOne = 0x2000000,
+ kDNSServiceFlagsThresholdFinder = 0x4000000,
+ kDNSServiceFlagsThresholdReached = kDNSServiceFlagsThresholdOne,
+ /*
+ * kDNSServiceFlagsThresholdOne is meaningful only in DNSServiceBrowse. When set,
+ * the system will stop issuing browse queries on the network once the number
+ * of answers returned is one or more. It will issue queries on the network
+ * again if the number of answers drops to zero.
+ * This flag is for Apple internal use only. Third party developers
+ * should not rely on this behavior being supported in any given software release.
+ *
+ * kDNSServiceFlagsThresholdFinder is meaningful only in DNSServiceBrowse. When set,
+ * the system will stop issuing browse queries on the network once the number
+ * of answers has reached the threshold set for Finder.
+ * It will issue queries on the network again if the number of answers drops below
+ * this threshold.
+ * This flag is for Apple internal use only. Third party developers
+ * should not rely on this behavior being supported in any given software release.
+ *
+ * When kDNSServiceFlagsThresholdReached is set in the client callback add or remove event,
+ * it indicates that the browse answer threshold has been reached and no
+ * browse requests will be generated on the network until the number of answers falls
+ * below the threshold value. Add and remove events can still occur based
+ * on incoming Bonjour traffic observed by the system.
+ * The set of services return to the client is not guaranteed to represent the
+ * entire set of services present on the network once the threshold has been reached.
+ *
+ * Note, while kDNSServiceFlagsThresholdReached and kDNSServiceFlagsThresholdOne
+ * have the same value, there isn't a conflict because kDNSServiceFlagsThresholdReached
+ * is only set in the callbacks and kDNSServiceFlagsThresholdOne is only set on
+ * input to a DNSServiceBrowse call.
+ */
+};
+
+#define kDNSServiceOutputFlags (kDNSServiceFlagsValidate | kDNSServiceFlagsValidateOptional | kDNSServiceFlagsMoreComing | kDNSServiceFlagsAdd | kDNSServiceFlagsDefault)
+ /* All the output flags excluding the DNSSEC Status flags. Typically used to check DNSSEC Status */
+
+/* Possible protocol values */
+enum
+{
+ /* for DNSServiceGetAddrInfo() */
+ kDNSServiceProtocol_IPv4 = 0x01,
+ kDNSServiceProtocol_IPv6 = 0x02,
+ /* 0x04 and 0x08 reserved for future internetwork protocols */
+
+ /* for DNSServiceNATPortMappingCreate() */
+ kDNSServiceProtocol_UDP = 0x10,
+ kDNSServiceProtocol_TCP = 0x20
+ /* 0x40 and 0x80 reserved for future transport protocols, e.g. SCTP [RFC 2960]
+ * or DCCP [RFC 4340]. If future NAT gateways are created that support port
+ * mappings for these protocols, new constants will be defined here.
+ */
+};
+
+/*
+ * The values for DNS Classes and Types are listed in RFC 1035, and are available
+ * on every OS in its DNS header file. Unfortunately every OS does not have the
+ * same header file containing DNS Class and Type constants, and the names of
+ * the constants are not consistent. For example, BIND 8 uses "T_A",
+ * BIND 9 uses "ns_t_a", Windows uses "DNS_TYPE_A", etc.
+ * For this reason, these constants are also listed here, so that code using
+ * the DNS-SD programming APIs can use these constants, so that the same code
+ * can compile on all our supported platforms.
+ */
+
+enum
+{
+ kDNSServiceClass_IN = 1 /* Internet */
+};
+
+enum
+{
+ kDNSServiceType_A = 1, /* Host address. */
+ kDNSServiceType_NS = 2, /* Authoritative server. */
+ kDNSServiceType_MD = 3, /* Mail destination. */
+ kDNSServiceType_MF = 4, /* Mail forwarder. */
+ kDNSServiceType_CNAME = 5, /* Canonical name. */
+ kDNSServiceType_SOA = 6, /* Start of authority zone. */
+ kDNSServiceType_MB = 7, /* Mailbox domain name. */
+ kDNSServiceType_MG = 8, /* Mail group member. */
+ kDNSServiceType_MR = 9, /* Mail rename name. */
+ kDNSServiceType_NULL = 10, /* Null resource record. */
+ kDNSServiceType_WKS = 11, /* Well known service. */
+ kDNSServiceType_PTR = 12, /* Domain name pointer. */
+ kDNSServiceType_HINFO = 13, /* Host information. */
+ kDNSServiceType_MINFO = 14, /* Mailbox information. */
+ kDNSServiceType_MX = 15, /* Mail routing information. */
+ kDNSServiceType_TXT = 16, /* One or more text strings (NOT "zero or more..."). */
+ kDNSServiceType_RP = 17, /* Responsible person. */
+ kDNSServiceType_AFSDB = 18, /* AFS cell database. */
+ kDNSServiceType_X25 = 19, /* X_25 calling address. */
+ kDNSServiceType_ISDN = 20, /* ISDN calling address. */
+ kDNSServiceType_RT = 21, /* Router. */
+ kDNSServiceType_NSAP = 22, /* NSAP address. */
+ kDNSServiceType_NSAP_PTR = 23, /* Reverse NSAP lookup (deprecated). */
+ kDNSServiceType_SIG = 24, /* Security signature. */
+ kDNSServiceType_KEY = 25, /* Security key. */
+ kDNSServiceType_PX = 26, /* X.400 mail mapping. */
+ kDNSServiceType_GPOS = 27, /* Geographical position (withdrawn). */
+ kDNSServiceType_AAAA = 28, /* IPv6 Address. */
+ kDNSServiceType_LOC = 29, /* Location Information. */
+ kDNSServiceType_NXT = 30, /* Next domain (security). */
+ kDNSServiceType_EID = 31, /* Endpoint identifier. */
+ kDNSServiceType_NIMLOC = 32, /* Nimrod Locator. */
+ kDNSServiceType_SRV = 33, /* Server Selection. */
+ kDNSServiceType_ATMA = 34, /* ATM Address */
+ kDNSServiceType_NAPTR = 35, /* Naming Authority PoinTeR */
+ kDNSServiceType_KX = 36, /* Key Exchange */
+ kDNSServiceType_CERT = 37, /* Certification record */
+ kDNSServiceType_A6 = 38, /* IPv6 Address (deprecated) */
+ kDNSServiceType_DNAME = 39, /* Non-terminal DNAME (for IPv6) */
+ kDNSServiceType_SINK = 40, /* Kitchen sink (experimental) */
+ kDNSServiceType_OPT = 41, /* EDNS0 option (meta-RR) */
+ kDNSServiceType_APL = 42, /* Address Prefix List */
+ kDNSServiceType_DS = 43, /* Delegation Signer */
+ kDNSServiceType_SSHFP = 44, /* SSH Key Fingerprint */
+ kDNSServiceType_IPSECKEY = 45, /* IPSECKEY */
+ kDNSServiceType_RRSIG = 46, /* RRSIG */
+ kDNSServiceType_NSEC = 47, /* Denial of Existence */
+ kDNSServiceType_DNSKEY = 48, /* DNSKEY */
+ kDNSServiceType_DHCID = 49, /* DHCP Client Identifier */
+ kDNSServiceType_NSEC3 = 50, /* Hashed Authenticated Denial of Existence */
+ kDNSServiceType_NSEC3PARAM = 51, /* Hashed Authenticated Denial of Existence */
+
+ kDNSServiceType_HIP = 55, /* Host Identity Protocol */
+
+ kDNSServiceType_SPF = 99, /* Sender Policy Framework for E-Mail */
+ kDNSServiceType_UINFO = 100, /* IANA-Reserved */
+ kDNSServiceType_UID = 101, /* IANA-Reserved */
+ kDNSServiceType_GID = 102, /* IANA-Reserved */
+ kDNSServiceType_UNSPEC = 103, /* IANA-Reserved */
+
+ kDNSServiceType_TKEY = 249, /* Transaction key */
+ kDNSServiceType_TSIG = 250, /* Transaction signature. */
+ kDNSServiceType_IXFR = 251, /* Incremental zone transfer. */
+ kDNSServiceType_AXFR = 252, /* Transfer zone of authority. */
+ kDNSServiceType_MAILB = 253, /* Transfer mailbox records. */
+ kDNSServiceType_MAILA = 254, /* Transfer mail agent records. */
+ kDNSServiceType_ANY = 255 /* Wildcard match. */
+};
+
+/* 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_Firewall = -65550,
+ kDNSServiceErr_Incompatible = -65551, /* client library incompatible with daemon */
+ kDNSServiceErr_BadInterfaceIndex = -65552,
+ kDNSServiceErr_Refused = -65553,
+ kDNSServiceErr_NoSuchRecord = -65554,
+ kDNSServiceErr_NoAuth = -65555,
+ kDNSServiceErr_NoSuchKey = -65556,
+ kDNSServiceErr_NATTraversal = -65557,
+ kDNSServiceErr_DoubleNAT = -65558,
+ kDNSServiceErr_BadTime = -65559, /* Codes up to here existed in Tiger */
+ kDNSServiceErr_BadSig = -65560,
+ kDNSServiceErr_BadKey = -65561,
+ kDNSServiceErr_Transient = -65562,
+ kDNSServiceErr_ServiceNotRunning = -65563, /* Background daemon not running */
+ kDNSServiceErr_NATPortMappingUnsupported = -65564, /* NAT doesn't support PCP, NAT-PMP or UPnP */
+ kDNSServiceErr_NATPortMappingDisabled = -65565, /* NAT supports PCP, NAT-PMP or UPnP, but it's disabled by the administrator */
+ kDNSServiceErr_NoRouter = -65566, /* No router currently configured (probably no network connectivity) */
+ kDNSServiceErr_PollingMode = -65567,
+ kDNSServiceErr_Timeout = -65568
+
+ /* mDNS Error codes are in the range
+ * FFFE FF00 (-65792) to FFFE FFFF (-65537) */
+};
+
+/* Maximum length, in bytes, of a service name represented as a */
+/* literal C-String, including the terminating NULL at the end. */
+
+#define kDNSServiceMaxServiceName 64
+
+/* Maximum length, in bytes, of a domain name represented as an *escaped* C-String */
+/* including the final trailing dot, and the C-String terminating NULL at the end. */
+
+#define kDNSServiceMaxDomainName 1009
+
+/*
+ * Notes on DNS Name Escaping
+ * -- or --
+ * "Why is kDNSServiceMaxDomainName 1009, when the maximum legal domain name is 256 bytes?"
+ *
+ * All strings used in the DNS-SD APIs are UTF-8 strings. Apart from the exceptions noted below,
+ * the APIs expect the strings to be properly escaped, using the conventional DNS escaping rules:
+ *
+ * '\\' represents a single literal '\' in the name
+ * '\.' represents a single literal '.' in the name
+ * '\ddd', where ddd is a three-digit decimal value from 000 to 255,
+ * represents a single literal byte with that value.
+ * A bare unescaped '.' is a label separator, marking a boundary between domain and subdomain.
+ *
+ * The exceptions, that do not use escaping, are the routines where the full
+ * DNS name of a resource is broken, for convenience, into servicename/regtype/domain.
+ * In these routines, the "servicename" is NOT escaped. It does not need to be, since
+ * it is, by definition, just a single literal string. Any characters in that string
+ * represent exactly what they are. The "regtype" portion is, technically speaking,
+ * escaped, but since legal regtypes are only allowed to contain letters, digits,
+ * and hyphens, there is nothing to escape, so the issue is moot. The "domain"
+ * portion is also escaped, though most domains in use on the public Internet
+ * today, like regtypes, don't contain any characters that need to be escaped.
+ * As DNS-SD becomes more popular, rich-text domains for service discovery will
+ * become common, so software should be written to cope with domains with escaping.
+ *
+ * The servicename may be up to 63 bytes of UTF-8 text (not counting the C-String
+ * terminating NULL at the end). The regtype is of the form _service._tcp or
+ * _service._udp, where the "service" part is 1-15 characters, which may be
+ * letters, digits, or hyphens. The domain part of the three-part name may be
+ * any legal domain, providing that the resulting servicename+regtype+domain
+ * name does not exceed 256 bytes.
+ *
+ * For most software, these issues are transparent. When browsing, the discovered
+ * servicenames should simply be displayed as-is. When resolving, the discovered
+ * servicename/regtype/domain are simply passed unchanged to DNSServiceResolve().
+ * When a DNSServiceResolve() succeeds, the returned fullname is already in
+ * the correct format to pass to standard system DNS APIs such as res_query().
+ * For converting from servicename/regtype/domain to a single properly-escaped
+ * full DNS name, the helper function DNSServiceConstructFullName() is provided.
+ *
+ * The following (highly contrived) example illustrates the escaping process.
+ * Suppose you have an service called "Dr. Smith\Dr. Johnson", of type "_ftp._tcp"
+ * in subdomain "4th. Floor" of subdomain "Building 2" of domain "apple.com."
+ * The full (escaped) DNS name of this service's SRV record would be:
+ * Dr\.\032Smith\\Dr\.\032Johnson._ftp._tcp.4th\.\032Floor.Building\0322.apple.com.
+ */
+
+
+/*
+ * Constants for specifying an interface index
+ *
+ * Specific interface indexes are identified via a 32-bit unsigned integer returned
+ * by the if_nametoindex() family of calls.
+ *
+ * If the client passes 0 for interface index, that means "do the right thing",
+ * which (at present) means, "if the name is in an mDNS local multicast domain
+ * (e.g. 'local.', '254.169.in-addr.arpa.', '{8,9,A,B}.E.F.ip6.arpa.') then multicast
+ * on all applicable interfaces, otherwise send via unicast to the appropriate
+ * DNS server." Normally, most clients will use 0 for interface index to
+ * automatically get the default sensible behaviour.
+ *
+ * If the client passes a positive interface index, then for multicast names that
+ * indicates to do the operation only on that one interface. For unicast names the
+ * interface index is ignored unless kDNSServiceFlagsForceMulticast is also set.
+ *
+ * If the client passes kDNSServiceInterfaceIndexLocalOnly when registering
+ * a service, then that service will be found *only* by other local clients
+ * on the same machine that are browsing using kDNSServiceInterfaceIndexLocalOnly
+ * or kDNSServiceInterfaceIndexAny.
+ * If a client has a 'private' service, accessible only to other processes
+ * running on the same machine, this allows the client to advertise that service
+ * in a way such that it does not inadvertently appear in service lists on
+ * all the other machines on the network.
+ *
+ * If the client passes kDNSServiceInterfaceIndexLocalOnly when browsing
+ * then it will find *all* records registered on that same local machine.
+ * Clients explicitly wishing to discover *only* LocalOnly services can
+ * accomplish this by inspecting the interfaceIndex of each service reported
+ * to their DNSServiceBrowseReply() callback function, and discarding those
+ * where the interface index is not kDNSServiceInterfaceIndexLocalOnly.
+ *
+ * kDNSServiceInterfaceIndexP2P is meaningful only in Browse, QueryRecord, Register,
+ * and Resolve operations. It should not be used in other DNSService APIs.
+ *
+ * - If kDNSServiceInterfaceIndexP2P is passed to DNSServiceBrowse or
+ * DNSServiceQueryRecord, it restricts the operation to P2P.
+ *
+ * - If kDNSServiceInterfaceIndexP2P is passed to DNSServiceRegister, it is
+ * mapped internally to kDNSServiceInterfaceIndexAny with the kDNSServiceFlagsIncludeP2P
+ * set.
+ *
+ * - If kDNSServiceInterfaceIndexP2P is passed to DNSServiceResolve, it is
+ * mapped internally to kDNSServiceInterfaceIndexAny with the kDNSServiceFlagsIncludeP2P
+ * set, because resolving a P2P service may create and/or enable an interface whose
+ * index is not known a priori. The resolve callback will indicate the index of the
+ * interface via which the service can be accessed.
+ *
+ * If applications pass kDNSServiceInterfaceIndexAny to DNSServiceBrowse
+ * or DNSServiceQueryRecord, they must set the kDNSServiceFlagsIncludeP2P flag
+ * to include P2P. In this case, if a service instance or the record being queried
+ * is found over P2P, the resulting ADD event will indicate kDNSServiceInterfaceIndexP2P
+ * as the interface index.
+ */
+
+#define kDNSServiceInterfaceIndexAny 0
+#define kDNSServiceInterfaceIndexLocalOnly ((uint32_t)-1)
+#define kDNSServiceInterfaceIndexUnicast ((uint32_t)-2)
+#define kDNSServiceInterfaceIndexP2P ((uint32_t)-3)
+
+typedef uint32_t DNSServiceFlags;
+typedef uint32_t DNSServiceProtocol;
+typedef int32_t DNSServiceErrorType;
+
+
+/*********************************************************************************************
+*
+* Version checking
+*
+*********************************************************************************************/
+
+/* DNSServiceGetProperty() Parameters:
+ *
+ * property: The requested property.
+ * Currently the only property defined is kDNSServiceProperty_DaemonVersion.
+ *
+ * result: Place to store result.
+ * For retrieving DaemonVersion, this should be the address of a uint32_t.
+ *
+ * size: Pointer to uint32_t containing size of the result location.
+ * For retrieving DaemonVersion, this should be sizeof(uint32_t).
+ * On return the uint32_t is updated to the size of the data returned.
+ * For DaemonVersion, the returned size is always sizeof(uint32_t), but
+ * future properties could be defined which return variable-sized results.
+ *
+ * return value: Returns kDNSServiceErr_NoError on success, or kDNSServiceErr_ServiceNotRunning
+ * if the daemon (or "system service" on Windows) is not running.
+ */
+
+DNSServiceErrorType DNSSD_API DNSServiceGetProperty
+(
+ const char *property, /* Requested property (i.e. kDNSServiceProperty_DaemonVersion) */
+ void *result, /* Pointer to place to store result */
+ uint32_t *size /* size of result location */
+);
+
+/*
+ * When requesting kDNSServiceProperty_DaemonVersion, the result pointer must point
+ * to a 32-bit unsigned integer, and the size parameter must be set to sizeof(uint32_t).
+ *
+ * On return, the 32-bit unsigned integer contains the version number, formatted as follows:
+ * Major part of the build number * 10000 +
+ * minor part of the build number * 100
+ *
+ * For example, Mac OS X 10.4.9 has mDNSResponder-108.4, which would be represented as
+ * version 1080400. This allows applications to do simple greater-than and less-than comparisons:
+ * e.g. an application that requires at least mDNSResponder-108.4 can check:
+ *
+ * if (version >= 1080400) ...
+ *
+ * Example usage:
+ *
+ * uint32_t version;
+ * uint32_t size = sizeof(version);
+ * DNSServiceErrorType err = DNSServiceGetProperty(kDNSServiceProperty_DaemonVersion, &version, &size);
+ * if (!err) printf("Bonjour version is %d.%d\n", version / 10000, version / 100 % 100);
+ */
+
+#define kDNSServiceProperty_DaemonVersion "DaemonVersion"
+
+
+// Map the source port of the local UDP socket that was opened for sending the DNS query
+// to the process ID of the application that triggered the DNS resolution.
+//
+/* DNSServiceGetPID() Parameters:
+ *
+ * srcport: Source port (in network byte order) of the UDP socket that was created by
+ * mDNSResponder to send the DNS query on the wire.
+ *
+ * pid: Process ID of the application that started the name resolution which triggered
+ * mDNSResponder to send the query on the wire. The value can be -1 if the srcport
+ * cannot be mapped.
+ *
+ * return value: Returns kDNSServiceErr_NoError on success, or kDNSServiceErr_ServiceNotRunning
+ * if the daemon is not running. The value of the pid is undefined if the return
+ * value has error.
+ */
+DNSServiceErrorType DNSSD_API DNSServiceGetPID
+(
+ uint16_t srcport,
+ int32_t *pid
+);
+
+/*********************************************************************************************
+*
+* Unix Domain Socket access, DNSServiceRef deallocation, and data processing functions
+*
+*********************************************************************************************/
+
+/* DNSServiceRefSockFD()
+ *
+ * Access underlying Unix domain socket for an initialized DNSServiceRef.
+ * The DNS Service Discovery implementation 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 kqueue event source, a CFRunLoop
+ * event source, in a select() loop, etc. When the underlying event management subsystem (kqueue/
+ * select/CFRunLoop etc.) indicates to the client that data is available for reading on the
+ * socket, the client should call DNSServiceProcessResult(), 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. Alternatively,
+ * a client can choose to fork a thread and have it loop calling "DNSServiceProcessResult(ref);"
+ * If DNSServiceProcessResult() is called when no data is available for reading on the socket, it
+ * will block until data does become available, and then process the data and return to the caller.
+ * When data arrives on the socket, the client is responsible for calling DNSServiceProcessResult(ref)
+ * in a timely fashion -- if the client allows a large backlog of data to build up the daemon
+ * may terminate the connection.
+ *
+ * sdRef: A DNSServiceRef initialized by any of the DNSService calls.
+ *
+ * return value: The DNSServiceRef's underlying socket descriptor, or -1 on
+ * error.
+ */
+
+int DNSSD_API 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 DNSSD_API 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.
+ *
+ * Note: 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: If the reference was initialized with DNSServiceCreateConnection(), any DNSRecordRefs
+ * created via this reference will be invalidated by this call - the resource records are
+ * deregistered, and their DNSRecordRefs may not be used in subsequent functions. Similarly,
+ * if the reference was initialized with DNSServiceRegister, and an extra resource record was
+ * added to the service via DNSServiceAddRecord(), the DNSRecordRef created by the Add() call
+ * is invalidated when this function is called - the DNSRecordRef may not be used in subsequent
+ * functions.
+ *
+ * Note: This call is to be used only with the DNSServiceRef defined by this API.
+ *
+ * sdRef: A DNSServiceRef initialized by any of the DNSService calls.
+ *
+ */
+
+void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef);
+
+
+/*********************************************************************************************
+*
+* Domain Enumeration
+*
+*********************************************************************************************/
+
+/* DNSServiceEnumerateDomains()
+ *
+ * Asynchronously enumerate domains available for browsing and registration.
+ *
+ * The enumeration MUST be cancelled via DNSServiceRefDeallocate() when no more domains
+ * are to be found.
+ *
+ * Note that the names returned are (like all of DNS-SD) UTF-8 strings,
+ * and are escaped using standard DNS escaping rules.
+ * (See "Notes on DNS Name Escaping" earlier in this file for more details.)
+ * A graphical browser displaying a hierarchical tree-structured view should cut
+ * the names at the bare dots to yield individual labels, then de-escape each
+ * label according to the escaping rules, and then display the resulting UTF-8 text.
+ *
+ * DNSServiceDomainEnumReply Callback Parameters:
+ *
+ * sdRef: The DNSServiceRef initialized by DNSServiceEnumerateDomains().
+ *
+ * flags: Possible values are:
+ * kDNSServiceFlagsMoreComing
+ * kDNSServiceFlagsAdd
+ * kDNSServiceFlagsDefault
+ *
+ * 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 (DNSSD_API *DNSServiceDomainEnumReply)
+(
+ DNSServiceRef sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char *replyDomain,
+ void *context
+);
+
+
+/* DNSServiceEnumerateDomains() Parameters:
+ *
+ * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds
+ * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError,
+ * and the enumeration operation will run indefinitely until the client
+ * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate().
+ *
+ * flags: Possible values are:
+ * kDNSServiceFlagsBrowseDomains to enumerate domains recommended for browsing.
+ * kDNSServiceFlagsRegistrationDomains 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. See "Constants for specifying an interface index" for more details.
+ *
+ * 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 success (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 DNSSD_API 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: When a name is successfully registered, the callback will be
+ * invoked with the kDNSServiceFlagsAdd flag set. When Wide-Area
+ * DNS-SD is in use, it is possible for a single service to get
+ * more than one success callback (e.g. one in the "local" multicast
+ * DNS domain, and another in a wide-area unicast DNS domain).
+ * If a successfully-registered name later suffers a name conflict
+ * or similar problem and has to be deregistered, the callback will
+ * be invoked with the kDNSServiceFlagsAdd flag not set. The callback
+ * is *not* invoked in the case where the caller explicitly terminates
+ * the service registration by calling DNSServiceRefDeallocate(ref);
+ *
+ * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will
+ * indicate the failure that occurred (including name conflicts,
+ * if the kDNSServiceFlagsNoAutoRename flag was used when registering.)
+ * 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 (DNSSD_API *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 DNSServiceRef. If the call succeeds
+ * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError,
+ * and the registration will remain active indefinitely until the client
+ * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate().
+ *
+ * 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. See "Constants for specifying an interface index" for more details.
+ *
+ * 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).
+ * If a name is specified, it must be 1-63 bytes of UTF-8 text.
+ * If the name is longer than 63 bytes it will be automatically truncated
+ * to a legal length, unless the NoAutoRename flag is set,
+ * in which case kDNSServiceErr_BadParam will be returned.
+ *
+ * regtype: The service type followed by the protocol, separated by a dot
+ * (e.g. "_ftp._tcp"). The service type must be an underscore, followed
+ * by 1-15 characters, which may be letters, digits, or hyphens.
+ * The transport protocol must be "_tcp" or "_udp". New service types
+ * should be registered at <http://www.dns-sd.org/ServiceTypes.html>.
+ *
+ * Additional subtypes of the primary service type (where a service
+ * type has defined subtypes) follow the primary service type in a
+ * comma-separated list, with no additional spaces, e.g.
+ * "_primarytype._tcp,_subtype1,_subtype2,_subtype3"
+ * Subtypes provide a mechanism for filtered browsing: A client browsing
+ * for "_primarytype._tcp" will discover all instances of this type;
+ * a client browsing for "_primarytype._tcp,_subtype2" will discover only
+ * those instances that were registered with "_subtype2" in their list of
+ * registered subtypes.
+ *
+ * The subtype mechanism can be illustrated with some examples using the
+ * dns-sd command-line tool:
+ *
+ * % dns-sd -R Simple _test._tcp "" 1001 &
+ * % dns-sd -R Better _test._tcp,HasFeatureA "" 1002 &
+ * % dns-sd -R Best _test._tcp,HasFeatureA,HasFeatureB "" 1003 &
+ *
+ * Now:
+ * % dns-sd -B _test._tcp # will find all three services
+ * % dns-sd -B _test._tcp,HasFeatureA # finds "Better" and "Best"
+ * % dns-sd -B _test._tcp,HasFeatureB # finds only "Best"
+ *
+ * Subtype labels may be up to 63 bytes long, and may contain any eight-
+ * bit byte values, including zero bytes. However, due to the nature of
+ * using a C-string-based API, conventional DNS escaping must be used for
+ * dots ('.'), commas (','), backslashes ('\') and zero bytes, as shown below:
+ *
+ * % dns-sd -R Test '_test._tcp,s\.one,s\,two,s\\three,s\000four' local 123
+ *
+ * When a service is registered, all the clients browsing for the registered
+ * type ("regtype") will discover it. If the discovery should be
+ * restricted to a smaller set of well known peers, the service can be
+ * registered with additional data (group identifier) that is known
+ * only to a smaller set of peers. The group identifier should follow primary
+ * service type using a colon (":") as a delimeter. If subtypes are also present,
+ * it should be given before the subtype as shown below.
+ *
+ * % dns-sd -R _test1 _http._tcp:mygroup1 local 1001
+ * % dns-sd -R _test2 _http._tcp:mygroup2 local 1001
+ * % dns-sd -R _test3 _http._tcp:mygroup3,HasFeatureA local 1001
+ *
+ * Now:
+ * % dns-sd -B _http._tcp:"mygroup1" # will discover only test1
+ * % dns-sd -B _http._tcp:"mygroup2" # will discover only test2
+ * % dns-sd -B _http._tcp:"mygroup3",HasFeatureA # will discover only test3
+ *
+ * By specifying the group information, only the members of that group are
+ * discovered.
+ *
+ * The group identifier itself is not sent in clear. Only a hash of the group
+ * identifier is sent and the clients discover them anonymously. The group identifier
+ * may be up to 256 bytes long and may contain any eight bit values except comma which
+ * should be escaped.
+ *
+ * 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, in network byte order, 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. A non-NULL txtRecord MUST be a properly formatted DNS
+ * TXT record, i.e. <length byte> <data> <length byte> <data> ...
+ * Passing NULL for the txtRecord is allowed as a synonym for txtLen=1, txtRecord="",
+ * i.e. it creates a TXT record of length one containing a single empty string.
+ * RFC 1035 doesn't allow a TXT record to contain *zero* strings, so a single empty
+ * string is the smallest legal DNS TXT record.
+ * As with the other parameters, the DNSServiceRegister call copies the txtRecord
+ * data; e.g. if you allocated the storage for the txtRecord parameter with malloc()
+ * then you can safely free that memory right after the DNSServiceRegister call returns.
+ *
+ * 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 success (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 DNSSD_API 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, /* In network byte order */
+ 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().
+ *
+ * Note that the DNSServiceAddRecord/UpdateRecord/RemoveRecord are *NOT* thread-safe
+ * with respect to a single DNSServiceRef. If you plan to have multiple threads
+ * in your program simultaneously add, update, or remove records from the same
+ * DNSServiceRef, then it's the caller's responsibility to use a mutext lock
+ * or take similar appropriate precautions to serialize those calls.
+ *
+ * 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().
+ * If the above DNSServiceRef is passed to DNSServiceRefDeallocate(), RecordRef is also
+ * invalidated and may not be used further.
+ *
+ * flags: Currently ignored, reserved for future use.
+ *
+ * rrtype: The type of the record (e.g. kDNSServiceType_TXT, kDNSServiceType_SRV, etc)
+ *
+ * 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.
+ * Most clients should pass 0 to indicate that the system should
+ * select a sensible default value.
+ *
+ * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an
+ * error code indicating the error that occurred (the RecordRef is not initialized).
+ */
+
+DNSServiceErrorType DNSSD_API 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.
+ * Most clients should pass 0 to indicate that the system should
+ * select a sensible default value.
+ *
+ * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an
+ * error code indicating the error that occurred.
+ */
+
+DNSServiceErrorType DNSSD_API 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 DNSSD_API 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 kDNSServiceFlagsMoreComing and kDNSServiceFlagsAdd.
+ * 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 discovered service name. This name should be displayed to the user,
+ * and stored for subsequent use in the DNSServiceResolve() call.
+ *
+ * regtype: The service type, which is usually (but not always) the same as was passed
+ * to DNSServiceBrowse(). One case where the discovered service type may
+ * not be the same as the requested service type is when using subtypes:
+ * The client may want to browse for only those ftp servers that allow
+ * anonymous connections. The client will pass the string "_ftp._tcp,_anon"
+ * to DNSServiceBrowse(), but the type of the service that's discovered
+ * is simply "_ftp._tcp". The regtype for each discovered service instance
+ * should be stored along with the name, so that it can be passed to
+ * DNSServiceResolve() when the service is later resolved.
+ *
+ * domain: The domain of the discovered service instance. This may or may not be the
+ * same as the domain that was passed to DNSServiceBrowse(). The domain for each
+ * discovered service instance should be stored along with the name, so that
+ * it can be passed to DNSServiceResolve() when the service is later resolved.
+ *
+ * context: The context pointer that was passed to the callout.
+ *
+ */
+
+typedef void (DNSSD_API *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 DNSServiceRef. If the call succeeds
+ * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError,
+ * and the browse operation will run indefinitely until the client
+ * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate().
+ *
+ * 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. See "Constants for specifying an interface index" for more details.
+ *
+ * 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".
+ * A client may optionally specify a single subtype to perform filtered browsing:
+ * e.g. browsing for "_primarytype._tcp,_subtype" will discover only those
+ * instances of "_primarytype._tcp" that were registered specifying "_subtype"
+ * in their list of registered subtypes. Additionally, a group identifier may
+ * also be specified before the subtype e.g., _primarytype._tcp:GroupID, which
+ * will discover only the members that register the service with GroupID. See
+ * DNSServiceRegister for more details.
+ *
+ * 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 success (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 DNSSD_API 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. 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: kDNSServiceFlagsMoreComing
+ *
+ * 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>.
+ * (This name is escaped following standard DNS rules, making it suitable for
+ * passing to standard system DNS APIs such as res_query(), or to the
+ * special-purpose functions included in this API that take fullname parameters.
+ * See "Notes on DNS Name Escaping" earlier in this file for more details.)
+ *
+ * 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, in network byte order, 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.
+ *
+ * NOTE: In earlier versions of this header file, the txtRecord parameter was declared "const char *"
+ * This is incorrect, since it contains length bytes which are values in the range 0 to 255, not -128 to +127.
+ * Depending on your compiler settings, this change may cause signed/unsigned mismatch warnings.
+ * These should be fixed by updating your own callback function definition to match the corrected
+ * function signature using "const unsigned char *txtRecord". Making this change may also fix inadvertent
+ * bugs in your callback function, where it could have incorrectly interpreted a length byte with value 250
+ * as being -6 instead, with various bad consequences ranging from incorrect operation to software crashes.
+ * If you need to maintain portable code that will compile cleanly with both the old and new versions of
+ * this header file, you should update your callback function definition to use the correct unsigned value,
+ * and then in the place where you pass your callback function to DNSServiceResolve(), use a cast to eliminate
+ * the compiler warning, e.g.:
+ * DNSServiceResolve(sd, flags, index, name, regtype, domain, (DNSServiceResolveReply)MyCallback, context);
+ * This will ensure that your code compiles cleanly without warnings (and more importantly, works correctly)
+ * with both the old header and with the new corrected version.
+ *
+ */
+
+typedef void (DNSSD_API *DNSServiceResolveReply)
+(
+ DNSServiceRef sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char *fullname,
+ const char *hosttarget,
+ uint16_t port, /* In network byte order */
+ uint16_t txtLen,
+ const unsigned char *txtRecord,
+ void *context
+);
+
+
+/* DNSServiceResolve() Parameters
+ *
+ * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds
+ * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError,
+ * and the resolve operation will run indefinitely until the client
+ * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate().
+ *
+ * flags: Specifying kDNSServiceFlagsForceMulticast will cause query to be
+ * performed with a link-local mDNS query, even if the name is an
+ * apparently non-local name (i.e. a name not ending in ".local.")
+ *
+ * interfaceIndex: The interface on which to resolve the service. If this resolve call is
+ * as a result of a currently active DNSServiceBrowse() operation, then the
+ * interfaceIndex should be the index reported in the DNSServiceBrowseReply
+ * callback. If this resolve call is using information previously saved
+ * (e.g. in a preference file) for later use, then use interfaceIndex 0, because
+ * the desired service may now be reachable via a different physical interface.
+ * See "Constants for specifying an interface index" for more details.
+ *
+ * name: The name of the service instance to be resolved, as reported to the
+ * DNSServiceBrowseReply() callback.
+ *
+ * regtype: The type of the service instance to be resolved, as reported to the
+ * DNSServiceBrowseReply() callback.
+ *
+ * domain: The domain of the service instance to be resolved, as reported 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 success (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 DNSSD_API DNSServiceResolve
+(
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ const char *name,
+ const char *regtype,
+ const char *domain,
+ DNSServiceResolveReply callBack,
+ void *context /* may be NULL */
+);
+
+
+/*********************************************************************************************
+*
+* Querying Individual Specific Records
+*
+*********************************************************************************************/
+
+/* DNSServiceQueryRecord
+ *
+ * Query for an arbitrary DNS record.
+ *
+ * DNSServiceQueryRecordReply() Callback Parameters:
+ *
+ * sdRef: The DNSServiceRef initialized by DNSServiceQueryRecord().
+ *
+ * flags: Possible values are kDNSServiceFlagsMoreComing and
+ * kDNSServiceFlagsAdd. The Add flag is NOT set for PTR records
+ * with a ttl of 0, i.e. "Remove" events.
+ *
+ * interfaceIndex: The interface on which the query was resolved (the index for a given
+ * interface is determined via the if_nametoindex() family of calls).
+ * See "Constants for specifying an interface index" for more details.
+ *
+ * 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. kDNSServiceType_PTR, kDNSServiceType_SRV, etc)
+ *
+ * rrclass: The class of the resource record (usually kDNSServiceClass_IN).
+ *
+ * rdlen: The length, in bytes, of the resource record rdata.
+ *
+ * rdata: The raw rdata of the resource record.
+ *
+ * ttl: If the client wishes to cache the result for performance reasons,
+ * the TTL indicates how long the client may legitimately hold onto
+ * this result, in seconds. After the TTL expires, the client should
+ * consider the result no longer valid, and if it requires this data
+ * again, it should be re-fetched with a new query. Of course, this
+ * only applies to clients that cancel the asynchronous operation when
+ * they get a result. Clients that leave the asynchronous operation
+ * running can safely assume that the data remains valid until they
+ * get another callback telling them otherwise.
+ *
+ * context: The context pointer that was passed to the callout.
+ *
+ */
+
+typedef void (DNSSD_API *DNSServiceQueryRecordReply)
+(
+ DNSServiceRef sdRef,
+ 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. If the call succeeds
+ * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError,
+ * and the query operation will run indefinitely until the client
+ * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate().
+ *
+ * flags: kDNSServiceFlagsForceMulticast or kDNSServiceFlagsLongLivedQuery.
+ * Pass kDNSServiceFlagsLongLivedQuery to create a "long-lived" unicast
+ * query to a unicast DNS server that implements the protocol. This flag
+ * has no effect on link-local multicast queries.
+ *
+ * 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. See "Constants for specifying an interface index" for more details.
+ *
+ * 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. kDNSServiceType_PTR, kDNSServiceType_SRV, etc)
+ *
+ * rrclass: The class of the resource record (usually kDNSServiceClass_IN).
+ *
+ * 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 success (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 DNSSD_API DNSServiceQueryRecord
+(
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ const char *fullname,
+ uint16_t rrtype,
+ uint16_t rrclass,
+ DNSServiceQueryRecordReply callBack,
+ void *context /* may be NULL */
+);
+
+
+/*********************************************************************************************
+*
+* Unified lookup of both IPv4 and IPv6 addresses for a fully qualified hostname
+*
+*********************************************************************************************/
+
+/* DNSServiceGetAddrInfo
+ *
+ * Queries for the IP address of a hostname by using either Multicast or Unicast DNS.
+ *
+ * DNSServiceGetAddrInfoReply() parameters:
+ *
+ * sdRef: The DNSServiceRef initialized by DNSServiceGetAddrInfo().
+ *
+ * flags: Possible values are kDNSServiceFlagsMoreComing and
+ * kDNSServiceFlagsAdd.
+ *
+ * interfaceIndex: The interface to which the answers pertain.
+ *
+ * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will
+ * indicate the failure that occurred. Other parameters are
+ * undefined if errorCode is nonzero.
+ *
+ * hostname: The fully qualified domain name of the host to be queried for.
+ *
+ * address: IPv4 or IPv6 address.
+ *
+ * ttl: If the client wishes to cache the result for performance reasons,
+ * the TTL indicates how long the client may legitimately hold onto
+ * this result, in seconds. After the TTL expires, the client should
+ * consider the result no longer valid, and if it requires this data
+ * again, it should be re-fetched with a new query. Of course, this
+ * only applies to clients that cancel the asynchronous operation when
+ * they get a result. Clients that leave the asynchronous operation
+ * running can safely assume that the data remains valid until they
+ * get another callback telling them otherwise.
+ *
+ * context: The context pointer that was passed to the callout.
+ *
+ */
+
+typedef void (DNSSD_API *DNSServiceGetAddrInfoReply)
+(
+ DNSServiceRef sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char *hostname,
+ const struct sockaddr *address,
+ uint32_t ttl,
+ void *context
+);
+
+
+/* DNSServiceGetAddrInfo() Parameters:
+ *
+ * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds then it
+ * initializes the DNSServiceRef, returns kDNSServiceErr_NoError, and the query
+ * begins and will last indefinitely until the client terminates the query
+ * by passing this DNSServiceRef to DNSServiceRefDeallocate().
+ *
+ * flags: kDNSServiceFlagsForceMulticast or kDNSServiceFlagsLongLivedQuery.
+ * Pass kDNSServiceFlagsLongLivedQuery to create a "long-lived" unicast
+ * query to a unicast DNS server that implements the protocol. This flag
+ * has no effect on link-local multicast queries.
+ *
+ * interfaceIndex: The interface on which to issue the query. Passing 0 causes the query to be
+ * sent on all active interfaces via Multicast or the primary interface via Unicast.
+ *
+ * protocol: Pass in kDNSServiceProtocol_IPv4 to look up IPv4 addresses, or kDNSServiceProtocol_IPv6
+ * to look up IPv6 addresses, or both to look up both kinds. If neither flag is
+ * set, the system will apply an intelligent heuristic, which is (currently)
+ * that it will attempt to look up both, except:
+ *
+ * * If "hostname" is a wide-area unicast DNS hostname (i.e. not a ".local." name)
+ * but this host has no routable IPv6 address, then the call will not try to
+ * look up IPv6 addresses for "hostname", since any addresses it found would be
+ * unlikely to be of any use anyway. Similarly, if this host has no routable
+ * IPv4 address, the call will not try to look up IPv4 addresses for "hostname".
+ *
+ * hostname: The fully qualified domain name of the host to be queried for.
+ *
+ * callBack: The function to be called when the query succeeds or fails asynchronously.
+ *
+ * context: An application context pointer which is passed to the callback function
+ * (may be NULL).
+ *
+ * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous
+ * errors are delivered to the callback), otherwise returns an error code indicating
+ * the error that occurred.
+ */
+
+DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo
+(
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceProtocol protocol,
+ const char *hostname,
+ DNSServiceGetAddrInfoReply callBack,
+ void *context /* may be NULL */
+);
+
+
+/*********************************************************************************************
+*
+* Special Purpose Calls:
+* DNSServiceCreateConnection(), DNSServiceRegisterRecord(), DNSServiceReconfirmRecord()
+* (most applications will not use these)
+*
+*********************************************************************************************/
+
+/* 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 DNSSD_API 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
+ * DNSServiceCreateConnection().
+ *
+ * RecordRef: The DNSRecordRef initialized by DNSServiceRegisterRecord(). If the above
+ * DNSServiceRef is passed to DNSServiceRefDeallocate(), this DNSRecordRef is
+ * invalidated, and may not be used further.
+ *
+ * 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 (DNSSD_API *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
+ * DNSServiceRefDeallocate()).
+ *
+ * flags: Possible values are kDNSServiceFlagsShared or kDNSServiceFlagsUnique
+ * (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.
+ * See "Constants for specifying an interface index" for more details.
+ *
+ * fullname: The full domain name of the resource record.
+ *
+ * rrtype: The numerical type of the resource record (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc)
+ *
+ * rrclass: The class of the resource record (usually kDNSServiceClass_IN)
+ *
+ * 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.
+ * Most clients should pass 0 to indicate that the system should
+ * select a sensible default value.
+ *
+ * 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 success (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 DNSSD_API 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 */
+);
+
+
+/* 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.
+ * Use this routine conservatively. Reconfirming a record necessarily consumes
+ * network bandwidth, so this should not be done indiscriminately.
+ *
+ * Parameters:
+ *
+ * flags: Not currently used.
+ *
+ * interfaceIndex: Specifies the interface of the record in question.
+ * The caller must specify the interface.
+ * This API (by design) causes increased network traffic, so it requires
+ * the caller to be precise about which record should be reconfirmed.
+ * It is not possible to pass zero for the interface index to perform
+ * a "wildcard" reconfirmation, where *all* matching records are reconfirmed.
+ *
+ * fullname: The resource record's full domain name.
+ *
+ * rrtype: The resource record's type (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc)
+ *
+ * rrclass: The class of the resource record (usually kDNSServiceClass_IN).
+ *
+ * rdlen: The length, in bytes, of the resource record rdata.
+ *
+ * rdata: The raw rdata of the resource record.
+ *
+ */
+
+DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord
+(
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ const char *fullname,
+ uint16_t rrtype,
+ uint16_t rrclass,
+ uint16_t rdlen,
+ const void *rdata
+);
+
+
+/*********************************************************************************************
+*
+* NAT Port Mapping
+*
+*********************************************************************************************/
+
+/* DNSServiceNATPortMappingCreate
+ *
+ * Request a port mapping in the NAT gateway, which maps a port on the local machine
+ * to an external port on the NAT. The NAT should support either PCP, NAT-PMP or the
+ * UPnP/IGD protocol for this API to create a successful mapping. Note that this API
+ * currently supports IPv4 addresses/mappings only. If the NAT gateway supports PCP and
+ * returns an IPv6 address (incorrectly, since this API specifically requests IPv4
+ * addresses), the DNSServiceNATPortMappingReply callback will be invoked with errorCode
+ * kDNSServiceErr_NATPortMappingUnsupported.
+ *
+ * The port mapping will be renewed indefinitely until the client process exits, or
+ * explicitly terminates the port mapping request by calling DNSServiceRefDeallocate().
+ * The client callback will be invoked, informing the client of the NAT gateway's
+ * external IP address and the external port that has been allocated for this client.
+ * The client should then record this external IP address and port using whatever
+ * directory service mechanism it is using to enable peers to connect to it.
+ * (Clients advertising services using Wide-Area DNS-SD DO NOT need to use this API
+ * -- when a client calls DNSServiceRegister() NAT mappings are automatically created
+ * and the external IP address and port for the service are recorded in the global DNS.
+ * Only clients using some directory mechanism other than Wide-Area DNS-SD need to use
+ * this API to explicitly map their own ports.)
+ *
+ * It's possible that the client callback could be called multiple times, for example
+ * if the NAT gateway's IP address changes, or if a configuration change results in a
+ * different external port being mapped for this client. Over the lifetime of any long-lived
+ * port mapping, the client should be prepared to handle these notifications of changes
+ * in the environment, and should update its recorded address and/or port as appropriate.
+ *
+ * NOTE: There are two unusual aspects of how the DNSServiceNATPortMappingCreate API works,
+ * which were intentionally designed to help simplify client code:
+ *
+ * 1. It's not an error to request a NAT mapping when the machine is not behind a NAT gateway.
+ * In other NAT mapping APIs, if you request a NAT mapping and the machine is not behind a NAT
+ * gateway, then the API returns an error code -- it can't get you a NAT mapping if there's no
+ * NAT gateway. The DNSServiceNATPortMappingCreate API takes a different view. Working out
+ * whether or not you need a NAT mapping can be tricky and non-obvious, particularly on
+ * a machine with multiple active network interfaces. Rather than make every client recreate
+ * this logic for deciding whether a NAT mapping is required, the PortMapping API does that
+ * work for you. If the client calls the PortMapping API when the machine already has a
+ * routable public IP address, then instead of complaining about it and giving an error,
+ * the PortMapping API just invokes your callback, giving the machine's public address
+ * and your own port number. This means you don't need to write code to work out whether
+ * your client needs to call the PortMapping API -- just call it anyway, and if it wasn't
+ * necessary, no harm is done:
+ *
+ * - If the machine already has a routable public IP address, then your callback
+ * will just be invoked giving your own address and port.
+ * - If a NAT mapping is required and obtained, then your callback will be invoked
+ * giving you the external address and port.
+ * - If a NAT mapping is required but not obtained from the local NAT gateway,
+ * or the machine has no network connectivity, then your callback will be
+ * invoked giving zero address and port.
+ *
+ * 2. In other NAT mapping APIs, if a laptop computer is put to sleep and woken up on a new
+ * network, it's the client's job to notice this, and work out whether a NAT mapping
+ * is required on the new network, and make a new NAT mapping request if necessary.
+ * The DNSServiceNATPortMappingCreate API does this for you, automatically.
+ * The client just needs to make one call to the PortMapping API, and its callback will
+ * be invoked any time the mapping state changes. This property complements point (1) above.
+ * If the client didn't make a NAT mapping request just because it determined that one was
+ * not required at that particular moment in time, the client would then have to monitor
+ * for network state changes to determine if a NAT port mapping later became necessary.
+ * By unconditionally making a NAT mapping request, even when a NAT mapping not to be
+ * necessary, the PortMapping API will then begin monitoring network state changes on behalf of
+ * the client, and if a NAT mapping later becomes necessary, it will automatically create a NAT
+ * mapping and inform the client with a new callback giving the new address and port information.
+ *
+ * DNSServiceNATPortMappingReply() parameters:
+ *
+ * sdRef: The DNSServiceRef initialized by DNSServiceNATPortMappingCreate().
+ *
+ * flags: Currently unused, reserved for future use.
+ *
+ * interfaceIndex: The interface through which the NAT gateway is reached.
+ *
+ * errorCode: Will be kDNSServiceErr_NoError on success.
+ * Will be kDNSServiceErr_DoubleNAT when the NAT gateway is itself behind one or
+ * more layers of NAT, in which case the other parameters have the defined values.
+ * For other failures, will indicate the failure that occurred, and the other
+ * parameters are undefined.
+ *
+ * externalAddress: Four byte IPv4 address in network byte order.
+ *
+ * protocol: Will be kDNSServiceProtocol_UDP or kDNSServiceProtocol_TCP or both.
+ *
+ * internalPort: The port on the local machine that was mapped.
+ *
+ * externalPort: The actual external port in the NAT gateway that was mapped.
+ * This is likely to be different than the requested external port.
+ *
+ * ttl: The lifetime of the NAT port mapping created on the gateway.
+ * This controls how quickly stale mappings will be garbage-collected
+ * if the client machine crashes, suffers a power failure, is disconnected
+ * from the network, or suffers some other unfortunate demise which
+ * causes it to vanish without explicitly removing its NAT port mapping.
+ * It's possible that the ttl value will differ from the requested ttl value.
+ *
+ * context: The context pointer that was passed to the callout.
+ *
+ */
+
+typedef void (DNSSD_API *DNSServiceNATPortMappingReply)
+(
+ DNSServiceRef sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ uint32_t externalAddress, /* four byte IPv4 address in network byte order */
+ DNSServiceProtocol protocol,
+ uint16_t internalPort, /* In network byte order */
+ uint16_t externalPort, /* In network byte order and may be different than the requested port */
+ uint32_t ttl, /* may be different than the requested ttl */
+ void *context
+);
+
+
+/* DNSServiceNATPortMappingCreate() Parameters:
+ *
+ * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds then it
+ * initializes the DNSServiceRef, returns kDNSServiceErr_NoError, and the nat
+ * port mapping will last indefinitely until the client terminates the port
+ * mapping request by passing this DNSServiceRef to DNSServiceRefDeallocate().
+ *
+ * flags: Currently ignored, reserved for future use.
+ *
+ * interfaceIndex: The interface on which to create port mappings in a NAT gateway. Passing 0 causes
+ * the port mapping request to be sent on the primary interface.
+ *
+ * protocol: To request a port mapping, pass in kDNSServiceProtocol_UDP, or kDNSServiceProtocol_TCP,
+ * or (kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP) to map both.
+ * The local listening port number must also be specified in the internalPort parameter.
+ * To just discover the NAT gateway's external IP address, pass zero for protocol,
+ * internalPort, externalPort and ttl.
+ *
+ * internalPort: The port number in network byte order on the local machine which is listening for packets.
+ *
+ * externalPort: The requested external port in network byte order in the NAT gateway that you would
+ * like to map to the internal port. Pass 0 if you don't care which external port is chosen for you.
+ *
+ * ttl: The requested renewal period of the NAT port mapping, in seconds.
+ * If the client machine crashes, suffers a power failure, is disconnected from
+ * the network, or suffers some other unfortunate demise which causes it to vanish
+ * unexpectedly without explicitly removing its NAT port mappings, then the NAT gateway
+ * will garbage-collect old stale NAT port mappings when their lifetime expires.
+ * Requesting a short TTL causes such orphaned mappings to be garbage-collected
+ * more promptly, but consumes system resources and network bandwidth with
+ * frequent renewal packets to keep the mapping from expiring.
+ * Requesting a long TTL is more efficient on the network, but in the event of the
+ * client vanishing, stale NAT port mappings will not be garbage-collected as quickly.
+ * Most clients should pass 0 to use a system-wide default value.
+ *
+ * callBack: The function to be called when the port mapping request succeeds or fails asynchronously.
+ *
+ * context: An application context pointer which is passed to the callback function
+ * (may be NULL).
+ *
+ * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous
+ * errors are delivered to the callback), otherwise returns an error code indicating
+ * the error that occurred.
+ *
+ * If you don't actually want a port mapped, and are just calling the API
+ * because you want to find out the NAT's external IP address (e.g. for UI
+ * display) then pass zero for protocol, internalPort, externalPort and ttl.
+ */
+
+DNSServiceErrorType DNSSD_API DNSServiceNATPortMappingCreate
+(
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceProtocol protocol, /* TCP and/or UDP */
+ uint16_t internalPort, /* network byte order */
+ uint16_t externalPort, /* network byte order */
+ uint32_t ttl, /* time to live in seconds */
+ DNSServiceNATPortMappingReply callBack,
+ void *context /* may be NULL */
+);
+
+
+/*********************************************************************************************
+*
+* General Utility Functions
+*
+*********************************************************************************************/
+
+/* 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 kDNSServiceMaxDomainName (1009) bytes in length to
+ * accommodate the longest legal domain name without buffer overrun.
+ *
+ * service: The service name - any dots or backslashes 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.". Literal dots or backslashes,
+ * if any, must be escaped, e.g. "1st\. Floor.apple.com."
+ *
+ * return value: Returns kDNSServiceErr_NoError (0) on success, kDNSServiceErr_BadParam on error.
+ *
+ */
+
+DNSServiceErrorType DNSSD_API DNSServiceConstructFullName
+(
+ char * const fullName,
+ const char * const service, /* may be NULL */
+ const char * const regtype,
+ const char * const domain
+);
+
+
+/*********************************************************************************************
+*
+* TXT Record Construction Functions
+*
+*********************************************************************************************/
+
+/*
+ * A typical calling sequence for TXT record construction is something like:
+ *
+ * Client allocates storage for TXTRecord data (e.g. declare buffer on the stack)
+ * TXTRecordCreate();
+ * TXTRecordSetValue();
+ * TXTRecordSetValue();
+ * TXTRecordSetValue();
+ * ...
+ * DNSServiceRegister( ... TXTRecordGetLength(), TXTRecordGetBytesPtr() ... );
+ * TXTRecordDeallocate();
+ * Explicitly deallocate storage for TXTRecord data (if not allocated on the stack)
+ */
+
+
+/* TXTRecordRef
+ *
+ * Opaque internal data type.
+ * Note: Represents a DNS-SD TXT record.
+ */
+
+typedef union _TXTRecordRef_t { char PrivateData[16]; char *ForceNaturalAlignment; } TXTRecordRef;
+
+
+/* TXTRecordCreate()
+ *
+ * Creates a new empty TXTRecordRef referencing the specified storage.
+ *
+ * If the buffer parameter is NULL, or the specified storage size is not
+ * large enough to hold a key subsequently added using TXTRecordSetValue(),
+ * then additional memory will be added as needed using malloc().
+ *
+ * On some platforms, when memory is low, malloc() may fail. In this
+ * case, TXTRecordSetValue() will return kDNSServiceErr_NoMemory, and this
+ * error condition will need to be handled as appropriate by the caller.
+ *
+ * You can avoid the need to handle this error condition if you ensure
+ * that the storage you initially provide is large enough to hold all
+ * the key/value pairs that are to be added to the record.
+ * The caller can precompute the exact length required for all of the
+ * key/value pairs to be added, or simply provide a fixed-sized buffer
+ * known in advance to be large enough.
+ * A no-value (key-only) key requires (1 + key length) bytes.
+ * A key with empty value requires (1 + key length + 1) bytes.
+ * A key with non-empty value requires (1 + key length + 1 + value length).
+ * For most applications, DNS-SD TXT records are generally
+ * less than 100 bytes, so in most cases a simple fixed-sized
+ * 256-byte buffer will be more than sufficient.
+ * Recommended size limits for DNS-SD TXT Records are discussed in
+ * <http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt>
+ *
+ * Note: When passing parameters to and from these TXT record APIs,
+ * the key name does not include the '=' character. The '=' character
+ * is the separator between the key and value in the on-the-wire
+ * packet format; it is not part of either the key or the value.
+ *
+ * txtRecord: A pointer to an uninitialized TXTRecordRef.
+ *
+ * bufferLen: The size of the storage provided in the "buffer" parameter.
+ *
+ * buffer: Optional caller-supplied storage used to hold the TXTRecord data.
+ * This storage must remain valid for as long as
+ * the TXTRecordRef.
+ */
+
+void DNSSD_API TXTRecordCreate
+(
+ TXTRecordRef *txtRecord,
+ uint16_t bufferLen,
+ void *buffer
+);
+
+
+/* TXTRecordDeallocate()
+ *
+ * Releases any resources allocated in the course of preparing a TXT Record
+ * using TXTRecordCreate()/TXTRecordSetValue()/TXTRecordRemoveValue().
+ * Ownership of the buffer provided in TXTRecordCreate() returns to the client.
+ *
+ * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate().
+ *
+ */
+
+void DNSSD_API TXTRecordDeallocate
+(
+ TXTRecordRef *txtRecord
+);
+
+
+/* TXTRecordSetValue()
+ *
+ * Adds a key (optionally with value) to a TXTRecordRef. If the "key" already
+ * exists in the TXTRecordRef, then the current value will be replaced with
+ * the new value.
+ * Keys may exist in four states with respect to a given TXT record:
+ * - Absent (key does not appear at all)
+ * - Present with no value ("key" appears alone)
+ * - Present with empty value ("key=" appears in TXT record)
+ * - Present with non-empty value ("key=value" appears in TXT record)
+ * For more details refer to "Data Syntax for DNS-SD TXT Records" in
+ * <http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt>
+ *
+ * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate().
+ *
+ * key: A null-terminated string which only contains printable ASCII
+ * values (0x20-0x7E), excluding '=' (0x3D). Keys should be
+ * 9 characters or fewer (not counting the terminating null).
+ *
+ * valueSize: The size of the value.
+ *
+ * value: Any binary value. For values that represent
+ * textual data, UTF-8 is STRONGLY recommended.
+ * For values that represent textual data, valueSize
+ * should NOT include the terminating null (if any)
+ * at the end of the string.
+ * If NULL, then "key" will be added with no value.
+ * If non-NULL but valueSize is zero, then "key=" will be
+ * added with empty value.
+ *
+ * return value: Returns kDNSServiceErr_NoError on success.
+ * Returns kDNSServiceErr_Invalid if the "key" string contains
+ * illegal characters.
+ * Returns kDNSServiceErr_NoMemory if adding this key would
+ * exceed the available storage.
+ */
+
+DNSServiceErrorType DNSSD_API TXTRecordSetValue
+(
+ TXTRecordRef *txtRecord,
+ const char *key,
+ uint8_t valueSize, /* may be zero */
+ const void *value /* may be NULL */
+);
+
+
+/* TXTRecordRemoveValue()
+ *
+ * Removes a key from a TXTRecordRef. The "key" must be an
+ * ASCII string which exists in the TXTRecordRef.
+ *
+ * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate().
+ *
+ * key: A key name which exists in the TXTRecordRef.
+ *
+ * return value: Returns kDNSServiceErr_NoError on success.
+ * Returns kDNSServiceErr_NoSuchKey if the "key" does not
+ * exist in the TXTRecordRef.
+ */
+
+DNSServiceErrorType DNSSD_API TXTRecordRemoveValue
+(
+ TXTRecordRef *txtRecord,
+ const char *key
+);
+
+
+/* TXTRecordGetLength()
+ *
+ * Allows you to determine the length of the raw bytes within a TXTRecordRef.
+ *
+ * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate().
+ *
+ * return value: Returns the size of the raw bytes inside a TXTRecordRef
+ * which you can pass directly to DNSServiceRegister() or
+ * to DNSServiceUpdateRecord().
+ * Returns 0 if the TXTRecordRef is empty.
+ */
+
+uint16_t DNSSD_API TXTRecordGetLength
+(
+ const TXTRecordRef *txtRecord
+);
+
+
+/* TXTRecordGetBytesPtr()
+ *
+ * Allows you to retrieve a pointer to the raw bytes within a TXTRecordRef.
+ *
+ * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate().
+ *
+ * return value: Returns a pointer to the raw bytes inside the TXTRecordRef
+ * which you can pass directly to DNSServiceRegister() or
+ * to DNSServiceUpdateRecord().
+ */
+
+const void * DNSSD_API TXTRecordGetBytesPtr
+(
+ const TXTRecordRef *txtRecord
+);
+
+
+/*********************************************************************************************
+*
+* TXT Record Parsing Functions
+*
+*********************************************************************************************/
+
+/*
+ * A typical calling sequence for TXT record parsing is something like:
+ *
+ * Receive TXT record data in DNSServiceResolve() callback
+ * if (TXTRecordContainsKey(txtLen, txtRecord, "key")) then do something
+ * val1ptr = TXTRecordGetValuePtr(txtLen, txtRecord, "key1", &len1);
+ * val2ptr = TXTRecordGetValuePtr(txtLen, txtRecord, "key2", &len2);
+ * ...
+ * memcpy(myval1, val1ptr, len1);
+ * memcpy(myval2, val2ptr, len2);
+ * ...
+ * return;
+ *
+ * If you wish to retain the values after return from the DNSServiceResolve()
+ * callback, then you need to copy the data to your own storage using memcpy()
+ * or similar, as shown in the example above.
+ *
+ * If for some reason you need to parse a TXT record you built yourself
+ * using the TXT record construction functions above, then you can do
+ * that using TXTRecordGetLength and TXTRecordGetBytesPtr calls:
+ * TXTRecordGetValue(TXTRecordGetLength(x), TXTRecordGetBytesPtr(x), key, &len);
+ *
+ * Most applications only fetch keys they know about from a TXT record and
+ * ignore the rest.
+ * However, some debugging tools wish to fetch and display all keys.
+ * To do that, use the TXTRecordGetCount() and TXTRecordGetItemAtIndex() calls.
+ */
+
+/* TXTRecordContainsKey()
+ *
+ * Allows you to determine if a given TXT Record contains a specified key.
+ *
+ * txtLen: The size of the received TXT Record.
+ *
+ * txtRecord: Pointer to the received TXT Record bytes.
+ *
+ * key: A null-terminated ASCII string containing the key name.
+ *
+ * return value: Returns 1 if the TXT Record contains the specified key.
+ * Otherwise, it returns 0.
+ */
+
+int DNSSD_API TXTRecordContainsKey
+(
+ uint16_t txtLen,
+ const void *txtRecord,
+ const char *key
+);
+
+
+/* TXTRecordGetValuePtr()
+ *
+ * Allows you to retrieve the value for a given key from a TXT Record.
+ *
+ * txtLen: The size of the received TXT Record
+ *
+ * txtRecord: Pointer to the received TXT Record bytes.
+ *
+ * key: A null-terminated ASCII string containing the key name.
+ *
+ * valueLen: On output, will be set to the size of the "value" data.
+ *
+ * return value: Returns NULL if the key does not exist in this TXT record,
+ * or exists with no value (to differentiate between
+ * these two cases use TXTRecordContainsKey()).
+ * Returns pointer to location within TXT Record bytes
+ * if the key exists with empty or non-empty value.
+ * For empty value, valueLen will be zero.
+ * For non-empty value, valueLen will be length of value data.
+ */
+
+const void * DNSSD_API TXTRecordGetValuePtr
+(
+ uint16_t txtLen,
+ const void *txtRecord,
+ const char *key,
+ uint8_t *valueLen
+);
+
+
+/* TXTRecordGetCount()
+ *
+ * Returns the number of keys stored in the TXT Record. The count
+ * can be used with TXTRecordGetItemAtIndex() to iterate through the keys.
+ *
+ * txtLen: The size of the received TXT Record.
+ *
+ * txtRecord: Pointer to the received TXT Record bytes.
+ *
+ * return value: Returns the total number of keys in the TXT Record.
+ *
+ */
+
+uint16_t DNSSD_API TXTRecordGetCount
+(
+ uint16_t txtLen,
+ const void *txtRecord
+);
+
+
+/* TXTRecordGetItemAtIndex()
+ *
+ * Allows you to retrieve a key name and value pointer, given an index into
+ * a TXT Record. Legal index values range from zero to TXTRecordGetCount()-1.
+ * It's also possible to iterate through keys in a TXT record by simply
+ * calling TXTRecordGetItemAtIndex() repeatedly, beginning with index zero
+ * and increasing until TXTRecordGetItemAtIndex() returns kDNSServiceErr_Invalid.
+ *
+ * On return:
+ * For keys with no value, *value is set to NULL and *valueLen is zero.
+ * For keys with empty value, *value is non-NULL and *valueLen is zero.
+ * For keys with non-empty value, *value is non-NULL and *valueLen is non-zero.
+ *
+ * txtLen: The size of the received TXT Record.
+ *
+ * txtRecord: Pointer to the received TXT Record bytes.
+ *
+ * itemIndex: An index into the TXT Record.
+ *
+ * keyBufLen: The size of the string buffer being supplied.
+ *
+ * key: A string buffer used to store the key name.
+ * On return, the buffer contains a null-terminated C string
+ * giving the key name. DNS-SD TXT keys are usually
+ * 9 characters or fewer. To hold the maximum possible
+ * key name, the buffer should be 256 bytes long.
+ *
+ * valueLen: On output, will be set to the size of the "value" data.
+ *
+ * value: On output, *value is set to point to location within TXT
+ * Record bytes that holds the value data.
+ *
+ * return value: Returns kDNSServiceErr_NoError on success.
+ * Returns kDNSServiceErr_NoMemory if keyBufLen is too short.
+ * Returns kDNSServiceErr_Invalid if index is greater than
+ * TXTRecordGetCount()-1.
+ */
+
+DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex
+(
+ uint16_t txtLen,
+ const void *txtRecord,
+ uint16_t itemIndex,
+ uint16_t keyBufLen,
+ char *key,
+ uint8_t *valueLen,
+ const void **value
+);
+
+#if _DNS_SD_LIBDISPATCH
+/*
+ * DNSServiceSetDispatchQueue
+ *
+ * Allows you to schedule a DNSServiceRef on a serial dispatch queue for receiving asynchronous
+ * callbacks. It's the clients responsibility to ensure that the provided dispatch queue is running.
+ *
+ * A typical application that uses CFRunLoopRun or dispatch_main on its main thread will
+ * usually schedule DNSServiceRefs on its main queue (which is always a serial queue)
+ * using "DNSServiceSetDispatchQueue(sdref, dispatch_get_main_queue());"
+ *
+ * If there is any error during the processing of events, the application callback will
+ * be called with an error code. For shared connections, each subordinate DNSServiceRef
+ * will get its own error callback. Currently these error callbacks only happen
+ * if the mDNSResponder daemon is manually terminated or crashes, and the error
+ * code in this case is kDNSServiceErr_ServiceNotRunning. The application must call
+ * DNSServiceRefDeallocate to free the DNSServiceRef when it gets such an error code.
+ * These error callbacks are rare and should not normally happen on customer machines,
+ * but application code should be written defensively to handle such error callbacks
+ * gracefully if they occur.
+ *
+ * After using DNSServiceSetDispatchQueue on a DNSServiceRef, calling DNSServiceProcessResult
+ * on the same DNSServiceRef will result in undefined behavior and should be avoided.
+ *
+ * Once the application successfully schedules a DNSServiceRef on a serial dispatch queue using
+ * DNSServiceSetDispatchQueue, it cannot remove the DNSServiceRef from the dispatch queue, or use
+ * DNSServiceSetDispatchQueue a second time to schedule the DNSServiceRef onto a different serial dispatch
+ * queue. Once scheduled onto a dispatch queue a DNSServiceRef will deliver events to that queue until
+ * the application no longer requires that operation and terminates it using DNSServiceRefDeallocate.
+ *
+ * service: DNSServiceRef that was allocated and returned to the application, when the
+ * application calls one of the DNSService API.
+ *
+ * queue: dispatch queue where the application callback will be scheduled
+ *
+ * return value: Returns kDNSServiceErr_NoError on success.
+ * Returns kDNSServiceErr_NoMemory if it cannot create a dispatch source
+ * Returns kDNSServiceErr_BadParam if the service param is invalid or the
+ * queue param is invalid
+ */
+
+DNSServiceErrorType DNSSD_API DNSServiceSetDispatchQueue
+(
+ DNSServiceRef service,
+ dispatch_queue_t queue
+);
+#endif //_DNS_SD_LIBDISPATCH
+
+#if !defined(_WIN32)
+typedef void (DNSSD_API *DNSServiceSleepKeepaliveReply)
+(
+ DNSServiceRef sdRef,
+ DNSServiceErrorType errorCode,
+ void *context
+);
+DNSServiceErrorType DNSSD_API DNSServiceSleepKeepalive
+(
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ int fd,
+ unsigned int timeout,
+ DNSServiceSleepKeepaliveReply callBack,
+ void *context
+);
+#endif
+
+#ifdef APPLE_OSX_mDNSResponder
+/* DNSServiceCreateDelegateConnection()
+ *
+ * Create a delegate 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.
+ *
+ * pid : Process ID of the delegate
+ *
+ * uuid: UUID of the delegate
+ *
+ * Note that only one of the two arguments (pid or uuid) can be specified. If pid
+ * is zero, uuid will be assumed to be a valid value; otherwise pid will be used.
+ *
+ * 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). kDNSServiceErr_NotAuth is
+ * returned to indicate that the calling process does not have entitlements
+ * to use this API.
+ */
+DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid);
+#endif
+
+#ifdef __APPLE_API_PRIVATE
+
+#define kDNSServiceCompPrivateDNS "PrivateDNS"
+#define kDNSServiceCompMulticastDNS "MulticastDNS"
+
+#endif //__APPLE_API_PRIVATE
+
+/* Some C compiler cleverness. We can make the compiler check certain things for us,
+ * and report errors at compile-time if anything is wrong. The usual way to do this would
+ * be to use a run-time "if" statement or the conventional run-time "assert" mechanism, but
+ * then you don't find out what's wrong until you run the software. This way, if the assertion
+ * condition is false, the array size is negative, and the complier complains immediately.
+ */
+
+struct CompileTimeAssertionChecks_DNS_SD
+{
+ char assert0[(sizeof(union _TXTRecordRef_t) == 16) ? 1 : -1];
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DNS_SD_H */
diff --git a/mDNSResponder/mDNSShared/dnsextd.8 b/mDNSResponder/mDNSShared/dnsextd.8
new file mode 100644
index 00000000..796caaba
--- /dev/null
+++ b/mDNSResponder/mDNSShared/dnsextd.8
@@ -0,0 +1,69 @@
+.\" -*- tab-width: 4 -*-
+.\"
+.\" Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved.
+.\"
+.\" Licensed under the Apache License, Version 2.0 (the "License");
+.\" you may not use this file except in compliance with the License.
+.\" You may obtain a copy of the License at
+.\"
+.\" http://www.apache.org/licenses/LICENSE-2.0
+.\"
+.\" Unless required by applicable law or agreed to in writing, software
+.\" distributed under the License is distributed on an "AS IS" BASIS,
+.\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+.\" See the License for the specific language governing permissions and
+.\" limitations under the License.
+.\"
+.Dd August 2004 \" Date
+.Dt dnsextd 8 \" Document Title
+.Os Darwin \" Operating System
+.\"
+.Sh NAME
+.Nm dnsextd
+.Nd BIND Extension Daemon \" Name Description for whatis database
+.\"
+.Sh SYNOPSIS
+.Nm
+.\"
+.Sh DESCRIPTION
+.Nm
+is a daemon invoked at boot time, running alongside BIND 9,
+to implement two EDNS0 extensions to the standard DNS protocol.
+.Pp
+.Nm
+allows clients to perform DNS Updates with an attached lease lifetime,
+so that if the client crashes or is disconnected from the network, its
+address records will be automatically deleted after the lease expires.
+.Pp
+.Nm
+allows clients to perform long-lived queries. Instead of rapidly polling
+the server to discover when information changes, long-lived queries
+enable a client to indicate its interest in some set of data, and then
+be notified asynchronously by the server whenever any of that data changes.
+.Pp
+.Nm
+has no user-specifiable command-line argument, and users should not run
+.Nm
+manually.
+.\"
+.Sh SEE ALSO
+.Xr mDNS 1
+.Xr mDNSResponder 8
+.Pp
+For information on Dynamic DNS Update, see RFC 2136
+"Dynamic Updates in the Domain Name System (DNS UPDATE)"
+.Pp
+For information on Dynamic DNS Update Leases, see
+.Pa http://files.dns-sd.org/draft-dns-update-leases.txt
+.Pp
+For information on Long-Lived Queries, see
+.Pa http://files.dns-sd.org/draft-dns-llq.txt
+.\"
+.Sh BUGS
+.Nm
+bugs are tracked in Apple Radar component "mDNSResponder".
+.\"
+.Sh HISTORY
+The
+.Nm
+daemon first appeared in Mac OS X 10.4 (Tiger).
diff --git a/mDNSResponder/mDNSShared/dnsextd.c b/mDNSResponder/mDNSShared/dnsextd.c
new file mode 100644
index 00000000..aa06650a
--- /dev/null
+++ b/mDNSResponder/mDNSShared/dnsextd.c
@@ -0,0 +1,3150 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if __APPLE__
+// In Mac OS X 10.5 and later trying to use the daemon function gives a “‘daemon’ is deprecated”
+// error, which prevents compilation because we build with "-Werror".
+// Since this is supposed to be portable cross-platform code, we don't care that daemon is
+// deprecated on Mac OS X 10.5, so we use this preprocessor trick to eliminate the error message.
+#define daemon yes_we_know_that_daemon_is_deprecated_in_os_x_10_5_thankyou
+#endif
+
+#include <signal.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <time.h>
+#include <errno.h>
+
+#if __APPLE__
+#undef daemon
+extern int daemon(int, int);
+#endif
+
+// Solaris doesn't have daemon(), so we define it here
+#ifdef NOT_HAVE_DAEMON
+#include "../mDNSPosix/mDNSUNP.h" // For daemon()
+#endif // NOT_HAVE_DAEMON
+
+#include "dnsextd.h"
+#include "../mDNSShared/uds_daemon.h"
+#include "../mDNSShared/dnssd_ipc.h"
+#include "../mDNSCore/uDNS.h"
+#include "../mDNSShared/DebugServices.h"
+
+// Compatibility workaround
+#ifndef AF_LOCAL
+#define AF_LOCAL AF_UNIX
+#endif
+
+//
+// Constants
+//
+mDNSexport const char ProgramName[] = "dnsextd";
+
+#define LOOPBACK "127.0.0.1"
+#if !defined(LISTENQ)
+# define LISTENQ 128 // tcp connection backlog
+#endif
+#define RECV_BUFLEN 9000
+#define LEASETABLE_INIT_NBUCKETS 256 // initial hashtable size (doubles as table fills)
+#define EXPIRATION_INTERVAL 300 // check for expired records every 5 minutes
+#define SRV_TTL 7200 // TTL For _dns-update SRV records
+#define CONFIG_FILE "/etc/dnsextd.conf"
+#define TCP_SOCKET_FLAGS kTCPSocketFlags_UseTLS
+
+// LLQ Lease bounds (seconds)
+#define LLQ_MIN_LEASE (15 * 60)
+#define LLQ_MAX_LEASE (120 * 60)
+#define LLQ_LEASE_FUDGE 60
+
+// LLQ SOA poll interval (microseconds)
+#define LLQ_MONITOR_ERR_INTERVAL (60 * 1000000)
+#define LLQ_MONITOR_INTERVAL 250000
+#ifdef SIGINFO
+#define INFO_SIGNAL SIGINFO
+#else
+#define INFO_SIGNAL SIGUSR1
+#endif
+
+#define SAME_INADDR(x,y) (*((mDNSu32 *)&x) == *((mDNSu32 *)&y))
+
+//
+// Data Structures
+// Structs/fields that must be locked for thread safety are explicitly commented
+//
+
+// args passed to UDP request handler thread as void*
+
+typedef struct
+{
+ PktMsg pkt;
+ struct sockaddr_in cliaddr;
+ DaemonInfo *d;
+ int sd;
+} UDPContext;
+
+// args passed to TCP request handler thread as void*
+typedef struct
+{
+ PktMsg pkt;
+ struct sockaddr_in cliaddr;
+ TCPSocket *sock; // socket connected to client
+ DaemonInfo *d;
+} TCPContext;
+
+// args passed to UpdateAnswerList thread as void*
+typedef struct
+{
+ DaemonInfo *d;
+ AnswerListElem *a;
+} UpdateAnswerListArgs;
+
+//
+// Global Variables
+//
+
+// booleans to determine runtime output
+// read-only after initialization (no mutex protection)
+static mDNSBool foreground = 0;
+static mDNSBool verbose = 0;
+
+// globals set via signal handler (accessed exclusively by main select loop and signal handler)
+static mDNSBool terminate = 0;
+static mDNSBool dumptable = 0;
+static mDNSBool hangup = 0;
+
+// global for config file location
+static char * cfgfile = NULL;
+
+//
+// Logging Routines
+// Log messages are delivered to syslog unless -f option specified
+//
+
+// common message logging subroutine
+mDNSlocal void PrintLog(const char *buffer)
+{
+ if (foreground)
+ {
+ fprintf(stderr,"%s\n", buffer);
+ fflush(stderr);
+ }
+ else
+ {
+ openlog("dnsextd", LOG_CONS, LOG_DAEMON);
+ syslog(LOG_ERR, "%s", buffer);
+ closelog();
+ }
+}
+
+// Verbose Logging (conditional on -v option)
+mDNSlocal void VLog(const char *format, ...)
+{
+ char buffer[512];
+ va_list ptr;
+
+ if (!verbose) return;
+ va_start(ptr,format);
+ buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0;
+ va_end(ptr);
+ PrintLog(buffer);
+}
+
+// Unconditional Logging
+mDNSlocal void Log(const char *format, ...)
+{
+ char buffer[512];
+ va_list ptr;
+
+ va_start(ptr,format);
+ buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0;
+ va_end(ptr);
+ PrintLog(buffer);
+}
+
+// Error Logging
+// prints message "dnsextd <function>: <operation> - <error message>"
+// must be compiled w/ -D_REENTRANT for thread-safe errno usage
+mDNSlocal void LogErr(const char *fn, const char *operation)
+{
+ char buf[512], errbuf[256];
+ strerror_r(errno, errbuf, sizeof(errbuf));
+ snprintf(buf, sizeof(buf), "%s: %s - %s", fn, operation, errbuf);
+ PrintLog(buf);
+}
+
+//
+// Networking Utility Routines
+//
+
+// Convert DNS Message Header from Network to Host byte order
+mDNSlocal void HdrNToH(PktMsg *pkt)
+{
+ // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
+ mDNSu8 *ptr = (mDNSu8 *)&pkt->msg.h.numQuestions;
+ pkt->msg.h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
+ pkt->msg.h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
+ pkt->msg.h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]);
+ pkt->msg.h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]);
+}
+
+// Convert DNS Message Header from Host to Network byte order
+mDNSlocal void HdrHToN(PktMsg *pkt)
+{
+ mDNSu16 numQuestions = pkt->msg.h.numQuestions;
+ mDNSu16 numAnswers = pkt->msg.h.numAnswers;
+ mDNSu16 numAuthorities = pkt->msg.h.numAuthorities;
+ mDNSu16 numAdditionals = pkt->msg.h.numAdditionals;
+ mDNSu8 *ptr = (mDNSu8 *)&pkt->msg.h.numQuestions;
+
+ // Put all the integer values in IETF byte-order (MSB first, LSB second)
+ *ptr++ = (mDNSu8)(numQuestions >> 8);
+ *ptr++ = (mDNSu8)(numQuestions & 0xFF);
+ *ptr++ = (mDNSu8)(numAnswers >> 8);
+ *ptr++ = (mDNSu8)(numAnswers & 0xFF);
+ *ptr++ = (mDNSu8)(numAuthorities >> 8);
+ *ptr++ = (mDNSu8)(numAuthorities & 0xFF);
+ *ptr++ = (mDNSu8)(numAdditionals >> 8);
+ *ptr++ = (mDNSu8)(numAdditionals & 0xFF);
+}
+
+
+// Add socket to event loop
+
+mDNSlocal mStatus AddSourceToEventLoop( DaemonInfo * self, TCPSocket *sock, EventCallback callback, void *context )
+{
+ EventSource * newSource;
+ mStatus err = mStatus_NoError;
+
+ if ( self->eventSources.LinkOffset == 0 )
+ {
+ InitLinkedList( &self->eventSources, offsetof( EventSource, next));
+ }
+
+ newSource = ( EventSource*) malloc( sizeof *newSource );
+ if ( newSource == NULL )
+ {
+ err = mStatus_NoMemoryErr;
+ goto exit;
+ }
+
+ newSource->callback = callback;
+ newSource->context = context;
+ newSource->sock = sock;
+ newSource->fd = mDNSPlatformTCPGetFD( sock );
+
+ AddToTail( &self->eventSources, newSource );
+
+exit:
+
+ return err;
+}
+
+
+// Remove socket from event loop
+
+mDNSlocal mStatus RemoveSourceFromEventLoop( DaemonInfo * self, TCPSocket *sock )
+{
+ EventSource * source;
+ mStatus err;
+
+ for ( source = ( EventSource* ) self->eventSources.Head; source; source = source->next )
+ {
+ if ( source->sock == sock )
+ {
+ RemoveFromList( &self->eventSources, source );
+
+ free( source );
+ err = mStatus_NoError;
+ goto exit;
+ }
+ }
+
+ err = mStatus_NoSuchNameErr;
+
+exit:
+
+ return err;
+}
+
+// create a socket connected to nameserver
+// caller terminates connection via close()
+mDNSlocal TCPSocket *ConnectToServer(DaemonInfo *d)
+{
+ int ntries = 0, retry = 0;
+
+ while (1)
+ {
+ mDNSIPPort port = zeroIPPort;
+ int fd;
+
+ TCPSocket *sock = mDNSPlatformTCPSocket( NULL, 0, &port, mDNSfalse );
+ if ( !sock ) { LogErr("ConnectToServer", "socket"); return NULL; }
+ fd = mDNSPlatformTCPGetFD( sock );
+ if (!connect( fd, (struct sockaddr *)&d->ns_addr, sizeof(d->ns_addr))) return sock;
+ mDNSPlatformTCPCloseConnection( sock );
+ if (++ntries < 10)
+ {
+ LogErr("ConnectToServer", "connect");
+ Log("ConnectToServer - retrying connection");
+ if (!retry) retry = 500000 + random() % 500000;
+ usleep(retry);
+ retry *= 2;
+ }
+ else { Log("ConnectToServer - %d failed attempts. Aborting.", ntries); return NULL; }
+ }
+}
+
+// send an entire block of data over a connected socket
+mDNSlocal int MySend(TCPSocket *sock, const void *msg, int len)
+{
+ int selectval, n, nsent = 0;
+ fd_set wset;
+ struct timeval timeout = { 3, 0 }; // until we remove all calls from main thread, keep timeout short
+
+ while (nsent < len)
+ {
+ int fd;
+
+ FD_ZERO(&wset);
+
+ fd = mDNSPlatformTCPGetFD( sock );
+
+ FD_SET( fd, &wset );
+ selectval = select( fd+1, NULL, &wset, NULL, &timeout);
+ if (selectval < 0) { LogErr("MySend", "select"); return -1; }
+ if (!selectval || !FD_ISSET(fd, &wset)) { Log("MySend - timeout"); return -1; }
+
+ n = mDNSPlatformWriteTCP( sock, ( char* ) msg + nsent, len - nsent);
+
+ if (n < 0) { LogErr("MySend", "send"); return -1; }
+ nsent += n;
+ }
+ return 0;
+}
+
+// Transmit a DNS message, prefixed by its length, over TCP, blocking if necessary
+mDNSlocal int SendPacket(TCPSocket *sock, PktMsg *pkt)
+{
+ // send the lenth, in network byte order
+ mDNSu16 len = htons((mDNSu16)pkt->len);
+ if (MySend(sock, &len, sizeof(len)) < 0) return -1;
+
+ // send the message
+ VLog("SendPacket Q:%d A:%d A:%d A:%d ",
+ ntohs(pkt->msg.h.numQuestions),
+ ntohs(pkt->msg.h.numAnswers),
+ ntohs(pkt->msg.h.numAuthorities),
+ ntohs(pkt->msg.h.numAdditionals));
+ return MySend(sock, &pkt->msg, pkt->len);
+}
+
+// Receive len bytes, waiting until we have all of them.
+// Returns number of bytes read (which should always be the number asked for).
+static int my_recv(TCPSocket *sock, void *const buf, const int len, mDNSBool * closed)
+{
+ // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions;
+ // use an explicit while() loop instead.
+ // Also, don't try to do '+=' arithmetic on the original "void *" pointer --
+ // arithmetic on "void *" pointers is compiler-dependent.
+
+ fd_set rset;
+ struct timeval timeout = { 3, 0 }; // until we remove all calls from main thread, keep timeout short
+ int selectval, remaining = len;
+ char *ptr = (char *)buf;
+ ssize_t num_read;
+
+ while (remaining)
+ {
+ int fd;
+
+ fd = mDNSPlatformTCPGetFD( sock );
+
+ FD_ZERO(&rset);
+ FD_SET(fd, &rset);
+ selectval = select(fd+1, &rset, NULL, NULL, &timeout);
+ if (selectval < 0) { LogErr("my_recv", "select"); return -1; }
+ if (!selectval || !FD_ISSET(fd, &rset))
+ {
+ Log("my_recv - timeout");
+ return -1;
+ }
+
+ num_read = mDNSPlatformReadTCP( sock, ptr, remaining, closed );
+
+ if (((num_read == 0) && *closed) || (num_read < 0) || (num_read > remaining)) return -1;
+ if (num_read == 0) return 0;
+ ptr += num_read;
+ remaining -= num_read;
+ }
+ return(len);
+}
+
+// Return a DNS Message read off of a TCP socket, or NULL on failure
+// If storage is non-null, result is placed in that buffer. Otherwise,
+// returned value is allocated with Malloc, and contains sufficient extra
+// storage for a Lease OPT RR
+
+mDNSlocal PktMsg*
+RecvPacket
+(
+ TCPSocket * sock,
+ PktMsg * storage,
+ mDNSBool * closed
+)
+{
+ int nread;
+ int allocsize;
+ mDNSu16 msglen = 0;
+ PktMsg * pkt = NULL;
+ unsigned int srclen;
+ int fd;
+ mStatus err = 0;
+
+ fd = mDNSPlatformTCPGetFD( sock );
+
+ nread = my_recv( sock, &msglen, sizeof( msglen ), closed );
+
+ require_action_quiet( nread != -1, exit, err = mStatus_UnknownErr );
+ require_action_quiet( nread > 0, exit, err = mStatus_NoError );
+
+ msglen = ntohs( msglen );
+ require_action_quiet( nread == sizeof( msglen ), exit, err = mStatus_UnknownErr; Log( "Could not read length field of message") );
+
+ if ( storage )
+ {
+ require_action_quiet( msglen <= sizeof( storage->msg ), exit, err = mStatus_UnknownErr; Log( "RecvPacket: provided buffer too small." ) );
+ pkt = storage;
+ }
+ else
+ {
+ // buffer extra space to add an OPT RR
+
+ if ( msglen > sizeof(DNSMessage))
+ {
+ allocsize = sizeof(PktMsg) - sizeof(DNSMessage) + msglen;
+ }
+ else
+ {
+ allocsize = sizeof(PktMsg);
+ }
+
+ pkt = malloc(allocsize);
+ require_action_quiet( pkt, exit, err = mStatus_NoMemoryErr; LogErr( "RecvPacket", "malloc" ) );
+ mDNSPlatformMemZero( pkt, sizeof( *pkt ) );
+ }
+
+ pkt->len = msglen;
+ srclen = sizeof(pkt->src);
+
+ if ( getpeername( fd, ( struct sockaddr* ) &pkt->src, &srclen ) || ( srclen != sizeof( pkt->src ) ) )
+ {
+ LogErr("RecvPacket", "getpeername");
+ mDNSPlatformMemZero(&pkt->src, sizeof(pkt->src));
+ }
+
+ nread = my_recv(sock, &pkt->msg, msglen, closed );
+ require_action_quiet( nread >= 0, exit, err = mStatus_UnknownErr ; LogErr( "RecvPacket", "recv" ) );
+ require_action_quiet( nread == msglen, exit, err = mStatus_UnknownErr ; Log( "Could not read entire message" ) );
+ require_action_quiet( pkt->len >= sizeof( DNSMessageHeader ), exit, err = mStatus_UnknownErr ; Log( "RecvPacket: Message too short (%d bytes)", pkt->len ) );
+
+exit:
+
+ if ( err && pkt )
+ {
+ if ( pkt != storage )
+ {
+ free(pkt);
+ }
+
+ pkt = NULL;
+ }
+
+ return pkt;
+}
+
+
+mDNSlocal DNSZone*
+FindZone
+(
+ DaemonInfo * self,
+ domainname * name
+)
+{
+ DNSZone * zone;
+
+ for ( zone = self->zones; zone; zone = zone->next )
+ {
+ if ( SameDomainName( &zone->name, name ) )
+ {
+ break;
+ }
+ }
+
+ return zone;
+}
+
+
+mDNSlocal mDNSBool
+ZoneHandlesName
+(
+ const domainname * zname,
+ const domainname * dname
+)
+{
+ mDNSu16 i = DomainNameLength( zname );
+ mDNSu16 j = DomainNameLength( dname );
+
+ if ( ( i == ( MAX_DOMAIN_NAME + 1 ) ) || ( j == ( MAX_DOMAIN_NAME + 1 ) ) || ( i > j ) || ( memcmp( zname->c, dname->c + ( j - i ), i ) != 0 ) )
+ {
+ return mDNSfalse;
+ }
+
+ return mDNStrue;
+}
+
+
+mDNSlocal mDNSBool IsQuery( PktMsg * pkt )
+{
+ return ( pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ) == (mDNSu8) ( kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery );
+}
+
+
+mDNSlocal mDNSBool IsUpdate( PktMsg * pkt )
+{
+ return ( pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ) == (mDNSu8) ( kDNSFlag0_OP_Update );
+}
+
+
+mDNSlocal mDNSBool IsNotify(PktMsg *pkt)
+{
+ return ( pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ) == ( mDNSu8) ( kDNSFlag0_OP_Notify );
+}
+
+
+mDNSlocal mDNSBool IsLLQRequest(PktMsg *pkt)
+{
+ const mDNSu8 *ptr = NULL, *end = (mDNSu8 *)&pkt->msg + pkt->len;
+ LargeCacheRecord lcr;
+ int i;
+ mDNSBool result = mDNSfalse;
+
+ HdrNToH(pkt);
+ if ((mDNSu8)(pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) != (mDNSu8)(kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery)) goto end;
+
+ if (!pkt->msg.h.numAdditionals) goto end;
+ ptr = LocateAdditionals(&pkt->msg, end);
+ if (!ptr) goto end;
+
+ // find last Additional info.
+ for (i = 0; i < pkt->msg.h.numAdditionals; i++)
+ {
+ ptr = GetLargeResourceRecord(NULL, &pkt->msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr);
+ if (!ptr) { Log("Unable to read additional record"); goto end; }
+ }
+
+ if ( lcr.r.resrec.rrtype == kDNSType_OPT && lcr.r.resrec.rdlength >= DNSOpt_LLQData_Space && lcr.r.resrec.rdata->u.opt[0].opt == kDNSOpt_LLQ )
+ {
+ result = mDNStrue;
+ }
+
+end:
+ HdrHToN(pkt);
+ return result;
+}
+
+// !!!KRS implement properly
+mDNSlocal mDNSBool IsLLQAck(PktMsg *pkt)
+{
+ if ((pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ) == (mDNSu8) ( kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery ) &&
+ pkt->msg.h.numQuestions && !pkt->msg.h.numAnswers && !pkt->msg.h.numAuthorities) return mDNStrue;
+ return mDNSfalse;
+}
+
+
+mDNSlocal mDNSBool
+IsPublicSRV
+(
+ DaemonInfo * self,
+ DNSQuestion * q
+)
+{
+ DNameListElem * elem;
+ mDNSBool ret = mDNSfalse;
+ int i = ( int ) DomainNameLength( &q->qname ) - 1;
+
+ for ( elem = self->public_names; elem; elem = elem->next )
+ {
+ int j = ( int ) DomainNameLength( &elem->name ) - 1;
+
+ if ( i > j )
+ {
+ for ( ; i >= 0; i--, j-- )
+ {
+ if ( q->qname.c[ i ] != elem->name.c[ j ] )
+ {
+ ret = mDNStrue;
+ goto exit;
+ }
+ }
+ }
+ }
+
+exit:
+
+ return ret;
+}
+
+
+mDNSlocal void
+SetZone
+(
+ DaemonInfo * self,
+ PktMsg * pkt
+)
+{
+ domainname zname;
+ mDNSu8 QR_OP;
+ const mDNSu8 * ptr = pkt->msg.data;
+ mDNSBool exception = mDNSfalse;
+
+ // Initialize
+
+ pkt->zone = NULL;
+ pkt->isZonePublic = mDNStrue;
+ zname.c[0] = '\0';
+
+ // Figure out what type of packet this is
+
+ QR_OP = ( mDNSu8 ) ( pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask );
+
+ if ( IsQuery( pkt ) )
+ {
+ DNSQuestion question;
+
+ // It's a query
+
+ ptr = getQuestion( &pkt->msg, ptr, ( ( mDNSu8* ) &pkt->msg ) + pkt->len, NULL, &question );
+
+ AppendDomainName( &zname, &question.qname );
+
+ exception = ( ( question.qtype == kDNSType_SOA ) || ( question.qtype == kDNSType_NS ) || ( ( question.qtype == kDNSType_SRV ) && IsPublicSRV( self, &question ) ) );
+ }
+ else if ( IsUpdate( pkt ) )
+ {
+ DNSQuestion question;
+
+ // It's an update. The format of the zone section is the same as the format for the question section
+ // according to RFC 2136, so we'll just treat this as a question so we can get at the zone.
+
+ ptr = getQuestion( &pkt->msg, ptr, ( ( mDNSu8* ) &pkt->msg ) + pkt->len, NULL, &question );
+
+ AppendDomainName( &zname, &question.qname );
+
+ exception = mDNSfalse;
+ }
+
+ if ( zname.c[0] != '\0' )
+ {
+ // Find the right zone
+
+ for ( pkt->zone = self->zones; pkt->zone; pkt->zone = pkt->zone->next )
+ {
+ if ( ZoneHandlesName( &pkt->zone->name, &zname ) )
+ {
+ VLog( "found correct zone %##s for query", pkt->zone->name.c );
+
+ pkt->isZonePublic = ( ( pkt->zone->type == kDNSZonePublic ) || exception );
+
+ VLog( "zone %##s is %s", pkt->zone->name.c, ( pkt->isZonePublic ) ? "public" : "private" );
+
+ break;
+ }
+ }
+ }
+}
+
+
+mDNSlocal int
+UDPServerTransaction(const DaemonInfo *d, const PktMsg *request, PktMsg *reply, mDNSBool *trunc)
+{
+ fd_set rset;
+ struct timeval timeout = { 3, 0 }; // until we remove all calls from main thread, keep timeout short
+ int sd;
+ int res;
+ mStatus err = mStatus_NoError;
+
+ // Initialize
+
+ *trunc = mDNSfalse;
+
+ // Create a socket
+
+ sd = socket( AF_INET, SOCK_DGRAM, 0 );
+ require_action( sd >= 0, exit, err = mStatus_UnknownErr; LogErr( "UDPServerTransaction", "socket" ) );
+
+ // Send the packet to the nameserver
+
+ VLog("UDPServerTransaction Q:%d A:%d A:%d A:%d ",
+ ntohs(request->msg.h.numQuestions),
+ ntohs(request->msg.h.numAnswers),
+ ntohs(request->msg.h.numAuthorities),
+ ntohs(request->msg.h.numAdditionals));
+ res = sendto( sd, (char *)&request->msg, request->len, 0, ( struct sockaddr* ) &d->ns_addr, sizeof( d->ns_addr ) );
+ require_action( res == (int) request->len, exit, err = mStatus_UnknownErr; LogErr( "UDPServerTransaction", "sendto" ) );
+
+ // Wait for reply
+
+ FD_ZERO( &rset );
+ FD_SET( sd, &rset );
+ res = select( sd + 1, &rset, NULL, NULL, &timeout );
+ require_action( res >= 0, exit, err = mStatus_UnknownErr; LogErr( "UDPServerTransaction", "select" ) );
+ require_action( ( res > 0 ) && FD_ISSET( sd, &rset ), exit, err = mStatus_UnknownErr; Log( "UDPServerTransaction - timeout" ) );
+
+ // Receive reply
+
+ reply->len = recvfrom( sd, &reply->msg, sizeof(reply->msg), 0, NULL, NULL );
+ require_action( ( ( int ) reply->len ) >= 0, exit, err = mStatus_UnknownErr; LogErr( "UDPServerTransaction", "recvfrom" ) );
+ require_action( reply->len >= sizeof( DNSMessageHeader ), exit, err = mStatus_UnknownErr; Log( "UDPServerTransaction - Message too short (%d bytes)", reply->len ) );
+
+ // Check for truncation bit
+
+ if ( reply->msg.h.flags.b[0] & kDNSFlag0_TC )
+ {
+ *trunc = mDNStrue;
+ }
+
+exit:
+
+ if ( sd >= 0 )
+ {
+ close( sd );
+ }
+
+ return err;
+}
+
+//
+// Dynamic Update Utility Routines
+//
+
+// check if a request and server response complete a successful dynamic update
+mDNSlocal mDNSBool SuccessfulUpdateTransaction(PktMsg *request, PktMsg *reply)
+{
+ char buf[32];
+ char *vlogmsg = NULL;
+
+ // check messages
+ if (!request || !reply) { vlogmsg = "NULL message"; goto failure; }
+ if (request->len < sizeof(DNSMessageHeader) || reply->len < sizeof(DNSMessageHeader)) { vlogmsg = "Malformatted message"; goto failure; }
+
+ // check request operation
+ if ((request->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) != (request->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask))
+ { vlogmsg = "Request opcode not an update"; goto failure; }
+
+ // check result
+ if ((reply->msg.h.flags.b[1] & kDNSFlag1_RC_Mask)) { vlogmsg = "Reply contains non-zero rcode"; goto failure; }
+ if ((reply->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) != (kDNSFlag0_OP_Update | kDNSFlag0_QR_Response))
+ { vlogmsg = "Reply opcode not an update response"; goto failure; }
+
+ VLog("Successful update from %s", inet_ntop(AF_INET, &request->src.sin_addr, buf, 32));
+ return mDNStrue;
+
+failure:
+ VLog("Request %s: %s", inet_ntop(AF_INET, &request->src.sin_addr, buf, 32), vlogmsg);
+ return mDNSfalse;
+}
+
+// Allocate an appropriately sized CacheRecord and copy data from original.
+// Name pointer in CacheRecord object is set to point to the name specified
+//
+mDNSlocal CacheRecord *CopyCacheRecord(const CacheRecord *orig, domainname *name)
+{
+ CacheRecord *cr;
+ size_t size = sizeof(*cr);
+ if (orig->resrec.rdlength > InlineCacheRDSize) size += orig->resrec.rdlength - InlineCacheRDSize;
+ cr = malloc(size);
+ if (!cr) { LogErr("CopyCacheRecord", "malloc"); return NULL; }
+ memcpy(cr, orig, size);
+ cr->resrec.rdata = (RData*)&cr->smallrdatastorage;
+ cr->resrec.name = name;
+
+ return cr;
+}
+
+
+//
+// Lease Hashtable Utility Routines
+//
+
+// double hash table size
+// caller must lock table prior to invocation
+mDNSlocal void RehashTable(DaemonInfo *d)
+{
+ RRTableElem *ptr, *tmp, **new;
+ int i, bucket, newnbuckets = d->nbuckets * 2;
+
+ VLog("Rehashing lease table (new size %d buckets)", newnbuckets);
+ new = malloc(sizeof(RRTableElem *) * newnbuckets);
+ if (!new) { LogErr("RehashTable", "malloc"); return; }
+ mDNSPlatformMemZero(new, newnbuckets * sizeof(RRTableElem *));
+
+ for (i = 0; i < d->nbuckets; i++)
+ {
+ ptr = d->table[i];
+ while (ptr)
+ {
+ bucket = ptr->rr.resrec.namehash % newnbuckets;
+ tmp = ptr;
+ ptr = ptr->next;
+ tmp->next = new[bucket];
+ new[bucket] = tmp;
+ }
+ }
+ d->nbuckets = newnbuckets;
+ free(d->table);
+ d->table = new;
+}
+
+// print entire contents of hashtable, invoked via SIGINFO
+mDNSlocal void PrintLeaseTable(DaemonInfo *d)
+{
+ int i;
+ RRTableElem *ptr;
+ char rrbuf[MaxMsg], addrbuf[16];
+ struct timeval now;
+ int hr, min, sec;
+
+ if (gettimeofday(&now, NULL)) { LogErr("PrintTable", "gettimeofday"); return; }
+ if (pthread_mutex_lock(&d->tablelock)) { LogErr("PrintTable", "pthread_mutex_lock"); return; }
+
+ Log("Dumping Lease Table Contents (table contains %d resource records)", d->nelems);
+ for (i = 0; i < d->nbuckets; i++)
+ {
+ for (ptr = d->table[i]; ptr; ptr = ptr->next)
+ {
+ hr = ((ptr->expire - now.tv_sec) / 60) / 60;
+ min = ((ptr->expire - now.tv_sec) / 60) % 60;
+ sec = (ptr->expire - now.tv_sec) % 60;
+ Log("Update from %s, Expires in %d:%d:%d\n\t%s", inet_ntop(AF_INET, &ptr->cli.sin_addr, addrbuf, 16), hr, min, sec,
+ GetRRDisplayString_rdb(&ptr->rr.resrec, &ptr->rr.resrec.rdata->u, rrbuf));
+ }
+ }
+ pthread_mutex_unlock(&d->tablelock);
+}
+
+//
+// Startup SRV Registration Routines
+// Register _dns-update._udp/_tcp.<zone> SRV records indicating the port on which
+// the daemon accepts requests
+//
+
+// delete all RRS of a given name/type
+mDNSlocal mDNSu8 *putRRSetDeletion(DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *limit, ResourceRecord *rr)
+{
+ ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name);
+ if (!ptr || ptr + 10 >= limit) return NULL; // out of space
+ ptr[0] = (mDNSu8)(rr->rrtype >> 8);
+ ptr[1] = (mDNSu8)(rr->rrtype & 0xFF);
+ ptr[2] = (mDNSu8)((mDNSu16)kDNSQClass_ANY >> 8);
+ ptr[3] = (mDNSu8)((mDNSu16)kDNSQClass_ANY & 0xFF);
+ mDNSPlatformMemZero(ptr+4, sizeof(rr->rroriginalttl) + sizeof(rr->rdlength)); // zero ttl/rdata
+ msg->h.mDNS_numUpdates++;
+ return ptr + 10;
+}
+
+mDNSlocal mDNSu8 *PutUpdateSRV(DaemonInfo *d, DNSZone * zone, PktMsg *pkt, mDNSu8 *ptr, char *regtype, mDNSIPPort port, mDNSBool registration)
+{
+ AuthRecord rr;
+ char hostname[1024], buf[MaxMsg];
+ mDNSu8 *end = (mDNSu8 *)&pkt->msg + sizeof(DNSMessage);
+
+ ( void ) d;
+
+ mDNS_SetupResourceRecord(&rr, NULL, 0, kDNSType_SRV, SRV_TTL, kDNSRecordTypeUnique, AuthRecordAny, NULL, NULL);
+ rr.resrec.rrclass = kDNSClass_IN;
+ rr.resrec.rdata->u.srv.priority = 0;
+ rr.resrec.rdata->u.srv.weight = 0;
+ rr.resrec.rdata->u.srv.port = port;
+ if (gethostname(hostname, 1024) < 0 || !MakeDomainNameFromDNSNameString(&rr.resrec.rdata->u.srv.target, hostname))
+ rr.resrec.rdata->u.srv.target.c[0] = '\0';
+
+ MakeDomainNameFromDNSNameString(&rr.namestorage, regtype);
+ AppendDomainName(&rr.namestorage, &zone->name);
+ VLog("%s %s", registration ? "Registering SRV record" : "Deleting existing RRSet",
+ GetRRDisplayString_rdb(&rr.resrec, &rr.resrec.rdata->u, buf));
+ if (registration) ptr = PutResourceRecord(&pkt->msg, ptr, &pkt->msg.h.mDNS_numUpdates, &rr.resrec);
+ else ptr = putRRSetDeletion(&pkt->msg, ptr, end, &rr.resrec);
+ return ptr;
+}
+
+
+// perform dynamic update.
+// specify deletion by passing false for the register parameter, otherwise register the records.
+mDNSlocal int UpdateSRV(DaemonInfo *d, mDNSBool registration)
+{
+ TCPSocket *sock = NULL;
+ DNSZone * zone;
+ int err = mStatus_NoError;
+
+ sock = ConnectToServer( d );
+ require_action( sock, exit, err = mStatus_UnknownErr; Log( "UpdateSRV: ConnectToServer failed" ) );
+
+ for ( zone = d->zones; zone; zone = zone->next )
+ {
+ PktMsg pkt;
+ mDNSu8 *ptr = pkt.msg.data;
+ mDNSu8 *end = (mDNSu8 *)&pkt.msg + sizeof(DNSMessage);
+ PktMsg *reply = NULL;
+ mDNSBool closed;
+ mDNSBool ok;
+
+ // Initialize message
+ InitializeDNSMessage(&pkt.msg.h, zeroID, UpdateReqFlags);
+ pkt.src.sin_addr.s_addr = zerov4Addr.NotAnInteger; // address field set solely for verbose logging in subroutines
+ pkt.src.sin_family = AF_INET;
+
+ // format message body
+ ptr = putZone(&pkt.msg, ptr, end, &zone->name, mDNSOpaque16fromIntVal(kDNSClass_IN));
+ require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
+
+ if ( zone->type == kDNSZonePrivate )
+ {
+ ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-update-tls._tcp.", d->private_port, registration);
+ require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
+ ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-query-tls._tcp.", d->private_port, registration);
+ require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
+ ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-llq-tls._tcp.", d->private_port, registration);
+ require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
+
+ if ( !registration )
+ {
+ ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-update._udp.", d->llq_port, registration);
+ require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
+ ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-llq._udp.", d->llq_port, registration);
+ require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
+ }
+ }
+ else
+ {
+ if ( !registration )
+ {
+ ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-update-tls.", d->private_port, registration);
+ require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
+ ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-query-tls.", d->private_port, registration);
+ require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
+ ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-llq-tls.", d->private_port, registration);
+ require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
+ }
+
+ ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-update._udp.", d->llq_port, registration);
+ require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
+ ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-llq._udp.", d->llq_port, registration);
+ require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
+ }
+
+ HdrHToN(&pkt);
+
+ if ( zone->updateKeys )
+ {
+ DNSDigest_SignMessage( &pkt.msg, &ptr, zone->updateKeys, 0 );
+ require_action( ptr, exit, Log("UpdateSRV: Error constructing lease expiration update" ) );
+ }
+
+ pkt.len = ptr - (mDNSu8 *)&pkt.msg;
+
+ // send message, receive reply
+
+ err = SendPacket( sock, &pkt );
+ require_action( !err, exit, Log( "UpdateSRV: SendPacket failed" ) );
+
+ reply = RecvPacket( sock, NULL, &closed );
+ require_action( reply, exit, err = mStatus_UnknownErr; Log( "UpdateSRV: RecvPacket returned NULL" ) );
+
+ ok = SuccessfulUpdateTransaction( &pkt, reply );
+
+ if ( !ok )
+ {
+ Log("SRV record registration failed with rcode %d", reply->msg.h.flags.b[1] & kDNSFlag1_RC_Mask);
+ }
+
+ free( reply );
+ }
+
+exit:
+
+ if ( sock )
+ {
+ mDNSPlatformTCPCloseConnection( sock );
+ }
+
+ return err;
+}
+
+// wrapper routines/macros
+#define ClearUpdateSRV(d) UpdateSRV(d, 0)
+
+// clear any existing records prior to registration
+mDNSlocal int SetUpdateSRV(DaemonInfo *d)
+{
+ int err;
+
+ err = ClearUpdateSRV(d); // clear any existing record
+ if (!err) err = UpdateSRV(d, 1);
+ return err;
+}
+
+//
+// Argument Parsing and Configuration
+//
+
+mDNSlocal void PrintUsage(void)
+{
+ fprintf(stderr, "Usage: dnsextd [-f <config file>] [-vhd] ...\n"
+ "Use \"dnsextd -h\" for help\n");
+}
+
+mDNSlocal void PrintHelp(void)
+{
+ fprintf(stderr, "\n\n");
+ PrintUsage();
+
+ fprintf(stderr,
+ "dnsextd is a daemon that implements DNS extensions supporting Dynamic DNS Update Leases\n"
+ "and Long Lived Queries, used in Wide-Area DNS Service Discovery, on behalf of name servers\n"
+ "that do not natively support these extensions. (See dns-sd.org for more info on DNS Service\n"
+ "Discovery, Update Leases, and Long Lived Queries.)\n\n"
+
+ "dnsextd requires one argument,the zone, which is the domain for which Update Leases\n"
+ "and Long Lived Queries are to be administered. dnsextd communicates directly with the\n"
+ "primary master server for this zone.\n\n"
+
+ "The options are as follows:\n\n"
+
+ "-f Specify configuration file. The default is /etc/dnsextd.conf.\n\n"
+
+ "-d Run daemon in foreground.\n\n"
+
+ "-h Print help.\n\n"
+
+ "-v Verbose output.\n\n"
+ );
+}
+
+
+// Note: ProcessArgs called before process is daemonized, and therefore must open no descriptors
+// returns 0 (success) if program is to continue execution
+// output control arguments (-f, -v) do not affect this routine
+mDNSlocal int ProcessArgs(int argc, char *argv[], DaemonInfo *d)
+{
+ DNSZone * zone;
+ int opt;
+ int err = 0;
+
+ cfgfile = strdup( CONFIG_FILE );
+ require_action( cfgfile, arg_error, err = mStatus_NoMemoryErr );
+
+ // defaults, may be overriden by command option
+
+ // setup our sockaddr
+
+ mDNSPlatformMemZero( &d->addr, sizeof( d->addr ) );
+ d->addr.sin_addr.s_addr = zerov4Addr.NotAnInteger;
+ d->addr.sin_port = UnicastDNSPort.NotAnInteger;
+ d->addr.sin_family = AF_INET;
+#ifndef NOT_HAVE_SA_LEN
+ d->addr.sin_len = sizeof( d->addr );
+#endif
+
+ // setup nameserver's sockaddr
+
+ mDNSPlatformMemZero(&d->ns_addr, sizeof(d->ns_addr));
+ d->ns_addr.sin_family = AF_INET;
+ inet_pton( AF_INET, LOOPBACK, &d->ns_addr.sin_addr );
+ d->ns_addr.sin_port = NSIPCPort.NotAnInteger;
+#ifndef NOT_HAVE_SA_LEN
+ d->ns_addr.sin_len = sizeof( d->ns_addr );
+#endif
+
+ // setup our ports
+
+ d->private_port = PrivateDNSPort;
+ d->llq_port = DNSEXTPort;
+
+ while ((opt = getopt(argc, argv, "f:hdv")) != -1)
+ {
+ switch(opt)
+ {
+ case 'f': free( cfgfile ); cfgfile = strdup( optarg ); require_action( cfgfile, arg_error, err = mStatus_NoMemoryErr ); break;
+ case 'h': PrintHelp(); return -1;
+ case 'd': foreground = 1; break; // Also used when launched via OS X's launchd mechanism
+ case 'v': verbose = 1; break;
+ default: goto arg_error;
+ }
+ }
+
+ err = ParseConfig( d, cfgfile );
+ require_noerr( err, arg_error );
+
+ // Make sure we've specified some zones
+
+ require_action( d->zones, arg_error, err = mStatus_UnknownErr );
+
+ // if we have a shared secret, use it for the entire zone
+
+ for ( zone = d->zones; zone; zone = zone->next )
+ {
+ if ( zone->updateKeys )
+ {
+ AssignDomainName( &zone->updateKeys->domain, &zone->name );
+ }
+ }
+
+ return 0;
+
+arg_error:
+
+ PrintUsage();
+ return -1;
+}
+
+
+//
+// Initialization Routines
+//
+
+// Allocate memory, initialize locks and bookkeeping variables
+mDNSlocal int InitLeaseTable(DaemonInfo *d)
+{
+ if (pthread_mutex_init(&d->tablelock, NULL)) { LogErr("InitLeaseTable", "pthread_mutex_init"); return -1; }
+ d->nbuckets = LEASETABLE_INIT_NBUCKETS;
+ d->nelems = 0;
+ d->table = malloc(sizeof(RRTableElem *) * LEASETABLE_INIT_NBUCKETS);
+ if (!d->table) { LogErr("InitLeaseTable", "malloc"); return -1; }
+ mDNSPlatformMemZero(d->table, sizeof(RRTableElem *) * LEASETABLE_INIT_NBUCKETS);
+ return 0;
+}
+
+
+mDNSlocal int
+SetupSockets
+(
+ DaemonInfo * self
+)
+{
+ static const int kOn = 1;
+ int sockpair[2];
+ mDNSBool private = mDNSfalse;
+ struct sockaddr_in daddr;
+ DNSZone * zone;
+ mStatus err = 0;
+
+ // set up sockets on which we all ns requests
+
+ self->tcpsd = socket( AF_INET, SOCK_STREAM, 0 );
+ require_action( dnssd_SocketValid(self->tcpsd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) );
+
+#if defined(SO_REUSEADDR)
+ err = setsockopt(self->tcpsd, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn));
+ require_action( !err, exit, LogErr( "SetupSockets", "SO_REUSEADDR self->tcpsd" ) );
+#endif
+
+ err = bind( self->tcpsd, ( struct sockaddr* ) &self->addr, sizeof( self->addr ) );
+ require_action( !err, exit, LogErr( "SetupSockets", "bind self->tcpsd" ) );
+
+ err = listen( self->tcpsd, LISTENQ );
+ require_action( !err, exit, LogErr( "SetupSockets", "listen" ) );
+
+ self->udpsd = socket( AF_INET, SOCK_DGRAM, 0 );
+ require_action( dnssd_SocketValid(self->udpsd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) );
+
+#if defined(SO_REUSEADDR)
+ err = setsockopt(self->udpsd, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn));
+ require_action( !err, exit, LogErr( "SetupSockets", "SO_REUSEADDR self->udpsd" ) );
+#endif
+
+ err = bind( self->udpsd, ( struct sockaddr* ) &self->addr, sizeof( self->addr ) );
+ require_action( !err, exit, LogErr( "SetupSockets", "bind self->udpsd" ) );
+
+ // set up sockets on which we receive llq requests
+
+ mDNSPlatformMemZero(&self->llq_addr, sizeof(self->llq_addr));
+ self->llq_addr.sin_family = AF_INET;
+ self->llq_addr.sin_addr.s_addr = zerov4Addr.NotAnInteger;
+ self->llq_addr.sin_port = ( self->llq_port.NotAnInteger ) ? self->llq_port.NotAnInteger : DNSEXTPort.NotAnInteger;
+
+ if (self->llq_addr.sin_port == self->addr.sin_port)
+ {
+ self->llq_tcpsd = self->tcpsd;
+ self->llq_udpsd = self->udpsd;
+ }
+ else
+ {
+ self->llq_tcpsd = socket( AF_INET, SOCK_STREAM, 0 );
+ require_action( dnssd_SocketValid(self->llq_tcpsd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) );
+
+#if defined(SO_REUSEADDR)
+ err = setsockopt(self->llq_tcpsd, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn));
+ require_action( !err, exit, LogErr( "SetupSockets", "SO_REUSEADDR self->llq_tcpsd" ) );
+#endif
+
+ err = bind( self->llq_tcpsd, ( struct sockaddr* ) &self->llq_addr, sizeof( self->llq_addr ) );
+ require_action( !err, exit, LogErr( "SetupSockets", "bind self->llq_tcpsd" ) );
+
+ err = listen( self->llq_tcpsd, LISTENQ );
+ require_action( !err, exit, LogErr( "SetupSockets", "listen" ) );
+
+ self->llq_udpsd = socket( AF_INET, SOCK_DGRAM, 0 );
+ require_action( dnssd_SocketValid(self->llq_udpsd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) );
+
+#if defined(SO_REUSEADDR)
+ err = setsockopt(self->llq_udpsd, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn));
+ require_action( !err, exit, LogErr( "SetupSockets", "SO_REUSEADDR self->llq_udpsd" ) );
+#endif
+
+ err = bind(self->llq_udpsd, ( struct sockaddr* ) &self->llq_addr, sizeof( self->llq_addr ) );
+ require_action( !err, exit, LogErr( "SetupSockets", "bind self->llq_udpsd" ) );
+ }
+
+ // set up Unix domain socket pair for LLQ polling thread to signal main thread that a change to the zone occurred
+
+ err = socketpair( AF_LOCAL, SOCK_STREAM, 0, sockpair );
+ require_action( !err, exit, LogErr( "SetupSockets", "socketpair" ) );
+
+ self->LLQEventListenSock = sockpair[0];
+ self->LLQEventNotifySock = sockpair[1];
+
+ // set up socket on which we receive private requests
+
+ self->llq_tcpsd = socket( AF_INET, SOCK_STREAM, 0 );
+ require_action( dnssd_SocketValid(self->tlssd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) );
+ mDNSPlatformMemZero(&daddr, sizeof(daddr));
+ daddr.sin_family = AF_INET;
+ daddr.sin_addr.s_addr = zerov4Addr.NotAnInteger;
+ daddr.sin_port = ( self->private_port.NotAnInteger ) ? self->private_port.NotAnInteger : PrivateDNSPort.NotAnInteger;
+
+ self->tlssd = socket( AF_INET, SOCK_STREAM, 0 );
+ require_action( dnssd_SocketValid(self->tlssd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) );
+
+#if defined(SO_REUSEADDR)
+ err = setsockopt(self->tlssd, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn));
+ require_action( !err, exit, LogErr( "SetupSockets", "SO_REUSEADDR self->tlssd" ) );
+#endif
+
+ err = bind( self->tlssd, ( struct sockaddr* ) &daddr, sizeof( daddr ) );
+ require_action( !err, exit, LogErr( "SetupSockets", "bind self->tlssd" ) );
+
+ err = listen( self->tlssd, LISTENQ );
+ require_action( !err, exit, LogErr( "SetupSockets", "listen" ) );
+
+ // Do we have any private zones?
+
+ for ( zone = self->zones; zone; zone = zone->next )
+ {
+ if ( zone->type == kDNSZonePrivate )
+ {
+ private = mDNStrue;
+ break;
+ }
+ }
+
+ if ( private )
+ {
+ err = mDNSPlatformTLSSetupCerts();
+ require_action( !err, exit, LogErr( "SetupSockets", "mDNSPlatformTLSSetupCerts" ) );
+ }
+
+exit:
+
+ return err;
+}
+
+//
+// periodic table updates
+//
+
+// Delete a resource record from the nameserver via a dynamic update
+// sd is a socket already connected to the server
+mDNSlocal void DeleteOneRecord(DaemonInfo *d, CacheRecord *rr, domainname *zname, TCPSocket *sock)
+{
+ DNSZone * zone;
+ PktMsg pkt;
+ mDNSu8 *ptr = pkt.msg.data;
+ mDNSu8 *end = (mDNSu8 *)&pkt.msg + sizeof(DNSMessage);
+ char buf[MaxMsg];
+ mDNSBool closed;
+ PktMsg *reply = NULL;
+
+ VLog("Expiring record %s", GetRRDisplayString_rdb(&rr->resrec, &rr->resrec.rdata->u, buf));
+
+ InitializeDNSMessage(&pkt.msg.h, zeroID, UpdateReqFlags);
+
+ ptr = putZone(&pkt.msg, ptr, end, zname, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
+ if (!ptr) goto end;
+ ptr = putDeletionRecord(&pkt.msg, ptr, &rr->resrec);
+ if (!ptr) goto end;
+
+ HdrHToN(&pkt);
+
+ zone = FindZone( d, zname );
+
+ if ( zone && zone->updateKeys)
+ {
+ DNSDigest_SignMessage(&pkt.msg, &ptr, zone->updateKeys, 0 );
+ if (!ptr) goto end;
+ }
+
+ pkt.len = ptr - (mDNSu8 *)&pkt.msg;
+ pkt.src.sin_addr.s_addr = zerov4Addr.NotAnInteger; // address field set solely for verbose logging in subroutines
+ pkt.src.sin_family = AF_INET;
+ if (SendPacket( sock, &pkt)) { Log("DeleteOneRecord: SendPacket failed"); }
+ reply = RecvPacket( sock, NULL, &closed );
+ if (reply) HdrNToH(reply);
+ require_action( reply, end, Log( "DeleteOneRecord: RecvPacket returned NULL" ) );
+
+ if (!SuccessfulUpdateTransaction(&pkt, reply))
+ Log("Expiration update failed with rcode %d", reply ? reply->msg.h.flags.b[1] & kDNSFlag1_RC_Mask : -1);
+
+end:
+ if (!ptr) { Log("DeleteOneRecord: Error constructing lease expiration update"); }
+ if (reply) free(reply);
+}
+
+// iterate over table, deleting expired records (or all records if DeleteAll is true)
+mDNSlocal void DeleteRecords(DaemonInfo *d, mDNSBool DeleteAll)
+{
+ struct timeval now;
+ int i;
+ TCPSocket *sock = ConnectToServer(d);
+ if (!sock) { Log("DeleteRecords: ConnectToServer failed"); return; }
+ if (gettimeofday(&now, NULL)) { LogErr("DeleteRecords ", "gettimeofday"); return; }
+ if (pthread_mutex_lock(&d->tablelock)) { LogErr("DeleteRecords", "pthread_mutex_lock"); return; }
+
+ for (i = 0; i < d->nbuckets; i++)
+ {
+ RRTableElem **ptr = &d->table[i];
+ while (*ptr)
+ {
+ if (DeleteAll || (*ptr)->expire - now.tv_sec < 0)
+ {
+ RRTableElem *fptr;
+ // delete record from server
+ DeleteOneRecord(d, &(*ptr)->rr, &(*ptr)->zone, sock);
+ fptr = *ptr;
+ *ptr = (*ptr)->next;
+ free(fptr);
+ d->nelems--;
+ }
+ else ptr = &(*ptr)->next;
+ }
+ }
+ pthread_mutex_unlock(&d->tablelock);
+ mDNSPlatformTCPCloseConnection( sock );
+}
+
+//
+// main update request handling
+//
+
+// Add, delete, or refresh records in table based on contents of a successfully completed dynamic update
+mDNSlocal void UpdateLeaseTable(PktMsg *pkt, DaemonInfo *d, mDNSs32 lease)
+{
+ RRTableElem **rptr, *tmp;
+ int i, allocsize, bucket;
+ LargeCacheRecord lcr;
+ ResourceRecord *rr = &lcr.r.resrec;
+ const mDNSu8 *ptr, *end;
+ struct timeval tv;
+ DNSQuestion zone;
+ char buf[MaxMsg];
+
+ if (pthread_mutex_lock(&d->tablelock)) { LogErr("UpdateLeaseTable", "pthread_mutex_lock"); return; }
+ HdrNToH(pkt);
+ ptr = pkt->msg.data;
+ end = (mDNSu8 *)&pkt->msg + pkt->len;
+ ptr = getQuestion(&pkt->msg, ptr, end, 0, &zone);
+ if (!ptr) { Log("UpdateLeaseTable: cannot read zone"); goto cleanup; }
+ ptr = LocateAuthorities(&pkt->msg, end);
+ if (!ptr) { Log("UpdateLeaseTable: Format error"); goto cleanup; }
+
+ for (i = 0; i < pkt->msg.h.mDNS_numUpdates; i++)
+ {
+ mDNSBool DeleteAllRRSets = mDNSfalse, DeleteOneRRSet = mDNSfalse, DeleteOneRR = mDNSfalse;
+
+ ptr = GetLargeResourceRecord(NULL, &pkt->msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr);
+ if (!ptr || lcr.r.resrec.RecordType == kDNSRecordTypePacketNegative) { Log("UpdateLeaseTable: GetLargeResourceRecord failed"); goto cleanup; }
+ bucket = rr->namehash % d->nbuckets;
+ rptr = &d->table[bucket];
+
+ // handle deletions
+ if (rr->rrtype == kDNSQType_ANY && !rr->rroriginalttl && rr->rrclass == kDNSQClass_ANY && !rr->rdlength)
+ DeleteAllRRSets = mDNStrue; // delete all rrsets for a name
+ else if (!rr->rroriginalttl && rr->rrclass == kDNSQClass_ANY && !rr->rdlength)
+ DeleteOneRRSet = mDNStrue;
+ else if (!rr->rroriginalttl && rr->rrclass == kDNSClass_NONE)
+ DeleteOneRR = mDNStrue;
+
+ if (DeleteAllRRSets || DeleteOneRRSet || DeleteOneRR)
+ {
+ while (*rptr)
+ {
+ if (SameDomainName((*rptr)->rr.resrec.name, rr->name) &&
+ (DeleteAllRRSets ||
+ (DeleteOneRRSet && (*rptr)->rr.resrec.rrtype == rr->rrtype) ||
+ (DeleteOneRR && IdenticalResourceRecord(&(*rptr)->rr.resrec, rr))))
+ {
+ tmp = *rptr;
+ VLog("Received deletion update for %s", GetRRDisplayString_rdb(&tmp->rr.resrec, &tmp->rr.resrec.rdata->u, buf));
+ *rptr = (*rptr)->next;
+ free(tmp);
+ d->nelems--;
+ }
+ else rptr = &(*rptr)->next;
+ }
+ }
+ else if (lease > 0)
+ {
+ // see if add or refresh
+ while (*rptr && !IdenticalResourceRecord(&(*rptr)->rr.resrec, rr)) rptr = &(*rptr)->next;
+ if (*rptr)
+ {
+ // refresh
+ if (gettimeofday(&tv, NULL)) { LogErr("UpdateLeaseTable", "gettimeofday"); goto cleanup; }
+ (*rptr)->expire = tv.tv_sec + (unsigned)lease;
+ VLog("Refreshing lease for %s", GetRRDisplayString_rdb(&lcr.r.resrec, &lcr.r.resrec.rdata->u, buf));
+ }
+ else
+ {
+ // New record - add to table
+ if (d->nelems > d->nbuckets)
+ {
+ RehashTable(d);
+ bucket = rr->namehash % d->nbuckets;
+ rptr = &d->table[bucket];
+ }
+ if (gettimeofday(&tv, NULL)) { LogErr("UpdateLeaseTable", "gettimeofday"); goto cleanup; }
+ allocsize = sizeof(RRTableElem);
+ if (rr->rdlength > InlineCacheRDSize) allocsize += (rr->rdlength - InlineCacheRDSize);
+ tmp = malloc(allocsize);
+ if (!tmp) { LogErr("UpdateLeaseTable", "malloc"); goto cleanup; }
+ memcpy(&tmp->rr, &lcr.r, sizeof(CacheRecord) + rr->rdlength - InlineCacheRDSize);
+ tmp->rr.resrec.rdata = (RData *)&tmp->rr.smallrdatastorage;
+ AssignDomainName(&tmp->name, rr->name);
+ tmp->rr.resrec.name = &tmp->name;
+ tmp->expire = tv.tv_sec + (unsigned)lease;
+ tmp->cli.sin_addr = pkt->src.sin_addr;
+ AssignDomainName(&tmp->zone, &zone.qname);
+ tmp->next = d->table[bucket];
+ d->table[bucket] = tmp;
+ d->nelems++;
+ VLog("Adding update for %s to lease table", GetRRDisplayString_rdb(&lcr.r.resrec, &lcr.r.resrec.rdata->u, buf));
+ }
+ }
+ }
+
+cleanup:
+ pthread_mutex_unlock(&d->tablelock);
+ HdrHToN(pkt);
+}
+
+// Given a successful reply from a server, create a new reply that contains lease information
+// Replies are currently not signed !!!KRS change this
+mDNSlocal PktMsg *FormatLeaseReply(DaemonInfo *d, PktMsg *orig, mDNSu32 lease)
+{
+ PktMsg *reply;
+ mDNSu8 *ptr, *end;
+ mDNSOpaque16 flags;
+
+ (void)d; //unused
+ reply = malloc(sizeof(*reply));
+ if (!reply) { LogErr("FormatLeaseReply", "malloc"); return NULL; }
+ flags.b[0] = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update;
+ flags.b[1] = 0;
+
+ InitializeDNSMessage(&reply->msg.h, orig->msg.h.id, flags);
+ reply->src.sin_addr.s_addr = zerov4Addr.NotAnInteger; // unused except for log messages
+ reply->src.sin_family = AF_INET;
+ ptr = reply->msg.data;
+ end = (mDNSu8 *)&reply->msg + sizeof(DNSMessage);
+ ptr = putUpdateLease(&reply->msg, ptr, lease);
+ if (!ptr) { Log("FormatLeaseReply: putUpdateLease failed"); free(reply); return NULL; }
+ reply->len = ptr - (mDNSu8 *)&reply->msg;
+ HdrHToN(reply);
+ return reply;
+}
+
+
+// pkt is thread-local, not requiring locking
+
+mDNSlocal PktMsg*
+HandleRequest
+(
+ DaemonInfo * self,
+ PktMsg * request
+)
+{
+ PktMsg * reply = NULL;
+ PktMsg * leaseReply;
+ PktMsg buf;
+ char addrbuf[32];
+ TCPSocket * sock = NULL;
+ mStatus err;
+ mDNSs32 lease = 0;
+ if ((request->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) == kDNSFlag0_OP_Update)
+ {
+ int i, adds = 0, dels = 0;
+ const mDNSu8 *ptr, *end = (mDNSu8 *)&request->msg + request->len;
+ HdrNToH(request);
+ lease = GetPktLease(&mDNSStorage, &request->msg, end);
+ ptr = LocateAuthorities(&request->msg, end);
+ for (i = 0; i < request->msg.h.mDNS_numUpdates; i++)
+ {
+ LargeCacheRecord lcr;
+ ptr = GetLargeResourceRecord(NULL, &request->msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr);
+ if (lcr.r.resrec.RecordType != kDNSRecordTypePacketNegative && lcr.r.resrec.rroriginalttl) adds++;else dels++;
+ }
+ HdrHToN(request);
+ if (adds && !lease)
+ {
+ static const mDNSOpaque16 UpdateRefused = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update, kDNSFlag1_RC_Refused } };
+ Log("Rejecting Update Request with %d additions but no lease", adds);
+ reply = malloc(sizeof(*reply));
+ mDNSPlatformMemZero(&reply->src, sizeof(reply->src));
+ reply->len = sizeof(DNSMessageHeader);
+ reply->zone = NULL;
+ reply->isZonePublic = 0;
+ InitializeDNSMessage(&reply->msg.h, request->msg.h.id, UpdateRefused);
+ return(reply);
+ }
+ if (lease > 7200) // Don't allow lease greater than two hours; typically 90-minute renewal period
+ lease = 7200;
+ }
+ // Send msg to server, read reply
+
+ if ( request->len <= 512 )
+ {
+ mDNSBool trunc;
+
+ if ( UDPServerTransaction( self, request, &buf, &trunc) < 0 )
+ {
+ Log("HandleRequest - UDPServerTransaction failed. Trying TCP");
+ }
+ else if ( trunc )
+ {
+ VLog("HandleRequest - answer truncated. Using TCP");
+ }
+ else
+ {
+ reply = &buf; // success
+ }
+ }
+
+ if ( !reply )
+ {
+ mDNSBool closed;
+ int res;
+
+ sock = ConnectToServer( self );
+ require_action_quiet( sock, exit, err = mStatus_UnknownErr ; Log( "Discarding request from %s due to connection errors", inet_ntop( AF_INET, &request->src.sin_addr, addrbuf, 32 ) ) );
+
+ res = SendPacket( sock, request );
+ require_action_quiet( res >= 0, exit, err = mStatus_UnknownErr ; Log( "Couldn't relay message from %s to server. Discarding.", inet_ntop(AF_INET, &request->src.sin_addr, addrbuf, 32 ) ) );
+
+ reply = RecvPacket( sock, &buf, &closed );
+ }
+
+ // IMPORTANT: reply is in network byte order at this point in the code
+ // We keep it this way because we send it back to the client in the same form
+
+ // Is it an update?
+
+ if ( reply && ( ( reply->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ) == ( kDNSFlag0_OP_Update | kDNSFlag0_QR_Response ) ) )
+ {
+ char pingmsg[4];
+ mDNSBool ok = SuccessfulUpdateTransaction( request, reply );
+ require_action( ok, exit, err = mStatus_UnknownErr; VLog( "Message from %s not a successful update.", inet_ntop(AF_INET, &request->src.sin_addr, addrbuf, 32 ) ) );
+
+ UpdateLeaseTable( request, self, lease );
+
+ if ( lease > 0 )
+ {
+ leaseReply = FormatLeaseReply( self, reply, lease );
+
+ if ( !leaseReply )
+ {
+ Log("HandleRequest - unable to format lease reply");
+ }
+
+ // %%% Looks like a potential memory leak -- who frees the original reply?
+ reply = leaseReply;
+ }
+
+ // tell the main thread there was an update so it can send LLQs
+
+ if ( send( self->LLQEventNotifySock, pingmsg, sizeof( pingmsg ), 0 ) != sizeof( pingmsg ) )
+ {
+ LogErr("HandleRequest", "send");
+ }
+ }
+
+exit:
+
+ if ( sock )
+ {
+ mDNSPlatformTCPCloseConnection( sock );
+ }
+
+ if ( reply == &buf )
+ {
+ reply = malloc( sizeof( *reply ) );
+
+ if ( reply )
+ {
+ reply->len = buf.len;
+ memcpy(&reply->msg, &buf.msg, buf.len);
+ }
+ else
+ {
+ LogErr("HandleRequest", "malloc");
+ }
+ }
+
+ return reply;
+}
+
+
+//
+// LLQ Support Routines
+//
+
+// Set fields of an LLQ OPT Resource Record
+mDNSlocal void FormatLLQOpt(AuthRecord *opt, int opcode, const mDNSOpaque64 *const id, mDNSs32 lease)
+{
+ mDNSPlatformMemZero(opt, sizeof(*opt));
+ mDNS_SetupResourceRecord(opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
+ opt->resrec.rrclass = NormalMaxDNSMessageData;
+ opt->resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record
+ opt->resrec.rdestimate = sizeof(rdataOPT);
+ opt->resrec.rdata->u.opt[0].opt = kDNSOpt_LLQ;
+ opt->resrec.rdata->u.opt[0].u.llq.vers = kLLQ_Vers;
+ opt->resrec.rdata->u.opt[0].u.llq.llqOp = opcode;
+ opt->resrec.rdata->u.opt[0].u.llq.err = LLQErr_NoError;
+ opt->resrec.rdata->u.opt[0].u.llq.id = *id;
+ opt->resrec.rdata->u.opt[0].u.llq.llqlease = lease;
+}
+
+// Calculate effective remaining lease of an LLQ
+mDNSlocal mDNSu32 LLQLease(LLQEntry *e)
+{
+ struct timeval t;
+
+ gettimeofday(&t, NULL);
+ if (e->expire < t.tv_sec) return 0;
+ else return e->expire - t.tv_sec;
+}
+
+mDNSlocal void DeleteLLQ(DaemonInfo *d, LLQEntry *e)
+{
+ int bucket = DomainNameHashValue(&e->qname) % LLQ_TABLESIZE;
+ LLQEntry **ptr = &d->LLQTable[bucket];
+ AnswerListElem *a = e->AnswerList;
+ char addr[32];
+
+ inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32);
+ VLog("Deleting LLQ table entry for %##s client %s", e->qname.c, addr);
+
+ if (a && !(--a->refcount) && d->AnswerTableCount >= LLQ_TABLESIZE)
+ {
+ // currently, generating initial answers blocks the main thread, so we keep the answer list
+ // even if the ref count drops to zero. To prevent unbounded table growth, we free shared answers
+ // if the ref count drops to zero AND there are more table elements than buckets
+ // !!!KRS update this when we make the table dynamically growable
+
+ CacheRecord *cr = a->KnownAnswers, *tmp;
+ AnswerListElem **tbl = &d->AnswerTable[bucket];
+
+ while (cr)
+ {
+ tmp = cr;
+ cr = cr->next;
+ free(tmp);
+ }
+
+ while (*tbl && *tbl != a) tbl = &(*tbl)->next;
+ if (*tbl) { *tbl = (*tbl)->next; free(a); d->AnswerTableCount--; }
+ else Log("Error: DeleteLLQ - AnswerList not found in table");
+ }
+
+ // remove LLQ from table, free memory
+ while(*ptr && *ptr != e) ptr = &(*ptr)->next;
+ if (!*ptr) { Log("Error: DeleteLLQ - LLQ not in table"); return; }
+ *ptr = (*ptr)->next;
+ free(e);
+}
+
+mDNSlocal int SendLLQ(DaemonInfo *d, PktMsg *pkt, struct sockaddr_in dst, TCPSocket *sock)
+{
+ char addr[32];
+ int err = -1;
+
+ HdrHToN(pkt);
+
+ if ( sock )
+ {
+ if ( SendPacket( sock, pkt ) != 0 )
+ {
+ LogErr("DaemonInfo", "MySend");
+ Log("Could not send response to client %s", inet_ntop(AF_INET, &dst.sin_addr, addr, 32));
+ }
+ }
+ else
+ {
+ if (sendto(d->llq_udpsd, &pkt->msg, pkt->len, 0, (struct sockaddr *)&dst, sizeof(dst)) != (int)pkt->len)
+ {
+ LogErr("DaemonInfo", "sendto");
+ Log("Could not send response to client %s", inet_ntop(AF_INET, &dst.sin_addr, addr, 32));
+ }
+ }
+
+ err = 0;
+ HdrNToH(pkt);
+ return err;
+}
+
+mDNSlocal CacheRecord *AnswerQuestion(DaemonInfo *d, AnswerListElem *e)
+{
+ PktMsg q;
+ int i;
+ TCPSocket *sock = NULL;
+ const mDNSu8 *ansptr;
+ mDNSu8 *end = q.msg.data;
+ PktMsg buf, *reply = NULL;
+ LargeCacheRecord lcr;
+ CacheRecord *AnswerList = NULL;
+ mDNSu8 rcode;
+
+ VLog("Querying server for %##s type %d", e->name.c, e->type);
+
+ InitializeDNSMessage(&q.msg.h, zeroID, uQueryFlags);
+
+ end = putQuestion(&q.msg, end, end + AbsoluteMaxDNSMessageData, &e->name, e->type, kDNSClass_IN);
+ if (!end) { Log("Error: AnswerQuestion - putQuestion returned NULL"); goto end; }
+ q.len = (int)(end - (mDNSu8 *)&q.msg);
+
+ HdrHToN(&q);
+
+ if (!e->UseTCP)
+ {
+ mDNSBool trunc;
+
+ if (UDPServerTransaction(d, &q, &buf, &trunc) < 0)
+ Log("AnswerQuestion %##s - UDPServerTransaction failed. Trying TCP", e->name.c);
+ else if (trunc)
+ { VLog("AnswerQuestion %##s - answer truncated. Using TCP", e->name.c); e->UseTCP = mDNStrue; }
+ else reply = &buf; // success
+ }
+
+ if (!reply)
+ {
+ mDNSBool closed;
+
+ sock = ConnectToServer(d);
+ if (!sock) { Log("AnswerQuestion: ConnectToServer failed"); goto end; }
+ if (SendPacket( sock, &q)) { Log("AnswerQuestion: SendPacket failed"); mDNSPlatformTCPCloseConnection( sock ); goto end; }
+ reply = RecvPacket( sock, NULL, &closed );
+ mDNSPlatformTCPCloseConnection( sock );
+ require_action( reply, end, Log( "AnswerQuestion: RecvPacket returned NULL" ) );
+ }
+
+ HdrNToH(&q);
+ if (reply) HdrNToH(reply);
+
+ if ((reply->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) != (kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery))
+ { Log("AnswerQuestion: %##s type %d - Invalid response flags from server"); goto end; }
+ rcode = (mDNSu8)(reply->msg.h.flags.b[1] & kDNSFlag1_RC_Mask);
+ if (rcode && rcode != kDNSFlag1_RC_NXDomain) { Log("AnswerQuestion: %##s type %d - non-zero rcode %d from server", e->name.c, e->type, rcode); goto end; }
+
+ end = (mDNSu8 *)&reply->msg + reply->len;
+ ansptr = LocateAnswers(&reply->msg, end);
+ if (!ansptr) { Log("Error: AnswerQuestion - LocateAnswers returned NULL"); goto end; }
+
+ for (i = 0; i < reply->msg.h.numAnswers; i++)
+ {
+ ansptr = GetLargeResourceRecord(NULL, &reply->msg, ansptr, end, 0, kDNSRecordTypePacketAns, &lcr);
+ if (!ansptr) { Log("AnswerQuestions: GetLargeResourceRecord returned NULL"); goto end; }
+ if (lcr.r.resrec.RecordType != kDNSRecordTypePacketNegative)
+ {
+ if (lcr.r.resrec.rrtype != e->type || lcr.r.resrec.rrclass != kDNSClass_IN || !SameDomainName(lcr.r.resrec.name, &e->name))
+ {
+ Log("AnswerQuestion: response %##s type #d does not answer question %##s type #d. Discarding",
+ lcr.r.resrec.name->c, lcr.r.resrec.rrtype, e->name.c, e->type);
+ }
+ else
+ {
+ CacheRecord *cr = CopyCacheRecord(&lcr.r, &e->name);
+ if (!cr) { Log("Error: AnswerQuestion - CopyCacheRecord returned NULL"); goto end; }
+ cr->next = AnswerList;
+ AnswerList = cr;
+ }
+ }
+ }
+
+end:
+ if (reply && reply != &buf) free(reply);
+ return AnswerList;
+}
+
+// Routine forks a thread to set EventList to contain Add/Remove events, and deletes any removes from the KnownAnswer list
+mDNSlocal void *UpdateAnswerList(void *args)
+{
+ CacheRecord *cr, *NewAnswers, **na, **ka; // "new answer", "known answer"
+ DaemonInfo *d = ((UpdateAnswerListArgs *)args)->d;
+ AnswerListElem *a = ((UpdateAnswerListArgs *)args)->a;
+
+ free(args);
+ args = NULL;
+
+ // get up to date answers
+ NewAnswers = AnswerQuestion(d, a);
+
+ // first pass - mark all answers for deletion
+ for (ka = &a->KnownAnswers; *ka; ka = &(*ka)->next)
+ (*ka)->resrec.rroriginalttl = (unsigned)-1; // -1 means delete
+
+ // second pass - mark answers pre-existent
+ for (ka = &a->KnownAnswers; *ka; ka = &(*ka)->next)
+ {
+ for (na = &NewAnswers; *na; na = &(*na)->next)
+ {
+ if (IdenticalResourceRecord(&(*ka)->resrec, &(*na)->resrec))
+ { (*ka)->resrec.rroriginalttl = 0; break; } // 0 means no change
+ }
+ }
+
+ // third pass - add new records to Event list
+ na = &NewAnswers;
+ while (*na)
+ {
+ for (ka = &a->KnownAnswers; *ka; ka = &(*ka)->next)
+ if (IdenticalResourceRecord(&(*ka)->resrec, &(*na)->resrec)) break;
+ if (!*ka)
+ {
+ // answer is not in list - splice from NewAnswers list, add to Event list
+ cr = *na;
+ *na = (*na)->next; // splice from list
+ cr->next = a->EventList; // add spliced record to event list
+ a->EventList = cr;
+ cr->resrec.rroriginalttl = 1; // 1 means add
+ }
+ else na = &(*na)->next;
+ }
+
+ // move all the removes from the answer list to the event list
+ ka = &a->KnownAnswers;
+ while (*ka)
+ {
+ if ((*ka)->resrec.rroriginalttl == (unsigned)-1)
+ {
+ cr = *ka;
+ *ka = (*ka)->next;
+ cr->next = a->EventList;
+ a->EventList = cr;
+ }
+ else ka = &(*ka)->next;
+ }
+
+ // lastly, free the remaining records (known answers) in NewAnswers list
+ while (NewAnswers)
+ {
+ cr = NewAnswers;
+ NewAnswers = NewAnswers->next;
+ free(cr);
+ }
+
+ return NULL;
+}
+
+mDNSlocal void SendEvents(DaemonInfo *d, LLQEntry *e)
+{
+ PktMsg response;
+ CacheRecord *cr;
+ mDNSu8 *end = (mDNSu8 *)&response.msg.data;
+ mDNSOpaque16 msgID;
+ char rrbuf[MaxMsg], addrbuf[32];
+ AuthRecord opt;
+
+ // Should this really be random? Do we use the msgID on the receiving end?
+ msgID.NotAnInteger = random();
+ if (verbose) inet_ntop(AF_INET, &e->cli.sin_addr, addrbuf, 32);
+ InitializeDNSMessage(&response.msg.h, msgID, ResponseFlags);
+ end = putQuestion(&response.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN);
+ if (!end) { Log("Error: SendEvents - putQuestion returned NULL"); return; }
+
+ // put adds/removes in packet
+ for (cr = e->AnswerList->EventList; cr; cr = cr->next)
+ {
+ if (verbose) GetRRDisplayString_rdb(&cr->resrec, &cr->resrec.rdata->u, rrbuf);
+ VLog("%s (%s): %s", addrbuf, (mDNSs32)cr->resrec.rroriginalttl < 0 ? "Remove" : "Add", rrbuf);
+ end = PutResourceRecordTTLJumbo(&response.msg, end, &response.msg.h.numAnswers, &cr->resrec, cr->resrec.rroriginalttl);
+ if (!end) { Log("Error: SendEvents - PutResourceRecordTTLJumbo returned NULL"); return; }
+ }
+
+ FormatLLQOpt(&opt, kLLQOp_Event, &e->id, LLQLease(e));
+ end = PutResourceRecordTTLJumbo(&response.msg, end, &response.msg.h.numAdditionals, &opt.resrec, 0);
+ if (!end) { Log("Error: SendEvents - PutResourceRecordTTLJumbo"); return; }
+
+ response.len = (int)(end - (mDNSu8 *)&response.msg);
+ if (SendLLQ(d, &response, e->cli, NULL ) < 0) LogMsg("Error: SendEvents - SendLLQ");
+}
+
+mDNSlocal void PrintLLQAnswers(DaemonInfo *d)
+{
+ int i;
+ char rrbuf[MaxMsg];
+
+ Log("Printing LLQ Answer Table contents");
+
+ for (i = 0; i < LLQ_TABLESIZE; i++)
+ {
+ AnswerListElem *a = d->AnswerTable[i];
+ while(a)
+ {
+ int ancount = 0;
+ const CacheRecord *rr = a->KnownAnswers;
+ while (rr) { ancount++; rr = rr->next; }
+ Log("%p : Question %##s; type %d; referenced by %d LLQs; %d answers:", a, a->name.c, a->type, a->refcount, ancount);
+ for (rr = a->KnownAnswers; rr; rr = rr->next) Log("\t%s", GetRRDisplayString_rdb(&rr->resrec, &rr->resrec.rdata->u, rrbuf));
+ a = a->next;
+ }
+ }
+}
+
+mDNSlocal void PrintLLQTable(DaemonInfo *d)
+{
+ LLQEntry *e;
+ char addr[32];
+ int i;
+
+ Log("Printing LLQ table contents");
+
+ for (i = 0; i < LLQ_TABLESIZE; i++)
+ {
+ e = d->LLQTable[i];
+ while(e)
+ {
+ char *state;
+
+ switch (e->state)
+ {
+ case RequestReceived: state = "RequestReceived"; break;
+ case ChallengeSent: state = "ChallengeSent"; break;
+ case Established: state = "Established"; break;
+ default: state = "unknown";
+ }
+ inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32);
+
+ Log("LLQ from %s in state %s; %##s; type %d; orig lease %d; remaining lease %d; AnswerList %p)",
+ addr, state, e->qname.c, e->qtype, e->lease, LLQLease(e), e->AnswerList);
+ e = e->next;
+ }
+ }
+}
+
+// Send events to clients as a result of a change in the zone
+mDNSlocal void GenLLQEvents(DaemonInfo *d)
+{
+ LLQEntry **e;
+ int i;
+ struct timeval t;
+ UpdateAnswerListArgs *args;
+
+ VLog("Generating LLQ Events");
+
+ gettimeofday(&t, NULL);
+
+ // get all answers up to date
+ for (i = 0; i < LLQ_TABLESIZE; i++)
+ {
+ AnswerListElem *a = d->AnswerTable[i];
+ while(a)
+ {
+ args = malloc(sizeof(*args));
+ if (!args) { LogErr("GenLLQEvents", "malloc"); return; }
+ args->d = d;
+ args->a = a;
+ if (pthread_create(&a->tid, NULL, UpdateAnswerList, args) < 0) { LogErr("GenLLQEvents", "pthread_create"); return; }
+ usleep(1);
+ a = a->next;
+ }
+ }
+
+ for (i = 0; i < LLQ_TABLESIZE; i++)
+ {
+ AnswerListElem *a = d->AnswerTable[i];
+ while(a)
+ {
+ if (pthread_join(a->tid, NULL)) LogErr("GenLLQEvents", "pthread_join");
+ a = a->next;
+ }
+ }
+
+ // for each established LLQ, send events
+ for (i = 0; i < LLQ_TABLESIZE; i++)
+ {
+ e = &d->LLQTable[i];
+ while(*e)
+ {
+ if ((*e)->expire < t.tv_sec) DeleteLLQ(d, *e);
+ else
+ {
+ if ((*e)->state == Established && (*e)->AnswerList->EventList) SendEvents(d, *e);
+ e = &(*e)->next;
+ }
+ }
+ }
+
+ // now that all LLQs are updated, we move Add events from the Event list to the Known Answer list, and free Removes
+ for (i = 0; i < LLQ_TABLESIZE; i++)
+ {
+ AnswerListElem *a = d->AnswerTable[i];
+ while(a)
+ {
+ if (a->EventList)
+ {
+ CacheRecord *cr = a->EventList, *tmp;
+ while (cr)
+ {
+ tmp = cr;
+ cr = cr->next;
+ if ((signed)tmp->resrec.rroriginalttl < 0) free(tmp);
+ else
+ {
+ tmp->next = a->KnownAnswers;
+ a->KnownAnswers = tmp;
+ tmp->resrec.rroriginalttl = 0;
+ }
+ }
+ a->EventList = NULL;
+ }
+ a = a->next;
+ }
+ }
+}
+
+mDNSlocal void SetAnswerList(DaemonInfo *d, LLQEntry *e)
+{
+ int bucket = DomainNameHashValue(&e->qname) % LLQ_TABLESIZE;
+ AnswerListElem *a = d->AnswerTable[bucket];
+ while (a && (a->type != e->qtype ||!SameDomainName(&a->name, &e->qname))) a = a->next;
+ if (!a)
+ {
+ a = malloc(sizeof(*a));
+ if (!a) { LogErr("SetAnswerList", "malloc"); return; }
+ AssignDomainName(&a->name, &e->qname);
+ a->type = e->qtype;
+ a->refcount = 0;
+ a->EventList = NULL;
+ a->UseTCP = mDNSfalse;
+ a->next = d->AnswerTable[bucket];
+ d->AnswerTable[bucket] = a;
+ d->AnswerTableCount++;
+ a->KnownAnswers = AnswerQuestion(d, a);
+ }
+
+ e->AnswerList = a;
+ a->refcount++;
+}
+
+// Allocate LLQ entry, insert into table
+mDNSlocal LLQEntry *NewLLQ(DaemonInfo *d, struct sockaddr_in cli, domainname *qname, mDNSu16 qtype, mDNSu32 lease )
+{
+ char addr[32];
+ struct timeval t;
+ int bucket = DomainNameHashValue(qname) % LLQ_TABLESIZE;
+ LLQEntry *e;
+
+ e = malloc(sizeof(*e));
+ if (!e) { LogErr("NewLLQ", "malloc"); return NULL; }
+
+ inet_ntop(AF_INET, &cli.sin_addr, addr, 32);
+ VLog("Allocating LLQ entry for client %s question %##s type %d", addr, qname->c, qtype);
+
+ // initialize structure
+ e->cli = cli;
+ AssignDomainName(&e->qname, qname);
+ e->qtype = qtype;
+ e->id = zeroOpaque64;
+ e->state = RequestReceived;
+ e->AnswerList = NULL;
+
+ if (lease < LLQ_MIN_LEASE) lease = LLQ_MIN_LEASE;
+ else if (lease > LLQ_MAX_LEASE) lease = LLQ_MAX_LEASE;
+
+ gettimeofday(&t, NULL);
+ e->expire = t.tv_sec + (int)lease;
+ e->lease = lease;
+
+ // add to table
+ e->next = d->LLQTable[bucket];
+ d->LLQTable[bucket] = e;
+
+ return e;
+}
+
+// Handle a refresh request from client
+mDNSlocal void LLQRefresh(DaemonInfo *d, LLQEntry *e, LLQOptData *llq, mDNSOpaque16 msgID, TCPSocket *sock )
+{
+ AuthRecord opt;
+ PktMsg ack;
+ mDNSu8 *end = (mDNSu8 *)&ack.msg.data;
+ char addr[32];
+
+ inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32);
+ VLog("%s LLQ for %##s from %s", llq->llqlease ? "Refreshing" : "Deleting", e->qname.c, addr);
+
+ if (llq->llqlease)
+ {
+ struct timeval t;
+ if (llq->llqlease < LLQ_MIN_LEASE) llq->llqlease = LLQ_MIN_LEASE;
+ else if (llq->llqlease > LLQ_MAX_LEASE) llq->llqlease = LLQ_MIN_LEASE;
+ gettimeofday(&t, NULL);
+ e->expire = t.tv_sec + llq->llqlease;
+ }
+
+ ack.src.sin_addr.s_addr = 0; // unused
+ InitializeDNSMessage(&ack.msg.h, msgID, ResponseFlags);
+ end = putQuestion(&ack.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN);
+ if (!end) { Log("Error: putQuestion"); return; }
+
+ FormatLLQOpt(&opt, kLLQOp_Refresh, &e->id, llq->llqlease ? LLQLease(e) : 0);
+ end = PutResourceRecordTTLJumbo(&ack.msg, end, &ack.msg.h.numAdditionals, &opt.resrec, 0);
+ if (!end) { Log("Error: PutResourceRecordTTLJumbo"); return; }
+
+ ack.len = (int)(end - (mDNSu8 *)&ack.msg);
+ if (SendLLQ(d, &ack, e->cli, sock)) Log("Error: LLQRefresh");
+
+ if (llq->llqlease) e->state = Established;
+ else DeleteLLQ(d, e);
+}
+
+// Complete handshake with Ack an initial answers
+mDNSlocal void LLQCompleteHandshake(DaemonInfo *d, LLQEntry *e, LLQOptData *llq, mDNSOpaque16 msgID, TCPSocket *sock)
+{
+ char addr[32];
+ CacheRecord *ptr;
+ AuthRecord opt;
+ PktMsg ack;
+ mDNSu8 *end = (mDNSu8 *)&ack.msg.data;
+ char rrbuf[MaxMsg], addrbuf[32];
+
+ inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32);
+
+ if (!mDNSSameOpaque64(&llq->id, &e->id) ||
+ llq->vers != kLLQ_Vers ||
+ llq->llqOp != kLLQOp_Setup ||
+ llq->err != LLQErr_NoError ||
+ llq->llqlease > e->lease + LLQ_LEASE_FUDGE ||
+ llq->llqlease < e->lease - LLQ_LEASE_FUDGE)
+ {
+ Log("Incorrect challenge response from %s", addr);
+ return;
+ }
+
+ if (e->state == Established) VLog("Retransmitting LLQ ack + answers for %##s", e->qname.c);
+ else VLog("Delivering LLQ ack + answers for %##s", e->qname.c);
+
+ // format ack + answers
+ ack.src.sin_addr.s_addr = 0; // unused
+ InitializeDNSMessage(&ack.msg.h, msgID, ResponseFlags);
+ end = putQuestion(&ack.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN);
+ if (!end) { Log("Error: putQuestion"); return; }
+
+ if (e->state != Established) { SetAnswerList(d, e); e->state = Established; }
+
+ if (verbose) inet_ntop(AF_INET, &e->cli.sin_addr, addrbuf, 32);
+ for (ptr = e->AnswerList->KnownAnswers; ptr; ptr = ptr->next)
+ {
+ if (verbose) GetRRDisplayString_rdb(&ptr->resrec, &ptr->resrec.rdata->u, rrbuf);
+ VLog("%s Intitial Answer - %s", addr, rrbuf);
+ end = PutResourceRecordTTLJumbo(&ack.msg, end, &ack.msg.h.numAnswers, &ptr->resrec, 1);
+ if (!end) { Log("Error: PutResourceRecordTTLJumbo"); return; }
+ }
+
+ FormatLLQOpt(&opt, kLLQOp_Setup, &e->id, LLQLease(e));
+ end = PutResourceRecordTTLJumbo(&ack.msg, end, &ack.msg.h.numAdditionals, &opt.resrec, 0);
+ if (!end) { Log("Error: PutResourceRecordTTLJumbo"); return; }
+
+ ack.len = (int)(end - (mDNSu8 *)&ack.msg);
+ if (SendLLQ(d, &ack, e->cli, sock)) Log("Error: LLQCompleteHandshake");
+}
+
+mDNSlocal void LLQSetupChallenge(DaemonInfo *d, LLQEntry *e, LLQOptData *llq, mDNSOpaque16 msgID)
+{
+ struct timeval t;
+ PktMsg challenge;
+ mDNSu8 *end = challenge.msg.data;
+ AuthRecord opt;
+
+ if (e->state == ChallengeSent) VLog("Retransmitting LLQ setup challenge for %##s", e->qname.c);
+ else VLog("Sending LLQ setup challenge for %##s", e->qname.c);
+
+ if (!mDNSOpaque64IsZero(&llq->id)) { Log("Error: LLQSetupChallenge - nonzero ID"); return; } // server bug
+ if (llq->llqOp != kLLQOp_Setup) { Log("LLQSetupChallenge - incorrrect operation from client"); return; } // client error
+
+ if (mDNSOpaque64IsZero(&e->id)) // don't regenerate random ID for retransmissions
+ {
+ // construct ID <time><random>
+ gettimeofday(&t, NULL);
+ e->id.l[0] = t.tv_sec;
+ e->id.l[1] = random();
+ }
+
+ // format response (query + LLQ opt rr)
+ challenge.src.sin_addr.s_addr = 0; // unused
+ InitializeDNSMessage(&challenge.msg.h, msgID, ResponseFlags);
+ end = putQuestion(&challenge.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN);
+ if (!end) { Log("Error: putQuestion"); return; }
+ FormatLLQOpt(&opt, kLLQOp_Setup, &e->id, LLQLease(e));
+ end = PutResourceRecordTTLJumbo(&challenge.msg, end, &challenge.msg.h.numAdditionals, &opt.resrec, 0);
+ if (!end) { Log("Error: PutResourceRecordTTLJumbo"); return; }
+ challenge.len = (int)(end - (mDNSu8 *)&challenge.msg);
+ if (SendLLQ(d, &challenge, e->cli, NULL)) { Log("Error: LLQSetupChallenge"); return; }
+ e->state = ChallengeSent;
+}
+
+// Take action on an LLQ message from client. Entry must be initialized and in table
+mDNSlocal void UpdateLLQ(DaemonInfo *d, LLQEntry *e, LLQOptData *llq, mDNSOpaque16 msgID, TCPSocket *sock )
+{
+ switch(e->state)
+ {
+ case RequestReceived:
+ if ( sock )
+ {
+ struct timeval t;
+ gettimeofday(&t, NULL);
+ e->id.l[0] = t.tv_sec; // construct ID <time><random>
+ e->id.l[1] = random();
+ llq->id = e->id;
+ LLQCompleteHandshake( d, e, llq, msgID, sock );
+
+ // Set the state to established because we've just set the LLQ up using TCP
+ e->state = Established;
+ }
+ else
+ {
+ LLQSetupChallenge(d, e, llq, msgID);
+ }
+ return;
+ case ChallengeSent:
+ if (mDNSOpaque64IsZero(&llq->id)) LLQSetupChallenge(d, e, llq, msgID); // challenge sent and lost
+ else LLQCompleteHandshake(d, e, llq, msgID, sock );
+ return;
+ case Established:
+ if (mDNSOpaque64IsZero(&llq->id))
+ {
+ // client started over. reset state.
+ LLQEntry *newe = NewLLQ(d, e->cli, &e->qname, e->qtype, llq->llqlease );
+ if (!newe) return;
+ DeleteLLQ(d, e);
+ LLQSetupChallenge(d, newe, llq, msgID);
+ return;
+ }
+ else if (llq->llqOp == kLLQOp_Setup)
+ { LLQCompleteHandshake(d, e, llq, msgID, sock); return; } // Ack lost
+ else if (llq->llqOp == kLLQOp_Refresh)
+ { LLQRefresh(d, e, llq, msgID, sock); return; }
+ else { Log("Unhandled message for established LLQ"); return; }
+ }
+}
+
+mDNSlocal LLQEntry *LookupLLQ(DaemonInfo *d, struct sockaddr_in cli, domainname *qname, mDNSu16 qtype, const mDNSOpaque64 *const id)
+{
+ int bucket = bucket = DomainNameHashValue(qname) % LLQ_TABLESIZE;
+ LLQEntry *ptr = d->LLQTable[bucket];
+
+ while(ptr)
+ {
+ if (((ptr->state == ChallengeSent && mDNSOpaque64IsZero(id) && (cli.sin_port == ptr->cli.sin_port)) || // zero-id due to packet loss OK in state ChallengeSent
+ mDNSSameOpaque64(id, &ptr->id)) && // id match
+ (cli.sin_addr.s_addr == ptr->cli.sin_addr.s_addr) && (qtype == ptr->qtype) && SameDomainName(&ptr->qname, qname)) // same source, type, qname
+ return ptr;
+ ptr = ptr->next;
+ }
+ return NULL;
+}
+
+mDNSlocal int
+RecvNotify
+(
+ DaemonInfo * d,
+ PktMsg * pkt
+)
+{
+ int res;
+ int err = 0;
+
+ pkt->msg.h.flags.b[0] |= kDNSFlag0_QR_Response;
+
+ res = sendto( d->udpsd, &pkt->msg, pkt->len, 0, ( struct sockaddr* ) &pkt->src, sizeof( pkt->src ) );
+ require_action( res == ( int ) pkt->len, exit, err = mStatus_UnknownErr; LogErr( "RecvNotify", "sendto" ) );
+
+exit:
+
+ return err;
+}
+
+
+mDNSlocal int RecvLLQ( DaemonInfo *d, PktMsg *pkt, TCPSocket *sock )
+{
+ DNSQuestion q;
+ LargeCacheRecord opt;
+ int i, err = -1;
+ char addr[32];
+ const mDNSu8 *qptr = pkt->msg.data;
+ const mDNSu8 *end = (mDNSu8 *)&pkt->msg + pkt->len;
+ const mDNSu8 *aptr;
+ LLQOptData *llq = NULL;
+ LLQEntry *e = NULL;
+
+ HdrNToH(pkt);
+ aptr = LocateAdditionals(&pkt->msg, end); // Can't do this until after HdrNToH(pkt);
+ inet_ntop(AF_INET, &pkt->src.sin_addr, addr, 32);
+
+ VLog("Received LLQ msg from %s", addr);
+ // sanity-check packet
+ if (!pkt->msg.h.numQuestions || !pkt->msg.h.numAdditionals)
+ {
+ Log("Malformatted LLQ from %s with %d questions, %d additionals", addr, pkt->msg.h.numQuestions, pkt->msg.h.numAdditionals);
+ goto end;
+ }
+
+ // Locate the OPT record.
+ // According to RFC 2671, "One OPT pseudo-RR can be added to the additional data section of either a request or a response."
+ // This implies that there may be *at most* one OPT record per DNS message, in the Additional Section,
+ // but not necessarily the *last* entry in the Additional Section.
+ for (i = 0; i < pkt->msg.h.numAdditionals; i++)
+ {
+ aptr = GetLargeResourceRecord(NULL, &pkt->msg, aptr, end, 0, kDNSRecordTypePacketAdd, &opt);
+ if (!aptr) { Log("Malformatted LLQ from %s: could not get Additional record %d", addr, i); goto end; }
+ if (opt.r.resrec.RecordType != kDNSRecordTypePacketNegative && opt.r.resrec.rrtype == kDNSType_OPT) break;
+ }
+
+ // validate OPT
+ if (opt.r.resrec.rrtype != kDNSType_OPT) { Log("Malformatted LLQ from %s: last Additional not an OPT RR", addr); goto end; }
+ if (opt.r.resrec.rdlength < pkt->msg.h.numQuestions * DNSOpt_LLQData_Space) { Log("Malformatted LLQ from %s: OPT RR to small (%d bytes for %d questions)", addr, opt.r.resrec.rdlength, pkt->msg.h.numQuestions); }
+
+ // dispatch each question
+ for (i = 0; i < pkt->msg.h.numQuestions; i++)
+ {
+ qptr = getQuestion(&pkt->msg, qptr, end, 0, &q);
+ if (!qptr) { Log("Malformatted LLQ from %s: cannot read question %d", addr, i); goto end; }
+ llq = (LLQOptData *)&opt.r.resrec.rdata->u.opt[0].u.llq + i; // point into OptData at index i
+ if (llq->vers != kLLQ_Vers) { Log("LLQ from %s contains bad version %d (expected %d)", addr, llq->vers, kLLQ_Vers); goto end; }
+
+ e = LookupLLQ(d, pkt->src, &q.qname, q.qtype, &llq->id);
+ if (!e)
+ {
+ // no entry - if zero ID, create new
+ e = NewLLQ(d, pkt->src, &q.qname, q.qtype, llq->llqlease );
+ if (!e) goto end;
+ }
+ UpdateLLQ(d, e, llq, pkt->msg.h.id, sock);
+ }
+ err = 0;
+
+end:
+ HdrHToN(pkt);
+ return err;
+}
+
+
+mDNSlocal mDNSBool IsAuthorized( DaemonInfo * d, PktMsg * pkt, DomainAuthInfo ** key, mDNSu16 * rcode, mDNSu16 * tcode )
+{
+ const mDNSu8 * lastPtr = NULL;
+ const mDNSu8 * ptr = NULL;
+ DomainAuthInfo * keys;
+ mDNSu8 * end = ( mDNSu8* ) &pkt->msg + pkt->len;
+ LargeCacheRecord lcr;
+ mDNSBool hasTSIG = mDNSfalse;
+ mDNSBool strip = mDNSfalse;
+ mDNSBool ok = mDNSfalse;
+ int i;
+
+ // Unused parameters
+
+ ( void ) d;
+
+ HdrNToH(pkt);
+
+ *key = NULL;
+
+ if ( pkt->msg.h.numAdditionals )
+ {
+ ptr = LocateAdditionals(&pkt->msg, end);
+ if (ptr)
+ {
+ for (i = 0; i < pkt->msg.h.numAdditionals; i++)
+ {
+ lastPtr = ptr;
+ ptr = GetLargeResourceRecord(NULL, &pkt->msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr);
+ if (!ptr)
+ {
+ Log("Unable to read additional record");
+ lastPtr = NULL;
+ break;
+ }
+ }
+
+ hasTSIG = ( ptr && lcr.r.resrec.RecordType != kDNSRecordTypePacketNegative && lcr.r.resrec.rrtype == kDNSType_TSIG );
+ }
+ else
+ {
+ LogMsg( "IsAuthorized: unable to find Additional section" );
+ }
+ }
+
+ // If we don't know what zone this is, then it's authorized.
+
+ if ( !pkt->zone )
+ {
+ ok = mDNStrue;
+ strip = mDNSfalse;
+ goto exit;
+ }
+
+ if ( IsQuery( pkt ) )
+ {
+ keys = pkt->zone->queryKeys;
+ strip = mDNStrue;
+ }
+ else if ( IsUpdate( pkt ) )
+ {
+ keys = pkt->zone->updateKeys;
+ strip = mDNSfalse;
+ }
+ else
+ {
+ ok = mDNStrue;
+ strip = mDNSfalse;
+ goto exit;
+ }
+
+ if ( pkt->isZonePublic )
+ {
+ ok = mDNStrue;
+ goto exit;
+ }
+
+ // If there are no keys, then we're authorized
+
+ if ( ( hasTSIG && !keys ) || ( !hasTSIG && keys ) )
+ {
+ Log( "Invalid TSIG spec %##s for zone %##s", lcr.r.resrec.name->c, pkt->zone->name.c );
+ *rcode = kDNSFlag1_RC_NotAuth;
+ *tcode = TSIG_ErrBadKey;
+ strip = mDNStrue;
+ ok = mDNSfalse;
+ goto exit;
+ }
+
+ // Find the right key
+
+ for ( *key = keys; *key; *key = (*key)->next )
+ {
+ if ( SameDomainName( lcr.r.resrec.name, &(*key)->keyname ) )
+ {
+ break;
+ }
+ }
+
+ if ( !(*key) )
+ {
+ Log( "Invalid TSIG name %##s for zone %##s", lcr.r.resrec.name->c, pkt->zone->name.c );
+ *rcode = kDNSFlag1_RC_NotAuth;
+ *tcode = TSIG_ErrBadKey;
+ strip = mDNStrue;
+ ok = mDNSfalse;
+ goto exit;
+ }
+
+ // Okay, we have the correct key and a TSIG record. DNSDigest_VerifyMessage does the heavy
+ // lifting of message verification
+
+ pkt->msg.h.numAdditionals--;
+
+ HdrHToN( pkt );
+
+ ok = DNSDigest_VerifyMessage( &pkt->msg, ( mDNSu8* ) lastPtr, &lcr, (*key), rcode, tcode );
+
+ HdrNToH( pkt );
+
+ pkt->msg.h.numAdditionals++;
+
+exit:
+
+ if ( hasTSIG && strip )
+ {
+ // Strip the TSIG from the message
+
+ pkt->msg.h.numAdditionals--;
+ pkt->len = lastPtr - ( mDNSu8* ) ( &pkt->msg );
+ }
+
+ HdrHToN(pkt);
+
+ return ok;
+}
+
+// request handler wrappers for TCP and UDP requests
+// (read message off socket, fork thread that invokes main processing routine and handles cleanup)
+
+mDNSlocal void*
+UDPMessageHandler
+(
+ void * vptr
+)
+{
+ UDPContext * context = ( UDPContext* ) vptr;
+ PktMsg * reply = NULL;
+ int res;
+ mStatus err;
+
+ // !!!KRS strictly speaking, we shouldn't use TCP for a UDP request because the server
+ // may give us a long answer that would require truncation for UDP delivery to client
+
+ reply = HandleRequest( context->d, &context->pkt );
+ require_action( reply, exit, err = mStatus_UnknownErr );
+
+ res = sendto( context->sd, &reply->msg, reply->len, 0, ( struct sockaddr* ) &context->pkt.src, sizeof( context->pkt.src ) );
+ require_action_quiet( res == ( int ) reply->len, exit, LogErr( "UDPMessageHandler", "sendto" ) );
+
+exit:
+
+ if ( reply )
+ {
+ free( reply );
+ }
+
+ free( context );
+
+ pthread_exit( NULL );
+
+ return NULL;
+}
+
+
+mDNSlocal int
+RecvUDPMessage
+(
+ DaemonInfo * self,
+ int sd
+)
+{
+ UDPContext * context = NULL;
+ pthread_t tid;
+ mDNSu16 rcode;
+ mDNSu16 tcode;
+ DomainAuthInfo * key;
+ unsigned int clisize = sizeof( context->cliaddr );
+ int res;
+ mStatus err = mStatus_NoError;
+
+ context = malloc( sizeof( UDPContext ) );
+ require_action( context, exit, err = mStatus_NoMemoryErr ; LogErr( "RecvUDPMessage", "malloc" ) );
+
+ mDNSPlatformMemZero( context, sizeof( *context ) );
+ context->d = self;
+ context->sd = sd;
+
+ res = recvfrom(sd, &context->pkt.msg, sizeof(context->pkt.msg), 0, (struct sockaddr *)&context->cliaddr, &clisize);
+
+ require_action( res >= 0, exit, err = mStatus_UnknownErr ; LogErr( "RecvUDPMessage", "recvfrom" ) );
+ context->pkt.len = res;
+ require_action( clisize == sizeof( context->cliaddr ), exit, err = mStatus_UnknownErr ; Log( "Client address of unknown size %d", clisize ) );
+ context->pkt.src = context->cliaddr;
+
+ // Set the zone in the packet
+
+ SetZone( context->d, &context->pkt );
+
+ // Notify messages handled by main thread
+
+ if ( IsNotify( &context->pkt ) )
+ {
+ int e = RecvNotify( self, &context->pkt );
+ free(context);
+ return e;
+ }
+ else if ( IsAuthorized( context->d, &context->pkt, &key, &rcode, &tcode ) )
+ {
+ if ( IsLLQRequest( &context->pkt ) )
+ {
+ // LLQ messages handled by main thread
+ int e = RecvLLQ( self, &context->pkt, NULL );
+ free(context);
+ return e;
+ }
+
+ if ( IsLLQAck(&context->pkt ) )
+ {
+ // !!!KRS need to do acks + retrans
+
+ free(context);
+ return 0;
+ }
+
+ err = pthread_create( &tid, NULL, UDPMessageHandler, context );
+ require_action( !err, exit, LogErr( "RecvUDPMessage", "pthread_create" ) );
+
+ pthread_detach(tid);
+ }
+ else
+ {
+ PktMsg reply;
+ int e;
+
+ memcpy( &reply, &context->pkt, sizeof( PktMsg ) );
+
+ reply.msg.h.flags.b[0] = kDNSFlag0_QR_Response | kDNSFlag0_AA | kDNSFlag0_RD;
+ reply.msg.h.flags.b[1] = kDNSFlag1_RA | kDNSFlag1_RC_NXDomain;
+
+ e = sendto( sd, &reply.msg, reply.len, 0, ( struct sockaddr* ) &context->pkt.src, sizeof( context->pkt.src ) );
+ require_action_quiet( e == ( int ) reply.len, exit, LogErr( "RecvUDPMessage", "sendto" ) );
+
+ err = mStatus_NoAuth;
+ }
+
+exit:
+
+ if ( err && context )
+ {
+ free( context );
+ }
+
+ return err;
+}
+
+
+mDNSlocal void
+FreeTCPContext
+(
+ TCPContext * context
+)
+{
+ if ( context )
+ {
+ if ( context->sock )
+ {
+ mDNSPlatformTCPCloseConnection( context->sock );
+ }
+
+ free( context );
+ }
+}
+
+
+mDNSlocal void*
+TCPMessageHandler
+(
+ void * vptr
+)
+{
+ TCPContext * context = ( TCPContext* ) vptr;
+ PktMsg * reply = NULL;
+ int res;
+ char buf[32];
+
+ //!!!KRS if this read blocks indefinitely, we can run out of threads
+ // read the request
+
+ reply = HandleRequest( context->d, &context->pkt );
+ require_action_quiet( reply, exit, LogMsg( "TCPMessageHandler: No reply for client %s", inet_ntop( AF_INET, &context->cliaddr.sin_addr, buf, 32 ) ) );
+
+ // deliver reply to client
+
+ res = SendPacket( context->sock, reply );
+ require_action( res >= 0, exit, LogMsg("TCPMessageHandler: Unable to send reply to client %s", inet_ntop(AF_INET, &context->cliaddr.sin_addr, buf, 32 ) ) );
+
+exit:
+
+ FreeTCPContext( context );
+
+ if ( reply )
+ {
+ free( reply );
+ }
+
+ pthread_exit(NULL);
+}
+
+
+mDNSlocal void
+RecvTCPMessage
+(
+ void * param
+)
+{
+ TCPContext * context = ( TCPContext* ) param;
+ mDNSu16 rcode;
+ mDNSu16 tcode;
+ pthread_t tid;
+ DomainAuthInfo * key;
+ PktMsg * pkt;
+ mDNSBool closed;
+ mDNSBool freeContext = mDNStrue;
+ mStatus err = mStatus_NoError;
+
+ // Receive a packet. It's okay if we don't actually read a packet, as long as the closed flag is
+ // set to false. This is because SSL/TLS layer might gobble up the first packet that we read off the
+ // wire. We'll let it do that, and wait for the next packet which will be ours.
+
+ pkt = RecvPacket( context->sock, &context->pkt, &closed );
+ if (pkt) HdrNToH(pkt);
+ require_action( pkt || !closed, exit, err = mStatus_UnknownErr; LogMsg( "client disconnected" ) );
+
+ if ( pkt )
+ {
+ // Always do this, regardless of what kind of packet it is. If we wanted LLQ events to be sent over TCP,
+ // we would change this line of code. As it is now, we will reply to an LLQ via TCP, but then events
+ // are sent over UDP
+
+ RemoveSourceFromEventLoop( context->d, context->sock );
+
+ // Set's the DNS Zone that is associated with this message
+
+ SetZone( context->d, &context->pkt );
+
+ // IsAuthorized will make sure the message is authorized for the designated zone.
+ // After verifying the signature, it will strip the TSIG from the message
+
+ if ( IsAuthorized( context->d, &context->pkt, &key, &rcode, &tcode ) )
+ {
+ if ( IsLLQRequest( &context->pkt ) )
+ {
+ // LLQ messages handled by main thread
+ RecvLLQ( context->d, &context->pkt, context->sock);
+ }
+ else
+ {
+ err = pthread_create( &tid, NULL, TCPMessageHandler, context );
+
+ if ( err )
+ {
+ LogErr( "RecvTCPMessage", "pthread_create" );
+ err = mStatus_NoError;
+ goto exit;
+ }
+
+ // Let the thread free the context
+
+ freeContext = mDNSfalse;
+
+ pthread_detach(tid);
+ }
+ }
+ else
+ {
+ PktMsg reply;
+
+ LogMsg( "Client %s Not authorized for zone %##s", inet_ntoa( context->pkt.src.sin_addr ), pkt->zone->name.c );
+
+ memcpy( &reply, &context->pkt, sizeof( PktMsg ) );
+
+ reply.msg.h.flags.b[0] = kDNSFlag0_QR_Response | kDNSFlag0_AA | kDNSFlag0_RD;
+ reply.msg.h.flags.b[1] = kDNSFlag1_RA | kDNSFlag1_RC_Refused;
+
+ SendPacket( context->sock, &reply );
+ }
+ }
+ else
+ {
+ freeContext = mDNSfalse;
+ }
+
+exit:
+
+ if ( err )
+ {
+ RemoveSourceFromEventLoop( context->d, context->sock );
+ }
+
+ if ( freeContext )
+ {
+ FreeTCPContext( context );
+ }
+}
+
+
+mDNSlocal int
+AcceptTCPConnection
+(
+ DaemonInfo * self,
+ int sd,
+ TCPSocketFlags flags
+)
+{
+ TCPContext * context = NULL;
+ unsigned int clilen = sizeof( context->cliaddr);
+ int newSock;
+ mStatus err = mStatus_NoError;
+
+ context = ( TCPContext* ) malloc( sizeof( TCPContext ) );
+ require_action( context, exit, err = mStatus_NoMemoryErr; LogErr( "AcceptTCPConnection", "malloc" ) );
+ mDNSPlatformMemZero( context, sizeof( sizeof( TCPContext ) ) );
+ context->d = self;
+ newSock = accept( sd, ( struct sockaddr* ) &context->cliaddr, &clilen );
+ require_action( newSock != -1, exit, err = mStatus_UnknownErr; LogErr( "AcceptTCPConnection", "accept" ) );
+
+ context->sock = mDNSPlatformTCPAccept( flags, newSock );
+ require_action( context->sock, exit, err = mStatus_UnknownErr; LogErr( "AcceptTCPConnection", "mDNSPlatformTCPAccept" ) );
+
+ err = AddSourceToEventLoop( self, context->sock, RecvTCPMessage, context );
+ require_action( !err, exit, LogErr( "AcceptTCPConnection", "AddSourceToEventLoop" ) );
+
+exit:
+
+ if ( err && context )
+ {
+ free( context );
+ context = NULL;
+ }
+
+ return err;
+}
+
+
+// main event loop
+// listen for incoming requests, periodically check table for expired records, respond to signals
+mDNSlocal int Run(DaemonInfo *d)
+{
+ int staticMaxFD, nfds;
+ fd_set rset;
+ struct timeval timenow, timeout, EventTS, tablecheck = { 0, 0 };
+ mDNSBool EventsPending = mDNSfalse;
+
+ VLog("Listening for requests...");
+
+ staticMaxFD = 0;
+
+ if ( d->tcpsd + 1 > staticMaxFD ) staticMaxFD = d->tcpsd + 1;
+ if ( d->udpsd + 1 > staticMaxFD ) staticMaxFD = d->udpsd + 1;
+ if ( d->tlssd + 1 > staticMaxFD ) staticMaxFD = d->tlssd + 1;
+ if ( d->llq_tcpsd + 1 > staticMaxFD ) staticMaxFD = d->llq_tcpsd + 1;
+ if ( d->llq_udpsd + 1 > staticMaxFD ) staticMaxFD = d->llq_udpsd + 1;
+ if ( d->LLQEventListenSock + 1 > staticMaxFD ) staticMaxFD = d->LLQEventListenSock + 1;
+
+ while(1)
+ {
+ EventSource * source;
+ int maxFD;
+
+ // set timeout
+ timeout.tv_sec = timeout.tv_usec = 0;
+ if (gettimeofday(&timenow, NULL)) { LogErr("Run", "gettimeofday"); return -1; }
+
+ if (EventsPending)
+ {
+ if (timenow.tv_sec - EventTS.tv_sec >= 5) // if we've been waiting 5 seconds for a "quiet" period to send
+ { GenLLQEvents(d); EventsPending = mDNSfalse; } // events, we go ahead and do it now
+ else timeout.tv_usec = 500000; // else do events after 1/2 second with no new events or LLQs
+ }
+ if (!EventsPending)
+ {
+ // if no pending events, timeout when we need to check for expired records
+ if (tablecheck.tv_sec && timenow.tv_sec - tablecheck.tv_sec >= 0)
+ { DeleteRecords(d, mDNSfalse); tablecheck.tv_sec = 0; } // table check overdue
+ if (!tablecheck.tv_sec) tablecheck.tv_sec = timenow.tv_sec + EXPIRATION_INTERVAL;
+ timeout.tv_sec = tablecheck.tv_sec - timenow.tv_sec;
+ }
+
+ FD_ZERO(&rset);
+ FD_SET( d->tcpsd, &rset );
+ FD_SET( d->udpsd, &rset );
+ FD_SET( d->tlssd, &rset );
+ FD_SET( d->llq_tcpsd, &rset );
+ FD_SET( d->llq_udpsd, &rset );
+ FD_SET( d->LLQEventListenSock, &rset );
+
+ maxFD = staticMaxFD;
+
+ for ( source = ( EventSource* ) d->eventSources.Head; source; source = source->next )
+ {
+ FD_SET( source->fd, &rset );
+
+ if ( source->fd > maxFD )
+ {
+ maxFD = source->fd;
+ }
+ }
+
+ nfds = select( maxFD + 1, &rset, NULL, NULL, &timeout);
+ if (nfds < 0)
+ {
+ if (errno == EINTR)
+ {
+ if (terminate)
+ {
+ // close sockets to prevent clients from making new requests during shutdown
+ close( d->tcpsd );
+ close( d->udpsd );
+ close( d->tlssd );
+ close( d->llq_tcpsd );
+ close( d->llq_udpsd );
+ d->tcpsd = d->udpsd = d->tlssd = d->llq_tcpsd = d->llq_udpsd = -1;
+ DeleteRecords(d, mDNStrue);
+ return 0;
+ }
+ else if (dumptable)
+ {
+ Log( "Received SIGINFO" );
+
+ PrintLeaseTable(d);
+ PrintLLQTable(d);
+ PrintLLQAnswers(d);
+ dumptable = 0;
+ }
+ else if (hangup)
+ {
+ int err;
+
+ Log( "Received SIGHUP" );
+
+ err = ParseConfig( d, cfgfile );
+
+ if ( err )
+ {
+ LogErr( "Run", "ParseConfig" );
+ return -1;
+ }
+
+ hangup = 0;
+ }
+ else
+ {
+ Log("Received unhandled signal - continuing");
+ }
+ }
+ else
+ {
+ LogErr("Run", "select"); return -1;
+ }
+ }
+ else if (nfds)
+ {
+ if (FD_ISSET(d->udpsd, &rset)) RecvUDPMessage( d, d->udpsd );
+ if (FD_ISSET(d->llq_udpsd, &rset)) RecvUDPMessage( d, d->llq_udpsd );
+ if (FD_ISSET(d->tcpsd, &rset)) AcceptTCPConnection( d, d->tcpsd, 0 );
+ if (FD_ISSET(d->llq_tcpsd, &rset)) AcceptTCPConnection( d, d->llq_tcpsd, 0 );
+ if (FD_ISSET(d->tlssd, &rset)) AcceptTCPConnection( d, d->tlssd, TCP_SOCKET_FLAGS );
+ if (FD_ISSET(d->LLQEventListenSock, &rset))
+ {
+ // clear signalling data off socket
+ char buf[256];
+ recv(d->LLQEventListenSock, buf, 256, 0);
+ if (!EventsPending)
+ {
+ EventsPending = mDNStrue;
+ if (gettimeofday(&EventTS, NULL)) { LogErr("Run", "gettimeofday"); return -1; }
+ }
+ }
+
+ for ( source = ( EventSource* ) d->eventSources.Head; source; source = source->next )
+ {
+ if ( FD_ISSET( source->fd, &rset ) )
+ {
+ source->callback( source->context );
+ break; // in case we removed this guy from the event loop
+ }
+ }
+ }
+ else
+ {
+ // timeout
+ if (EventsPending) { GenLLQEvents(d); EventsPending = mDNSfalse; }
+ else { DeleteRecords(d, mDNSfalse); tablecheck.tv_sec = 0; }
+ }
+ }
+ return 0;
+}
+
+// signal handler sets global variables, which are inspected by main event loop
+// (select automatically returns due to the handled signal)
+mDNSlocal void HndlSignal(int sig)
+{
+ if (sig == SIGTERM || sig == SIGINT ) { terminate = 1; return; }
+ if (sig == INFO_SIGNAL) { dumptable = 1; return; }
+ if (sig == SIGHUP) { hangup = 1; return; }
+}
+
+mDNSlocal mStatus
+SetPublicSRV
+(
+ DaemonInfo * d,
+ const char * name
+)
+{
+ DNameListElem * elem;
+ mStatus err = mStatus_NoError;
+
+ elem = ( DNameListElem* ) malloc( sizeof( DNameListElem ) );
+ require_action( elem, exit, err = mStatus_NoMemoryErr );
+ MakeDomainNameFromDNSNameString( &elem->name, name );
+ elem->next = d->public_names;
+ d->public_names = elem;
+
+exit:
+
+ return err;
+}
+
+
+int main(int argc, char *argv[])
+{
+ int started_via_launchd = 0;
+ DaemonInfo *d;
+ struct rlimit rlim;
+
+ Log("dnsextd starting");
+
+ d = malloc(sizeof(*d));
+ if (!d) { LogErr("main", "malloc"); exit(1); }
+ mDNSPlatformMemZero(d, sizeof(DaemonInfo));
+
+ // Setup the public SRV record names
+
+ SetPublicSRV(d, "_dns-update._udp.");
+ SetPublicSRV(d, "_dns-llq._udp.");
+ SetPublicSRV(d, "_dns-update-tls._tcp.");
+ SetPublicSRV(d, "_dns-query-tls._tcp.");
+ SetPublicSRV(d, "_dns-llq-tls._tcp.");
+
+ // Setup signal handling
+
+ if (signal(SIGHUP, HndlSignal) == SIG_ERR) perror("Can't catch SIGHUP");
+ if (signal(SIGTERM, HndlSignal) == SIG_ERR) perror("Can't catch SIGTERM");
+ if (signal(INFO_SIGNAL, HndlSignal) == SIG_ERR) perror("Can't catch SIGINFO");
+ if (signal(SIGINT, HndlSignal) == SIG_ERR) perror("Can't catch SIGINT");
+ if (signal(SIGPIPE, SIG_IGN ) == SIG_ERR) perror("Can't ignore SIGPIPE");
+
+ // remove open file limit
+ rlim.rlim_max = RLIM_INFINITY;
+ rlim.rlim_cur = RLIM_INFINITY;
+ if (setrlimit(RLIMIT_NOFILE, &rlim) < 0)
+ {
+ LogErr("main", "setrlimit");
+ Log("Using default file descriptor resource limit");
+ }
+
+ if (argc > 1 && !strcasecmp(argv[1], "-launchd"))
+ {
+ Log("started_via_launchd");
+ started_via_launchd = 1;
+ argv++;
+ argc--;
+ }
+ if (ProcessArgs(argc, argv, d) < 0) { LogErr("main", "ProcessArgs"); exit(1); }
+
+ if (!foreground && !started_via_launchd)
+ {
+ if (daemon(0,0))
+ {
+ LogErr("main", "daemon");
+ foreground = 1;
+ }
+ }
+
+ if (InitLeaseTable(d) < 0) { LogErr("main", "InitLeaseTable"); exit(1); }
+ if (SetupSockets(d) < 0) { LogErr("main", "SetupSockets"); exit(1); }
+ if (SetUpdateSRV(d) < 0) { LogErr("main", "SetUpdateSRV"); exit(1); }
+
+ Run(d);
+
+ Log("dnsextd stopping");
+
+ if (ClearUpdateSRV(d) < 0) { LogErr("main", "ClearUpdateSRV"); exit(1); } // clear update srv's even if Run or pthread_create returns an error
+ free(d);
+ exit(0);
+}
+
+
+// These are stubbed out implementations of up-call routines that the various platform support layers
+// call. These routines are fully implemented in both mDNS.c and uDNS.c, but dnsextd doesn't
+// link this code in.
+//
+// It's an error for these routines to actually be called, so perhaps we should log any call
+// to them.
+void mDNSCoreInitComplete( mDNS * const m, mStatus result) { ( void ) m; ( void ) result; }
+void mDNS_ConfigChanged(mDNS *const m) { ( void ) m; }
+void mDNSCoreMachineSleep(mDNS * const m, mDNSBool wake) { ( void ) m; ( void ) wake; }
+void mDNSCoreReceive(mDNS *const m, void *const msg, const mDNSu8 *const end,
+ const mDNSAddr *const srcaddr, const mDNSIPPort srcport,
+ const mDNSAddr *const dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID iid)
+{ ( void ) m; ( void ) msg; ( void ) end; ( void ) srcaddr; ( void ) srcport; ( void ) dstaddr; ( void ) dstport; ( void ) iid; }
+DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const int serviceID, const mDNSAddr *addr, const mDNSIPPort port,
+ mDNSu32 scoped, mDNSu32 timeout, mDNSBool cellIntf, mDNSu16 resGroupID, mDNSBool reqA, mDNSBool reqAAAA, mDNSBool reqDO)
+{ ( void ) m; ( void ) d; ( void ) interface; ( void ) serviceID; ( void ) addr; ( void ) port; ( void ) scoped; ( void ) timeout; (void) cellIntf;
+ (void) resGroupID; (void) reqA; (void) reqAAAA; (void) reqDO; return(NULL); }
+void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID) { (void)domain; (void) InterfaceID;}
+void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext)
+{ ( void ) m; ( void ) fqdn; ( void ) StatusCallback; ( void ) StatusContext; }
+mDNSs32 mDNS_Execute (mDNS *const m) { ( void ) m; return 0; }
+mDNSs32 mDNS_TimeNow(const mDNS *const m) { ( void ) m; return 0; }
+mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr) { ( void ) m; ( void ) rr; return 0; }
+void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping)
+{ ( void ) m; ( void ) set; ( void ) flapping; }
+const char * const mDNS_DomainTypeNames[1] = {};
+mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const domainname *dom,
+ const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context)
+{ ( void ) m; ( void ) question; ( void ) DomainType; ( void ) dom; ( void ) InterfaceID; ( void ) Callback; ( void ) Context; return 0; }
+mStatus mDNS_Register(mDNS *const m, AuthRecord *const rr) { ( void ) m; ( void ) rr; return 0; }
+mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping)
+{ ( void ) m; ( void ) set; ( void ) flapping; return 0; }
+void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn) { ( void ) m; ( void ) fqdn; }
+void mDNS_SetFQDN(mDNS * const m) { ( void ) m; }
+void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, const mDNSAddr *v6addr, const mDNSAddr *router)
+{ ( void ) m; ( void ) v4addr; ( void ) v6addr; ( void ) router; }
+mStatus uDNS_SetupDNSConfig( mDNS *const m ) { ( void ) m; return 0; }
+mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info,
+ const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, mDNSBool autoTunnel)
+{ ( void ) m; ( void ) info; ( void ) domain; ( void ) keyname; ( void ) b64keydata; ( void ) hostname; (void) port; ( void ) autoTunnel; return 0; }
+mStatus mDNS_StopQuery(mDNS *const m, DNSQuestion *const question) { ( void ) m; ( void ) question; return 0; }
+void TriggerEventCompletion(void);
+void TriggerEventCompletion() {}
+int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q);
+int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) { ( void ) rr; ( void ) q; return 1;}
+mDNS mDNSStorage;
+
+
+// For convenience when using the "strings" command, this is the last thing in the file
+// The "@(#) " pattern is a special prefix the "what" command looks for
+const char mDNSResponderVersionString_SCCS[] = "@(#) dnsextd " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
+
+#if _BUILDING_XCODE_PROJECT_
+// If the process crashes, then this string will be magically included in the automatically-generated crash log
+const char *__crashreporter_info__ = mDNSResponderVersionString_SCCS + 5;
+asm (".desc ___crashreporter_info__, 0x10");
+#endif
diff --git a/mDNSResponder/mDNSShared/dnsextd.conf b/mDNSResponder/mDNSShared/dnsextd.conf
new file mode 100644
index 00000000..0379580d
--- /dev/null
+++ b/mDNSResponder/mDNSShared/dnsextd.conf
@@ -0,0 +1,60 @@
+// ----------------------------------------------------------------------------
+//
+// Instructions for /etc/dnsextd.conf (this file)
+//
+// In most cases, you should not need to change these default options in
+// the "options" section below. The dnsextd daemon will receive DNS packets
+// on port 53, and forward them on as appropriate to BIND on localhost:5030.
+//
+// You need to edit the "zone" statement below to give the name of your
+// dynamic zone that will be accepting Wide-Area Bonjour DNS updates.
+//
+// ----------------------------------------------------------------------------
+//
+// Instructions for /etc/named.conf
+//
+// In /etc/named.conf you will need to modify the "options" section to
+// tell BIND to accept packets from localhost:5030, like this:
+//
+// listen-on port 5030 { 127.0.0.1; };
+//
+// You also need a "zone" statement in /etc/named.conf to tell BIND the update
+// policy for your dynamic zone. For example, within a small closed private
+// network, you might allow anyone to perform updates. To do that, you just
+// permit any and all updates coming from dnsextd on the same machine:
+//
+// zone "my-dynamic-subdomain.company.com."
+// { type master; file "db.xxx"; allow-update { 127.0.0.1; }; };
+//
+// On a machine connected to the Internet or other large open network,
+// you'll want to limit updates to only users with keys. For example,
+// you could choose to allow anyone with a DNS key on your server to
+// perform updates in your dynamic zone, like this:
+//
+// key keyname. { algorithm hmac-md5; secret "abcdefghijklmnopqrstuv=="; };
+// zone "my-dynamic-subdomain.company.com." in
+// {
+// type master;
+// file "db.my-dynamic-subdomain.company.com";
+// update-policy { grant * wildcard *.my-dynamic-subdomain.company.com.; };
+// };
+//
+// You could use a single key which you give to all authorized users, but
+// it is better (though more work) to create a unique key for each user.
+//
+// ----------------------------------------------------------------------------
+
+options {
+// This defaults to: * port 53
+// listen-on port 53 { 192.168.2.10; 127.0.0.1; };
+// This defaults to: 127.0.0.1:5030
+// nameserver address 127.0.0.1 port 5030;
+// This defaults to: 5533
+// private port 5533;
+// This defaults to: 5352
+// llq port 5352;
+};
+
+zone "my-dynamic-subdomain.company.com." {
+ type public;
+};
diff --git a/mDNSResponder/mDNSShared/dnsextd.h b/mDNSResponder/mDNSShared/dnsextd.h
new file mode 100644
index 00000000..67927c9b
--- /dev/null
+++ b/mDNSResponder/mDNSShared/dnsextd.h
@@ -0,0 +1,163 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef _dnsextd_h
+#define _dnsextd_h
+
+
+#include <mDNSEmbeddedAPI.h>
+#include <DNSCommon.h>
+#include <GenLinkedList.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+
+#define LLQ_TABLESIZE 1024 // !!!KRS make this dynamically growable
+
+
+typedef enum DNSZoneSpecType
+{
+ kDNSZonePublic,
+ kDNSZonePrivate
+} DNSZoneSpecType;
+
+
+typedef struct DNSZone
+{
+ domainname name;
+ DNSZoneSpecType type;
+ DomainAuthInfo * updateKeys; // linked list of keys for signing deletion updates
+ DomainAuthInfo * queryKeys; // linked list of keys for queries
+ struct DNSZone * next;
+} DNSZone;
+
+
+typedef struct
+{
+ struct sockaddr_in src;
+ size_t len;
+ DNSZone * zone;
+ mDNSBool isZonePublic;
+ DNSMessage msg;
+ // Note: extra storage for oversized (TCP) messages goes here
+} PktMsg;
+
+// lease table entry
+typedef struct RRTableElem
+{
+ struct RRTableElem *next;
+ struct sockaddr_in cli; // client's source address
+ long expire; // expiration time, in seconds since epoch
+ domainname zone; // from zone field of update message
+ domainname name; // name of the record
+ CacheRecord rr; // last field in struct allows for allocation of oversized RRs
+} RRTableElem;
+
+typedef enum
+{
+ RequestReceived = 0,
+ ChallengeSent = 1,
+ Established = 2
+} LLQState;
+
+typedef struct AnswerListElem
+{
+ struct AnswerListElem *next;
+ domainname name;
+ mDNSu16 type;
+ CacheRecord *KnownAnswers; // All valid answers delivered to client
+ CacheRecord *EventList; // New answers (adds/removes) to be sent to client
+ int refcount;
+ mDNSBool UseTCP; // Use TCP if UDP would cause truncation
+ pthread_t tid; // Allow parallel list updates
+} AnswerListElem;
+
+// llq table entry
+typedef struct LLQEntry
+{
+ struct LLQEntry *next;
+ struct sockaddr_in cli; // clien'ts source address
+ domainname qname;
+ mDNSu16 qtype;
+ mDNSOpaque64 id;
+ LLQState state;
+ mDNSu32 lease; // original lease, in seconds
+ mDNSs32 expire; // expiration, absolute, in seconds since epoch
+ AnswerListElem *AnswerList;
+} LLQEntry;
+
+
+typedef void (*EventCallback)( void * context );
+
+typedef struct EventSource
+{
+ EventCallback callback;
+ void * context;
+ TCPSocket * sock;
+ int fd;
+ mDNSBool markedForDeletion;
+ struct EventSource * next;
+} EventSource;
+
+
+// daemon-wide information
+typedef struct
+{
+ // server variables - read only after initialization (no locking)
+ struct sockaddr_in addr; // the address we will bind to
+ struct sockaddr_in llq_addr; // the address we will receive llq requests on.
+ struct sockaddr_in ns_addr; // the real ns server address
+ int tcpsd; // listening TCP socket for dns requests
+ int udpsd; // listening UDP socket for dns requests
+ int tlssd; // listening TCP socket for private browsing
+ int llq_tcpsd; // listening TCP socket for llq service
+ int llq_udpsd; // listening UDP socket for llq service
+ DNameListElem * public_names; // list of public SRV names
+ DNSZone * zones;
+
+ // daemon variables - read only after initialization (no locking)
+ mDNSIPPort private_port; // listening port for private messages
+ mDNSIPPort llq_port; // listening port for llq
+
+ // lease table variables (locked via mutex after initialization)
+ RRTableElem **table; // hashtable for records with leases
+ pthread_mutex_t tablelock; // mutex for lease table
+ mDNSs32 nbuckets; // buckets allocated
+ mDNSs32 nelems; // elements in table
+
+ // LLQ table variables
+ LLQEntry *LLQTable[LLQ_TABLESIZE]; // !!!KRS change this and RRTable to use a common data structure
+ AnswerListElem *AnswerTable[LLQ_TABLESIZE];
+ int AnswerTableCount;
+ int LLQEventNotifySock; // Unix domain socket pair - update handling thread writes to EventNotifySock, which wakes
+ int LLQEventListenSock; // the main thread listening on EventListenSock, indicating that the zone has changed
+
+ GenLinkedList eventSources; // linked list of EventSource's
+} DaemonInfo;
+
+
+int
+ParseConfig
+(
+ DaemonInfo * d,
+ const char * file
+);
+
+
+#endif
diff --git a/mDNSResponder/mDNSShared/dnsextd_lexer.l b/mDNSResponder/mDNSShared/dnsextd_lexer.l
new file mode 100644
index 00000000..5cac1060
--- /dev/null
+++ b/mDNSResponder/mDNSShared/dnsextd_lexer.l
@@ -0,0 +1,84 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2006-2011 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+%{
+#include <string.h>
+#include <stdio.h>
+#include "dnsextd_parser.h"
+
+
+extern YYSTYPE yylval;
+
+/* Mac OS X 10.4 has flex version 2.5.4, which doesn't define yylineno for us */
+/* Mac OS X 10.5 has flex version 2.5.33, which does define yylineno */
+#if YY_FLEX_MAJOR_VERSION <= 2 && YY_FLEX_MINOR_VERSION <= 5 && YY_FLEX_SUBMINOR_VERSION <= 4
+int yylineno = 1;
+#endif
+#define YY_NO_INPUT 1
+int yylex(void);
+
+static char*
+StripQuotes
+ (
+ const char * string
+ )
+{
+ char * dup;
+
+ dup = strdup( string + 1);
+
+ dup[ strlen( dup ) - 1 ] = '\0';
+
+ return dup;
+}
+
+
+%}
+
+%option nounput
+%%
+
+options return OPTIONS;
+listen-on return LISTEN_ON;
+nameserver return NAMESERVER;
+port return PORT;
+address return ADDRESS;
+llq return LLQ;
+public return PUBLIC;
+private return PRIVATE;
+key return KEY;
+allow-update return ALLOWUPDATE;
+allow-query return ALLOWQUERY;
+algorithm return ALGORITHM;
+secret return SECRET;
+zone return ZONE;
+type return TYPE;
+allow return ALLOW;
+\{ return OBRACE;
+\} return EBRACE;
+; return SEMICOLON;
+IN return IN;
+\* yylval.string = strdup(yytext); return WILDCARD;
+[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ yylval.string = strdup(yytext); return DOTTED_DECIMAL_ADDRESS;
+[0123456789]+ yylval.number = atoi(yytext); return NUMBER;
+[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)* yylval.string = strdup(yytext); return HOSTNAME;
+[a-zA-Z0-9\.]+([a-zA-Z0-9\.]+)* yylval.string = strdup(yytext); return DOMAINNAME;
+\"([^"\\\r\n]*(\\.[^"\\\r\n]*)*)\" yylval.string = StripQuotes(yytext); return QUOTEDSTRING;
+[\/][\/].* /* ignore C++ style comments */;
+\n yylineno++; /* ignore EOL */;
+[ \t]+ /* ignore whitespace */;
+%%
diff --git a/mDNSResponder/mDNSShared/dnsextd_parser.y b/mDNSResponder/mDNSShared/dnsextd_parser.y
new file mode 100644
index 00000000..18c5990f
--- /dev/null
+++ b/mDNSResponder/mDNSShared/dnsextd_parser.y
@@ -0,0 +1,585 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+%{
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "mDNSEmbeddedAPI.h"
+#include "DebugServices.h"
+#include "dnsextd.h"
+
+void yyerror( const char* error );
+int yylex(void);
+
+
+typedef struct StringListElem
+{
+ char * string;
+ struct StringListElem * next;
+} StringListElem;
+
+
+typedef struct OptionsInfo
+{
+ char server_address[ 256 ];
+ int server_port;
+ char source_address[ 256 ];
+ int source_port;
+ int private_port;
+ int llq_port;
+} OptionsInfo;
+
+
+typedef struct ZoneInfo
+{
+ char name[ 256 ];
+ char certificate_name[ 256 ];
+ char allow_clients_file[ 256 ];
+ char allow_clients[ 256 ];
+ char key[ 256 ];
+} ZoneInfo;
+
+
+typedef struct KeySpec
+{
+ char name[ 256 ];
+ char algorithm[ 256 ];
+ char secret[ 256 ];
+ struct KeySpec * next;
+} KeySpec;
+
+
+typedef struct ZoneSpec
+{
+ char name[ 256 ];
+ DNSZoneSpecType type;
+ StringListElem * allowUpdate;
+ StringListElem * allowQuery;
+ char key[ 256 ];
+ struct ZoneSpec * next;
+} ZoneSpec;
+
+
+static StringListElem * g_stringList = NULL;
+static KeySpec * g_keys;
+static ZoneSpec * g_zones;
+static ZoneSpec g_zoneSpec;
+static const char * g_filename;
+
+#define YYPARSE_PARAM context
+
+void
+SetupOptions
+ (
+ OptionsInfo * info,
+ void * context
+ );
+
+%}
+
+%union
+{
+ int number;
+ char * string;
+}
+
+%token OPTIONS
+%token LISTEN_ON
+%token NAMESERVER
+%token PORT
+%token ADDRESS
+%token LLQ
+%token PUBLIC
+%token PRIVATE
+%token ALLOWUPDATE
+%token ALLOWQUERY
+%token KEY
+%token ALGORITHM
+%token SECRET
+%token ISSUER
+%token SERIAL
+%token ZONE
+%token TYPE
+%token ALLOW
+%token OBRACE
+%token EBRACE
+%token SEMICOLON
+%token IN
+%token <string> DOTTED_DECIMAL_ADDRESS
+%token <string> WILDCARD
+%token <string> DOMAINNAME
+%token <string> HOSTNAME
+%token <string> QUOTEDSTRING
+%token <number> NUMBER
+
+%type <string> addressstatement
+%type <string> networkaddress
+
+%%
+
+commands:
+ |
+ commands command SEMICOLON
+ ;
+
+
+command:
+ options_set
+ |
+ zone_set
+ |
+ key_set
+ ;
+
+
+options_set:
+ OPTIONS optionscontent
+ {
+ // SetupOptions( &g_optionsInfo, context );
+ }
+ ;
+
+optionscontent:
+ OBRACE optionsstatements EBRACE
+ ;
+
+optionsstatements:
+ |
+ optionsstatements optionsstatement SEMICOLON
+ ;
+
+
+optionsstatement:
+ statements
+ |
+ LISTEN_ON addresscontent
+ {
+ }
+ |
+ LISTEN_ON PORT NUMBER addresscontent
+ {
+ }
+ |
+ NAMESERVER ADDRESS networkaddress
+ {
+ }
+ |
+ NAMESERVER ADDRESS networkaddress PORT NUMBER
+ {
+ }
+ |
+ PRIVATE PORT NUMBER
+ {
+ ( ( DaemonInfo* ) context )->private_port = mDNSOpaque16fromIntVal( $3 );
+ }
+ |
+ LLQ PORT NUMBER
+ {
+ ( ( DaemonInfo* ) context )->llq_port = mDNSOpaque16fromIntVal( $3 );
+ }
+ ;
+
+key_set:
+ KEY QUOTEDSTRING OBRACE SECRET QUOTEDSTRING SEMICOLON EBRACE
+ {
+ KeySpec * keySpec;
+
+ keySpec = ( KeySpec* ) malloc( sizeof( KeySpec ) );
+
+ if ( !keySpec )
+ {
+ LogMsg("ERROR: memory allocation failure");
+ YYABORT;
+ }
+
+ strncpy( keySpec->name, $2, sizeof( keySpec->name ) );
+ strncpy( keySpec->secret, $5, sizeof( keySpec->secret ) );
+
+ keySpec->next = g_keys;
+ g_keys = keySpec;
+ }
+ ;
+
+zone_set:
+ ZONE QUOTEDSTRING zonecontent
+ {
+ ZoneSpec * zoneSpec;
+
+ zoneSpec = ( ZoneSpec* ) malloc( sizeof( ZoneSpec ) );
+
+ if ( !zoneSpec )
+ {
+ LogMsg("ERROR: memory allocation failure");
+ YYABORT;
+ }
+
+ strncpy( zoneSpec->name, $2, sizeof( zoneSpec->name ) );
+ zoneSpec->type = g_zoneSpec.type;
+ strcpy( zoneSpec->key, g_zoneSpec.key );
+ zoneSpec->allowUpdate = g_zoneSpec.allowUpdate;
+ zoneSpec->allowQuery = g_zoneSpec.allowQuery;
+
+ zoneSpec->next = g_zones;
+ g_zones = zoneSpec;
+ }
+ |
+ ZONE QUOTEDSTRING IN zonecontent
+ {
+ ZoneSpec * zoneSpec;
+
+ zoneSpec = ( ZoneSpec* ) malloc( sizeof( ZoneSpec ) );
+
+ if ( !zoneSpec )
+ {
+ LogMsg("ERROR: memory allocation failure");
+ YYABORT;
+ }
+
+ strncpy( zoneSpec->name, $2, sizeof( zoneSpec->name ) );
+ zoneSpec->type = g_zoneSpec.type;
+ strcpy( zoneSpec->key, g_zoneSpec.key );
+ zoneSpec->allowUpdate = g_zoneSpec.allowUpdate;
+ zoneSpec->allowQuery = g_zoneSpec.allowQuery;
+
+ zoneSpec->next = g_zones;
+ g_zones = zoneSpec;
+ }
+ ;
+
+zonecontent:
+ OBRACE zonestatements EBRACE
+
+zonestatements:
+ |
+ zonestatements zonestatement SEMICOLON
+ ;
+
+zonestatement:
+ TYPE PUBLIC
+ {
+ g_zoneSpec.type = kDNSZonePublic;
+ }
+ |
+ TYPE PRIVATE
+ {
+ g_zoneSpec.type = kDNSZonePrivate;
+ }
+ |
+ ALLOWUPDATE keycontent
+ {
+ g_zoneSpec.allowUpdate = g_stringList;
+ g_stringList = NULL;
+ }
+ |
+ ALLOWQUERY keycontent
+ {
+ g_zoneSpec.allowQuery = g_stringList;
+ g_stringList = NULL;
+ }
+ ;
+
+addresscontent:
+ OBRACE addressstatements EBRACE
+ {
+ }
+
+addressstatements:
+ |
+ addressstatements addressstatement SEMICOLON
+ {
+ }
+ ;
+
+addressstatement:
+ DOTTED_DECIMAL_ADDRESS
+ {
+ }
+ ;
+
+
+keycontent:
+ OBRACE keystatements EBRACE
+ {
+ }
+
+keystatements:
+ |
+ keystatements keystatement SEMICOLON
+ {
+ }
+ ;
+
+keystatement:
+ KEY DOMAINNAME
+ {
+ StringListElem * elem;
+
+ elem = ( StringListElem* ) malloc( sizeof( StringListElem ) );
+
+ if ( !elem )
+ {
+ LogMsg("ERROR: memory allocation failure");
+ YYABORT;
+ }
+
+ elem->string = $2;
+
+ elem->next = g_stringList;
+ g_stringList = elem;
+ }
+ ;
+
+
+networkaddress:
+ DOTTED_DECIMAL_ADDRESS
+ |
+ HOSTNAME
+ |
+ WILDCARD
+ ;
+
+block:
+ OBRACE zonestatements EBRACE SEMICOLON
+ ;
+
+statements:
+ |
+ statements statement
+ ;
+
+statement:
+ block
+ {
+ $<string>$ = NULL;
+ }
+ |
+ QUOTEDSTRING
+ {
+ $<string>$ = $1;
+ }
+%%
+
+int yywrap(void);
+
+extern int yylineno;
+
+void yyerror( const char *str )
+{
+ fprintf( stderr,"%s:%d: error: %s\n", g_filename, yylineno, str );
+}
+
+int yywrap()
+{
+ return 1;
+}
+
+
+int
+ParseConfig
+ (
+ DaemonInfo * d,
+ const char * file
+ )
+ {
+ extern FILE * yyin;
+ DNSZone * zone;
+ DomainAuthInfo * key;
+ KeySpec * keySpec;
+ ZoneSpec * zoneSpec;
+ int err = 0;
+
+ g_filename = file;
+
+ // Tear down the current zone specifiers
+
+ zone = d->zones;
+
+ while ( zone )
+ {
+ DNSZone * next = zone->next;
+
+ key = zone->updateKeys;
+
+ while ( key )
+ {
+ DomainAuthInfo * nextKey = key->next;
+
+ free( key );
+
+ key = nextKey;
+ }
+
+ key = zone->queryKeys;
+
+ while ( key )
+ {
+ DomainAuthInfo * nextKey = key->next;
+
+ free( key );
+
+ key = nextKey;
+ }
+
+ free( zone );
+
+ zone = next;
+ }
+
+ d->zones = NULL;
+
+ yyin = fopen( file, "r" );
+ require_action( yyin, exit, err = 0 );
+
+ err = yyparse( ( void* ) d );
+ require_action( !err, exit, err = 1 );
+
+ for ( zoneSpec = g_zones; zoneSpec; zoneSpec = zoneSpec->next )
+ {
+ StringListElem * elem;
+ mDNSu8 * ok;
+
+ zone = ( DNSZone* ) malloc( sizeof( DNSZone ) );
+ require_action( zone, exit, err = 1 );
+ memset( zone, 0, sizeof( DNSZone ) );
+
+ zone->next = d->zones;
+ d->zones = zone;
+
+ // Fill in the domainname
+
+ ok = MakeDomainNameFromDNSNameString( &zone->name, zoneSpec->name );
+ require_action( ok, exit, err = 1 );
+
+ // Fill in the type
+
+ zone->type = zoneSpec->type;
+
+ // Fill in the allow-update keys
+
+ for ( elem = zoneSpec->allowUpdate; elem; elem = elem->next )
+ {
+ mDNSBool found = mDNSfalse;
+
+ for ( keySpec = g_keys; keySpec; keySpec = keySpec->next )
+ {
+ if ( strcmp( elem->string, keySpec->name ) == 0 )
+ {
+ DomainAuthInfo * authInfo = malloc( sizeof( DomainAuthInfo ) );
+ mDNSs32 keylen;
+ require_action( authInfo, exit, err = 1 );
+ memset( authInfo, 0, sizeof( DomainAuthInfo ) );
+
+ ok = MakeDomainNameFromDNSNameString( &authInfo->keyname, keySpec->name );
+ if (!ok) { free(authInfo); err = 1; goto exit; }
+
+ keylen = DNSDigest_ConstructHMACKeyfromBase64( authInfo, keySpec->secret );
+ if (keylen < 0) { free(authInfo); err = 1; goto exit; }
+
+ authInfo->next = zone->updateKeys;
+ zone->updateKeys = authInfo;
+
+ found = mDNStrue;
+
+ break;
+ }
+ }
+
+ // Log this
+ require_action( found, exit, err = 1 );
+ }
+
+ // Fill in the allow-query keys
+
+ for ( elem = zoneSpec->allowQuery; elem; elem = elem->next )
+ {
+ mDNSBool found = mDNSfalse;
+
+ for ( keySpec = g_keys; keySpec; keySpec = keySpec->next )
+ {
+ if ( strcmp( elem->string, keySpec->name ) == 0 )
+ {
+ DomainAuthInfo * authInfo = malloc( sizeof( DomainAuthInfo ) );
+ mDNSs32 keylen;
+ require_action( authInfo, exit, err = 1 );
+ memset( authInfo, 0, sizeof( DomainAuthInfo ) );
+
+ ok = MakeDomainNameFromDNSNameString( &authInfo->keyname, keySpec->name );
+ if (!ok) { free(authInfo); err = 1; goto exit; }
+
+ keylen = DNSDigest_ConstructHMACKeyfromBase64( authInfo, keySpec->secret );
+ if (keylen < 0) { free(authInfo); err = 1; goto exit; }
+
+ authInfo->next = zone->queryKeys;
+ zone->queryKeys = authInfo;
+
+ found = mDNStrue;
+
+ break;
+ }
+ }
+
+ // Log this
+ require_action( found, exit, err = 1 );
+ }
+ }
+
+exit:
+
+ return err;
+ }
+
+
+void
+SetupOptions
+ (
+ OptionsInfo * info,
+ void * context
+ )
+ {
+ DaemonInfo * d = ( DaemonInfo* ) context;
+
+ if ( strlen( info->source_address ) )
+ {
+ inet_pton( AF_INET, info->source_address, &d->addr.sin_addr );
+ }
+
+ if ( info->source_port )
+ {
+ d->addr.sin_port = htons( ( mDNSu16 ) info->source_port );
+ }
+
+ if ( strlen( info->server_address ) )
+ {
+ inet_pton( AF_INET, info->server_address, &d->ns_addr.sin_addr );
+ }
+
+ if ( info->server_port )
+ {
+ d->ns_addr.sin_port = htons( ( mDNSu16 ) info->server_port );
+ }
+
+ if ( info->private_port )
+ {
+ d->private_port = mDNSOpaque16fromIntVal( info->private_port );
+ }
+
+ if ( info->llq_port )
+ {
+ d->llq_port = mDNSOpaque16fromIntVal( info->llq_port );
+ }
+ }
diff --git a/mDNSResponder/mDNSShared/dnssd_clientlib.c b/mDNSResponder/mDNSShared/dnssd_clientlib.c
new file mode 100644
index 00000000..cca58853
--- /dev/null
+++ b/mDNSResponder/mDNSShared/dnssd_clientlib.c
@@ -0,0 +1,366 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004, Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "dns_sd.h"
+
+#if MDNS_BUILDINGSHAREDLIBRARY || MDNS_BUILDINGSTUBLIBRARY
+#pragma export on
+#endif
+
+#if defined(_WIN32)
+// disable warning "conversion from <data> to uint16_t"
+#pragma warning(disable:4244)
+#define strncasecmp _strnicmp
+#define strcasecmp _stricmp
+#endif
+
+/*********************************************************************************************
+*
+* Supporting Functions
+*
+*********************************************************************************************/
+
+#define mDNSIsDigit(X) ((X) >= '0' && (X) <= '9')
+
+// DomainEndsInDot returns 1 if name ends with a dot, 0 otherwise
+// (DNSServiceConstructFullName depends this returning 1 for true, rather than any non-zero value meaning true)
+
+static int DomainEndsInDot(const char *dom)
+{
+ while (dom[0] && dom[1])
+ {
+ if (dom[0] == '\\') // advance past escaped byte sequence
+ {
+ if (mDNSIsDigit(dom[1]) && mDNSIsDigit(dom[2]) && mDNSIsDigit(dom[3]))
+ dom += 4; // If "\ddd" then skip four
+ else dom += 2; // else if "\x" then skip two
+ }
+ else dom++; // else goto next character
+ }
+ return (dom[0] == '.');
+}
+
+static uint8_t *InternalTXTRecordSearch
+(
+ uint16_t txtLen,
+ const void *txtRecord,
+ const char *key,
+ unsigned long *keylen
+)
+{
+ uint8_t *p = (uint8_t*)txtRecord;
+ uint8_t *e = p + txtLen;
+ *keylen = (unsigned long) strlen(key);
+ while (p<e)
+ {
+ uint8_t *x = p;
+ p += 1 + p[0];
+ if (p <= e && *keylen <= x[0] && !strncasecmp(key, (char*)x+1, *keylen))
+ if (*keylen == x[0] || x[1+*keylen] == '=') return(x);
+ }
+ return(NULL);
+}
+
+/*********************************************************************************************
+*
+* General Utility Functions
+*
+*********************************************************************************************/
+
+// Note: Need to make sure we don't write more than kDNSServiceMaxDomainName (1009) bytes to fullName
+// In earlier builds this constant was defined to be 1005, so to avoid buffer overruns on clients
+// compiled with that constant we'll actually limit the output to 1005 bytes.
+
+DNSServiceErrorType DNSSD_API DNSServiceConstructFullName
+(
+ char *const fullName,
+ const char *const service, // May be NULL
+ const char *const regtype,
+ const char *const domain
+)
+{
+ const size_t len = !regtype ? 0 : strlen(regtype) - DomainEndsInDot(regtype);
+ char *fn = fullName;
+ char *const lim = fullName + 1005;
+ const char *s = service;
+ const char *r = regtype;
+ const char *d = domain;
+
+ // regtype must be at least "x._udp" or "x._tcp"
+ if (len < 6 || !domain || !domain[0]) return kDNSServiceErr_BadParam;
+ if (strncasecmp((regtype + len - 4), "_tcp", 4) && strncasecmp((regtype + len - 4), "_udp", 4)) return kDNSServiceErr_BadParam;
+
+ if (service && *service)
+ {
+ while (*s)
+ {
+ unsigned char c = *s++; // Needs to be unsigned, or values like 0xFF will be interpreted as < 32
+ if (c <= ' ') // Escape non-printable characters
+ {
+ if (fn+4 >= lim) goto fail;
+ *fn++ = '\\';
+ *fn++ = '0' + (c / 100);
+ *fn++ = '0' + (c / 10) % 10;
+ c = '0' + (c ) % 10;
+ }
+ else if (c == '.' || (c == '\\')) // Escape dot and backslash literals
+ {
+ if (fn+2 >= lim) goto fail;
+ *fn++ = '\\';
+ }
+ else
+ if (fn+1 >= lim) goto fail;
+ *fn++ = (char)c;
+ }
+ *fn++ = '.';
+ }
+
+ while (*r) if (fn+1 >= lim) goto fail;else *fn++ = *r++;
+ if (!DomainEndsInDot(regtype)) { if (fn+1 >= lim) goto fail;else *fn++ = '.';}
+
+ while (*d) if (fn+1 >= lim) goto fail;else *fn++ = *d++;
+ if (!DomainEndsInDot(domain)) { if (fn+1 >= lim) goto fail;else *fn++ = '.';}
+
+ *fn = '\0';
+ return kDNSServiceErr_NoError;
+
+fail:
+ *fn = '\0';
+ return kDNSServiceErr_BadParam;
+}
+
+/*********************************************************************************************
+*
+* TXT Record Construction Functions
+*
+*********************************************************************************************/
+
+typedef struct _TXTRecordRefRealType
+{
+ uint8_t *buffer; // Pointer to data
+ uint16_t buflen; // Length of buffer
+ uint16_t datalen; // Length currently in use
+ uint16_t malloced; // Non-zero if buffer was allocated via malloc()
+} TXTRecordRefRealType;
+
+#define txtRec ((TXTRecordRefRealType*)txtRecord)
+
+// The opaque storage defined in the public dns_sd.h header is 16 bytes;
+// make sure we don't exceed that.
+struct CompileTimeAssertionCheck_dnssd_clientlib
+{
+ char assert0[(sizeof(TXTRecordRefRealType) <= 16) ? 1 : -1];
+};
+
+void DNSSD_API TXTRecordCreate
+(
+ TXTRecordRef *txtRecord,
+ uint16_t bufferLen,
+ void *buffer
+)
+{
+ txtRec->buffer = buffer;
+ txtRec->buflen = buffer ? bufferLen : (uint16_t)0;
+ txtRec->datalen = 0;
+ txtRec->malloced = 0;
+}
+
+void DNSSD_API TXTRecordDeallocate(TXTRecordRef *txtRecord)
+{
+ if (txtRec->malloced) free(txtRec->buffer);
+}
+
+DNSServiceErrorType DNSSD_API TXTRecordSetValue
+(
+ TXTRecordRef *txtRecord,
+ const char *key,
+ uint8_t valueSize,
+ const void *value
+)
+{
+ uint8_t *start, *p;
+ const char *k;
+ unsigned long keysize, keyvalsize;
+
+ for (k = key; *k; k++) if (*k < 0x20 || *k > 0x7E || *k == '=') return(kDNSServiceErr_Invalid);
+ keysize = (unsigned long)(k - key);
+ keyvalsize = 1 + keysize + (value ? (1 + valueSize) : 0);
+ if (keysize < 1 || keyvalsize > 255) return(kDNSServiceErr_Invalid);
+ (void)TXTRecordRemoveValue(txtRecord, key);
+ if (txtRec->datalen + keyvalsize > txtRec->buflen)
+ {
+ unsigned char *newbuf;
+ unsigned long newlen = txtRec->datalen + keyvalsize;
+ if (newlen > 0xFFFF) return(kDNSServiceErr_Invalid);
+ newbuf = malloc((size_t)newlen);
+ if (!newbuf) return(kDNSServiceErr_NoMemory);
+ memcpy(newbuf, txtRec->buffer, txtRec->datalen);
+ if (txtRec->malloced) free(txtRec->buffer);
+ txtRec->buffer = newbuf;
+ txtRec->buflen = (uint16_t)(newlen);
+ txtRec->malloced = 1;
+ }
+ start = txtRec->buffer + txtRec->datalen;
+ p = start + 1;
+ memcpy(p, key, keysize);
+ p += keysize;
+ if (value)
+ {
+ *p++ = '=';
+ memcpy(p, value, valueSize);
+ p += valueSize;
+ }
+ *start = (uint8_t)(p - start - 1);
+ txtRec->datalen += p - start;
+ return(kDNSServiceErr_NoError);
+}
+
+DNSServiceErrorType DNSSD_API TXTRecordRemoveValue
+(
+ TXTRecordRef *txtRecord,
+ const char *key
+)
+{
+ unsigned long keylen, itemlen, remainder;
+ uint8_t *item = InternalTXTRecordSearch(txtRec->datalen, txtRec->buffer, key, &keylen);
+ if (!item) return(kDNSServiceErr_NoSuchKey);
+ itemlen = (unsigned long)(1 + item[0]);
+ remainder = (unsigned long)((txtRec->buffer + txtRec->datalen) - (item + itemlen));
+ // Use memmove because memcpy behaviour is undefined for overlapping regions
+ memmove(item, item + itemlen, remainder);
+ txtRec->datalen -= itemlen;
+ return(kDNSServiceErr_NoError);
+}
+
+uint16_t DNSSD_API TXTRecordGetLength (const TXTRecordRef *txtRecord) { return(txtRec->datalen); }
+const void * DNSSD_API TXTRecordGetBytesPtr(const TXTRecordRef *txtRecord) { return(txtRec->buffer); }
+
+/*********************************************************************************************
+*
+* TXT Record Parsing Functions
+*
+*********************************************************************************************/
+
+int DNSSD_API TXTRecordContainsKey
+(
+ uint16_t txtLen,
+ const void *txtRecord,
+ const char *key
+)
+{
+ unsigned long keylen;
+ return (InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen) ? 1 : 0);
+}
+
+const void * DNSSD_API TXTRecordGetValuePtr
+(
+ uint16_t txtLen,
+ const void *txtRecord,
+ const char *key,
+ uint8_t *valueLen
+)
+{
+ unsigned long keylen;
+ uint8_t *item = InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen);
+ if (!item || item[0] <= keylen) return(NULL); // If key not found, or found with no value, return NULL
+ *valueLen = (uint8_t)(item[0] - (keylen + 1));
+ return (item + 1 + keylen + 1);
+}
+
+uint16_t DNSSD_API TXTRecordGetCount
+(
+ uint16_t txtLen,
+ const void *txtRecord
+)
+{
+ uint16_t count = 0;
+ uint8_t *p = (uint8_t*)txtRecord;
+ uint8_t *e = p + txtLen;
+ while (p<e) { p += 1 + p[0]; count++; }
+ return((p>e) ? (uint16_t)0 : count);
+}
+
+DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex
+(
+ uint16_t txtLen,
+ const void *txtRecord,
+ uint16_t itemIndex,
+ uint16_t keyBufLen,
+ char *key,
+ uint8_t *valueLen,
+ const void **value
+)
+{
+ uint16_t count = 0;
+ uint8_t *p = (uint8_t*)txtRecord;
+ uint8_t *e = p + txtLen;
+ while (p<e && count<itemIndex) { p += 1 + p[0]; count++; } // Find requested item
+ if (p<e && p + 1 + p[0] <= e) // If valid
+ {
+ uint8_t *x = p+1;
+ unsigned long len = 0;
+ e = p + 1 + p[0];
+ while (x+len<e && x[len] != '=') len++;
+ if (len >= keyBufLen) return(kDNSServiceErr_NoMemory);
+ memcpy(key, x, len);
+ key[len] = 0;
+ if (x+len<e) // If we found '='
+ {
+ *value = x + len + 1;
+ *valueLen = (uint8_t)(p[0] - (len + 1));
+ }
+ else
+ {
+ *value = NULL;
+ *valueLen = 0;
+ }
+ return(kDNSServiceErr_NoError);
+ }
+ return(kDNSServiceErr_Invalid);
+}
+
+/*********************************************************************************************
+*
+* SCCS-compatible version string
+*
+*********************************************************************************************/
+
+// For convenience when using the "strings" command, this is the last thing in the file
+
+// Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
+// e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
+// To expand "version" to its value before making the string, use STRINGIFY(version) instead
+#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) # s
+#define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
+
+// NOT static -- otherwise the compiler may optimize it out
+// The "@(#) " pattern is a special prefix the "what" command looks for
+const char VersionString_SCCS_libdnssd[] = "@(#) libdns_sd " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
diff --git a/mDNSResponder/mDNSShared/dnssd_clientshim.c b/mDNSResponder/mDNSShared/dnssd_clientshim.c
new file mode 100644
index 00000000..cb143104
--- /dev/null
+++ b/mDNSResponder/mDNSShared/dnssd_clientshim.c
@@ -0,0 +1,811 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+
+ * This file defines a simple shim layer between a client calling the "/usr/include/dns_sd.h" APIs
+ * and an implementation of mDNSCore ("mDNSEmbeddedAPI.h" APIs) in the same address space.
+ * When the client calls a dns_sd.h function, the shim calls the corresponding mDNSEmbeddedAPI.h
+ * function, and when mDNSCore calls the shim's callback, we call through to the client's callback.
+ * The shim is responsible for two main things:
+ * - converting string parameters between C string format and native DNS format,
+ * - and for allocating and freeing memory.
+ */
+
+#include "dns_sd.h" // Defines the interface to the client layer above
+#include "mDNSEmbeddedAPI.h" // The interface we're building on top of
+extern mDNS mDNSStorage; // We need to pass the address of this storage to the lower-layer functions
+
+#if MDNS_BUILDINGSHAREDLIBRARY || MDNS_BUILDINGSTUBLIBRARY
+#pragma export on
+#endif
+
+//*************************************************************************************************************
+// General Utility Functions
+
+// All mDNS_DirectOP structures start with the pointer to the type-specific disposal function.
+// Optional type-specific data follows these three fields
+// When the client starts an operation, we return the address of the corresponding mDNS_DirectOP
+// as the DNSServiceRef for the operation
+// We stash the value in core context fields so we can get it back to recover our state in our callbacks,
+// and pass it though to the client for it to recover its state
+
+typedef struct mDNS_DirectOP_struct mDNS_DirectOP;
+typedef void mDNS_DirectOP_Dispose (mDNS_DirectOP *op);
+struct mDNS_DirectOP_struct
+{
+ mDNS_DirectOP_Dispose *disposefn;
+};
+
+typedef struct
+{
+ mDNS_DirectOP_Dispose *disposefn;
+ DNSServiceRegisterReply callback;
+ void *context;
+ mDNSBool autoname; // Set if this name is tied to the Computer Name
+ mDNSBool autorename; // Set if we just got a name conflict and now need to automatically pick a new name
+ domainlabel name;
+ domainname host;
+ ServiceRecordSet s;
+} mDNS_DirectOP_Register;
+
+typedef struct
+{
+ mDNS_DirectOP_Dispose *disposefn;
+ DNSServiceBrowseReply callback;
+ void *context;
+ DNSQuestion q;
+} mDNS_DirectOP_Browse;
+
+typedef struct
+{
+ mDNS_DirectOP_Dispose *disposefn;
+ DNSServiceResolveReply callback;
+ void *context;
+ const ResourceRecord *SRV;
+ const ResourceRecord *TXT;
+ DNSQuestion qSRV;
+ DNSQuestion qTXT;
+} mDNS_DirectOP_Resolve;
+
+typedef struct
+{
+ mDNS_DirectOP_Dispose *disposefn;
+ DNSServiceQueryRecordReply callback;
+ void *context;
+ DNSQuestion q;
+} mDNS_DirectOP_QueryRecord;
+
+int DNSServiceRefSockFD(DNSServiceRef sdRef)
+{
+ (void)sdRef; // Unused
+ return(0);
+}
+
+DNSServiceErrorType DNSServiceProcessResult(DNSServiceRef sdRef)
+{
+ (void)sdRef; // Unused
+ return(kDNSServiceErr_NoError);
+}
+
+void DNSServiceRefDeallocate(DNSServiceRef sdRef)
+{
+ mDNS_DirectOP *op = (mDNS_DirectOP *)sdRef;
+ //LogMsg("DNSServiceRefDeallocate");
+ op->disposefn(op);
+}
+
+//*************************************************************************************************************
+// Domain Enumeration
+
+// Not yet implemented, so don't include in stub library
+// We DO include it in the actual Extension, so that if a later client compiled to use this
+// is run against this Extension, it will get a reasonable error code instead of just
+// failing to launch (Strong Link) or calling an unresolved symbol and crashing (Weak Link)
+#if !MDNS_BUILDINGSTUBLIBRARY
+DNSServiceErrorType DNSServiceEnumerateDomains
+(
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceDomainEnumReply callback,
+ void *context /* may be NULL */
+)
+{
+ (void)sdRef; // Unused
+ (void)flags; // Unused
+ (void)interfaceIndex; // Unused
+ (void)callback; // Unused
+ (void)context; // Unused
+ return(kDNSServiceErr_Unsupported);
+}
+#endif
+
+//*************************************************************************************************************
+// Register Service
+
+mDNSlocal void FreeDNSServiceRegistration(mDNS_DirectOP_Register *x)
+{
+ while (x->s.Extras)
+ {
+ ExtraResourceRecord *extras = x->s.Extras;
+ x->s.Extras = x->s.Extras->next;
+ if (extras->r.resrec.rdata != &extras->r.rdatastorage)
+ mDNSPlatformMemFree(extras->r.resrec.rdata);
+ mDNSPlatformMemFree(extras);
+ }
+
+ if (x->s.RR_TXT.resrec.rdata != &x->s.RR_TXT.rdatastorage)
+ mDNSPlatformMemFree(x->s.RR_TXT.resrec.rdata);
+
+ if (x->s.SubTypes) mDNSPlatformMemFree(x->s.SubTypes);
+
+ mDNSPlatformMemFree(x);
+}
+
+static void DNSServiceRegisterDispose(mDNS_DirectOP *op)
+{
+ mDNS_DirectOP_Register *x = (mDNS_DirectOP_Register*)op;
+ x->autorename = mDNSfalse;
+ // If mDNS_DeregisterService() returns mStatus_NoError, that means that the service was found in the list,
+ // is sending its goodbye packet, and we'll get an mStatus_MemFree message when we can free the memory.
+ // If mDNS_DeregisterService() returns an error, it means that the service had already been removed from
+ // the list, so we should go ahead and free the memory right now
+ if (mDNS_DeregisterService(&mDNSStorage, &x->s) != mStatus_NoError)
+ FreeDNSServiceRegistration(x);
+}
+
+mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result)
+{
+ mDNS_DirectOP_Register *x = (mDNS_DirectOP_Register*)sr->ServiceContext;
+
+ 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];
+ if (!DeconstructServiceName(sr->RR_SRV.resrec.name, &name, &type, &dom)) return;
+ if (!ConvertDomainLabelToCString_unescaped(&name, namestr)) return;
+ if (!ConvertDomainNameToCString(&type, typestr)) return;
+ if (!ConvertDomainNameToCString(&dom, domstr)) return;
+
+ if (result == mStatus_NoError)
+ {
+ if (x->callback)
+ x->callback((DNSServiceRef)x, 0, result, namestr, typestr, domstr, x->context);
+ }
+ else if (result == mStatus_NameConflict)
+ {
+ if (x->autoname) mDNS_RenameAndReregisterService(m, sr, mDNSNULL);
+ else if (x->callback)
+ x->callback((DNSServiceRef)x, 0, result, namestr, typestr, domstr, x->context);
+ }
+ else if (result == mStatus_MemFree)
+ {
+ if (x->autorename)
+ {
+ x->autorename = mDNSfalse;
+ x->name = mDNSStorage.nicelabel;
+ mDNS_RenameAndReregisterService(m, &x->s, &x->name);
+ }
+ else
+ FreeDNSServiceRegistration(x);
+ }
+}
+
+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 notAnIntPort,
+ uint16_t txtLen,
+ const void *txtRecord, /* may be NULL */
+ DNSServiceRegisterReply callback, /* may be NULL */
+ void *context /* may be NULL */
+)
+{
+ mStatus err = mStatus_NoError;
+ const char *errormsg = "Unknown";
+ domainlabel n;
+ domainname t, d, h, srv;
+ mDNSIPPort port;
+ unsigned int size = sizeof(RDataBody);
+ AuthRecord *SubTypes = mDNSNULL;
+ mDNSu32 NumSubTypes = 0;
+ mDNS_DirectOP_Register *x;
+ (void)flags; // Unused
+ (void)interfaceIndex; // Unused
+
+ // Check parameters
+ if (!name) name = "";
+ if (!name[0]) n = mDNSStorage.nicelabel;
+ else if (!MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; }
+ if (!regtype || !*regtype || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; }
+ if (!MakeDomainNameFromDNSNameString(&d, (domain && *domain) ? domain : "local.")) { errormsg = "Bad Domain"; goto badparam; }
+ if (!MakeDomainNameFromDNSNameString(&h, (host && *host ) ? host : "")) { errormsg = "Bad Target Host"; goto badparam; }
+ if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; }
+ port.NotAnInteger = notAnIntPort;
+
+ // Allocate memory, and handle failure
+ if (size < txtLen)
+ size = txtLen;
+ x = (mDNS_DirectOP_Register *)mDNSPlatformMemAllocate(sizeof(*x) - sizeof(RDataBody) + size);
+ if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
+
+ // Set up object
+ x->disposefn = DNSServiceRegisterDispose;
+ x->callback = callback;
+ x->context = context;
+ x->autoname = (!name[0]);
+ x->autorename = mDNSfalse;
+ x->name = n;
+ x->host = h;
+
+ // Do the operation
+ err = mDNS_RegisterService(&mDNSStorage, &x->s,
+ &x->name, &t, &d, // Name, type, domain
+ &x->host, port, // Host and port
+ txtRecord, txtLen, // TXT data, length
+ SubTypes, NumSubTypes, // Subtypes
+ mDNSInterface_Any, // Interface ID
+ RegCallback, x, 0); // Callback, context, flags
+ if (err) { mDNSPlatformMemFree(x); errormsg = "mDNS_RegisterService"; goto fail; }
+
+ // Succeeded: Wrap up and return
+ *sdRef = (DNSServiceRef)x;
+ return(mStatus_NoError);
+
+badparam:
+ err = mStatus_BadParamErr;
+fail:
+ LogMsg("DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%ld)", regtype, domain, errormsg, err);
+ return(err);
+}
+
+//*************************************************************************************************************
+// Add / Update / Remove records from existing Registration
+
+// Not yet implemented, so don't include in stub library
+// We DO include it in the actual Extension, so that if a later client compiled to use this
+// is run against this Extension, it will get a reasonable error code instead of just
+// failing to launch (Strong Link) or calling an unresolved symbol and crashing (Weak Link)
+#if !MDNS_BUILDINGSTUBLIBRARY
+DNSServiceErrorType DNSServiceAddRecord
+(
+ DNSServiceRef sdRef,
+ DNSRecordRef *RecordRef,
+ DNSServiceFlags flags,
+ uint16_t rrtype,
+ uint16_t rdlen,
+ const void *rdata,
+ uint32_t ttl
+)
+{
+ (void)sdRef; // Unused
+ (void)RecordRef; // Unused
+ (void)flags; // Unused
+ (void)rrtype; // Unused
+ (void)rdlen; // Unused
+ (void)rdata; // Unused
+ (void)ttl; // Unused
+ return(kDNSServiceErr_Unsupported);
+}
+
+DNSServiceErrorType DNSServiceUpdateRecord
+(
+ DNSServiceRef sdRef,
+ DNSRecordRef RecordRef, /* may be NULL */
+ DNSServiceFlags flags,
+ uint16_t rdlen,
+ const void *rdata,
+ uint32_t ttl
+)
+{
+ (void)sdRef; // Unused
+ (void)RecordRef; // Unused
+ (void)flags; // Unused
+ (void)rdlen; // Unused
+ (void)rdata; // Unused
+ (void)ttl; // Unused
+ return(kDNSServiceErr_Unsupported);
+}
+
+DNSServiceErrorType DNSServiceRemoveRecord
+(
+ DNSServiceRef sdRef,
+ DNSRecordRef RecordRef,
+ DNSServiceFlags flags
+)
+{
+ (void)sdRef; // Unused
+ (void)RecordRef; // Unused
+ (void)flags; // Unused
+ return(kDNSServiceErr_Unsupported);
+}
+#endif
+
+//*************************************************************************************************************
+// Browse for services
+
+static void DNSServiceBrowseDispose(mDNS_DirectOP *op)
+{
+ mDNS_DirectOP_Browse *x = (mDNS_DirectOP_Browse*)op;
+ //LogMsg("DNSServiceBrowseDispose");
+ mDNS_StopBrowse(&mDNSStorage, &x->q);
+ mDNSPlatformMemFree(x);
+}
+
+mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+{
+ DNSServiceFlags flags = AddRecord ? kDNSServiceFlagsAdd : (DNSServiceFlags)0;
+ domainlabel name;
+ domainname type, domain;
+ char cname[MAX_DOMAIN_LABEL+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL.
+ char ctype[MAX_ESCAPED_DOMAIN_NAME];
+ char cdom [MAX_ESCAPED_DOMAIN_NAME];
+ mDNS_DirectOP_Browse *x = (mDNS_DirectOP_Browse*)question->QuestionContext;
+ (void)m; // Unused
+
+ if (answer->rrtype != kDNSType_PTR)
+ { LogMsg("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer->rrtype); return; }
+
+ if (!DeconstructServiceName(&answer->rdata->u.name, &name, &type, &domain))
+ {
+ LogMsg("FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer",
+ answer->name->c, answer->rdata->u.name.c);
+ return;
+ }
+
+ ConvertDomainLabelToCString_unescaped(&name, cname);
+ ConvertDomainNameToCString(&type, ctype);
+ ConvertDomainNameToCString(&domain, cdom);
+ if (x->callback)
+ x->callback((DNSServiceRef)x, flags, 0, 0, cname, ctype, cdom, x->context);
+}
+
+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 */
+)
+{
+ mStatus err = mStatus_NoError;
+ const char *errormsg = "Unknown";
+ domainname t, d;
+ mDNS_DirectOP_Browse *x;
+ (void)flags; // Unused
+ (void)interfaceIndex; // Unused
+
+ // Check parameters
+ if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Illegal regtype"; goto badparam; }
+ if (!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) { errormsg = "Illegal domain"; goto badparam; }
+
+ // Allocate memory, and handle failure
+ x = (mDNS_DirectOP_Browse *)mDNSPlatformMemAllocate(sizeof(*x));
+ if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
+
+ // Set up object
+ x->disposefn = DNSServiceBrowseDispose;
+ x->callback = callback;
+ x->context = context;
+ x->q.QuestionContext = x;
+
+ // Do the operation
+ err = mDNS_StartBrowse(&mDNSStorage, &x->q, &t, &d, mDNSNULL, mDNSInterface_Any, flags, (flags & kDNSServiceFlagsForceMulticast) != 0, (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0, FoundInstance, x);
+ if (err) { mDNSPlatformMemFree(x); errormsg = "mDNS_StartBrowse"; goto fail; }
+
+ // Succeeded: Wrap up and return
+ *sdRef = (DNSServiceRef)x;
+ return(mStatus_NoError);
+
+badparam:
+ err = mStatus_BadParamErr;
+fail:
+ LogMsg("DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%ld)", regtype, domain, errormsg, err);
+ return(err);
+}
+
+//*************************************************************************************************************
+// Resolve Service Info
+
+static void DNSServiceResolveDispose(mDNS_DirectOP *op)
+{
+ mDNS_DirectOP_Resolve *x = (mDNS_DirectOP_Resolve*)op;
+ if (x->qSRV.ThisQInterval >= 0) mDNS_StopQuery(&mDNSStorage, &x->qSRV);
+ if (x->qTXT.ThisQInterval >= 0) mDNS_StopQuery(&mDNSStorage, &x->qTXT);
+ mDNSPlatformMemFree(x);
+}
+
+mDNSlocal void FoundServiceInfo(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+{
+ mDNS_DirectOP_Resolve *x = (mDNS_DirectOP_Resolve*)question->QuestionContext;
+ (void)m; // Unused
+ if (!AddRecord)
+ {
+ if (answer->rrtype == kDNSType_SRV && x->SRV == answer) x->SRV = mDNSNULL;
+ if (answer->rrtype == kDNSType_TXT && x->TXT == answer) x->TXT = mDNSNULL;
+ }
+ else
+ {
+ if (answer->rrtype == kDNSType_SRV) x->SRV = answer;
+ if (answer->rrtype == kDNSType_TXT) x->TXT = answer;
+ if (x->SRV && x->TXT && x->callback)
+ {
+ char fullname[MAX_ESCAPED_DOMAIN_NAME], targethost[MAX_ESCAPED_DOMAIN_NAME];
+ ConvertDomainNameToCString(answer->name, fullname);
+ ConvertDomainNameToCString(&x->SRV->rdata->u.srv.target, targethost);
+ x->callback((DNSServiceRef)x, 0, 0, kDNSServiceErr_NoError, fullname, targethost,
+ x->SRV->rdata->u.srv.port.NotAnInteger, x->TXT->rdlength, (unsigned char*)x->TXT->rdata->u.txt.c, x->context);
+ }
+ }
+}
+
+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 */
+)
+{
+ mStatus err = mStatus_NoError;
+ const char *errormsg = "Unknown";
+ domainlabel n;
+ domainname t, d, srv;
+ mDNS_DirectOP_Resolve *x;
+
+ (void)flags; // Unused
+ (void)interfaceIndex; // Unused
+
+ // Check parameters
+ if (!name[0] || !MakeDomainLabelFromLiteralString(&n, name )) { errormsg = "Bad Instance Name"; goto badparam; }
+ if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; }
+ if (!domain[0] || !MakeDomainNameFromDNSNameString(&d, domain )) { errormsg = "Bad Domain"; goto badparam; }
+ if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; }
+
+ // Allocate memory, and handle failure
+ x = (mDNS_DirectOP_Resolve *)mDNSPlatformMemAllocate(sizeof(*x));
+ if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
+
+ // Set up object
+ x->disposefn = DNSServiceResolveDispose;
+ x->callback = callback;
+ x->context = context;
+ x->SRV = mDNSNULL;
+ x->TXT = mDNSNULL;
+
+ x->qSRV.ThisQInterval = -1; // So that DNSServiceResolveDispose() knows whether to cancel this question
+ x->qSRV.InterfaceID = mDNSInterface_Any;
+ x->qSRV.flags = 0;
+ x->qSRV.Target = zeroAddr;
+ AssignDomainName(&x->qSRV.qname, &srv);
+ x->qSRV.qtype = kDNSType_SRV;
+ x->qSRV.qclass = kDNSClass_IN;
+ x->qSRV.LongLived = mDNSfalse;
+ x->qSRV.ExpectUnique = mDNStrue;
+ x->qSRV.ForceMCast = mDNSfalse;
+ x->qSRV.ReturnIntermed = mDNSfalse;
+ x->qSRV.SuppressUnusable = mDNSfalse;
+ x->qSRV.SearchListIndex = 0;
+ x->qSRV.AppendSearchDomains = 0;
+ x->qSRV.RetryWithSearchDomains = mDNSfalse;
+ x->qSRV.TimeoutQuestion = 0;
+ x->qSRV.WakeOnResolve = 0;
+ x->qSRV.UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0;
+ x->qSRV.ValidationRequired = 0;
+ x->qSRV.ValidatingResponse = 0;
+ x->qSRV.ProxyQuestion = 0;
+ x->qSRV.qnameOrig = mDNSNULL;
+ x->qSRV.AnonInfo = mDNSNULL;
+ x->qSRV.pid = mDNSPlatformGetPID();
+ x->qSRV.QuestionCallback = FoundServiceInfo;
+ x->qSRV.QuestionContext = x;
+
+ x->qTXT.ThisQInterval = -1; // So that DNSServiceResolveDispose() knows whether to cancel this question
+ x->qTXT.InterfaceID = mDNSInterface_Any;
+ x->qTXT.flags = 0;
+ x->qTXT.Target = zeroAddr;
+ AssignDomainName(&x->qTXT.qname, &srv);
+ x->qTXT.qtype = kDNSType_TXT;
+ x->qTXT.qclass = kDNSClass_IN;
+ x->qTXT.LongLived = mDNSfalse;
+ x->qTXT.ExpectUnique = mDNStrue;
+ x->qTXT.ForceMCast = mDNSfalse;
+ x->qTXT.ReturnIntermed = mDNSfalse;
+ x->qTXT.SuppressUnusable = mDNSfalse;
+ x->qTXT.SearchListIndex = 0;
+ x->qTXT.AppendSearchDomains = 0;
+ x->qTXT.RetryWithSearchDomains = mDNSfalse;
+ x->qTXT.TimeoutQuestion = 0;
+ x->qTXT.WakeOnResolve = 0;
+ x->qTXT.UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0;
+ x->qTXT.ValidationRequired = 0;
+ x->qTXT.ValidatingResponse = 0;
+ x->qTXT.ProxyQuestion = 0;
+ x->qTXT.qnameOrig = mDNSNULL;
+ x->qTXT.AnonInfo = mDNSNULL;
+ x->qTXT.pid = mDNSPlatformGetPID();
+ x->qTXT.QuestionCallback = FoundServiceInfo;
+ x->qTXT.QuestionContext = x;
+
+ err = mDNS_StartQuery(&mDNSStorage, &x->qSRV);
+ if (err) { DNSServiceResolveDispose((mDNS_DirectOP*)x); errormsg = "mDNS_StartQuery qSRV"; goto fail; }
+ err = mDNS_StartQuery(&mDNSStorage, &x->qTXT);
+ if (err) { DNSServiceResolveDispose((mDNS_DirectOP*)x); errormsg = "mDNS_StartQuery qTXT"; goto fail; }
+
+ // Succeeded: Wrap up and return
+ *sdRef = (DNSServiceRef)x;
+ return(mStatus_NoError);
+
+badparam:
+ err = mStatus_BadParamErr;
+fail:
+ LogMsg("DNSServiceResolve(\"%s\", \"%s\", \"%s\") failed: %s (%ld)", name, regtype, domain, errormsg, err);
+ return(err);
+}
+
+//*************************************************************************************************************
+// Connection-oriented calls
+
+// Not yet implemented, so don't include in stub library
+// We DO include it in the actual Extension, so that if a later client compiled to use this
+// is run against this Extension, it will get a reasonable error code instead of just
+// failing to launch (Strong Link) or calling an unresolved symbol and crashing (Weak Link)
+#if !MDNS_BUILDINGSTUBLIBRARY
+DNSServiceErrorType DNSServiceCreateConnection(DNSServiceRef *sdRef)
+{
+ (void)sdRef; // Unused
+ return(kDNSServiceErr_Unsupported);
+}
+
+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 */
+)
+{
+ (void)sdRef; // Unused
+ (void)RecordRef; // Unused
+ (void)flags; // Unused
+ (void)interfaceIndex; // Unused
+ (void)fullname; // Unused
+ (void)rrtype; // Unused
+ (void)rrclass; // Unused
+ (void)rdlen; // Unused
+ (void)rdata; // Unused
+ (void)ttl; // Unused
+ (void)callback; // Unused
+ (void)context; // Unused
+ return(kDNSServiceErr_Unsupported);
+}
+#endif
+
+//*************************************************************************************************************
+// DNSServiceQueryRecord
+
+static void DNSServiceQueryRecordDispose(mDNS_DirectOP *op)
+{
+ mDNS_DirectOP_QueryRecord *x = (mDNS_DirectOP_QueryRecord*)op;
+ if (x->q.ThisQInterval >= 0) mDNS_StopQuery(&mDNSStorage, &x->q);
+ mDNSPlatformMemFree(x);
+}
+
+mDNSlocal void DNSServiceQueryRecordResponse(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+{
+ mDNS_DirectOP_QueryRecord *x = (mDNS_DirectOP_QueryRecord*)question->QuestionContext;
+ char fullname[MAX_ESCAPED_DOMAIN_NAME];
+ (void)m; // Unused
+ ConvertDomainNameToCString(answer->name, fullname);
+ x->callback((DNSServiceRef)x, AddRecord ? kDNSServiceFlagsAdd : (DNSServiceFlags)0, 0, kDNSServiceErr_NoError,
+ fullname, answer->rrtype, answer->rrclass, answer->rdlength, answer->rdata->u.data, answer->rroriginalttl, x->context);
+}
+
+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 */
+)
+{
+ mStatus err = mStatus_NoError;
+ const char *errormsg = "Unknown";
+ mDNS_DirectOP_QueryRecord *x;
+
+ (void)flags; // Unused
+ (void)interfaceIndex; // Unused
+
+ // Allocate memory, and handle failure
+ x = (mDNS_DirectOP_QueryRecord *)mDNSPlatformMemAllocate(sizeof(*x));
+ if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
+
+ // Set up object
+ x->disposefn = DNSServiceQueryRecordDispose;
+ x->callback = callback;
+ x->context = context;
+
+ x->q.ThisQInterval = -1; // So that DNSServiceResolveDispose() knows whether to cancel this question
+ x->q.InterfaceID = mDNSInterface_Any;
+ x->q.flags = flags;
+ x->q.Target = zeroAddr;
+ MakeDomainNameFromDNSNameString(&x->q.qname, fullname);
+ x->q.qtype = rrtype;
+ x->q.qclass = rrclass;
+ x->q.LongLived = (flags & kDNSServiceFlagsLongLivedQuery) != 0;
+ x->q.ExpectUnique = mDNSfalse;
+ x->q.ForceMCast = (flags & kDNSServiceFlagsForceMulticast) != 0;
+ x->q.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
+ x->q.SuppressUnsable = (flags & kDNSServiceFlagsSuppressUnusable) != 0;
+ x->q.SearchListIndex = 0;
+ x->q.AppendSearchDomains = 0;
+ x->q.RetryWithSearchDomains = mDNSfalse;
+ x->q.TimeoutQuestion = 0;
+ x->q.WakeOnResolve = 0;
+ x->q.UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0;
+ x->q.ValidationRequired = 0;
+ x->q.ValidatingResponse = 0;
+ x->q.ProxyQuestion = 0;
+ x->q.qnameOrig = mDNSNULL;
+ x->q.AnonInfo = mDNSNULL;
+ x->q.pid = mDNSPlatformGetPID();
+ x->q.QuestionCallback = DNSServiceQueryRecordResponse;
+ x->q.QuestionContext = x;
+
+ err = mDNS_StartQuery(&mDNSStorage, &x->q);
+ if (err) { DNSServiceResolveDispose((mDNS_DirectOP*)x); errormsg = "mDNS_StartQuery"; goto fail; }
+
+ // Succeeded: Wrap up and return
+ *sdRef = (DNSServiceRef)x;
+ return(mStatus_NoError);
+
+fail:
+ LogMsg("DNSServiceQueryRecord(\"%s\", %d, %d) failed: %s (%ld)", fullname, rrtype, rrclass, errormsg, err);
+ return(err);
+}
+
+//*************************************************************************************************************
+// DNSServiceGetAddrInfo
+
+static void DNSServiceGetAddrInfoDispose(mDNS_DirectOP *op)
+{
+ mDNS_DirectOP_GetAddrInfo *x = (mDNS_DirectOP_GetAddrInfo*)op;
+ if (x->aQuery) DNSServiceRefDeallocate(x->aQuery);
+ mDNSPlatformMemFree(x);
+}
+
+static void DNSSD_API DNSServiceGetAddrInfoResponse(
+ DNSServiceRef inRef,
+ DNSServiceFlags inFlags,
+ uint32_t inInterfaceIndex,
+ DNSServiceErrorType inErrorCode,
+ const char * inFullName,
+ uint16_t inRRType,
+ uint16_t inRRClass,
+ uint16_t inRDLen,
+ const void * inRData,
+ uint32_t inTTL,
+ void * inContext )
+{
+ mDNS_DirectOP_GetAddrInfo * x = (mDNS_DirectOP_GetAddrInfo*)inContext;
+ struct sockaddr_in sa4;
+
+ mDNSPlatformMemZero(&sa4, sizeof(sa4));
+ if (inErrorCode == kDNSServiceErr_NoError && inRRType == kDNSServiceType_A)
+ {
+ sa4.sin_family = AF_INET;
+ mDNSPlatformMemCopy(&sa4.sin_addr.s_addr, inRData, 4);
+ }
+
+ x->callback((DNSServiceRef)x, inFlags, inInterfaceIndex, inErrorCode, inFullName,
+ (const struct sockaddr *) &sa4, inTTL, x->context);
+}
+
+DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo(
+ DNSServiceRef * outRef,
+ DNSServiceFlags inFlags,
+ uint32_t inInterfaceIndex,
+ DNSServiceProtocol inProtocol,
+ const char * inHostName,
+ DNSServiceGetAddrInfoReply inCallback,
+ void * inContext )
+{
+ const char * errormsg = "Unknown";
+ DNSServiceErrorType err;
+ mDNS_DirectOP_GetAddrInfo * x;
+
+ // Allocate memory, and handle failure
+ x = (mDNS_DirectOP_GetAddrInfo *)mDNSPlatformMemAllocate(sizeof(*x));
+ if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
+
+ // Set up object
+ x->disposefn = DNSServiceGetAddrInfoDispose;
+ x->callback = inCallback;
+ x->context = inContext;
+ x->aQuery = mDNSNULL;
+
+ // Start the query.
+ // (It would probably be more efficient to code this using mDNS_StartQuery directly,
+ // instead of wrapping DNSServiceQueryRecord, which then unnecessarily allocates
+ // more memory and then just calls through to mDNS_StartQuery. -- SC June 2010)
+ err = DNSServiceQueryRecord(&x->aQuery, inFlags, inInterfaceIndex, inHostName, kDNSServiceType_A,
+ kDNSServiceClass_IN, DNSServiceGetAddrInfoResponse, x);
+ if (err) { DNSServiceGetAddrInfoDispose((mDNS_DirectOP*)x); errormsg = "DNSServiceQueryRecord"; goto fail; }
+
+ *outRef = (DNSServiceRef)x;
+ return(mStatus_NoError);
+
+fail:
+ LogMsg("DNSServiceGetAddrInfo(\"%s\", %d) failed: %s (%ld)", inHostName, inProtocol, errormsg, err);
+ return(err);
+}
+
+//*************************************************************************************************************
+// DNSServiceReconfirmRecord
+
+// Not yet implemented, so don't include in stub library
+// We DO include it in the actual Extension, so that if a later client compiled to use this
+// is run against this Extension, it will get a reasonable error code instead of just
+// failing to launch (Strong Link) or calling an unresolved symbol and crashing (Weak Link)
+#if !MDNS_BUILDINGSTUBLIBRARY
+DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord
+(
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ const char *fullname,
+ uint16_t rrtype,
+ uint16_t rrclass,
+ uint16_t rdlen,
+ const void *rdata
+)
+{
+ (void)flags; // Unused
+ (void)interfaceIndex; // Unused
+ (void)fullname; // Unused
+ (void)rrtype; // Unused
+ (void)rrclass; // Unused
+ (void)rdlen; // Unused
+ (void)rdata; // Unused
+ return(kDNSServiceErr_Unsupported);
+}
+
+
+#endif // !MDNS_BUILDINGSTUBLIBRARY
diff --git a/mDNSResponder/mDNSShared/dnssd_clientstub.c b/mDNSResponder/mDNSShared/dnssd_clientstub.c
new file mode 100644
index 00000000..d21658fa
--- /dev/null
+++ b/mDNSResponder/mDNSShared/dnssd_clientstub.c
@@ -0,0 +1,2363 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+
+#if APPLE_OSX_mDNSResponder
+#include <mach-o/dyld.h>
+#include <uuid/uuid.h>
+#include <TargetConditionals.h>
+#endif
+
+#include "dnssd_ipc.h"
+
+static int gDaemonErr = kDNSServiceErr_NoError;
+
+#if defined(_WIN32)
+
+ #define _SSIZE_T
+ #include <CommonServices.h>
+ #include <DebugServices.h>
+ #include <winsock2.h>
+ #include <ws2tcpip.h>
+ #include <windows.h>
+ #include <stdarg.h>
+ #include <stdio.h>
+
+ #define sockaddr_mdns sockaddr_in
+ #define AF_MDNS AF_INET
+
+// Disable warning: "'type cast' : from data pointer 'void *' to function pointer"
+ #pragma warning(disable:4055)
+
+// Disable warning: "nonstandard extension, function/data pointer conversion in expression"
+ #pragma warning(disable:4152)
+
+extern BOOL IsSystemServiceDisabled();
+
+ #define sleep(X) Sleep((X) * 1000)
+
+static int g_initWinsock = 0;
+ #define LOG_WARNING kDebugLevelWarning
+ #define LOG_INFO kDebugLevelInfo
+static void syslog( int priority, const char * message, ...)
+{
+ va_list args;
+ int len;
+ char * buffer;
+ DWORD err = WSAGetLastError();
+ (void) priority;
+ va_start( args, message );
+ len = _vscprintf( message, args ) + 1;
+ buffer = malloc( len * sizeof(char) );
+ if ( buffer ) { vsprintf( buffer, message, args ); OutputDebugString( buffer ); free( buffer ); }
+ WSASetLastError( err );
+}
+#else
+
+ #include <sys/fcntl.h> // For O_RDWR etc.
+ #include <sys/time.h>
+ #include <sys/socket.h>
+ #include <syslog.h>
+
+ #define sockaddr_mdns sockaddr_un
+ #define AF_MDNS AF_LOCAL
+
+#endif
+
+// <rdar://problem/4096913> Specifies how many times we'll try and connect to the server.
+
+#define DNSSD_CLIENT_MAXTRIES 4
+
+// Uncomment the line below to use the old error return mechanism of creating a temporary named socket (e.g. in /var/tmp)
+//#define USE_NAMED_ERROR_RETURN_SOCKET 1
+
+// If the UDS client has not received a response from the daemon in 60 secs, it is unlikely to get one
+// Note: Timeout of 3 secs should be sufficient in normal scenarios, but 60 secs is chosen as a safeguard since
+// some clients may come up before mDNSResponder itself after a BOOT and on rare ocassions IOPM/Keychain/D2D calls
+// in mDNSResponder's INIT may take a much longer time to return
+#define DNSSD_CLIENT_TIMEOUT 60
+
+#ifndef CTL_PATH_PREFIX
+#define CTL_PATH_PREFIX "/var/tmp/dnssd_result_socket."
+#endif
+
+typedef struct
+{
+ ipc_msg_hdr ipc_hdr;
+ DNSServiceFlags cb_flags;
+ uint32_t cb_interface;
+ DNSServiceErrorType cb_err;
+} CallbackHeader;
+
+typedef struct _DNSServiceRef_t DNSServiceOp;
+typedef struct _DNSRecordRef_t DNSRecord;
+
+#if !defined(_WIN32)
+typedef struct
+{
+ void *AppCallback; // Client callback function and context
+ void *AppContext;
+} SleepKAContext;
+#endif
+
+// client stub callback to process message from server and deliver results to client application
+typedef void (*ProcessReplyFn)(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *msg, const char *const end);
+
+#define ValidatorBits 0x12345678
+#define DNSServiceRefValid(X) (dnssd_SocketValid((X)->sockfd) && (((X)->sockfd ^ (X)->validator) == ValidatorBits))
+
+// When using kDNSServiceFlagsShareConnection, there is one primary _DNSServiceOp_t, and zero or more subordinates
+// For the primary, the 'next' field points to the first subordinate, and its 'next' field points to the next, and so on.
+// For the primary, the 'primary' field is NULL; for subordinates the 'primary' field points back to the associated primary
+//
+// _DNS_SD_LIBDISPATCH is defined where libdispatch/GCD is available. This does not mean that the application will use the
+// DNSServiceSetDispatchQueue API. Hence any new code guarded with _DNS_SD_LIBDISPATCH should still be backwards compatible.
+struct _DNSServiceRef_t
+{
+ DNSServiceOp *next; // For shared connection
+ DNSServiceOp *primary; // For shared connection
+ dnssd_sock_t sockfd; // Connected socket between client and daemon
+ dnssd_sock_t validator; // Used to detect memory corruption, double disposals, etc.
+ client_context_t uid; // For shared connection requests, each subordinate DNSServiceRef has its own ID,
+ // unique within the scope of the same shared parent DNSServiceRef
+ uint32_t op; // request_op_t or reply_op_t
+ uint32_t max_index; // Largest assigned record index - 0 if no additional records registered
+ uint32_t logcounter; // Counter used to control number of syslog messages we write
+ int *moreptr; // Set while DNSServiceProcessResult working on this particular DNSServiceRef
+ ProcessReplyFn ProcessReply; // Function pointer to the code to handle received messages
+ void *AppCallback; // Client callback function and context
+ void *AppContext;
+ DNSRecord *rec;
+#if _DNS_SD_LIBDISPATCH
+ dispatch_source_t disp_source;
+ dispatch_queue_t disp_queue;
+#endif
+ void *kacontext;
+};
+
+struct _DNSRecordRef_t
+{
+ DNSRecord *recnext;
+ void *AppContext;
+ DNSServiceRegisterRecordReply AppCallback;
+ DNSRecordRef recref;
+ uint32_t record_index; // index is unique to the ServiceDiscoveryRef
+ client_context_t uid; // For demultiplexing multiple DNSServiceRegisterRecord calls
+ DNSServiceOp *sdr;
+};
+
+// Write len bytes. Return 0 on success, -1 on error
+static int write_all(dnssd_sock_t sd, char *buf, size_t len)
+{
+ // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; use an explicit while() loop instead.
+ //if (send(sd, buf, len, MSG_WAITALL) != len) return -1;
+ while (len)
+ {
+ ssize_t num_written = send(sd, buf, (long)len, 0);
+ if (num_written < 0 || (size_t)num_written > len)
+ {
+ // Should never happen. If it does, it indicates some OS bug,
+ // or that the mDNSResponder daemon crashed (which should never happen).
+ #if !defined(__ppc__) && defined(SO_ISDEFUNCT)
+ int defunct;
+ socklen_t dlen = sizeof (defunct);
+ if (getsockopt(sd, SOL_SOCKET, SO_ISDEFUNCT, &defunct, &dlen) < 0)
+ syslog(LOG_WARNING, "dnssd_clientstub write_all: SO_ISDEFUNCT failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno));
+ if (!defunct)
+ syslog(LOG_WARNING, "dnssd_clientstub write_all(%d) failed %ld/%ld %d %s", sd,
+ (long)num_written, (long)len,
+ (num_written < 0) ? dnssd_errno : 0,
+ (num_written < 0) ? dnssd_strerror(dnssd_errno) : "");
+ else
+ syslog(LOG_INFO, "dnssd_clientstub write_all(%d) DEFUNCT", sd);
+ #else
+ syslog(LOG_WARNING, "dnssd_clientstub write_all(%d) failed %ld/%ld %d %s", sd,
+ (long)num_written, (long)len,
+ (num_written < 0) ? dnssd_errno : 0,
+ (num_written < 0) ? dnssd_strerror(dnssd_errno) : "");
+ #endif
+ return -1;
+ }
+ buf += num_written;
+ len -= num_written;
+ }
+ return 0;
+}
+
+enum { read_all_success = 0, read_all_fail = -1, read_all_wouldblock = -2 };
+
+// Read len bytes. Return 0 on success, read_all_fail on error, or read_all_wouldblock for
+static int read_all(dnssd_sock_t sd, char *buf, int len)
+{
+ // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; use an explicit while() loop instead.
+ //if (recv(sd, buf, len, MSG_WAITALL) != len) return -1;
+
+ while (len)
+ {
+ ssize_t num_read = recv(sd, buf, len, 0);
+ // It is valid to get an interrupted system call error e.g., somebody attaching
+ // in a debugger, retry without failing
+ if ((num_read < 0) && (errno == EINTR))
+ {
+ syslog(LOG_INFO, "dnssd_clientstub read_all: EINTR continue");
+ continue;
+ }
+ if ((num_read == 0) || (num_read < 0) || (num_read > len))
+ {
+ int printWarn = 0;
+ int defunct = 0;
+ // Should never happen. If it does, it indicates some OS bug,
+ // or that the mDNSResponder daemon crashed (which should never happen).
+#if defined(WIN32)
+ // <rdar://problem/7481776> Suppress logs for "A non-blocking socket operation
+ // could not be completed immediately"
+ if (WSAGetLastError() != WSAEWOULDBLOCK)
+ printWarn = 1;
+#endif
+#if !defined(__ppc__) && defined(SO_ISDEFUNCT)
+ {
+ socklen_t dlen = sizeof (defunct);
+ if (getsockopt(sd, SOL_SOCKET, SO_ISDEFUNCT, &defunct, &dlen) < 0)
+ syslog(LOG_WARNING, "dnssd_clientstub read_all: SO_ISDEFUNCT failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno));
+ }
+ if (!defunct)
+ printWarn = 1;
+#endif
+ if (printWarn)
+ syslog(LOG_WARNING, "dnssd_clientstub read_all(%d) failed %ld/%ld %d %s", sd,
+ (long)num_read, (long)len,
+ (num_read < 0) ? dnssd_errno : 0,
+ (num_read < 0) ? dnssd_strerror(dnssd_errno) : "");
+ else if (defunct)
+ syslog(LOG_INFO, "dnssd_clientstub read_all(%d) DEFUNCT", sd);
+ return (num_read < 0 && dnssd_errno == dnssd_EWOULDBLOCK) ? read_all_wouldblock : read_all_fail;
+ }
+ buf += num_read;
+ len -= num_read;
+ }
+ return read_all_success;
+}
+
+// Returns 1 if more bytes remain to be read on socket descriptor sd, 0 otherwise
+static int more_bytes(dnssd_sock_t sd)
+{
+ struct timeval tv = { 0, 0 };
+ fd_set readfds;
+ fd_set *fs;
+ int ret;
+
+ if (sd < FD_SETSIZE)
+ {
+ fs = &readfds;
+ FD_ZERO(fs);
+ }
+ else
+ {
+ // Compute the number of integers needed for storing "sd". Internally fd_set is stored
+ // as an array of ints with one bit for each fd and hence we need to compute
+ // the number of ints needed rather than the number of bytes. If "sd" is 32, we need
+ // two ints and not just one.
+ int nfdbits = sizeof (int) * 8;
+ int nints = (sd/nfdbits) + 1;
+ fs = (fd_set *)calloc(nints, sizeof(int));
+ if (fs == NULL)
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub more_bytes: malloc failed");
+ return 0;
+ }
+ }
+ FD_SET(sd, fs);
+ ret = select((int)sd+1, fs, (fd_set*)NULL, (fd_set*)NULL, &tv);
+ if (fs != &readfds)
+ free(fs);
+ return (ret > 0);
+}
+
+// set_waitlimit() implements a timeout using select. It is called from deliver_request() before recv() OR accept()
+// to ensure the UDS clients are not blocked in these system calls indefinitely.
+// Note: Ideally one should never be blocked here, because it indicates either mDNSResponder daemon is not yet up/hung/
+// superbusy/crashed or some other OS bug. For eg: On Windows which suffers from 3rd party software
+// (primarily 3rd party firewall software) interfering with proper functioning of the TCP protocol stack it is possible
+// the next operation on this socket(recv/accept) is blocked since we depend on TCP to communicate with the system service.
+static int set_waitlimit(dnssd_sock_t sock, int timeout)
+{
+ // To prevent stack corruption since select does not work with timeout if fds > FD_SETSIZE(1024)
+ if (!gDaemonErr && sock < FD_SETSIZE)
+ {
+ struct timeval tv;
+ fd_set set;
+
+ FD_ZERO(&set);
+ FD_SET(sock, &set);
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ if (!select((int)(sock + 1), &set, NULL, NULL, &tv))
+ {
+ // Ideally one should never hit this case: See comments before set_waitlimit()
+ syslog(LOG_WARNING, "dnssd_clientstub set_waitlimit:_daemon timed out (%d secs) without any response: Socket %d", timeout, sock);
+ gDaemonErr = kDNSServiceErr_Timeout;
+ }
+ }
+ return gDaemonErr;
+}
+
+/* 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. SeparateReturnSocket should be
+ * non-zero for calls that can't receive an immediate error return value on their primary
+ * socket, and therefore require a separate return path for the error code result.
+ * 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(uint32_t op, size_t *len, char **data_start, int SeparateReturnSocket, DNSServiceOp *ref)
+{
+ char *msg = NULL;
+ ipc_msg_hdr *hdr;
+ int datalen;
+#if !defined(USE_TCP_LOOPBACK)
+ char ctrl_path[64] = ""; // "/var/tmp/dnssd_result_socket.xxxxxxxxxx-xxx-xxxxxx"
+#endif
+
+ if (SeparateReturnSocket)
+ {
+#if defined(USE_TCP_LOOPBACK)
+ *len += 2; // Allocate space for two-byte port number
+#elif defined(USE_NAMED_ERROR_RETURN_SOCKET)
+ struct timeval tv;
+ if (gettimeofday(&tv, NULL) < 0)
+ { syslog(LOG_WARNING, "dnssd_clientstub create_hdr: gettimeofday failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); return NULL; }
+ sprintf(ctrl_path, "%s%d-%.3lx-%.6lu", CTL_PATH_PREFIX, (int)getpid(),
+ (unsigned long)(tv.tv_sec & 0xFFF), (unsigned long)(tv.tv_usec));
+ *len += strlen(ctrl_path) + 1;
+#else
+ *len += 1; // Allocate space for single zero byte (empty C string)
+#endif
+ }
+
+ datalen = (int) *len;
+ *len += sizeof(ipc_msg_hdr);
+
+ // Write message to buffer
+ msg = malloc(*len);
+ if (!msg) { syslog(LOG_WARNING, "dnssd_clientstub create_hdr: malloc failed"); return NULL; }
+
+ memset(msg, 0, *len);
+ hdr = (ipc_msg_hdr *)msg;
+ hdr->version = VERSION;
+ hdr->datalen = datalen;
+ hdr->ipc_flags = 0;
+ hdr->op = op;
+ hdr->client_context = ref->uid;
+ hdr->reg_index = 0;
+ *data_start = msg + sizeof(ipc_msg_hdr);
+#if defined(USE_TCP_LOOPBACK)
+ // Put dummy data in for the port, since we don't know what it is yet.
+ // The data will get filled in before we send the message. This happens in deliver_request().
+ if (SeparateReturnSocket) put_uint16(0, data_start);
+#else
+ if (SeparateReturnSocket) put_string(ctrl_path, data_start);
+#endif
+ return hdr;
+}
+
+static void FreeDNSRecords(DNSServiceOp *sdRef)
+{
+ DNSRecord *rec = sdRef->rec;
+ while (rec)
+ {
+ DNSRecord *next = rec->recnext;
+ free(rec);
+ rec = next;
+ }
+}
+
+static void FreeDNSServiceOp(DNSServiceOp *x)
+{
+ // We don't use our DNSServiceRefValid macro here because if we're cleaning up after a socket() call failed
+ // then sockfd could legitimately contain a failing value (e.g. dnssd_InvalidSocket)
+ if ((x->sockfd ^ x->validator) != ValidatorBits)
+ syslog(LOG_WARNING, "dnssd_clientstub attempt to dispose invalid DNSServiceRef %p %08X %08X", x, x->sockfd, x->validator);
+ else
+ {
+ x->next = NULL;
+ x->primary = NULL;
+ x->sockfd = dnssd_InvalidSocket;
+ x->validator = 0xDDDDDDDD;
+ x->op = request_op_none;
+ x->max_index = 0;
+ x->logcounter = 0;
+ x->moreptr = NULL;
+ x->ProcessReply = NULL;
+ x->AppCallback = NULL;
+ x->AppContext = NULL;
+#if _DNS_SD_LIBDISPATCH
+ if (x->disp_source) dispatch_release(x->disp_source);
+ x->disp_source = NULL;
+ x->disp_queue = NULL;
+#endif
+ // DNSRecords may have been added to subordinate sdRef e.g., DNSServiceRegister/DNSServiceAddRecord
+ // or on the main sdRef e.g., DNSServiceCreateConnection/DNSServiveRegisterRecord. DNSRecords may have
+ // been freed if the application called DNSRemoveRecord
+ FreeDNSRecords(x);
+ if (x->kacontext)
+ {
+ free(x->kacontext);
+ x->kacontext = NULL;
+ }
+ free(x);
+ }
+}
+
+// Return a connected service ref (deallocate with DNSServiceRefDeallocate)
+static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags flags, uint32_t op, ProcessReplyFn ProcessReply, void *AppCallback, void *AppContext)
+{
+ int NumTries = 0;
+
+ dnssd_sockaddr_t saddr;
+ DNSServiceOp *sdr;
+
+ if (!ref)
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub DNSService operation with NULL DNSServiceRef");
+ return kDNSServiceErr_BadParam;
+ }
+
+ if (flags & kDNSServiceFlagsShareConnection)
+ {
+ if (!*ref)
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub kDNSServiceFlagsShareConnection used with NULL DNSServiceRef");
+ return kDNSServiceErr_BadParam;
+ }
+ if (!DNSServiceRefValid(*ref) || ((*ref)->op != connection_request && (*ref)->op != connection_delegate_request) || (*ref)->primary)
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub kDNSServiceFlagsShareConnection used with invalid DNSServiceRef %p %08X %08X op %d",
+ (*ref), (*ref)->sockfd, (*ref)->validator, (*ref)->op);
+ *ref = NULL;
+ return kDNSServiceErr_BadReference;
+ }
+ }
+
+ #if defined(_WIN32)
+ if (!g_initWinsock)
+ {
+ WSADATA wsaData;
+ g_initWinsock = 1;
+ if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) { *ref = NULL; return kDNSServiceErr_ServiceNotRunning; }
+ }
+ // <rdar://problem/4096913> If the system service is disabled, we only want to try to connect once
+ if (IsSystemServiceDisabled())
+ NumTries = DNSSD_CLIENT_MAXTRIES;
+ #endif
+
+ sdr = malloc(sizeof(DNSServiceOp));
+ if (!sdr)
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: malloc failed");
+ *ref = NULL;
+ return kDNSServiceErr_NoMemory;
+ }
+ sdr->next = NULL;
+ sdr->primary = NULL;
+ sdr->sockfd = dnssd_InvalidSocket;
+ sdr->validator = sdr->sockfd ^ ValidatorBits;
+ sdr->op = op;
+ sdr->max_index = 0;
+ sdr->logcounter = 0;
+ sdr->moreptr = NULL;
+ sdr->uid.u32[0] = 0;
+ sdr->uid.u32[1] = 0;
+ sdr->ProcessReply = ProcessReply;
+ sdr->AppCallback = AppCallback;
+ sdr->AppContext = AppContext;
+ sdr->rec = NULL;
+#if _DNS_SD_LIBDISPATCH
+ sdr->disp_source = NULL;
+ sdr->disp_queue = NULL;
+#endif
+ sdr->kacontext = NULL;
+
+ if (flags & kDNSServiceFlagsShareConnection)
+ {
+ DNSServiceOp **p = &(*ref)->next; // Append ourselves to end of primary's list
+ while (*p)
+ p = &(*p)->next;
+ *p = sdr;
+ // Preincrement counter before we use it -- it helps with debugging if we know the all-zeroes ID should never appear
+ if (++(*ref)->uid.u32[0] == 0)
+ ++(*ref)->uid.u32[1]; // In parent DNSServiceOp increment UID counter
+ sdr->primary = *ref; // Set our primary pointer
+ sdr->sockfd = (*ref)->sockfd; // Inherit primary's socket
+ sdr->validator = (*ref)->validator;
+ sdr->uid = (*ref)->uid;
+ //printf("ConnectToServer sharing socket %d\n", sdr->sockfd);
+ }
+ else
+ {
+ #ifdef SO_NOSIGPIPE
+ const unsigned long optval = 1;
+ #endif
+ *ref = NULL;
+ sdr->sockfd = socket(AF_DNSSD, SOCK_STREAM, 0);
+ sdr->validator = sdr->sockfd ^ ValidatorBits;
+ if (!dnssd_SocketValid(sdr->sockfd))
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: socket failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno));
+ FreeDNSServiceOp(sdr);
+ return kDNSServiceErr_NoMemory;
+ }
+ #ifdef SO_NOSIGPIPE
+ // Some environments (e.g. OS X) support turning off SIGPIPE for a socket
+ if (setsockopt(sdr->sockfd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0)
+ syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: SO_NOSIGPIPE failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno));
+ #endif
+ #if defined(USE_TCP_LOOPBACK)
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR);
+ saddr.sin_port = htons(MDNS_TCP_SERVERPORT);
+ #else
+ saddr.sun_family = AF_LOCAL;
+ strcpy(saddr.sun_path, MDNS_UDS_SERVERPATH);
+ #if !defined(__ppc__) && defined(SO_DEFUNCTOK)
+ {
+ int defunct = 1;
+ if (setsockopt(sdr->sockfd, SOL_SOCKET, SO_DEFUNCTOK, &defunct, sizeof(defunct)) < 0)
+ syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: SO_DEFUNCTOK failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno));
+ }
+ #endif
+ #endif
+
+ while (1)
+ {
+ int err = connect(sdr->sockfd, (struct sockaddr *) &saddr, sizeof(saddr));
+ if (!err)
+ break; // If we succeeded, return sdr
+ // If we failed, then it may be because the daemon is still launching.
+ // This can happen for processes that launch early in the boot process, while the
+ // daemon is still coming up. Rather than fail here, we wait 1 sec and try again.
+ // If, after DNSSD_CLIENT_MAXTRIES, we still can't connect to the daemon,
+ // then we give up and return a failure code.
+ if (++NumTries < DNSSD_CLIENT_MAXTRIES)
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: connect()-> No of tries: %d", NumTries);
+ sleep(1); // Sleep a bit, then try again
+ }
+ else
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: connect() failed Socket:%d Err:%d Errno:%d %s",
+ sdr->sockfd, err, dnssd_errno, dnssd_strerror(dnssd_errno));
+ dnssd_close(sdr->sockfd);
+ FreeDNSServiceOp(sdr);
+ return kDNSServiceErr_ServiceNotRunning;
+ }
+ }
+ //printf("ConnectToServer opened socket %d\n", sdr->sockfd);
+ }
+
+ *ref = sdr;
+ return kDNSServiceErr_NoError;
+}
+
+#define deliver_request_bailout(MSG) \
+ do { syslog(LOG_WARNING, "dnssd_clientstub deliver_request: %s failed %d (%s)", (MSG), dnssd_errno, dnssd_strerror(dnssd_errno)); goto cleanup; } while(0)
+
+static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr)
+{
+ uint32_t datalen = hdr->datalen; // We take a copy here because we're going to convert hdr->datalen to network byte order
+ #if defined(USE_TCP_LOOPBACK) || defined(USE_NAMED_ERROR_RETURN_SOCKET)
+ char *const data = (char *)hdr + sizeof(ipc_msg_hdr);
+ #endif
+ dnssd_sock_t listenfd = dnssd_InvalidSocket, errsd = dnssd_InvalidSocket;
+ DNSServiceErrorType err = kDNSServiceErr_Unknown; // Default for the "goto cleanup" cases
+ int MakeSeparateReturnSocket = 0;
+
+ // Note: need to check hdr->op, not sdr->op.
+ // hdr->op contains the code for the specific operation we're currently doing, whereas sdr->op
+ // contains the original parent DNSServiceOp (e.g. for an add_record_request, hdr->op will be
+ // add_record_request but the parent sdr->op will be connection_request or reg_service_request)
+ if (sdr->primary ||
+ hdr->op == reg_record_request || hdr->op == add_record_request || hdr->op == update_record_request || hdr->op == remove_record_request)
+ MakeSeparateReturnSocket = 1;
+
+ if (!DNSServiceRefValid(sdr))
+ {
+ if (hdr)
+ free(hdr);
+ syslog(LOG_WARNING, "dnssd_clientstub deliver_request: invalid DNSServiceRef %p %08X %08X", sdr, sdr->sockfd, sdr->validator);
+ return kDNSServiceErr_BadReference;
+ }
+
+ if (!hdr)
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub deliver_request: !hdr");
+ return kDNSServiceErr_Unknown;
+ }
+
+ if (MakeSeparateReturnSocket)
+ {
+ #if defined(USE_TCP_LOOPBACK)
+ {
+ union { uint16_t s; u_char b[2]; } port;
+ dnssd_sockaddr_t caddr;
+ dnssd_socklen_t len = (dnssd_socklen_t) sizeof(caddr);
+ listenfd = socket(AF_DNSSD, SOCK_STREAM, 0);
+ if (!dnssd_SocketValid(listenfd)) deliver_request_bailout("TCP socket");
+
+ caddr.sin_family = AF_INET;
+ caddr.sin_port = 0;
+ caddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR);
+ if (bind(listenfd, (struct sockaddr*) &caddr, sizeof(caddr)) < 0) deliver_request_bailout("TCP bind");
+ if (getsockname(listenfd, (struct sockaddr*) &caddr, &len) < 0) deliver_request_bailout("TCP getsockname");
+ if (listen(listenfd, 1) < 0) deliver_request_bailout("TCP listen");
+ port.s = caddr.sin_port;
+ data[0] = port.b[0]; // don't switch the byte order, as the
+ data[1] = port.b[1]; // daemon expects it in network byte order
+ }
+ #elif defined(USE_NAMED_ERROR_RETURN_SOCKET)
+ {
+ mode_t mask;
+ int bindresult;
+ dnssd_sockaddr_t caddr;
+ listenfd = socket(AF_DNSSD, SOCK_STREAM, 0);
+ if (!dnssd_SocketValid(listenfd)) deliver_request_bailout("USE_NAMED_ERROR_RETURN_SOCKET socket");
+
+ caddr.sun_family = AF_LOCAL;
+ // According to Stevens (section 3.2), there is no portable way to
+ // determine whether sa_len is defined on a particular platform.
+ #ifndef NOT_HAVE_SA_LEN
+ caddr.sun_len = sizeof(struct sockaddr_un);
+ #endif
+ strcpy(caddr.sun_path, data);
+ mask = umask(0);
+ bindresult = bind(listenfd, (struct sockaddr *)&caddr, sizeof(caddr));
+ umask(mask);
+ if (bindresult < 0) deliver_request_bailout("USE_NAMED_ERROR_RETURN_SOCKET bind");
+ if (listen(listenfd, 1) < 0) deliver_request_bailout("USE_NAMED_ERROR_RETURN_SOCKET listen");
+ }
+ #else
+ {
+ dnssd_sock_t sp[2];
+ if (socketpair(AF_DNSSD, SOCK_STREAM, 0, sp) < 0) deliver_request_bailout("socketpair");
+ else
+ {
+ errsd = sp[0]; // We'll read our four-byte error code from sp[0]
+ listenfd = sp[1]; // We'll send sp[1] to the daemon
+ #if !defined(__ppc__) && defined(SO_DEFUNCTOK)
+ {
+ int defunct = 1;
+ if (setsockopt(errsd, SOL_SOCKET, SO_DEFUNCTOK, &defunct, sizeof(defunct)) < 0)
+ syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: SO_DEFUNCTOK failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno));
+ }
+ #endif
+ }
+ }
+ #endif
+ }
+
+#if !defined(USE_TCP_LOOPBACK) && !defined(USE_NAMED_ERROR_RETURN_SOCKET)
+ // If we're going to make a separate error return socket, and pass it to the daemon
+ // using sendmsg, then we'll hold back one data byte to go with it.
+ // On some versions of Unix (including Leopard) sending a control message without
+ // any associated data does not work reliably -- e.g. one particular issue we ran
+ // into is that if the receiving program is in a kqueue loop waiting to be notified
+ // of the received message, it doesn't get woken up when the control message arrives.
+ if (MakeSeparateReturnSocket || sdr->op == send_bpf)
+ datalen--; // Okay to use sdr->op when checking for op == send_bpf
+#endif
+
+ // At this point, our listening socket is set up and waiting, if necessary, for the daemon to connect back to
+ ConvertHeaderBytes(hdr);
+ //syslog(LOG_WARNING, "dnssd_clientstub deliver_request writing %lu bytes", (unsigned long)(datalen + sizeof(ipc_msg_hdr)));
+ //if (MakeSeparateReturnSocket) syslog(LOG_WARNING, "dnssd_clientstub deliver_request name is %s", data);
+#if TEST_SENDING_ONE_BYTE_AT_A_TIME
+ unsigned int i;
+ for (i=0; i<datalen + sizeof(ipc_msg_hdr); i++)
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub deliver_request writing %d", i);
+ if (write_all(sdr->sockfd, ((char *)hdr)+i, 1) < 0)
+ { syslog(LOG_WARNING, "write_all (byte %u) failed", i); goto cleanup; }
+ usleep(10000);
+ }
+#else
+ if (write_all(sdr->sockfd, (char *)hdr, datalen + sizeof(ipc_msg_hdr)) < 0)
+ {
+ // write_all already prints an error message if there is an error writing to
+ // the socket except for DEFUNCT. Logging here is unnecessary and also wrong
+ // in the case of DEFUNCT sockets
+ syslog(LOG_INFO, "dnssd_clientstub deliver_request ERROR: write_all(%d, %lu bytes) failed",
+ sdr->sockfd, (unsigned long)(datalen + sizeof(ipc_msg_hdr)));
+ goto cleanup;
+ }
+#endif
+
+ if (!MakeSeparateReturnSocket)
+ errsd = sdr->sockfd;
+ if (MakeSeparateReturnSocket || sdr->op == send_bpf) // Okay to use sdr->op when checking for op == send_bpf
+ {
+#if defined(USE_TCP_LOOPBACK) || defined(USE_NAMED_ERROR_RETURN_SOCKET)
+ // At this point we may wait in accept for a few milliseconds waiting for the daemon to connect back to us,
+ // but that's okay -- the daemon should not take more than a few milliseconds to respond.
+ // set_waitlimit() ensures we do not block indefinitely just in case something is wrong
+ dnssd_sockaddr_t daddr;
+ dnssd_socklen_t len = sizeof(daddr);
+ if ((err = set_waitlimit(listenfd, DNSSD_CLIENT_TIMEOUT)) != kDNSServiceErr_NoError)
+ goto cleanup;
+ errsd = accept(listenfd, (struct sockaddr *)&daddr, &len);
+ if (!dnssd_SocketValid(errsd))
+ deliver_request_bailout("accept");
+#else
+
+ struct iovec vec = { ((char *)hdr) + sizeof(ipc_msg_hdr) + datalen, 1 }; // Send the last byte along with the SCM_RIGHTS
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ char cbuf[CMSG_SPACE(4 * sizeof(dnssd_sock_t))];
+
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &vec;
+ msg.msg_iovlen = 1;
+ msg.msg_flags = 0;
+ if (MakeSeparateReturnSocket || sdr->op == send_bpf) // Okay to use sdr->op when checking for op == send_bpf
+ {
+ if (sdr->op == send_bpf)
+ {
+ int i;
+ char p[12]; // Room for "/dev/bpf999" with terminating null
+ for (i=0; i<100; i++)
+ {
+ snprintf(p, sizeof(p), "/dev/bpf%d", i);
+ listenfd = open(p, O_RDWR, 0);
+ //if (dnssd_SocketValid(listenfd)) syslog(LOG_WARNING, "Sending fd %d for %s", listenfd, p);
+ if (!dnssd_SocketValid(listenfd) && dnssd_errno != EBUSY)
+ syslog(LOG_WARNING, "Error opening %s %d (%s)", p, dnssd_errno, dnssd_strerror(dnssd_errno));
+ if (dnssd_SocketValid(listenfd) || dnssd_errno != EBUSY) break;
+ }
+ }
+ msg.msg_control = cbuf;
+ msg.msg_controllen = CMSG_LEN(sizeof(dnssd_sock_t));
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(dnssd_sock_t));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ *((dnssd_sock_t *)CMSG_DATA(cmsg)) = listenfd;
+ }
+
+#if TEST_KQUEUE_CONTROL_MESSAGE_BUG
+ sleep(1);
+#endif
+
+#if DEBUG_64BIT_SCM_RIGHTS
+ syslog(LOG_WARNING, "dnssd_clientstub sendmsg read sd=%d write sd=%d %ld %ld %ld/%ld/%ld/%ld",
+ errsd, listenfd, sizeof(dnssd_sock_t), sizeof(void*),
+ sizeof(struct cmsghdr) + sizeof(dnssd_sock_t),
+ CMSG_LEN(sizeof(dnssd_sock_t)), (long)CMSG_SPACE(sizeof(dnssd_sock_t)),
+ (long)((char*)CMSG_DATA(cmsg) + 4 - cbuf));
+#endif // DEBUG_64BIT_SCM_RIGHTS
+
+ if (sendmsg(sdr->sockfd, &msg, 0) < 0)
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub deliver_request ERROR: sendmsg failed read sd=%d write sd=%d errno %d (%s)",
+ errsd, listenfd, dnssd_errno, dnssd_strerror(dnssd_errno));
+ err = kDNSServiceErr_Incompatible;
+ goto cleanup;
+ }
+
+#if DEBUG_64BIT_SCM_RIGHTS
+ syslog(LOG_WARNING, "dnssd_clientstub sendmsg read sd=%d write sd=%d okay", errsd, listenfd);
+#endif // DEBUG_64BIT_SCM_RIGHTS
+
+#endif
+ // Close our end of the socketpair *before* calling read_all() to get the four-byte error code.
+ // Otherwise, if the daemon closes our socket (or crashes), we will have to wait for a timeout
+ // in read_all() because the socket is not closed (we still have an open reference to it)
+ // Note: listenfd is overwritten in the case of send_bpf above and that will be closed here
+ // for send_bpf operation.
+ dnssd_close(listenfd);
+ listenfd = dnssd_InvalidSocket; // Make sure we don't close it a second time in the cleanup handling below
+ }
+
+ // At this point we may wait in read_all for a few milliseconds waiting for the daemon to send us the error code,
+ // but that's okay -- the daemon should not take more than a few milliseconds to respond.
+ // set_waitlimit() ensures we do not block indefinitely just in case something is wrong
+ if (sdr->op == send_bpf) // Okay to use sdr->op when checking for op == send_bpf
+ err = kDNSServiceErr_NoError;
+ else if ((err = set_waitlimit(errsd, DNSSD_CLIENT_TIMEOUT)) == kDNSServiceErr_NoError)
+ {
+ if (read_all(errsd, (char*)&err, (int)sizeof(err)) < 0)
+ err = kDNSServiceErr_ServiceNotRunning; // On failure read_all will have written a message to syslog for us
+ else
+ err = ntohl(err);
+ }
+ //syslog(LOG_WARNING, "dnssd_clientstub deliver_request: retrieved error code %d", err);
+
+cleanup:
+ if (MakeSeparateReturnSocket)
+ {
+ if (dnssd_SocketValid(listenfd)) dnssd_close(listenfd);
+ if (dnssd_SocketValid(errsd)) dnssd_close(errsd);
+#if defined(USE_NAMED_ERROR_RETURN_SOCKET)
+ // syslog(LOG_WARNING, "dnssd_clientstub deliver_request: removing UDS: %s", data);
+ if (unlink(data) != 0)
+ syslog(LOG_WARNING, "dnssd_clientstub WARNING: unlink(\"%s\") failed errno %d (%s)", data, dnssd_errno, dnssd_strerror(dnssd_errno));
+ // else syslog(LOG_WARNING, "dnssd_clientstub deliver_request: removed UDS: %s", data);
+#endif
+ }
+
+ free(hdr);
+ return err;
+}
+
+int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef)
+{
+ if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefSockFD called with NULL DNSServiceRef"); return dnssd_InvalidSocket; }
+
+ if (!DNSServiceRefValid(sdRef))
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefSockFD called with invalid DNSServiceRef %p %08X %08X",
+ sdRef, sdRef->sockfd, sdRef->validator);
+ return dnssd_InvalidSocket;
+ }
+
+ if (sdRef->primary)
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefSockFD undefined for kDNSServiceFlagsShareConnection subordinate DNSServiceRef %p", sdRef);
+ return dnssd_InvalidSocket;
+ }
+
+ return (int) sdRef->sockfd;
+}
+
+#if _DNS_SD_LIBDISPATCH
+static void CallbackWithError(DNSServiceRef sdRef, DNSServiceErrorType error)
+{
+ DNSServiceOp *sdr = sdRef;
+ DNSServiceOp *sdrNext;
+ DNSRecord *rec;
+ DNSRecord *recnext;
+ int morebytes;
+
+ while (sdr)
+ {
+ // We can't touch the sdr after the callback as it can be deallocated in the callback
+ sdrNext = sdr->next;
+ morebytes = 1;
+ sdr->moreptr = &morebytes;
+ switch (sdr->op)
+ {
+ case resolve_request:
+ if (sdr->AppCallback) ((DNSServiceResolveReply) sdr->AppCallback)(sdr, 0, 0, error, NULL, 0, 0, 0, NULL, sdr->AppContext);
+ break;
+ case query_request:
+ if (sdr->AppCallback) ((DNSServiceQueryRecordReply)sdr->AppCallback)(sdr, 0, 0, error, NULL, 0, 0, 0, NULL, 0, sdr->AppContext);
+ break;
+ case addrinfo_request:
+ if (sdr->AppCallback) ((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, 0, 0, error, NULL, NULL, 0, sdr->AppContext);
+ break;
+ case browse_request:
+ if (sdr->AppCallback) ((DNSServiceBrowseReply) sdr->AppCallback)(sdr, 0, 0, error, NULL, 0, NULL, sdr->AppContext);
+ break;
+ case reg_service_request:
+ if (sdr->AppCallback) ((DNSServiceRegisterReply) sdr->AppCallback)(sdr, 0, error, NULL, 0, NULL, sdr->AppContext);
+ break;
+ case enumeration_request:
+ if (sdr->AppCallback) ((DNSServiceDomainEnumReply) sdr->AppCallback)(sdr, 0, 0, error, NULL, sdr->AppContext);
+ break;
+ case connection_request:
+ case connection_delegate_request:
+ // This means Register Record, walk the list of DNSRecords to do the callback
+ rec = sdr->rec;
+ while (rec)
+ {
+ recnext = rec->recnext;
+ if (rec->AppCallback) ((DNSServiceRegisterRecordReply)rec->AppCallback)(sdr, 0, 0, error, rec->AppContext);
+ // The Callback can call DNSServiceRefDeallocate which in turn frees sdr and all the records.
+ // Detect that and return early
+ if (!morebytes) {syslog(LOG_WARNING, "dnssdclientstub:Record: CallbackwithError morebytes zero"); return;}
+ rec = recnext;
+ }
+ break;
+ case port_mapping_request:
+ if (sdr->AppCallback) ((DNSServiceNATPortMappingReply)sdr->AppCallback)(sdr, 0, 0, error, 0, 0, 0, 0, 0, sdr->AppContext);
+ break;
+ default:
+ syslog(LOG_WARNING, "dnssd_clientstub CallbackWithError called with bad op %d", sdr->op);
+ }
+ // If DNSServiceRefDeallocate was called in the callback, morebytes will be zero. As the sdRef
+ // (and its subordinates) have been freed, we should not proceed further. Note that when we
+ // call the callback with a subordinate sdRef the application can call DNSServiceRefDeallocate
+ // on the main sdRef and DNSServiceRefDeallocate handles this case by walking all the sdRefs and
+ // clears the moreptr so that we can terminate here.
+ //
+ // If DNSServiceRefDeallocate was not called in the callback, then set moreptr to NULL so that
+ // we don't access the stack variable after we return from this function.
+ if (!morebytes) {syslog(LOG_WARNING, "dnssdclientstub:sdRef: CallbackwithError morebytes zero sdr %p", sdr); return;}
+ else {sdr->moreptr = NULL;}
+ sdr = sdrNext;
+ }
+}
+#endif // _DNS_SD_LIBDISPATCH
+
+// 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 DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef)
+{
+ int morebytes = 0;
+
+ if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult called with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; }
+
+ if (!DNSServiceRefValid(sdRef))
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator);
+ return kDNSServiceErr_BadReference;
+ }
+
+ if (sdRef->primary)
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult undefined for kDNSServiceFlagsShareConnection subordinate DNSServiceRef %p", sdRef);
+ return kDNSServiceErr_BadReference;
+ }
+
+ if (!sdRef->ProcessReply)
+ {
+ static int num_logs = 0;
+ if (num_logs < 10) syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult called with DNSServiceRef with no ProcessReply function");
+ if (num_logs < 1000) num_logs++;else sleep(1);
+ return kDNSServiceErr_BadReference;
+ }
+
+ do
+ {
+ CallbackHeader cbh;
+ char *data;
+
+ // return NoError on EWOULDBLOCK. This will handle the case
+ // where a non-blocking socket is told there is data, but it was a false positive.
+ // On error, read_all will write a message to syslog for us, so don't need to duplicate that here
+ // Note: If we want to properly support using non-blocking sockets in the future
+ int result = read_all(sdRef->sockfd, (void *)&cbh.ipc_hdr, sizeof(cbh.ipc_hdr));
+ if (result == read_all_fail)
+ {
+ // Set the ProcessReply to NULL before callback as the sdRef can get deallocated
+ // in the callback.
+ sdRef->ProcessReply = NULL;
+#if _DNS_SD_LIBDISPATCH
+ // Call the callbacks with an error if using the dispatch API, as DNSServiceProcessResult
+ // is not called by the application and hence need to communicate the error. Cancel the
+ // source so that we don't get any more events
+ // Note: read_all fails if we could not read from the daemon which can happen if the
+ // daemon dies or the file descriptor is disconnected (defunct).
+ if (sdRef->disp_source)
+ {
+ dispatch_source_cancel(sdRef->disp_source);
+ dispatch_release(sdRef->disp_source);
+ sdRef->disp_source = NULL;
+ CallbackWithError(sdRef, kDNSServiceErr_ServiceNotRunning);
+ }
+#endif
+ // Don't touch sdRef anymore as it might have been deallocated
+ return kDNSServiceErr_ServiceNotRunning;
+ }
+ else if (result == read_all_wouldblock)
+ {
+ if (morebytes && sdRef->logcounter < 100)
+ {
+ sdRef->logcounter++;
+ syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult error: select indicated data was waiting but read_all returned EWOULDBLOCK");
+ }
+ return kDNSServiceErr_NoError;
+ }
+
+ ConvertHeaderBytes(&cbh.ipc_hdr);
+ if (cbh.ipc_hdr.version != VERSION)
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult daemon version %d does not match client version %d", cbh.ipc_hdr.version, VERSION);
+ sdRef->ProcessReply = NULL;
+ return kDNSServiceErr_Incompatible;
+ }
+
+ data = malloc(cbh.ipc_hdr.datalen);
+ if (!data) return kDNSServiceErr_NoMemory;
+ if (read_all(sdRef->sockfd, data, cbh.ipc_hdr.datalen) < 0) // On error, read_all will write a message to syslog for us
+ {
+ // Set the ProcessReply to NULL before callback as the sdRef can get deallocated
+ // in the callback.
+ sdRef->ProcessReply = NULL;
+#if _DNS_SD_LIBDISPATCH
+ // Call the callbacks with an error if using the dispatch API, as DNSServiceProcessResult
+ // is not called by the application and hence need to communicate the error. Cancel the
+ // source so that we don't get any more events
+ if (sdRef->disp_source)
+ {
+ dispatch_source_cancel(sdRef->disp_source);
+ dispatch_release(sdRef->disp_source);
+ sdRef->disp_source = NULL;
+ CallbackWithError(sdRef, kDNSServiceErr_ServiceNotRunning);
+ }
+#endif
+ // Don't touch sdRef anymore as it might have been deallocated
+ free(data);
+ return kDNSServiceErr_ServiceNotRunning;
+ }
+ else
+ {
+ const char *ptr = data;
+ cbh.cb_flags = get_flags (&ptr, data + cbh.ipc_hdr.datalen);
+ cbh.cb_interface = get_uint32 (&ptr, data + cbh.ipc_hdr.datalen);
+ cbh.cb_err = get_error_code(&ptr, data + cbh.ipc_hdr.datalen);
+
+ // CAUTION: We have to handle the case where the client calls DNSServiceRefDeallocate from within the callback function.
+ // To do this we set moreptr to point to morebytes. If the client does call DNSServiceRefDeallocate(),
+ // then that routine will clear morebytes for us, and cause us to exit our loop.
+ morebytes = more_bytes(sdRef->sockfd);
+ if (morebytes)
+ {
+ cbh.cb_flags |= kDNSServiceFlagsMoreComing;
+ sdRef->moreptr = &morebytes;
+ }
+ if (ptr) sdRef->ProcessReply(sdRef, &cbh, ptr, data + cbh.ipc_hdr.datalen);
+ // Careful code here:
+ // If morebytes is non-zero, that means we set sdRef->moreptr above, and the operation was not
+ // cancelled out from under us, so now we need to clear sdRef->moreptr so we don't leave a stray
+ // dangling pointer pointing to a long-gone stack variable.
+ // If morebytes is zero, then one of two thing happened:
+ // (a) morebytes was 0 above, so we didn't set sdRef->moreptr, so we don't need to clear it
+ // (b) morebytes was 1 above, and we set sdRef->moreptr, but the operation was cancelled (with DNSServiceRefDeallocate()),
+ // so we MUST NOT try to dereference our stale sdRef pointer.
+ if (morebytes) sdRef->moreptr = NULL;
+ }
+ free(data);
+ } while (morebytes);
+
+ return kDNSServiceErr_NoError;
+}
+
+void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef)
+{
+ if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefDeallocate called with NULL DNSServiceRef"); return; }
+
+ if (!DNSServiceRefValid(sdRef)) // Also verifies dnssd_SocketValid(sdRef->sockfd) for us too
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefDeallocate called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator);
+ return;
+ }
+
+ // If we're in the middle of a DNSServiceProcessResult() invocation for this DNSServiceRef, clear its morebytes flag to break it out of its while loop
+ if (sdRef->moreptr) *(sdRef->moreptr) = 0;
+
+ if (sdRef->primary) // If this is a subordinate DNSServiceOp, just send a 'stop' command
+ {
+ DNSServiceOp **p = &sdRef->primary->next;
+ while (*p && *p != sdRef) p = &(*p)->next;
+ if (*p)
+ {
+ char *ptr;
+ size_t len = 0;
+ ipc_msg_hdr *hdr = create_hdr(cancel_request, &len, &ptr, 0, sdRef);
+ if (hdr)
+ {
+ ConvertHeaderBytes(hdr);
+ write_all(sdRef->sockfd, (char *)hdr, len);
+ free(hdr);
+ }
+ *p = sdRef->next;
+ FreeDNSServiceOp(sdRef);
+ }
+ }
+ else // else, make sure to terminate all subordinates as well
+ {
+#if _DNS_SD_LIBDISPATCH
+ // The cancel handler will close the fd if a dispatch source has been set
+ if (sdRef->disp_source)
+ {
+ // By setting the ProcessReply to NULL, we make sure that we never call
+ // the application callbacks ever, after returning from this function. We
+ // assume that DNSServiceRefDeallocate is called from the serial queue
+ // that was passed to DNSServiceSetDispatchQueue. Hence, dispatch_source_cancel
+ // should cancel all the blocks on the queue and hence there should be no more
+ // callbacks when we return from this function. Setting ProcessReply to NULL
+ // provides extra protection.
+ sdRef->ProcessReply = NULL;
+ dispatch_source_cancel(sdRef->disp_source);
+ dispatch_release(sdRef->disp_source);
+ sdRef->disp_source = NULL;
+ }
+ // if disp_queue is set, it means it used the DNSServiceSetDispatchQueue API. In that case,
+ // when the source was cancelled, the fd was closed in the handler. Currently the source
+ // is cancelled only when the mDNSResponder daemon dies
+ else if (!sdRef->disp_queue) dnssd_close(sdRef->sockfd);
+#else
+ dnssd_close(sdRef->sockfd);
+#endif
+ // Free DNSRecords added in DNSRegisterRecord if they have not
+ // been freed in DNSRemoveRecord
+ while (sdRef)
+ {
+ DNSServiceOp *p = sdRef;
+ sdRef = sdRef->next;
+ // When there is an error reading from the daemon e.g., bad fd, CallbackWithError
+ // is called which sets moreptr. It might set the moreptr on a subordinate sdRef
+ // but the application might call DNSServiceRefDeallocate with the main sdRef from
+ // the callback. Hence, when we loop through the subordinate sdRefs, we need
+ // to clear the moreptr so that CallbackWithError can terminate itself instead of
+ // walking through the freed sdRefs.
+ if (p->moreptr) *(p->moreptr) = 0;
+ FreeDNSServiceOp(p);
+ }
+ }
+}
+
+DNSServiceErrorType DNSSD_API DNSServiceGetProperty(const char *property, void *result, uint32_t *size)
+{
+ char *ptr;
+ size_t len = strlen(property) + 1;
+ ipc_msg_hdr *hdr;
+ DNSServiceOp *tmp;
+ uint32_t actualsize;
+
+ DNSServiceErrorType err = ConnectToServer(&tmp, 0, getproperty_request, NULL, NULL, NULL);
+ if (err) return err;
+
+ hdr = create_hdr(getproperty_request, &len, &ptr, 0, tmp);
+ if (!hdr) { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_NoMemory; }
+
+ put_string(property, &ptr);
+ err = deliver_request(hdr, tmp); // Will free hdr for us
+ if (read_all(tmp->sockfd, (char*)&actualsize, (int)sizeof(actualsize)) < 0)
+ { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_ServiceNotRunning; }
+
+ actualsize = ntohl(actualsize);
+ if (read_all(tmp->sockfd, (char*)result, actualsize < *size ? actualsize : *size) < 0)
+ { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_ServiceNotRunning; }
+ DNSServiceRefDeallocate(tmp);
+
+ // Swap version result back to local process byte order
+ if (!strcmp(property, kDNSServiceProperty_DaemonVersion) && *size >= 4)
+ *(uint32_t*)result = ntohl(*(uint32_t*)result);
+
+ *size = actualsize;
+ return kDNSServiceErr_NoError;
+}
+
+DNSServiceErrorType DNSSD_API DNSServiceGetPID(const uint16_t srcport, int32_t *pid)
+{
+ char *ptr;
+ ipc_msg_hdr *hdr;
+ DNSServiceOp *tmp;
+ size_t len = sizeof(int32_t);
+
+ DNSServiceErrorType err = ConnectToServer(&tmp, 0, getpid_request, NULL, NULL, NULL);
+ if (err)
+ return err;
+
+ hdr = create_hdr(getpid_request, &len, &ptr, 0, tmp);
+ if (!hdr)
+ {
+ DNSServiceRefDeallocate(tmp);
+ return kDNSServiceErr_NoMemory;
+ }
+
+ put_uint16(srcport, &ptr);
+ err = deliver_request(hdr, tmp); // Will free hdr for us
+
+ if (read_all(tmp->sockfd, (char*)pid, sizeof(int32_t)) < 0)
+ {
+ DNSServiceRefDeallocate(tmp);
+ return kDNSServiceErr_ServiceNotRunning;
+ }
+
+ DNSServiceRefDeallocate(tmp);
+ return kDNSServiceErr_NoError;
+}
+
+static void handle_resolve_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *end)
+{
+ char fullname[kDNSServiceMaxDomainName];
+ char target[kDNSServiceMaxDomainName];
+ uint16_t txtlen;
+ union { uint16_t s; u_char b[2]; } port;
+ unsigned char *txtrecord;
+
+ get_string(&data, end, fullname, kDNSServiceMaxDomainName);
+ get_string(&data, end, target, kDNSServiceMaxDomainName);
+ if (!data || data + 2 > end) goto fail;
+
+ port.b[0] = *data++;
+ port.b[1] = *data++;
+ txtlen = get_uint16(&data, end);
+ txtrecord = (unsigned char *)get_rdata(&data, end, txtlen);
+
+ if (!data) goto fail;
+ ((DNSServiceResolveReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, fullname, target, port.s, txtlen, txtrecord, sdr->AppContext);
+ return;
+ // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
+fail:
+ syslog(LOG_WARNING, "dnssd_clientstub handle_resolve_response: error reading result from daemon");
+}
+
+#if APPLE_OSX_mDNSResponder
+
+static int32_t libSystemVersion = 0;
+
+// Return true if the application linked against a version of libsystem where P2P
+// interfaces were included by default when using kDNSServiceInterfaceIndexAny.
+// Using 160.0.0 == 0xa00000 as the version threshold.
+static int includeP2PWithIndexAny()
+{
+ if (libSystemVersion == 0)
+ libSystemVersion = NSVersionOfLinkTimeLibrary("System");
+
+ if (libSystemVersion < 0xa00000)
+ return 1;
+ else
+ return 0;
+}
+
+#else // APPLE_OSX_mDNSResponder
+
+// always return false for non Apple platforms
+static int includeP2PWithIndexAny()
+{
+ return 0;
+}
+
+#endif // APPLE_OSX_mDNSResponder
+
+DNSServiceErrorType DNSSD_API DNSServiceResolve
+(
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ const char *name,
+ const char *regtype,
+ const char *domain,
+ DNSServiceResolveReply callBack,
+ void *context
+)
+{
+ char *ptr;
+ size_t len;
+ ipc_msg_hdr *hdr;
+ DNSServiceErrorType err;
+
+ if (!name || !regtype || !domain || !callBack) return kDNSServiceErr_BadParam;
+
+ // Need a real InterfaceID for WakeOnResolve
+ if ((flags & kDNSServiceFlagsWakeOnResolve) != 0 &&
+ ((interfaceIndex == kDNSServiceInterfaceIndexAny) ||
+ (interfaceIndex == kDNSServiceInterfaceIndexLocalOnly) ||
+ (interfaceIndex == kDNSServiceInterfaceIndexUnicast) ||
+ (interfaceIndex == kDNSServiceInterfaceIndexP2P)))
+ {
+ return kDNSServiceErr_BadParam;
+ }
+
+ if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny())
+ flags |= kDNSServiceFlagsIncludeP2P;
+
+ err = ConnectToServer(sdRef, flags, resolve_request, handle_resolve_response, callBack, context);
+ if (err) return err; // On error ConnectToServer leaves *sdRef set to 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, (*sdRef)->primary ? 1 : 0, *sdRef);
+ if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
+
+ put_flags(flags, &ptr);
+ put_uint32(interfaceIndex, &ptr);
+ put_string(name, &ptr);
+ put_string(regtype, &ptr);
+ put_string(domain, &ptr);
+
+ err = deliver_request(hdr, *sdRef); // Will free hdr for us
+ if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
+ return err;
+}
+
+static void handle_query_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end)
+{
+ uint32_t ttl;
+ char name[kDNSServiceMaxDomainName];
+ uint16_t rrtype, rrclass, rdlen;
+ const char *rdata;
+
+ get_string(&data, end, name, kDNSServiceMaxDomainName);
+ rrtype = get_uint16(&data, end);
+ rrclass = get_uint16(&data, end);
+ rdlen = get_uint16(&data, end);
+ rdata = get_rdata(&data, end, rdlen);
+ ttl = get_uint32(&data, end);
+
+ if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_query_response: error reading result from daemon");
+ else ((DNSServiceQueryRecordReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, name, rrtype, rrclass, rdlen, rdata, ttl, sdr->AppContext);
+ // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
+}
+
+DNSServiceErrorType DNSSD_API DNSServiceQueryRecord
+(
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ const char *name,
+ uint16_t rrtype,
+ uint16_t rrclass,
+ DNSServiceQueryRecordReply callBack,
+ void *context
+)
+{
+ char *ptr;
+ size_t len;
+ ipc_msg_hdr *hdr;
+ DNSServiceErrorType err;
+
+ if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny())
+ flags |= kDNSServiceFlagsIncludeP2P;
+
+ err = ConnectToServer(sdRef, flags, query_request, handle_query_response, callBack, context);
+ if (err) return err; // On error ConnectToServer leaves *sdRef set to 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, (*sdRef)->primary ? 1 : 0, *sdRef);
+ if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
+
+ put_flags(flags, &ptr);
+ put_uint32(interfaceIndex, &ptr);
+ put_string(name, &ptr);
+ put_uint16(rrtype, &ptr);
+ put_uint16(rrclass, &ptr);
+
+ err = deliver_request(hdr, *sdRef); // Will free hdr for us
+ if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
+ return err;
+}
+
+static void handle_addrinfo_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end)
+{
+ char hostname[kDNSServiceMaxDomainName];
+ uint16_t rrtype, rrclass, rdlen;
+ const char *rdata;
+ uint32_t ttl;
+
+ get_string(&data, end, hostname, kDNSServiceMaxDomainName);
+ rrtype = get_uint16(&data, end);
+ rrclass = get_uint16(&data, end);
+ rdlen = get_uint16(&data, end);
+ rdata = get_rdata (&data, end, rdlen);
+ ttl = get_uint32(&data, end);
+
+ // We only generate client callbacks for A and AAAA results (including NXDOMAIN results for
+ // those types, if the client has requested those with the kDNSServiceFlagsReturnIntermediates).
+ // Other result types, specifically CNAME referrals, are not communicated to the client, because
+ // the DNSServiceGetAddrInfoReply interface doesn't have any meaningful way to communiate CNAME referrals.
+ if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_addrinfo_response: error reading result from daemon");
+ else if (rrtype == kDNSServiceType_A || rrtype == kDNSServiceType_AAAA)
+ {
+ struct sockaddr_in sa4;
+ struct sockaddr_in6 sa6;
+ const struct sockaddr *const sa = (rrtype == kDNSServiceType_A) ? (struct sockaddr*)&sa4 : (struct sockaddr*)&sa6;
+ if (rrtype == kDNSServiceType_A)
+ {
+ memset(&sa4, 0, sizeof(sa4));
+ #ifndef NOT_HAVE_SA_LEN
+ sa4.sin_len = sizeof(struct sockaddr_in);
+ #endif
+ sa4.sin_family = AF_INET;
+ // sin_port = 0;
+ if (!cbh->cb_err) memcpy(&sa4.sin_addr, rdata, rdlen);
+ }
+ else
+ {
+ memset(&sa6, 0, sizeof(sa6));
+ #ifndef NOT_HAVE_SA_LEN
+ sa6.sin6_len = sizeof(struct sockaddr_in6);
+ #endif
+ sa6.sin6_family = AF_INET6;
+ // sin6_port = 0;
+ // sin6_flowinfo = 0;
+ // sin6_scope_id = 0;
+ if (!cbh->cb_err)
+ {
+ memcpy(&sa6.sin6_addr, rdata, rdlen);
+ if (IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr)) sa6.sin6_scope_id = cbh->cb_interface;
+ }
+ }
+ // Validation results are always delivered separately from the actual results of the
+ // DNSServiceGetAddrInfo. Set the "addr" to NULL as per the documentation.
+ //
+ // Note: If we deliver validation results along with the "addr" in the future, we need
+ // a way to differentiate the negative response from validation-only response as both
+ // has zero address.
+ if (!(cbh->cb_flags & kDNSServiceFlagsValidate))
+ ((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, hostname, sa, ttl, sdr->AppContext);
+ else
+ ((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, hostname, NULL, 0, sdr->AppContext);
+ // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
+ }
+}
+
+DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo
+(
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ uint32_t protocol,
+ const char *hostname,
+ DNSServiceGetAddrInfoReply callBack,
+ void *context /* may be NULL */
+)
+{
+ char *ptr;
+ size_t len;
+ ipc_msg_hdr *hdr;
+ DNSServiceErrorType err;
+
+ if (!hostname) return kDNSServiceErr_BadParam;
+
+ err = ConnectToServer(sdRef, flags, addrinfo_request, handle_addrinfo_response, callBack, context);
+ if (err)
+ {
+ return err; // On error ConnectToServer leaves *sdRef set to NULL
+ }
+
+ // Calculate total message length
+ len = sizeof(flags);
+ len += sizeof(uint32_t); // interfaceIndex
+ len += sizeof(uint32_t); // protocol
+ len += strlen(hostname) + 1;
+
+ hdr = create_hdr(addrinfo_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef);
+ if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
+
+ put_flags(flags, &ptr);
+ put_uint32(interfaceIndex, &ptr);
+ put_uint32(protocol, &ptr);
+ put_string(hostname, &ptr);
+
+ err = deliver_request(hdr, *sdRef); // Will free hdr for us
+ if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
+ return err;
+}
+
+static void handle_browse_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end)
+{
+ char replyName[256], replyType[kDNSServiceMaxDomainName], replyDomain[kDNSServiceMaxDomainName];
+ get_string(&data, end, replyName, 256);
+ get_string(&data, end, replyType, kDNSServiceMaxDomainName);
+ get_string(&data, end, replyDomain, kDNSServiceMaxDomainName);
+ if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_browse_response: error reading result from daemon");
+ else ((DNSServiceBrowseReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, replyName, replyType, replyDomain, sdr->AppContext);
+ // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
+}
+
+DNSServiceErrorType DNSSD_API DNSServiceBrowse
+(
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ const char *regtype,
+ const char *domain,
+ DNSServiceBrowseReply callBack,
+ void *context
+)
+{
+ char *ptr;
+ size_t len;
+ ipc_msg_hdr *hdr;
+ DNSServiceErrorType err;
+
+ if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny())
+ flags |= kDNSServiceFlagsIncludeP2P;
+
+ err = ConnectToServer(sdRef, flags, browse_request, handle_browse_response, callBack, context);
+ if (err) return err; // On error ConnectToServer leaves *sdRef set to 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, (*sdRef)->primary ? 1 : 0, *sdRef);
+ if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
+
+ put_flags(flags, &ptr);
+ put_uint32(interfaceIndex, &ptr);
+ put_string(regtype, &ptr);
+ put_string(domain, &ptr);
+
+ err = deliver_request(hdr, *sdRef); // Will free hdr for us
+ if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
+ return err;
+}
+
+DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser(DNSServiceFlags flags, const char *domain);
+DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser(DNSServiceFlags flags, const char *domain)
+{
+ DNSServiceOp *tmp;
+ char *ptr;
+ size_t len = sizeof(flags) + strlen(domain) + 1;
+ ipc_msg_hdr *hdr;
+ DNSServiceErrorType err = ConnectToServer(&tmp, 0, setdomain_request, NULL, NULL, NULL);
+ if (err) return err;
+
+ hdr = create_hdr(setdomain_request, &len, &ptr, 0, tmp);
+ if (!hdr) { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_NoMemory; }
+
+ put_flags(flags, &ptr);
+ put_string(domain, &ptr);
+ err = deliver_request(hdr, tmp); // Will free hdr for us
+ DNSServiceRefDeallocate(tmp);
+ return err;
+}
+
+static void handle_regservice_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end)
+{
+ char name[256], regtype[kDNSServiceMaxDomainName], domain[kDNSServiceMaxDomainName];
+ get_string(&data, end, name, 256);
+ get_string(&data, end, regtype, kDNSServiceMaxDomainName);
+ get_string(&data, end, domain, kDNSServiceMaxDomainName);
+ if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_regservice_response: error reading result from daemon");
+ else ((DNSServiceRegisterReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_err, name, regtype, domain, sdr->AppContext);
+ // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
+}
+
+DNSServiceErrorType DNSSD_API DNSServiceRegister
+(
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ const char *name,
+ const char *regtype,
+ const char *domain,
+ const char *host,
+ uint16_t PortInNetworkByteOrder,
+ uint16_t txtLen,
+ const void *txtRecord,
+ DNSServiceRegisterReply callBack,
+ void *context
+)
+{
+ char *ptr;
+ size_t len;
+ ipc_msg_hdr *hdr;
+ DNSServiceErrorType err;
+ union { uint16_t s; u_char b[2]; } port = { PortInNetworkByteOrder };
+
+ if (!name) name = "";
+ if (!regtype) return kDNSServiceErr_BadParam;
+ if (!domain) domain = "";
+ if (!host) host = "";
+ if (!txtRecord) txtRecord = (void*)"";
+
+ // No callback must have auto-rename
+ if (!callBack && (flags & kDNSServiceFlagsNoAutoRename)) return kDNSServiceErr_BadParam;
+
+ if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny())
+ flags |= kDNSServiceFlagsIncludeP2P;
+
+ err = ConnectToServer(sdRef, flags, reg_service_request, callBack ? handle_regservice_response : NULL, callBack, context);
+ if (err) return err; // On error ConnectToServer leaves *sdRef set to NULL
+
+ 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, (*sdRef)->primary ? 1 : 0, *sdRef);
+ if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
+
+ // If it is going over a shared connection, then don't set the IPC_FLAGS_NOREPLY
+ // as it affects all the operations over the shared connection. This is not
+ // a normal case and hence receiving the response back from the daemon and
+ // discarding it in ConnectionResponse is okay.
+
+ if (!(flags & kDNSServiceFlagsShareConnection) && !callBack) hdr->ipc_flags |= IPC_FLAGS_NOREPLY;
+
+ put_flags(flags, &ptr);
+ put_uint32(interfaceIndex, &ptr);
+ put_string(name, &ptr);
+ put_string(regtype, &ptr);
+ put_string(domain, &ptr);
+ put_string(host, &ptr);
+ *ptr++ = port.b[0];
+ *ptr++ = port.b[1];
+ put_uint16(txtLen, &ptr);
+ put_rdata(txtLen, txtRecord, &ptr);
+
+ err = deliver_request(hdr, *sdRef); // Will free hdr for us
+ if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
+ return err;
+}
+
+static void handle_enumeration_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end)
+{
+ char domain[kDNSServiceMaxDomainName];
+ get_string(&data, end, domain, kDNSServiceMaxDomainName);
+ if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_enumeration_response: error reading result from daemon");
+ else ((DNSServiceDomainEnumReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, domain, sdr->AppContext);
+ // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
+}
+
+DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains
+(
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceDomainEnumReply callBack,
+ void *context
+)
+{
+ char *ptr;
+ size_t len;
+ ipc_msg_hdr *hdr;
+ DNSServiceErrorType err;
+
+ int f1 = (flags & kDNSServiceFlagsBrowseDomains) != 0;
+ int f2 = (flags & kDNSServiceFlagsRegistrationDomains) != 0;
+ if (f1 + f2 != 1) return kDNSServiceErr_BadParam;
+
+ err = ConnectToServer(sdRef, flags, enumeration_request, handle_enumeration_response, callBack, context);
+ if (err) return err; // On error ConnectToServer leaves *sdRef set to NULL
+
+ len = sizeof(DNSServiceFlags);
+ len += sizeof(uint32_t);
+
+ hdr = create_hdr(enumeration_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef);
+ if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
+
+ put_flags(flags, &ptr);
+ put_uint32(interfaceIndex, &ptr);
+
+ err = deliver_request(hdr, *sdRef); // Will free hdr for us
+ if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
+ return err;
+}
+
+static void ConnectionResponse(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *const data, const char *const end)
+{
+ (void)data; // Unused
+
+ //printf("ConnectionResponse got %d\n", cbh->ipc_hdr.op);
+ if (cbh->ipc_hdr.op != reg_record_reply_op)
+ {
+ // When using kDNSServiceFlagsShareConnection, need to search the list of associated DNSServiceOps
+ // to find the one this response is intended for, and then call through to its ProcessReply handler.
+ // We start with our first subordinate DNSServiceRef -- don't want to accidentally match the parent DNSServiceRef.
+ DNSServiceOp *op = sdr->next;
+ while (op && (op->uid.u32[0] != cbh->ipc_hdr.client_context.u32[0] || op->uid.u32[1] != cbh->ipc_hdr.client_context.u32[1]))
+ op = op->next;
+ // Note: We may sometimes not find a matching DNSServiceOp, in the case where the client has
+ // cancelled the subordinate DNSServiceOp, but there are still messages in the pipeline from the daemon
+ if (op && op->ProcessReply) op->ProcessReply(op, cbh, data, end);
+ // WARNING: Don't touch op or sdr after this -- client may have called DNSServiceRefDeallocate
+ return;
+ }
+ else
+ {
+ DNSRecordRef rec;
+ for (rec = sdr->rec; rec; rec = rec->recnext)
+ {
+ if (rec->uid.u32[0] == cbh->ipc_hdr.client_context.u32[0] && rec->uid.u32[1] == cbh->ipc_hdr.client_context.u32[1])
+ break;
+ }
+ // The record might have been freed already and hence not an
+ // error if the record is not found.
+ if (!rec)
+ {
+ syslog(LOG_INFO, "ConnectionResponse: Record not found");
+ return;
+ }
+ if (rec->sdr != sdr)
+ {
+ syslog(LOG_WARNING, "ConnectionResponse: Record sdr mismatch: rec %p sdr %p", rec->sdr, sdr);
+ return;
+ }
+
+ if (sdr->op == connection_request || sdr->op == connection_delegate_request)
+ {
+ rec->AppCallback(rec->sdr, rec, cbh->cb_flags, cbh->cb_err, rec->AppContext);
+ }
+ else
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub ConnectionResponse: sdr->op != connection_request");
+ rec->AppCallback(rec->sdr, rec, 0, kDNSServiceErr_Unknown, rec->AppContext);
+ }
+ // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
+ }
+}
+
+DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef)
+{
+ char *ptr;
+ size_t len = 0;
+ ipc_msg_hdr *hdr;
+ DNSServiceErrorType err = ConnectToServer(sdRef, 0, connection_request, ConnectionResponse, NULL, NULL);
+ if (err) return err; // On error ConnectToServer leaves *sdRef set to NULL
+
+ hdr = create_hdr(connection_request, &len, &ptr, 0, *sdRef);
+ if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
+
+ err = deliver_request(hdr, *sdRef); // Will free hdr for us
+ if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
+ return err;
+}
+
+#if APPLE_OSX_mDNSResponder && !TARGET_IPHONE_SIMULATOR
+DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid)
+{
+ char *ptr;
+ size_t len = 0;
+ ipc_msg_hdr *hdr;
+
+ DNSServiceErrorType err = ConnectToServer(sdRef, 0, connection_delegate_request, ConnectionResponse, NULL, NULL);
+ if (err)
+ {
+ return err; // On error ConnectToServer leaves *sdRef set to NULL
+ }
+
+ // Only one of the two options can be set. If pid is zero, uuid is used.
+ // If both are specified only pid will be used. We send across the pid
+ // so that the daemon knows what to read from the socket.
+
+ len += sizeof(int32_t);
+
+ hdr = create_hdr(connection_delegate_request, &len, &ptr, 0, *sdRef);
+ if (!hdr)
+ {
+ DNSServiceRefDeallocate(*sdRef);
+ *sdRef = NULL;
+ return kDNSServiceErr_NoMemory;
+ }
+
+ if (pid && setsockopt((*sdRef)->sockfd, SOL_SOCKET, SO_DELEGATED, &pid, sizeof(pid)) == -1)
+ {
+ // Free the hdr in case we return before calling deliver_request()
+ if (hdr)
+ free(hdr);
+ DNSServiceRefDeallocate(*sdRef);
+ *sdRef = NULL;
+ return kDNSServiceErr_NoAuth;
+ }
+
+ if (!pid && setsockopt((*sdRef)->sockfd, SOL_SOCKET, SO_DELEGATED_UUID, uuid, sizeof(uuid_t)) == -1)
+ {
+ // Free the hdr in case we return before calling deliver_request()
+ if (hdr)
+ free(hdr);
+ DNSServiceRefDeallocate(*sdRef);
+ *sdRef = NULL;
+ return kDNSServiceErr_NoAuth;
+ }
+
+ put_uint32(pid, &ptr);
+
+ err = deliver_request(hdr, *sdRef); // Will free hdr for us
+ if (err)
+ {
+ DNSServiceRefDeallocate(*sdRef);
+ *sdRef = NULL;
+ }
+ return err;
+}
+#elif TARGET_IPHONE_SIMULATOR // This hack is for Simulator platform only
+DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid)
+{
+ (void) pid;
+ (void) uuid;
+ return DNSServiceCreateConnection(sdRef);
+}
+#endif
+
+DNSServiceErrorType DNSSD_API 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
+)
+{
+ char *ptr;
+ size_t len;
+ ipc_msg_hdr *hdr = NULL;
+ DNSRecordRef rref = NULL;
+ DNSRecord **p;
+ int f1 = (flags & kDNSServiceFlagsShared) != 0;
+ int f2 = (flags & kDNSServiceFlagsUnique) != 0;
+ if (f1 + f2 != 1) return kDNSServiceErr_BadParam;
+
+ if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny())
+ flags |= kDNSServiceFlagsIncludeP2P;
+
+ if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRegisterRecord called with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; }
+
+ if (!DNSServiceRefValid(sdRef))
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRegisterRecord called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator);
+ return kDNSServiceErr_BadReference;
+ }
+
+ if (sdRef->op != connection_request && sdRef->op != connection_delegate_request)
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRegisterRecord called with non-DNSServiceCreateConnection DNSServiceRef %p %d", sdRef, sdRef->op);
+ 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;
+
+ // Bump up the uid. Normally for shared operations (kDNSServiceFlagsShareConnection), this
+ // is done in ConnectToServer. For DNSServiceRegisterRecord, ConnectToServer has already
+ // been called. As multiple DNSServiceRegisterRecords can be multiplexed over a single
+ // connection, we need a way to demultiplex the response so that the callback corresponding
+ // to the right DNSServiceRegisterRecord instance can be called. Use the same mechanism that
+ // is used by kDNSServiceFlagsShareConnection. create_hdr copies the uid value to ipc
+ // hdr->client_context which will be returned in the ipc response.
+ if (++sdRef->uid.u32[0] == 0)
+ ++sdRef->uid.u32[1];
+ hdr = create_hdr(reg_record_request, &len, &ptr, 1, sdRef);
+ if (!hdr) return kDNSServiceErr_NoMemory;
+
+ put_flags(flags, &ptr);
+ put_uint32(interfaceIndex, &ptr);
+ put_string(fullname, &ptr);
+ put_uint16(rrtype, &ptr);
+ put_uint16(rrclass, &ptr);
+ put_uint16(rdlen, &ptr);
+ put_rdata(rdlen, rdata, &ptr);
+ put_uint32(ttl, &ptr);
+
+ rref = malloc(sizeof(DNSRecord));
+ if (!rref) { free(hdr); return kDNSServiceErr_NoMemory; }
+ rref->AppContext = context;
+ rref->AppCallback = callBack;
+ rref->record_index = sdRef->max_index++;
+ rref->sdr = sdRef;
+ rref->recnext = NULL;
+ *RecordRef = rref;
+ // Remember the uid that we are sending across so that we can match
+ // when the response comes back.
+ rref->uid = sdRef->uid;
+ hdr->reg_index = rref->record_index;
+
+ p = &(sdRef)->rec;
+ while (*p) p = &(*p)->recnext;
+ *p = rref;
+
+ return deliver_request(hdr, sdRef); // Will free hdr for us
+}
+
+// sdRef returned by DNSServiceRegister()
+DNSServiceErrorType DNSSD_API DNSServiceAddRecord
+(
+ DNSServiceRef sdRef,
+ DNSRecordRef *RecordRef,
+ DNSServiceFlags flags,
+ uint16_t rrtype,
+ uint16_t rdlen,
+ const void *rdata,
+ uint32_t ttl
+)
+{
+ ipc_msg_hdr *hdr;
+ size_t len = 0;
+ char *ptr;
+ DNSRecordRef rref;
+ DNSRecord **p;
+
+ if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; }
+ if (!RecordRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with NULL DNSRecordRef pointer"); return kDNSServiceErr_BadParam; }
+ if (sdRef->op != reg_service_request)
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with non-DNSServiceRegister DNSServiceRef %p %d", sdRef, sdRef->op);
+ return kDNSServiceErr_BadReference;
+ }
+
+ if (!DNSServiceRefValid(sdRef))
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator);
+ 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, 1, sdRef);
+ if (!hdr) return kDNSServiceErr_NoMemory;
+ put_flags(flags, &ptr);
+ put_uint16(rrtype, &ptr);
+ put_uint16(rdlen, &ptr);
+ put_rdata(rdlen, rdata, &ptr);
+ put_uint32(ttl, &ptr);
+
+ rref = malloc(sizeof(DNSRecord));
+ if (!rref) { free(hdr); return kDNSServiceErr_NoMemory; }
+ rref->AppContext = NULL;
+ rref->AppCallback = NULL;
+ rref->record_index = sdRef->max_index++;
+ rref->sdr = sdRef;
+ rref->recnext = NULL;
+ *RecordRef = rref;
+ hdr->reg_index = rref->record_index;
+
+ p = &(sdRef)->rec;
+ while (*p) p = &(*p)->recnext;
+ *p = rref;
+
+ return deliver_request(hdr, sdRef); // Will free hdr for us
+}
+
+// DNSRecordRef returned by DNSServiceRegisterRecord or DNSServiceAddRecord
+DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord
+(
+ DNSServiceRef sdRef,
+ DNSRecordRef RecordRef,
+ DNSServiceFlags flags,
+ uint16_t rdlen,
+ const void *rdata,
+ uint32_t ttl
+)
+{
+ ipc_msg_hdr *hdr;
+ size_t len = 0;
+ char *ptr;
+
+ if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceUpdateRecord called with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; }
+
+ if (!DNSServiceRefValid(sdRef))
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub DNSServiceUpdateRecord called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator);
+ return kDNSServiceErr_BadReference;
+ }
+
+ // Note: RecordRef is allowed to be NULL
+
+ len += sizeof(uint16_t);
+ len += rdlen;
+ len += sizeof(uint32_t);
+ len += sizeof(DNSServiceFlags);
+
+ hdr = create_hdr(update_record_request, &len, &ptr, 1, sdRef);
+ if (!hdr) return kDNSServiceErr_NoMemory;
+ hdr->reg_index = RecordRef ? RecordRef->record_index : TXT_RECORD_INDEX;
+ put_flags(flags, &ptr);
+ put_uint16(rdlen, &ptr);
+ put_rdata(rdlen, rdata, &ptr);
+ put_uint32(ttl, &ptr);
+ return deliver_request(hdr, sdRef); // Will free hdr for us
+}
+
+DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord
+(
+ DNSServiceRef sdRef,
+ DNSRecordRef RecordRef,
+ DNSServiceFlags flags
+)
+{
+ ipc_msg_hdr *hdr;
+ size_t len = 0;
+ char *ptr;
+ DNSServiceErrorType err;
+
+ if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRemoveRecord called with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; }
+ if (!RecordRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRemoveRecord called with NULL DNSRecordRef"); return kDNSServiceErr_BadParam; }
+ if (!sdRef->max_index) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRemoveRecord called with bad DNSServiceRef"); return kDNSServiceErr_BadReference; }
+
+ if (!DNSServiceRefValid(sdRef))
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRemoveRecord called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator);
+ return kDNSServiceErr_BadReference;
+ }
+
+ len += sizeof(flags);
+ hdr = create_hdr(remove_record_request, &len, &ptr, 1, sdRef);
+ if (!hdr) return kDNSServiceErr_NoMemory;
+ hdr->reg_index = RecordRef->record_index;
+ put_flags(flags, &ptr);
+ err = deliver_request(hdr, sdRef); // Will free hdr for us
+ if (!err)
+ {
+ // This RecordRef could have been allocated in DNSServiceRegisterRecord or DNSServiceAddRecord.
+ // If so, delink from the list before freeing
+ DNSRecord **p = &sdRef->rec;
+ while (*p && *p != RecordRef) p = &(*p)->recnext;
+ if (*p) *p = RecordRef->recnext;
+ free(RecordRef);
+ }
+ return err;
+}
+
+DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord
+(
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ const char *fullname,
+ uint16_t rrtype,
+ uint16_t rrclass,
+ uint16_t rdlen,
+ const void *rdata
+)
+{
+ char *ptr;
+ size_t len;
+ ipc_msg_hdr *hdr;
+ DNSServiceOp *tmp;
+
+ DNSServiceErrorType err = ConnectToServer(&tmp, flags, reconfirm_record_request, NULL, NULL, NULL);
+ if (err) return err;
+
+ len = sizeof(DNSServiceFlags);
+ len += sizeof(uint32_t);
+ len += strlen(fullname) + 1;
+ len += 3 * sizeof(uint16_t);
+ len += rdlen;
+ hdr = create_hdr(reconfirm_record_request, &len, &ptr, 0, tmp);
+ if (!hdr) { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_NoMemory; }
+
+ put_flags(flags, &ptr);
+ put_uint32(interfaceIndex, &ptr);
+ put_string(fullname, &ptr);
+ put_uint16(rrtype, &ptr);
+ put_uint16(rrclass, &ptr);
+ put_uint16(rdlen, &ptr);
+ put_rdata(rdlen, rdata, &ptr);
+
+ err = deliver_request(hdr, tmp); // Will free hdr for us
+ DNSServiceRefDeallocate(tmp);
+ return err;
+}
+
+
+static void handle_port_mapping_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end)
+{
+ union { uint32_t l; u_char b[4]; } addr;
+ uint8_t protocol;
+ union { uint16_t s; u_char b[2]; } internalPort;
+ union { uint16_t s; u_char b[2]; } externalPort;
+ uint32_t ttl;
+
+ if (!data || data + 13 > end) goto fail;
+
+ addr.b[0] = *data++;
+ addr.b[1] = *data++;
+ addr.b[2] = *data++;
+ addr.b[3] = *data++;
+ protocol = *data++;
+ internalPort.b[0] = *data++;
+ internalPort.b[1] = *data++;
+ externalPort.b[0] = *data++;
+ externalPort.b[1] = *data++;
+ ttl = get_uint32(&data, end);
+ if (!data) goto fail;
+
+ ((DNSServiceNATPortMappingReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, addr.l, protocol, internalPort.s, externalPort.s, ttl, sdr->AppContext);
+ return;
+ // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
+
+ fail :
+ syslog(LOG_WARNING, "dnssd_clientstub handle_port_mapping_response: error reading result from daemon");
+}
+
+DNSServiceErrorType DNSSD_API DNSServiceNATPortMappingCreate
+(
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ uint32_t protocol, /* TCP and/or UDP */
+ uint16_t internalPortInNetworkByteOrder,
+ uint16_t externalPortInNetworkByteOrder,
+ uint32_t ttl, /* time to live in seconds */
+ DNSServiceNATPortMappingReply callBack,
+ void *context /* may be NULL */
+)
+{
+ char *ptr;
+ size_t len;
+ ipc_msg_hdr *hdr;
+ union { uint16_t s; u_char b[2]; } internalPort = { internalPortInNetworkByteOrder };
+ union { uint16_t s; u_char b[2]; } externalPort = { externalPortInNetworkByteOrder };
+
+ DNSServiceErrorType err = ConnectToServer(sdRef, flags, port_mapping_request, handle_port_mapping_response, callBack, context);
+ if (err) return err; // On error ConnectToServer leaves *sdRef set to NULL
+
+ len = sizeof(flags);
+ len += sizeof(interfaceIndex);
+ len += sizeof(protocol);
+ len += sizeof(internalPort);
+ len += sizeof(externalPort);
+ len += sizeof(ttl);
+
+ hdr = create_hdr(port_mapping_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef);
+ if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
+
+ put_flags(flags, &ptr);
+ put_uint32(interfaceIndex, &ptr);
+ put_uint32(protocol, &ptr);
+ *ptr++ = internalPort.b[0];
+ *ptr++ = internalPort.b[1];
+ *ptr++ = externalPort.b[0];
+ *ptr++ = externalPort.b[1];
+ put_uint32(ttl, &ptr);
+
+ err = deliver_request(hdr, *sdRef); // Will free hdr for us
+ if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
+ return err;
+}
+
+#if _DNS_SD_LIBDISPATCH
+DNSServiceErrorType DNSSD_API DNSServiceSetDispatchQueue
+(
+ DNSServiceRef service,
+ dispatch_queue_t queue
+)
+{
+ int dnssd_fd = DNSServiceRefSockFD(service);
+ if (dnssd_fd == dnssd_InvalidSocket) return kDNSServiceErr_BadParam;
+ if (!queue)
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub: DNSServiceSetDispatchQueue dispatch queue NULL");
+ return kDNSServiceErr_BadParam;
+ }
+ if (service->disp_queue)
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSetDispatchQueue dispatch queue set already");
+ return kDNSServiceErr_BadParam;
+ }
+ if (service->disp_source)
+ {
+ syslog(LOG_WARNING, "DNSServiceSetDispatchQueue dispatch source set already");
+ return kDNSServiceErr_BadParam;
+ }
+ service->disp_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, dnssd_fd, 0, queue);
+ if (!service->disp_source)
+ {
+ syslog(LOG_WARNING, "DNSServiceSetDispatchQueue dispatch_source_create failed");
+ return kDNSServiceErr_NoMemory;
+ }
+ service->disp_queue = queue;
+ dispatch_source_set_event_handler(service->disp_source, ^{DNSServiceProcessResult(service);});
+ dispatch_source_set_cancel_handler(service->disp_source, ^{dnssd_close(dnssd_fd);});
+ dispatch_resume(service->disp_source);
+ return kDNSServiceErr_NoError;
+}
+#endif // _DNS_SD_LIBDISPATCH
+
+#if !defined(_WIN32)
+
+static void DNSSD_API SleepKeepaliveCallback(DNSServiceRef sdRef, DNSRecordRef rec, const DNSServiceFlags flags,
+ DNSServiceErrorType errorCode, void *context)
+{
+ SleepKAContext *ka = (SleepKAContext *)context;
+ (void)rec; // Unused
+ (void)flags; // Unused
+
+ if (sdRef->kacontext != context)
+ syslog(LOG_WARNING, "SleepKeepaliveCallback context mismatch");
+
+ if (ka->AppCallback)
+ ((DNSServiceSleepKeepaliveReply)ka->AppCallback)(sdRef, errorCode, ka->AppContext);
+}
+
+DNSServiceErrorType DNSSD_API DNSServiceSleepKeepalive
+(
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ int fd,
+ unsigned int timeout,
+ DNSServiceSleepKeepaliveReply callBack,
+ void *context
+)
+{
+ char source_str[INET6_ADDRSTRLEN];
+ char target_str[INET6_ADDRSTRLEN];
+ struct sockaddr_storage lss;
+ struct sockaddr_storage rss;
+ socklen_t len1, len2;
+ unsigned int len, proxyreclen;
+ char buf[256];
+ DNSServiceErrorType err;
+ DNSRecordRef record = NULL;
+ char name[10];
+ char recname[128];
+ SleepKAContext *ka;
+ unsigned int i, unique;
+
+
+ (void) flags; //unused
+ if (!timeout) return kDNSServiceErr_BadParam;
+
+
+ len1 = sizeof(lss);
+ if (getsockname(fd, (struct sockaddr *)&lss, &len1) < 0)
+ {
+ syslog(LOG_WARNING, "DNSServiceSleepKeepalive: getsockname %d\n", errno);
+ return kDNSServiceErr_BadParam;
+ }
+
+ len2 = sizeof(rss);
+ if (getpeername(fd, (struct sockaddr *)&rss, &len2) < 0)
+ {
+ syslog(LOG_WARNING, "DNSServiceSleepKeepalive: getpeername %d\n", errno);
+ return kDNSServiceErr_BadParam;
+ }
+
+ if (len1 != len2)
+ {
+ syslog(LOG_WARNING, "DNSServiceSleepKeepalive local/remote info not same");
+ return kDNSServiceErr_Unknown;
+ }
+
+ unique = 0;
+ if (lss.ss_family == AF_INET)
+ {
+ struct sockaddr_in *sl = (struct sockaddr_in *)&lss;
+ struct sockaddr_in *sr = (struct sockaddr_in *)&rss;
+ unsigned char *ptr = (unsigned char *)&sl->sin_addr;
+
+ if (!inet_ntop(AF_INET, (const void *)&sr->sin_addr, target_str, sizeof (target_str)))
+ {
+ syslog(LOG_WARNING, "DNSServiceSleepKeepalive remote info failed %d", errno);
+ return kDNSServiceErr_Unknown;
+ }
+ if (!inet_ntop(AF_INET, (const void *)&sl->sin_addr, source_str, sizeof (source_str)))
+ {
+ syslog(LOG_WARNING, "DNSServiceSleepKeepalive local info failed %d", errno);
+ return kDNSServiceErr_Unknown;
+ }
+ // Sum of all bytes in the local address and port should result in a unique
+ // number in the local network
+ for (i = 0; i < sizeof(struct in_addr); i++)
+ unique += ptr[i];
+ unique += sl->sin_port;
+ len = snprintf(buf+1, sizeof(buf) - 1, "t=%u h=%s d=%s l=%u r=%u", timeout, source_str, target_str, ntohs(sl->sin_port), ntohs(sr->sin_port));
+ }
+ else
+ {
+ struct sockaddr_in6 *sl6 = (struct sockaddr_in6 *)&lss;
+ struct sockaddr_in6 *sr6 = (struct sockaddr_in6 *)&rss;
+ unsigned char *ptr = (unsigned char *)&sl6->sin6_addr;
+
+ if (!inet_ntop(AF_INET6, (const void *)&sr6->sin6_addr, target_str, sizeof (target_str)))
+ {
+ syslog(LOG_WARNING, "DNSServiceSleepKeepalive remote6 info failed %d", errno);
+ return kDNSServiceErr_Unknown;
+ }
+ if (!inet_ntop(AF_INET6, (const void *)&sl6->sin6_addr, source_str, sizeof (source_str)))
+ {
+ syslog(LOG_WARNING, "DNSServiceSleepKeepalive local6 info failed %d", errno);
+ return kDNSServiceErr_Unknown;
+ }
+ for (i = 0; i < sizeof(struct in6_addr); i++)
+ unique += ptr[i];
+ unique += sl6->sin6_port;
+ len = snprintf(buf+1, sizeof(buf) - 1, "t=%u H=%s D=%s l=%u r=%u", timeout, source_str, target_str, ntohs(sl6->sin6_port), ntohs(sr6->sin6_port));
+ }
+
+ if (len >= (sizeof(buf) - 1))
+ {
+ syslog(LOG_WARNING, "DNSServiceSleepKeepalive could not fit local/remote info");
+ return kDNSServiceErr_Unknown;
+ }
+ // Include the NULL byte also in the first byte. The total length of the record includes the
+ // first byte also.
+ buf[0] = len + 1;
+ proxyreclen = len + 2;
+
+ len = snprintf(name, sizeof(name), "%u", unique);
+ if (len >= sizeof(name))
+ {
+ syslog(LOG_WARNING, "DNSServiceSleepKeepalive could not fit unique");
+ return kDNSServiceErr_Unknown;
+ }
+
+ len = snprintf(recname, sizeof(recname), "%s.%s", name, "_keepalive._dns-sd._udp.local");
+ if (len >= sizeof(recname))
+ {
+ syslog(LOG_WARNING, "DNSServiceSleepKeepalive could not fit name");
+ return kDNSServiceErr_Unknown;
+ }
+
+ ka = malloc(sizeof(SleepKAContext));
+ if (!ka) return kDNSServiceErr_NoMemory;
+ ka->AppCallback = callBack;
+ ka->AppContext = context;
+
+ err = DNSServiceCreateConnection(sdRef);
+ if (err)
+ {
+ syslog(LOG_WARNING, "DNSServiceSleepKeepalive cannot create connection");
+ free(ka);
+ return err;
+ }
+
+ // we don't care about the "record". When sdRef gets deallocated later, it will be freed too
+ err = DNSServiceRegisterRecord(*sdRef, &record, kDNSServiceFlagsUnique, 0, recname,
+ kDNSServiceType_NULL, kDNSServiceClass_IN, proxyreclen, buf, kDNSServiceInterfaceIndexAny, SleepKeepaliveCallback, ka);
+ if (err)
+ {
+ syslog(LOG_WARNING, "DNSServiceSleepKeepalive cannot create connection");
+ free(ka);
+ return err;
+ }
+ (*sdRef)->kacontext = ka;
+ return kDNSServiceErr_NoError;
+}
+#endif
diff --git a/mDNSResponder/mDNSShared/dnssd_ipc.c b/mDNSResponder/mDNSShared/dnssd_ipc.c
new file mode 100644
index 00000000..6059eb39
--- /dev/null
+++ b/mDNSResponder/mDNSShared/dnssd_ipc.c
@@ -0,0 +1,161 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "dnssd_ipc.h"
+
+#if defined(_WIN32)
+
+char *win32_strerror(int inErrorCode)
+{
+ static char buffer[1024];
+ DWORD n;
+ memset(buffer, 0, sizeof(buffer));
+ n = FormatMessageA(
+ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ (DWORD) inErrorCode,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ buffer,
+ sizeof(buffer),
+ NULL);
+ if (n > 0)
+ {
+ // Remove any trailing CR's or LF's since some messages have them.
+ while ((n > 0) && isspace(((unsigned char *) buffer)[n - 1]))
+ buffer[--n] = '\0';
+ }
+ return buffer;
+}
+
+#endif
+
+void put_uint32(const uint32_t l, char **ptr)
+{
+ (*ptr)[0] = (char)((l >> 24) & 0xFF);
+ (*ptr)[1] = (char)((l >> 16) & 0xFF);
+ (*ptr)[2] = (char)((l >> 8) & 0xFF);
+ (*ptr)[3] = (char)((l ) & 0xFF);
+ *ptr += sizeof(uint32_t);
+}
+
+uint32_t get_uint32(const char **ptr, const char *end)
+{
+ if (!*ptr || *ptr + sizeof(uint32_t) > end)
+ {
+ *ptr = NULL;
+ return(0);
+ }
+ else
+ {
+ uint8_t *p = (uint8_t*) *ptr;
+ *ptr += sizeof(uint32_t);
+ return((uint32_t) ((uint32_t)p[0] << 24 | (uint32_t)p[1] << 16 | (uint32_t)p[2] << 8 | p[3]));
+ }
+}
+
+void put_uint16(uint16_t s, char **ptr)
+{
+ (*ptr)[0] = (char)((s >> 8) & 0xFF);
+ (*ptr)[1] = (char)((s ) & 0xFF);
+ *ptr += sizeof(uint16_t);
+}
+
+uint16_t get_uint16(const char **ptr, const char *end)
+{
+ if (!*ptr || *ptr + sizeof(uint16_t) > end)
+ {
+ *ptr = NULL;
+ return(0);
+ }
+ else
+ {
+ uint8_t *p = (uint8_t*) *ptr;
+ *ptr += sizeof(uint16_t);
+ return((uint16_t) ((uint16_t)p[0] << 8 | p[1]));
+ }
+}
+
+int put_string(const char *str, char **ptr)
+{
+ if (!str) str = "";
+ strcpy(*ptr, str);
+ *ptr += strlen(str) + 1;
+ return 0;
+}
+
+int get_string(const char **ptr, const char *const end, char *buffer, int buflen)
+{
+ if (!*ptr)
+ {
+ *buffer = 0;
+ return(-1);
+ }
+ else
+ {
+ char *lim = buffer + buflen; // Calculate limit
+ while (*ptr < end && buffer < lim)
+ {
+ char c = *buffer++ = *(*ptr)++;
+ if (c == 0) return(0); // Success
+ }
+ if (buffer == lim) buffer--;
+ *buffer = 0; // Failed, so terminate string,
+ *ptr = NULL; // clear pointer,
+ return(-1); // and return failure indication
+ }
+}
+
+void put_rdata(const int rdlen, const unsigned char *rdata, char **ptr)
+{
+ memcpy(*ptr, rdata, rdlen);
+ *ptr += rdlen;
+}
+
+const char *get_rdata(const char **ptr, const char *end, int rdlen)
+{
+ if (!*ptr || *ptr + rdlen > end)
+ {
+ *ptr = NULL;
+ return(0);
+ }
+ else
+ {
+ const char *rd = *ptr;
+ *ptr += rdlen;
+ return rd;
+ }
+}
+
+void ConvertHeaderBytes(ipc_msg_hdr *hdr)
+{
+ hdr->version = htonl(hdr->version);
+ hdr->datalen = htonl(hdr->datalen);
+ hdr->ipc_flags = htonl(hdr->ipc_flags);
+ hdr->op = htonl(hdr->op );
+ hdr->reg_index = htonl(hdr->reg_index);
+}
diff --git a/mDNSResponder/mDNSShared/dnssd_ipc.h b/mDNSResponder/mDNSShared/dnssd_ipc.h
new file mode 100644
index 00000000..360d703f
--- /dev/null
+++ b/mDNSResponder/mDNSShared/dnssd_ipc.h
@@ -0,0 +1,221 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DNSSD_IPC_H
+#define DNSSD_IPC_H
+
+#include "dns_sd.h"
+
+//
+// Common cross platform services
+//
+#if defined(WIN32)
+# include <winsock2.h>
+# define dnssd_InvalidSocket INVALID_SOCKET
+# define dnssd_SocketValid(s) ((s) != INVALID_SOCKET)
+# define dnssd_EWOULDBLOCK WSAEWOULDBLOCK
+# define dnssd_EINTR WSAEINTR
+# define dnssd_ECONNRESET WSAECONNRESET
+# define dnssd_sock_t SOCKET
+# define dnssd_socklen_t int
+# define dnssd_close(sock) closesocket(sock)
+# define dnssd_errno WSAGetLastError()
+# define dnssd_strerror(X) win32_strerror(X)
+# define ssize_t int
+# define getpid _getpid
+# define unlink _unlink
+extern char *win32_strerror(int inErrorCode);
+#else
+# include <sys/types.h>
+# include <unistd.h>
+# include <sys/un.h>
+# include <string.h>
+# include <stdio.h>
+# include <stdlib.h>
+# include <sys/stat.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# define dnssd_InvalidSocket -1
+# define dnssd_SocketValid(s) ((s) >= 0)
+# define dnssd_EWOULDBLOCK EWOULDBLOCK
+# define dnssd_EINTR EINTR
+# define dnssd_ECONNRESET ECONNRESET
+# define dnssd_EPIPE EPIPE
+# define dnssd_sock_t int
+# define dnssd_socklen_t unsigned int
+# define dnssd_close(sock) close(sock)
+# define dnssd_errno errno
+# define dnssd_strerror(X) strerror(X)
+#endif
+
+#if defined(USE_TCP_LOOPBACK)
+# define AF_DNSSD AF_INET
+# define MDNS_TCP_SERVERADDR "127.0.0.1"
+# define MDNS_TCP_SERVERPORT 5354
+# define LISTENQ 5
+# define dnssd_sockaddr_t struct sockaddr_in
+#else
+# define AF_DNSSD AF_LOCAL
+# ifndef MDNS_UDS_SERVERPATH
+# define MDNS_UDS_SERVERPATH "/var/run/mDNSResponder"
+# endif
+# define LISTENQ 100
+// longest legal control path length
+# define MAX_CTLPATH 256
+# define dnssd_sockaddr_t struct sockaddr_un
+#endif
+
+// Compatibility workaround
+#ifndef AF_LOCAL
+#define AF_LOCAL AF_UNIX
+#endif
+
+// General UDS constants
+#define TXT_RECORD_INDEX ((uint32_t)(-1)) // record index for default text record
+
+// 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
+
+// Structure packing macro. If we're not using GNUC, it's not fatal. Most compilers naturally pack the on-the-wire
+// structures correctly anyway, so a plain "struct" is usually fine. In the event that structures are not packed
+// correctly, our compile-time assertion checks will catch it and prevent inadvertent generation of non-working code.
+#ifndef packedstruct
+ #if ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 9)))
+ #define packedstruct struct __attribute__((__packed__))
+ #define packedunion union __attribute__((__packed__))
+ #else
+ #define packedstruct struct
+ #define packedunion union
+ #endif
+#endif
+
+typedef enum
+{
+ request_op_none = 0, // No request yet received on this connection
+ connection_request = 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,
+ setdomain_request, // Up to here is in Tiger and B4W 1.0.3
+ getproperty_request, // New in B4W 1.0.4
+ port_mapping_request, // New in Leopard and B4W 2.0
+ addrinfo_request,
+ send_bpf, // New in SL
+ getpid_request,
+ release_request,
+ connection_delegate_request,
+
+ cancel_request = 63
+} request_op_t;
+
+typedef enum
+{
+ enumeration_reply_op = 64,
+ reg_service_reply_op,
+ browse_reply_op,
+ resolve_reply_op,
+ query_reply_op,
+ reg_record_reply_op, // Up to here is in Tiger and B4W 1.0.3
+ getproperty_reply_op, // New in B4W 1.0.4
+ port_mapping_reply_op, // New in Leopard and B4W 2.0
+ addrinfo_reply_op
+} reply_op_t;
+
+#if defined(_WIN64)
+# pragma pack(push,4)
+#endif
+
+// Define context object big enough to hold a 64-bit pointer,
+// to accomodate 64-bit clients communicating with 32-bit daemon.
+// There's no reason for the daemon to ever be a 64-bit process, but its clients might be
+typedef packedunion
+{
+ void *context;
+ uint32_t u32[2];
+} client_context_t;
+
+typedef packedstruct
+{
+ uint32_t version;
+ uint32_t datalen;
+ uint32_t ipc_flags;
+ uint32_t op; // request_op_t or reply_op_t
+ client_context_t client_context; // context passed from client, returned by server in corresponding reply
+ uint32_t reg_index; // identifier for a record registered via DNSServiceRegisterRecord() on a
+ // socket connected by DNSServiceCreateConnection(). 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;
+
+#if defined(_WIN64)
+# pragma pack(pop)
+#endif
+
+// 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_uint32(const uint32_t l, char **ptr);
+uint32_t get_uint32(const char **ptr, const char *end);
+
+void put_uint16(uint16_t s, char **ptr);
+uint16_t get_uint16(const char **ptr, const char *end);
+
+#define put_flags put_uint32
+#define get_flags get_uint32
+
+#define put_error_code put_uint32
+#define get_error_code get_uint32
+
+int put_string(const char *str, char **ptr);
+int get_string(const char **ptr, const char *const end, char *buffer, int buflen);
+
+void put_rdata(const int rdlen, const unsigned char *rdata, char **ptr);
+const char *get_rdata(const char **ptr, const char *end, int rdlen); // return value is rdata pointed to by *ptr -
+// rdata is not copied from buffer.
+
+void ConvertHeaderBytes(ipc_msg_hdr *hdr);
+
+struct CompileTimeAssertionChecks_dnssd_ipc
+{
+ // Check that the compiler generated our on-the-wire packet format structure definitions
+ // properly packed, without adding padding bytes to align fields on 32-bit or 64-bit boundaries.
+ char assert0[(sizeof(client_context_t) == 8) ? 1 : -1];
+ char assert1[(sizeof(ipc_msg_hdr) == 28) ? 1 : -1];
+};
+
+#endif // DNSSD_IPC_H
diff --git a/mDNSResponder/mDNSShared/mDNSDebug.c b/mDNSResponder/mDNSShared/mDNSDebug.c
new file mode 100644
index 00000000..cb4da6ec
--- /dev/null
+++ b/mDNSResponder/mDNSShared/mDNSDebug.c
@@ -0,0 +1,95 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+
+ File: mDNSDebug.c
+
+ Contains: Implementation of debugging utilities. Requires a POSIX environment.
+
+ Version: 1.0
+
+ */
+
+#include "mDNSDebug.h"
+
+#include <stdio.h>
+
+#if defined(WIN32) || defined(EFI32) || defined(EFI64) || defined(EFIX64)
+// Need to add Windows/EFI syslog support here
+#define LOG_PID 0x01
+#define LOG_CONS 0x02
+#define LOG_PERROR 0x20
+#else
+#include <syslog.h>
+#endif
+
+#include "mDNSEmbeddedAPI.h"
+
+mDNSexport int mDNS_LoggingEnabled = 0;
+mDNSexport int mDNS_PacketLoggingEnabled = 0;
+mDNSexport int mDNS_McastLoggingEnabled = 0;
+mDNSexport int mDNS_McastTracingEnabled = 0;
+
+#if MDNS_DEBUGMSGS
+mDNSexport int mDNS_DebugMode = mDNStrue;
+#else
+mDNSexport int mDNS_DebugMode = mDNSfalse;
+#endif
+
+// Note, this uses mDNS_vsnprintf instead of standard "vsnprintf", because mDNS_vsnprintf knows
+// how to print special data types like IP addresses and length-prefixed domain names
+#if MDNS_DEBUGMSGS > 1
+mDNSexport void verbosedebugf_(const char *format, ...)
+{
+ char buffer[512];
+ va_list ptr;
+ va_start(ptr,format);
+ buffer[mDNS_vsnprintf(buffer, sizeof(buffer), format, ptr)] = 0;
+ va_end(ptr);
+ mDNSPlatformWriteDebugMsg(buffer);
+}
+#endif
+
+// Log message with default "mDNSResponder" ident string at the start
+mDNSlocal void LogMsgWithLevelv(mDNSLogLevel_t logLevel, const char *format, va_list ptr)
+{
+ char buffer[512];
+ buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0;
+ mDNSPlatformWriteLogMsg(ProgramName, buffer, logLevel);
+}
+
+#define LOG_HELPER_BODY(L) \
+ { \
+ va_list ptr; \
+ va_start(ptr,format); \
+ LogMsgWithLevelv(L, format, ptr); \
+ va_end(ptr); \
+ }
+
+// see mDNSDebug.h
+#if !MDNS_HAS_VA_ARG_MACROS
+void LogMsg_(const char *format, ...) LOG_HELPER_BODY(MDNS_LOG_MSG)
+void LogOperation_(const char *format, ...) LOG_HELPER_BODY(MDNS_LOG_OPERATION)
+void LogSPS_(const char *format, ...) LOG_HELPER_BODY(MDNS_LOG_SPS)
+void LogInfo_(const char *format, ...) LOG_HELPER_BODY(MDNS_LOG_INFO)
+#endif
+
+#if MDNS_DEBUGMSGS
+void debugf_(const char *format, ...) LOG_HELPER_BODY(MDNS_LOG_DEBUG)
+#endif
+
+// Log message with default "mDNSResponder" ident string at the start
+mDNSexport void LogMsgWithLevel(mDNSLogLevel_t logLevel, const char *format, ...)
+LOG_HELPER_BODY(logLevel)
diff --git a/mDNSResponder/mDNSShared/mDNSResponder.8 b/mDNSResponder/mDNSShared/mDNSResponder.8
new file mode 100644
index 00000000..48c1cf65
--- /dev/null
+++ b/mDNSResponder/mDNSShared/mDNSResponder.8
@@ -0,0 +1,116 @@
+.\" -*- tab-width: 4 -*-
+.\"
+.\" Copyright (c) 2003-2004 Apple Computer, Inc. All Rights Reserved.
+.\"
+.\" Licensed under the Apache License, Version 2.0 (the "License");
+.\" you may not use this file except in compliance with the License.
+.\" You may obtain a copy of the License at
+.\"
+.\" http://www.apache.org/licenses/LICENSE-2.0
+.\"
+.\" Unless required by applicable law or agreed to in writing, software
+.\" distributed under the License is distributed on an "AS IS" BASIS,
+.\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+.\" See the License for the specific language governing permissions and
+.\" limitations under the License.
+.\"
+.Dd April 2004 \" Date
+.Dt mDNSResponder 8 \" Document Title
+.Os Darwin \" Operating System
+.\"
+.Sh NAME
+.Nm mDNSResponder
+.Nd Multicast and Unicast DNS daemon \" Name Description for whatis database
+.\"
+.Sh SYNOPSIS
+.Nm
+.\"
+.Sh DESCRIPTION
+.Nm
+(also known as
+.Nm mdnsd
+on some systems)
+is a daemon invoked at boot time to implement Multicast DNS and DNS Service Discovery. On
+Mac OS X 10.6 (Snow Leopard),
+.Nm
+is also the system-wide Unicast DNS Resolver.
+.Pp
+.Nm
+listens on UDP port 5353 for Multicast DNS Query packets.
+When it receives a query for which it knows an answer,
+.Nm
+issues the appropriate Multicast DNS Reply packet.
+.Pp
+.Nm
+also performs Unicast and Multicast DNS Queries on behalf of client processes, and
+maintains a cache of the replies.
+.Pp
+.Nm
+has no user-specifiable command-line argument, and users should not run
+.Nm
+manually.
+.Pp
+.Sh LOGGING
+There are several methods with which to examine
+.Nm Ns 's internal state for debugging and diagnostic purposes. The syslog(1)
+logging levels map as follows:
+.Pp
+.Dl Error - Error messages
+.Dl Warning - Client-initiated operations
+.Dl Notice - Sleep proxy operations
+.Dl Info - Informational messages
+.Pp
+By default, only log level Error is logged.
+.Pp
+A SIGUSR1 signal toggles additional logging, with Warning and Notice
+enabled by default:
+.Pp
+.Dl % sudo killall -USR1 mDNSResponder
+.Pp
+Once this logging is enabled, users can additionally use syslog(1)
+to change the log filter for the process. For example, to enable log levels Emergency - Debug:
+.Pp
+.Dl % sudo syslog -c mDNSResponder -d
+.Pp
+A SIGUSR2 signal toggles packet logging:
+.Pp
+.Dl % sudo killall -USR2 mDNSResponder
+.Pp
+A SIGINFO signal will dump a snapshot summary of the internal state to
+.Pa /var/log/system.log Ns :
+.Pp
+.Dl % sudo killall -INFO mDNSResponder
+.Sh FILES
+.Pa /usr/sbin/mDNSResponder \" Pathname
+.\"
+.Pp
+.Sh INFO
+.Pp
+For information on Multicast DNS, see
+.Pa http://www.multicastdns.org/
+.Pp
+For information on DNS Service Discovery, see
+.Pa http://www.dns-sd.org/
+.Pp
+For information on how to use the Multicast DNS and the
+DNS Service Discovery APIs on Mac OS X and other platforms, see
+.Pa http://developer.apple.com/bonjour/
+.Pp
+For the source code to
+.Nm , see
+.Pa http://developer.apple.com/darwin/projects/bonjour/
+.\"
+.Sh BUGS
+.Nm
+bugs are tracked in Apple Radar component "mDNSResponder".
+.\"
+.Sh HISTORY
+The
+.Nm
+daemon first appeared in Mac OS X 10.2 (Jaguar).
+.Pp
+Also available from the Darwin open source repository
+(though not officially supported by Apple) are
+.Nm
+daemons for other platforms, including Mac OS 9, Microsoft Windows,
+Linux, FreeBSD, NetBSD, Solaris, and other POSIX systems.
diff --git a/mDNSResponder/mDNSShared/uds_daemon.c b/mDNSResponder/mDNSShared/uds_daemon.c
new file mode 100644
index 00000000..53677bdf
--- /dev/null
+++ b/mDNSResponder/mDNSShared/uds_daemon.c
@@ -0,0 +1,6103 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2013 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(_WIN32)
+#include <process.h>
+#define usleep(X) Sleep(((X)+999)/1000)
+#else
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "mDNSEmbeddedAPI.h"
+#include "DNSCommon.h"
+#include "uDNS.h"
+#include "uds_daemon.h"
+
+// Normally we append search domains only for queries with a single label that are not
+// fully qualified. This can be overridden to apply search domains for queries (that are
+// not fully qualified) with any number of labels e.g., moon, moon.cs, moon.cs.be, etc.
+mDNSBool AlwaysAppendSearchDomains = mDNSfalse;
+
+// Apple-specific functionality, not required for other platforms
+#if APPLE_OSX_mDNSResponder
+#include <sys/ucred.h>
+#ifndef PID_FILE
+#define PID_FILE ""
+#endif
+#endif
+
+#ifdef LOCAL_PEERPID
+#include <sys/un.h> // for LOCAL_PEERPID
+#include <sys/socket.h> // for getsockopt
+#include <sys/proc_info.h> // for struct proc_bsdshortinfo
+#include <libproc.h> // for proc_pidinfo()
+#endif //LOCAL_PEERPID
+//upto 16 characters of process name (defined in <sys/proc.h> but we do not want to include that file)
+#define MAXCOMLEN 16
+
+#if APPLE_OSX_mDNSResponder
+#include <WebFilterDNS/WebFilterDNS.h>
+
+#if !NO_WCF
+
+int WCFIsServerRunning(WCFConnection *conn) __attribute__((weak_import));
+int WCFNameResolvesToAddr(WCFConnection *conn, char* domainName, struct sockaddr* address, uid_t userid) __attribute__((weak_import));
+int WCFNameResolvesToName(WCFConnection *conn, char* fromName, char* toName, uid_t userid) __attribute__((weak_import));
+
+// Do we really need to define a macro for "if"?
+#define CHECK_WCF_FUNCTION(X) if (X)
+#endif // ! NO_WCF
+
+#else
+#define NO_WCF 1
+#endif // APPLE_OSX_mDNSResponder
+
+// User IDs 0-500 are system-wide processes, not actual users in the usual sense
+// User IDs for real user accounts start at 501 and count up from there
+#define SystemUID(X) ((X) <= 500)
+
+#define MAX_ANONYMOUS_DATA 256
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Types and Data Structures
+#endif
+
+typedef enum
+{
+ t_uninitialized,
+ t_morecoming,
+ t_complete,
+ t_error,
+ t_terminated
+} transfer_state;
+
+typedef struct request_state request_state;
+
+typedef void (*req_termination_fn)(request_state *request);
+
+typedef struct registered_record_entry
+{
+ struct registered_record_entry *next;
+ mDNSu32 key;
+ client_context_t regrec_client_context;
+ request_state *request;
+ mDNSBool external_advertise;
+ mDNSInterfaceID origInterfaceID;
+ AuthRecord *rr; // Pointer to variable-sized AuthRecord (Why a pointer? Why not just embed it here?)
+} registered_record_entry;
+
+// A single registered service: ServiceRecordSet + bookkeeping
+// Note that we duplicate some fields from parent service_info object
+// to facilitate cleanup, when instances and parent may be deallocated at different times.
+typedef struct service_instance
+{
+ struct service_instance *next;
+ request_state *request;
+ AuthRecord *subtypes;
+ mDNSBool renameonmemfree; // Set on config change when we deregister original name
+ mDNSBool clientnotified; // Has client been notified of successful registration yet?
+ mDNSBool default_local; // is this the "local." from an empty-string registration?
+ mDNSBool external_advertise; // is this is being advertised externally?
+ domainname domain;
+ ServiceRecordSet srs; // note -- variable-sized object -- must be last field in struct
+} service_instance;
+
+// for multi-domain default browsing
+typedef struct browser_t
+{
+ struct browser_t *next;
+ domainname domain;
+ DNSQuestion q;
+} browser_t;
+
+#ifdef _WIN32
+ typedef unsigned int pid_t;
+ typedef unsigned int socklen_t;
+#endif
+
+struct request_state
+{
+ request_state *next;
+ request_state *primary; // If this operation is on a shared socket, pointer to primary
+ // request_state for the original DNSServiceCreateConnection() operation
+ dnssd_sock_t sd;
+ pid_t process_id; // Client's PID value
+ char pid_name[MAXCOMLEN]; // Client's process name
+ char uuid[UUID_SIZE];
+ mDNSBool validUUID;
+ dnssd_sock_t errsd;
+ mDNSu32 uid;
+ void * platform_data;
+
+ // Note: On a shared connection these fields in the primary structure, including hdr, are re-used
+ // for each new request. This is because, until we've read the ipc_msg_hdr to find out what the
+ // operation is, we don't know if we're going to need to allocate a new request_state or not.
+ transfer_state ts;
+ mDNSu32 hdr_bytes; // bytes of header already read
+ ipc_msg_hdr hdr;
+ mDNSu32 data_bytes; // bytes of message data already read
+ char *msgbuf; // pointer to data storage to pass to free()
+ const char *msgptr; // pointer to data to be read from (may be modified)
+ char *msgend; // pointer to byte after last byte of message
+
+ // reply, termination, error, and client context info
+ int no_reply; // don't send asynchronous replies to client
+ mDNSs32 time_blocked; // record time of a blocked client
+ int unresponsiveness_reports;
+ struct reply_state *replies; // corresponding (active) reply list
+ req_termination_fn terminate;
+ DNSServiceFlags flags;
+
+ union
+ {
+ registered_record_entry *reg_recs; // list of registrations for a connection-oriented request
+ struct
+ {
+ mDNSInterfaceID interface_id;
+ mDNSBool default_domain;
+ mDNSBool ForceMCast;
+ domainname regtype;
+ browser_t *browsers;
+ const mDNSu8 *AnonData;
+ } browser;
+ struct
+ {
+ mDNSInterfaceID InterfaceID;
+ mDNSu16 txtlen;
+ void *txtdata;
+ mDNSIPPort port;
+ domainlabel name;
+ char type_as_string[MAX_ESCAPED_DOMAIN_NAME];
+ domainname type;
+ mDNSBool default_domain;
+ domainname host;
+ mDNSBool autoname; // Set if this name is tied to the Computer Name
+ mDNSBool autorename; // Set if this client wants us to automatically rename on conflict
+ mDNSBool allowremotequery; // Respond to unicast queries from outside the local link?
+ int num_subtypes;
+ mDNSBool AnonData;
+ service_instance *instances;
+ } servicereg;
+ struct
+ {
+ mDNSInterfaceID interface_id;
+ mDNSu32 flags;
+ mDNSu32 protocol;
+ DNSQuestion q4;
+ DNSQuestion *q42;
+ DNSQuestion q6;
+ DNSQuestion *q62;
+ mDNSu8 v4ans;
+ mDNSu8 v6ans;
+ } addrinfo;
+ struct
+ {
+ mDNSIPPort ReqExt; // External port we originally requested, for logging purposes
+ NATTraversalInfo NATinfo;
+ } pm;
+ struct
+ {
+ DNSServiceFlags flags;
+ DNSQuestion q_all;
+ DNSQuestion q_default;
+ } enumeration;
+ struct
+ {
+ DNSQuestion q;
+ DNSQuestion *q2;
+ mDNSu8 ans;
+ } queryrecord;
+ struct
+ {
+ DNSQuestion qtxt;
+ DNSQuestion qsrv;
+ const ResourceRecord *txt;
+ const ResourceRecord *srv;
+ mDNSs32 ReportTime;
+ mDNSBool external_advertise;
+ } resolve;
+ } u;
+};
+
+// struct physically sits between ipc message header and call-specific fields in the message buffer
+typedef struct
+{
+ DNSServiceFlags flags; // Note: This field is in NETWORK byte order
+ mDNSu32 ifi; // Note: This field is in NETWORK byte order
+ DNSServiceErrorType error; // Note: This field is in NETWORK byte order
+} reply_hdr;
+
+typedef struct reply_state
+{
+ struct reply_state *next; // If there are multiple unsent replies
+ mDNSu32 totallen;
+ mDNSu32 nwriten;
+ ipc_msg_hdr mhdr[1];
+ reply_hdr rhdr[1];
+} reply_state;
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Globals
+#endif
+
+// globals
+mDNSexport mDNS mDNSStorage;
+mDNSexport const char ProgramName[] = "mDNSResponder";
+
+static dnssd_sock_t listenfd = dnssd_InvalidSocket;
+static request_state *all_requests = NULL;
+#ifdef LOCAL_PEERPID
+struct proc_bsdshortinfo proc;
+#endif //LOCAL_PEERPID
+mDNSlocal void set_peer_pid(request_state *request);
+mDNSlocal void LogMcastClientInfo(request_state *req);
+mDNSlocal void GetMcastClients(request_state *req);
+static mDNSu32 mcount; // tracks the current active mcast operations for McastLogging
+static mDNSu32 i_mcount; // sets mcount when McastLogging is enabled(PROF signal is sent)
+static mDNSu32 n_mrecords; // tracks the current active mcast records for McastLogging
+static mDNSu32 n_mquests; // tracks the current active mcast questions for McastLogging
+
+// Note asymmetry here between registration and browsing.
+// For service registrations we only automatically register in domains that explicitly appear in local configuration data
+// (so AutoRegistrationDomains could equally well be called SCPrefRegDomains)
+// For service browsing we also learn automatic browsing domains from the network, so for that case we have:
+// 1. SCPrefBrowseDomains (local configuration data)
+// 2. LocalDomainEnumRecords (locally-generated local-only PTR records -- equivalent to slElem->AuthRecs in uDNS.c)
+// 3. AutoBrowseDomains, which is populated by tracking add/rmv events in AutomaticBrowseDomainChange, the callback function for our mDNS_GetDomains call.
+// By creating and removing our own LocalDomainEnumRecords, we trigger AutomaticBrowseDomainChange callbacks just like domains learned from the network would.
+
+mDNSexport DNameListElem *AutoRegistrationDomains; // Domains where we automatically register for empty-string registrations
+
+static DNameListElem *SCPrefBrowseDomains; // List of automatic browsing domains read from SCPreferences for "empty string" browsing
+static ARListElem *LocalDomainEnumRecords; // List of locally-generated PTR records to augment those we learn from the network
+mDNSexport DNameListElem *AutoBrowseDomains; // List created from those local-only PTR records plus records we get from the network
+
+#define MSG_PAD_BYTES 5 // pad message buffer (read from client) with n zero'd bytes to guarantee
+ // n get_string() calls w/o buffer overrun
+// initialization, setup/teardown functions
+
+// If a platform specifies its own PID file name, we use that
+#ifndef PID_FILE
+#define PID_FILE "/var/run/mDNSResponder.pid"
+#endif
+
+mDNSlocal char *AnonDataToString(const mDNSu8 *ad, int adlen, char *adstr, int adstrlen);
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - General Utility Functions
+#endif
+
+mDNSlocal void FatalError(char *errmsg)
+{
+ char* ptr = NULL;
+ LogMsg("%s: %s", errmsg, dnssd_strerror(dnssd_errno));
+ *ptr = 0; // On OS X abort() doesn't generate a crash log, but writing to zero does
+ abort(); // On platforms where writing to zero doesn't generate an exception, abort instead
+}
+
+mDNSlocal mDNSu32 dnssd_htonl(mDNSu32 l)
+{
+ mDNSu32 ret;
+ char *data = (char*) &ret;
+ put_uint32(l, &data);
+ return ret;
+}
+
+// hack to search-replace perror's to LogMsg's
+mDNSlocal void my_perror(char *errmsg)
+{
+ LogMsg("%s: %d (%s)", errmsg, dnssd_errno, dnssd_strerror(dnssd_errno));
+}
+
+// Throttled version of my_perror: Logs once every 250 msgs
+mDNSlocal void my_throttled_perror(char *err_msg)
+{
+ static int uds_throttle_count = 0;
+ if ((uds_throttle_count++ % 250) == 0)
+ my_perror(err_msg);
+}
+
+// LogMcastQuestion/LogMcastQ should be called after the DNSQuestion struct is initialized(especially for q->TargetQID)
+// Hence all calls are made after mDNS_StartQuery()/mDNS_StopQuery()/mDNS_StopBrowse() is called.
+mDNSlocal void LogMcastQuestion(mDNS *const m, const DNSQuestion *const q, request_state *req, q_state status)
+{
+ if (mDNSOpaque16IsZero(q->TargetQID)) // Check for Mcast Query
+ {
+ mDNSBool mflag = mDNSfalse;
+ if (status == q_start)
+ {
+ if (++mcount == 1)
+ mflag = mDNStrue;
+ }
+ else
+ {
+ mcount--;
+ }
+ LogMcast("%s: %##s (%s) (%s) Client(%d)[%s]", status ? "+Question" : "-Question", q->qname.c, DNSTypeName(q->qtype),
+ q->InterfaceID == mDNSInterface_LocalOnly ? "lo" : q->InterfaceID == mDNSInterface_P2P ? "p2p" :
+ q->InterfaceID == mDNSInterface_Any ? "any" : InterfaceNameForID(m, q->InterfaceID),
+ req->process_id, req->pid_name);
+ LogMcastStateInfo(m, mflag, mDNSfalse, mDNSfalse);
+ }
+ return;
+}
+
+// LogMcastService/LogMcastS should be called after the AuthRecord struct is initialized
+// Hence all calls are made after mDNS_Register()/ just before mDNS_Deregister()
+mDNSlocal void LogMcastService(mDNS *const m, const AuthRecord *const ar, request_state *req, reg_state status)
+{
+ if (!AuthRecord_uDNS(ar)) // Check for Mcast Service
+ {
+ mDNSBool mflag = mDNSfalse;
+ if (status == reg_start)
+ {
+ if (++mcount == 1)
+ mflag = mDNStrue;
+ }
+ else
+ {
+ mcount--;
+ }
+ LogMcast("%s: %##s (%s) (%s) Client(%d)[%s]", status ? "+Service" : "-Service", ar->resrec.name->c, DNSTypeName(ar->resrec.rrtype),
+ ar->resrec.InterfaceID == mDNSInterface_LocalOnly ? "lo" : ar->resrec.InterfaceID == mDNSInterface_P2P ? "p2p" :
+ ar->resrec.InterfaceID == mDNSInterface_Any ? "all" : InterfaceNameForID(m, ar->resrec.InterfaceID),
+ req->process_id, req->pid_name);
+ LogMcastStateInfo(m, mflag, mDNSfalse, mDNSfalse);
+ }
+ return;
+}
+
+// For complete Mcast State Log, pass mDNStrue to mstatelog in LogMcastStateInfo()
+mDNSexport void LogMcastStateInfo(mDNS *const m, mDNSBool mflag, mDNSBool start, mDNSBool mstatelog)
+{
+ if (!mstatelog)
+ {
+ if (!all_requests)
+ {
+ LogMcastNoIdent("<None>");
+ }
+ else
+ {
+ request_state *req, *r;
+ for (req = all_requests; req; req=req->next)
+ {
+ if (req->primary) // If this is a subbordinate operation, check that the parent is in the list
+ {
+ for (r = all_requests; r && r != req; r=r->next)
+ if (r == req->primary)
+ goto foundpar;
+ }
+ // For non-subbordinate operations, and subbordinate operations that have lost their parent, write out their info
+ GetMcastClients(req);
+ foundpar:;
+ }
+ LogMcastNoIdent("--- MCAST RECORDS COUNT[%d] MCAST QUESTIONS COUNT[%d] ---", n_mrecords, n_mquests);
+ n_mrecords = n_mquests = 0; // Reset the values
+ }
+ }
+ else
+ {
+ static mDNSu32 i_mpktnum;
+ i_mcount = 0;
+ if (start)
+ mcount = 0;
+ // mcount is initialized to 0 when the PROF signal is sent since mcount could have
+ // wrong value if MulticastLogging is disabled and then re-enabled
+ LogMcastNoIdent("--- START MCAST STATE LOG ---");
+ if (!all_requests)
+ {
+ mcount = 0;
+ LogMcastNoIdent("<None>");
+ }
+ else
+ {
+ request_state *req, *r;
+ for (req = all_requests; req; req=req->next)
+ {
+ if (req->primary) // If this is a subbordinate operation, check that the parent is in the list
+ {
+ for (r = all_requests; r && r != req; r=r->next)
+ if (r == req->primary)
+ goto foundparent;
+ LogMcastNoIdent("%3d: Orphan operation; parent not found in request list", req->sd);
+ }
+ // For non-subbordinate operations, and subbordinate operations that have lost their parent, write out their info
+ LogMcastClientInfo(req);
+ foundparent:;
+ }
+ if(!mcount) // To initially set mcount
+ mcount = i_mcount;
+ }
+ if (mcount == 0)
+ {
+ i_mpktnum = m->MPktNum;
+ LogMcastNoIdent("--- MCOUNT[%d]: IMPKTNUM[%d] ---", mcount, i_mpktnum);
+ }
+ if (mflag)
+ LogMcastNoIdent("--- MCOUNT[%d]: CMPKTNUM[%d] - IMPKTNUM[%d] = [%d]PKTS ---", mcount, m->MPktNum, i_mpktnum, (m->MPktNum - i_mpktnum));
+ LogMcastNoIdent("--- END MCAST STATE LOG ---");
+ }
+}
+
+mDNSlocal void abort_request(request_state *req)
+{
+ if (req->terminate == (req_termination_fn) ~0)
+ { LogMsg("abort_request: ERROR: Attempt to abort operation %p with req->terminate %p", req, req->terminate); return; }
+
+ // First stop whatever mDNSCore operation we were doing
+ // If this is actually a shared connection operation, then its req->terminate function will scan
+ // the all_requests list and terminate any subbordinate operations sharing this file descriptor
+ if (req->terminate) req->terminate(req);
+
+ if (!dnssd_SocketValid(req->sd))
+ { LogMsg("abort_request: ERROR: Attempt to abort operation %p with invalid fd %d", req, req->sd); return; }
+
+ // Now, if this request_state is not subordinate to some other primary, close file descriptor and discard replies
+ if (!req->primary)
+ {
+ if (req->errsd != req->sd) LogOperation("%3d: Removing FD and closing errsd %d", req->sd, req->errsd);
+ else LogOperation("%3d: Removing FD", req->sd);
+ udsSupportRemoveFDFromEventLoop(req->sd, req->platform_data); // Note: This also closes file descriptor req->sd for us
+ if (req->errsd != req->sd) { dnssd_close(req->errsd); req->errsd = req->sd; }
+
+ while (req->replies) // free pending replies
+ {
+ reply_state *ptr = req->replies;
+ req->replies = req->replies->next;
+ freeL("reply_state (abort)", ptr);
+ }
+ }
+
+ // Set req->sd to something invalid, so that udsserver_idle knows to unlink and free this structure
+#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING
+ // Don't use dnssd_InvalidSocket (-1) because that's the sentinel value MACOSX_MDNS_MALLOC_DEBUGGING uses
+ // for detecting when the memory for an object is inadvertently freed while the object is still on some list
+ req->sd = req->errsd = -2;
+#else
+ req->sd = req->errsd = dnssd_InvalidSocket;
+#endif
+ // We also set req->terminate to a bogus value so we know if abort_request() gets called again for this request
+ req->terminate = (req_termination_fn) ~0;
+}
+
+mDNSlocal void AbortUnlinkAndFree(request_state *req)
+{
+ request_state **p = &all_requests;
+ abort_request(req);
+ while (*p && *p != req) p=&(*p)->next;
+ if (*p) { *p = req->next; freeL("request_state/AbortUnlinkAndFree", req); }
+ else LogMsg("AbortUnlinkAndFree: ERROR: Attempt to abort operation %p not in list", req);
+}
+
+mDNSlocal reply_state *create_reply(const reply_op_t op, const size_t datalen, request_state *const request)
+{
+ reply_state *reply;
+
+ if ((unsigned)datalen < sizeof(reply_hdr))
+ {
+ LogMsg("ERROR: create_reply - data length less than length of required fields");
+ return NULL;
+ }
+
+ reply = mallocL("reply_state", sizeof(reply_state) + datalen - sizeof(reply_hdr));
+ if (!reply) FatalError("ERROR: malloc");
+
+ reply->next = mDNSNULL;
+ reply->totallen = (mDNSu32)datalen + sizeof(ipc_msg_hdr);
+ reply->nwriten = 0;
+
+ reply->mhdr->version = VERSION;
+ reply->mhdr->datalen = (mDNSu32)datalen;
+ reply->mhdr->ipc_flags = 0;
+ reply->mhdr->op = op;
+ reply->mhdr->client_context = request->hdr.client_context;
+ reply->mhdr->reg_index = 0;
+
+ return reply;
+}
+
+// Append a reply to the list in a request object
+// If our request is sharing a connection, then we append our reply_state onto the primary's list
+mDNSlocal void append_reply(request_state *req, reply_state *rep)
+{
+ request_state *r = req->primary ? req->primary : req;
+ reply_state **ptr = &r->replies;
+ while (*ptr) ptr = &(*ptr)->next;
+ *ptr = rep;
+ rep->next = NULL;
+}
+
+// Generates a response message giving name, type, domain, plus interface index,
+// suitable for a browse result or service registration result.
+// On successful completion rep is set to point to a malloc'd reply_state struct
+mDNSlocal mStatus GenerateNTDResponse(const domainname *const servicename, const mDNSInterfaceID id,
+ request_state *const request, reply_state **const rep, reply_op_t op, DNSServiceFlags flags, mStatus err)
+{
+ domainlabel name;
+ domainname type, dom;
+ *rep = NULL;
+ if (!DeconstructServiceName(servicename, &name, &type, &dom))
+ return kDNSServiceErr_Invalid;
+ else
+ {
+ char namestr[MAX_DOMAIN_LABEL+1];
+ char typestr[MAX_ESCAPED_DOMAIN_NAME];
+ char domstr [MAX_ESCAPED_DOMAIN_NAME];
+ int len;
+ char *data;
+
+ ConvertDomainLabelToCString_unescaped(&name, namestr);
+ ConvertDomainNameToCString(&type, typestr);
+ ConvertDomainNameToCString(&dom, domstr);
+
+ // Calculate reply data length
+ len = sizeof(DNSServiceFlags);
+ len += sizeof(mDNSu32); // if index
+ len += sizeof(DNSServiceErrorType);
+ len += (int) (strlen(namestr) + 1);
+ len += (int) (strlen(typestr) + 1);
+ len += (int) (strlen(domstr) + 1);
+
+ // Build reply header
+ *rep = create_reply(op, len, request);
+ (*rep)->rhdr->flags = dnssd_htonl(flags);
+ (*rep)->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id, mDNSfalse));
+ (*rep)->rhdr->error = dnssd_htonl(err);
+
+ // Build reply body
+ data = (char *)&(*rep)->rhdr[1];
+ put_string(namestr, &data);
+ put_string(typestr, &data);
+ put_string(domstr, &data);
+
+ return mStatus_NoError;
+ }
+}
+
+// Special support to enable the DNSServiceBrowse call made by Bonjour Browser
+// Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse
+mDNSlocal void GenerateBonjourBrowserResponse(const domainname *const servicename, const mDNSInterfaceID id,
+ request_state *const request, reply_state **const rep, reply_op_t op, DNSServiceFlags flags, mStatus err)
+{
+ char namestr[MAX_DOMAIN_LABEL+1];
+ char typestr[MAX_ESCAPED_DOMAIN_NAME];
+ static const char domstr[] = ".";
+ int len;
+ char *data;
+
+ *rep = NULL;
+
+ // 1. Put first label in namestr
+ ConvertDomainLabelToCString_unescaped((const domainlabel *)servicename, namestr);
+
+ // 2. Put second label and "local" into typestr
+ mDNS_snprintf(typestr, sizeof(typestr), "%#s.local.", SecondLabel(servicename));
+
+ // Calculate reply data length
+ len = sizeof(DNSServiceFlags);
+ len += sizeof(mDNSu32); // if index
+ len += sizeof(DNSServiceErrorType);
+ len += (int) (strlen(namestr) + 1);
+ len += (int) (strlen(typestr) + 1);
+ len += (int) (strlen(domstr) + 1);
+
+ // Build reply header
+ *rep = create_reply(op, len, request);
+ (*rep)->rhdr->flags = dnssd_htonl(flags);
+ (*rep)->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id, mDNSfalse));
+ (*rep)->rhdr->error = dnssd_htonl(err);
+
+ // Build reply body
+ data = (char *)&(*rep)->rhdr[1];
+ put_string(namestr, &data);
+ put_string(typestr, &data);
+ put_string(domstr, &data);
+}
+
+// Returns a resource record (allocated w/ malloc) containing the data found in an IPC message
+// Data must be in the following 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
+mDNSlocal AuthRecord *read_rr_from_ipc_msg(request_state *request, int GetTTL, int validate_flags)
+{
+ DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
+ mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
+ char name[256];
+ int str_err = get_string(&request->msgptr, request->msgend, name, sizeof(name));
+ mDNSu16 type = get_uint16(&request->msgptr, request->msgend);
+ mDNSu16 class = get_uint16(&request->msgptr, request->msgend);
+ mDNSu16 rdlen = get_uint16(&request->msgptr, request->msgend);
+ const char *rdata = get_rdata (&request->msgptr, request->msgend, rdlen);
+ mDNSu32 ttl = GetTTL ? get_uint32(&request->msgptr, request->msgend) : 0;
+ int storage_size = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody);
+ AuthRecord *rr;
+ mDNSInterfaceID InterfaceID;
+ AuthRecType artype;
+
+ request->flags = flags;
+
+ if (str_err) { LogMsg("ERROR: read_rr_from_ipc_msg - get_string"); return NULL; }
+
+ if (!request->msgptr) { LogMsg("Error reading Resource Record from client"); return NULL; }
+
+ if (validate_flags &&
+ !((flags & kDNSServiceFlagsShared) == kDNSServiceFlagsShared) &&
+ !((flags & kDNSServiceFlagsUnique) == kDNSServiceFlagsUnique))
+ {
+ LogMsg("ERROR: Bad resource record flags (must be kDNSServiceFlagsShared or kDNSServiceFlagsUnique)");
+ return NULL;
+ }
+
+ rr = mallocL("AuthRecord/read_rr_from_ipc_msg", sizeof(AuthRecord) - sizeof(RDataBody) + storage_size);
+ if (!rr) FatalError("ERROR: malloc");
+
+ InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+ if (InterfaceID == mDNSInterface_LocalOnly)
+ artype = AuthRecordLocalOnly;
+ else if (InterfaceID == mDNSInterface_P2P)
+ artype = AuthRecordP2P;
+ else if ((InterfaceID == mDNSInterface_Any) && (flags & kDNSServiceFlagsIncludeP2P)
+ && (flags & kDNSServiceFlagsIncludeAWDL))
+ artype = AuthRecordAnyIncludeAWDLandP2P;
+ else if ((InterfaceID == mDNSInterface_Any) && (flags & kDNSServiceFlagsIncludeP2P))
+ artype = AuthRecordAnyIncludeP2P;
+ else if ((InterfaceID == mDNSInterface_Any) && (flags & kDNSServiceFlagsIncludeAWDL))
+ artype = AuthRecordAnyIncludeAWDL;
+ else
+ artype = AuthRecordAny;
+
+ mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, type, 0,
+ (mDNSu8) ((flags & kDNSServiceFlagsShared) ? kDNSRecordTypeShared : kDNSRecordTypeUnique), artype, mDNSNULL, mDNSNULL);
+
+ if (!MakeDomainNameFromDNSNameString(&rr->namestorage, name))
+ {
+ LogMsg("ERROR: bad name: %s", name);
+ freeL("AuthRecord/read_rr_from_ipc_msg", rr);
+ return NULL;
+ }
+
+ if (flags & kDNSServiceFlagsAllowRemoteQuery) rr->AllowRemoteQuery = mDNStrue;
+ rr->resrec.rrclass = class;
+ rr->resrec.rdlength = rdlen;
+ rr->resrec.rdata->MaxRDLength = rdlen;
+ mDNSPlatformMemCopy(rr->resrec.rdata->u.data, rdata, rdlen);
+ if (GetTTL) rr->resrec.rroriginalttl = ttl;
+ rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
+ SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rr->rdatahash for us
+ return rr;
+}
+
+mDNSlocal 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;
+}
+
+mDNSlocal void send_all(dnssd_sock_t s, const char *ptr, int len)
+{
+ int n = send(s, ptr, len, 0);
+ // On a freshly-created Unix Domain Socket, the kernel should *never* fail to buffer a small write for us
+ // (four bytes for a typical error code return, 12 bytes for DNSServiceGetProperty(DaemonVersion)).
+ // If it does fail, we don't attempt to handle this failure, but we do log it so we know something is wrong.
+ if (n < len)
+ LogMsg("ERROR: send_all(%d) wrote %d of %d errno %d (%s)",
+ s, n, len, dnssd_errno, dnssd_strerror(dnssd_errno));
+}
+
+#if 0
+mDNSlocal mDNSBool AuthorizedDomain(const request_state * const request, const domainname * const d, const DNameListElem * const doms)
+{
+ const DNameListElem *delem = mDNSNULL;
+ int bestDelta = -1; // the delta of the best match, lower is better
+ int dLabels = 0;
+ mDNSBool allow = mDNSfalse;
+
+ if (SystemUID(request->uid)) return mDNStrue;
+
+ dLabels = CountLabels(d);
+ for (delem = doms; delem; delem = delem->next)
+ {
+ if (delem->uid)
+ {
+ int delemLabels = CountLabels(&delem->name);
+ int delta = dLabels - delemLabels;
+ if ((bestDelta == -1 || delta <= bestDelta) && SameDomainName(&delem->name, SkipLeadingLabels(d, delta)))
+ {
+ bestDelta = delta;
+ allow = (allow || (delem->uid == request->uid));
+ }
+ }
+ }
+
+ return bestDelta == -1 ? mDNStrue : allow;
+}
+#endif
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - external helpers
+#endif
+
+mDNSlocal mDNSBool callExternalHelpers(mDNSInterfaceID InterfaceID, const domainname *const domain, DNSServiceFlags flags)
+{
+#if APPLE_OSX_mDNSResponder
+
+ if ( ((InterfaceID == mDNSInterface_Any) && (flags & (kDNSServiceFlagsIncludeP2P | kDNSServiceFlagsIncludeAWDL)) && IsLocalDomain(domain))
+ || mDNSPlatformInterfaceIsD2D(InterfaceID))
+ {
+ return mDNStrue;
+ }
+ else
+ return mDNSfalse;
+
+#else
+ (void) InterfaceID;
+ (void) domain;
+ (void) flags;
+
+ return mDNSfalse;
+#endif // APPLE_OSX_mDNSResponder
+}
+
+mDNSlocal void external_start_advertising_helper(service_instance *const instance)
+{
+ AuthRecord *st = instance->subtypes;
+ ExtraResourceRecord *e;
+ int i;
+
+ if (mDNSIPPortIsZero(instance->request->u.servicereg.port))
+ {
+ LogInfo("external_start_advertising_helper: Not registering service with port number zero");
+ return;
+ }
+
+ if (instance->external_advertise) LogMsg("external_start_advertising_helper: external_advertise already set!");
+
+ for ( i = 0; i < instance->request->u.servicereg.num_subtypes; i++)
+ external_start_advertising_service(&st[i].resrec, instance->request->flags);
+
+ external_start_advertising_service(&instance->srs.RR_PTR.resrec, instance->request->flags);
+ external_start_advertising_service(&instance->srs.RR_SRV.resrec, instance->request->flags);
+ external_start_advertising_service(&instance->srs.RR_TXT.resrec, instance->request->flags);
+
+ for (e = instance->srs.Extras; e; e = e->next)
+ external_start_advertising_service(&e->r.resrec, instance->request->flags);
+
+ instance->external_advertise = mDNStrue;
+}
+
+mDNSlocal void external_stop_advertising_helper(service_instance *const instance)
+{
+ AuthRecord *st = instance->subtypes;
+ ExtraResourceRecord *e;
+ int i;
+
+ if (!instance->external_advertise) return;
+
+ LogInfo("external_stop_advertising_helper: calling external_stop_advertising_service");
+
+ for ( i = 0; i < instance->request->u.servicereg.num_subtypes; i++)
+ external_stop_advertising_service(&st[i].resrec, instance->request->flags);
+
+ external_stop_advertising_service(&instance->srs.RR_PTR.resrec, instance->request->flags);
+ external_stop_advertising_service(&instance->srs.RR_SRV.resrec, instance->request->flags);
+ external_stop_advertising_service(&instance->srs.RR_TXT.resrec, instance->request->flags);
+
+ for (e = instance->srs.Extras; e; e = e->next)
+ external_stop_advertising_service(&e->r.resrec, instance->request->flags);
+
+ instance->external_advertise = mDNSfalse;
+}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - DNSServiceRegister
+#endif
+
+mDNSexport void FreeExtraRR(mDNS *const m, AuthRecord *const rr, mStatus result)
+{
+ ExtraResourceRecord *extra = (ExtraResourceRecord *)rr->RecordContext;
+ (void)m; // Unused
+
+ if (result != mStatus_MemFree) { LogMsg("Error: FreeExtraRR invoked with unexpected error %d", result); return; }
+
+ LogInfo(" FreeExtraRR %s", RRDisplayString(m, &rr->resrec));
+
+ if (rr->resrec.rdata != &rr->rdatastorage)
+ freeL("Extra RData", rr->resrec.rdata);
+ freeL("ExtraResourceRecord/FreeExtraRR", extra);
+}
+
+mDNSlocal void unlink_and_free_service_instance(service_instance *srv)
+{
+ ExtraResourceRecord *e = srv->srs.Extras, *tmp;
+
+ external_stop_advertising_helper(srv);
+
+ // clear pointers from parent struct
+ if (srv->request)
+ {
+ service_instance **p = &srv->request->u.servicereg.instances;
+ while (*p)
+ {
+ if (*p == srv) { *p = (*p)->next; break; }
+ p = &(*p)->next;
+ }
+ }
+
+ while (e)
+ {
+ e->r.RecordContext = e;
+ tmp = e;
+ e = e->next;
+ FreeExtraRR(&mDNSStorage, &tmp->r, mStatus_MemFree);
+ }
+
+ if (srv->srs.RR_TXT.resrec.rdata != &srv->srs.RR_TXT.rdatastorage)
+ freeL("TXT RData", srv->srs.RR_TXT.resrec.rdata);
+
+ if (srv->subtypes)
+ {
+ freeL("ServiceSubTypes", srv->subtypes);
+ srv->subtypes = NULL;
+ }
+ if (srv->srs.AnonData)
+ {
+ freeL("Anonymous", (void *)srv->srs.AnonData);
+ srv->srs.AnonData = NULL;
+ }
+ freeL("service_instance", srv);
+}
+
+// Count how many other service records we have locally with the same name, but different rdata.
+// For auto-named services, we can have at most one per machine -- if we allowed two auto-named services of
+// the same type on the same machine, we'd get into an infinite autoimmune-response loop of continuous renaming.
+mDNSexport int CountPeerRegistrations(mDNS *const m, ServiceRecordSet *const srs)
+{
+ int count = 0;
+ ResourceRecord *r = &srs->RR_SRV.resrec;
+ AuthRecord *rr;
+
+ for (rr = m->ResourceRecords; rr; rr=rr->next)
+ if (rr->resrec.rrtype == kDNSType_SRV && SameDomainName(rr->resrec.name, r->name) && !IdenticalSameNameRecord(&rr->resrec, r))
+ count++;
+
+ verbosedebugf("%d peer registrations for %##s", count, r->name->c);
+ return(count);
+}
+
+mDNSexport int CountExistingRegistrations(domainname *srv, mDNSIPPort port)
+{
+ int count = 0;
+ AuthRecord *rr;
+ for (rr = mDNSStorage.ResourceRecords; rr; rr=rr->next)
+ if (rr->resrec.rrtype == kDNSType_SRV &&
+ mDNSSameIPPort(rr->resrec.rdata->u.srv.port, port) &&
+ SameDomainName(rr->resrec.name, srv))
+ count++;
+ return(count);
+}
+
+mDNSlocal void SendServiceRemovalNotification(ServiceRecordSet *const srs)
+{
+ reply_state *rep;
+ service_instance *instance = srs->ServiceContext;
+ if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, 0, mStatus_NoError) != mStatus_NoError)
+ LogMsg("%3d: SendServiceRemovalNotification: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c);
+ else { append_reply(instance->request, rep); instance->clientnotified = mDNSfalse; }
+}
+
+// service registration callback performs three duties - frees memory for deregistered services,
+// handles name conflicts, and delivers completed registration information to the client
+mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result)
+{
+ mStatus err;
+ mDNSBool SuppressError = mDNSfalse;
+ service_instance *instance;
+ reply_state *rep;
+ (void)m; // Unused
+
+ if (!srs) { LogMsg("regservice_callback: srs is NULL %d", result); return; }
+
+ instance = srs->ServiceContext;
+ if (!instance) { LogMsg("regservice_callback: srs->ServiceContext is NULL %d", result); return; }
+
+ // don't send errors up to client for wide-area, empty-string registrations
+ if (instance->request &&
+ instance->request->u.servicereg.default_domain &&
+ !instance->default_local)
+ SuppressError = mDNStrue;
+
+ if (mDNS_LoggingEnabled)
+ {
+ const char *const fmt =
+ (result == mStatus_NoError) ? "%s DNSServiceRegister(%##s, %u) REGISTERED" :
+ (result == mStatus_MemFree) ? "%s DNSServiceRegister(%##s, %u) DEREGISTERED" :
+ (result == mStatus_NameConflict) ? "%s DNSServiceRegister(%##s, %u) NAME CONFLICT" :
+ "%s DNSServiceRegister(%##s, %u) %s %d";
+ char prefix[16] = "---:";
+ if (instance->request) mDNS_snprintf(prefix, sizeof(prefix), "%3d:", instance->request->sd);
+ LogOperation(fmt, prefix, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port),
+ SuppressError ? "suppressed error" : "CALLBACK", result);
+ }
+
+ if (!instance->request && result != mStatus_MemFree) { LogMsg("regservice_callback: instance->request is NULL %d", result); return; }
+
+ if (result == mStatus_NoError)
+ {
+ if (instance->request->u.servicereg.allowremotequery)
+ {
+ ExtraResourceRecord *e;
+ srs->RR_ADV.AllowRemoteQuery = mDNStrue;
+ srs->RR_PTR.AllowRemoteQuery = mDNStrue;
+ srs->RR_SRV.AllowRemoteQuery = mDNStrue;
+ srs->RR_TXT.AllowRemoteQuery = mDNStrue;
+ for (e = instance->srs.Extras; e; e = e->next) e->r.AllowRemoteQuery = mDNStrue;
+ }
+
+ if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError)
+ LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c);
+ else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; }
+
+ if (callExternalHelpers(instance->request->u.servicereg.InterfaceID, &instance->domain, instance->request->flags))
+ {
+ LogInfo("regservice_callback: calling external_start_advertising_helper()");
+ external_start_advertising_helper(instance);
+ }
+ if (instance->request->u.servicereg.autoname && CountPeerRegistrations(m, srs) == 0)
+ RecordUpdatedNiceLabel(m, 0); // Successfully got new name, tell user immediately
+ }
+ else if (result == mStatus_MemFree)
+ {
+ if (instance->request && instance->renameonmemfree)
+ {
+ external_stop_advertising_helper(instance);
+ instance->renameonmemfree = 0;
+ err = mDNS_RenameAndReregisterService(m, srs, &instance->request->u.servicereg.name);
+ if (err) LogMsg("ERROR: regservice_callback - RenameAndReregisterService returned %d", err);
+ // error should never happen - safest to log and continue
+ }
+ else
+ unlink_and_free_service_instance(instance);
+ }
+ else if (result == mStatus_NameConflict)
+ {
+ if (instance->request->u.servicereg.autorename)
+ {
+ external_stop_advertising_helper(instance);
+ if (instance->request->u.servicereg.autoname && CountPeerRegistrations(m, srs) == 0)
+ {
+ // On conflict for an autoname service, rename and reregister *all* autoname services
+ IncrementLabelSuffix(&m->nicelabel, mDNStrue);
+ mDNS_ConfigChanged(m); // Will call back into udsserver_handle_configchange()
+ }
+ else // On conflict for a non-autoname service, rename and reregister just that one service
+ {
+ if (instance->clientnotified) SendServiceRemovalNotification(srs);
+ mDNS_RenameAndReregisterService(m, srs, mDNSNULL);
+ }
+ }
+ else
+ {
+ if (!SuppressError)
+ {
+ if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError)
+ LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c);
+ else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; }
+ }
+ unlink_and_free_service_instance(instance);
+ }
+ }
+ else // Not mStatus_NoError, mStatus_MemFree, or mStatus_NameConflict
+ {
+ if (!SuppressError)
+ {
+ if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError)
+ LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c);
+ else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; }
+ }
+ }
+}
+
+mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord *rr, mStatus result)
+{
+ (void)m; // Unused
+ if (!rr->RecordContext) // parent struct already freed by termination callback
+ {
+ if (result == mStatus_NoError)
+ LogMsg("Error: regrecord_callback: successful registration of orphaned record %s", ARDisplayString(m, rr));
+ else
+ {
+ if (result != mStatus_MemFree) LogMsg("regrecord_callback: error %d received after parent termination", result);
+
+ // We come here when the record is being deregistered either from DNSServiceRemoveRecord or connection_termination.
+ // If the record has been updated, we need to free the rdata. Everytime we call mDNS_Update, it calls update_callback
+ // with the old rdata (so that we can free it) and stores the new rdata in "rr->resrec.rdata". This means, we need
+ // to free the latest rdata for which the update_callback was never called with.
+ if (rr->resrec.rdata != &rr->rdatastorage) freeL("RData/regrecord_callback", rr->resrec.rdata);
+ freeL("AuthRecord/regrecord_callback", rr);
+ }
+ }
+ else
+ {
+ registered_record_entry *re = rr->RecordContext;
+ request_state *request = re->request;
+
+ if (mDNS_LoggingEnabled)
+ {
+ char *fmt = (result == mStatus_NoError) ? "%3d: DNSServiceRegisterRecord(%u %s) REGISTERED" :
+ (result == mStatus_MemFree) ? "%3d: DNSServiceRegisterRecord(%u %s) DEREGISTERED" :
+ (result == mStatus_NameConflict) ? "%3d: DNSServiceRegisterRecord(%u %s) NAME CONFLICT" :
+ "%3d: DNSServiceRegisterRecord(%u %s) %d";
+ LogOperation(fmt, request->sd, re->key, RRDisplayString(m, &rr->resrec), result);
+ }
+
+ if (result != mStatus_MemFree)
+ {
+ int len = sizeof(DNSServiceFlags) + sizeof(mDNSu32) + sizeof(DNSServiceErrorType);
+ reply_state *reply = create_reply(reg_record_reply_op, len, request);
+ reply->mhdr->client_context = re->regrec_client_context;
+ reply->rhdr->flags = dnssd_htonl(0);
+ reply->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, rr->resrec.InterfaceID, mDNSfalse));
+ reply->rhdr->error = dnssd_htonl(result);
+ append_reply(request, reply);
+ }
+
+ if (result)
+ {
+ // If this is a callback to a keepalive record, do not free it.
+ if (result == mStatus_BadStateErr)
+ {
+ LogInfo("regrecord_callback: Callback with error code mStatus_BadStateErr - not freeing the record.");
+ }
+ else
+ {
+ // unlink from list, free memory
+ registered_record_entry **ptr = &request->u.reg_recs;
+ while (*ptr && (*ptr) != re) ptr = &(*ptr)->next;
+ if (!*ptr) { LogMsg("regrecord_callback - record not in list!"); return; }
+ *ptr = (*ptr)->next;
+ freeL("registered_record_entry AuthRecord regrecord_callback", re->rr);
+ freeL("registered_record_entry regrecord_callback", re);
+ }
+ }
+ else
+ {
+ if (re->external_advertise) LogMsg("regrecord_callback: external_advertise already set!");
+
+ if (callExternalHelpers(re->origInterfaceID, &rr->namestorage, request->flags))
+ {
+ LogInfo("regrecord_callback: calling external_start_advertising_service");
+ external_start_advertising_service(&rr->resrec, request->flags);
+ re->external_advertise = mDNStrue;
+ }
+ }
+ }
+}
+
+// set_peer_pid() is called after mem is allocated for each new request in NewRequest()
+// This accounts for 2 places (connect_callback, request_callback)
+mDNSlocal void set_peer_pid(request_state *request)
+{
+ pid_t p = (pid_t) -1;
+ socklen_t len = sizeof(p);
+ request->pid_name[0] = '\0';
+ request->process_id = -1;
+#ifdef LOCAL_PEERPID
+ if (request->sd < 0)
+ return;
+ // to extract the pid value
+ if (getsockopt(request->sd, SOL_LOCAL, LOCAL_PEERPID, &p, &len) != 0)
+ return;
+ // to extract the process name from the pid value
+ if (proc_pidinfo(p, PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0)
+ return;
+ mDNSPlatformStrCopy(request->pid_name, proc.pbsi_comm);
+ request->process_id = p;
+#else // !LOCAL_PEERPID
+ len = 0;
+ if (request->sd < 0)
+ return;
+ LogInfo("set_peer_pid: Not Supported on this version of OS");
+#endif // LOCAL_PEERPID
+}
+
+mDNSlocal void connection_termination(request_state *request)
+{
+ // When terminating a shared connection, we need to scan the all_requests list
+ // and terminate any subbordinate operations sharing this file descriptor
+ request_state **req = &all_requests;
+
+ LogOperation("%3d: DNSServiceCreateConnection STOP PID[%d](%s)", request->sd, request->process_id, request->pid_name);
+
+ while (*req)
+ {
+ if ((*req)->primary == request)
+ {
+ // Since we're already doing a list traversal, we unlink the request directly instead of using AbortUnlinkAndFree()
+ request_state *tmp = *req;
+ if (tmp->primary == tmp) LogMsg("connection_termination ERROR (*req)->primary == *req for %p %d", tmp, tmp->sd);
+ if (tmp->replies) LogMsg("connection_termination ERROR How can subordinate req %p %d have replies queued?", tmp, tmp->sd);
+ abort_request(tmp);
+ *req = tmp->next;
+ freeL("request_state/connection_termination", tmp);
+ }
+ else
+ req = &(*req)->next;
+ }
+
+ while (request->u.reg_recs)
+ {
+ registered_record_entry *ptr = request->u.reg_recs;
+ LogOperation("%3d: DNSServiceRegisterRecord(%u %s) STOP PID[%d](%s)", request->sd, ptr->key, RRDisplayString(&mDNSStorage, &ptr->rr->resrec), request->process_id, request->pid_name);
+ request->u.reg_recs = request->u.reg_recs->next;
+ ptr->rr->RecordContext = NULL;
+ if (ptr->external_advertise)
+ {
+ ptr->external_advertise = mDNSfalse;
+ external_stop_advertising_service(&ptr->rr->resrec, request->flags);
+ }
+ LogMcastS(&mDNSStorage, ptr->rr, request, reg_stop);
+ mDNS_Deregister(&mDNSStorage, ptr->rr); // Will free ptr->rr for us
+ freeL("registered_record_entry/connection_termination", ptr);
+ }
+}
+
+mDNSlocal void handle_cancel_request(request_state *request)
+{
+ request_state **req = &all_requests;
+ LogOperation("%3d: Cancel %08X %08X", request->sd, request->hdr.client_context.u32[1], request->hdr.client_context.u32[0]);
+ while (*req)
+ {
+ if ((*req)->primary == request &&
+ (*req)->hdr.client_context.u32[0] == request->hdr.client_context.u32[0] &&
+ (*req)->hdr.client_context.u32[1] == request->hdr.client_context.u32[1])
+ {
+ // Since we're already doing a list traversal, we unlink the request directly instead of using AbortUnlinkAndFree()
+ request_state *tmp = *req;
+ abort_request(tmp);
+ *req = tmp->next;
+ freeL("request_state/handle_cancel_request", tmp);
+ }
+ else
+ req = &(*req)->next;
+ }
+}
+
+mDNSlocal mStatus handle_regrecord_request(request_state *request)
+{
+ mStatus err = mStatus_BadParamErr;
+ AuthRecord *rr = read_rr_from_ipc_msg(request, 1, 1);
+ if (rr)
+ {
+ registered_record_entry *re;
+ // Don't allow non-local domains to be regsitered as LocalOnly. Allowing this would permit
+ // clients to register records such as www.bigbank.com A w.x.y.z to redirect Safari.
+ if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly && !IsLocalDomain(rr->resrec.name) &&
+ rr->resrec.rrclass == kDNSClass_IN && (rr->resrec.rrtype == kDNSType_A || rr->resrec.rrtype == kDNSType_AAAA ||
+ rr->resrec.rrtype == kDNSType_CNAME))
+ {
+ freeL("AuthRecord/handle_regrecord_request", rr);
+ return (mStatus_BadParamErr);
+ }
+ // allocate registration entry, link into list
+ re = mallocL("registered_record_entry", sizeof(registered_record_entry));
+ if (!re)
+ FatalError("ERROR: malloc");
+ re->key = request->hdr.reg_index;
+ re->rr = rr;
+ re->regrec_client_context = request->hdr.client_context;
+ re->request = request;
+ re->external_advertise = mDNSfalse;
+ rr->RecordContext = re;
+ rr->RecordCallback = regrecord_callback;
+
+ re->origInterfaceID = rr->resrec.InterfaceID;
+ if (rr->resrec.InterfaceID == mDNSInterface_P2P)
+ rr->resrec.InterfaceID = mDNSInterface_Any;
+#if 0
+ if (!AuthorizedDomain(request, rr->resrec.name, AutoRegistrationDomains)) return (mStatus_NoError);
+#endif
+ if (rr->resrec.rroriginalttl == 0)
+ rr->resrec.rroriginalttl = DefaultTTLforRRType(rr->resrec.rrtype);
+
+ LogOperation("%3d: DNSServiceRegisterRecord(%u %s) START PID[%d](%s)", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec),
+ request->process_id, request->pid_name);
+
+ err = mDNS_Register(&mDNSStorage, rr);
+ if (err)
+ {
+ LogOperation("%3d: DNSServiceRegisterRecord(%u %s) ERROR (%d)", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec), err);
+ freeL("registered_record_entry", re);
+ freeL("registered_record_entry/AuthRecord", rr);
+ }
+ else
+ {
+ LogMcastS(&mDNSStorage, rr, request, reg_start);
+ re->next = request->u.reg_recs;
+ request->u.reg_recs = re;
+ }
+ }
+ return(err);
+}
+
+mDNSlocal void UpdateDeviceInfoRecord(mDNS *const m);
+
+mDNSlocal void regservice_termination_callback(request_state *request)
+{
+ if (!request)
+ {
+ LogMsg("regservice_termination_callback context is NULL");
+ return;
+ }
+ while (request->u.servicereg.instances)
+ {
+ service_instance *p = request->u.servicereg.instances;
+ request->u.servicereg.instances = request->u.servicereg.instances->next;
+ // only safe to free memory if registration is not valid, i.e. deregister fails (which invalidates p)
+ LogOperation("%3d: DNSServiceRegister(%##s, %u) STOP PID[%d](%s)", request->sd, p->srs.RR_SRV.resrec.name->c,
+ mDNSVal16(p->srs.RR_SRV.resrec.rdata->u.srv.port), request->process_id, request->pid_name);
+
+ external_stop_advertising_helper(p);
+
+ // Clear backpointer *before* calling mDNS_DeregisterService/unlink_and_free_service_instance
+ // We don't need unlink_and_free_service_instance to cut its element from the list, because we're already advancing
+ // request->u.servicereg.instances as we work our way through the list, implicitly cutting one element at a time
+ // We can't clear p->request *after* the calling mDNS_DeregisterService/unlink_and_free_service_instance
+ // because by then we might have already freed p
+ p->request = NULL;
+ LogMcastS(&mDNSStorage, &p->srs.RR_SRV, request, reg_stop);
+ if (mDNS_DeregisterService(&mDNSStorage, &p->srs))
+ {
+ unlink_and_free_service_instance(p);
+ // Don't touch service_instance *p after this -- it's likely to have been freed already
+ }
+ }
+ if (request->u.servicereg.txtdata)
+ {
+ freeL("service_info txtdata", request->u.servicereg.txtdata);
+ request->u.servicereg.txtdata = NULL;
+ }
+ if (request->u.servicereg.autoname)
+ {
+ // Clear autoname before calling UpdateDeviceInfoRecord() so it doesn't mistakenly include this in its count of active autoname registrations
+ request->u.servicereg.autoname = mDNSfalse;
+ UpdateDeviceInfoRecord(&mDNSStorage);
+ }
+}
+
+mDNSlocal request_state *LocateSubordinateRequest(request_state *request)
+{
+ request_state *req;
+ for (req = all_requests; req; req = req->next)
+ if (req->primary == request &&
+ req->hdr.client_context.u32[0] == request->hdr.client_context.u32[0] &&
+ req->hdr.client_context.u32[1] == request->hdr.client_context.u32[1]) return(req);
+ return(request);
+}
+
+mDNSlocal mStatus add_record_to_service(request_state *request, service_instance *instance, mDNSu16 rrtype, mDNSu16 rdlen, const char *rdata, mDNSu32 ttl)
+{
+ ServiceRecordSet *srs = &instance->srs;
+ mStatus result;
+ mDNSu32 coreFlags = 0; // translate to corresponding mDNSCore flag definitions
+ int size = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody);
+ ExtraResourceRecord *extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size);
+ if (!extra) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; }
+
+ mDNSPlatformMemZero(extra, sizeof(ExtraResourceRecord)); // OK if oversized rdata not zero'd
+ extra->r.resrec.rrtype = rrtype;
+ extra->r.rdatastorage.MaxRDLength = (mDNSu16) size;
+ extra->r.resrec.rdlength = rdlen;
+ mDNSPlatformMemCopy(&extra->r.rdatastorage.u.data, rdata, rdlen);
+ // use InterfaceID value from DNSServiceRegister() call that created the original service
+ extra->r.resrec.InterfaceID = request->u.servicereg.InterfaceID;
+
+ if (request->flags & kDNSServiceFlagsIncludeP2P)
+ coreFlags |= coreFlagIncludeP2P;
+ if (request->flags & kDNSServiceFlagsIncludeAWDL)
+ coreFlags |= coreFlagIncludeAWDL;
+
+ result = mDNS_AddRecordToService(&mDNSStorage, srs, extra, &extra->r.rdatastorage, ttl, coreFlags);
+ if (result)
+ {
+ freeL("ExtraResourceRecord/add_record_to_service", extra);
+ return result;
+ }
+ LogMcastS(&mDNSStorage, &srs->RR_PTR, request, reg_start);
+
+ extra->ClientID = request->hdr.reg_index;
+ if ( instance->external_advertise
+ && callExternalHelpers(request->u.servicereg.InterfaceID, &instance->domain, request->flags))
+ {
+ LogInfo("add_record_to_service: calling external_start_advertising_service");
+ external_start_advertising_service(&extra->r.resrec, request->flags);
+ }
+ return result;
+}
+
+mDNSlocal mStatus handle_add_request(request_state *request)
+{
+ service_instance *i;
+ mStatus result = mStatus_UnknownErr;
+ DNSServiceFlags flags = get_flags (&request->msgptr, request->msgend);
+ mDNSu16 rrtype = get_uint16(&request->msgptr, request->msgend);
+ mDNSu16 rdlen = get_uint16(&request->msgptr, request->msgend);
+ const char *rdata = get_rdata (&request->msgptr, request->msgend, rdlen);
+ mDNSu32 ttl = get_uint32(&request->msgptr, request->msgend);
+ if (!ttl) ttl = DefaultTTLforRRType(rrtype);
+ (void)flags; // Unused
+
+ if (!request->msgptr) { LogMsg("%3d: DNSServiceAddRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+ // If this is a shared connection, check if the operation actually applies to a subordinate request_state object
+ if (request->terminate == connection_termination) request = LocateSubordinateRequest(request);
+
+ if (request->terminate != regservice_termination_callback)
+ { LogMsg("%3d: DNSServiceAddRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); }
+
+ // For a service registered with zero port, don't allow adding records. This mostly happens due to a bug
+ // in the application. See radar://9165807.
+ if (mDNSIPPortIsZero(request->u.servicereg.port))
+ { LogMsg("%3d: DNSServiceAddRecord: adding record to a service registered with zero port", request->sd); return(mStatus_BadParamErr); }
+
+ LogOperation("%3d: DNSServiceAddRecord(%X, %##s, %s, %d)", request->sd, flags,
+ (request->u.servicereg.instances) ? request->u.servicereg.instances->srs.RR_SRV.resrec.name->c : NULL, DNSTypeName(rrtype), rdlen);
+
+ for (i = request->u.servicereg.instances; i; i = i->next)
+ {
+ result = add_record_to_service(request, i, rrtype, rdlen, rdata, ttl);
+ if (result && i->default_local) break;
+ else result = mStatus_NoError; // suppress non-local default errors
+ }
+
+ return(result);
+}
+
+mDNSlocal void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd, mDNSu16 oldrdlen)
+{
+ mDNSBool external_advertise = (rr->UpdateContext) ? *((mDNSBool *)rr->UpdateContext) : mDNSfalse;
+ (void)m; // Unused
+
+ // There are three cases.
+ //
+ // 1. We have updated the primary TXT record of the service
+ // 2. We have updated the TXT record that was added to the service using DNSServiceAddRecord
+ // 3. We have updated the TXT record that was registered using DNSServiceRegisterRecord
+ //
+ // external_advertise is set if we have advertised at least once during the initial addition
+ // of the record in all of the three cases above. We should have checked for InterfaceID/LocalDomain
+ // checks during the first time and hence we don't do any checks here
+ if (external_advertise)
+ {
+ ResourceRecord ext = rr->resrec;
+ DNSServiceFlags flags = 0;
+
+ // Since we don't have a copy of the flags value used when the record was registered,
+ // we'll have to derive it from the ARType field.
+ if (rr->ARType == AuthRecordAnyIncludeP2P)
+ flags |= kDNSServiceFlagsIncludeP2P;
+ else if (rr->ARType == AuthRecordAnyIncludeAWDL)
+ flags |= kDNSServiceFlagsIncludeAWDL;
+
+ if (ext.rdlength == oldrdlen && mDNSPlatformMemSame(&ext.rdata->u, &oldrd->u, oldrdlen)) goto exit;
+ SetNewRData(&ext, oldrd, oldrdlen);
+ external_stop_advertising_service(&ext, flags);
+ LogInfo("update_callback: calling external_start_advertising_service");
+ external_start_advertising_service(&rr->resrec, flags);
+ }
+exit:
+ if (oldrd != &rr->rdatastorage) freeL("RData/update_callback", oldrd);
+}
+
+mDNSlocal mStatus update_record(AuthRecord *rr, mDNSu16 rdlen, const char *rdata, mDNSu32 ttl, const mDNSBool *const external_advertise)
+{
+ mStatus result;
+ const int rdsize = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody);
+ RData *newrd = mallocL("RData/update_record", sizeof(RData) - sizeof(RDataBody) + rdsize);
+ if (!newrd) FatalError("ERROR: malloc");
+ newrd->MaxRDLength = (mDNSu16) rdsize;
+ mDNSPlatformMemCopy(&newrd->u, rdata, rdlen);
+
+ // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
+ // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
+ // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
+ if (rr->resrec.rrtype == kDNSType_TXT && rdlen == 0) { rdlen = 1; newrd->u.txt.c[0] = 0; }
+
+ if (external_advertise) rr->UpdateContext = (void *)external_advertise;
+
+ result = mDNS_Update(&mDNSStorage, rr, ttl, rdlen, newrd, update_callback);
+ if (result) { LogMsg("update_record: Error %d for %s", (int)result, ARDisplayString(&mDNSStorage, rr)); freeL("RData/update_record", newrd); }
+ return result;
+}
+
+mDNSlocal mStatus handle_update_request(request_state *request)
+{
+ const ipc_msg_hdr *const hdr = &request->hdr;
+ mStatus result = mStatus_BadReferenceErr;
+ service_instance *i;
+ AuthRecord *rr = NULL;
+
+ // get the message data
+ DNSServiceFlags flags = get_flags (&request->msgptr, request->msgend); // flags unused
+ mDNSu16 rdlen = get_uint16(&request->msgptr, request->msgend);
+ const char *rdata = get_rdata (&request->msgptr, request->msgend, rdlen);
+ mDNSu32 ttl = get_uint32(&request->msgptr, request->msgend);
+ (void)flags; // Unused
+
+ if (!request->msgptr) { LogMsg("%3d: DNSServiceUpdateRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+ // If this is a shared connection, check if the operation actually applies to a subordinate request_state object
+ if (request->terminate == connection_termination) request = LocateSubordinateRequest(request);
+
+ if (request->terminate == connection_termination)
+ {
+ // update an individually registered record
+ registered_record_entry *reptr;
+ for (reptr = request->u.reg_recs; reptr; reptr = reptr->next)
+ {
+ if (reptr->key == hdr->reg_index)
+ {
+ result = update_record(reptr->rr, rdlen, rdata, ttl, &reptr->external_advertise);
+ LogOperation("%3d: DNSServiceUpdateRecord(%##s, %s)",
+ request->sd, reptr->rr->resrec.name->c, reptr->rr ? DNSTypeName(reptr->rr->resrec.rrtype) : "<NONE>");
+ goto end;
+ }
+ }
+ result = mStatus_BadReferenceErr;
+ goto end;
+ }
+
+ if (request->terminate != regservice_termination_callback)
+ { LogMsg("%3d: DNSServiceUpdateRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); }
+
+ // For a service registered with zero port, only SRV record is initialized. Don't allow any updates.
+ if (mDNSIPPortIsZero(request->u.servicereg.port))
+ { LogMsg("%3d: DNSServiceUpdateRecord: updating the record of a service registered with zero port", request->sd); return(mStatus_BadParamErr); }
+
+ // update the saved off TXT data for the service
+ if (hdr->reg_index == TXT_RECORD_INDEX)
+ {
+ if (request->u.servicereg.txtdata)
+ { freeL("service_info txtdata", request->u.servicereg.txtdata); request->u.servicereg.txtdata = NULL; }
+ if (rdlen > 0)
+ {
+ request->u.servicereg.txtdata = mallocL("service_info txtdata", rdlen);
+ if (!request->u.servicereg.txtdata) FatalError("ERROR: handle_update_request - malloc");
+ mDNSPlatformMemCopy(request->u.servicereg.txtdata, rdata, rdlen);
+ }
+ request->u.servicereg.txtlen = rdlen;
+ }
+
+ // update a record from a service record set
+ for (i = request->u.servicereg.instances; i; i = i->next)
+ {
+ if (hdr->reg_index == TXT_RECORD_INDEX) rr = &i->srs.RR_TXT;
+ else
+ {
+ ExtraResourceRecord *e;
+ for (e = i->srs.Extras; e; e = e->next)
+ if (e->ClientID == hdr->reg_index) { rr = &e->r; break; }
+ }
+
+ if (!rr) { result = mStatus_BadReferenceErr; goto end; }
+ result = update_record(rr, rdlen, rdata, ttl, &i->external_advertise);
+ if (result && i->default_local) goto end;
+ else result = mStatus_NoError; // suppress non-local default errors
+ }
+
+end:
+ if (request->terminate == regservice_termination_callback)
+ LogOperation("%3d: DNSServiceUpdateRecord(%##s, %s)", request->sd,
+ (request->u.servicereg.instances) ? request->u.servicereg.instances->srs.RR_SRV.resrec.name->c : NULL,
+ rr ? DNSTypeName(rr->resrec.rrtype) : "<NONE>");
+
+ return(result);
+}
+
+// remove a resource record registered via DNSServiceRegisterRecord()
+mDNSlocal mStatus remove_record(request_state *request)
+{
+ mStatus err = mStatus_UnknownErr;
+ registered_record_entry *e, **ptr = &request->u.reg_recs;
+
+ while (*ptr && (*ptr)->key != request->hdr.reg_index) ptr = &(*ptr)->next;
+ if (!*ptr) { LogMsg("%3d: DNSServiceRemoveRecord(%u) not found", request->sd, request->hdr.reg_index); return mStatus_BadReferenceErr; }
+ e = *ptr;
+ *ptr = e->next; // unlink
+
+ LogOperation("%3d: DNSServiceRemoveRecord(%u %s)", request->sd, e->key, RRDisplayString(&mDNSStorage, &e->rr->resrec));
+ e->rr->RecordContext = NULL;
+ if (e->external_advertise)
+ {
+ external_stop_advertising_service(&e->rr->resrec, request->flags);
+ e->external_advertise = mDNSfalse;
+ }
+ LogMcastS(&mDNSStorage, e->rr, request, reg_stop);
+ err = mDNS_Deregister(&mDNSStorage, e->rr); // Will free e->rr for us; we're responsible for freeing e
+ if (err)
+ {
+ LogMsg("ERROR: remove_record, mDNS_Deregister: %d", err);
+ freeL("registered_record_entry AuthRecord remove_record", e->rr);
+ }
+ freeL("registered_record_entry remove_record", e);
+ return err;
+}
+
+mDNSlocal mStatus remove_extra(const request_state *const request, service_instance *const serv, mDNSu16 *const rrtype)
+{
+ mStatus err = mStatus_BadReferenceErr;
+ ExtraResourceRecord *ptr;
+
+ for (ptr = serv->srs.Extras; ptr; ptr = ptr->next)
+ {
+ if (ptr->ClientID == request->hdr.reg_index) // found match
+ {
+ *rrtype = ptr->r.resrec.rrtype;
+ if (serv->external_advertise) external_stop_advertising_service(&ptr->r.resrec, request->flags);
+ err = mDNS_RemoveRecordFromService(&mDNSStorage, &serv->srs, ptr, FreeExtraRR, ptr);
+ break;
+ }
+ }
+ return err;
+}
+
+mDNSlocal mStatus handle_removerecord_request(request_state *request)
+{
+ mStatus err = mStatus_BadReferenceErr;
+ get_flags(&request->msgptr, request->msgend); // flags unused
+
+ if (!request->msgptr) { LogMsg("%3d: DNSServiceRemoveRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+ // If this is a shared connection, check if the operation actually applies to a subordinate request_state object
+ if (request->terminate == connection_termination) request = LocateSubordinateRequest(request);
+
+ if (request->terminate == connection_termination)
+ err = remove_record(request); // remove individually registered record
+ else if (request->terminate != regservice_termination_callback)
+ { LogMsg("%3d: DNSServiceRemoveRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); }
+ else
+ {
+ service_instance *i;
+ mDNSu16 rrtype = 0;
+ LogOperation("%3d: DNSServiceRemoveRecord(%##s, %s)", request->sd,
+ (request->u.servicereg.instances) ? request->u.servicereg.instances->srs.RR_SRV.resrec.name->c : NULL,
+ rrtype ? DNSTypeName(rrtype) : "<NONE>");
+ for (i = request->u.servicereg.instances; i; i = i->next)
+ {
+ err = remove_extra(request, i, &rrtype);
+ if (err && i->default_local) break;
+ else err = mStatus_NoError; // suppress non-local default errors
+ }
+ }
+
+ return(err);
+}
+
+// If there's a comma followed by another character,
+// FindFirstSubType overwrites the comma with a nul and returns the pointer to the next character.
+// Otherwise, it returns a pointer to the final nul at the end of the string
+mDNSlocal char *FindFirstSubType(char *p, char **AnonData)
+{
+ while (*p)
+ {
+ if (p[0] == '\\' && p[1])
+ {
+ p += 2;
+ }
+ else if (p[0] == ',' && p[1])
+ {
+ *p++ = 0;
+ return(p);
+ }
+ else if (p[0] == ':' && p[1])
+ {
+ *p++ = 0;
+ *AnonData = p;
+ }
+ else
+ {
+ p++;
+ }
+ }
+ return(p);
+}
+
+// If there's a comma followed by another character,
+// FindNextSubType overwrites the comma with a nul and returns the pointer to the next character.
+// If it finds an illegal unescaped dot in the subtype name, it returns mDNSNULL
+// Otherwise, it returns a pointer to the final nul at the end of the string
+mDNSlocal char *FindNextSubType(char *p)
+{
+ while (*p)
+ {
+ if (p[0] == '\\' && p[1]) // If escape character
+ p += 2; // ignore following character
+ else if (p[0] == ',') // If we found a comma
+ {
+ if (p[1]) *p++ = 0;
+ return(p);
+ }
+ else if (p[0] == '.')
+ return(mDNSNULL);
+ else p++;
+ }
+ return(p);
+}
+
+// Returns -1 if illegal subtype found
+mDNSexport mDNSs32 ChopSubTypes(char *regtype, char **AnonData)
+{
+ mDNSs32 NumSubTypes = 0;
+ char *stp = FindFirstSubType(regtype, AnonData);
+ while (stp && *stp) // If we found a comma...
+ {
+ if (*stp == ',') return(-1);
+ NumSubTypes++;
+ stp = FindNextSubType(stp);
+ }
+ if (!stp) return(-1);
+ return(NumSubTypes);
+}
+
+mDNSexport AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p, char **AnonData)
+{
+ AuthRecord *st = mDNSNULL;
+ //
+ // "p" is pointing at the regtype e.g., _http._tcp followed by ":<AnonData>" indicated
+ // by AnonData being non-NULL which is in turn follwed by ",<SubTypes>" indicated by
+ // NumSubTypes being non-zero. We need to skip the initial regtype to get to the actual
+ // data that we want. When we come here, ChopSubTypes has null terminated like this e.g.,
+ //
+ // _http._tcp<NULL><AnonData><NULL><SubType1><NULL><SubType2><NULL> etc.
+ //
+ // 1. If we have Anonymous data and subtypes, skip the regtype (e.g., "_http._tcp")
+ // to get the AnonData and then skip the AnonData to get to the SubType.
+ //
+ // 2. If we have only SubTypes, skip the regtype to get to the SubType data.
+ //
+ // 3. If we have only AnonData, skip the regtype to get to the AnonData.
+ //
+ // 4. If we don't have AnonData or NumStypes, it is a noop.
+ //
+ if (AnonData)
+ {
+ int len;
+
+ // Skip the regtype
+ while (*p) p++;
+ p++;
+
+ len = strlen(p) + 1;
+ *AnonData = mallocL("Anonymous", len);
+ if (!(*AnonData))
+ {
+ return (mDNSNULL);
+ }
+ mDNSPlatformMemCopy(*AnonData, p, len);
+ }
+ if (NumSubTypes)
+ {
+ mDNSs32 i;
+ st = mallocL("ServiceSubTypes", NumSubTypes * sizeof(AuthRecord));
+ if (!st) return(mDNSNULL);
+ for (i = 0; i < NumSubTypes; i++)
+ {
+ mDNS_SetupResourceRecord(&st[i], mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, AuthRecordAny, mDNSNULL, mDNSNULL);
+ // First time through we skip the regtype or AnonData. Subsequently, the
+ // previous subtype.
+ while (*p) p++;
+ p++;
+ if (!MakeDomainNameFromDNSNameString(&st[i].namestorage, p))
+ {
+ freeL("ServiceSubTypes", st);
+ if (*AnonData)
+ freeL("AnonymousData", *AnonData);
+ return(mDNSNULL);
+ }
+ }
+ }
+ // If NumSubTypes is zero and AnonData is non-NULL, we still return NULL but AnonData has been
+ // initialized. The caller knows how to handle this.
+ return(st);
+}
+
+mDNSlocal mStatus register_service_instance(request_state *request, const domainname *domain)
+{
+ service_instance **ptr, *instance;
+ const int extra_size = (request->u.servicereg.txtlen > sizeof(RDataBody)) ? (request->u.servicereg.txtlen - sizeof(RDataBody)) : 0;
+ const mDNSBool DomainIsLocal = SameDomainName(domain, &localdomain);
+ mStatus result;
+ mDNSInterfaceID interfaceID = request->u.servicereg.InterfaceID;
+ mDNSu32 coreFlags = 0;
+
+ if (request->flags & kDNSServiceFlagsIncludeP2P)
+ coreFlags |= coreFlagIncludeP2P;
+ if (request->flags & kDNSServiceFlagsIncludeAWDL)
+ coreFlags |= coreFlagIncludeAWDL;
+
+ // Client guarantees that record names are unique, so we can skip sending out initial
+ // probe messages. Standard name conflict resolution is still done if a conflict is discovered.
+ if (request->flags & kDNSServiceFlagsKnownUnique)
+ coreFlags |= coreFlagKnownUnique;
+
+ if (request->flags & kDNSServiceFlagsWakeOnlyService)
+ coreFlags |= coreFlagWakeOnly;
+
+ // If the client specified an interface, but no domain, then we honor the specified interface for the "local" (mDNS)
+ // registration but for the wide-area registrations we don't (currently) have any concept of a wide-area unicast
+ // registrations scoped to a specific interface, so for the automatic domains we add we must *not* specify an interface.
+ // (Specifying an interface with an apparently wide-area domain (i.e. something other than "local")
+ // currently forces the registration to use mDNS multicast despite the apparently wide-area domain.)
+ if (request->u.servicereg.default_domain && !DomainIsLocal) interfaceID = mDNSInterface_Any;
+
+ for (ptr = &request->u.servicereg.instances; *ptr; ptr = &(*ptr)->next)
+ {
+ if (SameDomainName(&(*ptr)->domain, domain))
+ {
+ LogMsg("register_service_instance: domain %##s already registered for %#s.%##s",
+ domain->c, &request->u.servicereg.name, &request->u.servicereg.type);
+ return mStatus_AlreadyRegistered;
+ }
+ }
+
+ instance = mallocL("service_instance", sizeof(*instance) + extra_size);
+ if (!instance) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; }
+
+ instance->next = mDNSNULL;
+ instance->request = request;
+ instance->renameonmemfree = 0;
+ instance->clientnotified = mDNSfalse;
+ instance->default_local = (request->u.servicereg.default_domain && DomainIsLocal);
+ instance->external_advertise = mDNSfalse;
+ AssignDomainName(&instance->domain, domain);
+
+ instance->srs.AnonData = mDNSNULL;
+ if (!request->u.servicereg.AnonData)
+ {
+ instance->subtypes = AllocateSubTypes(request->u.servicereg.num_subtypes, request->u.servicereg.type_as_string, mDNSNULL);
+ }
+ else
+ {
+ char *AnonData = mDNSNULL;
+ instance->subtypes = AllocateSubTypes(request->u.servicereg.num_subtypes, request->u.servicereg.type_as_string, &AnonData);
+ if (AnonData)
+ instance->srs.AnonData = (const mDNSu8 *)AnonData;
+ }
+
+ if (request->u.servicereg.num_subtypes && !instance->subtypes)
+ {
+ unlink_and_free_service_instance(instance);
+ instance = NULL;
+ FatalError("ERROR: malloc");
+ }
+
+ result = mDNS_RegisterService(&mDNSStorage, &instance->srs,
+ &request->u.servicereg.name, &request->u.servicereg.type, domain,
+ request->u.servicereg.host.c[0] ? &request->u.servicereg.host : NULL,
+ request->u.servicereg.port,
+ request->u.servicereg.txtdata, request->u.servicereg.txtlen,
+ instance->subtypes, request->u.servicereg.num_subtypes,
+ interfaceID, regservice_callback, instance, coreFlags);
+
+ if (!result)
+ {
+ *ptr = instance; // Append this to the end of our request->u.servicereg.instances list
+ LogOperation("%3d: DNSServiceRegister(%##s, %u) ADDED", instance->request->sd,
+ instance->srs.RR_SRV.resrec.name->c, mDNSVal16(request->u.servicereg.port));
+ LogMcastS(&mDNSStorage, &instance->srs.RR_SRV, request, reg_start);
+ }
+ else
+ {
+ LogMsg("register_service_instance %#s.%##s%##s error %d",
+ &request->u.servicereg.name, &request->u.servicereg.type, domain->c, result);
+ unlink_and_free_service_instance(instance);
+ }
+
+ return result;
+}
+
+mDNSlocal void udsserver_default_reg_domain_changed(const DNameListElem *const d, const mDNSBool add)
+{
+ request_state *request;
+
+#if APPLE_OSX_mDNSResponder
+ machserver_automatic_registration_domain_changed(&d->name, add);
+#endif // APPLE_OSX_mDNSResponder
+
+ LogMsg("%s registration domain %##s", add ? "Adding" : "Removing", d->name.c);
+ for (request = all_requests; request; request = request->next)
+ {
+ if (request->terminate != regservice_termination_callback) continue;
+ if (!request->u.servicereg.default_domain) continue;
+ if (!d->uid || SystemUID(request->uid) || request->uid == d->uid)
+ {
+ service_instance **ptr = &request->u.servicereg.instances;
+ while (*ptr && !SameDomainName(&(*ptr)->domain, &d->name)) ptr = &(*ptr)->next;
+ if (add)
+ {
+ // If we don't already have this domain in our list for this registration, add it now
+ if (!*ptr) register_service_instance(request, &d->name);
+ else debugf("udsserver_default_reg_domain_changed %##s already in list, not re-adding", &d->name);
+ }
+ else
+ {
+ // Normally we should not fail to find the specified instance
+ // One case where this can happen is if a uDNS update fails for some reason,
+ // and regservice_callback then calls unlink_and_free_service_instance and disposes of that instance.
+ if (!*ptr)
+ LogMsg("udsserver_default_reg_domain_changed domain %##s not found for service %#s type %s",
+ &d->name, request->u.servicereg.name.c, request->u.servicereg.type_as_string);
+ else
+ {
+ DNameListElem *p;
+ for (p = AutoRegistrationDomains; p; p=p->next)
+ if (!p->uid || SystemUID(request->uid) || request->uid == p->uid)
+ if (SameDomainName(&d->name, &p->name)) break;
+ if (p) debugf("udsserver_default_reg_domain_changed %##s still in list, not removing", &d->name);
+ else
+ {
+ mStatus err;
+ service_instance *si = *ptr;
+ *ptr = si->next;
+ if (si->clientnotified) SendServiceRemovalNotification(&si->srs); // Do this *before* clearing si->request backpointer
+ // Now that we've cut this service_instance from the list, we MUST clear the si->request backpointer.
+ // Otherwise what can happen is this: While our mDNS_DeregisterService is in the
+ // process of completing asynchronously, the client cancels the entire operation, so
+ // regservice_termination_callback then runs through the whole list deregistering each
+ // instance, clearing the backpointers, and then disposing the parent request_state object.
+ // However, because this service_instance isn't in the list any more, regservice_termination_callback
+ // has no way to find it and clear its backpointer, and then when our mDNS_DeregisterService finally
+ // completes later with a mStatus_MemFree message, it calls unlink_and_free_service_instance() with
+ // a service_instance with a stale si->request backpointer pointing to memory that's already been freed.
+ si->request = NULL;
+ err = mDNS_DeregisterService(&mDNSStorage, &si->srs);
+ if (err) { LogMsg("udsserver_default_reg_domain_changed err %d", err); unlink_and_free_service_instance(si); }
+ }
+ }
+ }
+ }
+ }
+}
+
+// Don't allow normal and anonymous registration to coexist.
+mDNSlocal mDNSBool CheckForMixedRegistrations(domainname *regtype, domainname *domain, mDNSBool AnonData)
+{
+ request_state *request;
+
+ // We only care about local domains where the anonymous extension is
+ // implemented.
+ if (!SameDomainName(domain, (const domainname *) "\x5" "local"))
+ {
+ return mDNStrue;
+ }
+
+ for (request = all_requests; request; request = request->next)
+ {
+ service_instance *ptr;
+
+ if (request->terminate != regservice_termination_callback) continue;
+ for (ptr = request->u.servicereg.instances; ptr ; ptr = ptr->next)
+ {
+ if (!SameDomainName(&ptr->domain, (const domainname *)"\x5" "local") ||
+ !SameDomainName(&request->u.servicereg.type, regtype))
+ {
+ continue;
+ }
+
+ // If we are about to register a anonymous registraion, we dont't want to
+ // allow the regular ones and vice versa.
+ if (AnonData)
+ {
+ if (!ptr->srs.AnonData)
+ {
+ LogMsg("CheckForMixedRegistrations: Normal registration already exists for %##s", regtype->c);
+ return mDNSfalse;
+ }
+ }
+ else
+ {
+ // Allow multiple regular registrations
+ if (ptr->srs.AnonData)
+ {
+ LogMsg("CheckForMixedRegistrations: Anonymous registration already exists for %##s", regtype->c);
+ return mDNSfalse;
+ }
+ }
+ }
+ }
+ return mDNStrue;
+}
+
+mDNSlocal mStatus handle_regservice_request(request_state *request)
+{
+ char name[256]; // Lots of spare space for extra-long names that we'll auto-truncate down to 63 bytes
+ char domain[MAX_ESCAPED_DOMAIN_NAME], host[MAX_ESCAPED_DOMAIN_NAME];
+ char type_as_string[MAX_ESCAPED_DOMAIN_NAME];
+ domainname d, srv;
+ mStatus err;
+ char *AnonData = mDNSNULL;
+
+ DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
+ mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
+ mDNSInterfaceID InterfaceID;
+
+ // Map kDNSServiceInterfaceIndexP2P to kDNSServiceInterfaceIndexAny with the
+ // kDNSServiceFlagsIncludeP2P flag set.
+ if (interfaceIndex == kDNSServiceInterfaceIndexP2P)
+ {
+ LogOperation("handle_regservice_request: mapping kDNSServiceInterfaceIndexP2P to kDNSServiceInterfaceIndexAny + kDNSServiceFlagsIncludeP2P");
+ flags |= kDNSServiceFlagsIncludeP2P;
+ interfaceIndex = kDNSServiceInterfaceIndexAny;
+ }
+
+ InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+ if (interfaceIndex && !InterfaceID)
+ { LogMsg("ERROR: handle_regservice_request - Couldn't find interfaceIndex %d", interfaceIndex); return(mStatus_BadParamErr); }
+
+ if (get_string(&request->msgptr, request->msgend, name, sizeof(name)) < 0 ||
+ get_string(&request->msgptr, request->msgend, type_as_string, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+ get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+ get_string(&request->msgptr, request->msgend, host, MAX_ESCAPED_DOMAIN_NAME) < 0)
+ { LogMsg("ERROR: handle_regservice_request - Couldn't read name/regtype/domain"); return(mStatus_BadParamErr); }
+
+ request->flags = flags;
+ request->u.servicereg.InterfaceID = InterfaceID;
+ request->u.servicereg.instances = NULL;
+ request->u.servicereg.txtlen = 0;
+ request->u.servicereg.txtdata = NULL;
+ mDNSPlatformStrCopy(request->u.servicereg.type_as_string, type_as_string);
+
+ if (request->msgptr + 2 > request->msgend) request->msgptr = NULL;
+ else
+ {
+ request->u.servicereg.port.b[0] = *request->msgptr++;
+ request->u.servicereg.port.b[1] = *request->msgptr++;
+ }
+
+ request->u.servicereg.txtlen = get_uint16(&request->msgptr, request->msgend);
+ if (request->u.servicereg.txtlen)
+ {
+ request->u.servicereg.txtdata = mallocL("service_info txtdata", request->u.servicereg.txtlen);
+ if (!request->u.servicereg.txtdata) FatalError("ERROR: handle_regservice_request - malloc");
+ mDNSPlatformMemCopy(request->u.servicereg.txtdata, get_rdata(&request->msgptr, request->msgend, request->u.servicereg.txtlen), request->u.servicereg.txtlen);
+ }
+
+ if (!request->msgptr) { LogMsg("%3d: DNSServiceRegister(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+ // Check for sub-types after the service type
+ request->u.servicereg.num_subtypes = ChopSubTypes(request->u.servicereg.type_as_string, &AnonData); // Note: Modifies regtype string to remove trailing subtypes
+ if (request->u.servicereg.num_subtypes < 0)
+ {
+ LogMsg("ERROR: handle_regservice_request - ChopSubTypes failed %s", request->u.servicereg.type_as_string);
+ return(mStatus_BadParamErr);
+ }
+ if (AnonData)
+ {
+ int AnonDataLen = strlen(AnonData);
+ if (AnonDataLen > MAX_ANONYMOUS_DATA)
+ {
+ LogMsg("ERROR: handle_regservice_request: AnonDataLen %d", AnonDataLen);
+ return(mStatus_BadParamErr);
+ }
+ request->u.servicereg.AnonData = mDNStrue;
+ }
+ else
+ {
+ request->u.servicereg.AnonData = mDNSfalse;
+ }
+
+ // Don't try to construct "domainname t" until *after* ChopSubTypes has worked its magic
+ if (!*request->u.servicereg.type_as_string || !MakeDomainNameFromDNSNameString(&request->u.servicereg.type, request->u.servicereg.type_as_string))
+ { LogMsg("ERROR: handle_regservice_request - type_as_string bad %s", request->u.servicereg.type_as_string); return(mStatus_BadParamErr); }
+
+ if (!name[0])
+ {
+ request->u.servicereg.name = mDNSStorage.nicelabel;
+ request->u.servicereg.autoname = mDNStrue;
+ }
+ else
+ {
+ // If the client is allowing AutoRename, then truncate name to legal length before converting it to a DomainLabel
+ if ((flags & kDNSServiceFlagsNoAutoRename) == 0)
+ {
+ int newlen = TruncateUTF8ToLength((mDNSu8*)name, mDNSPlatformStrLen(name), MAX_DOMAIN_LABEL);
+ name[newlen] = 0;
+ }
+ if (!MakeDomainLabelFromLiteralString(&request->u.servicereg.name, name))
+ { LogMsg("ERROR: handle_regservice_request - name bad %s", name); return(mStatus_BadParamErr); }
+ request->u.servicereg.autoname = mDNSfalse;
+ }
+
+ if (*domain)
+ {
+ request->u.servicereg.default_domain = mDNSfalse;
+ if (!MakeDomainNameFromDNSNameString(&d, domain))
+ { LogMsg("ERROR: handle_regservice_request - domain bad %s", domain); return(mStatus_BadParamErr); }
+ }
+ else
+ {
+ request->u.servicereg.default_domain = mDNStrue;
+ MakeDomainNameFromDNSNameString(&d, "local.");
+ }
+
+ // We don't allow the anonymous and the regular ones to coexist
+ if (!CheckForMixedRegistrations(&request->u.servicereg.type, &d, request->u.servicereg.AnonData))
+ {
+ return(mStatus_BadParamErr);
+ }
+
+ if (!ConstructServiceName(&srv, &request->u.servicereg.name, &request->u.servicereg.type, &d))
+ {
+ LogMsg("ERROR: handle_regservice_request - Couldn't ConstructServiceName from, “%#s” “%##s” “%##s”",
+ request->u.servicereg.name.c, request->u.servicereg.type.c, d.c); return(mStatus_BadParamErr);
+ }
+
+ if (!MakeDomainNameFromDNSNameString(&request->u.servicereg.host, host))
+ { LogMsg("ERROR: handle_regservice_request - host bad %s", host); return(mStatus_BadParamErr); }
+ request->u.servicereg.autorename = (flags & kDNSServiceFlagsNoAutoRename ) == 0;
+ request->u.servicereg.allowremotequery = (flags & kDNSServiceFlagsAllowRemoteQuery) != 0;
+
+ // Some clients use mDNS for lightweight copy protection, registering a pseudo-service with
+ // a port number of zero. When two instances of the protected client are allowed to run on one
+ // machine, we don't want to see misleading "Bogus client" messages in syslog and the console.
+ if (!mDNSIPPortIsZero(request->u.servicereg.port))
+ {
+ int count = CountExistingRegistrations(&srv, request->u.servicereg.port);
+ if (count)
+ LogMsg("Client application[%d](%s) registered %d identical instances of service %##s port %u.", request->process_id,
+ request->pid_name, count+1, srv.c, mDNSVal16(request->u.servicereg.port));
+ }
+
+ LogOperation("%3d: DNSServiceRegister(%X, %d, \"%s\", \"%s\", \"%s\", \"%s\", %u) START PID[%d](%s)",
+ request->sd, flags, interfaceIndex, name, request->u.servicereg.type_as_string, domain, host,
+ mDNSVal16(request->u.servicereg.port), request->process_id, request->pid_name);
+
+ // We need to unconditionally set request->terminate, because even if we didn't successfully
+ // start any registrations right now, subsequent configuration changes may cause successful
+ // registrations to be added, and we'll need to cancel them before freeing this memory.
+ // We also need to set request->terminate first, before adding additional service instances,
+ // because the uds_validatelists uses the request->terminate function pointer to determine
+ // what kind of request this is, and therefore what kind of list validation is required.
+ request->terminate = regservice_termination_callback;
+
+ err = register_service_instance(request, &d);
+
+#if 0
+ err = AuthorizedDomain(request, &d, AutoRegistrationDomains) ? register_service_instance(request, &d) : mStatus_NoError;
+#endif
+ if (!err)
+ {
+ if (request->u.servicereg.autoname) UpdateDeviceInfoRecord(&mDNSStorage);
+
+ if (!*domain)
+ {
+ DNameListElem *ptr;
+ // Note that we don't report errors for non-local, non-explicit domains
+ for (ptr = AutoRegistrationDomains; ptr; ptr = ptr->next)
+ if (!ptr->uid || SystemUID(request->uid) || request->uid == ptr->uid)
+ register_service_instance(request, &ptr->name);
+ }
+ }
+
+ return(err);
+}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - DNSServiceBrowse
+#endif
+
+mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+{
+ DNSServiceFlags flags = AddRecord ? kDNSServiceFlagsAdd : 0;
+ request_state *req = question->QuestionContext;
+ reply_state *rep;
+ (void)m; // Unused
+
+ if (answer->rrtype != kDNSType_PTR)
+ { LogMsg("%3d: FoundInstance: Should not be called with rrtype %d (not a PTR record)", req->sd, answer->rrtype); return; }
+
+ if (mDNSOpaque16IsZero(question->TargetQID) && (question->BrowseThreshold > 0) && (question->CurrentAnswers >= question->BrowseThreshold))
+ {
+ flags |= kDNSServiceFlagsThresholdReached;
+ }
+
+ if (GenerateNTDResponse(&answer->rdata->u.name, answer->InterfaceID, req, &rep, browse_reply_op, flags, mStatus_NoError) != mStatus_NoError)
+ {
+ if (SameDomainName(&req->u.browser.regtype, (const domainname*)"\x09_services\x07_dns-sd\x04_udp"))
+ {
+ // Special support to enable the DNSServiceBrowse call made by Bonjour Browser
+ // Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse
+ GenerateBonjourBrowserResponse(&answer->rdata->u.name, answer->InterfaceID, req, &rep, browse_reply_op, flags, mStatus_NoError);
+ goto bonjourbrowserhack;
+ }
+
+ LogMsg("%3d: FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer",
+ req->sd, answer->name->c, answer->rdata->u.name.c);
+ return;
+ }
+
+bonjourbrowserhack:
+
+ LogOperation("%3d: DNSServiceBrowse(%##s, %s) RESULT %s %d: %s",
+ req->sd, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "Add" : "Rmv",
+ mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNSfalse), RRDisplayString(m, answer));
+
+ append_reply(req, rep);
+}
+
+mDNSlocal mStatus add_domain_to_browser(request_state *info, const domainname *d)
+{
+ browser_t *b, *p;
+ mStatus err;
+
+ for (p = info->u.browser.browsers; p; p = p->next)
+ {
+ if (SameDomainName(&p->domain, d))
+ { debugf("add_domain_to_browser %##s already in list", d->c); return mStatus_AlreadyRegistered; }
+ }
+
+ b = mallocL("browser_t", sizeof(*b));
+ if (!b) return mStatus_NoMemoryErr;
+ AssignDomainName(&b->domain, d);
+ err = mDNS_StartBrowse(&mDNSStorage, &b->q, &info->u.browser.regtype, d, info->u.browser.AnonData, info->u.browser.interface_id, info->flags,
+ info->u.browser.ForceMCast, (info->flags & kDNSServiceFlagsBackgroundTrafficClass) != 0, FoundInstance, info);
+ if (err)
+ {
+ LogMsg("mDNS_StartBrowse returned %d for type %##s domain %##s", err, info->u.browser.regtype.c, d->c);
+ freeL("browser_t/add_domain_to_browser", b);
+ }
+ else
+ {
+ b->next = info->u.browser.browsers;
+ info->u.browser.browsers = b;
+ LogOperation("%3d: DNSServiceBrowse(%##s) START PID[%d](%s)", info->sd, b->q.qname.c, info->process_id,
+ info->pid_name);
+ LogMcastQ(&mDNSStorage, &b->q, info, q_start);
+ if (callExternalHelpers(info->u.browser.interface_id, &b->domain, info->flags))
+ {
+ domainname tmp;
+ ConstructServiceName(&tmp, NULL, &info->u.browser.regtype, &b->domain);
+ LogInfo("add_domain_to_browser: calling external_start_browsing_for_service()");
+ external_start_browsing_for_service(info->u.browser.interface_id, &tmp, kDNSType_PTR, info->flags);
+ }
+ }
+ return err;
+}
+
+mDNSlocal void browse_termination_callback(request_state *info)
+{
+ if (info->u.browser.default_domain)
+ {
+ // Stop the domain enumeration queries to discover the WAB legacy browse domains
+ LogInfo("%3d: DNSServiceBrowse Cancel WAB PID[%d](%s)", info->sd, info->process_id, info->pid_name);
+ uDNS_StopWABQueries(&mDNSStorage, UDNS_WAB_LBROWSE_QUERY);
+ }
+ if (info->u.browser.AnonData)
+ freeL("Anonymous", (void *)info->u.browser.AnonData);
+ while (info->u.browser.browsers)
+ {
+ browser_t *ptr = info->u.browser.browsers;
+
+ if (callExternalHelpers(info->u.browser.interface_id, &ptr->domain, info->flags))
+ {
+ domainname tmp;
+ ConstructServiceName(&tmp, NULL, &info->u.browser.regtype, &ptr->domain);
+ LogInfo("browse_termination_callback: calling external_stop_browsing_for_service()");
+ external_stop_browsing_for_service(info->u.browser.interface_id, &tmp, kDNSType_PTR, info->flags);
+ }
+
+ info->u.browser.browsers = ptr->next;
+ LogOperation("%3d: DNSServiceBrowse(%##s) STOP PID[%d](%s)", info->sd, ptr->q.qname.c, info->process_id, info->pid_name);
+ mDNS_StopBrowse(&mDNSStorage, &ptr->q); // no need to error-check result
+ LogMcastQ(&mDNSStorage, &ptr->q, info, q_stop);
+ freeL("browser_t/browse_termination_callback", ptr);
+ }
+}
+
+mDNSlocal void udsserver_automatic_browse_domain_changed(const DNameListElem *const d, const mDNSBool add)
+{
+ request_state *request;
+ debugf("udsserver_automatic_browse_domain_changed: %s default browse domain %##s", add ? "Adding" : "Removing", d->name.c);
+
+#if APPLE_OSX_mDNSResponder
+ machserver_automatic_browse_domain_changed(&d->name, add);
+#endif // APPLE_OSX_mDNSResponder
+
+ for (request = all_requests; request; request = request->next)
+ {
+ if (request->terminate != browse_termination_callback) continue; // Not a browse operation
+ if (!request->u.browser.default_domain) continue; // Not an auto-browse operation
+ if (!d->uid || SystemUID(request->uid) || request->uid == d->uid)
+ {
+ browser_t **ptr = &request->u.browser.browsers;
+ while (*ptr && !SameDomainName(&(*ptr)->domain, &d->name)) ptr = &(*ptr)->next;
+ if (add)
+ {
+ // If we don't already have this domain in our list for this browse operation, add it now
+ if (!*ptr) add_domain_to_browser(request, &d->name);
+ else debugf("udsserver_automatic_browse_domain_changed %##s already in list, not re-adding", &d->name);
+ }
+ else
+ {
+ if (!*ptr) LogMsg("udsserver_automatic_browse_domain_changed ERROR %##s not found", &d->name);
+ else
+ {
+ DNameListElem *p;
+ for (p = AutoBrowseDomains; p; p=p->next)
+ if (!p->uid || SystemUID(request->uid) || request->uid == p->uid)
+ if (SameDomainName(&d->name, &p->name)) break;
+ if (p) debugf("udsserver_automatic_browse_domain_changed %##s still in list, not removing", &d->name);
+ else
+ {
+ browser_t *rem = *ptr;
+ *ptr = (*ptr)->next;
+ mDNS_StopQueryWithRemoves(&mDNSStorage, &rem->q);
+ freeL("browser_t/udsserver_automatic_browse_domain_changed", rem);
+ }
+ }
+ }
+ }
+ }
+}
+
+mDNSlocal void FreeARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
+{
+ (void)m; // unused
+ if (result == mStatus_MemFree)
+ {
+ // On shutdown, mDNS_Close automatically deregisters all records
+ // Since in this case no one has called DeregisterLocalOnlyDomainEnumPTR to cut the record
+ // from the LocalDomainEnumRecords list, we do this here before we free the memory.
+ // (This should actually no longer be necessary, now that we do the proper cleanup in
+ // udsserver_exit. To confirm this, we'll log an error message if we do find a record that
+ // hasn't been cut from the list yet. If these messages don't appear, we can delete this code.)
+ ARListElem **ptr = &LocalDomainEnumRecords;
+ while (*ptr && &(*ptr)->ar != rr) ptr = &(*ptr)->next;
+ if (*ptr) { *ptr = (*ptr)->next; LogMsg("FreeARElemCallback: Have to cut %s", ARDisplayString(m, rr)); }
+ mDNSPlatformMemFree(rr->RecordContext);
+ }
+}
+
+// RegisterLocalOnlyDomainEnumPTR and DeregisterLocalOnlyDomainEnumPTR largely duplicate code in
+// "FoundDomain" in uDNS.c for creating and destroying these special mDNSInterface_LocalOnly records.
+// We may want to turn the common code into a subroutine.
+
+mDNSlocal void RegisterLocalOnlyDomainEnumPTR(mDNS *m, const domainname *d, int type)
+{
+ // allocate/register legacy and non-legacy _browse PTR record
+ mStatus err;
+ ARListElem *ptr = mDNSPlatformMemAllocate(sizeof(*ptr));
+
+ debugf("Incrementing %s refcount for %##s",
+ (type == mDNS_DomainTypeBrowse ) ? "browse domain " :
+ (type == mDNS_DomainTypeRegistration ) ? "registration dom" :
+ (type == mDNS_DomainTypeBrowseAutomatic) ? "automatic browse" : "?", d->c);
+
+ mDNS_SetupResourceRecord(&ptr->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, AuthRecordLocalOnly, FreeARElemCallback, ptr);
+ MakeDomainNameFromDNSNameString(&ptr->ar.namestorage, mDNS_DomainTypeNames[type]);
+ AppendDNSNameString (&ptr->ar.namestorage, "local");
+ AssignDomainName(&ptr->ar.resrec.rdata->u.name, d);
+ err = mDNS_Register(m, &ptr->ar);
+ if (err)
+ {
+ LogMsg("SetSCPrefsBrowseDomain: mDNS_Register returned error %d", err);
+ mDNSPlatformMemFree(ptr);
+ }
+ else
+ {
+ ptr->next = LocalDomainEnumRecords;
+ LocalDomainEnumRecords = ptr;
+ }
+}
+
+mDNSlocal void DeregisterLocalOnlyDomainEnumPTR(mDNS *m, const domainname *d, int type)
+{
+ ARListElem **ptr = &LocalDomainEnumRecords;
+ domainname lhs; // left-hand side of PTR, for comparison
+
+ debugf("Decrementing %s refcount for %##s",
+ (type == mDNS_DomainTypeBrowse ) ? "browse domain " :
+ (type == mDNS_DomainTypeRegistration ) ? "registration dom" :
+ (type == mDNS_DomainTypeBrowseAutomatic) ? "automatic browse" : "?", d->c);
+
+ MakeDomainNameFromDNSNameString(&lhs, mDNS_DomainTypeNames[type]);
+ AppendDNSNameString (&lhs, "local");
+
+ while (*ptr)
+ {
+ if (SameDomainName(&(*ptr)->ar.resrec.rdata->u.name, d) && SameDomainName((*ptr)->ar.resrec.name, &lhs))
+ {
+ ARListElem *rem = *ptr;
+ *ptr = (*ptr)->next;
+ mDNS_Deregister(m, &rem->ar);
+ return;
+ }
+ else ptr = &(*ptr)->next;
+ }
+}
+
+mDNSlocal void AddAutoBrowseDomain(const mDNSu32 uid, const domainname *const name)
+{
+ DNameListElem *new = mDNSPlatformMemAllocate(sizeof(DNameListElem));
+ if (!new) { LogMsg("ERROR: malloc"); return; }
+ AssignDomainName(&new->name, name);
+ new->uid = uid;
+ new->next = AutoBrowseDomains;
+ AutoBrowseDomains = new;
+ udsserver_automatic_browse_domain_changed(new, mDNStrue);
+}
+
+mDNSlocal void RmvAutoBrowseDomain(const mDNSu32 uid, const domainname *const name)
+{
+ DNameListElem **p = &AutoBrowseDomains;
+ while (*p && (!SameDomainName(&(*p)->name, name) || (*p)->uid != uid)) p = &(*p)->next;
+ if (!*p) LogMsg("RmvAutoBrowseDomain: Got remove event for domain %##s not in list", name->c);
+ else
+ {
+ DNameListElem *ptr = *p;
+ *p = ptr->next;
+ udsserver_automatic_browse_domain_changed(ptr, mDNSfalse);
+ mDNSPlatformMemFree(ptr);
+ }
+}
+
+mDNSlocal void SetPrefsBrowseDomains(mDNS *m, DNameListElem *browseDomains, mDNSBool add)
+{
+ DNameListElem *d;
+ for (d = browseDomains; d; d = d->next)
+ {
+ if (add)
+ {
+ RegisterLocalOnlyDomainEnumPTR(m, &d->name, mDNS_DomainTypeBrowse);
+ AddAutoBrowseDomain(d->uid, &d->name);
+ }
+ else
+ {
+ DeregisterLocalOnlyDomainEnumPTR(m, &d->name, mDNS_DomainTypeBrowse);
+ RmvAutoBrowseDomain(d->uid, &d->name);
+ }
+ }
+}
+
+#if APPLE_OSX_mDNSResponder
+
+mDNSlocal void UpdateDeviceInfoRecord(mDNS *const m)
+{
+ int num_autoname = 0;
+ request_state *req;
+ for (req = all_requests; req; req = req->next)
+ if (req->terminate == regservice_termination_callback && req->u.servicereg.autoname)
+ num_autoname++;
+
+ // If DeviceInfo record is currently registered, see if we need to deregister it
+ if (m->DeviceInfo.resrec.RecordType != kDNSRecordTypeUnregistered)
+ if (num_autoname == 0 || !SameDomainLabelCS(m->DeviceInfo.resrec.name->c, m->nicelabel.c))
+ {
+ LogOperation("UpdateDeviceInfoRecord Deregister %##s", m->DeviceInfo.resrec.name);
+ mDNS_Deregister(m, &m->DeviceInfo);
+ }
+
+ // If DeviceInfo record is not currently registered, see if we need to register it
+ if (m->DeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered)
+ if (num_autoname > 0)
+ {
+ mDNS_SetupResourceRecord(&m->DeviceInfo, mDNSNULL, mDNSNULL, kDNSType_TXT, kStandardTTL, kDNSRecordTypeAdvisory, AuthRecordAny, mDNSNULL, mDNSNULL);
+ ConstructServiceName(&m->DeviceInfo.namestorage, &m->nicelabel, &DeviceInfoName, &localdomain);
+ m->DeviceInfo.resrec.rdlength = initializeDeviceInfoTXT(m, m->DeviceInfo.resrec.rdata->u.data);
+ LogOperation("UpdateDeviceInfoRecord Register %##s", m->DeviceInfo.resrec.name);
+ mDNS_Register(m, &m->DeviceInfo);
+ }
+}
+#else // APPLE_OSX_mDNSResponder
+mDNSlocal void UpdateDeviceInfoRecord(mDNS *const m)
+{
+ (void)m; // unused
+}
+#endif // APPLE_OSX_mDNSResponder
+
+mDNSexport void udsserver_handle_configchange(mDNS *const m)
+{
+ request_state *req;
+ service_instance *ptr;
+ DNameListElem *RegDomains = NULL;
+ DNameListElem *BrowseDomains = NULL;
+ DNameListElem *p;
+
+ UpdateDeviceInfoRecord(m);
+
+ // For autoname services, see if the default service name has changed, necessitating an automatic update
+ for (req = all_requests; req; req = req->next)
+ if (req->terminate == regservice_termination_callback)
+ if (req->u.servicereg.autoname && !SameDomainLabelCS(req->u.servicereg.name.c, m->nicelabel.c))
+ {
+ req->u.servicereg.name = m->nicelabel;
+ for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next)
+ {
+ ptr->renameonmemfree = 1;
+ if (ptr->clientnotified) SendServiceRemovalNotification(&ptr->srs);
+ LogInfo("udsserver_handle_configchange: Calling deregister for Service %##s", ptr->srs.RR_PTR.resrec.name->c);
+ if (mDNS_DeregisterService_drt(m, &ptr->srs, mDNS_Dereg_rapid))
+ regservice_callback(m, &ptr->srs, mStatus_MemFree); // If service deregistered already, we can re-register immediately
+ }
+ }
+
+ // Let the platform layer get the current DNS information
+ mDNS_Lock(m);
+ mDNSPlatformSetDNSConfig(m, mDNSfalse, mDNSfalse, mDNSNULL, &RegDomains, &BrowseDomains, mDNSfalse);
+ mDNS_Unlock(m);
+
+ // Any automatic registration domains are also implicitly automatic browsing domains
+ if (RegDomains) SetPrefsBrowseDomains(m, RegDomains, mDNStrue); // Add the new list first
+ if (AutoRegistrationDomains) SetPrefsBrowseDomains(m, AutoRegistrationDomains, mDNSfalse); // Then clear the old list
+
+ // Add any new domains not already in our AutoRegistrationDomains list
+ for (p=RegDomains; p; p=p->next)
+ {
+ DNameListElem **pp = &AutoRegistrationDomains;
+ while (*pp && ((*pp)->uid != p->uid || !SameDomainName(&(*pp)->name, &p->name))) pp = &(*pp)->next;
+ if (!*pp) // If not found in our existing list, this is a new default registration domain
+ {
+ RegisterLocalOnlyDomainEnumPTR(m, &p->name, mDNS_DomainTypeRegistration);
+ udsserver_default_reg_domain_changed(p, mDNStrue);
+ }
+ else // else found same domainname in both old and new lists, so no change, just delete old copy
+ {
+ DNameListElem *del = *pp;
+ *pp = (*pp)->next;
+ mDNSPlatformMemFree(del);
+ }
+ }
+
+ // Delete any domains in our old AutoRegistrationDomains list that are now gone
+ while (AutoRegistrationDomains)
+ {
+ DNameListElem *del = AutoRegistrationDomains;
+ AutoRegistrationDomains = AutoRegistrationDomains->next; // Cut record from list FIRST,
+ DeregisterLocalOnlyDomainEnumPTR(m, &del->name, mDNS_DomainTypeRegistration);
+ udsserver_default_reg_domain_changed(del, mDNSfalse); // before calling udsserver_default_reg_domain_changed()
+ mDNSPlatformMemFree(del);
+ }
+
+ // Now we have our new updated automatic registration domain list
+ AutoRegistrationDomains = RegDomains;
+
+ // Add new browse domains to internal list
+ if (BrowseDomains) SetPrefsBrowseDomains(m, BrowseDomains, mDNStrue);
+
+ // Remove old browse domains from internal list
+ if (SCPrefBrowseDomains)
+ {
+ SetPrefsBrowseDomains(m, SCPrefBrowseDomains, mDNSfalse);
+ while (SCPrefBrowseDomains)
+ {
+ DNameListElem *fptr = SCPrefBrowseDomains;
+ SCPrefBrowseDomains = SCPrefBrowseDomains->next;
+ mDNSPlatformMemFree(fptr);
+ }
+ }
+
+ // Replace the old browse domains array with the new array
+ SCPrefBrowseDomains = BrowseDomains;
+}
+
+mDNSlocal void AutomaticBrowseDomainChange(mDNS *const m, DNSQuestion *q, const ResourceRecord *const answer, QC_result AddRecord)
+{
+ (void)m; // unused;
+ (void)q; // unused
+
+ LogOperation("AutomaticBrowseDomainChange: %s automatic browse domain %##s",
+ AddRecord ? "Adding" : "Removing", answer->rdata->u.name.c);
+
+ if (AddRecord) AddAutoBrowseDomain(0, &answer->rdata->u.name);
+ else RmvAutoBrowseDomain(0, &answer->rdata->u.name);
+}
+
+mDNSlocal mStatus handle_browse_request(request_state *request)
+{
+ char regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME];
+ domainname typedn, d, temp;
+ mDNSs32 NumSubTypes;
+ char *AnonData = mDNSNULL;
+ mStatus err = mStatus_NoError;
+ int AnonDataLen;
+
+ DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
+ mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
+ mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+ if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr);
+
+ if (get_string(&request->msgptr, request->msgend, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+ get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0) return(mStatus_BadParamErr);
+
+ if (!request->msgptr) { LogMsg("%3d: DNSServiceBrowse(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+ request->flags = flags;
+ typedn.c[0] = 0;
+ NumSubTypes = ChopSubTypes(regtype, &AnonData); // Note: Modifies regtype string to remove trailing subtypes
+ if (NumSubTypes < 0 || NumSubTypes > 1)
+ return(mStatus_BadParamErr);
+ AnonDataLen = 0;
+ if (AnonData)
+ {
+ AnonDataLen = strlen(AnonData);
+ if (AnonDataLen > MAX_ANONYMOUS_DATA)
+ {
+ LogMsg("handle_browse_request: AnonDataLen %d", AnonDataLen);
+ return(mStatus_BadParamErr);
+ }
+ // Account for the null byte
+ AnonDataLen += 1;
+ }
+ if (NumSubTypes == 1)
+ {
+ if (!AppendDNSNameString(&typedn, regtype + strlen(regtype) + 1 + AnonDataLen))
+ return(mStatus_BadParamErr);
+ }
+
+ if (!regtype[0] || !AppendDNSNameString(&typedn, regtype)) return(mStatus_BadParamErr);
+
+ if (!MakeDomainNameFromDNSNameString(&temp, regtype)) return(mStatus_BadParamErr);
+ // For over-long service types, we only allow domain "local"
+ if (temp.c[0] > 15 && domain[0] == 0) mDNSPlatformStrCopy(domain, "local.");
+
+ // Set up browser info
+ request->u.browser.ForceMCast = (flags & kDNSServiceFlagsForceMulticast) != 0;
+ request->u.browser.interface_id = InterfaceID;
+ AssignDomainName(&request->u.browser.regtype, &typedn);
+ request->u.browser.default_domain = !domain[0];
+ request->u.browser.browsers = NULL;
+
+ LogOperation("%3d: DNSServiceBrowse(%X, %d, \"%##s\", \"%s\") START PID[%d](%s)",
+ request->sd, request->flags, interfaceIndex, request->u.browser.regtype.c, domain, request->process_id, request->pid_name);
+
+ if (request->u.browser.default_domain)
+ {
+ // Start the domain enumeration queries to discover the WAB browse domains
+ LogInfo("%3d: DNSServiceBrowse Start WAB PID[%d](%s)", request->sd, request->process_id, request->pid_name);
+ uDNS_StartWABQueries(&mDNSStorage, UDNS_WAB_LBROWSE_QUERY);
+ }
+ request->u.browser.AnonData = mDNSNULL;
+ if (AnonData)
+ {
+ int len = strlen(AnonData) + 1;
+ request->u.browser.AnonData = mallocL("Anonymous", len);
+ if (!request->u.browser.AnonData)
+ return mStatus_NoMemoryErr;
+ else
+ mDNSPlatformMemCopy((void *)request->u.browser.AnonData, AnonData, len);
+ }
+ // We need to unconditionally set request->terminate, because even if we didn't successfully
+ // start any browses right now, subsequent configuration changes may cause successful
+ // browses to be added, and we'll need to cancel them before freeing this memory.
+ request->terminate = browse_termination_callback;
+
+ if (domain[0])
+ {
+ if (!MakeDomainNameFromDNSNameString(&d, domain)) return(mStatus_BadParamErr);
+ err = add_domain_to_browser(request, &d);
+ }
+ else
+ {
+ DNameListElem *sdom;
+ for (sdom = AutoBrowseDomains; sdom; sdom = sdom->next)
+ if (!sdom->uid || SystemUID(request->uid) || request->uid == sdom->uid)
+ {
+ err = add_domain_to_browser(request, &sdom->name);
+ if (err)
+ {
+ if (SameDomainName(&sdom->name, &localdomain)) break;
+ else err = mStatus_NoError; // suppress errors for non-local "default" domains
+ }
+ }
+ }
+
+ return(err);
+}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - DNSServiceResolve
+#endif
+
+mDNSlocal void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+{
+ size_t len = 0;
+ char fullname[MAX_ESCAPED_DOMAIN_NAME], target[MAX_ESCAPED_DOMAIN_NAME];
+ char *data;
+ reply_state *rep;
+ request_state *req = question->QuestionContext;
+ (void)m; // Unused
+
+ LogOperation("%3d: DNSServiceResolve(%##s) %s %s", req->sd, question->qname.c, AddRecord ? "ADD" : "RMV", RRDisplayString(m, answer));
+
+ if (!AddRecord)
+ {
+ if (req->u.resolve.srv == answer) req->u.resolve.srv = mDNSNULL;
+ if (req->u.resolve.txt == answer) req->u.resolve.txt = mDNSNULL;
+ return;
+ }
+
+ if (answer->rrtype == kDNSType_SRV) req->u.resolve.srv = answer;
+ if (answer->rrtype == kDNSType_TXT) req->u.resolve.txt = answer;
+
+ if (!req->u.resolve.txt || !req->u.resolve.srv) return; // only deliver result to client if we have both answers
+
+ ConvertDomainNameToCString(answer->name, fullname);
+ ConvertDomainNameToCString(&req->u.resolve.srv->rdata->u.srv.target, target);
+
+ // calculate reply length
+ len += sizeof(DNSServiceFlags);
+ len += sizeof(mDNSu32); // interface index
+ len += sizeof(DNSServiceErrorType);
+ len += strlen(fullname) + 1;
+ len += strlen(target) + 1;
+ len += 2 * sizeof(mDNSu16); // port, txtLen
+ len += req->u.resolve.txt->rdlength;
+
+ // allocate/init reply header
+ rep = create_reply(resolve_reply_op, len, req);
+ rep->rhdr->flags = dnssd_htonl(0);
+ rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNSfalse));
+ rep->rhdr->error = dnssd_htonl(kDNSServiceErr_NoError);
+
+ data = (char *)&rep->rhdr[1];
+
+ // write reply data to message
+ put_string(fullname, &data);
+ put_string(target, &data);
+ *data++ = req->u.resolve.srv->rdata->u.srv.port.b[0];
+ *data++ = req->u.resolve.srv->rdata->u.srv.port.b[1];
+ put_uint16(req->u.resolve.txt->rdlength, &data);
+ put_rdata (req->u.resolve.txt->rdlength, req->u.resolve.txt->rdata->u.data, &data);
+
+ LogOperation("%3d: DNSServiceResolve(%s) RESULT %s:%d", req->sd, fullname, target, mDNSVal16(req->u.resolve.srv->rdata->u.srv.port));
+ append_reply(req, rep);
+}
+
+mDNSlocal void resolve_termination_callback(request_state *request)
+{
+ LogOperation("%3d: DNSServiceResolve(%##s) STOP PID[%d](%s)", request->sd, request->u.resolve.qtxt.qname.c, request->process_id, request->pid_name);
+ mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qtxt);
+ mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv);
+ LogMcastQ(&mDNSStorage, &request->u.resolve.qsrv, request, q_stop);
+ if (request->u.resolve.external_advertise)
+ external_stop_resolving_service(request->u.resolve.qsrv.InterfaceID, &request->u.resolve.qsrv.qname, request->flags);
+}
+
+mDNSlocal mStatus handle_resolve_request(request_state *request)
+{
+ char name[256], regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME];
+ domainname fqdn;
+ mStatus err;
+
+ // extract the data from the message
+ DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
+ mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
+ mDNSInterfaceID InterfaceID;
+
+ // Map kDNSServiceInterfaceIndexP2P to kDNSServiceInterfaceIndexAny with the kDNSServiceFlagsIncludeP2P
+ // flag set so that the resolve will run over P2P interfaces that are not yet created.
+ if (interfaceIndex == kDNSServiceInterfaceIndexP2P)
+ {
+ LogOperation("handle_resolve_request: mapping kDNSServiceInterfaceIndexP2P to kDNSServiceInterfaceIndexAny + kDNSServiceFlagsIncludeP2P");
+ flags |= kDNSServiceFlagsIncludeP2P;
+ interfaceIndex = kDNSServiceInterfaceIndexAny;
+ }
+
+ InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+ if (interfaceIndex && !InterfaceID)
+ { LogMsg("ERROR: handle_resolve_request bad interfaceIndex %d", interfaceIndex); return(mStatus_BadParamErr); }
+
+ if (get_string(&request->msgptr, request->msgend, name, 256) < 0 ||
+ get_string(&request->msgptr, request->msgend, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+ get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0)
+ { LogMsg("ERROR: handle_resolve_request - Couldn't read name/regtype/domain"); return(mStatus_BadParamErr); }
+
+ if (!request->msgptr) { LogMsg("%3d: DNSServiceResolve(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+ if (build_domainname_from_strings(&fqdn, name, regtype, domain) < 0)
+ { LogMsg("ERROR: handle_resolve_request bad “%s” “%s” “%s”", name, regtype, domain); return(mStatus_BadParamErr); }
+
+ mDNSPlatformMemZero(&request->u.resolve, sizeof(request->u.resolve));
+
+ request->flags = flags;
+
+ // format questions
+ request->u.resolve.qsrv.InterfaceID = InterfaceID;
+ request->u.resolve.qsrv.flags = flags;
+ request->u.resolve.qsrv.Target = zeroAddr;
+ AssignDomainName(&request->u.resolve.qsrv.qname, &fqdn);
+ request->u.resolve.qsrv.qtype = kDNSType_SRV;
+ request->u.resolve.qsrv.qclass = kDNSClass_IN;
+ request->u.resolve.qsrv.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0;
+ request->u.resolve.qsrv.ExpectUnique = mDNStrue;
+ request->u.resolve.qsrv.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0;
+ request->u.resolve.qsrv.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
+ request->u.resolve.qsrv.SuppressUnusable = mDNSfalse;
+ request->u.resolve.qsrv.SearchListIndex = 0;
+ request->u.resolve.qsrv.AppendSearchDomains = 0;
+ request->u.resolve.qsrv.RetryWithSearchDomains = mDNSfalse;
+ request->u.resolve.qsrv.TimeoutQuestion = 0;
+ request->u.resolve.qsrv.WakeOnResolve = (flags & kDNSServiceFlagsWakeOnResolve) != 0;
+ request->u.resolve.qsrv.UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0;
+ request->u.resolve.qsrv.ValidationRequired = 0;
+ request->u.resolve.qsrv.ValidatingResponse = 0;
+ request->u.resolve.qsrv.ProxyQuestion = 0;
+ request->u.resolve.qsrv.qnameOrig = mDNSNULL;
+ request->u.resolve.qsrv.AnonInfo = mDNSNULL;
+ request->u.resolve.qsrv.pid = request->process_id;
+ request->u.resolve.qsrv.QuestionCallback = resolve_result_callback;
+ request->u.resolve.qsrv.QuestionContext = request;
+
+ request->u.resolve.qtxt.InterfaceID = InterfaceID;
+ request->u.resolve.qtxt.flags = flags;
+ request->u.resolve.qtxt.Target = zeroAddr;
+ AssignDomainName(&request->u.resolve.qtxt.qname, &fqdn);
+ request->u.resolve.qtxt.qtype = kDNSType_TXT;
+ request->u.resolve.qtxt.qclass = kDNSClass_IN;
+ request->u.resolve.qtxt.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0;
+ request->u.resolve.qtxt.ExpectUnique = mDNStrue;
+ request->u.resolve.qtxt.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0;
+ request->u.resolve.qtxt.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
+ request->u.resolve.qtxt.SuppressUnusable = mDNSfalse;
+ request->u.resolve.qtxt.SearchListIndex = 0;
+ request->u.resolve.qtxt.AppendSearchDomains = 0;
+ request->u.resolve.qtxt.RetryWithSearchDomains = mDNSfalse;
+ request->u.resolve.qtxt.TimeoutQuestion = 0;
+ request->u.resolve.qtxt.WakeOnResolve = 0;
+ request->u.resolve.qtxt.UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0;
+ request->u.resolve.qtxt.ValidationRequired = 0;
+ request->u.resolve.qtxt.ValidatingResponse = 0;
+ request->u.resolve.qtxt.ProxyQuestion = 0;
+ request->u.resolve.qtxt.qnameOrig = mDNSNULL;
+ request->u.resolve.qtxt.AnonInfo = mDNSNULL;
+ request->u.resolve.qtxt.pid = request->process_id;
+ request->u.resolve.qtxt.QuestionCallback = resolve_result_callback;
+ request->u.resolve.qtxt.QuestionContext = request;
+
+ request->u.resolve.ReportTime = NonZeroTime(mDNS_TimeNow(&mDNSStorage) + 130 * mDNSPlatformOneSecond);
+
+ request->u.resolve.external_advertise = mDNSfalse;
+
+#if 0
+ if (!AuthorizedDomain(request, &fqdn, AutoBrowseDomains)) return(mStatus_NoError);
+#endif
+
+ // ask the questions
+ LogOperation("%3d: DNSServiceResolve(%X %d %##s) START PID[%d](%s)", request->sd, flags, interfaceIndex,
+ request->u.resolve.qsrv.qname.c, request->process_id, request->pid_name);
+ err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qsrv);
+
+ if (!err)
+ {
+ err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qtxt);
+ if (err)
+ {
+ mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv);
+ }
+ else
+ {
+ request->terminate = resolve_termination_callback;
+ LogMcastQ(&mDNSStorage, &request->u.resolve.qsrv, request, q_start);
+ if (callExternalHelpers(InterfaceID, &fqdn, flags))
+ {
+ request->u.resolve.external_advertise = mDNStrue;
+ LogInfo("handle_resolve_request: calling external_start_resolving_service()");
+ external_start_resolving_service(InterfaceID, &fqdn, flags);
+ }
+ }
+ }
+
+ return(err);
+}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - DNSServiceQueryRecord
+#endif
+
+// 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.
+
+// Returns -1 to tell the caller that it should not try to reissue the query anymore
+// Returns 1 on successfully appending a search domain and the caller should reissue the new query
+// Returns 0 when there are no more search domains and the caller should reissue the query
+mDNSlocal int AppendNewSearchDomain(mDNS *const m, DNSQuestion *question)
+{
+ domainname *sd;
+ mStatus err;
+
+ // Sanity check: The caller already checks this. We use -1 to indicate that we have searched all
+ // the domains and should try the single label query directly on the wire.
+ if (question->SearchListIndex == -1)
+ {
+ LogMsg("AppendNewSearchDomain: question %##s (%s) SearchListIndex is -1", question->qname.c, DNSTypeName(question->qtype));
+ return -1;
+ }
+
+ if (!question->AppendSearchDomains)
+ {
+ LogMsg("AppendNewSearchDomain: question %##s (%s) AppendSearchDoamins is 0", question->qname.c, DNSTypeName(question->qtype));
+ return -1;
+ }
+
+ // Save the original name, before we modify them below.
+ if (!question->qnameOrig)
+ {
+ question->qnameOrig = mallocL("AppendNewSearchDomain", sizeof(domainname));
+ if (!question->qnameOrig) { LogMsg("AppendNewSearchDomain: ERROR!! malloc failure"); return -1; }
+ question->qnameOrig->c[0] = 0;
+ AssignDomainName(question->qnameOrig, &question->qname);
+ LogInfo("AppendSearchDomain: qnameOrig %##s", question->qnameOrig->c);
+ }
+
+ sd = uDNS_GetNextSearchDomain(m, question->InterfaceID, &question->SearchListIndex, !question->AppendLocalSearchDomains);
+ // We use -1 to indicate that we have searched all the domains and should try the single label
+ // query directly on the wire. uDNS_GetNextSearchDomain should never return a negative value
+ if (question->SearchListIndex == -1)
+ {
+ LogMsg("AppendNewSearchDomain: ERROR!! uDNS_GetNextSearchDomain returned -1");
+ return -1;
+ }
+
+ // Not a common case. Perhaps, we should try the next search domain if it exceeds ?
+ if (sd && (DomainNameLength(question->qnameOrig) + DomainNameLength(sd)) > MAX_DOMAIN_NAME)
+ {
+ LogMsg("AppendNewSearchDomain: ERROR!! exceeding max domain length for %##s (%s) SearchDomain %##s length %d, Question name length %d", question->qnameOrig->c, DNSTypeName(question->qtype), sd->c, DomainNameLength(question->qnameOrig), DomainNameLength(sd));
+ return -1;
+ }
+
+ // if there are no more search domains and we have already tried this question
+ // without appending search domains, then we are done.
+ if (!sd && !ApplySearchDomainsFirst(question))
+ {
+ LogInfo("AppnedNewSearchDomain: No more search domains for question with name %##s (%s), not trying anymore", question->qname.c, DNSTypeName(question->qtype));
+ return -1;
+ }
+
+ // Stop the question before changing the name as negative cache entries could be pointing at this question.
+ // Even if we don't change the question in the case of returning 0, the caller is going to restart the
+ // question.
+ err = mDNS_StopQuery(&mDNSStorage, question);
+ if (err) { LogMsg("AppendNewSearchDomain: ERROR!! %##s %s mDNS_StopQuery: %d, while retrying with search domains", question->qname.c, DNSTypeName(question->qtype), (int)err); }
+
+ AssignDomainName(&question->qname, question->qnameOrig);
+ if (sd)
+ {
+ AppendDomainName(&question->qname, sd);
+ LogInfo("AppnedNewSearchDomain: Returning question with name %##s, SearchListIndex %d", question->qname.c, question->SearchListIndex);
+ return 1;
+ }
+
+ // Try the question as single label
+ LogInfo("AppnedNewSearchDomain: No more search domains for question with name %##s (%s), trying one last time", question->qname.c, DNSTypeName(question->qtype));
+ return 0;
+}
+
+#if APPLE_OSX_mDNSResponder
+
+mDNSlocal mDNSBool DomainInSearchList(const domainname *domain, mDNSBool excludeLocal)
+{
+ const SearchListElem *s;
+ int qcount, scount;
+
+ qcount = CountLabels(domain);
+ for (s=SearchList; s; s=s->next)
+ {
+ if (excludeLocal && SameDomainName(&s->domain, &localdomain))
+ continue;
+ scount = CountLabels(&s->domain);
+ if (qcount >= scount)
+ {
+ // Note: When qcount == scount, we do a complete match of the domain
+ // which is expected by the callers.
+ const domainname *d = SkipLeadingLabels(domain, (qcount - scount));
+ if (SameDomainName(&s->domain, d))
+ {
+ return mDNStrue;
+ }
+ }
+ }
+ return mDNSfalse;
+}
+
+// The caller already checks that this is a dotlocal question.
+mDNSlocal mDNSBool ShouldDeliverNegativeResponse(mDNS *const m, DNSQuestion *question)
+{
+ mDNSu16 qtype;
+
+ // If the question matches the search domain exactly or the search domain is a
+ // subdomain of the question, it is most likely a valid unicast domain and hence
+ // don't suppress negative responses.
+ //
+ // If the user has configured ".local" as a search domain, we don't want
+ // to deliver a negative response for names ending in ".local" as that would
+ // prevent bonjour discovery. Passing mDNStrue for the last argument excludes
+ // ".local" search domains.
+ if (DomainInSearchList(&question->qname, mDNStrue))
+ {
+ LogOperation("ShouldDeliverNegativeResponse: Question %##s (%s) in SearchList", question->qname.c, DNSTypeName(question->qtype));
+ return mDNStrue;
+ }
+
+ // Deliver negative response for A/AAAA if there was a positive response for AAAA/A respectively.
+ if (question->qtype != kDNSType_A && question->qtype != kDNSType_AAAA)
+ {
+ LogOperation("ShouldDeliverNegativeResponse: Question %##s (%s) not answering local question with negative unicast response",
+ question->qname.c, DNSTypeName(question->qtype));
+ return mDNSfalse;
+ }
+ qtype = (question->qtype == kDNSType_A ? kDNSType_AAAA : kDNSType_A);
+ if (!mDNS_CheckForCacheRecord(m, question, qtype))
+ {
+ LogOperation("ShouldDeliverNegativeResponse:Question %##s (%s) not answering local question with negative unicast response"
+ " (can't find positive record)", question->qname.c, DNSTypeName(question->qtype));
+ return mDNSfalse;
+ }
+ LogOperation("ShouldDeliverNegativeResponse:Question %##s (%s) answering local with negative unicast response (found positive record)",
+ question->qname.c, DNSTypeName(question->qtype));
+ return mDNStrue;
+}
+
+// Workaround for networks using Microsoft Active Directory using "local" as a private internal
+// top-level domain
+mDNSlocal mStatus SendAdditionalQuery(DNSQuestion *q, request_state *request, mStatus err)
+{
+#ifndef UNICAST_DISABLED
+ extern domainname ActiveDirectoryPrimaryDomain;
+ DNSQuestion **question2;
+ #define VALID_MSAD_SRV_TRANSPORT(T) (SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_tcp") || SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_udp"))
+ #define VALID_MSAD_SRV(Q) ((Q)->qtype == kDNSType_SRV && VALID_MSAD_SRV_TRANSPORT(SecondLabel(&(Q)->qname)))
+
+ question2 = mDNSNULL;
+ if (request->hdr.op == query_request)
+ question2 = &request->u.queryrecord.q2;
+ else if (request->hdr.op == addrinfo_request)
+ {
+ if (q->qtype == kDNSType_A)
+ question2 = &request->u.addrinfo.q42;
+ else if (q->qtype == kDNSType_AAAA)
+ question2 = &request->u.addrinfo.q62;
+ }
+ if (!question2)
+ {
+ LogMsg("SendAdditionalQuery: question2 NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+ return mStatus_BadParamErr;
+ }
+
+ // Sanity check: If we already sent an additonal query, we don't need to send one more.
+ //
+ // 1. When the application calls DNSServiceQueryRecord or DNSServiceGetAddrInfo with a .local name, this function
+ // is called to see whether a unicast query should be sent or not.
+ //
+ // 2. As a result of appending search domains, the question may be end up with a .local suffix even though it
+ // was not a .local name to start with. In that case, queryrecord_result_callback calls this function to
+ // send the additional query.
+ //
+ // Thus, it should not be called more than once.
+ if (*question2)
+ {
+ LogInfo("SendAdditionalQuery: question2 already sent for %##s (%s), no more q2", q->qname.c, DNSTypeName(q->qtype));
+ return err;
+ }
+
+ if (!q->ForceMCast && SameDomainLabel(LastLabel(&q->qname), (const mDNSu8 *)&localdomain))
+ if (q->qtype == kDNSType_A || q->qtype == kDNSType_AAAA || VALID_MSAD_SRV(q))
+ {
+ DNSQuestion *q2;
+ int labels = CountLabels(&q->qname);
+ q2 = mallocL("DNSQuestion", sizeof(DNSQuestion));
+ if (!q2) FatalError("ERROR: SendAdditionalQuery malloc");
+ *question2 = q2;
+ *q2 = *q;
+ q2->InterfaceID = mDNSInterface_Unicast;
+ q2->ExpectUnique = mDNStrue;
+ // Always set the QuestionContext to indicate that this question should be stopped
+ // before freeing. Don't rely on "q".
+ q2->QuestionContext = request;
+ // If the query starts as a single label e.g., somehost, and we have search domains with .local,
+ // queryrecord_result_callback calls this function when .local is appended to "somehost".
+ // At that time, the name in "q" is pointing at somehost.local and its qnameOrig pointing at
+ // "somehost". We need to copy that information so that when we retry with a different search
+ // domain e.g., mycompany.local, we get "somehost.mycompany.local".
+ if (q->qnameOrig)
+ {
+ (*question2)->qnameOrig = mallocL("SendAdditionalQuery", DomainNameLength(q->qnameOrig));
+ if (!(*question2)->qnameOrig) { LogMsg("SendAdditionalQuery: ERROR!! malloc failure"); return mStatus_NoMemoryErr; }
+ (*question2)->qnameOrig->c[0] = 0;
+ AssignDomainName((*question2)->qnameOrig, q->qnameOrig);
+ LogInfo("SendAdditionalQuery: qnameOrig %##s", (*question2)->qnameOrig->c);
+ }
+ // For names of the form "<one-or-more-labels>.bar.local." we always do a second unicast query in parallel.
+ // For names of the form "<one-label>.local." it's less clear whether we should do a unicast query.
+ // If the name being queried is exactly the same as the name in the DHCP "domain" option (e.g. the DHCP
+ // "domain" is my-small-company.local, and the user types "my-small-company.local" into their web browser)
+ // then that's a hint that it's worth doing a unicast query. Otherwise, we first check to see if the
+ // site's DNS server claims there's an SOA record for "local", and if so, that's also a hint that queries
+ // for names in the "local" domain will be safely answered privately before they hit the root name servers.
+ // Note that in the "my-small-company.local" example above there will typically be an SOA record for
+ // "my-small-company.local" but *not* for "local", which is why the "local SOA" check would fail in that case.
+ // We need to check against both ActiveDirectoryPrimaryDomain and SearchList. If it matches against either
+ // of those, we don't want do the SOA check for the local
+ if (labels == 2 && !SameDomainName(&q->qname, &ActiveDirectoryPrimaryDomain) && !DomainInSearchList(&q->qname, mDNSfalse))
+ {
+ AssignDomainName(&q2->qname, &localdomain);
+ q2->qtype = kDNSType_SOA;
+ q2->LongLived = mDNSfalse;
+ q2->ForceMCast = mDNSfalse;
+ q2->ReturnIntermed = mDNStrue;
+ // Don't append search domains for the .local SOA query
+ q2->AppendSearchDomains = 0;
+ q2->AppendLocalSearchDomains = 0;
+ q2->RetryWithSearchDomains = mDNSfalse;
+ q2->SearchListIndex = 0;
+ q2->TimeoutQuestion = 0;
+ q2->AnonInfo = mDNSNULL;
+ q2->pid = request->process_id;
+ }
+ LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast", request->sd, q2->qname.c, DNSTypeName(q2->qtype));
+ err = mDNS_StartQuery(&mDNSStorage, q2);
+ if (err) LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q2->qname.c, DNSTypeName(q2->qtype), (int)err);
+ }
+ return(err);
+#else // !UNICAST_DISABLED
+ (void) q;
+ (void) request;
+ (void) err;
+
+ return mStatus_NoError;
+#endif // !UNICAST_DISABLED
+}
+#endif // APPLE_OSX_mDNSResponder
+
+// This function tries to append a search domain if valid and possible. If so, returns true.
+mDNSlocal mDNSBool RetryQuestionWithSearchDomains(mDNS *const m, DNSQuestion *question, request_state *req, QC_result AddRecord)
+{
+ int result;
+ // RetryWithSearchDomains tells the core to call us back so that we can retry with search domains if there is no
+ // answer in the cache or /etc/hosts. In the first call back from the core, we clear RetryWithSearchDomains so
+ // that we don't get called back repeatedly. If we got an answer from the cache or /etc/hosts, we don't touch
+ // RetryWithSearchDomains which may or may not be set.
+ //
+ // If we get e.g., NXDOMAIN and the query is neither suppressed nor exhausted the domain search list and
+ // is a valid question for appending search domains, retry by appending domains
+
+ if ((AddRecord != QC_suppressed) && question->SearchListIndex != -1 && question->AppendSearchDomains)
+ {
+ question->RetryWithSearchDomains = 0;
+ result = AppendNewSearchDomain(m, question);
+ // As long as the result is either zero or 1, we retry the question. If we exahaust the search
+ // domains (result is zero) we try the original query (as it was before appending the search
+ // domains) as such on the wire as a last resort if we have not tried them before. For queries
+ // with more than one label, we have already tried them before appending search domains and
+ // hence don't retry again
+ if (result != -1)
+ {
+ mStatus err;
+ err = mDNS_StartQuery(m, question);
+ if (!err)
+ {
+ LogOperation("%3d: RetryQuestionWithSearchDomains(%##s, %s), retrying after appending search domain", req->sd, question->qname.c, DNSTypeName(question->qtype));
+ // If the result was zero, it meant that there are no search domains and we just retried the question
+ // as a single label and we should not retry with search domains anymore.
+ if (!result) question->SearchListIndex = -1;
+ return mDNStrue;
+ }
+ else
+ {
+ LogMsg("%3d: ERROR: RetryQuestionWithSearchDomains %##s %s mDNS_StartQuery: %d, while retrying with search domains", req->sd, question->qname.c, DNSTypeName(question->qtype), (int)err);
+ // We have already stopped the query and could not restart. Reset the appropriate pointers
+ // so that we don't call stop again when the question terminates
+ question->QuestionContext = mDNSNULL;
+ }
+ }
+ }
+ else
+ {
+ LogInfo("%3d: RetryQuestionWithSearchDomains: Not appending search domains - SuppressQuery %d, SearchListIndex %d, AppendSearchDomains %d", req->sd, AddRecord, question->SearchListIndex, question->AppendSearchDomains);
+ }
+ return mDNSfalse;
+}
+
+mDNSlocal void queryrecord_result_reply(mDNS *const m, request_state *req, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord,
+ DNSServiceErrorType error)
+{
+ char name[MAX_ESCAPED_DOMAIN_NAME];
+ size_t len;
+ DNSServiceFlags flags = 0;
+ reply_state *rep;
+ char *data;
+
+ ConvertDomainNameToCString(answer->name, name);
+
+ LogOperation("%3d: %s(%##s, %s) %s %s", req->sd,
+ req->hdr.op == query_request ? "DNSServiceQueryRecord" : "DNSServiceGetAddrInfo",
+ question->qname.c, DNSTypeName(question->qtype), AddRecord ? "ADD" : "RMV", RRDisplayString(m, answer));
+
+ len = sizeof(DNSServiceFlags); // calculate reply data length
+ len += sizeof(mDNSu32); // interface index
+ len += sizeof(DNSServiceErrorType);
+ len += strlen(name) + 1;
+ len += 3 * sizeof(mDNSu16); // type, class, rdlen
+ len += answer->rdlength;
+ len += sizeof(mDNSu32); // TTL
+
+ rep = create_reply(req->hdr.op == query_request ? query_reply_op : addrinfo_reply_op, len, req);
+
+ if (AddRecord)
+ flags |= kDNSServiceFlagsAdd;
+ if (question->ValidationStatus != 0)
+ {
+ error = kDNSServiceErr_NoError;
+ if (question->ValidationRequired && question->ValidationState == DNSSECValDone)
+ {
+ switch (question->ValidationStatus) //Set the dnssec flags to be passed on to the Apps here
+ {
+ case DNSSEC_Secure:
+ flags |= kDNSServiceFlagsSecure;
+ break;
+ case DNSSEC_Insecure:
+ flags |= kDNSServiceFlagsInsecure;
+ break;
+ case DNSSEC_Indeterminate:
+ flags |= kDNSServiceFlagsIndeterminate;
+ break;
+ case DNSSEC_Bogus:
+ flags |= kDNSServiceFlagsBogus;
+ break;
+ default:
+ LogMsg("queryrecord_result_reply unknown status %d for %##s", question->ValidationStatus, question->qname.c);
+ }
+ }
+ }
+
+ rep->rhdr->flags = dnssd_htonl(flags);
+ // Call mDNSPlatformInterfaceIndexfromInterfaceID, but suppressNetworkChange (last argument). Otherwise, if the
+ // InterfaceID is not valid, then it simulates a "NetworkChanged" which in turn makes questions
+ // to be stopped and started including *this* one. Normally the InterfaceID is valid. But when we
+ // are using the /etc/hosts entries to answer a question, the InterfaceID may not be known to the
+ // mDNS core . Eventually, we should remove the calls to "NetworkChanged" in
+ // mDNSPlatformInterfaceIndexfromInterfaceID when it can't find InterfaceID as ResourceRecords
+ // should not have existed to answer this question if the corresponding interface is not valid.
+ rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNStrue));
+ rep->rhdr->error = dnssd_htonl(error);
+
+ data = (char *)&rep->rhdr[1];
+
+ put_string(name, &data);
+ put_uint16(answer->rrtype, &data);
+ put_uint16(answer->rrclass, &data);
+ put_uint16(answer->rdlength, &data);
+ // We need to use putRData here instead of the crude put_rdata function, because the crude put_rdata
+ // function just does a blind memory copy without regard to structures that may have holes in them.
+ if (answer->rdlength)
+ if (!putRData(mDNSNULL, (mDNSu8 *)data, (mDNSu8 *)rep->rhdr + len, answer))
+ LogMsg("queryrecord_result_reply putRData failed %d", (mDNSu8 *)rep->rhdr + len - (mDNSu8 *)data);
+ data += answer->rdlength;
+ put_uint32(AddRecord ? answer->rroriginalttl : 0, &data);
+
+ append_reply(req, rep);
+ // Stop the question, if we just timed out
+ if (error == kDNSServiceErr_Timeout)
+ {
+ mDNS_StopQuery(m, question);
+ // Reset the pointers so that we don't call stop on termination
+ question->QuestionContext = mDNSNULL;
+ }
+ else if ((AddRecord == QC_add) && req->hdr.op == addrinfo_request)
+ {
+ // Note: We count all answers including LocalOnly e.g., /etc/hosts. If we
+ // exclude that, v4ans/v6ans will be zero and we would wrongly think that
+ // we did not answer questions and setup the status to deliver triggers.
+ if (question->qtype == kDNSType_A)
+ req->u.addrinfo.v4ans = 1;
+ if (question->qtype == kDNSType_AAAA)
+ req->u.addrinfo.v6ans = 1;
+ }
+ else if ((AddRecord == QC_add) && req->hdr.op == query_request)
+ {
+ if (question->qtype == kDNSType_A || question->qtype == kDNSType_AAAA)
+ req->u.queryrecord.ans = 1;
+ }
+
+#if APPLE_OSX_mDNSResponder
+#if !NO_WCF
+ CHECK_WCF_FUNCTION(WCFIsServerRunning)
+ {
+ struct xucred x;
+ socklen_t xucredlen = sizeof(x);
+
+ if (WCFIsServerRunning((WCFConnection *)m->WCF) && answer->rdlength != 0)
+ {
+ if (getsockopt(req->sd, 0, LOCAL_PEERCRED, &x, &xucredlen) >= 0 &&
+ (x.cr_version == XUCRED_VERSION))
+ {
+ struct sockaddr_storage addr;
+ const RDataBody2 *const rdb = (RDataBody2 *)answer->rdata->u.data;
+ addr.ss_len = 0;
+ if (answer->rrtype == kDNSType_A || answer->rrtype == kDNSType_AAAA)
+ {
+ if (answer->rrtype == kDNSType_A)
+ {
+ struct sockaddr_in *sin = (struct sockaddr_in *)&addr;
+ sin->sin_port = 0;
+ if (!putRData(mDNSNULL, (mDNSu8 *)&sin->sin_addr, (mDNSu8 *)(&sin->sin_addr + sizeof(rdb->ipv4)), answer))
+ LogMsg("queryrecord_result_reply: WCF AF_INET putRData failed");
+ else
+ {
+ addr.ss_len = sizeof (struct sockaddr_in);
+ addr.ss_family = AF_INET;
+ }
+ }
+ else if (answer->rrtype == kDNSType_AAAA)
+ {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr;
+ sin6->sin6_port = 0;
+ if (!putRData(mDNSNULL, (mDNSu8 *)&sin6->sin6_addr, (mDNSu8 *)(&sin6->sin6_addr + sizeof(rdb->ipv6)), answer))
+ LogMsg("queryrecord_result_reply: WCF AF_INET6 putRData failed");
+ else
+ {
+ addr.ss_len = sizeof (struct sockaddr_in6);
+ addr.ss_family = AF_INET6;
+ }
+ }
+ if (addr.ss_len)
+ {
+ debugf("queryrecord_result_reply: Name %s, uid %u, addr length %d", name, x.cr_uid, addr.ss_len);
+ CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr)
+ {
+ WCFNameResolvesToAddr(m->WCF, name, (struct sockaddr *)&addr, x.cr_uid);
+ }
+ }
+ }
+ else if (answer->rrtype == kDNSType_CNAME)
+ {
+ domainname cname;
+ char cname_cstr[MAX_ESCAPED_DOMAIN_NAME];
+ if (!putRData(mDNSNULL, cname.c, (mDNSu8 *)(cname.c + MAX_DOMAIN_NAME), answer))
+ LogMsg("queryrecord_result_reply: WCF CNAME putRData failed");
+ else
+ {
+ ConvertDomainNameToCString(&cname, cname_cstr);
+ CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr)
+ {
+ WCFNameResolvesToName(m->WCF, name, cname_cstr, x.cr_uid);
+ }
+ }
+ }
+ }
+ else my_perror("queryrecord_result_reply: ERROR: getsockopt LOCAL_PEERCRED");
+ }
+ }
+#endif
+#endif
+}
+
+mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+{
+ request_state *req = question->QuestionContext;
+ DNSServiceErrorType error = kDNSServiceErr_NoError;
+ DNSQuestion *q = mDNSNULL;
+
+#if APPLE_OSX_mDNSResponder
+ {
+ // Sanity check: QuestionContext is set to NULL after we stop the question and hence we should not
+ // get any callbacks from the core after this.
+ if (!req)
+ {
+ LogMsg("queryrecord_result_callback: ERROR!! QuestionContext NULL for %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
+ return;
+ }
+ if (req->hdr.op == query_request && question == req->u.queryrecord.q2)
+ q = &req->u.queryrecord.q;
+ else if (req->hdr.op == addrinfo_request && question == req->u.addrinfo.q42)
+ q = &req->u.addrinfo.q4;
+ else if (req->hdr.op == addrinfo_request && question == req->u.addrinfo.q62)
+ q = &req->u.addrinfo.q6;
+
+ if (q && question->qtype != q->qtype && !SameDomainName(&question->qname, &q->qname))
+ {
+ mStatus err;
+ domainname *orig = question->qnameOrig;
+
+ LogInfo("queryrecord_result_callback: Stopping q2 local %##s", question->qname.c);
+ mDNS_StopQuery(m, question);
+ question->QuestionContext = mDNSNULL;
+
+ // We got a negative response for the SOA record indicating that .local does not exist.
+ // But we might have other search domains (that does not end in .local) that can be
+ // appended to this question. In that case, we want to retry the question. Otherwise,
+ // we don't want to try this question as unicast.
+ if (answer->RecordType == kDNSRecordTypePacketNegative && !q->AppendSearchDomains)
+ {
+ LogInfo("queryrecord_result_callback: question %##s AppendSearchDomains zero", q->qname.c);
+ return;
+ }
+
+ // If we got a non-negative answer for our "local SOA" test query, start an additional parallel unicast query
+ //
+ // Note: When we copy the original question, we copy everything including the AppendSearchDomains,
+ // RetryWithSearchDomains except for qnameOrig which can be non-NULL if the original question is
+ // e.g., somehost and then we appended e.g., ".local" and retried that question. See comment in
+ // SendAdditionalQuery as to how qnameOrig gets initialized.
+ *question = *q;
+ question->InterfaceID = mDNSInterface_Unicast;
+ question->ExpectUnique = mDNStrue;
+ question->qnameOrig = orig;
+
+ LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast, context %p", req->sd, question->qname.c, DNSTypeName(question->qtype), question->QuestionContext);
+
+ // If the original question timed out, its QuestionContext would already be set to NULL and that's what we copied above.
+ // Hence, we need to set it explicitly here.
+ question->QuestionContext = req;
+ err = mDNS_StartQuery(m, question);
+ if (err) LogMsg("%3d: ERROR: queryrecord_result_callback %##s %s mDNS_StartQuery: %d", req->sd, question->qname.c, DNSTypeName(question->qtype), (int)err);
+
+ // If we got a positive response to local SOA, then try the .local question as unicast
+ if (answer->RecordType != kDNSRecordTypePacketNegative) return;
+
+ // Fall through and get the next search domain. The question is pointing at .local
+ // and we don't want to try that. Try the next search domain. Don't try with local
+ // search domains for the unicast question anymore.
+ //
+ // Note: we started the question above which will be stopped immediately (never sent on the wire)
+ // before we pick the next search domain below. RetryQuestionWithSearchDomains assumes that the
+ // question has already started.
+ question->AppendLocalSearchDomains = 0;
+ }
+
+ if (q && AddRecord && AddRecord != QC_dnssec && (question->InterfaceID == mDNSInterface_Unicast) && !answer->rdlength)
+ {
+ // If we get a negative response to the unicast query that we sent above, retry after appending search domains
+ // Note: We could have appended search domains below (where do it for regular unicast questions) instead of doing it here.
+ // As we ignore negative unicast answers below, we would never reach the code where the search domains are appended.
+ // To keep things simple, we handle unicast ".local" separately here.
+ LogInfo("queryrecord_result_callback: Retrying .local question %##s (%s) as unicast after appending search domains", question->qname.c, DNSTypeName(question->qtype));
+ if (RetryQuestionWithSearchDomains(m, question, req, AddRecord))
+ return;
+ if (question->AppendSearchDomains && !question->AppendLocalSearchDomains && IsLocalDomain(&question->qname))
+ {
+ // If "local" is the last search domain, we need to stop the question so that we don't send the "local"
+ // question on the wire as we got a negative response for the local SOA. But, we can't stop the question
+ // yet as we may have to timeout the question (done by the "core") for which we need to leave the question
+ // in the list. We leave it disabled so that it does not hit the wire.
+ LogInfo("queryrecord_result_callback: Disabling .local question %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
+ question->ThisQInterval = 0;
+ }
+ }
+ // If we are here it means that either "question" is not "q2" OR we got a positive response for "q2" OR we have no more search
+ // domains to append for "q2". In all cases, fall through and deliver the response
+ }
+#endif // APPLE_OSX_mDNSResponder
+
+ // If a query is being suppressed for some reason, we don't have to do any other
+ // processing.
+ //
+ // Note: We don't check for "SuppressQuery" and instead use QC_suppressed because
+ // the "core" needs to temporarily turn off SuppressQuery to answer this query.
+ if (AddRecord == QC_suppressed)
+ {
+ LogInfo("queryrecord_result_callback: Suppressed question %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
+ queryrecord_result_reply(m, req, question, answer, AddRecord, kDNSServiceErr_NoSuchRecord);
+ return;
+ }
+
+ if (answer->RecordType == kDNSRecordTypePacketNegative)
+ {
+ // If this question needs to be timed out and we have reached the stop time, mark
+ // the error as timeout. It is possible that we might get a negative response from an
+ // external DNS server at the same time when this question reaches its stop time. We
+ // can't tell the difference as there is no indication in the callback. This should
+ // be okay as we will be timing out this query anyway.
+ mDNS_Lock(m);
+ if (question->TimeoutQuestion)
+ {
+ if ((m->timenow - question->StopTime) >= 0)
+ {
+ LogInfo("queryrecord_result_callback:Question %##s (%s) timing out, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID);
+ error = kDNSServiceErr_Timeout;
+ }
+ }
+ mDNS_Unlock(m);
+ // When we're doing parallel unicast and multicast queries for dot-local names (for supporting Microsoft
+ // Active Directory sites) we need to ignore negative unicast answers. Otherwise we'll generate negative
+ // answers for just about every single multicast name we ever look up, since the Microsoft Active Directory
+ // server is going to assert that pretty much every single multicast name doesn't exist.
+ //
+ // If we are timing out this query, we need to deliver the negative answer to the application
+ if (error != kDNSServiceErr_Timeout)
+ {
+ if (!answer->InterfaceID && IsLocalDomain(answer->name))
+ {
+ // Sanity check: "q" will be set only if "question" is the .local unicast query.
+ if (!q)
+ {
+ LogMsg("queryrecord_result_callback: ERROR!! answering multicast question %s with unicast cache record",
+ RRDisplayString(m, answer));
+ return;
+ }
+#if APPLE_OSX_mDNSResponder
+ if (!ShouldDeliverNegativeResponse(m, question))
+ {
+ return;
+ }
+#endif // APPLE_OSX_mDNSResponder
+ LogInfo("queryrecord_result_callback:Question %##s (%s) answering local with negative unicast response", question->qname.c,
+ DNSTypeName(question->qtype));
+ }
+ error = kDNSServiceErr_NoSuchRecord;
+ }
+ }
+ // If we get a negative answer, try appending search domains. Don't append search domains
+ // - if we are timing out this question
+ // - if the negative response was received as a result of a multicast query
+ // - if this is an additional query (q2), we already appended search domains above (indicated by "!q" below)
+ // - if this response is forced e.g., dnssec validation result
+ if (error != kDNSServiceErr_Timeout)
+ {
+ if (!q && !answer->InterfaceID && !answer->rdlength && AddRecord && AddRecord != QC_dnssec)
+ {
+ // If the original question did not end in .local, we did not send an SOA query
+ // to figure out whether we should send an additional unicast query or not. If we just
+ // appended .local, we need to see if we need to send an additional query. This should
+ // normally happen just once because after we append .local, we ignore all negative
+ // responses for .local above.
+ LogInfo("queryrecord_result_callback: Retrying question %##s (%s) after appending search domains", question->qname.c, DNSTypeName(question->qtype));
+ if (RetryQuestionWithSearchDomains(m, question, req, AddRecord))
+ {
+ // Note: We need to call SendAdditionalQuery every time after appending a search domain as .local could
+ // be anywhere in the search domain list.
+#if APPLE_OSX_mDNSResponder
+ mStatus err = mStatus_NoError;
+ err = SendAdditionalQuery(question, req, err);
+ if (err) LogMsg("queryrecord_result_callback: Sending .local SOA query failed, after appending domains");
+#endif // APPLE_OSX_mDNSResponder
+ return;
+ }
+ }
+ }
+ queryrecord_result_reply(m, req, question, answer, AddRecord, error);
+}
+
+mDNSlocal void queryrecord_termination_callback(request_state *request)
+{
+ LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) STOP PID[%d](%s)",
+ request->sd, request->u.queryrecord.q.qname.c, DNSTypeName(request->u.queryrecord.q.qtype), request->process_id, request->pid_name);
+ if (request->u.queryrecord.q.QuestionContext)
+ {
+ mDNS_StopQuery(&mDNSStorage, &request->u.queryrecord.q); // no need to error check
+ LogMcastQ(&mDNSStorage, &request->u.queryrecord.q, request, q_stop);
+ request->u.queryrecord.q.QuestionContext = mDNSNULL;
+ }
+ else
+ {
+ DNSQuestion *question = &request->u.queryrecord.q;
+ LogInfo("queryrecord_termination_callback: question %##s (%s) already stopped, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID);
+ }
+
+ if (request->u.queryrecord.q.qnameOrig)
+ {
+ freeL("QueryTermination", request->u.queryrecord.q.qnameOrig);
+ request->u.queryrecord.q.qnameOrig = mDNSNULL;
+ }
+
+ if (callExternalHelpers(request->u.queryrecord.q.InterfaceID, &request->u.queryrecord.q.qname, request->flags))
+ {
+ LogInfo("queryrecord_termination_callback: calling external_stop_browsing_for_service()");
+ external_stop_browsing_for_service(request->u.queryrecord.q.InterfaceID, &request->u.queryrecord.q.qname, request->u.queryrecord.q.qtype, request->flags);
+ }
+ if (request->u.queryrecord.q2)
+ {
+ if (request->u.queryrecord.q2->QuestionContext)
+ {
+ LogInfo("queryrecord_termination_callback: Stopping q2 %##s", request->u.queryrecord.q2->qname.c);
+ mDNS_StopQuery(&mDNSStorage, request->u.queryrecord.q2);
+ LogMcastQ(&mDNSStorage, request->u.queryrecord.q2, request, q_stop);
+ }
+ else
+ {
+ DNSQuestion *question = request->u.queryrecord.q2;
+ LogInfo("queryrecord_termination_callback: q2 %##s (%s) already stopped, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID);
+ }
+ if (request->u.queryrecord.q2->qnameOrig)
+ {
+ LogInfo("queryrecord_termination_callback: freeing q2 qnameOrig %##s", request->u.queryrecord.q2->qnameOrig->c);
+ freeL("QueryTermination q2", request->u.queryrecord.q2->qnameOrig);
+ request->u.queryrecord.q2->qnameOrig = mDNSNULL;
+ }
+ freeL("queryrecord Q2", request->u.queryrecord.q2);
+ request->u.queryrecord.q2 = mDNSNULL;
+ }
+#if APPLE_OSX_mDNSResponder
+ {
+ if (request->u.queryrecord.ans)
+ {
+ DNSQuestion *v4q, *v6q;
+ // If we are receiving poisitive answers, provide the hint to the
+ // upper layer.
+ v4q = v6q = mDNSNULL;
+ if (request->u.queryrecord.q.qtype == kDNSType_A)
+ v4q = &request->u.queryrecord.q;
+ else if (request->u.queryrecord.q.qtype == kDNSType_AAAA)
+ v6q = &request->u.queryrecord.q;
+ mDNSPlatformTriggerDNSRetry(&mDNSStorage, v4q, v6q);
+ }
+ }
+#endif // APPLE_OSX_mDNSResponder
+}
+
+mDNSlocal void SetQuestionPolicy(DNSQuestion *q, request_state *req)
+{
+ int i;
+
+ // The policy is either based on pid or UUID. Pass a zero pid
+ // to the "core" if the UUID is valid. If we always pass the pid,
+ // then the "core" needs to determine whether the uuid is valid
+ // by examining all the 16 bytes at the time of the policy
+ // check and also when setting the delegate socket option. Also, it
+ // requires that we zero out the uuid wherever the question is
+ // initialized to make sure that it is not interpreted as valid.
+ // To prevent these intrusive changes, just pass a zero pid to indicate
+ // that pid is not valid when uuid is valid. In future if we need the
+ // pid in the question, we will reevaluate this strategy.
+ if (req->validUUID)
+ {
+ for (i = 0; i < UUID_SIZE; i++)
+ {
+ q->uuid[i] = req->uuid[i];
+ }
+ q->pid = 0;
+ }
+ else
+ {
+ q->pid = req->process_id;
+ }
+}
+
+mDNSlocal mStatus handle_queryrecord_request(request_state *request)
+{
+ DNSQuestion *const q = &request->u.queryrecord.q;
+ char name[256];
+ mDNSu16 rrtype, rrclass;
+ mStatus err;
+
+ DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
+ mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
+ mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+ if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr);
+
+ if (get_string(&request->msgptr, request->msgend, name, 256) < 0) return(mStatus_BadParamErr);
+ rrtype = get_uint16(&request->msgptr, request->msgend);
+ rrclass = get_uint16(&request->msgptr, request->msgend);
+
+ if (!request->msgptr)
+ { LogMsg("%3d: DNSServiceQueryRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+ request->flags = flags;
+ mDNSPlatformMemZero(&request->u.queryrecord, sizeof(request->u.queryrecord));
+
+ q->InterfaceID = InterfaceID;
+ q->flags = flags;
+ q->Target = zeroAddr;
+ if (!MakeDomainNameFromDNSNameString(&q->qname, name)) return(mStatus_BadParamErr);
+#if 0
+ if (!AuthorizedDomain(request, &q->qname, AutoBrowseDomains)) return (mStatus_NoError);
+#endif
+ q->qtype = rrtype;
+ q->qclass = rrclass;
+ q->LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0;
+ q->ExpectUnique = mDNSfalse;
+ q->ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0;
+ q->ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
+ q->SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable ) != 0;
+ q->TimeoutQuestion = (flags & kDNSServiceFlagsTimeout ) != 0;
+ q->WakeOnResolve = 0;
+ q->UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0;
+ if ((flags & kDNSServiceFlagsValidate) != 0)
+ q->ValidationRequired = DNSSEC_VALIDATION_SECURE;
+ else if ((flags & kDNSServiceFlagsValidateOptional) != 0)
+ q->ValidationRequired = DNSSEC_VALIDATION_SECURE_OPTIONAL;
+ q->ValidatingResponse = 0;
+ q->ProxyQuestion = 0;
+ q->AnonInfo = mDNSNULL;
+ q->QuestionCallback = queryrecord_result_callback;
+ q->QuestionContext = request;
+ q->SearchListIndex = 0;
+
+ q->DNSSECAuthInfo = mDNSNULL;
+ q->DAIFreeCallback = mDNSNULL;
+
+ //Turn off dnssec validation for local domains and Question Types: RRSIG/ANY(ANY Type is not supported yet)
+ if ((IsLocalDomain(&q->qname)) || (q->qtype == kDNSServiceType_RRSIG) || (q->qtype == kDNSServiceType_ANY))
+ q->ValidationRequired = 0;
+
+ // Don't append search domains for fully qualified domain names including queries
+ // such as e.g., "abc." that has only one label. We convert all names to FQDNs as internally
+ // we only deal with FQDNs. Hence, we cannot look at qname to figure out whether we should
+ // append search domains or not. So, we record that information in AppendSearchDomains.
+ //
+ // We append search domains only for queries that are a single label. If overriden using command line
+ // argument "AlwaysAppendSearchDomains", then we do it for any query which is not fully qualified.
+ // For DNSSEC questions, append search domains only if kDNSServiceFlagsValidateOptional is set.
+
+ if ((!(q->ValidationRequired == DNSSEC_VALIDATION_SECURE)) && (!(q->ValidationRequired == DNSSEC_VALIDATION_INSECURE))
+ && (rrtype == kDNSType_A || rrtype == kDNSType_AAAA) && name[strlen(name) - 1] != '.' &&
+ (AlwaysAppendSearchDomains || CountLabels(&q->qname) == 1))
+ {
+ q->AppendSearchDomains = 1;
+ q->AppendLocalSearchDomains = 1;
+ }
+ else
+ {
+ q->AppendSearchDomains = 0;
+ q->AppendLocalSearchDomains = 0;
+ }
+
+ // For single label queries that are not fully qualified, look at /etc/hosts, cache and try
+ // search domains before trying them on the wire as a single label query. RetryWithSearchDomains
+ // tell the core to call back into the UDS layer if there is no valid response in /etc/hosts or
+ // the cache
+ q->RetryWithSearchDomains = ApplySearchDomainsFirst(q) ? 1 : 0;
+ q->qnameOrig = mDNSNULL;
+ SetQuestionPolicy(q, request);
+
+ LogOperation("%3d: DNSServiceQueryRecord(%X, %d, %##s, %s) START PID[%d](%s)",
+ request->sd, flags, interfaceIndex, q->qname.c, DNSTypeName(q->qtype), request->process_id, request->pid_name);
+ err = mDNS_StartQuery(&mDNSStorage, q);
+
+ if (err)
+ LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q->qname.c, DNSTypeName(q->qtype), (int)err);
+ else
+ {
+ request->terminate = queryrecord_termination_callback;
+ LogMcastQ(&mDNSStorage, q, request, q_start);
+ if (callExternalHelpers(q->InterfaceID, &q->qname, flags))
+ {
+ LogInfo("handle_queryrecord_request: calling external_start_browsing_for_service()");
+ external_start_browsing_for_service(q->InterfaceID, &q->qname, q->qtype, flags);
+ }
+ }
+
+#if APPLE_OSX_mDNSResponder
+ err = SendAdditionalQuery(q, request, err);
+#endif // APPLE_OSX_mDNSResponder
+
+ return(err);
+}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - DNSServiceEnumerateDomains
+#endif
+
+mDNSlocal reply_state *format_enumeration_reply(request_state *request,
+ const char *domain, DNSServiceFlags flags, mDNSu32 ifi, DNSServiceErrorType err)
+{
+ size_t len;
+ reply_state *reply;
+ char *data;
+
+ len = sizeof(DNSServiceFlags);
+ len += sizeof(mDNSu32);
+ len += sizeof(DNSServiceErrorType);
+ len += strlen(domain) + 1;
+
+ reply = create_reply(enumeration_reply_op, len, request);
+ reply->rhdr->flags = dnssd_htonl(flags);
+ reply->rhdr->ifi = dnssd_htonl(ifi);
+ reply->rhdr->error = dnssd_htonl(err);
+ data = (char *)&reply->rhdr[1];
+ put_string(domain, &data);
+ return reply;
+}
+
+mDNSlocal void enum_termination_callback(request_state *request)
+{
+ // Stop the domain enumeration queries to discover the WAB Browse/Registration domains
+ if (request->u.enumeration.flags & kDNSServiceFlagsRegistrationDomains)
+ {
+ LogInfo("%3d: DNSServiceEnumeration Cancel WAB Registration PID[%d](%s)", request->sd, request->process_id, request->pid_name);
+ uDNS_StopWABQueries(&mDNSStorage, UDNS_WAB_REG_QUERY);
+ }
+ else
+ {
+ LogInfo("%3d: DNSServiceEnumeration Cancel WAB Browse PID[%d](%s)", request->sd, request->process_id, request->pid_name);
+ uDNS_StopWABQueries(&mDNSStorage, UDNS_WAB_BROWSE_QUERY);
+ }
+ mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_all);
+ mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_default);
+}
+
+mDNSlocal void enum_result_callback(mDNS *const m,
+ DNSQuestion *const question, const ResourceRecord *const answer, QC_result AddRecord)
+{
+ char domain[MAX_ESCAPED_DOMAIN_NAME];
+ request_state *request = question->QuestionContext;
+ DNSServiceFlags flags = 0;
+ reply_state *reply;
+ (void)m; // Unused
+
+ if (answer->rrtype != kDNSType_PTR) return;
+
+#if 0
+ if (!AuthorizedDomain(request, &answer->rdata->u.name, request->u.enumeration.flags ? AutoRegistrationDomains : AutoBrowseDomains)) return;
+#endif
+
+ // We only return add/remove events for the browse and registration lists
+ // For the default browse and registration answers, we only give an "ADD" event
+ if (question == &request->u.enumeration.q_default && !AddRecord) return;
+
+ if (AddRecord)
+ {
+ flags |= kDNSServiceFlagsAdd;
+ if (question == &request->u.enumeration.q_default) flags |= kDNSServiceFlagsDefault;
+ }
+
+ ConvertDomainNameToCString(&answer->rdata->u.name, domain);
+ // Note that we do NOT propagate specific interface indexes to the client - for example, a domain we learn from
+ // a machine's system preferences may be discovered on the LocalOnly interface, but should be browsed on the
+ // network, so we just pass kDNSServiceInterfaceIndexAny
+ reply = format_enumeration_reply(request, domain, flags, kDNSServiceInterfaceIndexAny, kDNSServiceErr_NoError);
+ if (!reply) { LogMsg("ERROR: enum_result_callback, format_enumeration_reply"); return; }
+
+ LogOperation("%3d: DNSServiceEnumerateDomains(%#2s) RESULT %s: %s", request->sd, question->qname.c, AddRecord ? "Add" : "Rmv", domain);
+
+ append_reply(request, reply);
+}
+
+mDNSlocal mStatus handle_enum_request(request_state *request)
+{
+ mStatus err;
+ DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
+ DNSServiceFlags reg = flags & kDNSServiceFlagsRegistrationDomains;
+ mDNS_DomainType t_all = reg ? mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse;
+ mDNS_DomainType t_default = reg ? mDNS_DomainTypeRegistrationDefault : mDNS_DomainTypeBrowseDefault;
+ mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
+ mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+ if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr);
+
+ if (!request->msgptr)
+ { LogMsg("%3d: DNSServiceEnumerateDomains(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+ // mark which kind of enumeration we're doing so that we know what domain enumeration queries to stop
+ request->u.enumeration.flags = reg;
+
+ // enumeration requires multiple questions, so we must link all the context pointers so that
+ // necessary context can be reached from the callbacks
+ request->u.enumeration.q_all.QuestionContext = request;
+ request->u.enumeration.q_default.QuestionContext = request;
+
+ // if the caller hasn't specified an explicit interface, we use local-only to get the system-wide list.
+ if (!InterfaceID) InterfaceID = mDNSInterface_LocalOnly;
+
+ // make the calls
+ LogOperation("%3d: DNSServiceEnumerateDomains(%X=%s)", request->sd, flags,
+ (flags & kDNSServiceFlagsBrowseDomains ) ? "kDNSServiceFlagsBrowseDomains" :
+ (flags & kDNSServiceFlagsRegistrationDomains) ? "kDNSServiceFlagsRegistrationDomains" : "<<Unknown>>");
+ err = mDNS_GetDomains(&mDNSStorage, &request->u.enumeration.q_all, t_all, NULL, InterfaceID, enum_result_callback, request);
+ if (!err)
+ {
+ err = mDNS_GetDomains(&mDNSStorage, &request->u.enumeration.q_default, t_default, NULL, InterfaceID, enum_result_callback, request);
+ if (err) mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_all);
+ else request->terminate = enum_termination_callback;
+ }
+ if (!err)
+ {
+ // Start the domain enumeration queries to discover the WAB Browse/Registration domains
+ if (reg)
+ {
+ LogInfo("%3d: DNSServiceEnumerateDomains Start WAB Registration PID[%d](%s)", request->sd, request->process_id, request->pid_name);
+ uDNS_StartWABQueries(&mDNSStorage, UDNS_WAB_REG_QUERY);
+ }
+ else
+ {
+ LogInfo("%3d: DNSServiceEnumerateDomains Start WAB Browse PID[%d](%s)", request->sd, request->process_id, request->pid_name);
+ uDNS_StartWABQueries(&mDNSStorage, UDNS_WAB_BROWSE_QUERY);
+ }
+ }
+
+ return(err);
+}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - DNSServiceReconfirmRecord & Misc
+#endif
+
+mDNSlocal mStatus handle_reconfirm_request(request_state *request)
+{
+ mStatus status = mStatus_BadParamErr;
+ AuthRecord *rr = read_rr_from_ipc_msg(request, 0, 0);
+ if (rr)
+ {
+ status = mDNS_ReconfirmByValue(&mDNSStorage, &rr->resrec);
+ LogOperation(
+ (status == mStatus_NoError) ?
+ "%3d: DNSServiceReconfirmRecord(%s) interface %d initiated" :
+ "%3d: DNSServiceReconfirmRecord(%s) interface %d failed: %d",
+ request->sd, RRDisplayString(&mDNSStorage, &rr->resrec),
+ mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, rr->resrec.InterfaceID, mDNSfalse), status);
+ freeL("AuthRecord/handle_reconfirm_request", rr);
+ }
+ return(status);
+}
+
+#if APPLE_OSX_mDNSResponder
+
+mDNSlocal mStatus handle_release_request(request_state *request)
+{
+ mStatus err = 0;
+ char name[256], regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME];
+ domainname instance;
+
+ // extract the data from the message
+ DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
+
+ if (get_string(&request->msgptr, request->msgend, name, 256) < 0 ||
+ get_string(&request->msgptr, request->msgend, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+ get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0)
+ {
+ LogMsg("ERROR: handle_release_request - Couldn't read name/regtype/domain");
+ return(mStatus_BadParamErr);
+ }
+
+ if (!request->msgptr)
+ {
+ LogMsg("%3d: PeerConnectionRelease(unreadable parameters)", request->sd);
+ return(mStatus_BadParamErr);
+ }
+
+ if (build_domainname_from_strings(&instance, name, regtype, domain) < 0)
+ {
+ LogMsg("ERROR: handle_release_request bad “%s” “%s” “%s”", name, regtype, domain);
+ return(mStatus_BadParamErr);
+ }
+
+ LogOperation("%3d: PeerConnectionRelease(%X %##s) START PID[%d](%s)",
+ request->sd, flags, instance.c, request->process_id, request->pid_name);
+
+ external_connection_release(&instance);
+ return(err);
+}
+
+#else // APPLE_OSX_mDNSResponder
+
+mDNSlocal mStatus handle_release_request(request_state *request)
+{
+ (void) request;
+ return mStatus_UnsupportedErr;
+}
+
+#endif // APPLE_OSX_mDNSResponder
+
+mDNSlocal mStatus handle_setdomain_request(request_state *request)
+{
+ char domainstr[MAX_ESCAPED_DOMAIN_NAME];
+ domainname domain;
+ DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
+ (void)flags; // Unused
+ if (get_string(&request->msgptr, request->msgend, domainstr, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+ !MakeDomainNameFromDNSNameString(&domain, domainstr))
+ { LogMsg("%3d: DNSServiceSetDefaultDomainForUser(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+ LogOperation("%3d: DNSServiceSetDefaultDomainForUser(%##s)", request->sd, domain.c);
+ return(mStatus_NoError);
+}
+
+typedef packedstruct
+{
+ mStatus err;
+ mDNSu32 len;
+ mDNSu32 vers;
+} DaemonVersionReply;
+
+mDNSlocal void handle_getproperty_request(request_state *request)
+{
+ const mStatus BadParamErr = dnssd_htonl((mDNSu32)mStatus_BadParamErr);
+ char prop[256];
+ if (get_string(&request->msgptr, request->msgend, prop, sizeof(prop)) >= 0)
+ {
+ LogOperation("%3d: DNSServiceGetProperty(%s)", request->sd, prop);
+ if (!strcmp(prop, kDNSServiceProperty_DaemonVersion))
+ {
+ DaemonVersionReply x = { 0, dnssd_htonl(4), dnssd_htonl(_DNS_SD_H) };
+ send_all(request->sd, (const char *)&x, sizeof(x));
+ return;
+ }
+ }
+
+ // If we didn't recogize the requested property name, return BadParamErr
+ send_all(request->sd, (const char *)&BadParamErr, sizeof(BadParamErr));
+}
+
+#ifdef APPLE_OSX_mDNSResponder
+// The caller can specify either the pid or the uuid. If the pid is not specified,
+// update the effective uuid. Don't overwrite the pid which is used for debugging
+// purposes and initialized when the socket is opened.
+mDNSlocal void handle_connection_delegate_request(request_state *request)
+{
+ mDNSs32 pid;
+ socklen_t len;
+
+ len = 0;
+ pid = get_uint32(&request->msgptr, request->msgend);
+#ifdef LOCAL_PEEREPID
+ if (pid)
+ {
+ len = sizeof(pid);
+ if (getsockopt(request->sd, SOL_LOCAL, LOCAL_PEEREPID, &request->process_id, &len) != 0)
+ return;
+ // to extract the process name from the pid value
+ if (proc_pidinfo(request->process_id, PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0)
+ return;
+ mDNSPlatformStrCopy(request->pid_name, proc.pbsi_comm);
+ //LogMsg("handle_connection_delegate_request: process id %d, name %s", request->process_id, request->pid_name);
+ }
+#endif
+#ifdef LOCAL_PEEREUUID
+ if (!pid)
+ {
+ len = UUID_SIZE;
+ if (getsockopt(request->sd, SOL_LOCAL, LOCAL_PEEREUUID, request->uuid, &len) != 0)
+ return;
+ request->validUUID = mDNStrue;
+ }
+#endif
+}
+#else
+mDNSlocal void handle_connection_delegate_request(request_state *request)
+{
+ (void) request;
+}
+#endif
+
+typedef packedstruct
+{
+ mStatus err;
+ mDNSs32 pid;
+} PIDInfo;
+
+mDNSlocal void handle_getpid_request(request_state *request)
+{
+ const request_state *req;
+ mDNSs32 pid = -1;
+ mDNSu16 srcport = get_uint16(&request->msgptr, request->msgend);
+ const DNSQuestion *q = NULL;
+ PIDInfo pi;
+
+ LogOperation("%3d: DNSServiceGetPID START", request->sd);
+
+ for (req = all_requests; req; req=req->next)
+ {
+ if (req->hdr.op == query_request)
+ q = &req->u.queryrecord.q;
+ else if (req->hdr.op == addrinfo_request)
+ q = &req->u.addrinfo.q4;
+ else if (req->hdr.op == addrinfo_request)
+ q = &req->u.addrinfo.q6;
+
+ if (q && q->LocalSocket != NULL)
+ {
+ mDNSu16 port = mDNSPlatformGetUDPPort(q->LocalSocket);
+ if (port == srcport)
+ {
+ pid = req->process_id;
+ LogInfo("DNSServiceGetPID: srcport %d, pid %d [%s] question %##s", htons(srcport), pid, req->pid_name, q->qname.c);
+ break;
+ }
+ }
+ }
+ // If we cannot find in the client requests, look to see if this was
+ // started by mDNSResponder.
+ if (pid == -1)
+ {
+ for (q = mDNSStorage.Questions; q; q = q->next)
+ {
+ if (q && q->LocalSocket != NULL)
+ {
+ mDNSu16 port = mDNSPlatformGetUDPPort(q->LocalSocket);
+ if (port == srcport)
+ {
+#if APPLE_OSX_mDNSResponder
+ pid = getpid();
+#endif // APPLE_OSX_mDNSResponder
+ LogInfo("DNSServiceGetPID: srcport %d, pid %d [%s], question %##s", htons(srcport), pid, "_mDNSResponder", q->qname.c);
+ break;
+ }
+ }
+ }
+ }
+
+ pi.err = 0;
+ pi.pid = pid;
+ send_all(request->sd, (const char *)&pi, sizeof(PIDInfo));
+ LogOperation("%3d: DNSServiceGetPID STOP", request->sd);
+}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - DNSServiceNATPortMappingCreate
+#endif
+
+#define DNSServiceProtocol(X) ((X) == NATOp_AddrRequest ? 0 : (X) == NATOp_MapUDP ? kDNSServiceProtocol_UDP : kDNSServiceProtocol_TCP)
+
+mDNSlocal void port_mapping_termination_callback(request_state *request)
+{
+ LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) STOP PID[%d](%s)", request->sd,
+ DNSServiceProtocol(request->u.pm.NATinfo.Protocol),
+ mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease,
+ request->process_id, request->pid_name);
+ mDNS_StopNATOperation(&mDNSStorage, &request->u.pm.NATinfo);
+}
+
+// Called via function pointer when we get a NAT Traversal (address request or port mapping) response
+mDNSlocal void port_mapping_create_request_callback(mDNS *m, NATTraversalInfo *n)
+{
+ request_state *request = (request_state *)n->clientContext;
+ reply_state *rep;
+ int replyLen;
+ char *data;
+
+ if (!request) { LogMsg("port_mapping_create_request_callback called with unknown request_state object"); return; }
+
+ // calculate reply data length
+ replyLen = sizeof(DNSServiceFlags);
+ replyLen += 3 * sizeof(mDNSu32); // if index + addr + ttl
+ replyLen += sizeof(DNSServiceErrorType);
+ replyLen += 2 * sizeof(mDNSu16); // Internal Port + External Port
+ replyLen += sizeof(mDNSu8); // protocol
+
+ rep = create_reply(port_mapping_reply_op, replyLen, request);
+
+ rep->rhdr->flags = dnssd_htonl(0);
+ rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, n->InterfaceID, mDNSfalse));
+ rep->rhdr->error = dnssd_htonl(n->Result);
+
+ data = (char *)&rep->rhdr[1];
+
+ *data++ = request->u.pm.NATinfo.ExternalAddress.b[0];
+ *data++ = request->u.pm.NATinfo.ExternalAddress.b[1];
+ *data++ = request->u.pm.NATinfo.ExternalAddress.b[2];
+ *data++ = request->u.pm.NATinfo.ExternalAddress.b[3];
+ *data++ = DNSServiceProtocol(request->u.pm.NATinfo.Protocol);
+ *data++ = request->u.pm.NATinfo.IntPort.b[0];
+ *data++ = request->u.pm.NATinfo.IntPort.b[1];
+ *data++ = request->u.pm.NATinfo.ExternalPort.b[0];
+ *data++ = request->u.pm.NATinfo.ExternalPort.b[1];
+ put_uint32(request->u.pm.NATinfo.Lifetime, &data);
+
+ LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) RESULT %.4a:%u TTL %u", request->sd,
+ DNSServiceProtocol(request->u.pm.NATinfo.Protocol),
+ mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease,
+ &request->u.pm.NATinfo.ExternalAddress, mDNSVal16(request->u.pm.NATinfo.ExternalPort), request->u.pm.NATinfo.Lifetime);
+
+ append_reply(request, rep);
+}
+
+mDNSlocal mStatus handle_port_mapping_request(request_state *request)
+{
+ mDNSu32 ttl = 0;
+ mStatus err = mStatus_NoError;
+
+ DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
+ mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
+ mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+ mDNSu8 protocol = (mDNSu8)get_uint32(&request->msgptr, request->msgend);
+ (void)flags; // Unused
+ if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr);
+ if (request->msgptr + 8 > request->msgend) request->msgptr = NULL;
+ else
+ {
+ request->u.pm.NATinfo.IntPort.b[0] = *request->msgptr++;
+ request->u.pm.NATinfo.IntPort.b[1] = *request->msgptr++;
+ request->u.pm.ReqExt.b[0] = *request->msgptr++;
+ request->u.pm.ReqExt.b[1] = *request->msgptr++;
+ ttl = get_uint32(&request->msgptr, request->msgend);
+ }
+
+ if (!request->msgptr)
+ { LogMsg("%3d: DNSServiceNATPortMappingCreate(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+ if (protocol == 0) // If protocol == 0 (i.e. just request public address) then IntPort, ExtPort, ttl must be zero too
+ {
+ if (!mDNSIPPortIsZero(request->u.pm.NATinfo.IntPort) || !mDNSIPPortIsZero(request->u.pm.ReqExt) || ttl) return(mStatus_BadParamErr);
+ }
+ else
+ {
+ if (mDNSIPPortIsZero(request->u.pm.NATinfo.IntPort)) return(mStatus_BadParamErr);
+ if (!(protocol & (kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP))) return(mStatus_BadParamErr);
+ }
+
+ request->u.pm.NATinfo.Protocol = !protocol ? NATOp_AddrRequest : (protocol == kDNSServiceProtocol_UDP) ? NATOp_MapUDP : NATOp_MapTCP;
+ // u.pm.NATinfo.IntPort = already set above
+ request->u.pm.NATinfo.RequestedPort = request->u.pm.ReqExt;
+ request->u.pm.NATinfo.NATLease = ttl;
+ request->u.pm.NATinfo.clientCallback = port_mapping_create_request_callback;
+ request->u.pm.NATinfo.clientContext = request;
+
+ LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) START PID[%d](%s)", request->sd,
+ protocol, mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease,
+ request->process_id, request->pid_name);
+ err = mDNS_StartNATOperation(&mDNSStorage, &request->u.pm.NATinfo);
+ if (err) LogMsg("ERROR: mDNS_StartNATOperation: %d", (int)err);
+ else request->terminate = port_mapping_termination_callback;
+
+ return(err);
+}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - DNSServiceGetAddrInfo
+#endif
+
+mDNSlocal void addrinfo_termination_callback(request_state *request)
+{
+ LogOperation("%3d: DNSServiceGetAddrInfo(%##s) STOP PID[%d](%s)", request->sd, request->u.addrinfo.q4.qname.c,
+ request->process_id, request->pid_name);
+
+ if (request->u.addrinfo.q4.QuestionContext)
+ {
+ mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q4);
+ LogMcastQ(&mDNSStorage, &request->u.addrinfo.q4, request, q_stop);
+ request->u.addrinfo.q4.QuestionContext = mDNSNULL;
+ }
+ if (request->u.addrinfo.q4.qnameOrig)
+ {
+ freeL("QueryTermination", request->u.addrinfo.q4.qnameOrig);
+ request->u.addrinfo.q4.qnameOrig = mDNSNULL;
+ }
+ if (request->u.addrinfo.q42)
+ {
+ if (request->u.addrinfo.q42->QuestionContext)
+ {
+ LogInfo("addrinfo_termination_callback: Stopping q42 %##s", request->u.addrinfo.q42->qname.c);
+ mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q42);
+ LogMcastQ(&mDNSStorage, request->u.addrinfo.q42, request, q_stop);
+ }
+ if (request->u.addrinfo.q42->qnameOrig)
+ {
+ LogInfo("addrinfo_termination_callback: freeing q42 qnameOrig %##s", request->u.addrinfo.q42->qnameOrig->c);
+ freeL("QueryTermination q42", request->u.addrinfo.q42->qnameOrig);
+ request->u.addrinfo.q42->qnameOrig = mDNSNULL;
+ }
+ freeL("addrinfo Q42", request->u.addrinfo.q42);
+ request->u.addrinfo.q42 = mDNSNULL;
+ }
+
+ if (request->u.addrinfo.q6.QuestionContext)
+ {
+ mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q6);
+ LogMcastQ(&mDNSStorage, &request->u.addrinfo.q6, request, q_stop);
+ request->u.addrinfo.q6.QuestionContext = mDNSNULL;
+ }
+ if (request->u.addrinfo.q6.qnameOrig)
+ {
+ freeL("QueryTermination", request->u.addrinfo.q6.qnameOrig);
+ request->u.addrinfo.q6.qnameOrig = mDNSNULL;
+ }
+ if (request->u.addrinfo.q62)
+ {
+ if (request->u.addrinfo.q62->QuestionContext)
+ {
+ LogInfo("addrinfo_termination_callback: Stopping q62 %##s", request->u.addrinfo.q62->qname.c);
+ mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q62);
+ LogMcastQ(&mDNSStorage, request->u.addrinfo.q62, request, q_stop);
+ }
+ if (request->u.addrinfo.q62->qnameOrig)
+ {
+ LogInfo("addrinfo_termination_callback: freeing q62 qnameOrig %##s", request->u.addrinfo.q62->qnameOrig->c);
+ freeL("QueryTermination q62", request->u.addrinfo.q62->qnameOrig);
+ request->u.addrinfo.q62->qnameOrig = mDNSNULL;
+ }
+ freeL("addrinfo Q62", request->u.addrinfo.q62);
+ request->u.addrinfo.q62 = mDNSNULL;
+ }
+#if APPLE_OSX_mDNSResponder
+ {
+ DNSQuestion *v4q, *v6q;
+ v4q = v6q = mDNSNULL;
+ if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4)
+ {
+ // If we are not delivering answers, we may be timing out prematurely.
+ // Note down the current state so that we know to retry when we see a
+ // valid response again.
+ if (request->u.addrinfo.q4.TimeoutQuestion && !request->u.addrinfo.v4ans)
+ {
+ mDNSPlatformUpdateDNSStatus(&mDNSStorage, &request->u.addrinfo.q4);
+ }
+ // If we have a v4 answer and if we timed out prematurely before, provide
+ // a trigger to the upper layer so that it can retry questions if needed.
+ if (request->u.addrinfo.v4ans)
+ v4q = &request->u.addrinfo.q4;
+ }
+ if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6)
+ {
+ if (request->u.addrinfo.q6.TimeoutQuestion && !request->u.addrinfo.v6ans)
+ {
+ mDNSPlatformUpdateDNSStatus(&mDNSStorage, &request->u.addrinfo.q6);
+ }
+ if (request->u.addrinfo.v6ans)
+ v6q = &request->u.addrinfo.q6;
+ }
+ mDNSPlatformTriggerDNSRetry(&mDNSStorage, v4q, v6q);
+ }
+#endif // APPLE_OSX_mDNSResponder
+}
+
+mDNSlocal mStatus handle_addrinfo_request(request_state *request)
+{
+ char hostname[256];
+ domainname d;
+ mStatus err = 0;
+
+ DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
+ mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
+
+ mDNSPlatformMemZero(&request->u.addrinfo, sizeof(request->u.addrinfo));
+ request->u.addrinfo.interface_id = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+ request->u.addrinfo.flags = flags;
+ request->u.addrinfo.protocol = get_uint32(&request->msgptr, request->msgend);
+
+ if (interfaceIndex && !request->u.addrinfo.interface_id) return(mStatus_BadParamErr);
+ if (request->u.addrinfo.protocol > (kDNSServiceProtocol_IPv4|kDNSServiceProtocol_IPv6)) return(mStatus_BadParamErr);
+
+ if (get_string(&request->msgptr, request->msgend, hostname, 256) < 0) return(mStatus_BadParamErr);
+
+ if (!request->msgptr) { LogMsg("%3d: DNSServiceGetAddrInfo(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+ if (!MakeDomainNameFromDNSNameString(&d, hostname))
+ { LogMsg("ERROR: handle_addrinfo_request: bad hostname: %s", hostname); return(mStatus_BadParamErr); }
+
+#if 0
+ if (!AuthorizedDomain(request, &d, AutoBrowseDomains)) return (mStatus_NoError);
+#endif
+
+ if (!request->u.addrinfo.protocol)
+ {
+ flags |= kDNSServiceFlagsSuppressUnusable;
+ request->u.addrinfo.protocol = (kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6);
+ }
+
+ request->u.addrinfo.q4.InterfaceID = request->u.addrinfo.q6.InterfaceID = request->u.addrinfo.interface_id;
+ request->u.addrinfo.q4.flags = request->u.addrinfo.q6.flags = flags;
+ request->u.addrinfo.q4.Target = request->u.addrinfo.q6.Target = zeroAddr;
+ request->u.addrinfo.q4.qname = request->u.addrinfo.q6.qname = d;
+ request->u.addrinfo.q4.qclass = request->u.addrinfo.q6.qclass = kDNSServiceClass_IN;
+ request->u.addrinfo.q4.LongLived = request->u.addrinfo.q6.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0;
+ request->u.addrinfo.q4.ExpectUnique = request->u.addrinfo.q6.ExpectUnique = mDNSfalse;
+ request->u.addrinfo.q4.ForceMCast = request->u.addrinfo.q6.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0;
+ request->u.addrinfo.q4.ReturnIntermed = request->u.addrinfo.q6.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
+ request->u.addrinfo.q4.SuppressUnusable = request->u.addrinfo.q6.SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable ) != 0;
+ request->u.addrinfo.q4.TimeoutQuestion = request->u.addrinfo.q6.TimeoutQuestion = (flags & kDNSServiceFlagsTimeout ) != 0;
+ request->u.addrinfo.q4.WakeOnResolve = request->u.addrinfo.q6.WakeOnResolve = 0;
+ request->u.addrinfo.q4.UseBackgroundTrafficClass = request->u.addrinfo.q6.UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0;
+ if ((flags & kDNSServiceFlagsValidate) != 0)
+ request->u.addrinfo.q4.ValidationRequired = request->u.addrinfo.q6.ValidationRequired = DNSSEC_VALIDATION_SECURE;
+ else if ((flags & kDNSServiceFlagsValidateOptional) != 0)
+ request->u.addrinfo.q4.ValidationRequired = request->u.addrinfo.q6.ValidationRequired = DNSSEC_VALIDATION_SECURE_OPTIONAL;
+ request->u.addrinfo.q4.ValidatingResponse = request->u.addrinfo.q6.ValidatingResponse = 0;
+ request->u.addrinfo.q4.ProxyQuestion = request->u.addrinfo.q6.ProxyQuestion = 0;
+ request->u.addrinfo.q4.qnameOrig = request->u.addrinfo.q6.qnameOrig = mDNSNULL;
+ request->u.addrinfo.q4.AnonInfo = request->u.addrinfo.q6.AnonInfo = mDNSNULL;
+
+ SetQuestionPolicy(&request->u.addrinfo.q4, request);
+ SetQuestionPolicy(&request->u.addrinfo.q6, request);
+
+ request->u.addrinfo.q4.DNSSECAuthInfo = request->u.addrinfo.q6.DNSSECAuthInfo = mDNSNULL;
+ request->u.addrinfo.q4.DAIFreeCallback = request->u.addrinfo.q6.DAIFreeCallback = mDNSNULL;
+
+ //Turn off dnssec validation for local domains
+ if (IsLocalDomain(&d))
+ request->u.addrinfo.q4.ValidationRequired = request->u.addrinfo.q6.ValidationRequired = 0;
+
+ if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6)
+ {
+ request->u.addrinfo.q6.qtype = kDNSServiceType_AAAA;
+ request->u.addrinfo.q6.SearchListIndex = 0;
+ // For DNSSEC questions, append search domains only if kDNSServiceFlagsValidateOptional is set
+ if ((!(request->u.addrinfo.q6.ValidationRequired == DNSSEC_VALIDATION_SECURE)) && (!(request->u.addrinfo.q6.ValidationRequired == DNSSEC_VALIDATION_INSECURE))
+ && hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1))
+ {
+ request->u.addrinfo.q6.AppendSearchDomains = 1;
+ request->u.addrinfo.q6.AppendLocalSearchDomains = 1;
+ }
+ else
+ {
+ request->u.addrinfo.q6.AppendSearchDomains = 0;
+ request->u.addrinfo.q6.AppendLocalSearchDomains = 0;
+ }
+ request->u.addrinfo.q6.RetryWithSearchDomains = (ApplySearchDomainsFirst(&request->u.addrinfo.q6) ? 1 : 0);
+ request->u.addrinfo.q6.QuestionCallback = queryrecord_result_callback;
+ request->u.addrinfo.q6.QuestionContext = request;
+ err = mDNS_StartQuery(&mDNSStorage, &request->u.addrinfo.q6);
+ if (err != mStatus_NoError)
+ {
+ LogMsg("ERROR: mDNS_StartQuery: %d", (int)err);
+ request->u.addrinfo.q6.QuestionContext = mDNSNULL;
+ }
+ #if APPLE_OSX_mDNSResponder
+ err = SendAdditionalQuery(&request->u.addrinfo.q6, request, err);
+ #endif // APPLE_OSX_mDNSResponder
+ if (!err)
+ {
+ request->terminate = addrinfo_termination_callback;
+ LogMcastQ(&mDNSStorage, &request->u.addrinfo.q6, request, q_start);
+ }
+ }
+
+ if (!err && (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4))
+ {
+ request->u.addrinfo.q4.qtype = kDNSServiceType_A;
+ request->u.addrinfo.q4.SearchListIndex = 0;
+
+ // We append search domains only for queries that are a single label. If overriden using cmd line arg
+ // "AlwaysAppendSearchDomains", then we do it for any query which is not fully qualified.
+ // For DNSSEC questions, append search domains only if kDNSServiceFlagsValidateOptional is set.
+
+ if ((!(request->u.addrinfo.q4.ValidationRequired == DNSSEC_VALIDATION_SECURE)) && (!(request->u.addrinfo.q4.ValidationRequired == DNSSEC_VALIDATION_INSECURE))
+ && hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1))
+ {
+ request->u.addrinfo.q4.AppendSearchDomains = 1;
+ request->u.addrinfo.q4.AppendLocalSearchDomains = 1;
+ }
+ else
+ {
+ request->u.addrinfo.q4.AppendSearchDomains = 0;
+ request->u.addrinfo.q4.AppendLocalSearchDomains = 0;
+ }
+ request->u.addrinfo.q4.RetryWithSearchDomains = (ApplySearchDomainsFirst(&request->u.addrinfo.q4) ? 1 : 0);
+ request->u.addrinfo.q4.QuestionCallback = queryrecord_result_callback;
+ request->u.addrinfo.q4.QuestionContext = request;
+ err = mDNS_StartQuery(&mDNSStorage, &request->u.addrinfo.q4);
+ if (err != mStatus_NoError)
+ {
+ LogMsg("ERROR: mDNS_StartQuery: %d", (int)err);
+ request->u.addrinfo.q4.QuestionContext = mDNSNULL;
+ if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6)
+ {
+ // If we started a query for IPv6, we need to cancel it
+ mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q6);
+ request->u.addrinfo.q6.QuestionContext = mDNSNULL;
+ }
+ }
+ #if APPLE_OSX_mDNSResponder
+ err = SendAdditionalQuery(&request->u.addrinfo.q4, request, err);
+ #endif // APPLE_OSX_mDNSResponder
+ if (!err)
+ {
+ request->terminate = addrinfo_termination_callback;
+ LogMcastQ(&mDNSStorage, &request->u.addrinfo.q4, request, q_start);
+ }
+ }
+
+ LogOperation("%3d: DNSServiceGetAddrInfo(%X, %d, %d, %##s) START PID[%d](%s)", request->sd, flags, interfaceIndex,
+ request->u.addrinfo.protocol, d.c, request->process_id, request->pid_name);
+ return(err);
+}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Main Request Handler etc.
+#endif
+
+mDNSlocal request_state *NewRequest(void)
+{
+ request_state **p = &all_requests;
+ while (*p)
+ p=&(*p)->next;
+ *p = mallocL("request_state", sizeof(request_state));
+ if (!*p)
+ FatalError("ERROR: malloc");
+ mDNSPlatformMemZero(*p, sizeof(request_state));
+ return(*p);
+}
+
+// read_msg may be called any time when the transfer state (req->ts) is t_morecoming.
+// if there is no data on the socket, the socket will be closed and t_terminated will be returned
+mDNSlocal void read_msg(request_state *req)
+{
+ if (req->ts == t_terminated || req->ts == t_error)
+ { LogMsg("%3d: ERROR: read_msg called with transfer state terminated or error", req->sd); req->ts = t_error; return; }
+
+ if (req->ts == t_complete) // this must be death or something is wrong
+ {
+ char buf[4]; // dummy for death notification
+ int nread = udsSupportReadFD(req->sd, buf, 4, 0, req->platform_data);
+ if (!nread) { req->ts = t_terminated; return; }
+ if (nread < 0) goto rerror;
+ LogMsg("%3d: ERROR: read data from a completed request", req->sd);
+ req->ts = t_error;
+ return;
+ }
+
+ if (req->ts != t_morecoming)
+ { LogMsg("%3d: ERROR: read_msg called with invalid transfer state (%d)", req->sd, req->ts); req->ts = t_error; return; }
+
+ if (req->hdr_bytes < sizeof(ipc_msg_hdr))
+ {
+ mDNSu32 nleft = sizeof(ipc_msg_hdr) - req->hdr_bytes;
+ int nread = udsSupportReadFD(req->sd, (char *)&req->hdr + req->hdr_bytes, nleft, 0, req->platform_data);
+ if (nread == 0) { req->ts = t_terminated; return; }
+ if (nread < 0) goto rerror;
+ req->hdr_bytes += nread;
+ if (req->hdr_bytes > sizeof(ipc_msg_hdr))
+ { LogMsg("%3d: ERROR: read_msg - read too many header bytes", req->sd); req->ts = t_error; return; }
+
+ // only read data if header is complete
+ if (req->hdr_bytes == sizeof(ipc_msg_hdr))
+ {
+ ConvertHeaderBytes(&req->hdr);
+ if (req->hdr.version != VERSION)
+ { LogMsg("%3d: ERROR: client version 0x%08X daemon version 0x%08X", req->sd, req->hdr.version, VERSION); req->ts = t_error; return; }
+
+ // Largest conceivable single request is a DNSServiceRegisterRecord() or DNSServiceAddRecord()
+ // with 64kB of rdata. Adding 1009 byte for a maximal domain name, plus a safety margin
+ // for other overhead, this means any message above 70kB is definitely bogus.
+ if (req->hdr.datalen > 70000)
+ { LogMsg("%3d: ERROR: read_msg: hdr.datalen %u (0x%X) > 70000", req->sd, req->hdr.datalen, req->hdr.datalen); req->ts = t_error; return; }
+ req->msgbuf = mallocL("request_state msgbuf", req->hdr.datalen + MSG_PAD_BYTES);
+ if (!req->msgbuf) { my_perror("ERROR: malloc"); req->ts = t_error; return; }
+ req->msgptr = req->msgbuf;
+ req->msgend = req->msgbuf + req->hdr.datalen;
+ mDNSPlatformMemZero(req->msgbuf, req->hdr.datalen + MSG_PAD_BYTES);
+ }
+ }
+
+ // If our header is complete, but we're still needing more body data, then try to read it now
+ // Note: For cancel_request req->hdr.datalen == 0, but there's no error return socket for cancel_request
+ // Any time we need to get the error return socket we know we'll have at least one data byte
+ // (even if only the one-byte empty C string placeholder for the old ctrl_path parameter)
+ if (req->hdr_bytes == sizeof(ipc_msg_hdr) && req->data_bytes < req->hdr.datalen)
+ {
+ mDNSu32 nleft = req->hdr.datalen - req->data_bytes;
+ int nread;
+#if !defined(_WIN32)
+ struct iovec vec = { req->msgbuf + req->data_bytes, nleft }; // Tell recvmsg where we want the bytes put
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ char cbuf[CMSG_SPACE(4 * sizeof(dnssd_sock_t))];
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &vec;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cbuf;
+ msg.msg_controllen = sizeof(cbuf);
+ msg.msg_flags = 0;
+ nread = recvmsg(req->sd, &msg, 0);
+#else
+ nread = udsSupportReadFD(req->sd, (char *)req->msgbuf + req->data_bytes, nleft, 0, req->platform_data);
+#endif
+ if (nread == 0) { req->ts = t_terminated; return; }
+ if (nread < 0) goto rerror;
+ req->data_bytes += nread;
+ if (req->data_bytes > req->hdr.datalen)
+ { LogMsg("%3d: ERROR: read_msg - read too many data bytes", req->sd); req->ts = t_error; return; }
+#if !defined(_WIN32)
+ cmsg = CMSG_FIRSTHDR(&msg);
+#if DEBUG_64BIT_SCM_RIGHTS
+ LogMsg("%3d: Expecting %d %d %d %d", req->sd, sizeof(cbuf), sizeof(cbuf), SOL_SOCKET, SCM_RIGHTS);
+ LogMsg("%3d: Got %d %d %d %d", req->sd, msg.msg_controllen, cmsg->cmsg_len, cmsg->cmsg_level, cmsg->cmsg_type);
+#endif // DEBUG_64BIT_SCM_RIGHTS
+ if (msg.msg_controllen != 0 &&
+ cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS)
+ {
+#if APPLE_OSX_mDNSResponder
+ // Strictly speaking BPF_fd belongs solely in the platform support layer, but because
+ // of privilege separation on Mac OS X we need to get BPF_fd from mDNSResponderHelper,
+ // and it's convenient to repurpose the existing fd-passing code here for that task
+ if (req->hdr.op == send_bpf)
+ {
+ dnssd_sock_t x = *(dnssd_sock_t *)CMSG_DATA(cmsg);
+ LogOperation("%3d: Got len %d, BPF %d", req->sd, cmsg->cmsg_len, x);
+ mDNSPlatformReceiveBPF_fd(&mDNSStorage, x);
+ }
+ else
+#endif // APPLE_OSX_mDNSResponder
+ req->errsd = *(dnssd_sock_t *)CMSG_DATA(cmsg);
+#if DEBUG_64BIT_SCM_RIGHTS
+ LogMsg("%3d: read req->errsd %d", req->sd, req->errsd);
+#endif // DEBUG_64BIT_SCM_RIGHTS
+ if (req->data_bytes < req->hdr.datalen)
+ {
+ LogMsg("%3d: Client(PID [%d](%s)) sent error socket %d via SCM_RIGHTS with req->data_bytes %d < req->hdr.datalen %d",
+ req->sd, req->process_id, req->pid_name, req->errsd, req->data_bytes, req->hdr.datalen);
+ req->ts = t_error;
+ return;
+ }
+ }
+#endif
+ }
+
+ // If our header and data are both complete, see if we need to make our separate error return socket
+ if (req->hdr_bytes == sizeof(ipc_msg_hdr) && req->data_bytes == req->hdr.datalen)
+ {
+ if (req->terminate && req->hdr.op != cancel_request)
+ {
+ dnssd_sockaddr_t cliaddr;
+#if defined(USE_TCP_LOOPBACK)
+ mDNSOpaque16 port;
+ u_long opt = 1;
+ port.b[0] = req->msgptr[0];
+ port.b[1] = req->msgptr[1];
+ req->msgptr += 2;
+ cliaddr.sin_family = AF_INET;
+ cliaddr.sin_port = port.NotAnInteger;
+ cliaddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR);
+#else
+ char ctrl_path[MAX_CTLPATH];
+ get_string(&req->msgptr, req->msgend, ctrl_path, MAX_CTLPATH); // path is first element in message buffer
+ mDNSPlatformMemZero(&cliaddr, sizeof(cliaddr));
+ cliaddr.sun_family = AF_LOCAL;
+ mDNSPlatformStrCopy(cliaddr.sun_path, ctrl_path);
+ // If the error return path UDS name is empty string, that tells us
+ // that this is a new version of the library that's going to pass us
+ // the error return path socket via sendmsg/recvmsg
+ if (ctrl_path[0] == 0)
+ {
+ if (req->errsd == req->sd)
+ { LogMsg("%3d: read_msg: ERROR failed to get errsd via SCM_RIGHTS", req->sd); req->ts = t_error; return; }
+ goto got_errfd;
+ }
+#endif
+
+ req->errsd = socket(AF_DNSSD, SOCK_STREAM, 0);
+ if (!dnssd_SocketValid(req->errsd))
+ {
+ my_throttled_perror("ERROR: socket");
+ req->ts = t_error;
+ return;
+ }
+
+ if (connect(req->errsd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)) < 0)
+ {
+#if !defined(USE_TCP_LOOPBACK)
+ struct stat sb;
+ LogMsg("%3d: read_msg: Couldn't connect to error return path socket “%s” errno %d (%s)",
+ req->sd, cliaddr.sun_path, dnssd_errno, dnssd_strerror(dnssd_errno));
+ if (stat(cliaddr.sun_path, &sb) < 0)
+ LogMsg("%3d: read_msg: stat failed “%s” errno %d (%s)", req->sd, cliaddr.sun_path, dnssd_errno, dnssd_strerror(dnssd_errno));
+ else
+ LogMsg("%3d: read_msg: file “%s” mode %o (octal) uid %d gid %d", req->sd, cliaddr.sun_path, sb.st_mode, sb.st_uid, sb.st_gid);
+#endif
+ req->ts = t_error;
+ return;
+ }
+
+#if !defined(USE_TCP_LOOPBACK)
+got_errfd:
+#endif
+ LogOperation("%3d: Error socket %d created %08X %08X", req->sd, req->errsd, req->hdr.client_context.u32[1], req->hdr.client_context.u32[0]);
+#if defined(_WIN32)
+ if (ioctlsocket(req->errsd, FIONBIO, &opt) != 0)
+#else
+ if (fcntl(req->errsd, F_SETFL, fcntl(req->errsd, F_GETFL, 0) | O_NONBLOCK) != 0)
+#endif
+ {
+ LogMsg("%3d: ERROR: could not set control socket to non-blocking mode errno %d (%s)",
+ req->sd, dnssd_errno, dnssd_strerror(dnssd_errno));
+ req->ts = t_error;
+ return;
+ }
+ }
+
+ req->ts = t_complete;
+ }
+
+ return;
+
+rerror:
+ if (dnssd_errno == dnssd_EWOULDBLOCK || dnssd_errno == dnssd_EINTR) return;
+ LogMsg("%3d: ERROR: read_msg errno %d (%s)", req->sd, dnssd_errno, dnssd_strerror(dnssd_errno));
+ req->ts = t_error;
+}
+
+#define RecordOrientedOp(X) \
+ ((X) == reg_record_request || (X) == add_record_request || (X) == update_record_request || (X) == remove_record_request)
+
+// The lightweight operations are the ones that don't need a dedicated request_state structure allocated for them
+#define LightweightOp(X) (RecordOrientedOp(X) || (X) == cancel_request)
+
+mDNSlocal void request_callback(int fd, short filter, void *info)
+{
+ mStatus err = 0;
+ request_state *req = info;
+ mDNSs32 min_size = sizeof(DNSServiceFlags);
+ (void)fd; // Unused
+ (void)filter; // Unused
+
+ for (;;)
+ {
+ read_msg(req);
+ if (req->ts == t_morecoming)
+ return;
+ if (req->ts == t_terminated || req->ts == t_error)
+ {
+ AbortUnlinkAndFree(req);
+ return;
+ }
+ if (req->ts != t_complete)
+ {
+ LogMsg("request_callback: req->ts %d != t_complete PID[%d][%s]", req->ts, req->process_id, req->pid_name);
+ AbortUnlinkAndFree(req);
+ return;
+ }
+ if (req->hdr.version != VERSION)
+ {
+ LogMsg("request_callback: ERROR: client IPC version %d incompatible with daemon IPC version %d PID[%d][%s]",
+ req->hdr.version, VERSION, req->process_id, req->pid_name);
+ AbortUnlinkAndFree(req);
+ return;
+ }
+
+ switch(req->hdr.op) // Interface + other data
+ {
+ case connection_request: min_size = 0; break;
+ case connection_delegate_request: min_size = 4; /* pid */ break;
+ case reg_service_request: min_size += sizeof(mDNSu32) + 4 /* name, type, domain, host */ + 4 /* port, textlen */; break;
+ case add_record_request: min_size += 4 /* type, rdlen */ + 4 /* ttl */; break;
+ case update_record_request: min_size += 2 /* rdlen */ + 4 /* ttl */; break;
+ case remove_record_request: break;
+ case browse_request: min_size += sizeof(mDNSu32) + 2 /* type, domain */; break;
+ case resolve_request: min_size += sizeof(mDNSu32) + 3 /* type, type, domain */; break;
+ case query_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 4 /* type, class*/; break;
+ case enumeration_request: min_size += sizeof(mDNSu32); break;
+ case reg_record_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */ + 4 /* ttl */; break;
+ case reconfirm_record_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */; break;
+ case setdomain_request: min_size += 1 /* domain */; break;
+ case getproperty_request: min_size = 2; break;
+ case getpid_request: min_size = 2; break;
+ case port_mapping_request: min_size += sizeof(mDNSu32) + 4 /* udp/tcp */ + 4 /* int/ext port */ + 4 /* ttl */; break;
+ case addrinfo_request: min_size += sizeof(mDNSu32) + 4 /* v4/v6 */ + 1 /* hostname */; break;
+ case send_bpf: // Same as cancel_request below
+ case cancel_request: min_size = 0; break;
+ case release_request: min_size += sizeof(mDNSu32) + 3 /* type, type, domain */; break;
+ default: LogMsg("request_callback: ERROR: validate_message - unsupported req type: %d PID[%d][%s]",
+ req->hdr.op, req->process_id, req->pid_name);
+ min_size = -1; break;
+ }
+
+ if ((mDNSs32)req->data_bytes < min_size)
+ {
+ LogMsg("request_callback: Invalid message %d bytes; min for %d is %d PID[%d][%s]",
+ req->data_bytes, req->hdr.op, min_size, req->process_id, req->pid_name);
+ AbortUnlinkAndFree(req);
+ return;
+ }
+ if (LightweightOp(req->hdr.op) && !req->terminate)
+ {
+ LogMsg("request_callback: Reg/Add/Update/Remove %d require existing connection PID[%d][%s]",
+ req->hdr.op, req->process_id, req->pid_name);
+ AbortUnlinkAndFree(req);
+ return;
+ }
+
+ // check if client wants silent operation
+ if (req->hdr.ipc_flags & IPC_FLAGS_NOREPLY) req->no_reply = 1;
+
+ // If req->terminate is already set, this means this operation is sharing an existing connection
+ if (req->terminate && !LightweightOp(req->hdr.op))
+ {
+ request_state *newreq = NewRequest();
+ newreq->primary = req;
+ newreq->sd = req->sd;
+ newreq->errsd = req->errsd;
+ newreq->uid = req->uid;
+ newreq->hdr = req->hdr;
+ newreq->msgbuf = req->msgbuf;
+ newreq->msgptr = req->msgptr;
+ newreq->msgend = req->msgend;
+ // if the parent request is a delegate connection, copy the
+ // relevant bits
+ if (req->validUUID)
+ {
+ int i;
+ newreq->validUUID = mDNStrue;
+ for (i = 0; i < UUID_SIZE; i++)
+ {
+ newreq->uuid[i] = req->uuid[i];
+ }
+ }
+ else
+ {
+ if (req->process_id)
+ {
+ newreq->process_id = req->process_id;
+ }
+ else
+ {
+ set_peer_pid(newreq);
+ }
+ }
+ req = newreq;
+ }
+
+ // If we're shutting down, don't allow new client requests
+ // We do allow "cancel" and "getproperty" during shutdown
+ if (mDNSStorage.ShutdownTime && req->hdr.op != cancel_request && req->hdr.op != getproperty_request)
+ {
+ err = mStatus_ServiceNotRunning;
+ }
+ else
+ {
+ switch(req->hdr.op)
+ {
+ // These are all operations that have their own first-class request_state object
+ case connection_request:
+ LogOperation("%3d: DNSServiceCreateConnection START PID[%d](%s)",
+ req->sd, req->process_id, req->pid_name);
+ req->terminate = connection_termination;
+ break;
+ case connection_delegate_request:
+ LogOperation("%3d: DNSServiceCreateDelegateConnection START PID[%d](%s)",
+ req->sd, req->process_id, req->pid_name);
+ req->terminate = connection_termination;
+ handle_connection_delegate_request(req);
+ break;
+ case resolve_request: err = handle_resolve_request (req); break;
+ case query_request: err = handle_queryrecord_request (req); break;
+ case browse_request: err = handle_browse_request (req); break;
+ case reg_service_request: err = handle_regservice_request (req); break;
+ case enumeration_request: err = handle_enum_request (req); break;
+ case reconfirm_record_request: err = handle_reconfirm_request (req); break;
+ case setdomain_request: err = handle_setdomain_request (req); break;
+ case getproperty_request: handle_getproperty_request (req); break;
+ case getpid_request: handle_getpid_request (req); break;
+ case port_mapping_request: err = handle_port_mapping_request(req); break;
+ case addrinfo_request: err = handle_addrinfo_request (req); break;
+ case send_bpf: /* Do nothing for send_bpf */ break;
+
+ // These are all operations that work with an existing request_state object
+ case reg_record_request: err = handle_regrecord_request (req); break;
+ case add_record_request: err = handle_add_request (req); break;
+ case update_record_request: err = handle_update_request (req); break;
+ case remove_record_request: err = handle_removerecord_request(req); break;
+ case cancel_request: handle_cancel_request (req); break;
+ case release_request: err = handle_release_request (req); break;
+ default: LogMsg("request_callback: %3d:ERROR: Unsupported UDS req:%d PID[%d][%s]",
+ req->sd, req->hdr.op, req->process_id, req->pid_name); break;
+ }
+ }
+ // req->msgbuf may be NULL, e.g. for connection_request or remove_record_request
+ if (req->msgbuf) freeL("request_state msgbuf", req->msgbuf);
+
+ // There's no return data for a cancel request (DNSServiceRefDeallocate returns no result)
+ // For a DNSServiceGetProperty call, the handler already generated the response, so no need to do it again here
+ if (req->hdr.op != cancel_request && req->hdr.op != getproperty_request && req->hdr.op != send_bpf && req->hdr.op != getpid_request)
+ {
+ const mStatus err_netorder = dnssd_htonl(err);
+ send_all(req->errsd, (const char *)&err_netorder, sizeof(err_netorder));
+ if (req->errsd != req->sd)
+ {
+ LogOperation("%3d: Error socket %d closed %08X %08X (%d)",
+ req->sd, req->errsd, req->hdr.client_context.u32[1], req->hdr.client_context.u32[0], err);
+ dnssd_close(req->errsd);
+ req->errsd = req->sd;
+ // Also need to reset the parent's errsd, if this is a subordinate operation
+ if (req->primary) req->primary->errsd = req->primary->sd;
+ }
+ }
+
+ // Reset ready to accept the next req on this pipe
+ if (req->primary) req = req->primary;
+ req->ts = t_morecoming;
+ req->hdr_bytes = 0;
+ req->data_bytes = 0;
+ req->msgbuf = mDNSNULL;
+ req->msgptr = mDNSNULL;
+ req->msgend = 0;
+ }
+}
+
+mDNSlocal void connect_callback(int fd, short filter, void *info)
+{
+ dnssd_sockaddr_t cliaddr;
+ dnssd_socklen_t len = (dnssd_socklen_t) sizeof(cliaddr);
+ dnssd_sock_t sd = accept(fd, (struct sockaddr*) &cliaddr, &len);
+#if defined(SO_NOSIGPIPE) || defined(_WIN32)
+ unsigned long optval = 1;
+#endif
+
+ (void)filter; // Unused
+ (void)info; // Unused
+
+ if (!dnssd_SocketValid(sd))
+ {
+ if (dnssd_errno != dnssd_EWOULDBLOCK)
+ my_throttled_perror("ERROR: accept");
+ return;
+ }
+
+#ifdef SO_NOSIGPIPE
+ // Some environments (e.g. OS X) support turning off SIGPIPE for a socket
+ if (setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0)
+ LogMsg("%3d: WARNING: setsockopt - SO_NOSIGPIPE %d (%s)", sd, dnssd_errno, dnssd_strerror(dnssd_errno));
+#endif
+
+#if defined(_WIN32)
+ if (ioctlsocket(sd, FIONBIO, &optval) != 0)
+#else
+ if (fcntl(sd, F_SETFL, fcntl(sd, F_GETFL, 0) | O_NONBLOCK) != 0)
+#endif
+ {
+ my_perror("ERROR: fcntl(sd, F_SETFL, O_NONBLOCK) - aborting client");
+ dnssd_close(sd);
+ return;
+ }
+ else
+ {
+ request_state *request = NewRequest();
+ request->ts = t_morecoming;
+ request->sd = sd;
+ request->errsd = sd;
+ set_peer_pid(request);
+#if APPLE_OSX_mDNSResponder
+ struct xucred x;
+ socklen_t xucredlen = sizeof(x);
+ if (getsockopt(sd, 0, LOCAL_PEERCRED, &x, &xucredlen) >= 0 && x.cr_version == XUCRED_VERSION) request->uid = x.cr_uid;
+ else my_perror("ERROR: getsockopt, LOCAL_PEERCRED");
+ debugf("LOCAL_PEERCRED %d %u %u %d", xucredlen, x.cr_version, x.cr_uid, x.cr_ngroups);
+#endif // APPLE_OSX_mDNSResponder
+ LogOperation("%3d: Adding FD for uid %u", request->sd, request->uid);
+ udsSupportAddFDToEventLoop(sd, request_callback, request, &request->platform_data);
+ }
+}
+
+mDNSlocal mDNSBool uds_socket_setup(dnssd_sock_t skt)
+{
+#if defined(SO_NP_EXTENSIONS)
+ struct so_np_extensions sonpx;
+ socklen_t optlen = sizeof(struct so_np_extensions);
+ sonpx.npx_flags = SONPX_SETOPTSHUT;
+ sonpx.npx_mask = SONPX_SETOPTSHUT;
+ if (setsockopt(skt, SOL_SOCKET, SO_NP_EXTENSIONS, &sonpx, optlen) < 0)
+ my_perror("WARNING: could not set sockopt - SO_NP_EXTENSIONS");
+#endif
+#if defined(_WIN32)
+ // SEH: do we even need to do this on windows?
+ // This socket will be given to WSAEventSelect which will automatically set it to non-blocking
+ u_long opt = 1;
+ if (ioctlsocket(skt, FIONBIO, &opt) != 0)
+#else
+ if (fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK) != 0)
+#endif
+ {
+ my_perror("ERROR: could not set listen socket to non-blocking mode");
+ return mDNSfalse;
+ }
+
+ if (listen(skt, LISTENQ) != 0)
+ {
+ my_perror("ERROR: could not listen on listen socket");
+ return mDNSfalse;
+ }
+
+ if (mStatus_NoError != udsSupportAddFDToEventLoop(skt, connect_callback, (void *) NULL, (void **) NULL))
+ {
+ my_perror("ERROR: could not add listen socket to event loop");
+ return mDNSfalse;
+ }
+ else
+ {
+ LogMsg("%3d: Listening for incoming Unix Domain Socket client requests", skt);
+ mDNSStorage.uds_listener_skt = skt;
+ }
+ return mDNStrue;
+}
+
+mDNSexport int udsserver_init(dnssd_sock_t skts[], mDNSu32 count)
+{
+ dnssd_sockaddr_t laddr;
+ int ret;
+ mDNSu32 i = 0;
+
+ LogInfo("udsserver_init: %d %d", _DNS_SD_H, mDNSStorage.mDNS_plat);
+
+ // If a particular platform wants to opt out of having a PID file, define PID_FILE to be ""
+ if (PID_FILE[0])
+ {
+ FILE *fp = fopen(PID_FILE, "w");
+ if (fp != NULL)
+ {
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+ }
+
+ if (skts)
+ {
+ for (i = 0; i < count; i++)
+ if (dnssd_SocketValid(skts[i]) && !uds_socket_setup(skts[i]))
+ goto error;
+ }
+ else
+ {
+ listenfd = socket(AF_DNSSD, SOCK_STREAM, 0);
+ if (!dnssd_SocketValid(listenfd))
+ {
+ my_perror("ERROR: socket(AF_DNSSD, SOCK_STREAM, 0); failed");
+ goto error;
+ }
+
+ mDNSPlatformMemZero(&laddr, sizeof(laddr));
+
+ #if defined(USE_TCP_LOOPBACK)
+ {
+ laddr.sin_family = AF_INET;
+ laddr.sin_port = htons(MDNS_TCP_SERVERPORT);
+ laddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR);
+ ret = bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr));
+ if (ret < 0)
+ {
+ my_perror("ERROR: bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); failed");
+ goto error;
+ }
+ }
+ #else
+ {
+ mode_t mask = umask(0);
+ unlink(MDNS_UDS_SERVERPATH); // OK if this fails
+ laddr.sun_family = AF_LOCAL;
+ #ifndef NOT_HAVE_SA_LEN
+ // According to Stevens (section 3.2), there is no portable way to
+ // determine whether sa_len is defined on a particular platform.
+ laddr.sun_len = sizeof(struct sockaddr_un);
+ #endif
+ if (strlen(MDNS_UDS_SERVERPATH) >= sizeof(laddr.sun_path))
+ {
+ LogMsg("ERROR: MDNS_UDS_SERVERPATH must be < %d characters", (int)sizeof(laddr.sun_path));
+ goto error;
+ }
+ mDNSPlatformStrCopy(laddr.sun_path, MDNS_UDS_SERVERPATH);
+ ret = bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr));
+ umask(mask);
+ if (ret < 0)
+ {
+ my_perror("ERROR: bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); failed");
+ goto error;
+ }
+ }
+ #endif
+
+ if (!uds_socket_setup(listenfd)) goto error;
+ }
+
+#if !defined(PLATFORM_NO_RLIMIT)
+ {
+ // Set maximum number of open file descriptors
+ #define MIN_OPENFILES 10240
+ struct rlimit maxfds, newfds;
+
+ // Due to bugs in OS X (<rdar://problem/2941095>, <rdar://problem/3342704>, <rdar://problem/3839173>)
+ // you have to get and set rlimits once before getrlimit will return sensible values
+ if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; }
+ if (setrlimit(RLIMIT_NOFILE, &maxfds) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit");
+
+ if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; }
+ newfds.rlim_max = (maxfds.rlim_max > MIN_OPENFILES) ? maxfds.rlim_max : MIN_OPENFILES;
+ newfds.rlim_cur = (maxfds.rlim_cur > MIN_OPENFILES) ? maxfds.rlim_cur : MIN_OPENFILES;
+ if (newfds.rlim_max != maxfds.rlim_max || newfds.rlim_cur != maxfds.rlim_cur)
+ if (setrlimit(RLIMIT_NOFILE, &newfds) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit");
+
+ if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; }
+ debugf("maxfds.rlim_max %d", (long)maxfds.rlim_max);
+ debugf("maxfds.rlim_cur %d", (long)maxfds.rlim_cur);
+ }
+#endif
+
+ // We start a "LocalOnly" query looking for Automatic Browse Domain records.
+ // When Domain Enumeration in uDNS.c finds an "lb" record from the network, its "FoundDomain" routine
+ // creates a "LocalOnly" record, which results in our AutomaticBrowseDomainChange callback being invoked
+ mDNS_GetDomains(&mDNSStorage, &mDNSStorage.AutomaticBrowseDomainQ, mDNS_DomainTypeBrowseAutomatic,
+ mDNSNULL, mDNSInterface_LocalOnly, AutomaticBrowseDomainChange, mDNSNULL);
+
+ // Add "local" as recommended registration domain ("dns-sd -E"), recommended browsing domain ("dns-sd -F"), and automatic browsing domain
+ RegisterLocalOnlyDomainEnumPTR(&mDNSStorage, &localdomain, mDNS_DomainTypeRegistration);
+ RegisterLocalOnlyDomainEnumPTR(&mDNSStorage, &localdomain, mDNS_DomainTypeBrowse);
+ AddAutoBrowseDomain(0, &localdomain);
+
+ udsserver_handle_configchange(&mDNSStorage);
+ return 0;
+
+error:
+
+ my_perror("ERROR: udsserver_init");
+ return -1;
+}
+
+mDNSexport int udsserver_exit(void)
+{
+ // Cancel all outstanding client requests
+ while (all_requests) AbortUnlinkAndFree(all_requests);
+
+ // Clean up any special mDNSInterface_LocalOnly records we created, both the entries for "local" we
+ // created in udsserver_init, and others we created as a result of reading local configuration data
+ while (LocalDomainEnumRecords)
+ {
+ ARListElem *rem = LocalDomainEnumRecords;
+ LocalDomainEnumRecords = LocalDomainEnumRecords->next;
+ mDNS_Deregister(&mDNSStorage, &rem->ar);
+ }
+
+ // If the launching environment created no listening socket,
+ // that means we created it ourselves, so we should clean it up on exit
+ if (dnssd_SocketValid(listenfd))
+ {
+ dnssd_close(listenfd);
+#if !defined(USE_TCP_LOOPBACK)
+ // Currently, we're unable to remove /var/run/mdnsd because we've changed to userid "nobody"
+ // to give up unnecessary privilege, but we need to be root to remove this Unix Domain Socket.
+ // It would be nice if we could find a solution to this problem
+ if (unlink(MDNS_UDS_SERVERPATH))
+ debugf("Unable to remove %s", MDNS_UDS_SERVERPATH);
+#endif
+ }
+
+ if (PID_FILE[0]) unlink(PID_FILE);
+
+ return 0;
+}
+
+mDNSlocal void LogClientInfo(mDNS *const m, request_state *req)
+{
+ char prefix[16];
+ if (req->primary)
+ mDNS_snprintf(prefix, sizeof(prefix), " -> ");
+ else
+ mDNS_snprintf(prefix, sizeof(prefix), "%3d:", req->sd);
+
+ if (!req->terminate)
+ LogMsgNoIdent("%s No operation yet on this socket", prefix);
+ else if (req->terminate == connection_termination)
+ {
+ int num_records = 0, num_ops = 0;
+ const registered_record_entry *p;
+ request_state *r;
+ for (p = req->u.reg_recs; p; p=p->next) num_records++;
+ for (r = req->next; r; r=r->next) if (r->primary == req) num_ops++;
+ LogMsgNoIdent("%s DNSServiceCreateConnection: %d registered record%s, %d kDNSServiceFlagsShareConnection operation%s PID[%d](%s)",
+ prefix, num_records, num_records != 1 ? "s" : "", num_ops, num_ops != 1 ? "s" : "",
+ req->process_id, req->pid_name);
+ for (p = req->u.reg_recs; p; p=p->next)
+ LogMsgNoIdent(" -> DNSServiceRegisterRecord %3d %s PID[%d](%s)", p->key, ARDisplayString(m, p->rr),
+ req->process_id, req->pid_name);
+ for (r = req->next; r; r=r->next) if (r->primary == req) LogClientInfo(m, r);
+ }
+ else if (req->terminate == regservice_termination_callback)
+ {
+ service_instance *ptr;
+ char anonstr[256];
+ for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next)
+ LogMsgNoIdent("%s DNSServiceRegister %##s%s %u/%u PID[%d](%s)",
+ (ptr == req->u.servicereg.instances) ? prefix : " ", ptr->srs.RR_SRV.resrec.name->c,
+ AnonDataToString(ptr->srs.AnonData, 0, anonstr, sizeof(anonstr)), mDNSVal16(req->u.servicereg.port),
+ SRS_PORT(&ptr->srs), req->process_id, req->pid_name);
+ }
+ else if (req->terminate == browse_termination_callback)
+ {
+ browser_t *blist;
+ char anonstr[256];
+ for (blist = req->u.browser.browsers; blist; blist = blist->next)
+ LogMsgNoIdent("%s DNSServiceBrowse %##s%s PID[%d](%s)",
+ (blist == req->u.browser.browsers) ? prefix : " ",blist->q.qname.c,
+ AnonDataToString(req->u.browser.AnonData, 0, anonstr, sizeof(anonstr)), req->process_id, req->pid_name);
+ }
+ else if (req->terminate == resolve_termination_callback)
+ LogMsgNoIdent("%s DNSServiceResolve %##s PID[%d](%s)",
+ prefix, req->u.resolve.qsrv.qname.c, req->process_id, req->pid_name);
+ else if (req->terminate == queryrecord_termination_callback)
+ LogMsgNoIdent("%s DNSServiceQueryRecord %##s (%s) PID[%d](%s)",
+ prefix, req->u.queryrecord.q.qname.c, DNSTypeName(req->u.queryrecord.q.qtype), req->process_id, req->pid_name);
+ else if (req->terminate == enum_termination_callback)
+ LogMsgNoIdent("%s DNSServiceEnumerateDomains %##s PID[%d](%s)", prefix, req->u.enumeration.q_all.qname.c,
+ req->process_id, req->pid_name);
+ else if (req->terminate == port_mapping_termination_callback)
+ LogMsgNoIdent("%s DNSServiceNATPortMapping %s%s Int %5d Req %5d Ext %.4a:%5d Req TTL %5d Granted TTL %5d PID[%d](%s)",
+ prefix,
+ req->u.pm.NATinfo.Protocol & NATOp_MapTCP ? "TCP" : " ",
+ req->u.pm.NATinfo.Protocol & NATOp_MapUDP ? "UDP" : " ",
+ mDNSVal16(req->u.pm.NATinfo.IntPort),
+ mDNSVal16(req->u.pm.ReqExt),
+ &req->u.pm.NATinfo.ExternalAddress,
+ mDNSVal16(req->u.pm.NATinfo.ExternalPort),
+ req->u.pm.NATinfo.NATLease,
+ req->u.pm.NATinfo.Lifetime,
+ req->process_id, req->pid_name);
+ else if (req->terminate == addrinfo_termination_callback)
+ LogMsgNoIdent("%s DNSServiceGetAddrInfo %s%s %##s PID[%d](%s)", prefix,
+ req->u.addrinfo.protocol & kDNSServiceProtocol_IPv4 ? "v4" : " ",
+ req->u.addrinfo.protocol & kDNSServiceProtocol_IPv6 ? "v6" : " ",
+ req->u.addrinfo.q4.qname.c, req->process_id, req->pid_name);
+ else
+ LogMsgNoIdent("%s Unrecognized operation %p", prefix, req->terminate);
+}
+
+mDNSlocal void GetMcastClients(request_state *req)
+{
+ if (req->terminate == connection_termination)
+ {
+ int num_records = 0, num_ops = 0;
+ const registered_record_entry *p;
+ request_state *r;
+ for (p = req->u.reg_recs; p; p=p->next)
+ num_records++;
+ for (r = req->next; r; r=r->next)
+ if (r->primary == req)
+ num_ops++;
+ for (p = req->u.reg_recs; p; p=p->next)
+ {
+ if (!AuthRecord_uDNS(p->rr))
+ n_mrecords++;
+ }
+ for (r = req->next; r; r=r->next)
+ if (r->primary == req)
+ GetMcastClients(r);
+ }
+ else if (req->terminate == regservice_termination_callback)
+ {
+ service_instance *ptr;
+ for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next)
+ {
+ if (!AuthRecord_uDNS(&ptr->srs.RR_SRV))
+ n_mrecords++;
+ }
+ }
+ else if (req->terminate == browse_termination_callback)
+ {
+ browser_t *blist;
+ for (blist = req->u.browser.browsers; blist; blist = blist->next)
+ {
+ if (mDNSOpaque16IsZero(blist->q.TargetQID))
+ n_mquests++;
+ }
+ }
+ else if (req->terminate == resolve_termination_callback)
+ {
+ if ((mDNSOpaque16IsZero(req->u.resolve.qsrv.TargetQID)) && (req->u.resolve.qsrv.ThisQInterval > 0))
+ n_mquests++;
+ }
+ else if (req->terminate == queryrecord_termination_callback)
+ {
+ if ((mDNSOpaque16IsZero(req->u.queryrecord.q.TargetQID)) && (req->u.queryrecord.q.ThisQInterval > 0))
+ n_mquests++;
+ }
+ else if (req->terminate == addrinfo_termination_callback)
+ {
+ if ((mDNSOpaque16IsZero(req->u.addrinfo.q4.TargetQID)) && (req->u.addrinfo.q4.ThisQInterval > 0))
+ n_mquests++;
+ }
+ else
+ {
+ return;
+ }
+}
+
+
+mDNSlocal void LogMcastClientInfo(request_state *req)
+{
+ if (!req->terminate)
+ LogMcastNoIdent("No operation yet on this socket");
+ else if (req->terminate == connection_termination)
+ {
+ int num_records = 0, num_ops = 0;
+ const registered_record_entry *p;
+ request_state *r;
+ for (p = req->u.reg_recs; p; p=p->next)
+ num_records++;
+ for (r = req->next; r; r=r->next)
+ if (r->primary == req)
+ num_ops++;
+ for (p = req->u.reg_recs; p; p=p->next)
+ {
+ if (!AuthRecord_uDNS(p->rr))
+ LogMcastNoIdent("R: -> DNSServiceRegisterRecord: %##s %s PID[%d](%s)", p->rr->resrec.name->c,
+ DNSTypeName(p->rr->resrec.rrtype), req->process_id, req->pid_name, i_mcount++);
+ }
+ for (r = req->next; r; r=r->next)
+ if (r->primary == req)
+ LogMcastClientInfo(r);
+ }
+ else if (req->terminate == regservice_termination_callback)
+ {
+ service_instance *ptr;
+ for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next)
+ {
+ if (!AuthRecord_uDNS(&ptr->srs.RR_SRV))
+ LogMcastNoIdent("R: DNSServiceRegister: %##s %u/%u PID[%d](%s)", ptr->srs.RR_SRV.resrec.name->c, mDNSVal16(req->u.servicereg.port),
+ SRS_PORT(&ptr->srs), req->process_id, req->pid_name, i_mcount++);
+ }
+ }
+ else if (req->terminate == browse_termination_callback)
+ {
+ browser_t *blist;
+ for (blist = req->u.browser.browsers; blist; blist = blist->next)
+ {
+ if (mDNSOpaque16IsZero(blist->q.TargetQID))
+ LogMcastNoIdent("Q: DNSServiceBrowse %##s %s PID[%d](%s)", blist->q.qname.c, DNSTypeName(blist->q.qtype),
+ req->process_id, req->pid_name, i_mcount++);
+ }
+ }
+ else if (req->terminate == resolve_termination_callback)
+ {
+ if ((mDNSOpaque16IsZero(req->u.resolve.qsrv.TargetQID)) && (req->u.resolve.qsrv.ThisQInterval > 0))
+ LogMcastNoIdent("Q: DNSServiceResolve %##s %s PID[%d](%s)", req->u.resolve.qsrv.qname.c, DNSTypeName(req->u.resolve.qsrv.qtype),
+ req->process_id, req->pid_name, i_mcount++);
+ }
+ else if (req->terminate == queryrecord_termination_callback)
+ {
+ if ((mDNSOpaque16IsZero(req->u.queryrecord.q.TargetQID)) && (req->u.queryrecord.q.ThisQInterval > 0))
+ LogMcastNoIdent("Q: DNSServiceQueryRecord %##s %s PID[%d](%s)", req->u.queryrecord.q.qname.c, DNSTypeName(req->u.queryrecord.q.qtype),
+ req->process_id, req->pid_name, i_mcount++);
+ }
+ else if (req->terminate == addrinfo_termination_callback)
+ {
+ if ((mDNSOpaque16IsZero(req->u.addrinfo.q4.TargetQID)) && (req->u.addrinfo.q4.ThisQInterval > 0))
+ LogMcastNoIdent("Q: DNSServiceGetAddrInfo %s%s %##s PID[%d](%s)",
+ req->u.addrinfo.protocol & kDNSServiceProtocol_IPv4 ? "v4" : " ",
+ req->u.addrinfo.protocol & kDNSServiceProtocol_IPv6 ? "v6" : " ",
+ req->u.addrinfo.q4.qname.c, req->process_id, req->pid_name, i_mcount++);
+ }
+ else
+ {
+ return;
+ }
+
+}
+
+mDNSlocal char *RecordTypeName(mDNSu8 rtype)
+{
+ switch (rtype)
+ {
+ case kDNSRecordTypeUnregistered: return ("Unregistered ");
+ case kDNSRecordTypeDeregistering: return ("Deregistering");
+ case kDNSRecordTypeUnique: return ("Unique ");
+ case kDNSRecordTypeAdvisory: return ("Advisory ");
+ case kDNSRecordTypeShared: return ("Shared ");
+ case kDNSRecordTypeVerified: return ("Verified ");
+ case kDNSRecordTypeKnownUnique: return ("KnownUnique ");
+ default: return("Unknown");
+ }
+}
+
+mDNSlocal void LogEtcHosts(mDNS *const m)
+{
+ mDNSBool showheader = mDNStrue;
+ const AuthRecord *ar;
+ mDNSu32 slot;
+ AuthGroup *ag;
+ int count = 0;
+ int authslot = 0;
+ mDNSBool truncated = 0;
+
+ for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+ {
+ if (m->rrauth.rrauth_hash[slot]) authslot++;
+ for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next)
+ for (ar = ag->members; ar; ar = ar->next)
+ {
+ if (ar->RecordCallback != FreeEtcHosts) continue;
+ if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" State Interface"); }
+
+ // Print a maximum of 50 records
+ if (count++ >= 50) { truncated = mDNStrue; continue; }
+ if (ar->ARType == AuthRecordLocalOnly)
+ {
+ if (ar->resrec.InterfaceID == mDNSInterface_LocalOnly)
+ LogMsgNoIdent(" %s LO %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar));
+ else
+ {
+ mDNSu32 scopeid = (mDNSu32)(uintptr_t)ar->resrec.InterfaceID;
+ LogMsgNoIdent(" %s %u %s", RecordTypeName(ar->resrec.RecordType), scopeid, ARDisplayString(m, ar));
+ }
+ }
+ }
+ }
+
+ if (showheader) LogMsgNoIdent("<None>");
+ else if (truncated) LogMsgNoIdent("<Truncated: to 50 records, Total records %d, Total Auth Groups %d, Auth Slots %d>", count, m->rrauth.rrauth_totalused, authslot);
+}
+
+mDNSlocal void LogLocalOnlyAuthRecords(mDNS *const m)
+{
+ mDNSBool showheader = mDNStrue;
+ const AuthRecord *ar;
+ mDNSu32 slot;
+ AuthGroup *ag;
+
+ for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+ {
+ for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next)
+ for (ar = ag->members; ar; ar = ar->next)
+ {
+ if (ar->RecordCallback == FreeEtcHosts) continue;
+ if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" State Interface"); }
+
+ // Print a maximum of 400 records
+ if (ar->ARType == AuthRecordLocalOnly)
+ LogMsgNoIdent(" %s LO %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar));
+ else if (ar->ARType == AuthRecordP2P)
+ LogMsgNoIdent(" %s PP %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar));
+ }
+ }
+
+ if (showheader) LogMsgNoIdent("<None>");
+}
+
+mDNSlocal char *AnonInfoToString(AnonymousInfo *ai, char *anonstr, int anstrlen)
+{
+ anonstr[0] = 0;
+ if (ai && ai->AnonData)
+ {
+ return (AnonDataToString(ai->AnonData, ai->AnonDataLen, anonstr, anstrlen));
+ }
+ return anonstr;
+}
+
+mDNSlocal void LogOneAuthRecord(mDNS *const m, const AuthRecord *ar, mDNSs32 now, const char *const ifname)
+{
+ char anstr[256];
+ if (AuthRecord_uDNS(ar))
+ {
+ LogMsgNoIdent("%7d %7d %7d %7d %s",
+ ar->ThisAPInterval / mDNSPlatformOneSecond,
+ (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond,
+ ar->expire ? (ar->expire - now) / mDNSPlatformOneSecond : 0,
+ ar->state, ARDisplayString(m, ar));
+ }
+ else
+ {
+ LogMsgNoIdent("%7d %7d %7d %7s %s%s",
+ ar->ThisAPInterval / mDNSPlatformOneSecond,
+ ar->AnnounceCount ? (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond : 0,
+ ar->TimeExpire ? (ar->TimeExpire - now) / mDNSPlatformOneSecond : 0,
+ ifname ? ifname : "ALL",
+ ARDisplayString(m, ar), AnonInfoToString(ar->resrec.AnonInfo, anstr, sizeof(anstr)));
+ }
+}
+
+mDNSlocal void LogAuthRecords(mDNS *const m, const mDNSs32 now, AuthRecord *ResourceRecords, int *proxy)
+{
+ mDNSBool showheader = mDNStrue;
+ const AuthRecord *ar;
+ OwnerOptData owner = zeroOwner;
+ for (ar = ResourceRecords; ar; ar=ar->next)
+ {
+ const char *const ifname = InterfaceNameForID(m, ar->resrec.InterfaceID);
+ if ((ar->WakeUp.HMAC.l[0] != 0) == (proxy != mDNSNULL))
+ {
+ if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" Int Next Expire State"); }
+ if (proxy) (*proxy)++;
+ if (!mDNSPlatformMemSame(&owner, &ar->WakeUp, sizeof(owner)))
+ {
+ owner = ar->WakeUp;
+ if (owner.password.l[0])
+ LogMsgNoIdent("Proxying for H-MAC %.6a I-MAC %.6a Password %.6a seq %d", &owner.HMAC, &owner.IMAC, &owner.password, owner.seq);
+ else if (!mDNSSameEthAddress(&owner.HMAC, &owner.IMAC))
+ LogMsgNoIdent("Proxying for H-MAC %.6a I-MAC %.6a seq %d", &owner.HMAC, &owner.IMAC, owner.seq);
+ else
+ LogMsgNoIdent("Proxying for %.6a seq %d", &owner.HMAC, owner.seq);
+ }
+ if (AuthRecord_uDNS(ar))
+ {
+ LogOneAuthRecord(m, ar, now, ifname);
+ }
+ else if (ar->ARType == AuthRecordLocalOnly)
+ {
+ LogMsgNoIdent(" LO %s", ARDisplayString(m, ar));
+ }
+ else if (ar->ARType == AuthRecordP2P)
+ {
+ LogMsgNoIdent(" PP %s", ARDisplayString(m, ar));
+ }
+ else
+ {
+ LogOneAuthRecord(m, ar, now, ifname);
+ if (ar->resrec.AnonInfo)
+ {
+ ResourceRecord *nsec3 = ar->resrec.AnonInfo->nsec3RR;
+ // We just print the values from the AuthRecord to keep it nicely aligned though
+ // all we want here is the nsec3 information.
+ LogMsgNoIdent("%7d %7d %7d %7s %s",
+ ar->ThisAPInterval / mDNSPlatformOneSecond,
+ ar->AnnounceCount ? (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond : 0,
+ ar->TimeExpire ? (ar->TimeExpire - now) / mDNSPlatformOneSecond : 0,
+ ifname ? ifname : "ALL",
+ RRDisplayString(m, nsec3));
+ }
+ }
+ }
+ }
+ if (showheader) LogMsgNoIdent("<None>");
+}
+
+mDNSlocal void PrintOneCacheRecord(mDNS *const m, const CacheRecord *cr, mDNSu32 slot, const mDNSu32 remain, const char *ifname, mDNSu32 *CacheUsed)
+{
+ LogMsgNoIdent("%3d %s%8d %-7s%s %-6s%s",
+ slot,
+ cr->CRActiveQuestion ? "*" : " ",
+ remain,
+ ifname ? ifname : "-U-",
+ (cr->resrec.RecordType == kDNSRecordTypePacketNegative) ? "-" :
+ (cr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? " " : "+",
+ DNSTypeName(cr->resrec.rrtype),
+ CRDisplayString(m, cr));
+ (*CacheUsed)++;
+}
+
+mDNSlocal void PrintCachedRecords(mDNS *const m, const CacheRecord *cr, mDNSu32 slot, const mDNSu32 remain, const char *ifname, mDNSu32 *CacheUsed)
+{
+ CacheRecord *nsec;
+ CacheRecord *soa;
+ nsec = cr->nsec;
+
+ // The records that are cached under the main cache record like nsec, soa don't have
+ // their own lifetime. If the main cache record expires, they also expire.
+ while (nsec)
+ {
+ PrintOneCacheRecord(m, nsec, slot, remain, ifname, CacheUsed);
+ nsec = nsec->next;
+ }
+ soa = cr->soa;
+ if (soa)
+ {
+ PrintOneCacheRecord(m, soa, slot, remain, ifname, CacheUsed);
+ }
+ if (cr->resrec.AnonInfo)
+ {
+ ResourceRecord *nsec3 = cr->resrec.AnonInfo->nsec3RR;
+ // Even though it is a resource record, we print the sameway
+ // as a cache record so that it aligns properly.
+ if (nsec3)
+ {
+ LogMsgNoIdent("%3d %s%8d %-7s%s %-6s%s",
+ slot,
+ " ",
+ remain,
+ ifname ? ifname : "-U-",
+ (nsec3->RecordType == kDNSRecordTypePacketNegative) ? "-" :
+ (nsec3->RecordType & kDNSRecordTypePacketUniqueMask) ? " " : "+",
+ DNSTypeName(nsec3->rrtype),
+ RRDisplayString(m, nsec3));
+ }
+ }
+}
+
+mDNSlocal char *AnonDataToString(const mDNSu8 *ad, int adlen, char *adstr, int adstrlen)
+{
+ adstr[0] = 0;
+ if (ad)
+ {
+ int len;
+ char *orig = adstr;
+
+ // If the caller is lazy to compute the length, we do it for them.
+ if (!adlen)
+ len = strlen((const char *)ad);
+ else
+ len = adlen;
+
+ // Print the anondata within brackets. Hence, we need space for two
+ // brackets and a NULL byte.
+ if (len > (adstrlen - 3))
+ len = adstrlen - 3;
+
+ *adstr++ = '(';
+ mDNSPlatformMemCopy(adstr, ad, len);
+ adstr[len] = ')';
+ adstr[len+1] = 0;
+ return orig;
+ }
+ return adstr;
+}
+
+mDNSexport void LogMDNSStatistics(mDNS *const m)
+{
+ LogMsgNoIdent("--- MDNS Statistics ---");
+
+ LogMsgNoIdent("Name Conflicts %u", m->mDNSStats.NameConflicts);
+ LogMsgNoIdent("KnownUnique Name Conflicts %u", m->mDNSStats.KnownUniqueNameConflicts);
+ LogMsgNoIdent("Duplicate Query Suppressions %u", m->mDNSStats.DupQuerySuppressions);
+ LogMsgNoIdent("KA Suppressions %u", m->mDNSStats.KnownAnswerSuppressions);
+ LogMsgNoIdent("KA Multiple Packets %u", m->mDNSStats.KnownAnswerMultiplePkts);
+ LogMsgNoIdent("Poof Cache Deletions %u", m->mDNSStats.PoofCacheDeletions);
+ LogMsgNoIdent("--------------------------------");
+
+ LogMsgNoIdent("Multicast packets Sent %u", m->MulticastPacketsSent);
+ LogMsgNoIdent("Multicast packets Received %u", m->MPktNum);
+ LogMsgNoIdent("Remote Subnet packets %u", m->RemoteSubnet);
+ LogMsgNoIdent("QU questions received %u", m->mDNSStats.UnicastBitInQueries);
+ LogMsgNoIdent("Normal multicast questions %u", m->mDNSStats.NormalQueries);
+ LogMsgNoIdent("Answers for questions %u", m->mDNSStats.MatchingAnswersForQueries);
+ LogMsgNoIdent("Unicast responses %u", m->mDNSStats.UnicastResponses);
+ LogMsgNoIdent("Multicast responses %u", m->mDNSStats.MulticastResponses);
+ LogMsgNoIdent("Unicast response Demotions %u", m->mDNSStats.UnicastDemotedToMulticast);
+ LogMsgNoIdent("--------------------------------");
+
+ LogMsgNoIdent("Sleeps %u", m->mDNSStats.Sleeps);
+ LogMsgNoIdent("Wakeups %u", m->mDNSStats.Wakes);
+ LogMsgNoIdent("Interface UP events %u", m->mDNSStats.InterfaceUp);
+ LogMsgNoIdent("Interface UP Flap events %u", m->mDNSStats.InterfaceUpFlap);
+ LogMsgNoIdent("Interface Down events %u", m->mDNSStats.InterfaceDown);
+ LogMsgNoIdent("Interface DownFlap events %u", m->mDNSStats.InterfaceDownFlap);
+ LogMsgNoIdent("Cache refresh queries %u", m->mDNSStats.CacheRefreshQueries);
+ LogMsgNoIdent("Cache refreshed %u", m->mDNSStats.CacheRefreshed);
+ LogMsgNoIdent("Wakeup on Resolves %u", m->mDNSStats.WakeOnResolves);
+}
+
+mDNSexport void udsserver_info(mDNS *const m)
+{
+ const mDNSs32 now = mDNS_TimeNow(m);
+ mDNSu32 CacheUsed = 0, CacheActive = 0, slot;
+ int ProxyA = 0, ProxyD = 0;
+ const CacheGroup *cg;
+ const CacheRecord *cr;
+ const DNSQuestion *q;
+ const DNameListElem *d;
+ const SearchListElem *s;
+
+ LogMsgNoIdent("Timenow 0x%08lX (%d)", (mDNSu32)now, now);
+
+ LogMsgNoIdent("------------ Cache -------------");
+ LogMsgNoIdent("Slt Q TTL if U Type rdlen");
+ for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
+ {
+ for (cg = m->rrcache_hash[slot]; cg; cg=cg->next)
+ {
+ CacheUsed++; // Count one cache entity for the CacheGroup object
+ for (cr = cg->members; cr; cr=cr->next)
+ {
+ const mDNSs32 remain = cr->resrec.rroriginalttl - (now - cr->TimeRcvd) / mDNSPlatformOneSecond;
+ const char *ifname;
+ mDNSInterfaceID InterfaceID = cr->resrec.InterfaceID;
+ if (!InterfaceID && cr->resrec.rDNSServer && cr->resrec.rDNSServer->scoped)
+ InterfaceID = cr->resrec.rDNSServer->interface;
+ ifname = InterfaceNameForID(m, InterfaceID);
+ if (cr->CRActiveQuestion) CacheActive++;
+ PrintOneCacheRecord(m, cr, slot, remain, ifname, &CacheUsed);
+ PrintCachedRecords(m, cr, slot, remain, ifname, &CacheUsed);
+ }
+ }
+ }
+
+ if (m->rrcache_totalused != CacheUsed)
+ LogMsgNoIdent("Cache use mismatch: rrcache_totalused is %lu, true count %lu", m->rrcache_totalused, CacheUsed);
+ if (m->rrcache_active != CacheActive)
+ LogMsgNoIdent("Cache use mismatch: rrcache_active is %lu, true count %lu", m->rrcache_active, CacheActive);
+ LogMsgNoIdent("Cache currently contains %lu entities; %lu referenced by active questions", CacheUsed, CacheActive);
+
+ LogMsgNoIdent("--------- Auth Records ---------");
+ LogAuthRecords(m, now, m->ResourceRecords, mDNSNULL);
+
+ LogMsgNoIdent("--------- LocalOnly, P2P Auth Records ---------");
+ LogLocalOnlyAuthRecords(m);
+
+ LogMsgNoIdent("--------- /etc/hosts ---------");
+ LogEtcHosts(m);
+
+ LogMsgNoIdent("------ Duplicate Records -------");
+ LogAuthRecords(m, now, m->DuplicateRecords, mDNSNULL);
+
+ LogMsgNoIdent("----- Auth Records Proxied -----");
+ LogAuthRecords(m, now, m->ResourceRecords, &ProxyA);
+
+ LogMsgNoIdent("-- Duplicate Records Proxied ---");
+ LogAuthRecords(m, now, m->DuplicateRecords, &ProxyD);
+
+ LogMsgNoIdent("---------- Questions -----------");
+ if (!m->Questions) LogMsgNoIdent("<None>");
+ else
+ {
+ char anonstr[256];
+ CacheUsed = 0;
+ CacheActive = 0;
+ LogMsgNoIdent(" Int Next if T NumAns VDNS Qptr DupOf SU SQ Type Name");
+ for (q = m->Questions; q; q=q->next)
+ {
+ mDNSs32 i = q->ThisQInterval / mDNSPlatformOneSecond;
+ mDNSs32 n = (NextQSendTime(q) - now) / mDNSPlatformOneSecond;
+ char *ifname = InterfaceNameForID(m, q->InterfaceID);
+ CacheUsed++;
+ if (q->ThisQInterval) CacheActive++;
+ LogMsgNoIdent("%6d%6d %-7s%s%s %5d 0x%x%x 0x%p 0x%p %1d %2d %-5s%##s%s%s",
+ i, n,
+ ifname ? ifname : mDNSOpaque16IsZero(q->TargetQID) ? "" : "-U-",
+ mDNSOpaque16IsZero(q->TargetQID) ? (q->LongLived ? "l" : " ") : (q->LongLived ? "L" : "O"),
+ PrivateQuery(q) ? "P" : q->ValidationRequired ? "V" : q->ValidatingResponse ? "R" : " ",
+ q->CurrentAnswers, q->validDNSServers.l[1], q->validDNSServers.l[0], q, q->DuplicateOf,
+ q->SuppressUnusable, q->SuppressQuery, DNSTypeName(q->qtype), q->qname.c,
+ AnonInfoToString(q->AnonInfo, anonstr, sizeof(anonstr)),
+ q->DuplicateOf ? " (dup)" : "");
+ }
+ LogMsgNoIdent("%lu question%s; %lu active", CacheUsed, CacheUsed > 1 ? "s" : "", CacheActive);
+ }
+
+ LogMsgNoIdent("----- Local-Only Questions -----");
+ if (!m->LocalOnlyQuestions) LogMsgNoIdent("<None>");
+ else for (q = m->LocalOnlyQuestions; q; q=q->next)
+ LogMsgNoIdent(" %5d %-6s%##s%s",
+ q->CurrentAnswers, DNSTypeName(q->qtype), q->qname.c, q->DuplicateOf ? " (dup)" : "");
+
+ LogMsgNoIdent("---- Active UDS Client Requests ----");
+ if (!all_requests) LogMsgNoIdent("<None>");
+ else
+ {
+ request_state *req, *r;
+ for (req = all_requests; req; req=req->next)
+ {
+ if (req->primary) // If this is a subbordinate operation, check that the parent is in the list
+ {
+ for (r = all_requests; r && r != req; r=r->next) if (r == req->primary) goto foundparent;
+ LogMsgNoIdent("%3d: Orhpan operation %p; parent %p not found in request list", req->sd);
+ }
+ // For non-subbordinate operations, and subbordinate operations that have lost their parent, write out their info
+ LogClientInfo(m, req);
+foundparent:;
+ }
+ }
+
+ LogMsgNoIdent("-------- NAT Traversals --------");
+ LogMsgNoIdent("ExtAddress %.4a Retry %d Interval %d",
+ &m->ExtAddress,
+ m->retryGetAddr ? (m->retryGetAddr - now) / mDNSPlatformOneSecond : 0,
+ m->retryIntervalGetAddr / mDNSPlatformOneSecond);
+ if (m->NATTraversals)
+ {
+ const NATTraversalInfo *nat;
+ for (nat = m->NATTraversals; nat; nat=nat->next)
+ {
+ LogMsgNoIdent("%p %s Int %5d %s Err %d Retry %5d Interval %5d Expire %5d Req %.4a:%d Ext %.4a:%d",
+ nat,
+ nat->Protocol ? (nat->Protocol == NATOp_MapTCP ? "TCP" : "UDP") : "ADD",
+ mDNSVal16(nat->IntPort),
+ (nat->lastSuccessfulProtocol == NATTProtocolNone ? "None " :
+ nat->lastSuccessfulProtocol == NATTProtocolNATPMP ? "NAT-PMP " :
+ nat->lastSuccessfulProtocol == NATTProtocolUPNPIGD ? "UPnP/IGD" :
+ nat->lastSuccessfulProtocol == NATTProtocolPCP ? "PCP " :
+ /* else */ "Unknown " ),
+ nat->Result,
+ nat->retryPortMap ? (nat->retryPortMap - now) / mDNSPlatformOneSecond : 0,
+ nat->retryInterval / mDNSPlatformOneSecond,
+ nat->ExpiryTime ? (nat->ExpiryTime - now) / mDNSPlatformOneSecond : 0,
+ &nat->NewAddress, mDNSVal16(nat->RequestedPort),
+ &nat->ExternalAddress, mDNSVal16(nat->ExternalPort));
+ }
+ }
+
+ LogMsgNoIdent("--------- AuthInfoList ---------");
+ if (!m->AuthInfoList) LogMsgNoIdent("<None>");
+ else
+ {
+ const DomainAuthInfo *a;
+ for (a = m->AuthInfoList; a; a = a->next)
+ {
+ LogMsgNoIdent("%##s %##s %##s %d %d %.16a%s",
+ a->domain.c, a->keyname.c,
+ a->hostname.c, (a->port.b[0] << 8 | a->port.b[1]),
+ (a->deltime ? (a->deltime - now) : 0),
+ &a->AutoTunnelInnerAddress, a->AutoTunnel ? " AutoTunnel" : "");
+ }
+ }
+
+ #if APPLE_OSX_mDNSResponder
+ LogMsgNoIdent("--------- TunnelClients --------");
+ if (!m->TunnelClients) LogMsgNoIdent("<None>");
+ else
+ {
+ const ClientTunnel *c;
+ for (c = m->TunnelClients; c; c = c->next)
+ LogMsgNoIdent("%##s local %.16a %.4a %.16a remote %.16a %.4a %5d %.16a interval %d",
+ c->dstname.c, &c->loc_inner, &c->loc_outer, &c->loc_outer6, &c->rmt_inner, &c->rmt_outer, mDNSVal16(c->rmt_outer_port), &c->rmt_outer6, c->q.ThisQInterval);
+ }
+ #endif // APPLE_OSX_mDNSResponder
+
+ LogMsgNoIdent("---------- Misc State ----------");
+
+ LogMsgNoIdent("PrimaryMAC: %.6a", &m->PrimaryMAC);
+
+ LogMsgNoIdent("m->SleepState %d (%s) seq %d",
+ m->SleepState,
+ m->SleepState == SleepState_Awake ? "Awake" :
+ m->SleepState == SleepState_Transferring ? "Transferring" :
+ m->SleepState == SleepState_Sleeping ? "Sleeping" : "?",
+ m->SleepSeqNum);
+
+ if (!m->SPSSocket) LogMsgNoIdent("Not offering Sleep Proxy Service");
+#ifndef SPC_DISABLED
+ else LogMsgNoIdent("Offering Sleep Proxy Service: %#s", m->SPSRecords.RR_SRV.resrec.name->c);
+#endif
+ if (m->ProxyRecords == ProxyA + ProxyD) LogMsgNoIdent("ProxyRecords: %d + %d = %d", ProxyA, ProxyD, ProxyA + ProxyD);
+ else LogMsgNoIdent("ProxyRecords: MISMATCH %d + %d = %d ≠ %d", ProxyA, ProxyD, ProxyA + ProxyD, m->ProxyRecords);
+
+ LogMsgNoIdent("------ Auto Browse Domains -----");
+ if (!AutoBrowseDomains) LogMsgNoIdent("<None>");
+ else for (d=AutoBrowseDomains; d; d=d->next) LogMsgNoIdent("%##s", d->name.c);
+
+ LogMsgNoIdent("--- Auto Registration Domains --");
+ if (!AutoRegistrationDomains) LogMsgNoIdent("<None>");
+ else for (d=AutoRegistrationDomains; d; d=d->next) LogMsgNoIdent("%##s", d->name.c);
+
+ LogMsgNoIdent("--- Search Domains --");
+ if (!SearchList) LogMsgNoIdent("<None>");
+ else
+ {
+ for (s=SearchList; s; s=s->next)
+ {
+ char *ifname = InterfaceNameForID(m, s->InterfaceID);
+ LogMsgNoIdent("%##s %s", s->domain.c, ifname ? ifname : "");
+ }
+ }
+ LogInfo("--- Trust Anchors ---");
+ if (!m->TrustAnchors)
+ {
+ LogInfo("<None>");
+ }
+ else
+ {
+ TrustAnchor *ta;
+ mDNSu8 fromTimeBuf[64];
+ mDNSu8 untilTimeBuf[64];
+
+ for (ta=m->TrustAnchors; ta; ta=ta->next)
+ {
+ mDNSPlatformFormatTime((unsigned long)ta->validFrom, fromTimeBuf, sizeof(fromTimeBuf));
+ mDNSPlatformFormatTime((unsigned long)ta->validUntil, untilTimeBuf, sizeof(untilTimeBuf));
+ LogInfo("%##s %d %d %d %d %s %s", ta->zone.c, ta->rds.keyTag,
+ ta->rds.alg, ta->rds.digestType, ta->digestLen, fromTimeBuf, untilTimeBuf);
+ }
+ }
+
+ LogInfo("--- DNSSEC Statistics ---");
+
+ LogInfo("Next Stats Time %u", m->NextStatLogTime - mDNSPlatformUTC());
+ LogMsgNoIdent("Unicast Cache size %u", m->rrcache_totalused_unicast);
+ LogInfo("DNSSEC Cache size %u", m->DNSSECStats.TotalMemUsed);
+ if (m->rrcache_totalused_unicast)
+ LogInfo("DNSSEC usage percentage %u", ((unsigned long)(m->DNSSECStats.TotalMemUsed * 100))/m->rrcache_totalused_unicast);
+ LogInfo("DNSSEC Extra Packets (0 to 2) %u", m->DNSSECStats.ExtraPackets0);
+ LogInfo("DNSSEC Extra Packets (3 to 6) %u", m->DNSSECStats.ExtraPackets3);
+ LogInfo("DNSSEC Extra Packets (7 to 9) %u", m->DNSSECStats.ExtraPackets7);
+ LogInfo("DNSSEC Extra Packets ( >= 10) %u", m->DNSSECStats.ExtraPackets10);
+
+ LogInfo("DNSSEC Latency (0 to 4ms) %u", m->DNSSECStats.Latency0);
+ LogInfo("DNSSEC Latency (4 to 9ms) %u", m->DNSSECStats.Latency5);
+ LogInfo("DNSSEC Latency (10 to 19ms) %u", m->DNSSECStats.Latency10);
+ LogInfo("DNSSEC Latency (20 to 49ms) %u", m->DNSSECStats.Latency20);
+ LogInfo("DNSSEC Latency (50 to 99ms) %u", m->DNSSECStats.Latency50);
+ LogInfo("DNSSEC Latency ( >=100ms) %u", m->DNSSECStats.Latency100);
+
+ LogInfo("DNSSEC Secure Status %u", m->DNSSECStats.SecureStatus);
+ LogInfo("DNSSEC Insecure Status %u", m->DNSSECStats.InsecureStatus);
+ LogInfo("DNSSEC Indeterminate Status %u", m->DNSSECStats.IndeterminateStatus);
+ LogInfo("DNSSEC Bogus Status %u", m->DNSSECStats.BogusStatus);
+ LogInfo("DNSSEC NoResponse Status %u", m->DNSSECStats.NoResponseStatus);
+ LogInfo("DNSSEC Probes sent %u", m->DNSSECStats.NumProbesSent);
+ LogInfo("DNSSEC Msg Size (<=1024) %u", m->DNSSECStats.MsgSize0);
+ LogInfo("DNSSEC Msg Size (<=2048) %u", m->DNSSECStats.MsgSize1);
+ LogInfo("DNSSEC Msg Size (> 2048) %u", m->DNSSECStats.MsgSize2);
+
+ LogMDNSStatistics(m);
+
+ LogMsgNoIdent("---- Task Scheduling Timers ----");
+
+ if (!m->NewQuestions)
+ LogMsgNoIdent("NewQuestion <NONE>");
+ else
+ LogMsgNoIdent("NewQuestion DelayAnswering %d %d %##s (%s)",
+ m->NewQuestions->DelayAnswering, m->NewQuestions->DelayAnswering-now,
+ m->NewQuestions->qname.c, DNSTypeName(m->NewQuestions->qtype));
+
+ if (!m->NewLocalOnlyQuestions)
+ LogMsgNoIdent("NewLocalOnlyQuestions <NONE>");
+ else
+ LogMsgNoIdent("NewLocalOnlyQuestions %##s (%s)",
+ m->NewLocalOnlyQuestions->qname.c, DNSTypeName(m->NewLocalOnlyQuestions->qtype));
+
+ if (!m->NewLocalRecords)
+ LogMsgNoIdent("NewLocalRecords <NONE>");
+ else
+ LogMsgNoIdent("NewLocalRecords %02X %s", m->NewLocalRecords->resrec.RecordType, ARDisplayString(m, m->NewLocalRecords));
+
+ LogMsgNoIdent("SPSProxyListChanged%s", m->SPSProxyListChanged ? "" : " <NONE>");
+ LogMsgNoIdent("LocalRemoveEvents%s", m->LocalRemoveEvents ? "" : " <NONE>");
+ LogMsgNoIdent("m->AutoTunnelRelayAddr %.16a", &m->AutoTunnelRelayAddr);
+ LogMsgNoIdent("m->WABBrowseQueriesCount %d", m->WABBrowseQueriesCount);
+ LogMsgNoIdent("m->WABLBrowseQueriesCount %d", m->WABLBrowseQueriesCount);
+ LogMsgNoIdent("m->WABRegQueriesCount %d", m->WABRegQueriesCount);
+ LogMsgNoIdent("m->mDNSOppCaching %d", m->mDNSOppCaching);
+ LogMsgNoIdent("m->AutoTargetServices %d", m->AutoTargetServices);
+
+#define LogTimer(MSG,T) LogMsgNoIdent( MSG " %08X %11d %08X %11d", (T), (T), (T)-now, (T)-now)
+
+ LogMsgNoIdent(" ABS (hex) ABS (dec) REL (hex) REL (dec)");
+ LogMsgNoIdent("m->timenow %08X %11d", now, now);
+ LogMsgNoIdent("m->timenow_adjust %08X %11d", m->timenow_adjust, m->timenow_adjust);
+ LogTimer("m->NextScheduledEvent ", m->NextScheduledEvent);
+
+#ifndef UNICAST_DISABLED
+ LogTimer("m->NextuDNSEvent ", m->NextuDNSEvent);
+ LogTimer("m->NextSRVUpdate ", m->NextSRVUpdate);
+ LogTimer("m->NextScheduledNATOp ", m->NextScheduledNATOp);
+ LogTimer("m->retryGetAddr ", m->retryGetAddr);
+#endif
+
+ LogTimer("m->NextCacheCheck ", m->NextCacheCheck);
+ LogTimer("m->NextScheduledSPS ", m->NextScheduledSPS);
+ LogTimer("m->NextScheduledKA ", m->NextScheduledKA);
+ LogTimer("m->NextScheduledSPRetry ", m->NextScheduledSPRetry);
+ LogTimer("m->DelaySleep ", m->DelaySleep);
+
+ LogTimer("m->NextScheduledQuery ", m->NextScheduledQuery);
+ LogTimer("m->NextScheduledProbe ", m->NextScheduledProbe);
+ LogTimer("m->NextScheduledResponse", m->NextScheduledResponse);
+
+ LogTimer("m->SuppressSending ", m->SuppressSending);
+ LogTimer("m->SuppressProbes ", m->SuppressProbes);
+ LogTimer("m->ProbeFailTime ", m->ProbeFailTime);
+ LogTimer("m->DelaySleep ", m->DelaySleep);
+ LogTimer("m->SleepLimit ", m->SleepLimit);
+ LogTimer("m->NextScheduledStopTime ", m->NextScheduledStopTime);
+}
+
+#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING
+mDNSexport void uds_validatelists(void)
+{
+ const request_state *req, *p;
+ for (req = all_requests; req; req=req->next)
+ {
+ if (req->next == (request_state *)~0 || (req->sd < 0 && req->sd != -2))
+ LogMemCorruption("UDS request list: %p is garbage (%d)", req, req->sd);
+
+ if (req->primary == req)
+ LogMemCorruption("UDS request list: req->primary should not point to self %p/%d", req, req->sd);
+
+ if (req->primary && req->replies)
+ LogMemCorruption("UDS request list: Subordinate request %p/%d/%p should not have replies (%p)",
+ req, req->sd, req->primary && req->replies);
+
+ p = req->primary;
+ if ((long)p & 3)
+ LogMemCorruption("UDS request list: req %p primary %p is misaligned (%d)", req, p, req->sd);
+ else if (p && (p->next == (request_state *)~0 || (p->sd < 0 && p->sd != -2)))
+ LogMemCorruption("UDS request list: req %p primary %p is garbage (%d)", req, p, p->sd);
+
+ reply_state *rep;
+ for (rep = req->replies; rep; rep=rep->next)
+ if (rep->next == (reply_state *)~0)
+ LogMemCorruption("UDS req->replies: %p is garbage", rep);
+
+ if (req->terminate == connection_termination)
+ {
+ registered_record_entry *r;
+ for (r = req->u.reg_recs; r; r=r->next)
+ if (r->next == (registered_record_entry *)~0)
+ LogMemCorruption("UDS req->u.reg_recs: %p is garbage", r);
+ }
+ else if (req->terminate == regservice_termination_callback)
+ {
+ service_instance *s;
+ for (s = req->u.servicereg.instances; s; s=s->next)
+ if (s->next == (service_instance *)~0)
+ LogMemCorruption("UDS req->u.servicereg.instances: %p is garbage", s);
+ }
+ else if (req->terminate == browse_termination_callback)
+ {
+ browser_t *b;
+ for (b = req->u.browser.browsers; b; b=b->next)
+ if (b->next == (browser_t *)~0)
+ LogMemCorruption("UDS req->u.browser.browsers: %p is garbage", b);
+ }
+ }
+
+ DNameListElem *d;
+ for (d = SCPrefBrowseDomains; d; d=d->next)
+ if (d->next == (DNameListElem *)~0 || d->name.c[0] > 63)
+ LogMemCorruption("SCPrefBrowseDomains: %p is garbage (%d)", d, d->name.c[0]);
+
+ ARListElem *b;
+ for (b = LocalDomainEnumRecords; b; b=b->next)
+ if (b->next == (ARListElem *)~0 || b->ar.resrec.name->c[0] > 63)
+ LogMemCorruption("LocalDomainEnumRecords: %p is garbage (%d)", b, b->ar.resrec.name->c[0]);
+
+ for (d = AutoBrowseDomains; d; d=d->next)
+ if (d->next == (DNameListElem *)~0 || d->name.c[0] > 63)
+ LogMemCorruption("AutoBrowseDomains: %p is garbage (%d)", d, d->name.c[0]);
+
+ for (d = AutoRegistrationDomains; d; d=d->next)
+ if (d->next == (DNameListElem *)~0 || d->name.c[0] > 63)
+ LogMemCorruption("AutoRegistrationDomains: %p is garbage (%d)", d, d->name.c[0]);
+}
+#endif // APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING
+
+mDNSlocal int send_msg(request_state *const req)
+{
+ reply_state *const rep = req->replies; // Send the first waiting reply
+ ssize_t nwriten;
+ if (req->no_reply) return(t_complete);
+
+ ConvertHeaderBytes(rep->mhdr);
+ nwriten = send(req->sd, (char *)&rep->mhdr + rep->nwriten, rep->totallen - rep->nwriten, 0);
+ ConvertHeaderBytes(rep->mhdr);
+
+ if (nwriten < 0)
+ {
+ if (dnssd_errno == dnssd_EINTR || dnssd_errno == dnssd_EWOULDBLOCK) nwriten = 0;
+ else
+ {
+#if !defined(PLATFORM_NO_EPIPE)
+ if (dnssd_errno == EPIPE)
+ return(req->ts = t_terminated);
+ else
+#endif
+ {
+ LogMsg("send_msg ERROR: failed to write %d of %d bytes to fd %d errno %d (%s)",
+ rep->totallen - rep->nwriten, rep->totallen, req->sd, dnssd_errno, dnssd_strerror(dnssd_errno));
+ return(t_error);
+ }
+ }
+ }
+ rep->nwriten += nwriten;
+ return (rep->nwriten == rep->totallen) ? t_complete : t_morecoming;
+}
+
+mDNSexport mDNSs32 udsserver_idle(mDNSs32 nextevent)
+{
+ mDNSs32 now = mDNS_TimeNow(&mDNSStorage);
+ request_state **req = &all_requests;
+
+ while (*req)
+ {
+ request_state *const r = *req;
+
+ if (r->terminate == resolve_termination_callback)
+ if (r->u.resolve.ReportTime && now - r->u.resolve.ReportTime >= 0)
+ {
+ r->u.resolve.ReportTime = 0;
+ LogMsgNoIdent("Client application bug PID[%d](%s) : DNSServiceResolve(%##s) active for over two minutes. "
+ "This places considerable burden on the network.", r->process_id, r->pid_name, r->u.resolve.qsrv.qname.c);
+ }
+
+ // Note: Only primary req's have reply lists, not subordinate req's.
+ while (r->replies) // Send queued replies
+ {
+ transfer_state result;
+ if (r->replies->next)
+ r->replies->rhdr->flags |= dnssd_htonl(kDNSServiceFlagsMoreComing);
+ result = send_msg(r); // Returns t_morecoming if buffer full because client is not reading
+ if (result == t_complete)
+ {
+ reply_state *fptr = r->replies;
+ r->replies = r->replies->next;
+ freeL("reply_state/udsserver_idle", fptr);
+ r->time_blocked = 0; // reset failure counter after successful send
+ r->unresponsiveness_reports = 0;
+ continue;
+ }
+ else if (result == t_terminated || result == t_error)
+ {
+ LogMsg("%3d: Could not write data to clientPID[%d](%s) because of error - aborting connection", r->sd, r->process_id, r->pid_name);
+ LogClientInfo(&mDNSStorage, r);
+ abort_request(r);
+ }
+ break;
+ }
+
+ if (r->replies) // If we failed to send everything, check our time_blocked timer
+ {
+ if (nextevent - now > mDNSPlatformOneSecond)
+ nextevent = now + mDNSPlatformOneSecond;
+
+ if (mDNSStorage.SleepState != SleepState_Awake)
+ r->time_blocked = 0;
+ else if (!r->time_blocked)
+ r->time_blocked = NonZeroTime(now);
+ else if (now - r->time_blocked >= 10 * mDNSPlatformOneSecond * (r->unresponsiveness_reports+1))
+ {
+ int num = 0;
+ struct reply_state *x = r->replies;
+ while (x)
+ {
+ num++;
+ x=x->next;
+ }
+ LogMsg("%3d: Could not write data to client PID[%d](%s) after %ld seconds, %d repl%s waiting",
+ r->sd, r->process_id, r->pid_name, (now - r->time_blocked) / mDNSPlatformOneSecond, num, num == 1 ? "y" : "ies");
+ if (++r->unresponsiveness_reports >= 60)
+ {
+ LogMsg("%3d: Client PID[%d](%s) unresponsive; aborting connection", r->sd, r->process_id, r->pid_name);
+ LogClientInfo(&mDNSStorage, r);
+ abort_request(r);
+ }
+ }
+ }
+
+ if (!dnssd_SocketValid(r->sd)) // If this request is finished, unlink it from the list and free the memory
+ {
+ // Since we're already doing a list traversal, we unlink the request directly instead of using AbortUnlinkAndFree()
+ *req = r->next;
+ freeL("request_state/udsserver_idle", r);
+ }
+ else
+ req = &r->next;
+ }
+ return nextevent;
+}
+
+struct CompileTimeAssertionChecks_uds_daemon
+{
+ // Check our structures are reasonable sizes. Including overly-large buffers, or embedding
+ // other overly-large structures instead of having a pointer to them, can inadvertently
+ // cause structure sizes (and therefore memory usage) to balloon unreasonably.
+ char sizecheck_request_state [(sizeof(request_state) <= 2000) ? 1 : -1];
+ char sizecheck_registered_record_entry[(sizeof(registered_record_entry) <= 60) ? 1 : -1];
+ char sizecheck_service_instance [(sizeof(service_instance) <= 6552) ? 1 : -1];
+ char sizecheck_browser_t [(sizeof(browser_t) <= 1096) ? 1 : -1];
+ char sizecheck_reply_hdr [(sizeof(reply_hdr) <= 12) ? 1 : -1];
+ char sizecheck_reply_state [(sizeof(reply_state) <= 64) ? 1 : -1];
+};
diff --git a/mDNSResponder/mDNSShared/uds_daemon.h b/mDNSResponder/mDNSShared/uds_daemon.h
new file mode 100644
index 00000000..ca361172
--- /dev/null
+++ b/mDNSResponder/mDNSShared/uds_daemon.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+
+ File: uds_daemon.h
+
+ Contains: Interfaces necessary to talk to uds_daemon.c.
+
+ Version: 1.0
+
+ */
+
+#include "mDNSEmbeddedAPI.h"
+#include "dnssd_ipc.h"
+
+/* Client interface: */
+
+#define SRS_PORT(S) mDNSVal16((S)->RR_SRV.resrec.rdata->u.srv.port)
+
+extern int udsserver_init(dnssd_sock_t skts[], mDNSu32 count);
+extern mDNSs32 udsserver_idle(mDNSs32 nextevent);
+extern void udsserver_info(mDNS *const m); // print out info about current state
+extern void udsserver_handle_configchange(mDNS *const m);
+extern int udsserver_exit(void); // should be called prior to app exit
+extern void LogMcastStateInfo(mDNS *const m, mDNSBool mflag, mDNSBool start, mDNSBool mstatelog);
+#define LogMcastQ (mDNS_McastLoggingEnabled == 0) ? ((void)0) : LogMcastQuestion
+#define LogMcastS (mDNS_McastLoggingEnabled == 0) ? ((void)0) : LogMcastService
+#define LogMcast (mDNS_McastLoggingEnabled == 0) ? ((void)0) : LogMsg
+#define LogMcastNoIdent (mDNS_McastLoggingEnabled == 0) ? ((void)0) : LogMsgNoIdent
+
+/* Routines that uds_daemon expects to link against: */
+
+typedef void (*udsEventCallback)(int fd, short filter, void *context);
+extern mStatus udsSupportAddFDToEventLoop(dnssd_sock_t fd, udsEventCallback callback, void *context, void **platform_data);
+extern int udsSupportReadFD(dnssd_sock_t fd, char* buf, int len, int flags, void *platform_data);
+extern mStatus udsSupportRemoveFDFromEventLoop(dnssd_sock_t fd, void *platform_data); // Note: This also CLOSES the file descriptor as well
+
+extern void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay);
+
+// Globals and functions defined in uds_daemon.c and also shared with the old "daemon.c" on OS X
+
+extern mDNS mDNSStorage;
+extern DNameListElem *AutoRegistrationDomains;
+extern DNameListElem *AutoBrowseDomains;
+
+extern mDNSs32 ChopSubTypes(char *regtype, char **AnonData);
+extern AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p, char **AnonData);
+extern int CountExistingRegistrations(domainname *srv, mDNSIPPort port);
+extern void FreeExtraRR(mDNS *const m, AuthRecord *const rr, mStatus result);
+extern int CountPeerRegistrations(mDNS *const m, ServiceRecordSet *const srs);
+
+#if APPLE_OSX_mDNSResponder
+
+extern void machserver_automatic_browse_domain_changed(const domainname *d, mDNSBool add);
+extern void machserver_automatic_registration_domain_changed(const domainname *d, mDNSBool add);
+// D2D interface support
+extern void external_start_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const type, DNS_TypeValues qtype, DNSServiceFlags flags);
+extern void external_stop_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const type, DNS_TypeValues qtype, DNSServiceFlags flags);
+extern void external_start_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags);
+extern void external_stop_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags);
+extern void external_start_resolving_service(mDNSInterfaceID InterfaceID, const domainname *const fqdn, DNSServiceFlags flags);
+extern void external_stop_resolving_service(mDNSInterfaceID InterfaceID, const domainname *const fqdn, DNSServiceFlags flags);
+extern void external_connection_release(const domainname *instance);
+
+#else // APPLE_OSX_mDNSResponder
+
+#define external_start_browsing_for_service(A,B,C,D) (void)(A)
+#define external_stop_browsing_for_service(A,B,C,D) (void)(A)
+#define external_start_advertising_service(A,B) (void)(A)
+#define external_stop_advertising_service(A,B) (void)(A)
+#define external_start_resolving_service(A,B,C) (void)(A)
+#define external_stop_resolving_service(A,B,C) (void)(A)
+#define external_connection_release(A) (void)(A)
+
+#endif // APPLE_OSX_mDNSResponder
+
+extern const char mDNSResponderVersionString_SCCS[];
+#define mDNSResponderVersionString (mDNSResponderVersionString_SCCS+5)