diff options
author | Siqi LIU <yy_sq_000@Siqi-PC.(none)> | 2013-08-15 15:18:33 +0200 |
---|---|---|
committer | siqi <me@siqi.fr> | 2013-08-16 16:41:50 +0200 |
commit | 03fd0a02f2c0868c40853aa974ed20b8026fed17 (patch) | |
tree | 006c8938ee39063b317209d19840ac258ea5f6fa | |
parent | 61a2c9fbacebb15511dfc233ffbd25937ea2d04f (diff) |
build mDNSResponder on Windows within LibO
Change-Id: I6973be21f6f4009d6934220f978e9369fef67ede
18 files changed, 12263 insertions, 4 deletions
diff --git a/sd/Library_sd.mk b/sd/Library_sd.mk index 2adb1595a654..e791d03488e6 100644 --- a/sd/Library_sd.mk +++ b/sd/Library_sd.mk @@ -562,6 +562,23 @@ $(eval $(call gb_Library_add_exception_objects,sd,\ sd/source/ui/remotecontrol/DiscoveryService \ )) +ifeq ($(OS),WNT) + +$(eval $(call gb_Library_add_exception_objects,sd,\ + sd/source/ui/remotecontrol/WINNetworkService \ +)) + +$(eval $(call gb_Library_add_cobjects,sd,\ + sd/source/ui/remotecontrol/mDNSResponder/DebugServices \ + sd/source/ui/remotecontrol/mDNSResponder/dnssd_clientlib \ + sd/source/ui/remotecontrol/mDNSResponder/dnssd_clientstub \ + sd/source/ui/remotecontrol/mDNSResponder/dnssd_ipc \ + sd/source/ui/remotecontrol/mDNSResponder/GenLinkedList \ + sd/source/ui/remotecontrol/mDNSResponder/dllmain \ +)) + +endif + endif $(eval $(call gb_Library_add_defs,sd,\ diff --git a/sd/source/ui/remotecontrol/AvahiNetworkService.cxx b/sd/source/ui/remotecontrol/AvahiNetworkService.cxx index 19f9f842309a..8fc4eb7216bb 100644 --- a/sd/source/ui/remotecontrol/AvahiNetworkService.cxx +++ b/sd/source/ui/remotecontrol/AvahiNetworkService.cxx @@ -95,7 +95,7 @@ static void create_services(AvahiClient *c) { if (avahi_entry_group_is_empty(group)) { fprintf(stderr, "Adding service '%s'\n", avahiService->getName().c_str()); snprintf(r, sizeof(r), "random=%i", rand()); - if ((ret = avahi_entry_group_add_service(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, static_cast<AvahiPublishFlags>(0), avahiService->getName().c_str(), "_impressremote._tcp", NULL, NULL, 1599, "local", r, NULL)) < 0) { + if ((ret = avahi_entry_group_add_service(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, static_cast<AvahiPublishFlags>(0), avahiService->getName().c_str(), kREG_TYPE, NULL, NULL, 1599, "local", r, NULL)) < 0) { if (ret == AVAHI_ERR_COLLISION){ /* A service name collision with a local service happened. Let's diff --git a/sd/source/ui/remotecontrol/DiscoveryService.cxx b/sd/source/ui/remotecontrol/DiscoveryService.cxx index 22aca47fd33f..94252f7f4603 100644 --- a/sd/source/ui/remotecontrol/DiscoveryService.cxx +++ b/sd/source/ui/remotecontrol/DiscoveryService.cxx @@ -27,6 +27,8 @@ #include <winsock2.h> #include <ws2tcpip.h> + + #include "WINNetworkService.hxx" typedef int socklen_t; #else #include <unistd.h> @@ -70,6 +72,10 @@ DiscoveryService::DiscoveryService() zService = new AvahiNetworkService(hostname); #endif +#ifdef WIN32 + zService = new WINNetworkService(); +#endif + if (zService) zService->setup(); @@ -111,9 +117,9 @@ DiscoveryService::DiscoveryService() DiscoveryService::~DiscoveryService() { #ifdef WNT - closesocket( mSocket ); + closesocket( mSocket ); #else - close( mSocket ); + close( mSocket ); #endif if (zService) diff --git a/sd/source/ui/remotecontrol/OSXNetworkService.mm b/sd/source/ui/remotecontrol/OSXNetworkService.mm index d7f3ebc3ba0b..892439951e0a 100644 --- a/sd/source/ui/remotecontrol/OSXNetworkService.mm +++ b/sd/source/ui/remotecontrol/OSXNetworkService.mm @@ -17,7 +17,7 @@ - (void) publishImpressRemoteServiceOnLocalNetworkWithName:(NSString *)sName { - netService = [[NSNetService alloc] initWithDomain:@"local" type:@"_impressremote._tcp" name:sName port:1599]; + netService = [[NSNetService alloc] initWithDomain:@"local" type:@kREG_TYPE name:sName port:1599]; if (netService != nil) { diff --git a/sd/source/ui/remotecontrol/WINNetworkService.cxx b/sd/source/ui/remotecontrol/WINNetworkService.cxx new file mode 100755 index 000000000000..341b01e2da0c --- /dev/null +++ b/sd/source/ui/remotecontrol/WINNetworkService.cxx @@ -0,0 +1,19 @@ +#include <string> +#include <iostream> +#include "WINNetworkService.hxx" +#include "mDNSResponder/dns_sd.h" + +void sd::WINNetworkService::setup() +{ + DNSServiceErrorType err = DNSServiceRegister(&client, 0, 0, NULL, kREG_TYPE, NULL, NULL, 1599, 1, "", NULL, this ); + + if ( err == 0 ) { + SAL_INFO("sd", "Windows bonjour service setup"); + } // Fail silently otherwise +} + +void sd::WINNetworkService::clear() +{ + DNSServiceRefDeallocate(client); + SAL_INFO("sd", "Windows mDNSResponder removed"); +}
\ No newline at end of file diff --git a/sd/source/ui/remotecontrol/WINNetworkService.hxx b/sd/source/ui/remotecontrol/WINNetworkService.hxx new file mode 100755 index 000000000000..bf59d7ae32e6 --- /dev/null +++ b/sd/source/ui/remotecontrol/WINNetworkService.hxx @@ -0,0 +1,26 @@ +#ifndef WINNETWORKSERVICE_HXX +#define WINNETWORKSERVICE_HXX + +#include <string> +#undef WB_LEFT +#undef WB_RIGHT +#include "mDNSResponder/dns_sd.h" +#include "ZeroconfService.hxx" + +namespace sd{ + class WINNetworkService : public ZeroconfService + { + private: + DNSServiceRef client; + + public: + WINNetworkService(const std::string& aname = "", unsigned int aport = 1599) + : ZeroconfService(aname, aport), client(0) {} + virtual ~WINNetworkService(){} + + void clear(); + void setup(); + + }; +} +#endif
\ No newline at end of file diff --git a/sd/source/ui/remotecontrol/ZeroconfService.hxx b/sd/source/ui/remotecontrol/ZeroconfService.hxx index 0418dd8c28e6..246efced5683 100644 --- a/sd/source/ui/remotecontrol/ZeroconfService.hxx +++ b/sd/source/ui/remotecontrol/ZeroconfService.hxx @@ -19,6 +19,8 @@ #define PORT_DISCOVERY 1598 #define BUFFER_SIZE 200 +#define kREG_TYPE "_impressremote._tcp" + #define CHARSET RTL_TEXTENCODING_UTF8 struct sockaddr_in; diff --git a/sd/source/ui/remotecontrol/mDNSResponder/CommonServices.h b/sd/source/ui/remotecontrol/mDNSResponder/CommonServices.h new file mode 100755 index 000000000000..8326ea0e0e16 --- /dev/null +++ b/sd/source/ui/remotecontrol/mDNSResponder/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/sd/source/ui/remotecontrol/mDNSResponder/DebugServices.c b/sd/source/ui/remotecontrol/mDNSResponder/DebugServices.c new file mode 100755 index 000000000000..3bcc42380d8c --- /dev/null +++ b/sd/source/ui/remotecontrol/mDNSResponder/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/sd/source/ui/remotecontrol/mDNSResponder/DebugServices.h b/sd/source/ui/remotecontrol/mDNSResponder/DebugServices.h new file mode 100755 index 000000000000..cb0ef4d1acae --- /dev/null +++ b/sd/source/ui/remotecontrol/mDNSResponder/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/sd/source/ui/remotecontrol/mDNSResponder/GenLinkedList.c b/sd/source/ui/remotecontrol/mDNSResponder/GenLinkedList.c new file mode 100755 index 000000000000..a36183d7a61e --- /dev/null +++ b/sd/source/ui/remotecontrol/mDNSResponder/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/sd/source/ui/remotecontrol/mDNSResponder/GenLinkedList.h b/sd/source/ui/remotecontrol/mDNSResponder/GenLinkedList.h new file mode 100755 index 000000000000..71d5638c03b9 --- /dev/null +++ b/sd/source/ui/remotecontrol/mDNSResponder/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/sd/source/ui/remotecontrol/mDNSResponder/dllmain.c b/sd/source/ui/remotecontrol/mDNSResponder/dllmain.c new file mode 100755 index 000000000000..ce3fce554037 --- /dev/null +++ b/sd/source/ui/remotecontrol/mDNSResponder/dllmain.c @@ -0,0 +1,113 @@ +/* -*- 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 <windows.h> +#include <DebugServices.h> +#include <stdlib.h> + +BOOL APIENTRY DllMain( HANDLE inModule, DWORD inReason, LPVOID inReserved ) +{ + (void) inModule; + (void) inReserved; + + switch( inReason ) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return( TRUE ); +} + + +BOOL +IsSystemServiceDisabled() +{ + ENUM_SERVICE_STATUS * lpService = NULL; + SC_HANDLE sc; + BOOL ret = FALSE; + BOOL ok; + DWORD bytesNeeded = 0; + DWORD srvCount; + DWORD resumeHandle = 0; + DWORD srvType; + DWORD srvState; + DWORD dwBytes = 0; + DWORD i; + OSStatus err; + + sc = OpenSCManager( NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE ); + err = translate_errno( sc, GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + srvType = SERVICE_WIN32; + srvState = SERVICE_STATE_ALL; + + for ( ;; ) + { + // Call EnumServicesStatus using the handle returned by OpenSCManager + + ok = EnumServicesStatus ( sc, srvType, srvState, lpService, dwBytes, &bytesNeeded, &srvCount, &resumeHandle ); + + if ( ok || ( GetLastError() != ERROR_MORE_DATA ) ) + { + break; + } + + if ( lpService ) + { + free( lpService ); + } + + dwBytes = bytesNeeded; + + lpService = ( ENUM_SERVICE_STATUS* ) malloc( dwBytes ); + require_action( lpService, exit, ret = FALSE ); + } + + err = translate_errno( ok, GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + for ( i = 0; i < srvCount; i++ ) + { + if ( strcmp( lpService[i].lpServiceName, "Bonjour Service" ) == 0 ) + { + if ( ( lpService[i].ServiceStatus.dwCurrentState == SERVICE_PAUSED ) || ( lpService[i].ServiceStatus.dwCurrentState == SERVICE_STOPPED ) ) + { + ret = TRUE; + } + + break; + } + } + +exit: + + if ( lpService ) + { + free( lpService ); + } + + if ( sc ) + { + CloseServiceHandle ( sc ); + } + + return ret; +} diff --git a/sd/source/ui/remotecontrol/mDNSResponder/dns_sd.h b/sd/source/ui/remotecontrol/mDNSResponder/dns_sd.h new file mode 100755 index 000000000000..eb65465a9132 --- /dev/null +++ b/sd/source/ui/remotecontrol/mDNSResponder/dns_sd.h @@ -0,0 +1,2492 @@ +/* -*- 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. + */ + + +/*! @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 3793700 + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef WIN32 +#define WIN32 +#endif +#ifndef _DEBUG +#define _DEBUG +#endif +#ifndef _WINDOWS +#define _WINDOWS +#endif +#ifndef _USRDLL +#define _USRDLL +#endif +#ifndef DEBUG +#define DEBUG 1 +#endif +#ifndef NOT_HAVE_SA_LEN +#define NOT_HAVE_SA_LEN +#endif +#ifndef MDNS_DEBUGMSGS +#define MDNS_DEBUGMSGS 0 +#endif +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef USE_TCP_LOOPBACK +#define USE_TCP_LOOPBACK +#endif +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE +#endif +#ifndef _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES +#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1 +#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) ... + */ +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, + /* Flag for signifying a "stronger" variant of an operation. + * Currently defined only for DNSServiceReconfirmRecord(), where it forces a record to + * be removed from the cache immediately, instead of querying for a few seconds before + * concluding that the record is no longer valid and then removing it. This flag should + * be used with caution because if a service browsing PTR record is indeed still valid + * on the network, forcing its removal will result in a user-interface flap -- the + * discovered service instance will disappear, and then re-appear moments later. + */ + + 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. + */ +}; + +/* Possible protocols for DNSServiceNATPortMappingCreate(). */ +enum +{ + kDNSServiceProtocol_IPv4 = 0x01, + kDNSServiceProtocol_IPv6 = 0x02, + /* 0x04 and 0x08 reserved for future internetwork protocols */ + + 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 NAT-PMP or UPnP */ + kDNSServiceErr_NATPortMappingDisabled = -65565, /* NAT supports 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, + * 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 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" + + +/********************************************************************************************* +* +* 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. It is + * not compatible with dns_service_discovery_ref objects defined in the legacy Mach-based + * DNSServiceDiscovery.h API. + * + * sdRef: A DNSServiceRef initialized by any of the DNSService calls. + * + */ + +void 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 + * + * 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. + * + * 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: Pass kDNSServiceFlagsForce to force immediate deletion of record, + * instead of after some number of reconfirmation queries have gone unanswered. + * + * 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 the NAT-PMP or the UPnP IGD + * protocol for this API to create a successful mapping. + * + * 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_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/sd/source/ui/remotecontrol/mDNSResponder/dnssd_clientlib.c b/sd/source/ui/remotecontrol/mDNSResponder/dnssd_clientlib.c new file mode 100755 index 000000000000..cca58853338c --- /dev/null +++ b/sd/source/ui/remotecontrol/mDNSResponder/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/sd/source/ui/remotecontrol/mDNSResponder/dnssd_clientstub.c b/sd/source/ui/remotecontrol/mDNSResponder/dnssd_clientstub.c new file mode 100755 index 000000000000..fb4ae9532e59 --- /dev/null +++ b/sd/source/ui/remotecontrol/mDNSResponder/dnssd_clientstub.c @@ -0,0 +1,2206 @@ +/* -*- 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> +#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 + +#define DNSSD_CLIENT_TIMEOUT 10 // In seconds + +#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); +} + +// Wait for daemon to write to socket +static int wait_for_daemon(dnssd_sock_t sock, int timeout) +{ +#ifndef WIN32 + // At this point the next operation (accept() or read()) on this socket may block for a few milliseconds waiting + // for the daemon to respond, but that's okay -- the daemon is a trusted service and we know if won't take more + // than a few milliseconds to respond. So we'll forego checking for readability of the socket. + (void) sock; + (void) timeout; +#else + // Windows on the other hand suffers from 3rd party software (primarily 3rd party firewall software) that + // interferes with proper functioning of the TCP protocol stack. Because of this and because we depend on TCP + // to communicate with the system service, we want to make sure that the next operation on this socket (accept() or + // read()) doesn't block indefinitely. + if (!gDaemonErr) + { + 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)) + { + syslog(LOG_WARNING, "dnssd_clientstub wait_for_daemon timed out"); + gDaemonErr = kDNSServiceErr_Timeout; + } + } +#endif + 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) +{ + #if APPLE_OSX_mDNSResponder + int NumTries = DNSSD_CLIENT_MAXTRIES; + #else + int NumTries = 0; + #endif + + 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)->primary) + { + syslog(LOG_WARNING, "dnssd_clientstub kDNSServiceFlagsShareConnection used with invalid DNSServiceRef %p %08X %08X", + (*ref), (*ref)->sockfd, (*ref)->validator); + *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'll wait a bit and try again. + // If, after four seconds, we still can't connect to the daemon, + // then we give up and return a failure code. + if (++NumTries < DNSSD_CLIENT_MAXTRIES) sleep(1); // Sleep a bit, then try again + else { 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)) + { + 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 block in accept for a few milliseconds waiting for the daemon to connect back to us, + // but that's okay -- the daemon is a trusted service and we know if won't take more than a few milliseconds to respond. + dnssd_sockaddr_t daddr; + dnssd_socklen_t len = sizeof(daddr); + if ((err = wait_for_daemon(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(sizeof(dnssd_sock_t))]; + + if (sdr->op == send_bpf) // Okay to use sdr->op when checking for 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_name = 0; + msg.msg_namelen = 0; + msg.msg_iov = &vec; + msg.msg_iovlen = 1; + msg.msg_control = cbuf; + msg.msg_controllen = CMSG_LEN(sizeof(dnssd_sock_t)); + msg.msg_flags = 0; + 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* blocking in read_all to get the four-byte error code. + // Otherwise, if the daemon closes our socket (or crashes), we block in read_all() forever + // because the socket is not closed (we still have an open reference to it ourselves). + 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 block in read_all for a few milliseconds waiting for the daemon to send us the error code, + // but that's okay -- the daemon is a trusted service and we know if won't take more than a few milliseconds to respond. + if (sdr->op == send_bpf) // Okay to use sdr->op when checking for op == send_bpf + err = kDNSServiceErr_NoError; + else if ((err = wait_for_daemon(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: + // 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; +} + +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; + } + } + ((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, hostname, sa, ttl, 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) + { + 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; +} + +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) + { + 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/sd/source/ui/remotecontrol/mDNSResponder/dnssd_ipc.c b/sd/source/ui/remotecontrol/mDNSResponder/dnssd_ipc.c new file mode 100755 index 000000000000..6059eb392c73 --- /dev/null +++ b/sd/source/ui/remotecontrol/mDNSResponder/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/sd/source/ui/remotecontrol/mDNSResponder/dnssd_ipc.h b/sd/source/ui/remotecontrol/mDNSResponder/dnssd_ipc.h new file mode 100755 index 000000000000..30ce0f96cff2 --- /dev/null +++ b/sd/source/ui/remotecontrol/mDNSResponder/dnssd_ipc.h @@ -0,0 +1,223 @@ +/* -*- 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 + + +// +// Common cross platform services +// +#if defined(WIN32) +# include <winsock2.h> +#endif + +#include "dns_sd.h" + +#if defined(WIN32) +# 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 + release_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 |