diff --git a/src/tools/mdnssd/CommonServices.h b/src/tools/mdnssd/CommonServices.h new file mode 100644 index 00000000000..1261f1d5ab3 --- /dev/null +++ b/src/tools/mdnssd/CommonServices.h @@ -0,0 +1,1518 @@ +/* -*- 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 + +// 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( __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 +#endif + +#if( ( macintosh || __MACH__ ) && !KERNEL ) + + #if( defined( __MWERKS__ ) ) + #if( __option( c9x ) ) + #include + #endif + #else + #include + #endif + + #include + + #if( __MACH__ ) + + // Mac OS X + + #include + #include + #include + #include + #include + #include + #include + #include + + #else + + // Classic Mac OS + + #include + #include + + #endif + +#elif( KERNEL ) + + // Mac OS X Kernel + + #include + + #include + #include + +#elif( TARGET_OS_LINUX ) + + // Linux + + #include + #include + +#elif( TARGET_OS_SOLARIS ) + + // Solaris + + #include + + #include + #include + + #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 + #endif + + #include + + #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 + #include + #include + + #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_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 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/src/tools/mdnssd/DebugServices.c b/src/tools/mdnssd/DebugServices.c new file mode 100644 index 00000000000..647329628ce --- /dev/null +++ b/src/tools/mdnssd/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 + #include + #include +#endif + +#include "CommonServices.h" + +#include "DebugServices.h" + +#if( DEBUG ) + +#if( TARGET_OS_VXWORKS ) + #include "intLib.h" +#endif + +#if( TARGET_OS_WIN32 ) + #include + + #if( !TARGET_OS_WINDOWS_CE ) + #include + #include + #endif +#endif + +#if( DEBUG_IDEBUG_ENABLED && TARGET_API_MAC_OSX_KERNEL ) + #include +#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[] = "<>"; 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[] = "<>"; 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[] = "<>"; 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), "<>", *a); break; } + if (s + *a >= &mDNS_VACB[254]) { s += DebugSNPrintF(s, mDNS_VACB_Remain(s), "<>"); 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[] = "<>"; 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: " ); + } + } + 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), "<>", 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= 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 = ""; + } + } + 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/src/tools/mdnssd/EventLog.mc b/src/tools/mdnssd/EventLog.mc new file mode 100644 index 00000000000..248e6c1a18f --- /dev/null +++ b/src/tools/mdnssd/EventLog.mc @@ -0,0 +1,11 @@ +MessageIdTypedef=WORD +LanguageNames=(English=0x409:MSG00409) + +MessageId=100 +SymbolicName=MDNSRESPONDER_LOG +Severity=Success +Facility=Application +Language=English +%1 +. + diff --git a/src/tools/mdnssd/Firewall.cpp b/src/tools/mdnssd/Firewall.cpp new file mode 100644 index 00000000000..c7c96d09f8c --- /dev/null +++ b/src/tools/mdnssd/Firewall.cpp @@ -0,0 +1,484 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Doesn't compile correctly with latest Platform SDK + +#if !defined(_WIN32_DCOM) +# define _WIN32_DCOM +#endif + + +#include "Firewall.h" +#include +#include +#include +#include +#include + + +static const int kMaxTries = 30; +static const int kRetrySleepPeriod = 1 * 1000; // 1 second + + +static OSStatus +mDNSFirewallInitialize(OUT INetFwProfile ** fwProfile) +{ + INetFwMgr * fwMgr = NULL; + INetFwPolicy * fwPolicy = NULL; + int numRetries = 0; + HRESULT err = kNoErr; + + _ASSERT(fwProfile != NULL); + + *fwProfile = NULL; + + // Use COM to get a reference to the firewall settings manager. This + // call will fail on anything other than XP SP2 + + err = CoCreateInstance( __uuidof(NetFwMgr), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwMgr), (void**)&fwMgr ); + require(SUCCEEDED(err) && ( fwMgr != NULL ), exit); + + // Use the reference to get the local firewall policy + + err = fwMgr->get_LocalPolicy(&fwPolicy); + require(SUCCEEDED(err) && ( fwPolicy != NULL ), exit); + + // Use the reference to get the extant profile. Empirical evidence + // suggests that there is the potential for a race condition when a system + // service whose startup type is automatic calls this method. + // This is true even when the service declares itself to be dependent + // on the firewall service. Re-trying the method will succeed within + // a few seconds. + + do + { + err = fwPolicy->get_CurrentProfile(fwProfile); + + if (err) + { + Sleep(kRetrySleepPeriod); + } + } + while (err && (numRetries++ < kMaxTries)); + + require(SUCCEEDED(err), exit); + + err = kNoErr; + +exit: + + // Release temporary COM objects + + if (fwPolicy != NULL) + { + fwPolicy->Release(); + } + + if (fwMgr != NULL) + { + fwMgr->Release(); + } + + return err; +} + + +static void +mDNSFirewallCleanup + ( + IN INetFwProfile * fwProfile + ) +{ + // Call Release on the COM reference. + + if (fwProfile != NULL) + { + fwProfile->Release(); + } +} + + +static OSStatus +mDNSFirewallAppIsEnabled + ( + IN INetFwProfile * fwProfile, + IN const wchar_t * fwProcessImageFileName, + OUT BOOL * fwAppEnabled + ) +{ + BSTR fwBstrProcessImageFileName = NULL; + VARIANT_BOOL fwEnabled; + INetFwAuthorizedApplication * fwApp = NULL; + INetFwAuthorizedApplications* fwApps = NULL; + OSStatus err = kNoErr; + + _ASSERT(fwProfile != NULL); + _ASSERT(fwProcessImageFileName != NULL); + _ASSERT(fwAppEnabled != NULL); + + *fwAppEnabled = FALSE; + + // Get the list of authorized applications + + err = fwProfile->get_AuthorizedApplications(&fwApps); + require(SUCCEEDED(err) && ( fwApps != NULL ), exit); + + fwBstrProcessImageFileName = SysAllocString(fwProcessImageFileName); + require_action( ( fwProcessImageFileName != NULL ) && ( SysStringLen(fwBstrProcessImageFileName) > 0 ), exit, err = kNoMemoryErr); + + // Look for us + + err = fwApps->Item(fwBstrProcessImageFileName, &fwApp); + + if (SUCCEEDED(err) && ( fwApp != NULL ) ) + { + // It's listed, but is it enabled? + + err = fwApp->get_Enabled(&fwEnabled); + require(SUCCEEDED(err), exit); + + if (fwEnabled != VARIANT_FALSE) + { + // Yes, it's enabled + + *fwAppEnabled = TRUE; + } + } + + err = kNoErr; + +exit: + + // Deallocate the BSTR + + if ( fwBstrProcessImageFileName != NULL ) + { + SysFreeString(fwBstrProcessImageFileName); + } + + // Release the COM objects + + if (fwApp != NULL) + { + fwApp->Release(); + } + + if (fwApps != NULL) + { + fwApps->Release(); + } + + return err; +} + + +static OSStatus +mDNSFirewallAddApp + ( + IN INetFwProfile * fwProfile, + IN const wchar_t * fwProcessImageFileName, + IN const wchar_t * fwName + ) +{ + BOOL fwAppEnabled; + BSTR fwBstrName = NULL; + BSTR fwBstrProcessImageFileName = NULL; + INetFwAuthorizedApplication * fwApp = NULL; + INetFwAuthorizedApplications* fwApps = NULL; + OSStatus err = S_OK; + + _ASSERT(fwProfile != NULL); + _ASSERT(fwProcessImageFileName != NULL); + _ASSERT(fwName != NULL); + + // First check to see if the application is already authorized. + err = mDNSFirewallAppIsEnabled( fwProfile, fwProcessImageFileName, &fwAppEnabled ); + require_noerr(err, exit); + + // Only add the application if it isn't enabled + + if (!fwAppEnabled) + { + // Get the list of authorized applications + + err = fwProfile->get_AuthorizedApplications(&fwApps); + require(SUCCEEDED(err) && ( fwApps != NULL ), exit); + + // Create an instance of an authorized application. + + err = CoCreateInstance( __uuidof(NetFwAuthorizedApplication), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwAuthorizedApplication), (void**)&fwApp ); + require(SUCCEEDED(err) && ( fwApp != NULL ), exit); + + fwBstrProcessImageFileName = SysAllocString(fwProcessImageFileName); + require_action(( fwProcessImageFileName != NULL ) && ( SysStringLen(fwBstrProcessImageFileName) > 0 ), exit, err = kNoMemoryErr); + + // Set the executable file name + + err = fwApp->put_ProcessImageFileName(fwBstrProcessImageFileName); + require(SUCCEEDED(err), exit); + + fwBstrName = SysAllocString(fwName); + require_action( ( fwBstrName != NULL ) && ( SysStringLen(fwBstrName) > 0 ), exit, err = kNoMemoryErr); + + // Set the friendly name + + err = fwApp->put_Name(fwBstrName); + require(SUCCEEDED(err), exit); + + // Now add the application + + err = fwApps->Add(fwApp); + require(SUCCEEDED(err), exit); + } + + err = kNoErr; + +exit: + + // Deallocate the BSTR objects + + if ( fwBstrName != NULL ) + { + SysFreeString(fwBstrName); + } + + if ( fwBstrProcessImageFileName != NULL ) + { + SysFreeString(fwBstrProcessImageFileName); + } + + // Release the COM objects + + if (fwApp != NULL) + { + fwApp->Release(); + } + + if (fwApps != NULL) + { + fwApps->Release(); + } + + return err; +} + + + + + +static OSStatus + +mDNSFirewallIsFileAndPrintSharingEnabled + + ( + + IN INetFwProfile * fwProfile, + + OUT BOOL * fwServiceEnabled + + ) + +{ + + VARIANT_BOOL fwEnabled; + + INetFwService* fwService = NULL; + + INetFwServices* fwServices = NULL; + + OSStatus err = S_OK; + + + + _ASSERT(fwProfile != NULL); + + _ASSERT(fwServiceEnabled != NULL); + + + + *fwServiceEnabled = FALSE; + + + + // Retrieve the globally open ports collection. + + err = fwProfile->get_Services(&fwServices); + + require( SUCCEEDED( err ), exit ); + + + + // Attempt to retrieve the globally open port. + + err = fwServices->Item(NET_FW_SERVICE_FILE_AND_PRINT, &fwService); + + require( SUCCEEDED( err ), exit ); + + + + // Find out if the globally open port is enabled. + + err = fwService->get_Enabled(&fwEnabled); + + require( SUCCEEDED( err ), exit ); + + if (fwEnabled != VARIANT_FALSE) + + { + + *fwServiceEnabled = TRUE; + + } + + + +exit: + + + + // Release the globally open port. + + if (fwService != NULL) + + { + + fwService->Release(); + + } + + + + // Release the globally open ports collection. + + if (fwServices != NULL) + + { + + fwServices->Release(); + + } + + + + return err; + +} + + +OSStatus +mDNSAddToFirewall + ( + LPWSTR executable, + LPWSTR name + ) +{ + INetFwProfile * fwProfile = NULL; + HRESULT comInit = E_FAIL; + OSStatus err = kNoErr; + + // Initialize COM. + + comInit = CoInitializeEx( 0, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE ); + + // Ignore this case. RPC_E_CHANGED_MODE means that COM has already been + // initialized with a different mode. + + if (comInit != RPC_E_CHANGED_MODE) + { + err = comInit; + require(SUCCEEDED(err), exit); + } + + // Connect to the firewall + + err = mDNSFirewallInitialize(&fwProfile); + require( SUCCEEDED( err ) && ( fwProfile != NULL ), exit); + + // Add us to the list of exempt programs + + err = mDNSFirewallAddApp( fwProfile, executable, name ); + require_noerr(err, exit); + +exit: + + // Disconnect from the firewall + + if ( fwProfile != NULL ) + { + mDNSFirewallCleanup(fwProfile); + } + + // De-initialize COM + + if (SUCCEEDED(comInit)) + { + CoUninitialize(); + } + + return err; +} + + +BOOL +mDNSIsFileAndPrintSharingEnabled( BOOL * retry ) +{ + INetFwProfile * fwProfile = NULL; + HRESULT comInit = E_FAIL; + BOOL enabled = FALSE; + OSStatus err = kNoErr; + + // Initialize COM. + + *retry = FALSE; + comInit = CoInitializeEx( 0, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE ); + + // Ignore this case. RPC_E_CHANGED_MODE means that COM has already been + // initialized with a different mode. + + if (comInit != RPC_E_CHANGED_MODE) + { + *retry = TRUE; + err = comInit; + require(SUCCEEDED(err), exit); + } + + // Connect to the firewall + + err = mDNSFirewallInitialize(&fwProfile); + require( SUCCEEDED( err ) && ( fwProfile != NULL ), exit); + + err = mDNSFirewallIsFileAndPrintSharingEnabled( fwProfile, &enabled ); + require_noerr( err, exit ); + +exit: + + // Disconnect from the firewall + + if ( fwProfile != NULL ) + { + mDNSFirewallCleanup(fwProfile); + } + + // De-initialize COM + + if (SUCCEEDED(comInit)) + { + CoUninitialize(); + } + + return enabled; +} diff --git a/src/tools/mdnssd/Firewall.h b/src/tools/mdnssd/Firewall.h new file mode 100644 index 00000000000..3d7d532d172 --- /dev/null +++ b/src/tools/mdnssd/Firewall.h @@ -0,0 +1,79 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + + +#ifndef _Firewall_h + +#define _Firewall_h + + + + + +#include "CommonServices.h" + +#include "DebugServices.h" + + + + + +#if defined(__cplusplus) + +extern "C" + +{ + +#endif + + + + + +OSStatus + +mDNSAddToFirewall + + ( + + LPWSTR executable, + + LPWSTR name + + ); + + +BOOL +mDNSIsFileAndPrintSharingEnabled( BOOL * retry ); + + + + + +#if defined(__cplusplus) + +} + +#endif + + + + + +#endif + diff --git a/src/tools/mdnssd/LegacyNATTraversal.c b/src/tools/mdnssd/LegacyNATTraversal.c new file mode 100644 index 00000000000..115719e0334 --- /dev/null +++ b/src/tools/mdnssd/LegacyNATTraversal.c @@ -0,0 +1,906 @@ +/* -*- 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. + */ + +#ifdef _LEGACY_NAT_TRAVERSAL_ + +#include "stdlib.h" // For strtol() +#include "string.h" // For strlcpy(), For strncpy(), strncasecmp() + +#if defined( WIN32 ) +# include +# include +# define strcasecmp _stricmp +# define strncasecmp _strnicmp +# define mDNSASLLog( UUID, SUBDOMAIN, RESULT, SIGNATURE, FORMAT, ... ) ; + +static int +inet_pton( int family, const char * addr, void * dst ) + { + struct sockaddr_storage ss; + int sslen = sizeof( ss ); + + ZeroMemory( &ss, sizeof( ss ) ); + ss.ss_family = (ADDRESS_FAMILY)family; + + if ( WSAStringToAddressA( (LPSTR)addr, family, NULL, ( struct sockaddr* ) &ss, &sslen ) == 0 ) + { + if ( family == AF_INET ) { memcpy( dst, &( ( struct sockaddr_in* ) &ss)->sin_addr, sizeof( IN_ADDR ) ); return 1; } + else if ( family == AF_INET6 ) { memcpy( dst, &( ( struct sockaddr_in6* ) &ss)->sin6_addr, sizeof( IN6_ADDR ) ); return 1; } + else return 0; + } + else return 0; + } +#else +# include // For inet_pton() +#endif + +#include "mDNSEmbeddedAPI.h" +#include "uDNS.h" // For natTraversalHandleAddressReply() etc. + +// used to format SOAP port mapping arguments +typedef struct Property_struct + { + char *name; + char *type; + char *value; + } Property; + +// All of the text parsing in this file is intentionally transparent so that we know exactly +// what's being done to the text, with an eye towards preventing security problems. + +// This is an evolving list of useful acronyms to know. Please add to it at will. +// ST Service Type +// NT Notification Type +// USN Unique Service Name +// UDN Unique Device Name +// UUID Universally Unique Identifier +// URN/urn Universal Resource Name + +// Forward declaration because of circular reference: +// SendPortMapRequest -> SendSOAPMsgControlAction -> MakeTCPConnection -> tcpConnectionCallback -> handleLNTPortMappingResponse +// In the event of a port conflict, handleLNTPortMappingResponse then increments tcpInfo->retries and calls back to SendPortMapRequest to try again +mDNSlocal mStatus SendPortMapRequest(mDNS *m, NATTraversalInfo *n); + +#define RequestedPortNum(n) (mDNSVal16(mDNSIPPortIsZero((n)->RequestedPort) ? (n)->IntPort : (n)->RequestedPort) + (mDNSu16)(n)->tcpInfo.retries) + +// Note that this function assumes src is already NULL terminated +mDNSlocal void AllocAndCopy(mDNSu8 **const dst, const mDNSu8 *const src) + { + if (src == mDNSNULL) return; + if ((*dst = mDNSPlatformMemAllocate((mDNSu32)strlen((char*)src) + 1)) == mDNSNULL) + { LogMsg("AllocAndCopy: can't allocate string"); return; } + strcpy((char*)*dst, (char*)src); + } + +// This function does a simple parse of an HTTP URL that may include a hostname, port, and path +// If found in the URL, addressAndPort and path out params will point to newly allocated space (and will leak if they were previously pointing at allocated space) +mDNSlocal mStatus ParseHttpUrl(const mDNSu8 *ptr, const mDNSu8 *const end, mDNSu8 **const addressAndPort, mDNSIPPort *const port, mDNSu8 **const path) + { + // if the data begins with "http://", we assume there is a hostname and possibly a port number + if (end - ptr >= 7 && strncasecmp((char*)ptr, "http://", 7) == 0) + { + int i; + const mDNSu8 *stop = end; + const mDNSu8 *addrPtr = mDNSNULL; + + ptr += 7; //skip over "http://" + if (ptr >= end) { LogInfo("ParseHttpUrl: past end of buffer parsing host:port"); return mStatus_BadParamErr; } + + // find the end of the host:port + addrPtr = ptr; + for (i = 0; addrPtr && addrPtr != end; i++, addrPtr++) if (*addrPtr == '/') break; + + // allocate the buffer (len i+1 so we have space to terminate the string) + if ((*addressAndPort = mDNSPlatformMemAllocate(i+1)) == mDNSNULL) + { LogMsg("ParseHttpUrl: can't allocate address string"); return mStatus_NoMemoryErr; } + strncpy((char*)*addressAndPort, (char*)ptr, i); + (*addressAndPort)[i] = '\0'; + + // find the port number in the string, by looking backwards for the ':' + stop = ptr; // can't go back farther than the original start + ptr = addrPtr; // move ptr to the path part + + for (addrPtr--; addrPtr>stop; addrPtr--) + { + if (*addrPtr == ':') + { + addrPtr++; // skip over ':' + *port = mDNSOpaque16fromIntVal((mDNSu16)strtol((char*)addrPtr, mDNSNULL, 10)); // store it properly converted + break; + } + } + } + + // ptr should now point to the first character we haven't yet processed + // everything that remains is the path + if (path && ptr < end) + { + if ((*path = mDNSPlatformMemAllocate((mDNSu32)(end - ptr) + 1)) == mDNSNULL) + { LogMsg("ParseHttpUrl: can't mDNSPlatformMemAllocate path"); return mStatus_NoMemoryErr; } + strncpy((char*)*path, (char*)ptr, end - ptr); + (*path)[end - ptr] = '\0'; + } + + return mStatus_NoError; + } + +enum + { + HTTPCode_NeedMoreData = -1, // No code found in stream + HTTPCode_Other = -2, // Valid code other than those below found in stream + HTTPCode_Bad = -3, + HTTPCode_200 = 200, + HTTPCode_404 = 404, + HTTPCode_500 = 500, + }; + +mDNSlocal mDNSs16 ParseHTTPResponseCode(const mDNSu8 **const data, const mDNSu8 *const end) + { + const mDNSu8 *ptr = *data; + const mDNSu8 *code; + + if (end - ptr < 5) return HTTPCode_NeedMoreData; + if (strncasecmp((char*)ptr, "HTTP/", 5) != 0) return HTTPCode_Bad; + ptr += 5; + // should we care about the HTTP protocol version? + + // look for first space, which must come before first LF + while (ptr && ptr != end) + { + if (*ptr == '\n') return HTTPCode_Bad; + if (*ptr == ' ') break; + ptr++; + } + if (ptr == end) return HTTPCode_NeedMoreData; + ptr++; + + if (end - ptr < 3) return HTTPCode_NeedMoreData; + + code = ptr; + ptr += 3; + while (ptr && ptr != end) + { + if (*ptr == '\n') break; + ptr++; + } + if (ptr == end) return HTTPCode_NeedMoreData; + *data = ++ptr; + + if (memcmp((char*)code, "200", 3) == 0) return HTTPCode_200; + if (memcmp((char*)code, "404", 3) == 0) return HTTPCode_404; + if (memcmp((char*)code, "500", 3) == 0) return HTTPCode_500; + + LogInfo("ParseHTTPResponseCode found unexpected result code: %c%c%c", code[0], code[1], code[2]); + return HTTPCode_Other; + } + +// This function parses the xml body of the device description response from the router. Basically, we look to +// make sure this is a response referencing a service we care about (WANIPConnection or WANPPPConnection), +// look for the "controlURL" header immediately following, and copy the addressing and URL info we need +mDNSlocal void handleLNTDeviceDescriptionResponse(tcpLNTInfo *tcpInfo) + { + mDNS *m = tcpInfo->m; + const mDNSu8 *ptr = tcpInfo->Reply; + const mDNSu8 *end = tcpInfo->Reply + tcpInfo->nread; + const mDNSu8 *stop; + mDNSs16 http_result; + + if (!mDNSIPPortIsZero(m->UPnPSOAPPort)) return; // already have the info we need + + http_result = ParseHTTPResponseCode(&ptr, end); // Note: modifies ptr + if (http_result == HTTPCode_404) LNT_ClearState(m); + if (http_result != HTTPCode_200) + { + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "noop", "HTTP Result", "HTTP code: %d", http_result); + return; + } + + // Always reset our flag to use WANIPConnection. We'll use WANPPPConnection if we find it and don't find WANIPConnection. + m->UPnPWANPPPConnection = mDNSfalse; + + // find either service we care about + while (ptr && ptr < end) + { + if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANIPConnection:1", 17) == 0)) break; + ptr++; + } + if (ptr == end) + { + ptr = tcpInfo->Reply; + while (ptr && ptr < end) + { + if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANPPPConnection:1", 18) == 0)) + { + m->UPnPWANPPPConnection = mDNStrue; + break; + } + ptr++; + } + } + if (ptr == mDNSNULL || ptr == end) { LogInfo("handleLNTDeviceDescriptionResponse: didn't find WANIPConnection:1 or WANPPPConnection:1 string"); return; } + + // find "controlURL", starting from where we left off + while (ptr && ptr < end) + { + if ((*ptr & 0xDF) == 'C' && (strncasecmp((char*)ptr, "controlURL", 10) == 0)) break; // find the first 'c'; is this controlURL? if not, keep looking + ptr++; + } + if (ptr == mDNSNULL || ptr == end) { LogInfo("handleLNTDeviceDescriptionResponse: didn't find controlURL string"); return; } + ptr += 11; // skip over "controlURL>" + if (ptr >= end) { LogInfo("handleLNTDeviceDescriptionResponse: past end of buffer and no body!"); return; } // check ptr again in case we skipped over the end of the buffer + + // find the end of the controlURL element + for (stop = ptr; stop < end; stop++) { if (*stop == '<') { end = stop; break; } } + + // fill in default port + m->UPnPSOAPPort = m->UPnPRouterPort; + + // free string pointers and set to NULL + if (m->UPnPSOAPAddressString != mDNSNULL) + { + mDNSPlatformMemFree(m->UPnPSOAPAddressString); + m->UPnPSOAPAddressString = mDNSNULL; + } + if (m->UPnPSOAPURL != mDNSNULL) + { + mDNSPlatformMemFree(m->UPnPSOAPURL); + m->UPnPSOAPURL = mDNSNULL; + } + + if (ParseHttpUrl(ptr, end, &m->UPnPSOAPAddressString, &m->UPnPSOAPPort, &m->UPnPSOAPURL) != mStatus_NoError) return; + // the SOAPURL should look something like "/uuid:0013-108c-4b3f0000f3dc" + + if (m->UPnPSOAPAddressString == mDNSNULL) + { + ptr = tcpInfo->Reply; + while (ptr && ptr < end) + { + if ((*ptr & 0xDF) == 'U' && (strncasecmp((char*)ptr, "URLBase", 7) == 0)) break; + ptr++; + } + + if (ptr < end) // found URLBase + { + LogInfo("handleLNTDeviceDescriptionResponse: found URLBase"); + ptr += 8; // skip over "URLBase>" + // find the end of the URLBase element + for (stop = ptr; stop < end; stop++) { if (*stop == '<') { end = stop; break; } } + if (ParseHttpUrl(ptr, end, &m->UPnPSOAPAddressString, &m->UPnPSOAPPort, mDNSNULL) != mStatus_NoError) + { + LogInfo("handleLNTDeviceDescriptionResponse: failed to parse URLBase"); + } + } + + // if all else fails, use the router address string + if (m->UPnPSOAPAddressString == mDNSNULL) AllocAndCopy(&m->UPnPSOAPAddressString, m->UPnPRouterAddressString); + } + if (m->UPnPSOAPAddressString == mDNSNULL) LogMsg("handleLNTDeviceDescriptionResponse: UPnPSOAPAddressString is NULL"); + else LogInfo("handleLNTDeviceDescriptionResponse: SOAP address string [%s]", m->UPnPSOAPAddressString); + + if (m->UPnPSOAPURL == mDNSNULL) AllocAndCopy(&m->UPnPSOAPURL, m->UPnPRouterURL); + if (m->UPnPSOAPURL == mDNSNULL) LogMsg("handleLNTDeviceDescriptionResponse: UPnPSOAPURL is NULL"); + else LogInfo("handleLNTDeviceDescriptionResponse: SOAP URL [%s]", m->UPnPSOAPURL); + } + +mDNSlocal void handleLNTGetExternalAddressResponse(tcpLNTInfo *tcpInfo) + { + mDNS *m = tcpInfo->m; + mDNSu16 err = NATErr_None; + mDNSv4Addr ExtAddr; + const mDNSu8 *ptr = tcpInfo->Reply; + const mDNSu8 *end = tcpInfo->Reply + tcpInfo->nread; + mDNSu8 *addrend; + static char tagname[20] = { 'N','e','w','E','x','t','e','r','n','a','l','I','P','A','d','d','r','e','s','s' }; + // Array NOT including a terminating nul + +// LogInfo("handleLNTGetExternalAddressResponse: %s", ptr); + + mDNSs16 http_result = ParseHTTPResponseCode(&ptr, end); // Note: modifies ptr + if (http_result == HTTPCode_404) LNT_ClearState(m); + if (http_result != HTTPCode_200) + { + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest", "noop", "HTTP Result", "HTTP code: %d", http_result); + return; + } + + while (ptr < end && strncasecmp((char*)ptr, tagname, sizeof(tagname))) ptr++; + ptr += sizeof(tagname); // Skip over "NewExternalIPAddress" + while (ptr < end && *ptr != '>') ptr++; + ptr += 1; // Skip over ">" + + // Find the end of the address and terminate the string so inet_pton() can convert it + // (Might be better to copy this to a local string here -- this is overwriting tcpInfo->Reply in-place + addrend = (mDNSu8*)ptr; + while (addrend < end && (mDNSIsDigit(*addrend) || *addrend == '.')) addrend++; + if (addrend >= end) return; + *addrend = 0; + + if (inet_pton(AF_INET, (char*)ptr, &ExtAddr) <= 0) + { + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest", "noop", "inet_pton", ""); + LogMsg("handleLNTGetExternalAddressResponse: Router returned bad address %s", ptr); + err = NATErr_NetFail; + ExtAddr = zerov4Addr; + } + if (!err) LogInfo("handleLNTGetExternalAddressResponse: External IP address is %.4a", &ExtAddr); + + natTraversalHandleAddressReply(m, err, ExtAddr); + } + +mDNSlocal void handleLNTPortMappingResponse(tcpLNTInfo *tcpInfo) + { + mDNS *m = tcpInfo->m; + mDNSIPPort extport = zeroIPPort; + const mDNSu8 *ptr = tcpInfo->Reply; + const mDNSu8 *const end = tcpInfo->Reply + tcpInfo->nread; + NATTraversalInfo *natInfo; + mDNSs16 http_result; + + for (natInfo = m->NATTraversals; natInfo; natInfo=natInfo->next) { if (natInfo == tcpInfo->parentNATInfo) break; } + + if (!natInfo) { LogInfo("handleLNTPortMappingResponse: can't find matching tcpInfo in NATTraversals!"); return; } + + http_result = ParseHTTPResponseCode(&ptr, end); // Note: modifies ptr + if (http_result == HTTPCode_200) + { + LogInfo("handleLNTPortMappingResponse: got a valid response, sending reply to natTraversalHandlePortMapReply(internal %d external %d retries %d)", + mDNSVal16(natInfo->IntPort), RequestedPortNum(natInfo), tcpInfo->retries); + + // Make sure to compute extport *before* we zero tcpInfo->retries + extport = mDNSOpaque16fromIntVal(RequestedPortNum(natInfo)); + tcpInfo->retries = 0; + natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, mStatus_NoError, extport, NATMAP_DEFAULT_LEASE); + } + else if (http_result == HTTPCode_500) + { + while (ptr && ptr != end) + { + if (((*ptr & 0xDF) == 'C' && end - ptr >= 8 && strncasecmp((char*)ptr, "Conflict", 8) == 0) || + (*ptr == '>' && end - ptr >= 15 && strncasecmp((char*)ptr, ">718retries < 100) + { + tcpInfo->retries++; SendPortMapRequest(tcpInfo->m, natInfo); + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "Conflict", "Retry %d", tcpInfo->retries); + } + else + { + LogMsg("handleLNTPortMappingResponse too many conflict retries %d %d", mDNSVal16(natInfo->IntPort), mDNSVal16(natInfo->RequestedPort)); + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "Conflict - too many retries", "Retries: %d", tcpInfo->retries); + natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0); + } + return; + } + ptr++; + } + } + else if (http_result == HTTPCode_Bad) LogMsg("handleLNTPortMappingResponse got data that was not a valid HTTP response"); + else if (http_result == HTTPCode_Other) LogMsg("handleLNTPortMappingResponse got unexpected response code"); + else if (http_result == HTTPCode_404) LNT_ClearState(m); + if (http_result != HTTPCode_200 && http_result != HTTPCode_500) + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "HTTP Result", "HTTP code: %d", http_result); + } + +mDNSlocal void DisposeInfoFromUnmapList(mDNS *m, tcpLNTInfo *tcpInfo) + { + tcpLNTInfo **ptr = &m->tcpInfoUnmapList; + while (*ptr && *ptr != tcpInfo) ptr = &(*ptr)->next; + if (*ptr) { *ptr = (*ptr)->next; mDNSPlatformMemFree(tcpInfo); } // If we found it, cut it from our list and free the memory + } + +mDNSlocal void tcpConnectionCallback(TCPSocket *sock, void *context, mDNSBool ConnectionEstablished, mStatus err) + { + mStatus status = mStatus_NoError; + tcpLNTInfo *tcpInfo = (tcpLNTInfo *)context; + mDNSBool closed = mDNSfalse; + long n = 0; + long nsent = 0; + + if (tcpInfo == mDNSNULL) { LogInfo("tcpConnectionCallback: no tcpInfo context"); status = mStatus_Invalid; goto exit; } + + // The handlers below expect to be called with the lock held + mDNS_Lock(tcpInfo->m); + + if (err) { LogInfo("tcpConnectionCallback: received error"); goto exit; } + + if (ConnectionEstablished) // connection is established - send the message + { + LogInfo("tcpConnectionCallback: connection established, sending message"); + nsent = mDNSPlatformWriteTCP(sock, (char*)tcpInfo->Request, tcpInfo->requestLen); + if (nsent != (long)tcpInfo->requestLen) { LogMsg("tcpConnectionCallback: error writing"); status = mStatus_UnknownErr; goto exit; } + } + else + { + n = mDNSPlatformReadTCP(sock, (char*)tcpInfo->Reply + tcpInfo->nread, tcpInfo->replyLen - tcpInfo->nread, &closed); + LogInfo("tcpConnectionCallback: mDNSPlatformReadTCP read %d bytes", n); + + if (n < 0) { LogInfo("tcpConnectionCallback - read returned %d", n); status = mStatus_ConnFailed; goto exit; } + else if (closed) { LogInfo("tcpConnectionCallback: socket closed by remote end %d", tcpInfo->nread); status = mStatus_ConnFailed; goto exit; } + + tcpInfo->nread += n; + LogInfo("tcpConnectionCallback tcpInfo->nread %d", tcpInfo->nread); + if (tcpInfo->nread > LNT_MAXBUFSIZE) + { + LogInfo("result truncated..."); + tcpInfo->nread = LNT_MAXBUFSIZE; + } + + switch (tcpInfo->op) + { + case LNTDiscoveryOp: handleLNTDeviceDescriptionResponse (tcpInfo); break; + case LNTExternalAddrOp: handleLNTGetExternalAddressResponse(tcpInfo); break; + case LNTPortMapOp: handleLNTPortMappingResponse (tcpInfo); break; + case LNTPortMapDeleteOp: status = mStatus_ConfigChanged; break; + default: LogMsg("tcpConnectionCallback: bad tcp operation! %d", tcpInfo->op); status = mStatus_Invalid; break; + } + } +exit: + if (err || status) + { + mDNS *m = tcpInfo->m; + switch (tcpInfo->op) + { + case LNTDiscoveryOp: if (m->UPnPSOAPAddressString == mDNSNULL) + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "failure", "SOAP Address", ""); + if (m->UPnPSOAPURL == mDNSNULL) + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "failure", "SOAP path", ""); + if (m->UPnPSOAPAddressString && m->UPnPSOAPURL) + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "success", "success", ""); + break; + case LNTExternalAddrOp: mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest", + mDNSIPv4AddressIsZero(m->ExternalAddress) ? "failure" : "success", + mDNSIPv4AddressIsZero(m->ExternalAddress) ? "failure" : "success", ""); + break; + case LNTPortMapOp: if (tcpInfo->parentNATInfo) + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", (tcpInfo->parentNATInfo->Result) ? "failure" : "success", + (tcpInfo->parentNATInfo->Result) ? "failure" : "success", "Result: %d", tcpInfo->parentNATInfo->Result); + break; + case LNTPortMapDeleteOp: break; + default: break; + } + + mDNSPlatformTCPCloseConnection(tcpInfo->sock); + tcpInfo->sock = mDNSNULL; + if (tcpInfo->Request) { mDNSPlatformMemFree(tcpInfo->Request); tcpInfo->Request = mDNSNULL; } + if (tcpInfo->Reply ) { mDNSPlatformMemFree(tcpInfo->Reply); tcpInfo->Reply = mDNSNULL; } + } + + if (tcpInfo) mDNS_Unlock(tcpInfo->m); + + if (status == mStatus_ConfigChanged) DisposeInfoFromUnmapList(tcpInfo->m, tcpInfo); + } + +mDNSlocal mStatus MakeTCPConnection(mDNS *const m, tcpLNTInfo *info, const mDNSAddr *const Addr, const mDNSIPPort Port, LNTOp_t op) + { + mStatus err = mStatus_NoError; + mDNSIPPort srcport = zeroIPPort; + + if (mDNSIPv4AddressIsZero(Addr->ip.v4) || mDNSIPPortIsZero(Port)) + { LogMsg("LNT MakeTCPConnection: bad address/port %#a:%d", Addr, mDNSVal16(Port)); return(mStatus_Invalid); } + info->m = m; + info->Address = *Addr; + info->Port = Port; + info->op = op; + info->nread = 0; + info->replyLen = LNT_MAXBUFSIZE; + if (info->Reply != mDNSNULL) mDNSPlatformMemZero(info->Reply, LNT_MAXBUFSIZE); // reuse previously allocated buffer + else if ((info->Reply = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE)) == mDNSNULL) { LogInfo("can't allocate reply buffer"); return (mStatus_NoMemoryErr); } + + if (info->sock) { LogInfo("MakeTCPConnection: closing previous open connection"); mDNSPlatformTCPCloseConnection(info->sock); info->sock = mDNSNULL; } + info->sock = mDNSPlatformTCPSocket(m, kTCPSocketFlags_Zero, &srcport); + if (!info->sock) { LogMsg("LNT MakeTCPConnection: unable to create TCP socket"); mDNSPlatformMemFree(info->Reply); info->Reply = mDNSNULL; return(mStatus_NoMemoryErr); } + LogInfo("MakeTCPConnection: connecting to %#a:%d", &info->Address, mDNSVal16(info->Port)); + err = mDNSPlatformTCPConnect(info->sock, Addr, Port, mDNSNULL, 0, tcpConnectionCallback, info); + + if (err == mStatus_ConnPending) err = mStatus_NoError; + else if (err == mStatus_ConnEstablished) + { + mDNS_DropLockBeforeCallback(); + tcpConnectionCallback(info->sock, info, mDNStrue, mStatus_NoError); + mDNS_ReclaimLockAfterCallback(); + err = mStatus_NoError; + } + else + { + // Don't need to log this in customer builds -- it happens quite often during sleep, wake, configuration changes, etc. + LogInfo("LNT MakeTCPConnection: connection failed"); + mDNSPlatformTCPCloseConnection(info->sock); // Dispose the socket we created with mDNSPlatformTCPSocket() above + info->sock = mDNSNULL; + mDNSPlatformMemFree(info->Reply); + info->Reply = mDNSNULL; + } + return(err); + } + +mDNSlocal unsigned int AddSOAPArguments(char *const buf, const unsigned int maxlen, const int numArgs, const Property *const a) + { + static const char f1[] = "<%s>%s"; + static const char f2[] = "<%s xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"%s\">%s"; + int i, len = 0; + *buf = 0; + for (i = 0; i < numArgs; i++) + { + if (a[i].type) len += mDNS_snprintf(buf + len, maxlen - len, f2, a[i].name, a[i].type, a[i].value, a[i].name); + else len += mDNS_snprintf(buf + len, maxlen - len, f1, a[i].name, a[i].value, a[i].name); + } + return(len); + } + +mDNSlocal mStatus SendSOAPMsgControlAction(mDNS *m, tcpLNTInfo *info, const char *const Action, const int numArgs, const Property *const Arguments, const LNTOp_t op) + { + // SOAP message header format - + // - control URL + // - action (string) + // - router's host/port ("host:port") + // - content-length + static const char header[] = + "POST %s HTTP/1.1\r\n" + "Content-Type: text/xml; charset=\"utf-8\"\r\n" + "SOAPAction: \"urn:schemas-upnp-org:service:WAN%sConnection:1#%s\"\r\n" + "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows 9x)\r\n" + "Host: %s\r\n" + "Content-Length: %d\r\n" + "Connection: close\r\n" + "Pragma: no-cache\r\n" + "\r\n" + "%s\r\n"; + + static const char body1[] = + "\r\n" + "" + "" + ""; + + static const char body2[] = + "" + "" + "\r\n"; + + mStatus err; + char *body = (char*)&m->omsg; // Typically requires 1110-1122 bytes; m->omsg is 8952 bytes, which is plenty + int bodyLen; + + if (mDNSIPPortIsZero(m->UPnPSOAPPort) || m->UPnPSOAPURL == mDNSNULL || m->UPnPSOAPAddressString == mDNSNULL) // if no SOAP URL or address exists get out here + { LogInfo("SendSOAPMsgControlAction: no SOAP port, URL or address string"); return mStatus_Invalid; } + + // Create body + bodyLen = mDNS_snprintf (body, sizeof(m->omsg), body1, Action, m->UPnPWANPPPConnection ? "PPP" : "IP"); + bodyLen += AddSOAPArguments(body + bodyLen, sizeof(m->omsg) - bodyLen, numArgs, Arguments); + bodyLen += mDNS_snprintf (body + bodyLen, sizeof(m->omsg) - bodyLen, body2, Action); + + // Create info->Request; the header needs to contain the bodyLen in the "Content-Length" field + if (!info->Request) info->Request = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE); + if (!info->Request) { LogMsg("SendSOAPMsgControlAction: Can't allocate info->Request"); return mStatus_NoMemoryErr; } + info->requestLen = mDNS_snprintf((char*)info->Request, LNT_MAXBUFSIZE, header, m->UPnPSOAPURL, m->UPnPWANPPPConnection ? "PPP" : "IP", Action, m->UPnPSOAPAddressString, bodyLen, body); + + err = MakeTCPConnection(m, info, &m->Router, m->UPnPSOAPPort, op); + if (err) { mDNSPlatformMemFree(info->Request); info->Request = mDNSNULL; } + return err; + } + +// Build port mapping request with new port (up to max) and send it +mDNSlocal mStatus SendPortMapRequest(mDNS *m, NATTraversalInfo *n) + { + char externalPort[6]; + char internalPort[6]; + char localIPAddrString[30]; + char publicPortString[40]; + Property propArgs[8]; + mDNSu16 ReqPortNum = RequestedPortNum(n); + NATTraversalInfo *n2 = m->NATTraversals; + + // Scan our m->NATTraversals list to make sure the external port we're requesting is locally unique. + // UPnP gateways will report conflicts if different devices request the same external port, but if two + // clients on the same device request the same external port the second one just stomps over the first. + // One way this can happen is like this: + // 1. Client A binds local port 80 + // 2. Client A requests external port 80 -> internal port 80 + // 3. UPnP NAT gateway refuses external port 80 (some other client already has it) + // 4. Client A tries again, and successfully gets external port 80 -> internal port 81 + // 5. Client B on same machine tries to bind local port 80, and fails + // 6. Client B tries again, and successfully binds local port 81 + // 7. Client B now requests external port 81 -> internal port 81 + // 8. UPnP NAT gateway allows this, stomping over Client A's existing mapping + + while (n2) + { + if (n2 == n || RequestedPortNum(n2) != ReqPortNum) n2=n2->next; + else + { + if (n->tcpInfo.retries < 100) + { + n->tcpInfo.retries++; + ReqPortNum = RequestedPortNum(n); // Pick a new port number + n2 = m->NATTraversals; // And re-scan the list looking for conflicts + } + else + { + natTraversalHandlePortMapReply(m, n, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0); + return mStatus_NoError; + } + } + } + + // create strings to use in the message + mDNS_snprintf(externalPort, sizeof(externalPort), "%u", ReqPortNum); + mDNS_snprintf(internalPort, sizeof(internalPort), "%u", mDNSVal16(n->IntPort)); + mDNS_snprintf(publicPortString, sizeof(publicPortString), "iC%u", ReqPortNum); + mDNS_snprintf(localIPAddrString, sizeof(localIPAddrString), "%u.%u.%u.%u", + m->AdvertisedV4.ip.v4.b[0], m->AdvertisedV4.ip.v4.b[1], m->AdvertisedV4.ip.v4.b[2], m->AdvertisedV4.ip.v4.b[3]); + + // build the message + mDNSPlatformMemZero(propArgs, sizeof(propArgs)); + propArgs[0].name = "NewRemoteHost"; + propArgs[0].type = "string"; + propArgs[0].value = ""; + propArgs[1].name = "NewExternalPort"; + propArgs[1].type = "ui2"; + propArgs[1].value = externalPort; + propArgs[2].name = "NewProtocol"; + propArgs[2].type = "string"; + propArgs[2].value = (n->Protocol == NATOp_MapUDP) ? "UDP" : "TCP"; + propArgs[3].name = "NewInternalPort"; + propArgs[3].type = "ui2"; + propArgs[3].value = internalPort; + propArgs[4].name = "NewInternalClient"; + propArgs[4].type = "string"; + propArgs[4].value = localIPAddrString; + propArgs[5].name = "NewEnabled"; + propArgs[5].type = "boolean"; + propArgs[5].value = "1"; + propArgs[6].name = "NewPortMappingDescription"; + propArgs[6].type = "string"; + propArgs[6].value = publicPortString; + propArgs[7].name = "NewLeaseDuration"; + propArgs[7].type = "ui4"; + propArgs[7].value = "0"; + + LogInfo("SendPortMapRequest: internal %u external %u", mDNSVal16(n->IntPort), ReqPortNum); + return SendSOAPMsgControlAction(m, &n->tcpInfo, "AddPortMapping", 8, propArgs, LNTPortMapOp); + } + +mDNSexport mStatus LNT_MapPort(mDNS *m, NATTraversalInfo *const n) + { + LogInfo("LNT_MapPort"); + if (n->tcpInfo.sock) return(mStatus_NoError); // If we already have a connection up don't make another request for the same thing + n->tcpInfo.parentNATInfo = n; + n->tcpInfo.retries = 0; + return SendPortMapRequest(m, n); + } + +mDNSexport mStatus LNT_UnmapPort(mDNS *m, NATTraversalInfo *const n) + { + char externalPort[10]; + Property propArgs[3]; + tcpLNTInfo *info; + tcpLNTInfo **infoPtr = &m->tcpInfoUnmapList; + mStatus err; + + // If no NAT gateway to talk to, no need to do all this work for nothing + if (mDNSIPPortIsZero(m->UPnPSOAPPort) || !m->UPnPSOAPURL || !m->UPnPSOAPAddressString) return mStatus_NoError; + + mDNS_snprintf(externalPort, sizeof(externalPort), "%u", mDNSVal16(mDNSIPPortIsZero(n->RequestedPort) ? n->IntPort : n->RequestedPort)); + + mDNSPlatformMemZero(propArgs, sizeof(propArgs)); + propArgs[0].name = "NewRemoteHost"; + propArgs[0].type = "string"; + propArgs[0].value = ""; + propArgs[1].name = "NewExternalPort"; + propArgs[1].type = "ui2"; + propArgs[1].value = externalPort; + propArgs[2].name = "NewProtocol"; + propArgs[2].type = "string"; + propArgs[2].value = (n->Protocol == NATOp_MapUDP) ? "UDP" : "TCP"; + + n->tcpInfo.parentNATInfo = n; + + // clean up previous port mapping requests and allocations + if (n->tcpInfo.sock) LogInfo("LNT_UnmapPort: closing previous open connection"); + if (n->tcpInfo.sock ) { mDNSPlatformTCPCloseConnection(n->tcpInfo.sock); n->tcpInfo.sock = mDNSNULL; } + if (n->tcpInfo.Request) { mDNSPlatformMemFree(n->tcpInfo.Request); n->tcpInfo.Request = mDNSNULL; } + if (n->tcpInfo.Reply ) { mDNSPlatformMemFree(n->tcpInfo.Reply); n->tcpInfo.Reply = mDNSNULL; } + + // make a copy of the tcpInfo that we can clean up later (the one passed in will be destroyed by the client as soon as this returns) + if ((info = mDNSPlatformMemAllocate(sizeof(tcpLNTInfo))) == mDNSNULL) + { LogInfo("LNT_UnmapPort: can't allocate tcpInfo"); return(mStatus_NoMemoryErr); } + *info = n->tcpInfo; + + while (*infoPtr) infoPtr = &(*infoPtr)->next; // find the end of the list + *infoPtr = info; // append + + err = SendSOAPMsgControlAction(m, info, "DeletePortMapping", 3, propArgs, LNTPortMapDeleteOp); + if (err) DisposeInfoFromUnmapList(m, info); + return err; + } + +mDNSexport mStatus LNT_GetExternalAddress(mDNS *m) + { + return SendSOAPMsgControlAction(m, &m->tcpAddrInfo, "GetExternalIPAddress", 0, mDNSNULL, LNTExternalAddrOp); + } + +mDNSlocal mStatus GetDeviceDescription(mDNS *m, tcpLNTInfo *info) + { + // Device description format - + // - device description URL + // - host/port + static const char szSSDPMsgDescribeDeviceFMT[] = + "GET %s HTTP/1.1\r\n" + "Accept: text/xml, application/xml\r\n" + "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n" + "Host: %s\r\n" + "Connection: close\r\n" + "\r\n"; + + if (!mDNSIPPortIsZero(m->UPnPSOAPPort)) return mStatus_NoError; // already have the info we need + + if (m->UPnPRouterURL == mDNSNULL || m->UPnPRouterAddressString == mDNSNULL) { LogInfo("GetDeviceDescription: no router URL or address string!"); return (mStatus_Invalid); } + + // build message + if (info->Request != mDNSNULL) mDNSPlatformMemZero(info->Request, LNT_MAXBUFSIZE); // reuse previously allocated buffer + else if ((info->Request = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE)) == mDNSNULL) { LogInfo("can't allocate send buffer for discovery"); return (mStatus_NoMemoryErr); } + info->requestLen = mDNS_snprintf((char*)info->Request, LNT_MAXBUFSIZE, szSSDPMsgDescribeDeviceFMT, m->UPnPRouterURL, m->UPnPRouterAddressString); + LogInfo("Describe Device: [%s]", info->Request); + return MakeTCPConnection(m, info, &m->Router, m->UPnPRouterPort, LNTDiscoveryOp); + } + +// This function parses the response to our SSDP discovery message. Basically, we look to make sure this is a response +// referencing a service we care about (WANIPConnection or WANPPPConnection), then look for the "Location:" header and copy the addressing and +// URL info we need. +mDNSexport void LNT_ConfigureRouterInfo(mDNS *m, const mDNSInterfaceID InterfaceID, const mDNSu8 *const data, const mDNSu16 len) + { + const mDNSu8 *ptr = data; + const mDNSu8 *end = data + len; + const mDNSu8 *stop = ptr; + + if (!mDNSIPPortIsZero(m->UPnPRouterPort)) return; // already have the info we need + + // The formatting of the HTTP header is not always the same when it comes to the placement of + // the service and location strings, so we just look for each of them from the beginning for every response + + // figure out if this is a message from a service we care about + while (ptr && ptr != end) + { + if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANIPConnection:1", 17) == 0)) break; + ptr++; + } + if (ptr == end) + { + ptr = data; + while (ptr && ptr != end) + { + if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANPPPConnection:1", 18) == 0)) break; + ptr++; + } + } + if (ptr == mDNSNULL || ptr == end) return; // not a message we care about + + // find "Location:", starting from the beginning + ptr = data; + while (ptr && ptr != end) + { + if ((*ptr & 0xDF) == 'L' && (strncasecmp((char*)ptr, "Location:", 9) == 0)) break; // find the first 'L'; is this Location? if not, keep looking + ptr++; + } + if (ptr == mDNSNULL || ptr == end) + { + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Location", ""); + return; // not a message we care about + } + ptr += 9; //Skip over 'Location:' + while (*ptr == ' ' && ptr < end) ptr++; // skip over spaces + if (ptr >= end) return; + + // find the end of the line + for (stop = ptr; stop != end; stop++) { if (*stop == '\r') { end = stop; break; } } + + // fill in default port + m->UPnPRouterPort = mDNSOpaque16fromIntVal(80); + + // free string pointers and set to NULL + if (m->UPnPRouterAddressString != mDNSNULL) + { + mDNSPlatformMemFree(m->UPnPRouterAddressString); + m->UPnPRouterAddressString = mDNSNULL; + } + if (m->UPnPRouterURL != mDNSNULL) + { + mDNSPlatformMemFree(m->UPnPRouterURL); + m->UPnPRouterURL = mDNSNULL; + } + + // the Router URL should look something like "/dyndev/uuid:0013-108c-4b3f0000f3dc" + if (ParseHttpUrl(ptr, end, &m->UPnPRouterAddressString, &m->UPnPRouterPort, &m->UPnPRouterURL) != mStatus_NoError) + { + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Parse URL", ""); + return; + } + + m->UPnPInterfaceID = InterfaceID; + + if (m->UPnPRouterAddressString == mDNSNULL) + { + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Router address", ""); + LogMsg("LNT_ConfigureRouterInfo: UPnPRouterAddressString is NULL"); + } + else LogInfo("LNT_ConfigureRouterInfo: Router address string [%s]", m->UPnPRouterAddressString); + + if (m->UPnPRouterURL == mDNSNULL) + { + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Router path", ""); + LogMsg("LNT_ConfigureRouterInfo: UPnPRouterURL is NULL"); + } + else LogInfo("LNT_ConfigureRouterInfo: Router URL [%s]", m->UPnPRouterURL); + + LogInfo("LNT_ConfigureRouterInfo: Router port %d", mDNSVal16(m->UPnPRouterPort)); + LogInfo("LNT_ConfigureRouterInfo: Router interface %d", m->UPnPInterfaceID); + + // Don't need the SSDP socket anymore + if (m->SSDPSocket) { debugf("LNT_ConfigureRouterInfo destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; } + + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "success", "success", ""); + // now send message to get the device description + GetDeviceDescription(m, &m->tcpDeviceInfo); + } + +mDNSexport void LNT_SendDiscoveryMsg(mDNS *m) + { + static const char msg[] = + "M-SEARCH * HTTP/1.1\r\n" + "Host:239.255.255.250:1900\r\n" + "ST:urn:schemas-upnp-org:service:WAN%sConnection:1\r\n" + "Man:\"ssdp:discover\"\r\n" + "MX:3\r\n\r\n"; + static const mDNSAddr multicastDest = { mDNSAddrType_IPv4, { { { 239, 255, 255, 250 } } } }; + + mDNSu8 *buf = (mDNSu8*)&m->omsg; //m->omsg is 8952 bytes, which is plenty + unsigned int bufLen; + + if (!mDNSIPPortIsZero(m->UPnPRouterPort)) + { + if (m->SSDPSocket) { debugf("LNT_SendDiscoveryMsg destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; } + if (mDNSIPPortIsZero(m->UPnPSOAPPort) && !m->tcpDeviceInfo.sock) GetDeviceDescription(m, &m->tcpDeviceInfo); + return; + } + + // Always query for WANIPConnection in the first SSDP packet + if (m->retryIntervalGetAddr <= NATMAP_INIT_RETRY) m->SSDPWANPPPConnection = mDNSfalse; + + // Create message + bufLen = mDNS_snprintf((char*)buf, sizeof(m->omsg), msg, m->SSDPWANPPPConnection ? "PPP" : "IP"); + + debugf("LNT_SendDiscoveryMsg Router %.4a Current External Address %.4a", &m->Router.ip.v4, &m->ExternalAddress); + + if (!mDNSIPv4AddressIsZero(m->Router.ip.v4)) + { + if (!m->SSDPSocket) { m->SSDPSocket = mDNSPlatformUDPSocket(m, zeroIPPort); debugf("LNT_SendDiscoveryMsg created SSDPSocket %p", &m->SSDPSocket); } + mDNSPlatformSendUDP(m, buf, buf + bufLen, 0, m->SSDPSocket, &m->Router, SSDPPort); + mDNSPlatformSendUDP(m, buf, buf + bufLen, 0, m->SSDPSocket, &multicastDest, SSDPPort); + } + + m->SSDPWANPPPConnection = !m->SSDPWANPPPConnection; + } + +mDNSexport void LNT_ClearState(mDNS *const m) + { + if (m->tcpAddrInfo.sock) { mDNSPlatformTCPCloseConnection(m->tcpAddrInfo.sock); m->tcpAddrInfo.sock = mDNSNULL; } + if (m->tcpDeviceInfo.sock) { mDNSPlatformTCPCloseConnection(m->tcpDeviceInfo.sock); m->tcpDeviceInfo.sock = mDNSNULL; } + m->UPnPSOAPPort = m->UPnPRouterPort = zeroIPPort; // Reset UPnP ports + } + +#endif /* _LEGACY_NAT_TRAVERSAL_ */ diff --git a/src/tools/mdnssd/Poll.c b/src/tools/mdnssd/Poll.c new file mode 100644 index 00000000000..9adc632b717 --- /dev/null +++ b/src/tools/mdnssd/Poll.c @@ -0,0 +1,728 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Poll.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "GenLinkedList.h" +#include "DebugServices.h" + + +typedef struct PollSource_struct +{ + SOCKET socket; + HANDLE handle; + void *context; + + union + { + mDNSPollSocketCallback socket; + mDNSPollEventCallback event; + } callback; + + struct Worker_struct *worker; + struct PollSource_struct *next; + +} PollSource; + + +typedef struct Worker_struct +{ + HANDLE thread; // NULL for main worker + unsigned id; // 0 for main worker + + HANDLE start; // NULL for main worker + HANDLE stop; // NULL for main worker + BOOL done; // Not used for main worker + + DWORD numSources; + PollSource *sources[ MAXIMUM_WAIT_OBJECTS ]; + HANDLE handles[ MAXIMUM_WAIT_OBJECTS ]; + DWORD result; + struct Worker_struct *next; +} Worker; + + +typedef struct Poll_struct +{ + mDNSBool setup; + HANDLE wakeup; + GenLinkedList sources; + DWORD numSources; + Worker main; + GenLinkedList workers; + HANDLE workerHandles[ MAXIMUM_WAIT_OBJECTS ]; + DWORD numWorkers; + +} Poll; + + +/* + * Poll Methods + */ + +mDNSlocal mStatus PollSetup(); +mDNSlocal mStatus PollRegisterSource( PollSource *source ); +mDNSlocal void PollUnregisterSource( PollSource *source ); +mDNSlocal mStatus PollStartWorkers(); +mDNSlocal mStatus PollStopWorkers(); +mDNSlocal void PollRemoveWorker( Worker *worker ); + + +/* + * Worker Methods + */ + +mDNSlocal mStatus WorkerInit( Worker *worker ); +mDNSlocal void WorkerFree( Worker *worker ); +mDNSlocal void WorkerRegisterSource( Worker *worker, PollSource *source ); +mDNSlocal int WorkerSourceToIndex( Worker *worker, PollSource *source ); +mDNSlocal void WorkerUnregisterSource( Worker *worker, PollSource *source ); +mDNSlocal void WorkerDispatch( Worker *worker); +mDNSlocal void CALLBACK WorkerWakeupNotification( HANDLE event, void *context ); +mDNSlocal unsigned WINAPI WorkerMain( LPVOID inParam ); + + +static void +ShiftDown( void * arr, size_t arraySize, size_t itemSize, int index ) +{ + memmove( ( ( unsigned char* ) arr ) + ( ( index - 1 ) * itemSize ), ( ( unsigned char* ) arr ) + ( index * itemSize ), ( arraySize - index ) * itemSize ); +} + + +#define DEBUG_NAME "[mDNSWin32] " +#define gMDNSRecord mDNSStorage +mDNSlocal Poll gPoll = { mDNSfalse, NULL }; + +#define LogErr( err, FUNC ) LogMsg( "%s:%d - %s failed: %d\n", __FUNCTION__, __LINE__, FUNC, err ); + + +mStatus +mDNSPollRegisterSocket( SOCKET socket, int networkEvents, mDNSPollSocketCallback callback, void *context ) +{ + PollSource *source = NULL; + HANDLE event = INVALID_HANDLE_VALUE; + mStatus err = mStatus_NoError; + + if ( !gPoll.setup ) + { + err = PollSetup(); + require_noerr( err, exit ); + } + + source = malloc( sizeof( PollSource ) ); + require_action( source, exit, err = mStatus_NoMemoryErr ); + + event = WSACreateEvent(); + require_action( event, exit, err = mStatus_NoMemoryErr ); + + err = WSAEventSelect( socket, event, networkEvents ); + require_noerr( err, exit ); + + source->socket = socket; + source->handle = event; + source->callback.socket = callback; + source->context = context; + + err = PollRegisterSource( source ); + require_noerr( err, exit ); + +exit: + + if ( err != mStatus_NoError ) + { + if ( event != INVALID_HANDLE_VALUE ) + { + WSACloseEvent( event ); + } + + if ( source != NULL ) + { + free( source ); + } + } + + return err; +} + + +void +mDNSPollUnregisterSocket( SOCKET socket ) +{ + PollSource *source; + + for ( source = gPoll.sources.Head; source; source = source->next ) + { + if ( source->socket == socket ) + { + break; + } + } + + if ( source ) + { + WSACloseEvent( source->handle ); + PollUnregisterSource( source ); + free( source ); + } +} + + +mStatus +mDNSPollRegisterEvent( HANDLE event, mDNSPollEventCallback callback, void *context ) +{ + PollSource *source = NULL; + mStatus err = mStatus_NoError; + + if ( !gPoll.setup ) + { + err = PollSetup(); + require_noerr( err, exit ); + } + + source = malloc( sizeof( PollSource ) ); + require_action( source, exit, err = mStatus_NoMemoryErr ); + + source->socket = INVALID_SOCKET; + source->handle = event; + source->callback.event = callback; + source->context = context; + + err = PollRegisterSource( source ); + require_noerr( err, exit ); + +exit: + + if ( err != mStatus_NoError ) + { + if ( source != NULL ) + { + free( source ); + } + } + + return err; +} + + +void +mDNSPollUnregisterEvent( HANDLE event ) +{ + PollSource *source; + + for ( source = gPoll.sources.Head; source; source = source->next ) + { + if ( source->handle == event ) + { + break; + } + } + + if ( source ) + { + PollUnregisterSource( source ); + free( source ); + } +} + + +mStatus +mDNSPoll( DWORD msec ) +{ + mStatus err = mStatus_NoError; + + if ( gPoll.numWorkers > 0 ) + { + err = PollStartWorkers(); + require_noerr( err, exit ); + } + + gPoll.main.result = WaitForMultipleObjects( gPoll.main.numSources, gPoll.main.handles, FALSE, msec ); + err = translate_errno( ( gPoll.main.result != WAIT_FAILED ), ( mStatus ) GetLastError(), kUnknownErr ); + if ( err ) LogErr( err, "WaitForMultipleObjects()" ); + require_action( gPoll.main.result != WAIT_FAILED, exit, err = ( mStatus ) GetLastError() ); + + if ( gPoll.numWorkers > 0 ) + { + err = PollStopWorkers(); + require_noerr( err, exit ); + } + + WorkerDispatch( &gPoll.main ); + +exit: + + return ( err ); +} + + +mDNSlocal mStatus +PollSetup() +{ + mStatus err = mStatus_NoError; + + if ( !gPoll.setup ) + { + memset( &gPoll, 0, sizeof( gPoll ) ); + + InitLinkedList( &gPoll.sources, offsetof( PollSource, next ) ); + InitLinkedList( &gPoll.workers, offsetof( Worker, next ) ); + + gPoll.wakeup = CreateEvent( NULL, TRUE, FALSE, NULL ); + require_action( gPoll.wakeup, exit, err = mStatus_NoMemoryErr ); + + err = WorkerInit( &gPoll.main ); + require_noerr( err, exit ); + + gPoll.setup = mDNStrue; + } + +exit: + + return err; +} + + +mDNSlocal mStatus +PollRegisterSource( PollSource *source ) +{ + Worker *worker = NULL; + mStatus err = mStatus_NoError; + + AddToTail( &gPoll.sources, source ); + gPoll.numSources++; + + // First check our main worker. In most cases, we won't have to worry about threads + + if ( gPoll.main.numSources < MAXIMUM_WAIT_OBJECTS ) + { + WorkerRegisterSource( &gPoll.main, source ); + } + else + { + // Try to find a thread to use that we've already created + + for ( worker = gPoll.workers.Head; worker; worker = worker->next ) + { + if ( worker->numSources < MAXIMUM_WAIT_OBJECTS ) + { + WorkerRegisterSource( worker, source ); + break; + } + } + + // If not, then create a worker and make a thread to run it in + + if ( !worker ) + { + worker = ( Worker* ) malloc( sizeof( Worker ) ); + require_action( worker, exit, err = mStatus_NoMemoryErr ); + + memset( worker, 0, sizeof( Worker ) ); + + worker->start = CreateEvent( NULL, FALSE, FALSE, NULL ); + require_action( worker->start, exit, err = mStatus_NoMemoryErr ); + + worker->stop = CreateEvent( NULL, FALSE, FALSE, NULL ); + require_action( worker->stop, exit, err = mStatus_NoMemoryErr ); + + err = WorkerInit( worker ); + require_noerr( err, exit ); + + // Create thread with _beginthreadex() instead of CreateThread() to avoid + // memory leaks when using static run-time libraries. + // See . + + worker->thread = ( HANDLE ) _beginthreadex_compat( NULL, 0, WorkerMain, worker, 0, &worker->id ); + err = translate_errno( worker->thread, ( mStatus ) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + AddToTail( &gPoll.workers, worker ); + gPoll.workerHandles[ gPoll.numWorkers++ ] = worker->stop; + + WorkerRegisterSource( worker, source ); + } + } + +exit: + + if ( err && worker ) + { + WorkerFree( worker ); + } + + return err; +} + + +mDNSlocal void +PollUnregisterSource( PollSource *source ) +{ + RemoveFromList( &gPoll.sources, source ); + gPoll.numSources--; + + WorkerUnregisterSource( source->worker, source ); +} + + +mDNSlocal mStatus +PollStartWorkers() +{ + Worker *worker; + mStatus err = mStatus_NoError; + BOOL ok; + + dlog( kDebugLevelChatty, DEBUG_NAME "starting workers\n" ); + + worker = gPoll.workers.Head; + + while ( worker ) + { + Worker *next = worker->next; + + if ( worker->numSources == 1 ) + { + PollRemoveWorker( worker ); + } + else + { + dlog( kDebugLevelChatty, DEBUG_NAME "waking up worker\n" ); + + ok = SetEvent( worker->start ); + err = translate_errno( ok, ( mStatus ) GetLastError(), kUnknownErr ); + if ( err ) LogErr( err, "SetEvent()" ); + + if ( err ) + { + PollRemoveWorker( worker ); + } + } + + worker = next; + } + + err = mStatus_NoError; + + return err; +} + + +mDNSlocal mStatus +PollStopWorkers() +{ + DWORD result; + Worker *worker; + BOOL ok; + mStatus err = mStatus_NoError; + + dlog( kDebugLevelChatty, DEBUG_NAME "stopping workers\n" ); + + ok = SetEvent( gPoll.wakeup ); + err = translate_errno( ok, ( mStatus ) GetLastError(), kUnknownErr ); + if ( err ) LogErr( err, "SetEvent()" ); + + // Wait For 5 seconds for all the workers to wake up + + result = WaitForMultipleObjects( gPoll.numWorkers, gPoll.workerHandles, TRUE, 5000 ); + err = translate_errno( ( result != WAIT_FAILED ), ( mStatus ) GetLastError(), kUnknownErr ); + if ( err ) LogErr( err, "WaitForMultipleObjects()" ); + + ok = ResetEvent( gPoll.wakeup ); + err = translate_errno( ok, ( mStatus ) GetLastError(), kUnknownErr ); + if ( err ) LogErr( err, "ResetEvent()" ); + + for ( worker = gPoll.workers.Head; worker; worker = worker->next ) + { + WorkerDispatch( worker ); + } + + err = mStatus_NoError; + + return err; +} + + +mDNSlocal void +PollRemoveWorker( Worker *worker ) +{ + DWORD result; + mStatus err; + BOOL ok; + DWORD i; + + dlog( kDebugLevelChatty, DEBUG_NAME "removing worker %d\n", worker->id ); + + RemoveFromList( &gPoll.workers, worker ); + + // Remove handle from gPoll.workerHandles + + for ( i = 0; i < gPoll.numWorkers; i++ ) + { + if ( gPoll.workerHandles[ i ] == worker->stop ) + { + ShiftDown( gPoll.workerHandles, gPoll.numWorkers, sizeof( gPoll.workerHandles[ 0 ] ), i + 1 ); + break; + } + } + + worker->done = TRUE; + gPoll.numWorkers--; + + // Cause the thread to exit. + + ok = SetEvent( worker->start ); + err = translate_errno( ok, ( OSStatus ) GetLastError(), kUnknownErr ); + if ( err ) LogErr( err, "SetEvent()" ); + + result = WaitForSingleObject( worker->thread, 5000 ); + err = translate_errno( result != WAIT_FAILED, ( OSStatus ) GetLastError(), kUnknownErr ); + if ( err ) LogErr( err, "WaitForSingleObject()" ); + + if ( ( result == WAIT_FAILED ) || ( result == WAIT_TIMEOUT ) ) + { + ok = TerminateThread( worker->thread, 0 ); + err = translate_errno( ok, ( OSStatus ) GetLastError(), kUnknownErr ); + if ( err ) LogErr( err, "TerminateThread()" ); + } + + CloseHandle( worker->thread ); + worker->thread = NULL; + + WorkerFree( worker ); +} + + +mDNSlocal void +WorkerRegisterSource( Worker *worker, PollSource *source ) +{ + source->worker = worker; + worker->sources[ worker->numSources ] = source; + worker->handles[ worker->numSources ] = source->handle; + worker->numSources++; +} + + +mDNSlocal int +WorkerSourceToIndex( Worker *worker, PollSource *source ) +{ + int index; + + for ( index = 0; index < ( int ) worker->numSources; index++ ) + { + if ( worker->sources[ index ] == source ) + { + break; + } + } + + if ( index == ( int ) worker->numSources ) + { + index = -1; + } + + return index; +} + + +mDNSlocal void +WorkerUnregisterSource( Worker *worker, PollSource *source ) +{ + int sourceIndex = WorkerSourceToIndex( worker, source ); + DWORD delta; + + if ( sourceIndex == -1 ) + { + LogMsg( "WorkerUnregisterSource: source not found in list" ); + goto exit; + } + + delta = ( worker->numSources - sourceIndex - 1 ); + + // If this source is not at the end of the list, then move memory + + if ( delta > 0 ) + { + ShiftDown( worker->sources, worker->numSources, sizeof( worker->sources[ 0 ] ), sourceIndex + 1 ); + ShiftDown( worker->handles, worker->numSources, sizeof( worker->handles[ 0 ] ), sourceIndex + 1 ); + } + + worker->numSources--; + +exit: + + return; +} + + +mDNSlocal void CALLBACK +WorkerWakeupNotification( HANDLE event, void *context ) +{ + DEBUG_UNUSED( event ); + DEBUG_UNUSED( context ); + + dlog( kDebugLevelChatty, DEBUG_NAME "Worker thread wakeup\n" ); +} + + +mDNSlocal void +WorkerDispatch( Worker *worker ) +{ + if ( worker->result == WAIT_FAILED ) + { + /* What should we do here? */ + } + else if ( worker->result == WAIT_TIMEOUT ) + { + dlog( kDebugLevelChatty, DEBUG_NAME "timeout\n" ); + } + else + { + DWORD waitItemIndex = ( DWORD )( ( ( int ) worker->result ) - WAIT_OBJECT_0 ); + PollSource *source = NULL; + + // Sanity check + + if ( waitItemIndex >= worker->numSources ) + { + LogMsg( "WorkerDispatch: waitItemIndex (%d) is >= numSources (%d)", waitItemIndex, worker->numSources ); + goto exit; + } + + source = worker->sources[ waitItemIndex ]; + + if ( source->socket != INVALID_SOCKET ) + { + WSANETWORKEVENTS event; + + if ( WSAEnumNetworkEvents( source->socket, source->handle, &event ) == 0 ) + { + source->callback.socket( source->socket, &event, source->context ); + } + else + { + source->callback.socket( source->socket, NULL, source->context ); + } + } + else + { + source->callback.event( source->handle, source->context ); + } + } + +exit: + + return; +} + + +mDNSlocal mStatus +WorkerInit( Worker *worker ) +{ + PollSource *source = NULL; + mStatus err = mStatus_NoError; + + require_action( worker, exit, err = mStatus_BadParamErr ); + + source = malloc( sizeof( PollSource ) ); + require_action( source, exit, err = mStatus_NoMemoryErr ); + + source->socket = INVALID_SOCKET; + source->handle = gPoll.wakeup; + source->callback.event = WorkerWakeupNotification; + source->context = NULL; + + WorkerRegisterSource( worker, source ); + +exit: + + return err; +} + + +mDNSlocal void +WorkerFree( Worker *worker ) +{ + if ( worker->start ) + { + CloseHandle( worker->start ); + worker->start = NULL; + } + + if ( worker->stop ) + { + CloseHandle( worker->stop ); + worker->stop = NULL; + } + + free( worker ); +} + + +mDNSlocal unsigned WINAPI +WorkerMain( LPVOID inParam ) +{ + Worker *worker = ( Worker* ) inParam; + mStatus err = mStatus_NoError; + + require_action( worker, exit, err = mStatus_BadParamErr ); + + dlog( kDebugLevelVerbose, DEBUG_NAME, "entering WorkerMain()\n" ); + + while ( TRUE ) + { + DWORD result; + BOOL ok; + + dlog( kDebugLevelChatty, DEBUG_NAME, "worker thread %d will wait on main loop\n", worker->id ); + + result = WaitForSingleObject( worker->start, INFINITE ); + err = translate_errno( ( result != WAIT_FAILED ), ( mStatus ) GetLastError(), kUnknownErr ); + if ( err ) { LogErr( err, "WaitForSingleObject()" ); break; } + if ( worker->done ) break; + + dlog( kDebugLevelChatty, DEBUG_NAME "worker thread %d will wait on sockets\n", worker->id ); + + worker->result = WaitForMultipleObjects( worker->numSources, worker->handles, FALSE, INFINITE ); + err = translate_errno( ( worker->result != WAIT_FAILED ), ( mStatus ) GetLastError(), kUnknownErr ); + if ( err ) { LogErr( err, "WaitForMultipleObjects()" ); break; } + + dlog( kDebugLevelChatty, DEBUG_NAME "worker thread %d did wait on sockets: %d\n", worker->id, worker->result ); + + ok = SetEvent( gPoll.wakeup ); + err = translate_errno( ok, ( mStatus ) GetLastError(), kUnknownErr ); + if ( err ) { LogErr( err, "SetEvent()" ); break; } + + dlog( kDebugLevelChatty, DEBUG_NAME, "worker thread %d preparing to sleep\n", worker->id ); + + ok = SetEvent( worker->stop ); + err = translate_errno( ok, ( mStatus ) GetLastError(), kUnknownErr ); + if ( err ) { LogErr( err, "SetEvent()" ); break; } + } + + dlog( kDebugLevelVerbose, DEBUG_NAME "exiting WorkerMain()\n" ); + +exit: + + return 0; +} diff --git a/src/tools/mdnssd/Poll.h b/src/tools/mdnssd/Poll.h new file mode 100644 index 00000000000..bd1b10fc220 --- /dev/null +++ b/src/tools/mdnssd/Poll.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _Poll_h +#define _Poll_h + +#include "CommonServices.h" +#include +#include "mDNSEmbeddedAPI.h" +#include "uDNS.h" + + +#if defined(__cplusplus ) +extern "C" { +#endif + + +typedef void ( CALLBACK *mDNSPollSocketCallback )( SOCKET socket, LPWSANETWORKEVENTS event, void *context ); +typedef void ( CALLBACK *mDNSPollEventCallback )( HANDLE event, void *context ); + + +extern mStatus +mDNSPollRegisterSocket( SOCKET socket, int networkEvents, mDNSPollSocketCallback callback, void *context ); + + +extern void +mDNSPollUnregisterSocket( SOCKET socket ); + + +extern mStatus +mDNSPollRegisterEvent( HANDLE event, mDNSPollEventCallback callback, void *context ); + + +extern void +mDNSPollUnregisterEvent( HANDLE event ); + + +extern mStatus +mDNSPoll( DWORD msec ); + + +#if defined(__cplusplus) +} +#endif + + +#endif diff --git a/src/tools/mdnssd/RegNames.h b/src/tools/mdnssd/RegNames.h new file mode 100644 index 00000000000..bc885d61e59 --- /dev/null +++ b/src/tools/mdnssd/RegNames.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//---------------------------------------------------------------------------------------- +// Registry Constants +//---------------------------------------------------------------------------------------- + +#if defined(UNICODE) + +# define kServiceParametersSoftware L"SOFTWARE" +# define kServiceParametersAppleComputer L"Apple Computer, Inc." +# define kServiceParametersBonjour L"Bonjour" +# define kServiceParametersNode L"SOFTWARE\\Apple Inc.\\Bonjour" +# define kServiceName L"Bonjour Service" +# define kServiceDynDNSBrowseDomains L"BrowseDomains" +# define kServiceDynDNSHostNames L"HostNames" +# define kServiceDynDNSRegistrationDomains L"RegistrationDomains" +# define kServiceDynDNSDomains L"Domains" // value is comma separated list of domains +# define kServiceDynDNSEnabled L"Enabled" +# define kServiceDynDNSStatus L"Status" +# define kServiceManageLLRouting L"ManageLLRouting" +# define kServiceCacheEntryCount L"CacheEntryCount" +# define kServiceManageFirewall L"ManageFirewall" +# define kServiceAdvertisedServices L"Services" + +# else + +# define kServiceParametersSoftware "SOFTWARE" +# define kServiceParametersAppleComputer "Apple Computer, Inc." +# define kServiceParametersBonjour "Bonjour" +# define kServiceParametersNode "SOFTWARE\\Apple Inc.\\Bonjour" +# define kServiceName "Bonjour Service" +# define kServiceDynDNSBrowseDomains "BrowseDomains" +# define kServiceDynDNSHostNames "HostNames" +# define kServiceDynDNSRegistrationDomains "RegistrationDomains" +# define kServiceDynDNSDomains "Domains" // value is comma separated list of domains +# define kServiceDynDNSEnabled "Enabled" +# define kServiceDynDNSStatus "Status" +# define kServiceManageLLRouting "ManageLLRouting" +# define kServiceCacheEntryCount "CacheEntryCount" +# define kServiceManageFirewall "ManageFirewall" + +#endif diff --git a/src/tools/mdnssd/Secret.c b/src/tools/mdnssd/Secret.c new file mode 100644 index 00000000000..5abd28b39f1 --- /dev/null +++ b/src/tools/mdnssd/Secret.c @@ -0,0 +1,338 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Secret.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "DebugServices.h" + + +mDNSlocal OSStatus MakeLsaStringFromUTF8String( PLSA_UNICODE_STRING output, const char * input ); +mDNSlocal OSStatus MakeUTF8StringFromLsaString( char * output, size_t len, PLSA_UNICODE_STRING input ); + + +BOOL +LsaGetSecret( const char * inDomain, char * outDomain, unsigned outDomainSize, char * outKey, unsigned outKeySize, char * outSecret, unsigned outSecretSize ) +{ + PLSA_UNICODE_STRING domainLSA; + PLSA_UNICODE_STRING keyLSA; + PLSA_UNICODE_STRING secretLSA; + size_t i; + size_t dlen; + LSA_OBJECT_ATTRIBUTES attrs; + LSA_HANDLE handle = NULL; + NTSTATUS res; + OSStatus err; + + check( inDomain ); + check( outDomain ); + check( outKey ); + check( outSecret ); + + // Initialize + + domainLSA = NULL; + keyLSA = NULL; + secretLSA = NULL; + + // Make sure we have enough space to add trailing dot + + dlen = strlen( inDomain ); + err = strcpy_s( outDomain, outDomainSize - 2, inDomain ); + require_noerr( err, exit ); + + // If there isn't a trailing dot, add one because the mDNSResponder + // presents names with the trailing dot. + + if ( outDomain[ dlen - 1 ] != '.' ) + { + outDomain[ dlen++ ] = '.'; + outDomain[ dlen ] = '\0'; + } + + // Canonicalize name by converting to lower case (keychain and some name servers are case sensitive) + + for ( i = 0; i < dlen; i++ ) + { + outDomain[i] = (char) tolower( outDomain[i] ); // canonicalize -> lower case + } + + // attrs are reserved, so initialize to zeroes. + + ZeroMemory( &attrs, sizeof( attrs ) ); + + // Get a handle to the Policy object on the local system + + res = LsaOpenPolicy( NULL, &attrs, POLICY_GET_PRIVATE_INFORMATION, &handle ); + err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr ); + require_noerr( err, exit ); + + // Get the encrypted data + + domainLSA = ( PLSA_UNICODE_STRING ) malloc( sizeof( LSA_UNICODE_STRING ) ); + require_action( domainLSA != NULL, exit, err = mStatus_NoMemoryErr ); + err = MakeLsaStringFromUTF8String( domainLSA, outDomain ); + require_noerr( err, exit ); + + // Retrieve the key + + res = LsaRetrievePrivateData( handle, domainLSA, &keyLSA ); + err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr ); + require_noerr_quiet( err, exit ); + + // Lsa secrets use a flat naming space. Therefore, we will prepend "$" to the keyname to + // make sure it doesn't conflict with a zone name. + // Strip off the "$" prefix. + + err = MakeUTF8StringFromLsaString( outKey, outKeySize, keyLSA ); + require_noerr( err, exit ); + require_action( outKey[0] == '$', exit, err = kUnknownErr ); + memcpy( outKey, outKey + 1, strlen( outKey ) ); + + // Retrieve the secret + + res = LsaRetrievePrivateData( handle, keyLSA, &secretLSA ); + err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr ); + require_noerr_quiet( err, exit ); + + // Convert the secret to UTF8 string + + err = MakeUTF8StringFromLsaString( outSecret, outSecretSize, secretLSA ); + require_noerr( err, exit ); + +exit: + + if ( domainLSA != NULL ) + { + if ( domainLSA->Buffer != NULL ) + { + free( domainLSA->Buffer ); + } + + free( domainLSA ); + } + + if ( keyLSA != NULL ) + { + LsaFreeMemory( keyLSA ); + } + + if ( secretLSA != NULL ) + { + LsaFreeMemory( secretLSA ); + } + + if ( handle ) + { + LsaClose( handle ); + handle = NULL; + } + + return ( !err ) ? TRUE : FALSE; +} + + +mDNSBool +LsaSetSecret( const char * inDomain, const char * inKey, const char * inSecret ) +{ + size_t inDomainLength; + size_t inKeyLength; + char domain[ 1024 ]; + char key[ 1024 ]; + LSA_OBJECT_ATTRIBUTES attrs; + LSA_HANDLE handle = NULL; + NTSTATUS res; + LSA_UNICODE_STRING lucZoneName; + LSA_UNICODE_STRING lucKeyName; + LSA_UNICODE_STRING lucSecretName; + BOOL ok = TRUE; + OSStatus err; + + require_action( inDomain != NULL, exit, ok = FALSE ); + require_action( inKey != NULL, exit, ok = FALSE ); + require_action( inSecret != NULL, exit, ok = FALSE ); + + // If there isn't a trailing dot, add one because the mDNSResponder + // presents names with the trailing dot. + + ZeroMemory( domain, sizeof( domain ) ); + inDomainLength = strlen( inDomain ); + require_action( inDomainLength > 0, exit, ok = FALSE ); + err = strcpy_s( domain, sizeof( domain ) - 2, inDomain ); + require_action( !err, exit, ok = FALSE ); + + if ( domain[ inDomainLength - 1 ] != '.' ) + { + domain[ inDomainLength++ ] = '.'; + domain[ inDomainLength ] = '\0'; + } + + // + // + // Prepend "$" to the key name, so that there will + // be no conflict between the zone name and the key + // name + + ZeroMemory( key, sizeof( key ) ); + inKeyLength = strlen( inKey ); + require_action( inKeyLength > 0 , exit, ok = FALSE ); + key[ 0 ] = '$'; + err = strcpy_s( key + 1, sizeof( key ) - 3, inKey ); + require_action( !err, exit, ok = FALSE ); + inKeyLength++; + + if ( key[ inKeyLength - 1 ] != '.' ) + { + key[ inKeyLength++ ] = '.'; + key[ inKeyLength ] = '\0'; + } + + // attrs are reserved, so initialize to zeroes. + + ZeroMemory( &attrs, sizeof( attrs ) ); + + // Get a handle to the Policy object on the local system + + res = LsaOpenPolicy( NULL, &attrs, POLICY_ALL_ACCESS, &handle ); + err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr ); + require_noerr( err, exit ); + + // Intializing PLSA_UNICODE_STRING structures + + err = MakeLsaStringFromUTF8String( &lucZoneName, domain ); + require_noerr( err, exit ); + + err = MakeLsaStringFromUTF8String( &lucKeyName, key ); + require_noerr( err, exit ); + + err = MakeLsaStringFromUTF8String( &lucSecretName, inSecret ); + require_noerr( err, exit ); + + // Store the private data. + + res = LsaStorePrivateData( handle, &lucZoneName, &lucKeyName ); + err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr ); + require_noerr( err, exit ); + + res = LsaStorePrivateData( handle, &lucKeyName, &lucSecretName ); + err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr ); + require_noerr( err, exit ); + +exit: + + if ( handle ) + { + LsaClose( handle ); + handle = NULL; + } + + return ok; +} + + +//=========================================================================================================================== +// MakeLsaStringFromUTF8String +//=========================================================================================================================== + +mDNSlocal OSStatus +MakeLsaStringFromUTF8String( PLSA_UNICODE_STRING output, const char * input ) +{ + int size; + OSStatus err; + + check( input ); + check( output ); + + output->Buffer = NULL; + + size = MultiByteToWideChar( CP_UTF8, 0, input, -1, NULL, 0 ); + err = translate_errno( size > 0, GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + output->Length = (USHORT)( size * sizeof( wchar_t ) ); + output->Buffer = (PWCHAR) malloc( output->Length ); + require_action( output->Buffer, exit, err = mStatus_NoMemoryErr ); + size = MultiByteToWideChar( CP_UTF8, 0, input, -1, output->Buffer, size ); + err = translate_errno( size > 0, GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + // We're going to subtrace one wchar_t from the size, because we didn't + // include it when we encoded the string + + output->MaximumLength = output->Length; + output->Length -= sizeof( wchar_t ); + +exit: + + if ( err && output->Buffer ) + { + free( output->Buffer ); + output->Buffer = NULL; + } + + return( err ); +} + + + +//=========================================================================================================================== +// MakeUTF8StringFromLsaString +//=========================================================================================================================== + +mDNSlocal OSStatus +MakeUTF8StringFromLsaString( char * output, size_t len, PLSA_UNICODE_STRING input ) +{ + size_t size; + OSStatus err = kNoErr; + + // The Length field of this structure holds the number of bytes, + // but WideCharToMultiByte expects the number of wchar_t's. So + // we divide by sizeof(wchar_t) to get the correct number. + + size = (size_t) WideCharToMultiByte(CP_UTF8, 0, input->Buffer, ( input->Length / sizeof( wchar_t ) ), NULL, 0, NULL, NULL); + err = translate_errno( size != 0, GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + // Ensure that we have enough space (Add one for trailing '\0') + + require_action( ( size + 1 ) <= len, exit, err = mStatus_NoMemoryErr ); + + // Convert the string + + size = (size_t) WideCharToMultiByte( CP_UTF8, 0, input->Buffer, ( input->Length / sizeof( wchar_t ) ), output, (int) size, NULL, NULL); + err = translate_errno( size != 0, GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + // have to add the trailing 0 because WideCharToMultiByte doesn't do it, + // although it does return the correct size + + output[size] = '\0'; + +exit: + + return err; +} + diff --git a/src/tools/mdnssd/Secret.h b/src/tools/mdnssd/Secret.h new file mode 100644 index 00000000000..79643d6242f --- /dev/null +++ b/src/tools/mdnssd/Secret.h @@ -0,0 +1,42 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _Secret_h +#define _Secret_h + +#include "mDNSEmbeddedAPI.h" + + +#if defined(__cplusplus ) +extern "C" { +#endif + + +extern mDNSBool +LsaGetSecret( const char * inDomain, char * outDomain, unsigned outDomainLength, char * outKey, unsigned outKeyLength, char * outSecret, unsigned outSecretLength ); + + +extern mDNSBool +LsaSetSecret( const char * inDomain, const char * inKey, const char * inSecret ); + + +#if defined(__cplusplus) +} +#endif + + +#endif \ No newline at end of file diff --git a/src/tools/mdnssd/Service.c b/src/tools/mdnssd/Service.c new file mode 100644 index 00000000000..406c4e1cd35 --- /dev/null +++ b/src/tools/mdnssd/Service.c @@ -0,0 +1,2597 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include "Poll.h" +#include "CommonServices.h" +#include "DebugServices.h" +#include "RegNames.h" + +#include "uds_daemon.h" +#include "GenLinkedList.h" +#include "Service.h" +#include "EventLog.h" + +#include "Resource.h" + +#include "mDNSEmbeddedAPI.h" +#include "uDNS.h" +#include "mDNSWin32.h" +#include "mDNSDebug.h" + +#include "Firewall.h" + +#if( !TARGET_OS_WINDOWS_CE ) + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif + +#ifndef HeapEnableTerminationOnCorruption +# define HeapEnableTerminationOnCorruption (HEAP_INFORMATION_CLASS)1 +#endif + +#if 0 +#pragma mark == Constants == +#endif + +//=========================================================================================================================== +// Constants +//=========================================================================================================================== + +#define DEBUG_NAME "[mDNSWin32] " +#define kServiceFirewallName L"Bonjour" +#define kServiceDependencies TEXT("Tcpip\0\0") +#define kDNSServiceCacheEntryCountDefault 512 +#define kRetryFirewallPeriod 30 * 1000 +#define kDefValueSize MAX_PATH + 1 +#define kZeroIndex 0 +#define kDefaultRouteMetric 399 +#define kSecondsTo100NSUnits ( 10 * 1000 * 1000 ) +#define kSPSMaintenanceWakePeriod -30 +#define kWaitToRetry (60 * 5) + +#define RR_CACHE_SIZE 500 +static CacheEntity gRRCache[RR_CACHE_SIZE]; +#if 0 +#pragma mark == Structures == +#endif + +#if 0 +#pragma mark == Prototypes == +#endif + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== +static void Usage( void ); +static BOOL WINAPI ConsoleControlHandler( DWORD inControlEvent ); +static OSStatus InstallService( LPCTSTR inName, LPCTSTR inDisplayName, LPCTSTR inDescription, LPCTSTR inPath ); +static OSStatus RemoveService( LPCTSTR inName ); +static OSStatus SetServiceParameters(); +static OSStatus GetServiceParameters(); +static OSStatus CheckFirewall(); +static OSStatus SetServiceInfo( SC_HANDLE inSCM, LPCTSTR inServiceName, LPCTSTR inDescription ); +static void ReportStatus( int inType, const char *inFormat, ... ); + +static void WINAPI ServiceMain( DWORD argc, LPTSTR argv[] ); +static OSStatus ServiceSetupEventLogging( void ); +static DWORD WINAPI ServiceControlHandler( DWORD inControl, DWORD inEventType, LPVOID inEventData, LPVOID inContext ); + +static OSStatus ServiceRun( int argc, LPTSTR argv[] ); +static void ServiceStop( void ); + +static OSStatus ServiceSpecificInitialize( int argc, LPTSTR argv[] ); +static OSStatus ServiceSpecificRun( int argc, LPTSTR argv[] ); +static OSStatus ServiceSpecificStop( void ); +static void ServiceSpecificFinalize( int argc, LPTSTR argv[] ); +static mStatus SetupServiceEvents(); +static mStatus TearDownServiceEvents(); +static mStatus SetupNotifications(); +static mStatus TearDownNotifications(); +static void CALLBACK StopNotification( HANDLE event, void * context ); +static void CALLBACK PowerSuspendNotification( HANDLE event, void * context ); +static void CALLBACK PowerResumeNotification( HANDLE event, void * context ); +static void CALLBACK InterfaceListNotification( SOCKET socket, LPWSANETWORKEVENTS event, void *context ); +static void CALLBACK ComputerDescriptionNotification( HANDLE event, void *context ); +static void CALLBACK TCPChangedNotification( HANDLE event, void *context ); +static void CALLBACK DDNSChangedNotification( HANDLE event, void *context ); +static void CALLBACK FileSharingChangedNotification( HANDLE event, void *context ); +static void CALLBACK FirewallChangedNotification( HANDLE event, void *context ); +static void CALLBACK AdvertisedServicesChangedNotification( HANDLE event, void *context ); +static void CALLBACK SPSWakeupNotification( HANDLE event, void *context ); +static void CALLBACK SPSSleepNotification( HANDLE event, void *context ); +static void CALLBACK UDSAcceptNotification( SOCKET sock, LPWSANETWORKEVENTS event, void *context ); +static void CALLBACK UDSReadNotification( SOCKET sock, LPWSANETWORKEVENTS event, void *context ); +static void CoreCallback(mDNS * const inMDNS, mStatus result); +static mDNSu8 SystemWakeForNetworkAccess( LARGE_INTEGER * timeout ); +static OSStatus GetRouteDestination(DWORD * ifIndex, DWORD * address); +static OSStatus SetLLRoute( mDNS * const inMDNS ); +static bool HaveRoute( PMIB_IPFORWARDROW rowExtant, unsigned long addr, unsigned long metric ); +static bool IsValidAddress( const char * addr ); +static bool IsNortelVPN( IP_ADAPTER_INFO * pAdapter ); +static bool IsJuniperVPN( IP_ADAPTER_INFO * pAdapter ); +static bool IsCiscoVPN( IP_ADAPTER_INFO * pAdapter ); +static const char * strnistr( const char * string, const char * subString, size_t max ); + +#if defined(UNICODE) +# define StrLen(X) wcslen(X) +# define StrCmp(X,Y) wcscmp(X,Y) +#else +# define StrLen(X) strlen(X) +# define StrCmp(X,Y) strcmp(X,Y) +#endif + + +#define kLLNetworkAddr "169.254.0.0" +#define kLLNetworkAddrMask "255.255.0.0" + + +#include "mDNSEmbeddedAPI.h" + +#if 0 +#pragma mark == Globals == +#endif + +//=========================================================================================================================== +// Globals +//=========================================================================================================================== +#define gMDNSRecord mDNSStorage +DEBUG_LOCAL mDNS_PlatformSupport gPlatformStorage; +DEBUG_LOCAL BOOL gServiceQuietMode = FALSE; +DEBUG_LOCAL SERVICE_TABLE_ENTRY gServiceDispatchTable[] = +{ + { kServiceName, ServiceMain }, + { NULL, NULL } +}; +DEBUG_LOCAL HANDLE gStopEvent = NULL; +DEBUG_LOCAL HANDLE gPowerSuspendEvent = NULL; +DEBUG_LOCAL HANDLE gPowerSuspendAckEvent = NULL; +DEBUG_LOCAL HANDLE gPowerResumeEvent = NULL; +DEBUG_LOCAL SOCKET gInterfaceListChangedSocket = INVALID_SOCKET; +DEBUG_LOCAL HKEY gDescKey = NULL; +DEBUG_LOCAL HANDLE gDescChangedEvent = NULL; // Computer description changed event +DEBUG_LOCAL HKEY gTcpipKey = NULL; +DEBUG_LOCAL HANDLE gTcpipChangedEvent = NULL; // TCP/IP config changed +DEBUG_LOCAL HKEY gDdnsKey = NULL; +DEBUG_LOCAL HANDLE gDdnsChangedEvent = NULL; // DynDNS config changed +DEBUG_LOCAL HKEY gFileSharingKey = NULL; +DEBUG_LOCAL HANDLE gFileSharingChangedEvent = NULL; // File Sharing changed +DEBUG_LOCAL HKEY gFirewallKey = NULL; +DEBUG_LOCAL HANDLE gFirewallChangedEvent = NULL; // Firewall changed +DEBUG_LOCAL HKEY gAdvertisedServicesKey = NULL; +DEBUG_LOCAL HANDLE gAdvertisedServicesChangedEvent = NULL; // Advertised services changed +DEBUG_LOCAL SERVICE_STATUS gServiceStatus; +DEBUG_LOCAL SERVICE_STATUS_HANDLE gServiceStatusHandle = NULL; +DEBUG_LOCAL HANDLE gServiceEventSource = NULL; +DEBUG_LOCAL bool gServiceAllowRemote = false; +DEBUG_LOCAL int gServiceCacheEntryCount = 0; // 0 means to use the DNS-SD default. +DEBUG_LOCAL bool gServiceManageLLRouting = true; +DEBUG_LOCAL HANDLE gSPSWakeupEvent = NULL; +DEBUG_LOCAL HANDLE gSPSSleepEvent = NULL; +DEBUG_LOCAL SocketRef gUDSSocket = 0; +DEBUG_LOCAL udsEventCallback gUDSCallback = NULL; +DEBUG_LOCAL BOOL gRetryFirewall = FALSE; +DEBUG_LOCAL BOOL gJustCheckFirewall = FALSE; + +typedef DWORD ( WINAPI * GetIpInterfaceEntryFunctionPtr )( PMIB_IPINTERFACE_ROW ); +mDNSlocal HMODULE gIPHelperLibraryInstance = NULL; +mDNSlocal GetIpInterfaceEntryFunctionPtr gGetIpInterfaceEntryFunctionPtr = NULL; + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// Main +//=========================================================================================================================== +int Main( int argc, LPTSTR argv[] ) +{ + OSStatus err; +// BOOL ok; + BOOL start; + int i; + + HeapSetInformation( NULL, HeapEnableTerminationOnCorruption, NULL, 0 ); + + debug_initialize( kDebugOutputTypeMetaConsole ); + debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelVerbose ); + + // Default to automatically starting the service dispatcher if no extra arguments are specified. + + start = ( argc <= 1 ); + + // Parse arguments. + + for( i = 1; i < argc; ++i ) + { + if( StrCmp( argv[ i ], TEXT("-check-firewall") ) == 0 ) // Check firewall + { + gJustCheckFirewall = TRUE; + start = TRUE; + break; + } else + if( StrCmp( argv[ i ], TEXT("-start") ) == 0 ) // Start + { + start = TRUE; + } + else if( StrCmp( argv[ i ], TEXT("-server") ) == 0 ) // Server + { + err = RunDirect( argc, argv ); + if( err ) + { + ReportStatus( EVENTLOG_ERROR_TYPE, "run service directly failed (%d)\n", err ); + } + goto exit; + } + else if( StrCmp( argv[ i ], TEXT("-q") ) == 0 ) // Quiet Mode (toggle) + { + gServiceQuietMode = !gServiceQuietMode; + } + else if( ( StrCmp( argv[ i ], TEXT("-help") ) == 0 ) || // Help + ( StrCmp( argv[ i ], TEXT("-h") ) == 0 ) ) + { + Usage(); + err = 0; + break; + } + else + { + Usage(); + err = kParamErr; + break; + } + } + + // Start the service dispatcher if requested. This does not return until all services have terminated. If any + // global initialization is needed, it should be done before starting the service dispatcher, but only if it + // will take less than 30 seconds. Otherwise, use a separate thread for it and start the dispatcher immediately. + + if( start ) + { + ServiceMain( argc, argv ); + //ok = StartServiceCtrlDispatcher( gServiceDispatchTable ); + //err = translate_errno( ok, (OSStatus) GetLastError(), kInUseErr ); + //if( err != kNoErr ) + //{ + // ReportStatus( EVENTLOG_ERROR_TYPE, "start service dispatcher failed (%d)\n", err ); + // goto exit; + //} + } + err = 0; + +exit: + dlog( kDebugLevelTrace, DEBUG_NAME "exited (%d %m)\n", err, err ); + _CrtDumpMemoryLeaks(); + return( (int) err ); +} + +//=========================================================================================================================== +// Usage +//=========================================================================================================================== + +static void Usage( void ) +{ + fprintf( stderr, "\n" ); + fprintf( stderr, "mdnsd 1.0d1\n" ); + fprintf( stderr, "\n" ); + fprintf( stderr, " Runs the service normally\n" ); + fprintf( stderr, " -start Starts the service dispatcher after processing all other arguments\n" ); + fprintf( stderr, " -server Runs the service directly as a server (for debugging)\n" ); + fprintf( stderr, " -q Toggles Quiet Mode (no events or output)\n" ); + fprintf( stderr, " -h[elp] Display Help/Usage\n" ); + fprintf( stderr, "\n" ); +} + +//=========================================================================================================================== +// ConsoleControlHandler +//=========================================================================================================================== + +static BOOL WINAPI ConsoleControlHandler( DWORD inControlEvent ) +{ + BOOL handled; + OSStatus err; + + handled = FALSE; + switch( inControlEvent ) + { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + case CTRL_CLOSE_EVENT: + case CTRL_LOGOFF_EVENT: + case CTRL_SHUTDOWN_EVENT: + err = ServiceSpecificStop(); + require_noerr( err, exit ); + + handled = TRUE; + break; + + default: + break; + } + +exit: + return( handled ); +} + +//=========================================================================================================================== +// InstallService +//=========================================================================================================================== + +static OSStatus InstallService( LPCTSTR inName, LPCTSTR inDisplayName, LPCTSTR inDescription, LPCTSTR inPath ) +{ + OSStatus err; + SC_HANDLE scm; + SC_HANDLE service; + BOOL ok; + TCHAR fullPath[ MAX_PATH ]; + TCHAR * namePtr; + DWORD size; + + scm = NULL; + service = NULL; + + // Get a full path to the executable since a relative path may have been specified. + + size = GetFullPathName( inPath, MAX_PATH, fullPath, &namePtr ); + err = translate_errno( size > 0, (OSStatus) GetLastError(), kPathErr ); + require_noerr( err, exit ); + + // Create the service and start it. + + scm = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS ); + err = translate_errno( scm, (OSStatus) GetLastError(), kOpenErr ); + require_noerr( err, exit ); + + service = CreateService( scm, inName, inDisplayName, SERVICE_ALL_ACCESS, SERVICE_WIN32_SHARE_PROCESS, + SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, fullPath, NULL, NULL, kServiceDependencies, + NULL, NULL ); + err = translate_errno( service, (OSStatus) GetLastError(), kDuplicateErr ); + require_noerr( err, exit ); + + err = SetServiceParameters(); + check_noerr( err ); + + if( inDescription ) + { + err = SetServiceInfo( scm, inName, inDescription ); + check_noerr( err ); + } + + ok = StartService( service, 0, NULL ); + err = translate_errno( ok, (OSStatus) GetLastError(), kInUseErr ); + require_noerr( err, exit ); + + ReportStatus( EVENTLOG_SUCCESS, "installed service\n" ); + err = kNoErr; + +exit: + if( service ) + { + CloseServiceHandle( service ); + } + if( scm ) + { + CloseServiceHandle( scm ); + } + return( err ); +} + +//=========================================================================================================================== +// RemoveService +//=========================================================================================================================== + +static OSStatus RemoveService( LPCTSTR inName ) +{ + OSStatus err; + SC_HANDLE scm; + SC_HANDLE service; + BOOL ok; + SERVICE_STATUS status; + + scm = NULL; + service = NULL; + + // Open a connection to the service. + + scm = OpenSCManager( 0, 0, SC_MANAGER_ALL_ACCESS ); + err = translate_errno( scm, (OSStatus) GetLastError(), kOpenErr ); + require_noerr( err, exit ); + + service = OpenService( scm, inName, SERVICE_STOP | SERVICE_QUERY_STATUS | DELETE ); + err = translate_errno( service, (OSStatus) GetLastError(), kNotFoundErr ); + require_noerr( err, exit ); + + // Stop the service, if it is not already stopped, then delete it. + + ok = QueryServiceStatus( service, &status ); + err = translate_errno( ok, (OSStatus) GetLastError(), kAuthenticationErr ); + require_noerr( err, exit ); + + if( status.dwCurrentState != SERVICE_STOPPED ) + { + ok = ControlService( service, SERVICE_CONTROL_STOP, &status ); + check_translated_errno( ok, (OSStatus) GetLastError(), kAuthenticationErr ); + } + + ok = DeleteService( service ); + err = translate_errno( ok, (OSStatus) GetLastError(), kDeletedErr ); + require_noerr( err, exit ); + + ReportStatus( EVENTLOG_SUCCESS, "Removed service\n" ); + err = ERROR_SUCCESS; + +exit: + if( service ) + { + CloseServiceHandle( service ); + } + if( scm ) + { + CloseServiceHandle( scm ); + } + return( err ); +} + + + +//=========================================================================================================================== +// SetServiceParameters +//=========================================================================================================================== + +static OSStatus SetServiceParameters() +{ + DWORD value; + DWORD valueLen = sizeof(DWORD); + DWORD type; + OSStatus err; + HKEY key; + + key = NULL; + + // + // Add/Open Parameters section under service entry in registry + // + err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode, &key ); + require_noerr( err, exit ); + + // + // If the value isn't already there, then we create it + // + err = RegQueryValueEx(key, kServiceManageLLRouting, 0, &type, (LPBYTE) &value, &valueLen); + + if (err != ERROR_SUCCESS) + { + value = 1; + + err = RegSetValueEx( key, kServiceManageLLRouting, 0, REG_DWORD, (const LPBYTE) &value, sizeof(DWORD) ); + require_noerr( err, exit ); + } + +exit: + + if ( key ) + { + RegCloseKey( key ); + } + + return( err ); +} + + + +//=========================================================================================================================== +// GetServiceParameters +//=========================================================================================================================== + +static OSStatus GetServiceParameters() +{ + DWORD value; + DWORD valueLen; + DWORD type; + OSStatus err; + HKEY key; + + key = NULL; + + // + // Add/Open Parameters section under service entry in registry + // + err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode, &key ); + require_noerr( err, exit ); + + valueLen = sizeof(DWORD); + err = RegQueryValueEx(key, kServiceManageLLRouting, 0, &type, (LPBYTE) &value, &valueLen); + if (err == ERROR_SUCCESS) + { + gServiceManageLLRouting = (value) ? true : false; + } + + valueLen = sizeof(DWORD); + err = RegQueryValueEx(key, kServiceCacheEntryCount, 0, &type, (LPBYTE) &value, &valueLen); + if (err == ERROR_SUCCESS) + { + gServiceCacheEntryCount = value; + } + +exit: + + if ( key ) + { + RegCloseKey( key ); + } + + return( err ); +} + + +//=========================================================================================================================== +// CheckFirewall +//=========================================================================================================================== + +static OSStatus CheckFirewall() +{ + DWORD value; + DWORD valueLen; + DWORD type; + ENUM_SERVICE_STATUS * lpService = NULL; + SC_HANDLE sc = NULL; + HKEY key = NULL; + BOOL ok; + DWORD bytesNeeded = 0; + DWORD srvCount; + DWORD resumeHandle = 0; + DWORD srvType; + DWORD srvState; + DWORD dwBytes = 0; + DWORD i; + BOOL isRunning = FALSE; + OSStatus err = kUnknownErr; + + // Check to see if the firewall service is running. If it isn't, then + // we want to return immediately + + 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, err = mStatus_NoMemoryErr ); + } + + err = translate_errno( ok, GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + for ( i = 0; i < srvCount; i++ ) + { + if ( wcscmp( lpService[i].lpServiceName, L"SharedAccess" ) == 0 ) + { + if ( lpService[i].ServiceStatus.dwCurrentState == SERVICE_RUNNING ) + { + isRunning = TRUE; + } + + break; + } + } + + require_action( isRunning, exit, err = kUnknownErr ); + + // Check to see if we've managed the firewall. + // This package might have been installed, then + // the OS was upgraded to SP2 or above. If that's + // the case, then we need to manipulate the firewall + // so networking works correctly. + + err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode, &key ); + require_noerr( err, exit ); + + valueLen = sizeof(DWORD); + err = RegQueryValueEx(key, kServiceManageFirewall, 0, &type, (LPBYTE) &value, &valueLen); + + if ((err != ERROR_SUCCESS) || (value == 0)) + { + wchar_t fullPath[ MAX_PATH ]; + DWORD size; + + // Get a full path to the executable + + size = GetModuleFileNameW( NULL, fullPath, MAX_PATH ); + err = translate_errno( size > 0, (OSStatus) GetLastError(), kPathErr ); + require_noerr( err, exit ); + + err = mDNSAddToFirewall(fullPath, kServiceFirewallName); + require_noerr( err, exit ); + + value = 1; + err = RegSetValueEx( key, kServiceManageFirewall, 0, REG_DWORD, (const LPBYTE) &value, sizeof( DWORD ) ); + require_noerr( err, exit ); + } + +exit: + + if ( key ) + { + RegCloseKey( key ); + } + + if ( lpService ) + { + free( lpService ); + } + + if ( sc ) + { + CloseServiceHandle ( sc ); + } + + return( err ); +} + + + +//=========================================================================================================================== +// SetServiceInfo +//=========================================================================================================================== + +static OSStatus SetServiceInfo( SC_HANDLE inSCM, LPCTSTR inServiceName, LPCTSTR inDescription ) +{ + OSStatus err; + SC_LOCK lock; + SC_HANDLE service; + SERVICE_DESCRIPTION description; + SERVICE_FAILURE_ACTIONS actions; + SC_ACTION action; + BOOL ok; + + check( inServiceName ); + check( inDescription ); + + lock = NULL; + service = NULL; + + // Open the database (if not provided) and lock it to prevent other access while re-configuring. + + if( !inSCM ) + { + inSCM = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS ); + err = translate_errno( inSCM, (OSStatus) GetLastError(), kOpenErr ); + require_noerr( err, exit ); + } + + lock = LockServiceDatabase( inSCM ); + err = translate_errno( lock, (OSStatus) GetLastError(), kInUseErr ); + require_noerr( err, exit ); + + // Open a handle to the service. + + service = OpenService( inSCM, inServiceName, SERVICE_CHANGE_CONFIG|SERVICE_START ); + err = translate_errno( service, (OSStatus) GetLastError(), kNotFoundErr ); + require_noerr( err, exit ); + + // Change the description. + + description.lpDescription = (LPTSTR) inDescription; + ok = ChangeServiceConfig2( service, SERVICE_CONFIG_DESCRIPTION, &description ); + err = translate_errno( ok, (OSStatus) GetLastError(), kParamErr ); + require_noerr( err, exit ); + + actions.dwResetPeriod = INFINITE; + actions.lpRebootMsg = NULL; + actions.lpCommand = NULL; + actions.cActions = 1; + actions.lpsaActions = &action; + action.Delay = 500; + action.Type = SC_ACTION_RESTART; + + ok = ChangeServiceConfig2( service, SERVICE_CONFIG_FAILURE_ACTIONS, &actions ); + err = translate_errno( ok, (OSStatus) GetLastError(), kParamErr ); + require_noerr( err, exit ); + + err = ERROR_SUCCESS; + +exit: + // Close the service and release the lock. + + if( service ) + { + CloseServiceHandle( service ); + } + if( lock ) + { + UnlockServiceDatabase( lock ); + } + return( err ); +} + +//=========================================================================================================================== +// ReportStatus +//=========================================================================================================================== + +static void ReportStatus( int inType, const char *inFormat, ... ) +{ + if( !gServiceQuietMode ) + { + va_list args; + + va_start( args, inFormat ); + if( gServiceEventSource ) + { + char s[ 1024 ]; + BOOL ok; + const char * array[ 1 ]; + + vsprintf( s, inFormat, args ); + array[ 0 ] = s; + ok = ReportEventA( gServiceEventSource, (WORD) inType, 0, MDNSRESPONDER_LOG, NULL, 1, 0, array, NULL ); + check_translated_errno( ok, GetLastError(), kUnknownErr ); + } + else + { + int n; + + n = vfprintf( stderr, inFormat, args ); + check( n >= 0 ); + } + va_end( args ); + } +} + +//=========================================================================================================================== +// RunDirect +//=========================================================================================================================== + +int RunDirect( int argc, LPTSTR argv[] ) +{ + OSStatus err; + BOOL initialized; + BOOL ok; + + initialized = FALSE; + + err = SetupServiceEvents(); + require_noerr( err, exit ); + + // Install a Console Control Handler to handle things like control-c signals. + + ok = SetConsoleCtrlHandler( ConsoleControlHandler, TRUE ); + err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + err = ServiceSpecificInitialize( argc, argv ); + require_noerr( err, exit ); + initialized = TRUE; + + // Run the service. This does not return until the service quits or is stopped. + + ReportStatus( EVENTLOG_INFORMATION_TYPE, "Running service directly\n" ); + + err = ServiceSpecificRun( argc, argv ); + require_noerr( err, exit ); + + // Clean up. + +exit: + if( initialized ) + { + ServiceSpecificFinalize( argc, argv ); + } + + TearDownServiceEvents(); + + return( err ); +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// ServiceMain +//=========================================================================================================================== + +static void WINAPI ServiceMain( DWORD argc, LPTSTR argv[] ) +{ + OSStatus err; + BOOL ok; + + err = SetupServiceEvents(); + require_noerr( err, exit ); + + err = ServiceSetupEventLogging(); + check_noerr( err ); + + //err = GetServiceParameters(); + //check_noerr( err ); + + // Initialize the service status and register the service control handler with the name of the service. + + gServiceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS; + gServiceStatus.dwCurrentState = 0; + gServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN|SERVICE_ACCEPT_POWEREVENT; + gServiceStatus.dwWin32ExitCode = NO_ERROR; + gServiceStatus.dwServiceSpecificExitCode = NO_ERROR; + gServiceStatus.dwCheckPoint = 0; + gServiceStatus.dwWaitHint = 0; + + //gServiceStatusHandle = RegisterServiceCtrlHandlerEx( argv[ 0 ], ServiceControlHandler, NULL ); + //err = translate_errno( gServiceStatusHandle, (OSStatus) GetLastError(), kInUseErr ); + //require_noerr( err, exit ); + + // Mark the service as starting. + + gServiceStatus.dwCurrentState = SERVICE_START_PENDING; + gServiceStatus.dwCheckPoint = 0; + gServiceStatus.dwWaitHint = 5000; // 5 seconds + //ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus ); + //check_translated_errno( ok, GetLastError(), kParamErr ); + + // Run the service. This does not return until the service quits or is stopped. + + err = ServiceRun( (int) argc, argv ); + if( err != kNoErr ) + { + gServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; + gServiceStatus.dwServiceSpecificExitCode = (DWORD) err; + } + + // Service-specific work is done so mark the service as stopped. + + gServiceStatus.dwCurrentState = SERVICE_STOPPED; + //ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus ); + //check_translated_errno( ok, GetLastError(), kParamErr ); + + // Note: The service status handle should not be closed according to Microsoft documentation. + +exit: + + if( gServiceEventSource ) + { + ok = DeregisterEventSource( gServiceEventSource ); + check_translated_errno( ok, GetLastError(), kUnknownErr ); + gServiceEventSource = NULL; + } + + TearDownServiceEvents(); +} + +//=========================================================================================================================== +// ServiceSetupEventLogging +//=========================================================================================================================== + +static OSStatus ServiceSetupEventLogging( void ) +{ + OSStatus err; + HKEY key; + LPCTSTR s; + DWORD typesSupported; + TCHAR path[ MAX_PATH ]; + DWORD n; + + key = NULL; + + // Add/Open source name as a sub-key under the Application key in the EventLog registry key. + + s = TEXT("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\") kServiceName; + err = RegCreateKey( HKEY_LOCAL_MACHINE, s, &key ); + require_noerr( err, exit ); + + // Add the name to the EventMessageFile subkey. + + path[ 0 ] = '\0'; + GetModuleFileName( NULL, path, MAX_PATH ); + n = (DWORD) ( ( StrLen( path ) + 1 ) * sizeof( TCHAR ) ); + err = RegSetValueEx( key, TEXT("EventMessageFile"), 0, REG_EXPAND_SZ, (const LPBYTE) path, n ); + require_noerr( err, exit ); + + // Set the supported event types in the TypesSupported subkey. + + typesSupported = 0 + | 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( err, exit ); + + // Set up the event source. + + gServiceEventSource = RegisterEventSource( NULL, kServiceName ); + err = translate_errno( gServiceEventSource, (OSStatus) GetLastError(), kParamErr ); + require_noerr( err, exit ); + +exit: + if( key ) + { + RegCloseKey( key ); + } + return( err ); +} + + +//=========================================================================================================================== +// ServiceControlHandler +//=========================================================================================================================== + +static DWORD WINAPI ServiceControlHandler( DWORD inControl, DWORD inEventType, LPVOID inEventData, LPVOID inContext ) +{ + BOOL setStatus; + OSStatus err; + BOOL ok; + + DEBUG_UNUSED( inEventData ); + DEBUG_UNUSED( inContext ); + + setStatus = TRUE; + switch( inControl ) + { + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + + dlog( kDebugLevelInfo, DEBUG_NAME "ServiceControlHandler: SERVICE_CONTROL_STOP|SERVICE_CONTROL_SHUTDOWN\n" ); + + ServiceStop(); + setStatus = FALSE; + break; + + case SERVICE_CONTROL_POWEREVENT: + + if (inEventType == PBT_APMSUSPEND) + { + dlog( kDebugLevelInfo, DEBUG_NAME "ServiceControlHandler: PBT_APMSUSPEND\n" ); + + if ( gPowerSuspendEvent ) + { + ok = SetEvent( gPowerSuspendEvent ); + err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr ); + check_noerr( err ); + + switch ( WaitForSingleObject( gPowerSuspendAckEvent, 5 * 1000 ) ) + { + case WAIT_OBJECT_0: + { + // No error + } + break; + + case WAIT_TIMEOUT: + { + dlog( kDebugLevelError, DEBUG_NAME "Timed out waiting for acknowledgement of machine sleep\n" ); + ReportStatus( EVENTLOG_ERROR_TYPE, "Timed out waiting for acknowledgement of machine sleep" ); + } + break; + + default: + { + dlog( kDebugLevelError, DEBUG_NAME "Error waiting for acknowledgement of machine sleep: %d", GetLastError() ); + ReportStatus( EVENTLOG_ERROR_TYPE, "Error waiting for acknowledgement of machine sleep: %d", GetLastError() ); + } + break; + } + } + } + else if (inEventType == PBT_APMRESUMESUSPEND) + { + dlog( kDebugLevelInfo, DEBUG_NAME "ServiceControlHandler: PBT_APMRESUMESUSPEND\n" ); + + if ( gPowerResumeEvent ) + { + ok = SetEvent( gPowerResumeEvent ); + err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr ); + check_noerr( err ); + } + } + + break; + + default: + dlog( kDebugLevelNotice, DEBUG_NAME "ServiceControlHandler: event (0x%08X)\n", inControl ); + break; + } + + if( setStatus && gServiceStatusHandle ) + { + //ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus ); + //check_translated_errno( ok, GetLastError(), kUnknownErr ); + } + + return NO_ERROR; +} + +//=========================================================================================================================== +// ServiceRun +//=========================================================================================================================== + +static OSStatus ServiceRun( int argc, LPTSTR argv[] ) +{ + OSStatus err; + BOOL initialized; + + DEBUG_UNUSED( argc ); + DEBUG_UNUSED( argv ); + + initialized = FALSE; + + // Make the service as running before we call ServiceSpecificInitialize. We've + // had reports that some machines with McAfee firewall installed cause a problem with iTunes installation. + // We think that the firewall product is interferring with code in ServiceSpecificInitialize. So as a + // simple workaround, we'll mark us as running *before* we call ServiceSpecificInitialize. This will unblock + // any installers that are waiting for our state to change. + + gServiceStatus.dwCurrentState = SERVICE_RUNNING; + //ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus ); + //check_translated_errno( ok, GetLastError(), kParamErr ); + + // Initialize the service-specific stuff + + while ( 1 ) + { + DWORD ret; + + //ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service initializing" ); + + err = ServiceSpecificInitialize( argc, argv ); + + if ( !err ) + { + //ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service initialized" ); + break; + } + + ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service initialization failed with err %d. Waiting %d seconds to retry...", err, kWaitToRetry ); + + ret = WaitForSingleObject( gStopEvent, 1000 * kWaitToRetry ); + + if ( ret == WAIT_OBJECT_0 ) + { + ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service received a stop event" ); + goto exit; + } + else if ( ret == WAIT_OBJECT_0 + 1 ) + { + ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service received a power suspend event" ); + } + else if ( ret == WAIT_OBJECT_0 + 2 ) + { + ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service received a power resume event" ); + } + else if ( ret != WAIT_TIMEOUT ) + { + ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service received an error in WaitForSingleObject() : %d, %d", ret, GetLastError() ); + goto exit; + } + } + + initialized = TRUE; + + err = CheckFirewall(); + check_noerr( err ); + + if ( err ) + { + gRetryFirewall = TRUE; + } + + if (gJustCheckFirewall) + goto exit; + // Run the service-specific stuff. This does not return until the service quits or is stopped. + + //ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service started\n" ); + + err = ServiceSpecificRun( argc, argv ); + require_noerr( err, exit ); + +exit: + + // Service stopped. Clean up and we're done. + + ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service stopped (%d)\n", err ); + + if( initialized ) + { + ServiceSpecificFinalize( argc, argv ); + } + + return( err ); +} + +//=========================================================================================================================== +// ServiceStop +//=========================================================================================================================== + +static void ServiceStop( void ) +{ + OSStatus err; + + // Signal the event to cause the service to exit. + + if( gServiceStatusHandle ) + { + gServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; + //ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus ); + //check_translated_errno( ok, GetLastError(), kParamErr ); + } + + err = ServiceSpecificStop(); + check_noerr( err ); +} + + +#if 0 +#pragma mark - +#pragma mark == Service Specific == +#endif + +//=========================================================================================================================== +// ServiceSpecificInitialize +//=========================================================================================================================== + +static OSStatus ServiceSpecificInitialize( int argc, LPTSTR argv[] ) +{ + OSStatus err; + + DEBUG_UNUSED( argc ); + DEBUG_UNUSED( argv ); + + mDNSPlatformMemZero( &gMDNSRecord, sizeof gMDNSRecord); + mDNSPlatformMemZero( &gPlatformStorage, sizeof gPlatformStorage); + + gPlatformStorage.reportStatusFunc = ReportStatus; + + err = mDNS_Init( &gMDNSRecord, &gPlatformStorage, gRRCache, RR_CACHE_SIZE, mDNS_Init_AdvertiseLocalAddresses, CoreCallback, mDNS_Init_NoInitCallbackContext); + require_noerr( err, exit); + + err = SetupNotifications(); + check_noerr( err ); + + err = udsserver_init(mDNSNULL, 0); + require_noerr( err, exit); + + SetLLRoute( &gMDNSRecord ); + +exit: + if( err != kNoErr ) + { + ServiceSpecificFinalize( argc, argv ); + } + return( err ); +} + +//=========================================================================================================================== +// ServiceSpecificRun +//=========================================================================================================================== + +static OSStatus ServiceSpecificRun( int argc, LPTSTR argv[] ) +{ + mDNSBool done = mDNSfalse; + mStatus err = mStatus_NoError; + + DEBUG_UNUSED( argc ); + DEBUG_UNUSED( argv ); + + err = SetupInterfaceList( &gMDNSRecord ); + check( !err ); + + err = uDNS_SetupDNSConfig( &gMDNSRecord ); + check( !err ); + + while( !done ) + { + static mDNSs32 RepeatedBusy = 0; + mDNSs32 nextTimerEvent; + mStatus err; + + // Give the mDNS core a chance to do its work and determine next event time. + + nextTimerEvent = udsserver_idle( mDNS_Execute( &gMDNSRecord ) - mDNS_TimeNow( &gMDNSRecord ) ); + + if ( nextTimerEvent < 0) nextTimerEvent = 0; + else if ( nextTimerEvent > (0x7FFFFFFF / 1000)) nextTimerEvent = 0x7FFFFFFF / mDNSPlatformOneSecond; + else nextTimerEvent = ( nextTimerEvent * 1000) / mDNSPlatformOneSecond; + + // Debugging sanity check, to guard against CPU spins + + if ( nextTimerEvent > 0 ) + { + RepeatedBusy = 0; + } + else + { + nextTimerEvent = 1; + + if ( ++RepeatedBusy >= mDNSPlatformOneSecond ) + { + ShowTaskSchedulingError( &gMDNSRecord ); + RepeatedBusy = 0; + } + } + + if ( gMDNSRecord.ShutdownTime ) + { + mDNSs32 now = mDNS_TimeNow( &gMDNSRecord ); + + if ( mDNS_ExitNow( &gMDNSRecord, now ) ) + { + mDNS_FinalExit( &gMDNSRecord ); + done = TRUE; + break; + } + + if ( nextTimerEvent - gMDNSRecord.ShutdownTime >= 0 ) + { + nextTimerEvent = gMDNSRecord.ShutdownTime; + } + } + + err = mDNSPoll( nextTimerEvent ); + + if ( err ) + { + Sleep( 3 * 1000 ); + + err = SetupInterfaceList( &gMDNSRecord ); + check( !err ); + + err = uDNS_SetupDNSConfig( &gMDNSRecord ); + check( !err ); + + break; + } + } + + return ( err ); +} + + +//=========================================================================================================================== +// ServiceSpecificStop +//=========================================================================================================================== + +static OSStatus ServiceSpecificStop( void ) +{ + OSStatus err; + BOOL ok; + + ok = SetEvent(gStopEvent); + err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + +exit: + + return( err ); +} + +//=========================================================================================================================== +// ServiceSpecificFinalize +//=========================================================================================================================== + +static void ServiceSpecificFinalize( int argc, LPTSTR argv[] ) +{ + DEBUG_UNUSED( argc ); + DEBUG_UNUSED( argv ); + + // + // clean up the notifications + // + TearDownNotifications(); + + // + // clean up loaded library + // + + if( gIPHelperLibraryInstance ) + { + gGetIpInterfaceEntryFunctionPtr = NULL; + + FreeLibrary( gIPHelperLibraryInstance ); + gIPHelperLibraryInstance = NULL; + } +} + + +//=========================================================================================================================== +// SetupServiceEvents +//=========================================================================================================================== + +mDNSlocal mStatus SetupServiceEvents() +{ + mStatus err; + + // Stop Event + + gStopEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + err = translate_errno( gStopEvent, (mStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + +exit: + + if ( err ) + { + TearDownServiceEvents(); + } + + return err; +} + + +//=========================================================================================================================== +// TearDownServiceNotifications +//=========================================================================================================================== + +mDNSlocal mStatus TearDownServiceEvents() +{ + if ( gStopEvent ) + { + CloseHandle( gStopEvent ); + gStopEvent = NULL; + } + + return mStatus_NoError; +} + + +//=========================================================================================================================== +// SetupNotifications +//=========================================================================================================================== + +mDNSlocal mStatus SetupNotifications() +{ + mStatus err; + SocketRef sock; + unsigned long param; + int inBuffer; + int outBuffer; + DWORD outSize; + + require_action( gStopEvent, exit, err = kUnknownErr ); + err = mDNSPollRegisterEvent( gStopEvent, StopNotification, NULL ); + require_noerr( err, exit ); + + // Power Suspend + + gPowerSuspendEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + err = translate_errno( gPowerSuspendEvent, (mStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + err = mDNSPollRegisterEvent( gPowerSuspendEvent, PowerSuspendNotification, NULL ); + require_noerr( err, exit ); + + // Power Suspend Ack + + gPowerSuspendAckEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + err = translate_errno( gPowerSuspendAckEvent, ( mStatus ) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + // Power Resume + + gPowerResumeEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + err = translate_errno( gPowerResumeEvent, (mStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + err = mDNSPollRegisterEvent( gPowerResumeEvent, PowerResumeNotification, NULL ); + require_noerr( err, exit ); + + // Register to listen for address list changes. + + sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); + err = translate_errno( IsValidSocket( sock ), errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + gInterfaceListChangedSocket = sock; + + // Make the socket non-blocking so the WSAIoctl returns immediately with WSAEWOULDBLOCK. It will set the event + // when a change to the interface list is detected. + + param = 1; + err = ioctlsocket( sock, FIONBIO, ¶m ); + err = translate_errno( err == 0, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + inBuffer = 0; + outBuffer = 0; + err = WSAIoctl( sock, SIO_ADDRESS_LIST_CHANGE, &inBuffer, 0, &outBuffer, 0, &outSize, NULL, NULL ); + if( err < 0 ) + { + check( errno_compat() == WSAEWOULDBLOCK ); + } + + err = mDNSPollRegisterSocket( sock, FD_ADDRESS_LIST_CHANGE, InterfaceListNotification, NULL ); + require_noerr( err, exit ); + + gDescChangedEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + err = translate_errno( gDescChangedEvent, (mStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + err = RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\lanmanserver\\parameters"), 0, KEY_READ, &gDescKey); + check_translated_errno( err == 0, errno_compat(), kNameErr ); + + if ( gDescKey != NULL ) + { + err = RegNotifyChangeKeyValue( gDescKey, TRUE, REG_NOTIFY_CHANGE_LAST_SET, gDescChangedEvent, TRUE); + require_noerr( err, exit ); + } + + err = mDNSPollRegisterEvent( gDescChangedEvent, ComputerDescriptionNotification, NULL ); + require_noerr( err, exit ); + + // This will catch all changes to tcp/ip networking, including changes to the domain search list + + gTcpipChangedEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + err = translate_errno( gTcpipChangedEvent, (mStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + err = RegCreateKey( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"), &gTcpipKey ); + require_noerr( err, exit ); + err = RegNotifyChangeKeyValue( gTcpipKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gTcpipChangedEvent, TRUE); + require_noerr( err, exit ); + err = mDNSPollRegisterEvent( gTcpipChangedEvent, TCPChangedNotification, NULL ); + require_noerr( err, exit ); + + // This will catch all changes to ddns configuration + + gDdnsChangedEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + err = translate_errno( gDdnsChangedEvent, (mStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode TEXT("\\DynDNS\\Setup"), &gDdnsKey ); + require_noerr( err, exit ); + err = RegNotifyChangeKeyValue( gDdnsKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gDdnsChangedEvent, TRUE); + require_noerr( err, exit ); + err = mDNSPollRegisterEvent( gDdnsChangedEvent, DDNSChangedNotification, NULL ); + require_noerr( err, exit ); + + // This will catch all changes to file sharing + + gFileSharingChangedEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + err = translate_errno( gFileSharingChangedEvent, (mStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + err = RegCreateKey( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\lanmanserver\\Shares"), &gFileSharingKey ); + + // Just to make sure that initialization doesn't fail on some old OS + // that doesn't have this key, we'll only add the notification if + // the key exists. + + if ( !err ) + { + err = RegNotifyChangeKeyValue( gFileSharingKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gFileSharingChangedEvent, TRUE); + require_noerr( err, exit ); + err = mDNSPollRegisterEvent( gFileSharingChangedEvent, FileSharingChangedNotification, NULL ); + require_noerr( err, exit ); + } + else + { + err = mStatus_NoError; + } + + // This will catch changes to the Windows firewall + + gFirewallChangedEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + err = translate_errno( gFirewallChangedEvent, (mStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + // Just to make sure that initialization doesn't fail on some old OS + // that doesn't have this key, we'll only add the notification if + // the key exists. + + err = RegCreateKey( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\SharedAccess\\Parameters\\FirewallPolicy\\FirewallRules"), &gFirewallKey ); + + if ( !err ) + { + err = RegNotifyChangeKeyValue( gFirewallKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gFirewallChangedEvent, TRUE); + require_noerr( err, exit ); + err = mDNSPollRegisterEvent( gFirewallChangedEvent, FirewallChangedNotification, NULL ); + require_noerr( err, exit ); + } + else + { + err = mStatus_NoError; + } + + // This will catch all changes to advertised services configuration + + gAdvertisedServicesChangedEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + err = translate_errno( gAdvertisedServicesChangedEvent, (mStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode TEXT("\\Services"), &gAdvertisedServicesKey ); + require_noerr( err, exit ); + err = RegNotifyChangeKeyValue( gAdvertisedServicesKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gAdvertisedServicesChangedEvent, TRUE); + require_noerr( err, exit ); + err = mDNSPollRegisterEvent( gAdvertisedServicesChangedEvent, AdvertisedServicesChangedNotification, NULL ); + require_noerr( err, exit ); + + // SPSWakeup timer + + gSPSWakeupEvent = CreateWaitableTimer( NULL, FALSE, NULL ); + err = translate_errno( gSPSWakeupEvent, (mStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + err = mDNSPollRegisterEvent( gSPSWakeupEvent, SPSWakeupNotification, NULL ); + require_noerr( err, exit ); + + // SPSSleep timer + + gSPSSleepEvent = CreateWaitableTimer( NULL, FALSE, NULL ); + err = translate_errno( gSPSSleepEvent, (mStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + err = mDNSPollRegisterEvent( gSPSSleepEvent, SPSSleepNotification, NULL ); + require_noerr( err, exit ); + +exit: + if( err ) + { + TearDownNotifications(); + } + return( err ); +} + +//=========================================================================================================================== +// TearDownNotifications +//=========================================================================================================================== + +mDNSlocal mStatus TearDownNotifications() +{ + if( IsValidSocket( gInterfaceListChangedSocket ) ) + { + mDNSPollUnregisterSocket( gInterfaceListChangedSocket ); + + close_compat( gInterfaceListChangedSocket ); + gInterfaceListChangedSocket = kInvalidSocketRef; + } + + if ( gDescChangedEvent != NULL ) + { + mDNSPollUnregisterEvent( gDescChangedEvent ); + CloseHandle( gDescChangedEvent ); + gDescChangedEvent = NULL; + } + + if ( gDescKey != NULL ) + { + RegCloseKey( gDescKey ); + gDescKey = NULL; + } + + if ( gTcpipChangedEvent != NULL ) + { + mDNSPollUnregisterEvent( gTcpipChangedEvent ); + CloseHandle( gTcpipChangedEvent ); + gTcpipChangedEvent = NULL; + } + + if ( gDdnsChangedEvent != NULL ) + { + mDNSPollUnregisterEvent( gDdnsChangedEvent ); + CloseHandle( gDdnsChangedEvent ); + gDdnsChangedEvent = NULL; + } + + if ( gDdnsKey != NULL ) + { + RegCloseKey( gDdnsKey ); + gDdnsKey = NULL; + } + + if ( gFileSharingChangedEvent != NULL ) + { + mDNSPollUnregisterEvent( gFileSharingChangedEvent ); + CloseHandle( gFileSharingChangedEvent ); + gFileSharingChangedEvent = NULL; + } + + if ( gFileSharingKey != NULL ) + { + RegCloseKey( gFileSharingKey ); + gFileSharingKey = NULL; + } + + if ( gFirewallChangedEvent != NULL ) + { + mDNSPollUnregisterEvent( gFirewallChangedEvent ); + CloseHandle( gFirewallChangedEvent ); + gFirewallChangedEvent = NULL; + } + + if ( gFirewallKey != NULL ) + { + RegCloseKey( gFirewallKey ); + gFirewallKey = NULL; + } + + if ( gAdvertisedServicesChangedEvent != NULL ) + { + mDNSPollUnregisterEvent( gAdvertisedServicesChangedEvent ); + CloseHandle( gAdvertisedServicesChangedEvent ); + gAdvertisedServicesChangedEvent = NULL; + } + + if ( gAdvertisedServicesKey != NULL ) + { + RegCloseKey( gAdvertisedServicesKey ); + gAdvertisedServicesKey = NULL; + } + + if ( gSPSWakeupEvent ) + { + mDNSPollUnregisterEvent( gSPSWakeupEvent ); + CloseHandle( gSPSWakeupEvent ); + gSPSWakeupEvent = NULL; + } + + if ( gSPSSleepEvent ) + { + mDNSPollUnregisterEvent( gSPSSleepEvent ); + CloseHandle( gSPSSleepEvent ); + gSPSSleepEvent = NULL; + } + + if ( gPowerResumeEvent ) + { + mDNSPollUnregisterEvent( gPowerResumeEvent ); + CloseHandle( gPowerResumeEvent ); + gPowerResumeEvent = NULL; + } + + if ( gPowerSuspendAckEvent ) + { + CloseHandle( gPowerSuspendAckEvent ); + gPowerSuspendAckEvent = NULL; + } + + if ( gPowerSuspendEvent ) + { + mDNSPollUnregisterEvent( gPowerSuspendEvent ); + CloseHandle( gPowerSuspendEvent ); + gPowerSuspendEvent = NULL; + } + + if ( gStopEvent ) + { + mDNSPollUnregisterEvent( gStopEvent ); + } + + return( mStatus_NoError ); +} + + +mDNSlocal void CALLBACK +StopNotification( HANDLE event, void *context ) +{ + DEBUG_UNUSED( event ); + DEBUG_UNUSED( context ); + + dlog( kDebugLevelVerbose, DEBUG_NAME "stopping...\n" ); + udsserver_exit(); + mDNS_StartExit( &gMDNSRecord ); +} + + +mDNSlocal void CALLBACK +PowerSuspendNotification( HANDLE event, void * context ) +{ + LARGE_INTEGER timeout; + BOOL ok; + + DEBUG_UNUSED( event ); + DEBUG_UNUSED( context ); + + dlog( kDebugLevelInfo, DEBUG_NAME "PowerSuspendNotification\n" ); + + gMDNSRecord.SystemWakeOnLANEnabled = SystemWakeForNetworkAccess( &timeout ); + + if ( gMDNSRecord.SystemWakeOnLANEnabled ) + { + ok = SetWaitableTimer( gSPSWakeupEvent, &timeout, 0, NULL, NULL, TRUE ); + check( ok ); + } + + mDNSCoreMachineSleep(&gMDNSRecord, TRUE); + + ok = SetEvent( gPowerSuspendAckEvent ); + + if ( !ok ) + { + dlog( kDebugLevelError, DEBUG_NAME "PowerSuspendNotification: error while setting acknowledgement: %d", GetLastError() ); + ReportStatus( EVENTLOG_ERROR_TYPE, "PowerSuspendNotification: error while setting acknowledgement: %d", GetLastError() ); + } +} + + +mDNSlocal void CALLBACK +PowerResumeNotification( HANDLE event, void * context ) +{ + DEBUG_UNUSED( event ); + DEBUG_UNUSED( context ); + + dlog( kDebugLevelInfo, DEBUG_NAME "PowerResumeNotification\n" ); + + if ( gSPSWakeupEvent ) + { + CancelWaitableTimer( gSPSWakeupEvent ); + } + + if ( gSPSSleepEvent ) + { + CancelWaitableTimer( gSPSSleepEvent ); + } + + mDNSCoreMachineSleep(&gMDNSRecord, FALSE); +} + + + +mDNSlocal void CALLBACK +InterfaceListNotification( SOCKET socket, LPWSANETWORKEVENTS event, void *context ) +{ + int inBuffer; + int outBuffer; + DWORD outSize; + int err; + + DEBUG_UNUSED( socket ); + DEBUG_UNUSED( event ); + DEBUG_UNUSED( context ); + + // It would be nice to come up with a more elegant solution to this, but it seems that + // GetAdaptersAddresses doesn't always stay in sync after network changed events. So as + // as a simple workaround, we'll pause for a couple of seconds before processing the change. + + // We arrived at 2 secs by trial and error. We could reproduce the problem after sleeping + // for 500 msec and 750 msec, but couldn't after sleeping for 1 sec. We added another + // second on top of that to account for machine load or some other exigency. + + Sleep( 2000 ); + + // Interface list changed event. Break out of the inner loop to re-setup the wait list. + + InterfaceListDidChange( &gMDNSRecord ); + + // reset the event handler + inBuffer = 0; + outBuffer = 0; + err = WSAIoctl( gInterfaceListChangedSocket, SIO_ADDRESS_LIST_CHANGE, &inBuffer, 0, &outBuffer, 0, &outSize, NULL, NULL ); + if( err < 0 ) + { + check( errno_compat() == WSAEWOULDBLOCK ); + } +} + + +mDNSlocal void CALLBACK +ComputerDescriptionNotification( HANDLE event, void *context ) +{ + // The computer description might have changed + + DEBUG_UNUSED( event ); + DEBUG_UNUSED( context ); + + ComputerDescriptionDidChange( &gMDNSRecord ); + udsserver_handle_configchange( &gMDNSRecord ); + + // and reset the event handler + if ( ( gDescKey != NULL ) && ( gDescChangedEvent != NULL ) ) + { + int err = RegNotifyChangeKeyValue( gDescKey, TRUE, REG_NOTIFY_CHANGE_LAST_SET, gDescChangedEvent, TRUE); + check_noerr( err ); + } +} + + +mDNSlocal void CALLBACK +TCPChangedNotification( HANDLE event, void *context ) +{ + // The TCP/IP might have changed + + DEBUG_UNUSED( event ); + DEBUG_UNUSED( context ); + + TCPIPConfigDidChange( &gMDNSRecord ); + udsserver_handle_configchange( &gMDNSRecord ); + + // and reset the event handler + + if ( ( gTcpipKey != NULL ) && ( gTcpipChangedEvent ) ) + { + int err = RegNotifyChangeKeyValue( gTcpipKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gTcpipChangedEvent, TRUE ); + check_noerr( err ); + } +} + + +mDNSlocal void CALLBACK +DDNSChangedNotification( HANDLE event, void *context ) +{ + // The DynDNS config might have changed + + DEBUG_UNUSED( event ); + DEBUG_UNUSED( context ); + + DynDNSConfigDidChange( &gMDNSRecord ); + udsserver_handle_configchange( &gMDNSRecord ); + + // and reset the event handler + + if ((gDdnsKey != NULL) && (gDdnsChangedEvent)) + { + int err = RegNotifyChangeKeyValue(gDdnsKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gDdnsChangedEvent, TRUE); + check_noerr( err ); + } +} + + +mDNSlocal void CALLBACK +FileSharingChangedNotification( HANDLE event, void *context ) +{ + // File sharing changed + + DEBUG_UNUSED( event ); + DEBUG_UNUSED( context ); + + FileSharingDidChange( &gMDNSRecord ); + + // and reset the event handler + + if ((gFileSharingKey != NULL) && (gFileSharingChangedEvent)) + { + int err = RegNotifyChangeKeyValue(gFileSharingKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gFileSharingChangedEvent, TRUE); + check_noerr( err ); + } +} + + +mDNSlocal void CALLBACK +FirewallChangedNotification( HANDLE event, void *context ) +{ + // Firewall configuration changed + + DEBUG_UNUSED( event ); + DEBUG_UNUSED( context ); + + FirewallDidChange( &gMDNSRecord ); + + // and reset the event handler + + if ((gFirewallKey != NULL) && (gFirewallChangedEvent)) + { + int err = RegNotifyChangeKeyValue(gFirewallKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gFirewallChangedEvent, TRUE); + check_noerr( err ); + } +} + + +mDNSlocal void CALLBACK +AdvertisedServicesChangedNotification( HANDLE event, void *context ) +{ + // Ultimately we'll want to manage multiple services, but right now the only service + // we'll be managing is SMB. + + DEBUG_UNUSED( event ); + DEBUG_UNUSED( context ); + + FileSharingDidChange( &gMDNSRecord ); + + // and reset the event handler + + if ( ( gAdvertisedServicesKey != NULL ) && ( gAdvertisedServicesChangedEvent ) ) + { + int err = RegNotifyChangeKeyValue(gAdvertisedServicesKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gAdvertisedServicesChangedEvent, TRUE); + check_noerr( err ); + } +} + + +mDNSlocal void CALLBACK +SPSWakeupNotification( HANDLE event, void *context ) +{ + LARGE_INTEGER timeout; + + DEBUG_UNUSED( event ); + DEBUG_UNUSED( context ); + + ReportStatus( EVENTLOG_INFORMATION_TYPE, "Maintenance wake" ); + + timeout.QuadPart = kSPSMaintenanceWakePeriod; + timeout.QuadPart *= kSecondsTo100NSUnits; + + SetWaitableTimer( gSPSSleepEvent, &timeout, 0, NULL, NULL, TRUE ); +} + + +mDNSlocal void CALLBACK +SPSSleepNotification( HANDLE event, void *context ) +{ + DEBUG_UNUSED( event ); + DEBUG_UNUSED( context ); + + ReportStatus( EVENTLOG_INFORMATION_TYPE, "Returning to sleep after maintenance wake" ); + + // Calling SetSuspendState() doesn't invoke our sleep handlers, so we'll + // call HandlePowerSuspend() explicity. This will reset the + // maintenance wake timers. + + PowerSuspendNotification( gPowerSuspendEvent, NULL ); + SetSuspendState( FALSE, FALSE, FALSE ); +} + + +//=========================================================================================================================== +// CoreCallback +//=========================================================================================================================== + +static void +CoreCallback(mDNS * const inMDNS, mStatus status) +{ + if (status == mStatus_ConfigChanged) + { + SetLLRoute( inMDNS ); + } +} + + +//=========================================================================================================================== +// UDSAcceptNotification +//=========================================================================================================================== + +mDNSlocal void CALLBACK +UDSAcceptNotification( SOCKET sock, LPWSANETWORKEVENTS event, void *context ) +{ + ( void ) sock; + ( void ) event; + ( void ) context; + + if ( gUDSCallback ) + { + gUDSCallback( ( int ) gUDSSocket, 0, context ); + } +} + + +//=========================================================================================================================== +// UDSReadNotification +//=========================================================================================================================== + +mDNSlocal void CALLBACK +UDSReadNotification( SOCKET sock, LPWSANETWORKEVENTS event, void *context ) +{ + TCPSocket *tcpSock = ( TCPSocket* ) context; + + ( void ) sock; + ( void ) event; + + if ( tcpSock ) + { + tcpSock->userCallback( ( int ) tcpSock->fd, 0, tcpSock->userContext ); + } +} + + +//=========================================================================================================================== +// udsSupportAddFDToEventLoop +//=========================================================================================================================== + +mStatus +udsSupportAddFDToEventLoop( SocketRef fd, udsEventCallback callback, void *context, void **platform_data) +{ + mStatus err = mStatus_NoError; + + // We are using some knowledge of what is being passed to us here. If the fd is a listen socket, + // then the "context" parameter is NULL. If it is an actual read/write socket, then the "context" + // parameter is not null. + + if ( context ) + { + TCPSocket * sock; + + sock = malloc( sizeof( TCPSocket ) ); + require_action( sock, exit, err = mStatus_NoMemoryErr ); + mDNSPlatformMemZero( sock, sizeof( TCPSocket ) ); + + sock->fd = (SOCKET) fd; + sock->userCallback = callback; + sock->userContext = context; + sock->m = &gMDNSRecord; + + *platform_data = sock; + + err = mDNSPollRegisterSocket( sock->fd, FD_READ | FD_CLOSE, UDSReadNotification, sock ); + require_noerr( err, exit ); + } + else + { + gUDSSocket = fd; + gUDSCallback = callback; + + err = mDNSPollRegisterSocket( gUDSSocket, FD_ACCEPT | FD_CLOSE, UDSAcceptNotification, NULL ); + require_noerr( err, exit ); + } + +exit: + + return err; +} + + +int +udsSupportReadFD( SocketRef fd, char *buf, int len, int flags, void *platform_data ) +{ + TCPSocket * sock; + mDNSBool closed; + int ret; + + ( void ) flags; + + sock = ( TCPSocket* ) platform_data; + require_action( sock, exit, ret = -1 ); + require_action( sock->fd == fd, exit, ret = -1 ); + + ret = mDNSPlatformReadTCP( sock, buf, len, &closed ); + + if ( closed ) + { + ret = 0; + } + else if ( !ret && ( WSAGetLastError() == WSAEWOULDBLOCK ) ) + { + // mDNSPlatformReadTCP will return 0 if it gets WSAEWOULDBLOCK, but + // that caller of this routine interprets that as close connection. + // We'll fix that by returning -1 in that case. + + ret = -1; + } + +exit: + + return ret; +} + + +mStatus +udsSupportRemoveFDFromEventLoop( SocketRef fd, void *platform_data) // Note: This also CLOSES the socket +{ + mStatus err = kNoErr; + + mDNSPollUnregisterSocket( fd ); + + if ( platform_data != NULL ) + { + TCPSocket * sock; + + dlog( kDebugLevelInfo, DEBUG_NAME "session closed\n" ); + sock = ( TCPSocket* ) platform_data; + check( sock->fd == fd ); + mDNSPlatformTCPCloseConnection( sock ); + } + + return err; +} + + +mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay) + { + (void)m; + (void)delay; + // No-op, for now + } + + +//=========================================================================================================================== +// SystemWakeForNetworkAccess +//=========================================================================================================================== + +mDNSu8 +SystemWakeForNetworkAccess( LARGE_INTEGER * timeout ) +{ + HKEY key = NULL; + DWORD dwSize; + DWORD enabled; + mDNSu8 ok; + SYSTEM_POWER_STATUS powerStatus; + time_t startTime; + time_t nextWakeupTime; + int delta; + DWORD err; + + dlog( kDebugLevelInfo, DEBUG_NAME "SystemWakeForNetworkAccess\n" ); + + // Make sure we have a timer + + require_action( gSPSWakeupEvent != NULL, exit, ok = FALSE ); + require_action( gSPSSleepEvent != NULL, exit, ok = FALSE ); + + // Make sure the user enabled bonjour sleep proxy client + + err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\Power Management", &key ); + require_action( !err, exit, ok = FALSE ); + dwSize = sizeof( DWORD ); + err = RegQueryValueEx( key, L"Enabled", NULL, NULL, (LPBYTE) &enabled, &dwSize ); + require_action( !err, exit, ok = FALSE ); + require_action( enabled, exit, ok = FALSE ); + + // Make sure machine is on AC power + + ok = ( mDNSu8 ) GetSystemPowerStatus( &powerStatus ); + require_action( ok, exit, ok = FALSE ); + require_action( powerStatus.ACLineStatus == AC_LINE_ONLINE, exit, ok = FALSE ); + + // Now make sure we have a network interface that does wake-on-lan + + ok = ( mDNSu8 ) IsWOMPEnabled( &gMDNSRecord ); + require_action( ok, exit, ok = FALSE ); + + // Now make sure we have advertised services. Doesn't make sense to + // enable sleep proxy if we have no multicast services that could + // potentially wake us up. + + ok = ( mDNSu8 ) mDNSCoreHaveAdvertisedMulticastServices( &gMDNSRecord ); + require_action( ok, exit, ok = FALSE ); + + // Calculate next wake up time + + startTime = time( NULL ); // Seconds since midnight January 1, 1970 + nextWakeupTime = startTime + ( 120 * 60 ); // 2 hours later + + if ( gMDNSRecord.p->nextDHCPLeaseExpires < nextWakeupTime ) + { + nextWakeupTime = gMDNSRecord.p->nextDHCPLeaseExpires; + } + + // Finally calculate the next relative wakeup time + + delta = ( int )( ( ( double )( nextWakeupTime - startTime ) ) * 0.9 ); + ReportStatus( EVENTLOG_INFORMATION_TYPE, "enabling sleep proxy client with next maintenance wake in %d seconds", delta ); + + // Convert seconds to 100 nanosecond units expected by SetWaitableTimer + + timeout->QuadPart = -delta; + timeout->QuadPart *= kSecondsTo100NSUnits; + + ok = TRUE; + +exit: + + if ( key ) + { + RegCloseKey( key ); + } + + return ok; +} + + +//=========================================================================================================================== +// HaveRoute +//=========================================================================================================================== + +static bool +HaveRoute( PMIB_IPFORWARDROW rowExtant, unsigned long addr, unsigned long metric ) +{ + PMIB_IPFORWARDTABLE pIpForwardTable = NULL; + DWORD dwSize = 0; + BOOL bOrder = FALSE; + OSStatus err; + bool found = false; + unsigned long int i; + + // + // Find out how big our buffer needs to be. + // + err = GetIpForwardTable(NULL, &dwSize, bOrder); + require_action( err == ERROR_INSUFFICIENT_BUFFER, exit, err = kUnknownErr ); + + // + // Allocate the memory for the table + // + pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc( dwSize ); + require_action( pIpForwardTable, exit, err = kNoMemoryErr ); + + // + // Now get the table. + // + err = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder); + require_noerr( err, exit ); + + // + // Search for the row in the table we want. + // + for ( i = 0; i < pIpForwardTable->dwNumEntries; i++) + { + if ( ( pIpForwardTable->table[i].dwForwardDest == addr ) && ( !metric || ( pIpForwardTable->table[i].dwForwardMetric1 == metric ) ) ) + { + memcpy( rowExtant, &(pIpForwardTable->table[i]), sizeof(*rowExtant) ); + found = true; + break; + } + } + +exit: + + if ( pIpForwardTable != NULL ) + { + free(pIpForwardTable); + } + + return found; +} + + +//=========================================================================================================================== +// IsValidAddress +//=========================================================================================================================== + +static bool +IsValidAddress( const char * addr ) +{ + return ( addr && ( strcmp( addr, "0.0.0.0" ) != 0 ) ) ? true : false; +} + + +//=========================================================================================================================== +// GetAdditionalMetric +//=========================================================================================================================== + +static ULONG +GetAdditionalMetric( DWORD ifIndex ) +{ + ULONG metric = 0; + + if( !gIPHelperLibraryInstance ) + { + gIPHelperLibraryInstance = LoadLibrary( TEXT( "Iphlpapi" ) ); + + gGetIpInterfaceEntryFunctionPtr = + (GetIpInterfaceEntryFunctionPtr) GetProcAddress( gIPHelperLibraryInstance, "GetIpInterfaceEntry" ); + + if( !gGetIpInterfaceEntryFunctionPtr ) + { + BOOL ok; + + ok = FreeLibrary( gIPHelperLibraryInstance ); + check_translated_errno( ok, GetLastError(), kUnknownErr ); + gIPHelperLibraryInstance = NULL; + } + } + + if ( gGetIpInterfaceEntryFunctionPtr ) + { + MIB_IPINTERFACE_ROW row; + DWORD err; + + ZeroMemory( &row, sizeof( MIB_IPINTERFACE_ROW ) ); + row.Family = AF_INET; + row.InterfaceIndex = ifIndex; + err = gGetIpInterfaceEntryFunctionPtr( &row ); + require_noerr( err, exit ); + metric = row.Metric + 256; + } + +exit: + + return metric; +} + + +//=========================================================================================================================== +// SetLLRoute +//=========================================================================================================================== + +static OSStatus +SetLLRoute( mDNS * const inMDNS ) +{ + OSStatus err = kNoErr; + + DEBUG_UNUSED( inMDNS ); + + // + // Don't call SetLLRoute on loopback + // Default route on Windows 7 breaks network connectivity + // + // Don't mess w/ the routing table on Vista and later OSes, as + // they have a permanent route to link-local addresses. Otherwise, + // set a route to link local addresses (169.254.0.0) + // + if ( ( inMDNS->p->osMajorVersion < 6 ) && gServiceManageLLRouting && !gPlatformStorage.registeredLoopback4 ) + { + DWORD ifIndex; + MIB_IPFORWARDROW rowExtant; + bool addRoute; + MIB_IPFORWARDROW row; + + ZeroMemory(&row, sizeof(row)); + + err = GetRouteDestination(&ifIndex, &row.dwForwardNextHop); + require_noerr( err, exit ); + row.dwForwardDest = inet_addr(kLLNetworkAddr); + row.dwForwardIfIndex = ifIndex; + row.dwForwardMask = inet_addr(kLLNetworkAddrMask); + row.dwForwardType = 3; + row.dwForwardProto = MIB_IPPROTO_NETMGMT; + row.dwForwardAge = 0; + row.dwForwardPolicy = 0; + row.dwForwardMetric1 = 20 + GetAdditionalMetric( ifIndex ); + row.dwForwardMetric2 = (DWORD) - 1; + row.dwForwardMetric3 = (DWORD) - 1; + row.dwForwardMetric4 = (DWORD) - 1; + row.dwForwardMetric5 = (DWORD) - 1; + + addRoute = true; + + // + // check to make sure we don't already have a route + // + if ( HaveRoute( &rowExtant, inet_addr( kLLNetworkAddr ), 0 ) ) + { + // + // set the age to 0 so that we can do a memcmp. + // + rowExtant.dwForwardAge = 0; + + // + // check to see if this route is the same as our route + // + if (memcmp(&row, &rowExtant, sizeof(row)) != 0) + { + // + // if it isn't then delete this entry + // + DeleteIpForwardEntry(&rowExtant); + } + else + { + // + // else it is, so we don't want to create another route + // + addRoute = false; + } + } + + if (addRoute && row.dwForwardNextHop) + { + err = CreateIpForwardEntry(&row); + check_noerr( err ); + } + } + +exit: + + return ( err ); +} + + +//=========================================================================================================================== +// GetRouteDestination +//=========================================================================================================================== + +static OSStatus +GetRouteDestination(DWORD * ifIndex, DWORD * address) +{ + struct in_addr ia; + IP_ADAPTER_INFO * pAdapterInfo = NULL; + IP_ADAPTER_INFO * pAdapter = NULL; + ULONG bufLen; + mDNSBool done = mDNSfalse; + OSStatus err; + + // + // GetBestInterface will fail if there is no default gateway + // configured. If that happens, we will just take the first + // interface in the list. MSDN support says there is no surefire + // way to manually determine what the best interface might + // be for a particular network address. + // + ia.s_addr = inet_addr(kLLNetworkAddr); + err = GetBestInterface(*(IPAddr*) &ia, ifIndex); + + if (err) + { + *ifIndex = 0; + } + + // + // Make an initial call to GetAdaptersInfo to get + // the necessary size into the bufLen variable + // + err = GetAdaptersInfo( NULL, &bufLen); + require_action( err == ERROR_BUFFER_OVERFLOW, exit, err = kUnknownErr ); + + pAdapterInfo = (IP_ADAPTER_INFO*) malloc( bufLen ); + require_action( pAdapterInfo, exit, err = kNoMemoryErr ); + + err = GetAdaptersInfo( pAdapterInfo, &bufLen); + require_noerr( err, exit ); + + pAdapter = pAdapterInfo; + err = kUnknownErr; + + // + // + // + // Look for the Nortel VPN virtual interface, along with Juniper virtual interface. + // + // If these interfaces are active (i.e., has a non-zero IP Address), + // then we want to disable routing table modifications. + + while (pAdapter) + { + if ( ( IsNortelVPN( pAdapter ) || IsJuniperVPN( pAdapter ) || IsCiscoVPN( pAdapter ) ) && + ( inet_addr( pAdapter->IpAddressList.IpAddress.String ) != 0 ) ) + { + dlog( kDebugLevelTrace, DEBUG_NAME "disabling routing table management due to VPN incompatibility" ); + goto exit; + } + + pAdapter = pAdapter->Next; + } + + while ( !done ) + { + pAdapter = pAdapterInfo; + err = kUnknownErr; + + while (pAdapter) + { + // If we don't have an interface selected, choose the first one that is of type ethernet and + // has a valid IP Address + + if ((pAdapter->Type == MIB_IF_TYPE_ETHERNET) && ( IsValidAddress( pAdapter->IpAddressList.IpAddress.String ) ) && (!(*ifIndex) || (pAdapter->Index == (*ifIndex)))) + { + *address = inet_addr( pAdapter->IpAddressList.IpAddress.String ); + *ifIndex = pAdapter->Index; + err = kNoErr; + break; + } + + pAdapter = pAdapter->Next; + } + + // If we found the right interface, or we weren't trying to find a specific interface then we're done + + if ( !err || !( *ifIndex) ) + { + done = mDNStrue; + } + + // Otherwise, try again by wildcarding the interface + + else + { + *ifIndex = 0; + } + } + +exit: + + if ( pAdapterInfo != NULL ) + { + free( pAdapterInfo ); + } + + return( err ); +} + + +static bool +IsNortelVPN( IP_ADAPTER_INFO * pAdapter ) +{ + return ((pAdapter->Type == MIB_IF_TYPE_ETHERNET) && + (pAdapter->AddressLength == 6) && + (pAdapter->Address[0] == 0x44) && + (pAdapter->Address[1] == 0x45) && + (pAdapter->Address[2] == 0x53) && + (pAdapter->Address[3] == 0x54) && + (pAdapter->Address[4] == 0x42) && + (pAdapter->Address[5] == 0x00)) ? true : false; +} + + +static bool +IsJuniperVPN( IP_ADAPTER_INFO * pAdapter ) +{ + return ( strnistr( pAdapter->Description, "Juniper", sizeof( pAdapter->Description ) ) != NULL ) ? true : false; +} + + +static bool +IsCiscoVPN( IP_ADAPTER_INFO * pAdapter ) +{ + return ((pAdapter->Type == MIB_IF_TYPE_ETHERNET) && + (pAdapter->AddressLength == 6) && + (pAdapter->Address[0] == 0x00) && + (pAdapter->Address[1] == 0x05) && + (pAdapter->Address[2] == 0x9a) && + (pAdapter->Address[3] == 0x3c) && + (pAdapter->Address[4] == 0x7a) && + (pAdapter->Address[5] == 0x00)) ? true : false; +} + + +static const char * +strnistr( const char * string, const char * subString, size_t max ) +{ + size_t subStringLen; + size_t offset; + size_t maxOffset; + size_t stringLen; + const char * pPos; + + if ( ( string == NULL ) || ( subString == NULL ) ) + { + return string; + } + + stringLen = ( max > strlen( string ) ) ? strlen( string ) : max; + + if ( stringLen == 0 ) + { + return NULL; + } + + subStringLen = strlen( subString ); + + if ( subStringLen == 0 ) + { + return string; + } + + if ( subStringLen > stringLen ) + { + return NULL; + } + + maxOffset = stringLen - subStringLen; + pPos = string; + + for ( offset = 0; offset <= maxOffset; offset++ ) + { + if ( _strnicmp( pPos, subString, subStringLen ) == 0 ) + { + return pPos; + } + + pPos++; + } + + return NULL; +} + diff --git a/src/tools/mdnssd/Service.h b/src/tools/mdnssd/Service.h new file mode 100644 index 00000000000..0b806f002b6 --- /dev/null +++ b/src/tools/mdnssd/Service.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MDNS_SERVICE_H__ +#define __MDNS_SERVICE_H__ + + +#include + + +extern int RunDirect( int argc, LPTSTR argv[] ); +extern int Main( int argc, LPTSTR argv[] ); + + +#endif + diff --git a/src/tools/mdnssd/Service.rc b/src/tools/mdnssd/Service.rc new file mode 100644 index 00000000000..92c2883dc9f --- /dev/null +++ b/src/tools/mdnssd/Service.rc @@ -0,0 +1,115 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +//#include "winres.h" +//#include "WinVersRes.h" +#include "windows.h" +#include "EventLog.rc" +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,0 + PRODUCTVERSION 1,0,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Nokia Corporation and/or its subsidiary(-ies)\0" + VALUE "FileDescription", "Bonjour Service Fallback\0" + VALUE "FileVersion", 1,0,0,0 + VALUE "InternalName", "mdnssd.exe\0" + VALUE "LegalCopyright", "Copyright(c)2003-2012, Apple Computer, 2012 Nokia Corporation and/or its subsidiary(-ies).\0" + VALUE "OriginalFilename", "mdnssd.exe\0" + VALUE "ProductName", "Bonjour Fallback\0" + VALUE "ProductVersion", "1.0\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "#include ""WinVersRes.h""\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_SERVICE_DESCRIPTION "Enables hardware devices and software services to automatically configure themselves on the network and advertise their presence, so that users can discover and use those services without any unnecessary manual setup or administration." +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/tools/mdnssd/mDNS.c b/src/tools/mdnssd/mDNS.c index 9de7f92c181..d93f5451527 100644 --- a/src/tools/mdnssd/mDNS.c +++ b/src/tools/mdnssd/mDNS.c @@ -1884,7 +1884,7 @@ mDNSlocal mDNSu16 CheckSum(const void *const data, mDNSs32 length, mDNSu32 sum) while (length > 0) { length -= 2; sum += *ptr++; } sum = (sum & 0xFFFF) + (sum >> 16); sum = (sum & 0xFFFF) + (sum >> 16); - return(sum != 0xFFFF ? sum : 0); + return (mDNSu16)(sum != 0xFFFF ? sum : 0); } mDNSlocal mDNSu16 IPv6CheckSum(const mDNSv6Addr *const src, const mDNSv6Addr *const dst, const mDNSu8 protocol, const void *const data, const mDNSu32 length) @@ -1892,10 +1892,10 @@ mDNSlocal mDNSu16 IPv6CheckSum(const mDNSv6Addr *const src, const mDNSv6Addr *co IPv6PseudoHeader ph; ph.src = *src; ph.dst = *dst; - ph.len.b[0] = length >> 24; - ph.len.b[1] = length >> 16; - ph.len.b[2] = length >> 8; - ph.len.b[3] = length; + ph.len.b[0] = (0xFF & (length >> 24)); + ph.len.b[1] = (0xFF & (length >> 16)); + ph.len.b[2] = (0xFF & (length >> 8)); + ph.len.b[3] = (0xFF & length); ph.pro.b[0] = 0; ph.pro.b[1] = 0; ph.pro.b[2] = 0; @@ -7706,23 +7706,23 @@ mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const questi } } -mDNSexport McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, mDNSu32 timeout) +mDNSexport McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname *d, const mDNSInterfaceID interface1, mDNSu32 timeout) { McastResolver **p = &m->McastResolvers; McastResolver *tmp = mDNSNULL; if (!d) d = (const domainname *)""; - LogInfo("mDNS_AddMcastResolver: Adding %##s, InterfaceID %p, timeout %u", d->c, interface, timeout); + LogInfo("mDNS_AddMcastResolver: Adding %##s, InterfaceID %p, timeout %u", d->c, interface1, timeout); if (m->mDNS_busy != m->mDNS_reentrancy+1) LogMsg("mDNS_AddMcastResolver: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); while (*p) // Check if we already have this {interface, domain} tuple registered { - if ((*p)->interface == interface && SameDomainName(&(*p)->domain, d)) + if ((*p)->interface1 == interface1 && SameDomainName(&(*p)->domain, d)) { - if (!((*p)->flags & DNSServer_FlagDelete)) LogMsg("Note: Mcast Resolver domain %##s (%p) registered more than once", d->c, interface); + if (!((*p)->flags & DNSServer_FlagDelete)) LogMsg("Note: Mcast Resolver domain %##s (%p) registered more than once", d->c, interface1); (*p)->flags &= ~DNSServer_FlagDelete; tmp = *p; *p = tmp->next; @@ -7740,7 +7740,7 @@ mDNSexport McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname if (!*p) LogMsg("mDNS_AddMcastResolver: ERROR!! - malloc"); else { - (*p)->interface = interface; + (*p)->interface1 = interface1; (*p)->flags = DNSServer_FlagNew; (*p)->timeout = timeout; AssignDomainName(&(*p)->domain, d); @@ -7905,13 +7905,13 @@ mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question) // // Note: DNS configuration change will help pick the new dns servers but currently it does not affect the timeout - if (curr->scoped && curr->interface == mDNSInterface_Any) + if (curr->scoped && curr->interface1 == mDNSInterface_Any) { debugf("SetValidDNSServers: Scoped DNS server %#a (Domain %##s) with Interface Any", &curr->addr, curr->domain.c); continue; } currcount = CountLabels(&curr->domain); if ((!DEQuery || !curr->cellIntf) && ((!curr->scoped && (!question->InterfaceID || (question->InterfaceID == mDNSInterface_Unicast))) || - (curr->interface == question->InterfaceID))) + (curr->interface1 == question->InterfaceID))) { bettermatch = BetterMatchForName(&question->qname, namecount, &curr->domain, currcount, bestmatchlen); @@ -7926,7 +7926,7 @@ mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question) if (bettermatch) { debugf("SetValidDNSServers: Resetting all the bits"); question->validDNSServers = zeroOpaque64; timeout = 0; } debugf("SetValidDNSServers: question %##s Setting the bit for DNS server Address %#a (Domain %##s), Scoped:%d index %d," " Timeout %d, interface %p", question->qname.c, &curr->addr, curr->domain.c, curr->scoped, index, curr->timeout, - curr->interface); + curr->interface1); timeout += curr->timeout; if (DEQuery) debugf("DomainEnumQuery: Question %##s, DNSServer %#a, cell %d", question->qname.c, &curr->addr, curr->cellIntf); bit_set_opaque64(question->validDNSServers, index); @@ -7993,7 +7993,7 @@ mDNSlocal DNSServer *GetBestServer(mDNS *m, const domainname *name, mDNSInterfac // is the new way of specifying an InterfaceID option for DNSServer. These will be considered // only when the question has non-zero interfaceID. - if ((!curr->scoped && !InterfaceID) || (curr->interface == InterfaceID)) + if ((!curr->scoped && !InterfaceID) || (curr->interface1 == InterfaceID)) { // If we know that all the names are already equally good matches, then skip calling BetterMatchForName. @@ -9065,7 +9065,7 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, query->ServiceInfoQueryContext = Context; // info->name = Must already be set up by client -// info->interface = Must already be set up by client +// info->interface1 = Must already be set up by client info->ip = zeroAddr; info->port = zeroIPPort; info->TXTlen = 0; @@ -9713,9 +9713,9 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se // 3. Any DNS servers specific to this interface are now unusable for (s = m->DNSServers; s; s = s->next) - if (s->interface == set->InterfaceID) + if (s->interface1 == set->InterfaceID) { - s->interface = mDNSInterface_Any; + s->interface1 = mDNSInterface_Any; s->teststate = DNSServer_Disabled; } } diff --git a/src/tools/mdnssd/mDNSEmbeddedAPI.h b/src/tools/mdnssd/mDNSEmbeddedAPI.h index 7310dc56e3d..44bcaaa22fd 100755 --- a/src/tools/mdnssd/mDNSEmbeddedAPI.h +++ b/src/tools/mdnssd/mDNSEmbeddedAPI.h @@ -1054,7 +1054,7 @@ enum typedef struct McastResolver { struct McastResolver *next; - mDNSInterfaceID interface; + mDNSInterfaceID interface1; mDNSu32 flags; // Set when we're planning to delete this from the list domainname domain; mDNSu32 timeout; // timeout value for questions @@ -1063,7 +1063,7 @@ typedef struct McastResolver typedef struct DNSServer { struct DNSServer *next; - mDNSInterfaceID interface; // For specialized uses; we can have DNS servers reachable over specific interfaces + mDNSInterfaceID interface1; // For specialized uses; we can have DNS servers reachable over specific interfaces mDNSAddr addr; mDNSIPPort port; mDNSOpaque16 testid; @@ -2469,11 +2469,11 @@ extern void RecreateNATMappings(mDNS *const m); extern void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext); extern void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn); extern void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, const mDNSAddr *v6addr, const mDNSAddr *router); -extern DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout, mDNSBool cellIntf); +extern DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface1, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout, mDNSBool cellIntf); extern void PenalizeDNSServer(mDNS *const m, DNSQuestion *q); extern void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID); -extern McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, mDNSu32 timeout); +extern McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname *d, const mDNSInterfaceID interface1, mDNSu32 timeout); // We use ((void *)0) here instead of mDNSNULL to avoid compile warnings on gcc 4.2 #define mDNS_AddSearchDomain_CString(X, I) \ diff --git a/src/tools/mdnssd/mDNSWin32.c b/src/tools/mdnssd/mDNSWin32.c new file mode 100644 index 00000000000..15832cb98c9 --- /dev/null +++ b/src/tools/mdnssd/mDNSWin32.c @@ -0,0 +1,5018 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + To Do: + + - Get unicode name of machine for nice name instead of just the host name. + - Use the IPv6 Internet Connection Firewall API to allow IPv6 mDNS without manually changing the firewall. + - Get DNS server address(es) from Windows and provide them to the uDNS layer. + - Implement TCP support for truncated packets (only stubs now). + +*/ + +#define _CRT_RAND_S + +#include +#include +#include +#include +#include +#include + +#include "Poll.h" +#include "CommonServices.h" +#include "DebugServices.h" +#include "Firewall.h" +#include "RegNames.h" +#include "Secret.h" +#include + +#include +#include +#include +#include +#include +#include +#include // This defines the IOCTL constants. + +#include "mDNSEmbeddedAPI.h" +#include "GenLinkedList.h" +#include "DNSCommon.h" +#include "mDNSWin32.h" + +#if 0 +#pragma mark == Constants == +#endif + +//=========================================================================================================================== +// Constants +//=========================================================================================================================== + +#define DEBUG_NAME "[mDNSWin32] " + +#define MDNS_WINDOWS_USE_IPV6_IF_ADDRS 1 +#define MDNS_WINDOWS_ENABLE_IPV4 1 +#define MDNS_WINDOWS_ENABLE_IPV6 1 +#define MDNS_FIX_IPHLPAPI_PREFIX_BUG 1 +#define MDNS_SET_HINFO_STRINGS 0 + +#define kMDNSDefaultName "My Computer" + +#define kWinSockMajorMin 2 +#define kWinSockMinorMin 2 + +#define kRegistryMaxKeyLength 255 +#define kRegistryMaxValueName 16383 + +static GUID kWSARecvMsgGUID = WSAID_WSARECVMSG; + +#define kIPv6IfIndexBase (10000000L) +#define SMBPortAsNumber 445 +#define DEVICE_PREFIX "\\\\.\\" + +#if 0 +#pragma mark == Prototypes == +#endif + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +mDNSlocal mStatus SetupNiceName( mDNS * const inMDNS ); +mDNSlocal mStatus SetupHostName( mDNS * const inMDNS ); +mDNSlocal mStatus SetupName( mDNS * const inMDNS ); +mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inIFA, mDNSInterfaceData **outIFD ); +mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, mDNSInterfaceData *inIFD ); +mDNSlocal void CALLBACK FreeInterface( mDNSInterfaceData *inIFD ); +mDNSlocal mStatus SetupSocket( mDNS * const inMDNS, const struct sockaddr *inAddr, mDNSIPPort port, SocketRef *outSocketRef ); +mDNSlocal mStatus SockAddrToMDNSAddr( const struct sockaddr * const inSA, mDNSAddr *outIP, mDNSIPPort *outPort ); +mDNSlocal OSStatus GetWindowsVersionString( char *inBuffer, size_t inBufferSize ); +mDNSlocal int getifaddrs( struct ifaddrs **outAddrs ); +mDNSlocal void freeifaddrs( struct ifaddrs *inAddrs ); + + + +// Platform Accessors + +#ifdef __cplusplus + extern "C" { +#endif + +typedef struct mDNSPlatformInterfaceInfo mDNSPlatformInterfaceInfo; +struct mDNSPlatformInterfaceInfo +{ + const char * name; + mDNSAddr ip; +}; + + +mDNSexport mStatus mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID ); +mDNSexport mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo ); + + +// Wakeup Structs + +#define kUnicastWakeupNumTries ( 1 ) +#define kUnicastWakeupSleepBetweenTries ( 0 ) +#define kMulticastWakeupNumTries ( 18 ) +#define kMulticastWakeupSleepBetweenTries ( 100 ) + +typedef struct MulticastWakeupStruct +{ + mDNS *inMDNS; + struct sockaddr_in addr; + INT addrLen; + unsigned char data[ 102 ]; + INT dataLen; + INT numTries; + INT msecSleep; +} MulticastWakeupStruct; + + +// Utilities + +#if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS ) + mDNSlocal int getifaddrs_ipv6( struct ifaddrs **outAddrs ); +#endif + +mDNSlocal int getifaddrs_ipv4( struct ifaddrs **outAddrs ); + + +mDNSlocal DWORD GetPrimaryInterface(); +mDNSlocal mStatus AddressToIndexAndMask( struct sockaddr * address, uint32_t * index, struct sockaddr * mask ); +mDNSlocal mDNSBool CanReceiveUnicast( void ); +mDNSlocal mDNSBool IsPointToPoint( IP_ADAPTER_UNICAST_ADDRESS * addr ); + +mDNSlocal mStatus StringToAddress( mDNSAddr * ip, LPSTR string ); +mDNSlocal mStatus RegQueryString( HKEY key, LPCSTR param, LPSTR * string, DWORD * stringLen, DWORD * enabled ); +mDNSlocal struct ifaddrs* myGetIfAddrs(int refresh); +mDNSlocal OSStatus TCHARtoUTF8( const TCHAR *inString, char *inBuffer, size_t inBufferSize ); +mDNSlocal OSStatus WindowsLatin1toUTF8( const char *inString, char *inBuffer, size_t inBufferSize ); +mDNSlocal void CALLBACK TCPSocketNotification( SOCKET sock, LPWSANETWORKEVENTS event, void *context ); +mDNSlocal void TCPCloseSocket( TCPSocket * socket ); +mDNSlocal void CALLBACK UDPSocketNotification( SOCKET sock, LPWSANETWORKEVENTS event, void *context ); +mDNSlocal void UDPCloseSocket( UDPSocket * sock ); +mDNSlocal mStatus SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa); +mDNSlocal void GetDDNSFQDN( domainname *const fqdn ); +#ifdef UNICODE +mDNSlocal void GetDDNSDomains( DNameListElem ** domains, LPCWSTR lpSubKey ); +#else +mDNSlocal void GetDDNSDomains( DNameListElem ** domains, LPCSTR lpSubKey ); +#endif +mDNSlocal void SetDomainSecrets( mDNS * const inMDNS ); +mDNSlocal void SetDomainSecret( mDNS * const m, const domainname * inDomain ); +mDNSlocal VOID CALLBACK CheckFileSharesProc( LPVOID arg, DWORD dwTimerLowValue, DWORD dwTimerHighValue ); +mDNSlocal void CheckFileShares( mDNS * const inMDNS ); +mDNSlocal void SMBCallback(mDNS *const m, ServiceRecordSet *const srs, mStatus result); +mDNSlocal mDNSu8 IsWOMPEnabledForAdapter( const char * adapterName ); +mDNSlocal void SendWakeupPacket( mDNS * const inMDNS, LPSOCKADDR addr, INT addrlen, const char * buf, INT buflen, INT numTries, INT msecSleep ); +mDNSlocal void _cdecl SendMulticastWakeupPacket( void *arg ); + +#ifdef __cplusplus + } +#endif + +#if 0 +#pragma mark == Globals == +#endif + +//=========================================================================================================================== +// Globals +//=========================================================================================================================== + +mDNSlocal mDNS_PlatformSupport gMDNSPlatformSupport; +mDNSs32 mDNSPlatformOneSecond = 0; +mDNSlocal UDPSocket * gUDPSockets = NULL; +mDNSlocal int gUDPNumSockets = 0; +mDNSlocal BOOL gEnableIPv6 = TRUE; + +#if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS ) + + typedef DWORD + ( WINAPI * GetAdaptersAddressesFunctionPtr )( + ULONG inFamily, + DWORD inFlags, + PVOID inReserved, + PIP_ADAPTER_ADDRESSES inAdapter, + PULONG outBufferSize ); + + mDNSlocal HMODULE gIPHelperLibraryInstance = NULL; + mDNSlocal GetAdaptersAddressesFunctionPtr gGetAdaptersAddressesFunctionPtr = NULL; + +#endif + + +#ifndef HCRYPTPROV + typedef ULONG_PTR HCRYPTPROV; // WinCrypt.h, line 249 +#endif + + +#ifndef CRYPT_MACHINE_KEYSET +# define CRYPT_MACHINE_KEYSET 0x00000020 +#endif + +#ifndef CRYPT_NEWKEYSET +# define CRYPT_NEWKEYSET 0x00000008 +#endif + +#ifndef PROV_RSA_FULL +# define PROV_RSA_FULL 1 +#endif + +typedef BOOL (__stdcall *fnCryptGenRandom)( HCRYPTPROV, DWORD, BYTE* ); +typedef BOOL (__stdcall *fnCryptAcquireContext)( HCRYPTPROV*, LPCTSTR, LPCTSTR, DWORD, DWORD); +typedef BOOL (__stdcall *fnCryptReleaseContext)(HCRYPTPROV, DWORD); + +static fnCryptAcquireContext g_lpCryptAcquireContext = NULL; +static fnCryptReleaseContext g_lpCryptReleaseContext = NULL; +static fnCryptGenRandom g_lpCryptGenRandom = NULL; +static HINSTANCE g_hAAPI32 = NULL; +static HCRYPTPROV g_hProvider = ( ULONG_PTR ) NULL; + + +typedef DNSServiceErrorType ( DNSSD_API *DNSServiceRegisterFunc ) + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *name, /* may be NULL */ + const char *regtype, + const char *domain, /* may be NULL */ + const char *host, /* may be NULL */ + uint16_t port, + uint16_t txtLen, + const void *txtRecord, /* may be NULL */ + DNSServiceRegisterReply callBack, /* may be NULL */ + void *context /* may be NULL */ + ); + + +typedef void ( DNSSD_API *DNSServiceRefDeallocateFunc )( DNSServiceRef sdRef ); + +mDNSlocal HMODULE gDNSSDLibrary = NULL; +mDNSlocal DNSServiceRegisterFunc gDNSServiceRegister = NULL; +mDNSlocal DNSServiceRefDeallocateFunc gDNSServiceRefDeallocate = NULL; +mDNSlocal HANDLE gSMBThread = NULL; +mDNSlocal HANDLE gSMBThreadRegisterEvent = NULL; +mDNSlocal HANDLE gSMBThreadDeregisterEvent = NULL; +mDNSlocal HANDLE gSMBThreadStopEvent = NULL; +mDNSlocal HANDLE gSMBThreadQuitEvent = NULL; + +#define kSMBStopEvent ( WAIT_OBJECT_0 + 0 ) +#define kSMBRegisterEvent ( WAIT_OBJECT_0 + 1 ) +#define kSMBDeregisterEvent ( WAIT_OBJECT_0 + 2 ) + + +#if 0 +#pragma mark - +#pragma mark == Platform Support == +#endif + +//=========================================================================================================================== +// mDNSPlatformInit +//=========================================================================================================================== + +mDNSexport mStatus mDNSPlatformInit( mDNS * const inMDNS ) +{ + mStatus err; + OSVERSIONINFO osInfo; + BOOL ok; + WSADATA wsaData; + int supported; + struct sockaddr_in sa4; + struct sockaddr_in6 sa6; + int sa4len; + int sa6len; + DWORD size; + + dlog( kDebugLevelTrace, DEBUG_NAME "platform init\n" ); + + // Initialize variables. If the PlatformSupport pointer is not null then just assume that a non-Apple client is + // calling mDNS_Init and wants to provide its own storage for the platform-specific data so do not overwrite it. + + mDNSPlatformMemZero( &gMDNSPlatformSupport, sizeof( gMDNSPlatformSupport ) ); + if( !inMDNS->p ) inMDNS->p = &gMDNSPlatformSupport; + inMDNS->p->mainThread = OpenThread( THREAD_ALL_ACCESS, FALSE, GetCurrentThreadId() ); + require_action( inMDNS->p->mainThread, exit, err = mStatus_UnknownErr ); + inMDNS->p->checkFileSharesTimer = CreateWaitableTimer( NULL, FALSE, NULL ); + require_action( inMDNS->p->checkFileSharesTimer, exit, err = mStatus_UnknownErr ); + inMDNS->p->checkFileSharesTimeout = 10; // Retry time for CheckFileShares() in seconds + mDNSPlatformOneSecond = 1000; // Use milliseconds as the quantum of time + + // Get OS version info + + osInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO ); + ok = GetVersionEx( &osInfo ); + err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + inMDNS->p->osMajorVersion = osInfo.dwMajorVersion; + inMDNS->p->osMinorVersion = osInfo.dwMinorVersion; + + // Don't enable IPv6 on anything less recent than Windows Vista + + if ( inMDNS->p->osMajorVersion < 6 ) + { + gEnableIPv6 = FALSE; + } + + // Startup WinSock 2.2 or later. + + err = WSAStartup( MAKEWORD( kWinSockMajorMin, kWinSockMinorMin ), &wsaData ); + require_noerr( err, exit ); + + supported = ( ( LOBYTE( wsaData.wVersion ) == kWinSockMajorMin ) && ( HIBYTE( wsaData.wVersion ) == kWinSockMinorMin ) ); + require_action( supported, exit, err = mStatus_UnsupportedErr ); + + inMDNS->CanReceiveUnicastOn5353 = CanReceiveUnicast(); + + // Setup the HINFO HW strings. + // device-info should have model=Windows + + strcpy_s( ( char* ) &inMDNS->HIHardware.c[ 1 ], sizeof( inMDNS->HIHardware.c ) - 2, "Windows" ); + inMDNS->HIHardware.c[ 0 ] = ( mDNSu8 ) mDNSPlatformStrLen( &inMDNS->HIHardware.c[ 1 ] ); + dlog( kDebugLevelInfo, DEBUG_NAME "HIHardware: %#s\n", inMDNS->HIHardware.c ); + + // Setup the HINFO SW strings. +#if ( MDNS_SET_HINFO_STRINGS ) + mDNS_snprintf( (char *) &inMDNS->HISoftware.c[ 1 ], sizeof( inMDNS->HISoftware.c ) - 2, + "mDNSResponder (%s %s)", __DATE__, __TIME__ ); + inMDNS->HISoftware.c[ 0 ] = (mDNSu8) mDNSPlatformStrLen( &inMDNS->HISoftware.c[ 1 ] ); + dlog( kDebugLevelInfo, DEBUG_NAME "HISoftware: %#s\n", inMDNS->HISoftware.c ); +#endif + + // Set up the IPv4 unicast socket + + inMDNS->p->unicastSock4.fd = INVALID_SOCKET; + inMDNS->p->unicastSock4.recvMsgPtr = NULL; + inMDNS->p->unicastSock4.ifd = NULL; + inMDNS->p->unicastSock4.next = NULL; + inMDNS->p->unicastSock4.m = inMDNS; + +#if ( MDNS_WINDOWS_ENABLE_IPV4 ) + + sa4.sin_family = AF_INET; + sa4.sin_addr.s_addr = INADDR_ANY; + err = SetupSocket( inMDNS, (const struct sockaddr*) &sa4, zeroIPPort, &inMDNS->p->unicastSock4.fd ); + check_noerr( err ); + sa4len = sizeof( sa4 ); + err = getsockname( inMDNS->p->unicastSock4.fd, (struct sockaddr*) &sa4, &sa4len ); + require_noerr( err, exit ); + inMDNS->p->unicastSock4.port.NotAnInteger = sa4.sin_port; + inMDNS->UnicastPort4 = inMDNS->p->unicastSock4.port; + err = WSAIoctl( inMDNS->p->unicastSock4.fd, SIO_GET_EXTENSION_FUNCTION_POINTER, &kWSARecvMsgGUID, sizeof( kWSARecvMsgGUID ), &inMDNS->p->unicastSock4.recvMsgPtr, sizeof( inMDNS->p->unicastSock4.recvMsgPtr ), &size, NULL, NULL ); + + if ( err ) + { + inMDNS->p->unicastSock4.recvMsgPtr = NULL; + } + + err = mDNSPollRegisterSocket( inMDNS->p->unicastSock4.fd, FD_READ, UDPSocketNotification, &inMDNS->p->unicastSock4 ); + require_noerr( err, exit ); + +#endif + + // Set up the IPv6 unicast socket + + inMDNS->p->unicastSock6.fd = INVALID_SOCKET; + inMDNS->p->unicastSock6.recvMsgPtr = NULL; + inMDNS->p->unicastSock6.ifd = NULL; + inMDNS->p->unicastSock6.next = NULL; + inMDNS->p->unicastSock6.m = inMDNS; + +#if ( MDNS_WINDOWS_ENABLE_IPV6 ) + + if ( gEnableIPv6 ) + { + sa6.sin6_family = AF_INET6; + sa6.sin6_addr = in6addr_any; + sa6.sin6_scope_id = 0; + + // This call will fail if the machine hasn't installed IPv6. In that case, + // the error will be WSAEAFNOSUPPORT. + + err = SetupSocket( inMDNS, (const struct sockaddr*) &sa6, zeroIPPort, &inMDNS->p->unicastSock6.fd ); + require_action( !err || ( err == WSAEAFNOSUPPORT ), exit, err = (mStatus) WSAGetLastError() ); + err = kNoErr; + + // If we weren't able to create the socket (because IPv6 hasn't been installed) don't do this + + if ( inMDNS->p->unicastSock6.fd != INVALID_SOCKET ) + { + sa6len = sizeof( sa6 ); + err = getsockname( inMDNS->p->unicastSock6.fd, (struct sockaddr*) &sa6, &sa6len ); + require_noerr( err, exit ); + inMDNS->p->unicastSock6.port.NotAnInteger = sa6.sin6_port; + inMDNS->UnicastPort6 = inMDNS->p->unicastSock6.port; + + err = WSAIoctl( inMDNS->p->unicastSock6.fd, SIO_GET_EXTENSION_FUNCTION_POINTER, &kWSARecvMsgGUID, sizeof( kWSARecvMsgGUID ), &inMDNS->p->unicastSock6.recvMsgPtr, sizeof( inMDNS->p->unicastSock6.recvMsgPtr ), &size, NULL, NULL ); + + if ( err != 0 ) + { + inMDNS->p->unicastSock6.recvMsgPtr = NULL; + } + + err = mDNSPollRegisterSocket( inMDNS->p->unicastSock6.fd, FD_READ, UDPSocketNotification, &inMDNS->p->unicastSock6 ); + require_noerr( err, exit ); + } + } + +#endif + + // Notify core of domain secret keys + + SetDomainSecrets( inMDNS ); + + // Success! + + mDNSCoreInitComplete( inMDNS, err ); + + +exit: + + if ( err ) + { + mDNSPlatformClose( inMDNS ); + } + + dlog( kDebugLevelTrace, DEBUG_NAME "platform init done (err=%d %m)\n", err, err ); + return( err ); +} + +//=========================================================================================================================== +// mDNSPlatformClose +//=========================================================================================================================== + +mDNSexport void mDNSPlatformClose( mDNS * const inMDNS ) +{ + mStatus err; + + dlog( kDebugLevelTrace, DEBUG_NAME "platform close\n" ); + check( inMDNS ); + + if ( gSMBThread != NULL ) + { + dlog( kDebugLevelTrace, DEBUG_NAME "tearing down smb registration thread\n" ); + SetEvent( gSMBThreadStopEvent ); + + if ( WaitForSingleObject( gSMBThreadQuitEvent, 5 * 1000 ) == WAIT_OBJECT_0 ) + { + if ( gSMBThreadQuitEvent ) + { + CloseHandle( gSMBThreadQuitEvent ); + gSMBThreadQuitEvent = NULL; + } + + if ( gSMBThreadStopEvent ) + { + CloseHandle( gSMBThreadStopEvent ); + gSMBThreadStopEvent = NULL; + } + + if ( gSMBThreadDeregisterEvent ) + { + CloseHandle( gSMBThreadDeregisterEvent ); + gSMBThreadDeregisterEvent = NULL; + } + + if ( gSMBThreadRegisterEvent ) + { + CloseHandle( gSMBThreadRegisterEvent ); + gSMBThreadRegisterEvent = NULL; + } + + if ( gDNSSDLibrary ) + { + FreeLibrary( gDNSSDLibrary ); + gDNSSDLibrary = NULL; + } + } + else + { + LogMsg( "Unable to stop SMBThread" ); + } + + inMDNS->p->smbFileSharing = mDNSfalse; + inMDNS->p->smbPrintSharing = mDNSfalse; + } + + // Tear everything down in reverse order to how it was set up. + + err = TearDownInterfaceList( inMDNS ); + check_noerr( err ); + check( !inMDNS->p->inactiveInterfaceList ); + +#if ( MDNS_WINDOWS_ENABLE_IPV4 ) + + UDPCloseSocket( &inMDNS->p->unicastSock4 ); + +#endif + +#if ( MDNS_WINDOWS_ENABLE_IPV6 ) + + if ( gEnableIPv6 ) + { + UDPCloseSocket( &inMDNS->p->unicastSock6 ); + } + +#endif + + // Free the DLL needed for IPv6 support. + +#if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS ) + if( gIPHelperLibraryInstance ) + { + gGetAdaptersAddressesFunctionPtr = NULL; + + FreeLibrary( gIPHelperLibraryInstance ); + gIPHelperLibraryInstance = NULL; + } +#endif + + if ( g_hAAPI32 ) + { + // Release any resources + + if ( g_hProvider && g_lpCryptReleaseContext ) + { + ( g_lpCryptReleaseContext )( g_hProvider, 0 ); + } + + // Free the AdvApi32.dll + + FreeLibrary( g_hAAPI32 ); + + // And reset all the data + + g_lpCryptAcquireContext = NULL; + g_lpCryptReleaseContext = NULL; + g_lpCryptGenRandom = NULL; + g_hProvider = ( ULONG_PTR ) NULL; + g_hAAPI32 = NULL; + } + + WSACleanup(); + + dlog( kDebugLevelTrace, DEBUG_NAME "platform close done\n" ); +} + + +//=========================================================================================================================== +// mDNSPlatformLock +//=========================================================================================================================== + +mDNSexport void mDNSPlatformLock( const mDNS * const inMDNS ) +{ + ( void ) inMDNS; +} + +//=========================================================================================================================== +// mDNSPlatformUnlock +//=========================================================================================================================== + +mDNSexport void mDNSPlatformUnlock( const mDNS * const inMDNS ) +{ + ( void ) inMDNS; +} + +//=========================================================================================================================== +// mDNSPlatformStrCopy +//=========================================================================================================================== + +mDNSexport void mDNSPlatformStrCopy( void *inDst, const void *inSrc ) +{ + check( inSrc ); + check( inDst ); + + strcpy( (char *) inDst, (const char*) inSrc ); +} + +//=========================================================================================================================== +// mDNSPlatformStrLen +//=========================================================================================================================== + +mDNSexport mDNSu32 mDNSPlatformStrLen( const void *inSrc ) +{ + check( inSrc ); + + return( (mDNSu32) strlen( (const char *) inSrc ) ); +} + +//=========================================================================================================================== +// mDNSPlatformMemCopy +//=========================================================================================================================== + +mDNSexport void mDNSPlatformMemCopy( void *inDst, const void *inSrc, mDNSu32 inSize ) +{ + check( inSrc ); + check( inDst ); + + memcpy( inDst, inSrc, inSize ); +} + +//=========================================================================================================================== +// mDNSPlatformMemSame +//=========================================================================================================================== + +mDNSexport mDNSBool mDNSPlatformMemSame( const void *inDst, const void *inSrc, mDNSu32 inSize ) +{ + check( inSrc ); + check( inDst ); + + return( (mDNSBool)( memcmp( inSrc, inDst, inSize ) == 0 ) ); +} + +//=========================================================================================================================== +// mDNSPlatformMemZero +//=========================================================================================================================== + +mDNSexport void mDNSPlatformMemZero( void *inDst, mDNSu32 inSize ) +{ + check( inDst ); + + memset( inDst, 0, inSize ); +} + +//=========================================================================================================================== +// mDNSPlatformMemAllocate +//=========================================================================================================================== + +mDNSexport void * mDNSPlatformMemAllocate( mDNSu32 inSize ) +{ + void * mem; + + check( inSize > 0 ); + + mem = malloc( inSize ); + check( mem ); + + return( mem ); +} + +//=========================================================================================================================== +// mDNSPlatformMemFree +//=========================================================================================================================== + +mDNSexport void mDNSPlatformMemFree( void *inMem ) +{ + check( inMem ); + + free( inMem ); +} + +//=========================================================================================================================== +// mDNSPlatformRandomNumber +//=========================================================================================================================== + +mDNSexport mDNSu32 mDNSPlatformRandomNumber(void) +{ + unsigned int randomNumber; + errno_t err; + + err = rand_s( &randomNumber ); + require_noerr( err, exit ); + +exit: + + if ( err ) + { + randomNumber = rand(); + } + + return ( mDNSu32 ) randomNumber; +} + +//=========================================================================================================================== +// mDNSPlatformTimeInit +//=========================================================================================================================== + +mDNSexport mStatus mDNSPlatformTimeInit( void ) +{ + // No special setup is required on Windows -- we just use GetTickCount(). + return( mStatus_NoError ); +} + +//=========================================================================================================================== +// mDNSPlatformRawTime +//=========================================================================================================================== + +mDNSexport mDNSs32 mDNSPlatformRawTime( void ) +{ + return( (mDNSs32) GetTickCount() ); +} + +//=========================================================================================================================== +// mDNSPlatformUTC +//=========================================================================================================================== + +mDNSexport mDNSs32 mDNSPlatformUTC( void ) +{ + return ( mDNSs32 ) time( NULL ); +} + +//=========================================================================================================================== +// mDNSPlatformInterfaceNameToID +//=========================================================================================================================== + +mDNSexport mStatus mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID ) +{ + mStatus err; + mDNSInterfaceData * ifd; + + check( inMDNS ); + check( inMDNS->p ); + check( inName ); + + // Search for an interface with the specified name, + + for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next ) + { + if( strcmp( ifd->name, inName ) == 0 ) + { + break; + } + } + require_action_quiet( ifd, exit, err = mStatus_NoSuchNameErr ); + + // Success! + + if( outID ) + { + *outID = (mDNSInterfaceID) ifd; + } + err = mStatus_NoError; + +exit: + return( err ); +} + +//=========================================================================================================================== +// mDNSPlatformInterfaceIDToInfo +//=========================================================================================================================== + +mDNSexport mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo ) +{ + mStatus err; + mDNSInterfaceData * ifd; + + check( inMDNS ); + check( inID ); + check( outInfo ); + + // Search for an interface with the specified ID, + + for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next ) + { + if( ifd == (mDNSInterfaceData *) inID ) + { + break; + } + } + require_action_quiet( ifd, exit, err = mStatus_NoSuchNameErr ); + + // Success! + + outInfo->name = ifd->name; + outInfo->ip = ifd->interfaceInfo.ip; + err = mStatus_NoError; + +exit: + return( err ); +} + +//=========================================================================================================================== +// mDNSPlatformInterfaceIDfromInterfaceIndex +//=========================================================================================================================== + +mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex( mDNS * const inMDNS, mDNSu32 inIndex ) +{ + mDNSInterfaceID id; + + id = mDNSNULL; + if( inIndex == kDNSServiceInterfaceIndexLocalOnly ) + { + id = mDNSInterface_LocalOnly; + } + else if( inIndex != 0 ) + { + mDNSInterfaceData * ifd; + + for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next ) + { + if( ( ifd->scopeID == inIndex ) && ifd->interfaceInfo.InterfaceActive ) + { + id = ifd->interfaceInfo.InterfaceID; + break; + } + } + check( ifd ); + } + return( id ); +} + +//=========================================================================================================================== +// mDNSPlatformInterfaceIndexfromInterfaceID +//=========================================================================================================================== + +mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSBool suppressNetworkChange ) +{ + mDNSu32 index; + + (void) suppressNetworkChange; + + index = 0; + if( inID == mDNSInterface_LocalOnly ) + { + index = (mDNSu32) kDNSServiceInterfaceIndexLocalOnly; + } + else if( inID ) + { + mDNSInterfaceData * ifd; + + // Search active interfaces. + for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next ) + { + if( (mDNSInterfaceID) ifd == inID ) + { + index = ifd->scopeID; + break; + } + } + + // Search inactive interfaces too so remove events for inactive interfaces report the old interface index. + + if( !ifd ) + { + for( ifd = inMDNS->p->inactiveInterfaceList; ifd; ifd = ifd->next ) + { + if( (mDNSInterfaceID) ifd == inID ) + { + index = ifd->scopeID; + break; + } + } + } + check( ifd ); + } + return( index ); +} + + +//=========================================================================================================================== +// mDNSPlatformTCPSocket +//=========================================================================================================================== + +TCPSocket * +mDNSPlatformTCPSocket + ( + mDNS * const m, + TCPSocketFlags flags, + mDNSIPPort * port + ) +{ + TCPSocket * sock = NULL; + u_long on = 1; // "on" for setsockopt + struct sockaddr_in saddr; + int len; + mStatus err = mStatus_NoError; + + DEBUG_UNUSED( m ); + + require_action( flags == 0, exit, err = mStatus_UnsupportedErr ); + + // Setup connection data object + + sock = (TCPSocket *) malloc( sizeof( TCPSocket ) ); + require_action( sock, exit, err = mStatus_NoMemoryErr ); + mDNSPlatformMemZero( sock, sizeof( TCPSocket ) ); + sock->fd = INVALID_SOCKET; + sock->flags = flags; + sock->m = m; + + mDNSPlatformMemZero(&saddr, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = htonl( INADDR_ANY ); + saddr.sin_port = port->NotAnInteger; + + // Create the socket + + sock->fd = socket(AF_INET, SOCK_STREAM, 0); + err = translate_errno( sock->fd != INVALID_SOCKET, WSAGetLastError(), mStatus_UnknownErr ); + require_noerr( err, exit ); + + // bind + + err = bind( sock->fd, ( struct sockaddr* ) &saddr, sizeof( saddr ) ); + err = translate_errno( err == 0, WSAGetLastError(), mStatus_UnknownErr ); + require_noerr( err, exit ); + + // Set it to be non-blocking + + err = ioctlsocket( sock->fd, FIONBIO, &on ); + err = translate_errno( err == 0, WSAGetLastError(), mStatus_UnknownErr ); + require_noerr( err, exit ); + + // Get port number + + mDNSPlatformMemZero( &saddr, sizeof( saddr ) ); + len = sizeof( saddr ); + + err = getsockname( sock->fd, ( struct sockaddr* ) &saddr, &len ); + err = translate_errno( err == 0, WSAGetLastError(), mStatus_UnknownErr ); + require_noerr( err, exit ); + + port->NotAnInteger = saddr.sin_port; + +exit: + + if ( err && sock ) + { + TCPCloseSocket( sock ); + free( sock ); + sock = mDNSNULL; + } + + return sock; +} + +//=========================================================================================================================== +// mDNSPlatformTCPConnect +//=========================================================================================================================== + +mStatus +mDNSPlatformTCPConnect + ( + TCPSocket * sock, + const mDNSAddr * inDstIP, + mDNSOpaque16 inDstPort, + domainname * hostname, + mDNSInterfaceID inInterfaceID, + TCPConnectionCallback inCallback, + void * inContext + ) +{ + struct sockaddr_in saddr; + mStatus err = mStatus_NoError; + + DEBUG_UNUSED( hostname ); + DEBUG_UNUSED( inInterfaceID ); + + if ( inDstIP->type != mDNSAddrType_IPv4 ) + { + LogMsg("ERROR: mDNSPlatformTCPConnect - attempt to connect to an IPv6 address: operation not supported"); + return mStatus_UnknownErr; + } + + // Setup connection data object + + sock->userCallback = inCallback; + sock->userContext = inContext; + + mDNSPlatformMemZero(&saddr, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = inDstPort.NotAnInteger; + memcpy(&saddr.sin_addr, &inDstIP->ip.v4.NotAnInteger, sizeof(saddr.sin_addr)); + + // Try and do connect + + err = connect( sock->fd, ( struct sockaddr* ) &saddr, sizeof( saddr ) ); + require_action( !err || ( WSAGetLastError() == WSAEWOULDBLOCK ), exit, err = mStatus_ConnFailed ); + sock->connected = !err ? TRUE : FALSE; + + err = mDNSPollRegisterSocket( sock->fd, FD_CONNECT | FD_READ | FD_CLOSE, TCPSocketNotification, sock ); + require_noerr( err, exit ); + +exit: + + if ( !err ) + { + err = sock->connected ? mStatus_ConnEstablished : mStatus_ConnPending; + } + + return err; +} + + +//=========================================================================================================================== +// mDNSPlatformTCPAccept +//=========================================================================================================================== + +mDNSexport +mDNSexport TCPSocket *mDNSPlatformTCPAccept( TCPSocketFlags flags, int fd ) + { + TCPSocket * sock = NULL; + mStatus err = mStatus_NoError; + + require_action( !flags, exit, err = mStatus_UnsupportedErr ); + + sock = malloc( sizeof( TCPSocket ) ); + require_action( sock, exit, err = mStatus_NoMemoryErr ); + + mDNSPlatformMemZero( sock, sizeof( *sock ) ); + + sock->fd = fd; + sock->flags = flags; + +exit: + + if ( err && sock ) + { + free( sock ); + sock = NULL; + } + + return sock; + } + + +//=========================================================================================================================== +// mDNSPlatformTCPCloseConnection +//=========================================================================================================================== + +mDNSexport void mDNSPlatformTCPCloseConnection( TCPSocket *sock ) +{ + check( sock ); + + if ( sock ) + { + dlog( kDebugLevelChatty, DEBUG_NAME "mDNSPlatformTCPCloseConnection 0x%x:%d\n", sock, sock->fd ); + + if ( sock->fd != INVALID_SOCKET ) + { + mDNSPollUnregisterSocket( sock->fd ); + closesocket( sock->fd ); + sock->fd = INVALID_SOCKET; + } + + free( sock ); + } +} + + +//=========================================================================================================================== +// mDNSPlatformReadTCP +//=========================================================================================================================== + +mDNSexport long mDNSPlatformReadTCP( TCPSocket *sock, void *inBuffer, unsigned long inBufferSize, mDNSBool * closed ) +{ + int nread; + OSStatus err; + + *closed = mDNSfalse; + nread = recv( sock->fd, inBuffer, inBufferSize, 0 ); + err = translate_errno( ( nread >= 0 ), WSAGetLastError(), mStatus_UnknownErr ); + + if ( nread > 0 ) + { + dlog( kDebugLevelChatty, DEBUG_NAME "mDNSPlatformReadTCP: 0x%x:%d read %d bytes\n", sock, sock->fd, nread ); + } + else if ( !nread ) + { + *closed = mDNStrue; + } + else if ( err == WSAECONNRESET ) + { + *closed = mDNStrue; + nread = 0; + } + else if ( err == WSAEWOULDBLOCK ) + { + nread = 0; + } + else + { + LogMsg( "ERROR: mDNSPlatformReadTCP - recv: %d\n", err ); + nread = -1; + } + + return nread; +} + + +//=========================================================================================================================== +// mDNSPlatformWriteTCP +//=========================================================================================================================== + +mDNSexport long mDNSPlatformWriteTCP( TCPSocket *sock, const char *inMsg, unsigned long inMsgSize ) +{ + int nsent; + OSStatus err; + + nsent = send( sock->fd, inMsg, inMsgSize, 0 ); + + err = translate_errno( ( nsent >= 0 ) || ( WSAGetLastError() == WSAEWOULDBLOCK ), WSAGetLastError(), mStatus_UnknownErr ); + require_noerr( err, exit ); + + if ( nsent < 0) + { + nsent = 0; + } + +exit: + + return nsent; +} + +//=========================================================================================================================== +// mDNSPlatformTCPGetFD +//=========================================================================================================================== + +mDNSexport int mDNSPlatformTCPGetFD(TCPSocket *sock ) +{ + return ( int ) sock->fd; +} + + + +//=========================================================================================================================== +// TCPSocketNotification +//=========================================================================================================================== + +mDNSlocal void CALLBACK +TCPSocketNotification( SOCKET sock, LPWSANETWORKEVENTS event, void *context ) +{ + TCPSocket *tcpSock = ( TCPSocket* ) context; + TCPConnectionCallback callback; + int err; + + DEBUG_UNUSED( sock ); + + require_action( tcpSock, exit, err = mStatus_BadParamErr ); + callback = ( TCPConnectionCallback ) tcpSock->userCallback; + require_action( callback, exit, err = mStatus_BadParamErr ); + + if ( event && ( event->lNetworkEvents & FD_CONNECT ) ) + { + if ( event->iErrorCode[ FD_CONNECT_BIT ] == 0 ) + { + callback( tcpSock, tcpSock->userContext, mDNStrue, 0 ); + tcpSock->connected = mDNStrue; + } + else + { + callback( tcpSock, tcpSock->userContext, mDNSfalse, event->iErrorCode[ FD_CONNECT_BIT ] ); + } + } + else + { + callback( tcpSock, tcpSock->userContext, mDNSfalse, 0 ); + } + +exit: + + return; +} + + + +//=========================================================================================================================== +// mDNSPlatformUDPSocket +//=========================================================================================================================== + +mDNSexport UDPSocket* mDNSPlatformUDPSocket(mDNS *const m, const mDNSIPPort requestedport) +{ + UDPSocket* sock = NULL; + mDNSIPPort port = requestedport; + mStatus err = mStatus_NoError; + unsigned i; + + // Setup connection data object + + sock = ( UDPSocket* ) malloc(sizeof( UDPSocket ) ); + require_action( sock, exit, err = mStatus_NoMemoryErr ); + memset( sock, 0, sizeof( UDPSocket ) ); + + // Create the socket + + sock->fd = INVALID_SOCKET; + sock->recvMsgPtr = m->p->unicastSock4.recvMsgPtr; + sock->addr = m->p->unicastSock4.addr; + sock->ifd = NULL; + sock->m = m; + + // Try at most 10000 times to get a unique random port + + for (i=0; i<10000; i++) + { + struct sockaddr_in saddr; + + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = 0; + + // The kernel doesn't do cryptographically strong random port + // allocation, so we do it ourselves here + + if (mDNSIPPortIsZero(requestedport)) + { + port = mDNSOpaque16fromIntVal( ( mDNSu16 ) ( 0xC000 + mDNSRandom(0x3FFF) ) ); + } + + saddr.sin_port = port.NotAnInteger; + + err = SetupSocket(m, ( struct sockaddr* ) &saddr, port, &sock->fd ); + if (!err) break; + } + + require_noerr( err, exit ); + + // Set the port + + sock->port = port; + + // Arm the completion routine + + err = mDNSPollRegisterSocket( sock->fd, FD_READ, UDPSocketNotification, sock ); + require_noerr( err, exit ); + + // Bookkeeping + + sock->next = gUDPSockets; + gUDPSockets = sock; + gUDPNumSockets++; + +exit: + + if ( err && sock ) + { + UDPCloseSocket( sock ); + free( sock ); + sock = NULL; + } + + return sock; +} + +//=========================================================================================================================== +// mDNSPlatformUDPClose +//=========================================================================================================================== + +mDNSexport void mDNSPlatformUDPClose( UDPSocket *sock ) +{ + UDPSocket * current = gUDPSockets; + UDPSocket * last = NULL; + + while ( current ) + { + if ( current == sock ) + { + if ( last == NULL ) + { + gUDPSockets = sock->next; + } + else + { + last->next = sock->next; + } + + UDPCloseSocket( sock ); + free( sock ); + + gUDPNumSockets--; + + break; + } + + last = current; + current = current->next; + } +} + + +//=========================================================================================================================== +// mDNSPlatformSendUDP +//=========================================================================================================================== + +mDNSexport mStatus + mDNSPlatformSendUDP( + const mDNS * const inMDNS, + const void * const inMsg, + const mDNSu8 * const inMsgEnd, + mDNSInterfaceID inInterfaceID, + UDPSocket * inSrcSocket, + const mDNSAddr * inDstIP, + mDNSIPPort inDstPort ) +{ + SOCKET sendingsocket = INVALID_SOCKET; + mStatus err = mStatus_NoError; + mDNSInterfaceData * ifd = (mDNSInterfaceData*) inInterfaceID; + struct sockaddr_storage addr; + int n; + + DEBUG_USE_ONLY( inMDNS ); + + n = (int)( inMsgEnd - ( (const mDNSu8 * const) inMsg ) ); + check( inMDNS ); + check( inMsg ); + check( inMsgEnd ); + check( inDstIP ); + + dlog( kDebugLevelChatty, DEBUG_NAME "platform send %d bytes to %#a:%u\n", n, inDstIP, ntohs( inDstPort.NotAnInteger ) ); + + if( inDstIP->type == mDNSAddrType_IPv4 ) + { + struct sockaddr_in * sa4; + + sa4 = (struct sockaddr_in *) &addr; + sa4->sin_family = AF_INET; + sa4->sin_port = inDstPort.NotAnInteger; + sa4->sin_addr.s_addr = inDstIP->ip.v4.NotAnInteger; + sendingsocket = ifd ? ifd->sock.fd : inMDNS->p->unicastSock4.fd; + + if (inSrcSocket) { sendingsocket = inSrcSocket->fd; debugf("mDNSPlatformSendUDP using port %d, static port %d, sock %d", mDNSVal16(inSrcSocket->port), inMDNS->p->unicastSock4.fd, sendingsocket); } + } + else if( inDstIP->type == mDNSAddrType_IPv6 ) + { + struct sockaddr_in6 * sa6; + + sa6 = (struct sockaddr_in6 *) &addr; + sa6->sin6_family = AF_INET6; + sa6->sin6_port = inDstPort.NotAnInteger; + sa6->sin6_flowinfo = 0; + sa6->sin6_addr = *( (struct in6_addr *) &inDstIP->ip.v6 ); + sa6->sin6_scope_id = 0; // Windows requires the scope ID to be zero. IPV6_MULTICAST_IF specifies interface. + sendingsocket = ifd ? ifd->sock.fd : inMDNS->p->unicastSock6.fd; + } + else + { + dlog( kDebugLevelError, DEBUG_NAME "%s: dst is not an IPv4 or IPv6 address (type=%d)\n", __ROUTINE__, inDstIP->type ); + err = mStatus_BadParamErr; + goto exit; + } + + if (IsValidSocket(sendingsocket)) + { + n = sendto( sendingsocket, (char *) inMsg, n, 0, (struct sockaddr *) &addr, sizeof( addr ) ); + err = translate_errno( n > 0, errno_compat(), kWriteErr ); + + if ( err ) + { + // Don't report EHOSTDOWN (i.e. ARP failure), ENETDOWN, or no route to host for unicast destinations + + if ( !mDNSAddressIsAllDNSLinkGroup( inDstIP ) && ( WSAGetLastError() == WSAEHOSTDOWN || WSAGetLastError() == WSAENETDOWN || WSAGetLastError() == WSAEHOSTUNREACH || WSAGetLastError() == WSAENETUNREACH ) ) + { + err = mStatus_TransientErr; + } + else + { + //require_noerr( err, exit ); + } + } + } + +exit: + return( err ); +} + + +mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID InterfaceID) + { + DEBUG_UNUSED( m ); + DEBUG_UNUSED( InterfaceID ); + } + + +mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason) + { + DEBUG_UNUSED( m ); + DEBUG_UNUSED( allowSleep ); + DEBUG_UNUSED( reason ); + } + +//=========================================================================================================================== +// mDNSPlatformSendRawPacket +//=========================================================================================================================== + +mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *ethaddr, char *ipaddr, int iteration) +{ + unsigned char mac[ 6 ]; + unsigned char buf[ 102 ]; + char hex[ 3 ] = { 0 }; + unsigned char *bufPtr = buf; + struct sockaddr_storage saddr; + INT len = sizeof( saddr ); + mDNSBool unicast = mDNSfalse; + MulticastWakeupStruct *info; + int i; + mStatus err; + + (void) InterfaceID; + + require_action( ethaddr, exit, err = mStatus_BadParamErr ); + + for ( i = 0; i < 6; i++ ) + { + memcpy( hex, ethaddr + ( i * 3 ), 2 ); + mac[ i ] = ( unsigned char ) strtoul( hex, NULL, 16 ); + } + + memset( buf, 0, sizeof( buf ) ); + + for ( i = 0; i < 6; i++ ) + { + *bufPtr++ = 0xff; + } + + for ( i = 0; i < 16; i++ ) + { + memcpy( bufPtr, mac, sizeof( mac ) ); + bufPtr += sizeof( mac ); + } + + if ( ipaddr ) + { + if ( WSAStringToAddressA( ipaddr, AF_INET, NULL, ( LPSOCKADDR ) &saddr, &len ) == 0 ) + { + struct sockaddr_in * saddr4 = ( struct sockaddr_in* ) &saddr; + saddr4->sin_port = htons( 9 ); + len = sizeof( *saddr4 ); + + if ( saddr4->sin_addr.s_addr != htonl( INADDR_ANY ) ) + { + unicast = mDNStrue; + } + } + else if ( WSAStringToAddressA( ipaddr, AF_INET6, NULL, ( LPSOCKADDR ) &saddr, &len ) == 0 ) + { + mDNSInterfaceData *ifd = ( mDNSInterfaceData* ) InterfaceID; + struct sockaddr_in6 * saddr6 = ( struct sockaddr_in6* ) &saddr; + saddr6->sin6_port = htons( 9 ); + + if ( ifd != NULL ) + { + saddr6->sin6_scope_id = ifd->scopeID; + } + + len = sizeof( *saddr6 ); + + if ( memcmp( &saddr6->sin6_addr, &in6addr_any, sizeof( IN6_ADDR ) ) != 0 ) + { + unicast = mDNStrue; + } + } + } + + if ( ( iteration < 2 ) && ( unicast ) ) + { + SendWakeupPacket( m, ( LPSOCKADDR ) &saddr, len, ( const char* ) buf, sizeof( buf ), kUnicastWakeupNumTries, kUnicastWakeupSleepBetweenTries ); + } + + info = ( MulticastWakeupStruct* ) malloc( sizeof( MulticastWakeupStruct ) ); + require_action( info, exit, err = mStatus_NoMemoryErr ); + info->inMDNS = m; + memset( &info->addr, 0, sizeof( info->addr ) ); + info->addr.sin_family = AF_INET; + info->addr.sin_addr.s_addr = AllDNSLinkGroup_v4.ip.v4.NotAnInteger; + info->addr.sin_port = htons( 9 ); + info->addrLen = sizeof( info->addr ); + memcpy( info->data, buf, sizeof( buf ) ); + info->dataLen = sizeof( buf ); + info->numTries = kMulticastWakeupNumTries; + info->msecSleep = kMulticastWakeupSleepBetweenTries; + + _beginthread( SendMulticastWakeupPacket, 0, ( void* ) info ); + +exit: + + return; +} + + +mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf) +{ + DEBUG_UNUSED( rr ); + DEBUG_UNUSED( intf ); + + return mDNStrue; +} + + +mDNSexport void mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID) + { + DEBUG_UNUSED( msg ); + DEBUG_UNUSED( end ); + DEBUG_UNUSED( InterfaceID ); + } + + +mDNSexport void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSAddr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID) + { + DEBUG_UNUSED( m ); + DEBUG_UNUSED( tpa ); + DEBUG_UNUSED( tha ); + DEBUG_UNUSED( InterfaceID ); + } + + +mDNSexport void mDNSPlatformReceiveRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID) + { + DEBUG_UNUSED( msg ); + DEBUG_UNUSED( end ); + DEBUG_UNUSED( InterfaceID ); + } + +mDNSexport void mDNSPlatformSetLocalARP( const mDNSv4Addr * const tpa, const mDNSEthAddr * const tha, mDNSInterfaceID InterfaceID ) + { + DEBUG_UNUSED( tpa ); + DEBUG_UNUSED( tha ); + DEBUG_UNUSED( InterfaceID ); + } + +mDNSexport void mDNSPlatformWriteDebugMsg(const char *msg) + { + dlog( kDebugLevelInfo, "%s\n", msg ); + } + +mDNSexport void mDNSPlatformWriteLogMsg( const char * ident, const char * msg, mDNSLogLevel_t loglevel ) + { + extern mDNS mDNSStorage; + int type; + + DEBUG_UNUSED( ident ); + + type = EVENTLOG_ERROR_TYPE; + + switch (loglevel) + { + case MDNS_LOG_MSG: type = EVENTLOG_ERROR_TYPE; break; + case MDNS_LOG_OPERATION: type = EVENTLOG_WARNING_TYPE; break; + case MDNS_LOG_SPS: type = EVENTLOG_INFORMATION_TYPE; break; + case MDNS_LOG_INFO: type = EVENTLOG_INFORMATION_TYPE; break; + case MDNS_LOG_DEBUG: type = EVENTLOG_INFORMATION_TYPE; break; + default: + fprintf(stderr, "Unknown loglevel %d, assuming LOG_ERR\n", loglevel); + fflush(stderr); + } + + mDNSStorage.p->reportStatusFunc( type, msg ); + dlog( kDebugLevelInfo, "%s\n", msg ); + } + +mDNSexport void mDNSPlatformSourceAddrForDest( mDNSAddr * const src, const mDNSAddr * const dst ) + { + DEBUG_UNUSED( src ); + DEBUG_UNUSED( dst ); + } + +//=========================================================================================================================== +// mDNSPlatformTLSSetupCerts +//=========================================================================================================================== + +mDNSexport mStatus +mDNSPlatformTLSSetupCerts(void) +{ + return mStatus_UnsupportedErr; +} + +//=========================================================================================================================== +// mDNSPlatformTLSTearDownCerts +//=========================================================================================================================== + +mDNSexport void +mDNSPlatformTLSTearDownCerts(void) +{ +} + +//=========================================================================================================================== +// mDNSPlatformSetDNSConfig +//=========================================================================================================================== + +mDNSlocal void SetDNSServers( mDNS *const m ); +mDNSlocal void SetSearchDomainList( void ); + +mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **regDomains, DNameListElem **browseDomains) +{ + if (setservers) SetDNSServers(m); + if (setsearch) SetSearchDomainList(); + + if ( fqdn ) + { + GetDDNSFQDN( fqdn ); + } + + if ( browseDomains ) + { + GetDDNSDomains( browseDomains, kServiceParametersNode TEXT("\\DynDNS\\Setup\\") kServiceDynDNSBrowseDomains ); + } + + if ( regDomains ) + { + GetDDNSDomains( regDomains, kServiceParametersNode TEXT("\\DynDNS\\Setup\\") kServiceDynDNSRegistrationDomains ); + } +} + + +//=========================================================================================================================== +// mDNSPlatformDynDNSHostNameStatusChanged +//=========================================================================================================================== + +mDNSexport void +mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const dname, const mStatus status) +{ + char uname[MAX_ESCAPED_DOMAIN_NAME]; + BYTE bStatus; + LPCTSTR name; + HKEY key = NULL; + mStatus err; + char * p; + + ConvertDomainNameToCString(dname, uname); + + p = uname; + + while (*p) + { + *p = (char) tolower(*p); + if (!(*(p+1)) && *p == '.') *p = 0; // if last character, strip trailing dot + p++; + } + + check( strlen( p ) <= MAX_ESCAPED_DOMAIN_NAME ); + name = kServiceParametersNode TEXT("\\DynDNS\\State\\HostNames"); + err = RegCreateKey( HKEY_LOCAL_MACHINE, name, &key ); + require_noerr( err, exit ); + + bStatus = ( status ) ? 0 : 1; + err = RegSetValueEx( key, kServiceDynDNSStatus, 0, REG_DWORD, (const LPBYTE) &bStatus, sizeof(DWORD) ); + require_noerr( err, exit ); + +exit: + + if ( key ) + { + RegCloseKey( key ); + } + + return; +} + + +//=========================================================================================================================== +// SetDomainSecrets +//=========================================================================================================================== + +// This routine needs to be called whenever the system secrets database changes. +// We call it from DynDNSConfigDidChange and mDNSPlatformInit + +void +SetDomainSecrets( mDNS * const m ) +{ + DomainAuthInfo *ptr; + domainname fqdn; + DNameListElem * regDomains = NULL; + + // Rather than immediately deleting all keys now, we mark them for deletion in ten seconds. + // In the case where the user simultaneously removes their DDNS host name and the key + // for it, this gives mDNSResponder ten seconds to gracefully delete the name from the + // server before it loses access to the necessary key. Otherwise, we'd leave orphaned + // address records behind that we no longer have permission to delete. + + for (ptr = m->AuthInfoList; ptr; ptr = ptr->next) + ptr->deltime = NonZeroTime(m->timenow + mDNSPlatformOneSecond*10); + + GetDDNSFQDN( &fqdn ); + + if ( fqdn.c[ 0 ] ) + { + SetDomainSecret( m, &fqdn ); + } + + GetDDNSDomains( ®Domains, kServiceParametersNode TEXT("\\DynDNS\\Setup\\") kServiceDynDNSRegistrationDomains ); + + while ( regDomains ) + { + DNameListElem * current = regDomains; + SetDomainSecret( m, ¤t->name ); + regDomains = regDomains->next; + free( current ); + } +} + + +//=========================================================================================================================== +// SetSearchDomainList +//=========================================================================================================================== + +mDNSlocal void SetDomainFromDHCP( void ); +mDNSlocal void SetReverseMapSearchDomainList( void ); + +mDNSlocal void +SetSearchDomainList( void ) +{ + char * searchList = NULL; + DWORD searchListLen; + //DNameListElem * head = NULL; + //DNameListElem * current = NULL; + char * tok; + HKEY key; + mStatus err; + + err = RegCreateKey( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"), &key ); + require_noerr( err, exit ); + + err = RegQueryString( key, "SearchList", &searchList, &searchListLen, NULL ); + require_noerr( err, exit ); + + // Windows separates the search domains with ',' + + tok = strtok( searchList, "," ); + while ( tok ) + { + if ( ( strcmp( tok, "" ) != 0 ) && ( strcmp( tok, "." ) != 0 ) ) + mDNS_AddSearchDomain_CString(tok, mDNSNULL); + tok = strtok( NULL, "," ); + } + +exit: + + if ( searchList ) + { + free( searchList ); + } + + if ( key ) + { + RegCloseKey( key ); + } + + SetDomainFromDHCP(); + SetReverseMapSearchDomainList(); +} + + +//=========================================================================================================================== +// SetReverseMapSearchDomainList +//=========================================================================================================================== + +mDNSlocal void +SetReverseMapSearchDomainList( void ) +{ + struct ifaddrs * ifa; + + ifa = myGetIfAddrs( 1 ); + while (ifa) + { + mDNSAddr addr; + + if (ifa->ifa_addr->sa_family == AF_INET && !SetupAddr(&addr, ifa->ifa_addr) && !(ifa->ifa_flags & IFF_LOOPBACK) && ifa->ifa_netmask) + { + mDNSAddr netmask; + char buffer[256]; + + if (!SetupAddr(&netmask, ifa->ifa_netmask)) + { + sprintf(buffer, "%d.%d.%d.%d.in-addr.arpa.", addr.ip.v4.b[3] & netmask.ip.v4.b[3], + addr.ip.v4.b[2] & netmask.ip.v4.b[2], + addr.ip.v4.b[1] & netmask.ip.v4.b[1], + addr.ip.v4.b[0] & netmask.ip.v4.b[0]); + mDNS_AddSearchDomain_CString(buffer, mDNSNULL); + } + } + + ifa = ifa->ifa_next; + } + + return; +} + + +//=========================================================================================================================== +// SetDNSServers +//=========================================================================================================================== + +mDNSlocal void +SetDNSServers( mDNS *const m ) +{ + PIP_PER_ADAPTER_INFO pAdapterInfo = NULL; + FIXED_INFO * fixedInfo = NULL; + ULONG bufLen = 0; + IP_ADDR_STRING * dnsServerList; + IP_ADDR_STRING * ipAddr; + DWORD index; + int i = 0; + mStatus err = kUnknownErr; + + // Get the primary interface. + + index = GetPrimaryInterface(); + + // This should have the interface index of the primary index. Fall back in cases where + // it can't be determined. + + if ( index ) + { + bufLen = 0; + + for ( i = 0; i < 100; i++ ) + { + err = GetPerAdapterInfo( index, pAdapterInfo, &bufLen ); + + if ( err != ERROR_BUFFER_OVERFLOW ) + { + break; + } + + pAdapterInfo = (PIP_PER_ADAPTER_INFO) realloc( pAdapterInfo, bufLen ); + require_action( pAdapterInfo, exit, err = mStatus_NoMemoryErr ); + } + + require_noerr( err, exit ); + + dnsServerList = &pAdapterInfo->DnsServerList; + } + else + { + bufLen = sizeof( FIXED_INFO ); + + for ( i = 0; i < 100; i++ ) + { + if ( fixedInfo ) + { + GlobalFree( fixedInfo ); + fixedInfo = NULL; + } + + fixedInfo = (FIXED_INFO*) GlobalAlloc( GPTR, bufLen ); + require_action( fixedInfo, exit, err = mStatus_NoMemoryErr ); + + err = GetNetworkParams( fixedInfo, &bufLen ); + + if ( err != ERROR_BUFFER_OVERFLOW ) + { + break; + } + } + + require_noerr( err, exit ); + + dnsServerList = &fixedInfo->DnsServerList; + } + + for ( ipAddr = dnsServerList; ipAddr; ipAddr = ipAddr->Next ) + { + mDNSAddr addr; + err = StringToAddress( &addr, ipAddr->IpAddress.String ); + if ( !err ) mDNS_AddDNSServer(m, mDNSNULL, mDNSInterface_Any, &addr, UnicastDNSPort, mDNSfalse, DEFAULT_UDNS_TIMEOUT, mDNSfalse); + } + +exit: + + if ( pAdapterInfo ) + { + free( pAdapterInfo ); + } + + if ( fixedInfo ) + { + GlobalFree( fixedInfo ); + } +} + + +//=========================================================================================================================== +// SetDomainFromDHCP +//=========================================================================================================================== + +mDNSlocal void +SetDomainFromDHCP( void ) +{ + int i = 0; + IP_ADAPTER_INFO * pAdapterInfo; + IP_ADAPTER_INFO * pAdapter; + DWORD bufLen; + DWORD index; + HKEY key = NULL; + LPSTR domain = NULL; + DWORD dwSize; + mStatus err = mStatus_NoError; + + pAdapterInfo = NULL; + + for ( i = 0; i < 100; i++ ) + { + err = GetAdaptersInfo( pAdapterInfo, &bufLen); + + if ( err != ERROR_BUFFER_OVERFLOW ) + { + break; + } + + pAdapterInfo = (IP_ADAPTER_INFO*) realloc( pAdapterInfo, bufLen ); + require_action( pAdapterInfo, exit, err = kNoMemoryErr ); + } + + require_noerr( err, exit ); + + index = GetPrimaryInterface(); + + for ( pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next ) + { + if ( pAdapter->IpAddressList.IpAddress.String && + pAdapter->IpAddressList.IpAddress.String[0] && + pAdapter->GatewayList.IpAddress.String && + pAdapter->GatewayList.IpAddress.String[0] && + ( !index || ( pAdapter->Index == index ) ) ) + { + // Found one that will work + + char keyName[1024]; + + _snprintf( keyName, 1024, "%s%s", "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\", pAdapter->AdapterName ); + + err = RegCreateKeyA( HKEY_LOCAL_MACHINE, keyName, &key ); + require_noerr( err, exit ); + + err = RegQueryString( key, "Domain", &domain, &dwSize, NULL ); + check_noerr( err ); + + if ( !domain || !domain[0] ) + { + if ( domain ) + { + free( domain ); + domain = NULL; + } + + err = RegQueryString( key, "DhcpDomain", &domain, &dwSize, NULL ); + check_noerr( err ); + } + + if ( domain && domain[0] ) mDNS_AddSearchDomain_CString(domain, mDNSNULL); + + break; + } + } + +exit: + + if ( pAdapterInfo ) + { + free( pAdapterInfo ); + } + + if ( domain ) + { + free( domain ); + } + + if ( key ) + { + RegCloseKey( key ); + } +} + + +//=========================================================================================================================== +// mDNSPlatformGetPrimaryInterface +//=========================================================================================================================== + +mDNSexport mStatus +mDNSPlatformGetPrimaryInterface( mDNS * const m, mDNSAddr * v4, mDNSAddr * v6, mDNSAddr * router ) +{ + IP_ADAPTER_INFO * pAdapterInfo; + IP_ADAPTER_INFO * pAdapter; + DWORD bufLen; + int i; + BOOL found; + DWORD index; + mStatus err = mStatus_NoError; + + DEBUG_UNUSED( m ); + + *v6 = zeroAddr; + + pAdapterInfo = NULL; + bufLen = 0; + found = FALSE; + + for ( i = 0; i < 100; i++ ) + { + err = GetAdaptersInfo( pAdapterInfo, &bufLen); + + if ( err != ERROR_BUFFER_OVERFLOW ) + { + break; + } + + pAdapterInfo = (IP_ADAPTER_INFO*) realloc( pAdapterInfo, bufLen ); + require_action( pAdapterInfo, exit, err = kNoMemoryErr ); + } + + require_noerr( err, exit ); + + index = GetPrimaryInterface(); + + for ( pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next ) + { + if ( pAdapter->IpAddressList.IpAddress.String && + pAdapter->IpAddressList.IpAddress.String[0] && + pAdapter->GatewayList.IpAddress.String && + pAdapter->GatewayList.IpAddress.String[0] && + ( StringToAddress( v4, pAdapter->IpAddressList.IpAddress.String ) == mStatus_NoError ) && + ( StringToAddress( router, pAdapter->GatewayList.IpAddress.String ) == mStatus_NoError ) && + ( !index || ( pAdapter->Index == index ) ) ) + { + // Found one that will work + + if ( pAdapter->AddressLength == sizeof( m->PrimaryMAC ) ) + { + memcpy( &m->PrimaryMAC, pAdapter->Address, pAdapter->AddressLength ); + } + + found = TRUE; + break; + } + } + +exit: + + if ( pAdapterInfo ) + { + free( pAdapterInfo ); + } + + return err; +} + + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// debugf_ +//=========================================================================================================================== +#if( MDNS_DEBUGMSGS ) +mDNSexport void debugf_( const char *inFormat, ... ) +{ + char buffer[ 512 ]; + va_list args; + mDNSu32 length; + + va_start( args, inFormat ); + length = mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args ); + va_end( args ); + + dlog( kDebugLevelInfo, "%s\n", buffer ); +} +#endif + +//=========================================================================================================================== +// verbosedebugf_ +//=========================================================================================================================== + +#if( MDNS_DEBUGMSGS > 1 ) +mDNSexport void verbosedebugf_( const char *inFormat, ... ) +{ + char buffer[ 512 ]; + va_list args; + mDNSu32 length; + + va_start( args, inFormat ); + length = mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args ); + va_end( args ); + + dlog( kDebugLevelVerbose, "%s\n", buffer ); +} +#endif + + +#if 0 +#pragma mark - +#pragma mark == Platform Internals == +#endif + + +//=========================================================================================================================== +// SetupNiceName +//=========================================================================================================================== + +mStatus SetupNiceName( mDNS * const inMDNS ) +{ + HKEY descKey = NULL; + char utf8[ 256 ]; + LPCTSTR s; + LPWSTR joinName; + NETSETUP_JOIN_STATUS joinStatus; + mStatus err = 0; + DWORD namelen; + BOOL ok; + + check( inMDNS ); + + // Set up the nice name. + utf8[0] = '\0'; + + // First try and open the registry key that contains the computer description value + s = TEXT("SYSTEM\\CurrentControlSet\\Services\\lanmanserver\\parameters"); + err = RegOpenKeyEx( HKEY_LOCAL_MACHINE, s, 0, KEY_READ, &descKey); + check_translated_errno( err == 0, errno_compat(), kNameErr ); + + if ( !err ) + { + TCHAR desc[256]; + DWORD descSize = sizeof( desc ); + + // look for the computer description + err = RegQueryValueEx( descKey, TEXT("srvcomment"), 0, NULL, (LPBYTE) &desc, &descSize); + + if ( !err ) + { + err = TCHARtoUTF8( desc, utf8, sizeof( utf8 ) ); + } + + if ( err ) + { + utf8[ 0 ] = '\0'; + } + } + + // if we can't find it in the registry, then use the hostname of the machine + if ( err || ( utf8[ 0 ] == '\0' ) ) + { + TCHAR hostname[256]; + + namelen = sizeof( hostname ) / sizeof( TCHAR ); + + ok = GetComputerNameExW( ComputerNamePhysicalDnsHostname, hostname, &namelen ); + err = translate_errno( ok, (mStatus) GetLastError(), kNameErr ); + check_noerr( err ); + + if( !err ) + { + err = TCHARtoUTF8( hostname, utf8, sizeof( utf8 ) ); + } + + if ( err ) + { + utf8[ 0 ] = '\0'; + } + } + + // if we can't get the hostname + if ( err || ( utf8[ 0 ] == '\0' ) ) + { + // Invalidate name so fall back to a default name. + + strcpy( utf8, kMDNSDefaultName ); + } + + utf8[ sizeof( utf8 ) - 1 ] = '\0'; + inMDNS->nicelabel.c[ 0 ] = (mDNSu8) (strlen( utf8 ) < MAX_DOMAIN_LABEL ? strlen( utf8 ) : MAX_DOMAIN_LABEL); + memcpy( &inMDNS->nicelabel.c[ 1 ], utf8, inMDNS->nicelabel.c[ 0 ] ); + + dlog( kDebugLevelInfo, DEBUG_NAME "nice name \"%.*s\"\n", inMDNS->nicelabel.c[ 0 ], &inMDNS->nicelabel.c[ 1 ] ); + + if ( descKey ) + { + RegCloseKey( descKey ); + } + + ZeroMemory( inMDNS->p->nbname, sizeof( inMDNS->p->nbname ) ); + ZeroMemory( inMDNS->p->nbdomain, sizeof( inMDNS->p->nbdomain ) ); + + namelen = sizeof( inMDNS->p->nbname ); + ok = GetComputerNameExA( ComputerNamePhysicalNetBIOS, inMDNS->p->nbname, &namelen ); + check( ok ); + if ( ok ) dlog( kDebugLevelInfo, DEBUG_NAME "netbios name \"%s\"\n", inMDNS->p->nbname ); + + err = NetGetJoinInformation( NULL, &joinName, &joinStatus ); + check ( err == NERR_Success ); + if ( err == NERR_Success ) + { + if ( ( joinStatus == NetSetupWorkgroupName ) || ( joinStatus == NetSetupDomainName ) ) + { + err = TCHARtoUTF8( joinName, inMDNS->p->nbdomain, sizeof( inMDNS->p->nbdomain ) ); + check( !err ); + if ( !err ) dlog( kDebugLevelInfo, DEBUG_NAME "netbios domain/workgroup \"%s\"\n", inMDNS->p->nbdomain ); + } + + NetApiBufferFree( joinName ); + joinName = NULL; + } + + err = 0; + + return( err ); +} + +//=========================================================================================================================== +// SetupHostName +//=========================================================================================================================== + +mDNSlocal mStatus SetupHostName( mDNS * const inMDNS ) +{ + mStatus err = 0; + char tempString[ 256 ]; + DWORD tempStringLen; + domainlabel tempLabel; + BOOL ok; + + check( inMDNS ); + + // Set up the nice name. + tempString[ 0 ] = '\0'; + + // use the hostname of the machine + tempStringLen = sizeof( tempString ); + ok = GetComputerNameExA( ComputerNamePhysicalDnsHostname, tempString, &tempStringLen ); + err = translate_errno( ok, (mStatus) GetLastError(), kNameErr ); + check_noerr( err ); + + // if we can't get the hostname + if( err || ( tempString[ 0 ] == '\0' ) ) + { + // Invalidate name so fall back to a default name. + + strcpy( tempString, kMDNSDefaultName ); + } + + tempString[ sizeof( tempString ) - 1 ] = '\0'; + tempLabel.c[ 0 ] = (mDNSu8) (strlen( tempString ) < MAX_DOMAIN_LABEL ? strlen( tempString ) : MAX_DOMAIN_LABEL ); + memcpy( &tempLabel.c[ 1 ], tempString, tempLabel.c[ 0 ] ); + + // Set up the host name. + + ConvertUTF8PstringToRFC1034HostLabel( tempLabel.c, &inMDNS->hostlabel ); + if( inMDNS->hostlabel.c[ 0 ] == 0 ) + { + // Nice name has no characters that are representable as an RFC1034 name (e.g. Japanese) so use the default. + + MakeDomainLabelFromLiteralString( &inMDNS->hostlabel, kMDNSDefaultName ); + } + + check( inMDNS->hostlabel.c[ 0 ] != 0 ); + + mDNS_SetFQDN( inMDNS ); + + dlog( kDebugLevelInfo, DEBUG_NAME "host name \"%.*s\"\n", inMDNS->hostlabel.c[ 0 ], &inMDNS->hostlabel.c[ 1 ] ); + + return( err ); +} + +//=========================================================================================================================== +// SetupName +//=========================================================================================================================== + +mDNSlocal mStatus SetupName( mDNS * const inMDNS ) +{ + mStatus err = 0; + + check( inMDNS ); + + err = SetupNiceName( inMDNS ); + check_noerr( err ); + + err = SetupHostName( inMDNS ); + check_noerr( err ); + + return err; +} + + +//=========================================================================================================================== +// SetupInterfaceList +//=========================================================================================================================== + +mStatus SetupInterfaceList( mDNS * const inMDNS ) +{ + mStatus err; + mDNSInterfaceData ** next; + mDNSInterfaceData * ifd; + struct ifaddrs * addrs; + struct ifaddrs * p; + struct ifaddrs * loopbackv4; + struct ifaddrs * loopbackv6; + u_int flagMask; + u_int flagTest; + mDNSBool foundv4; + mDNSBool foundv6; + mDNSBool foundUnicastSock4DestAddr; + mDNSBool foundUnicastSock6DestAddr; + + dlog( kDebugLevelTrace, DEBUG_NAME "setting up interface list\n" ); + check( inMDNS ); + check( inMDNS->p ); + + inMDNS->p->registeredLoopback4 = mDNSfalse; + inMDNS->p->nextDHCPLeaseExpires = 0x7FFFFFFF; + addrs = NULL; + foundv4 = mDNSfalse; + foundv6 = mDNSfalse; + foundUnicastSock4DestAddr = mDNSfalse; + foundUnicastSock6DestAddr = mDNSfalse; + + // Tear down any existing interfaces that may be set up. + + TearDownInterfaceList( inMDNS ); + + // Set up the name of this machine. + + err = SetupName( inMDNS ); + check_noerr( err ); + + // Set up IPv4 interface(s). We have to set up IPv4 first so any IPv6 interface with an IPv4-routable address + // can refer to the IPv4 interface when it registers to allow DNS AAAA records over the IPv4 interface. + + err = getifaddrs( &addrs ); + require_noerr( err, exit ); + + loopbackv4 = NULL; + loopbackv6 = NULL; + next = &inMDNS->p->interfaceList; + + flagMask = IFF_UP | IFF_MULTICAST; + flagTest = IFF_UP | IFF_MULTICAST; + +#if( MDNS_WINDOWS_ENABLE_IPV4 ) + for( p = addrs; p; p = p->ifa_next ) + { + if( !p->ifa_addr || ( p->ifa_addr->sa_family != AF_INET ) || ( ( p->ifa_flags & flagMask ) != flagTest ) ) + { + continue; + } + if( p->ifa_flags & IFF_LOOPBACK ) + { + if( !loopbackv4 ) + { + loopbackv4 = p; + } + continue; + } + dlog( kDebugLevelVerbose, DEBUG_NAME "Interface %40s (0x%08X) %##a\n", + p->ifa_name ? p->ifa_name : "", p->ifa_extra.index, p->ifa_addr ); + + err = SetupInterface( inMDNS, p, &ifd ); + require_noerr( err, exit ); + + // If this guy is point-to-point (ifd->interfaceInfo.McastTxRx == 0 ) we still want to + // register him, but we also want to note that we haven't found a v4 interface + // so that we register loopback so same host operations work + + if ( ifd->interfaceInfo.McastTxRx == mDNStrue ) + { + foundv4 = mDNStrue; + } + + if ( p->ifa_dhcpEnabled && ( p->ifa_dhcpLeaseExpires < inMDNS->p->nextDHCPLeaseExpires ) ) + { + inMDNS->p->nextDHCPLeaseExpires = p->ifa_dhcpLeaseExpires; + } + + // If we're on a platform that doesn't have WSARecvMsg(), there's no way + // of determing the destination address of a packet that is sent to us. + // For multicast packets, that's easy to determine. But for the unicast + // sockets, we'll fake it by taking the address of the first interface + // that is successfully setup. + + if ( !foundUnicastSock4DestAddr ) + { + inMDNS->p->unicastSock4.addr = ifd->interfaceInfo.ip; + foundUnicastSock4DestAddr = TRUE; + } + + *next = ifd; + next = &ifd->next; + ++inMDNS->p->interfaceCount; + } +#endif + + // Set up IPv6 interface(s) after IPv4 is set up (see IPv4 notes above for reasoning). + +#if( MDNS_WINDOWS_ENABLE_IPV6 ) + + if ( gEnableIPv6 ) + { + for( p = addrs; p; p = p->ifa_next ) + { + if( !p->ifa_addr || ( p->ifa_addr->sa_family != AF_INET6 ) || ( ( p->ifa_flags & flagMask ) != flagTest ) ) + { + continue; + } + if( p->ifa_flags & IFF_LOOPBACK ) + { + if( !loopbackv6 ) + { + loopbackv6 = p; + } + continue; + } + dlog( kDebugLevelVerbose, DEBUG_NAME "Interface %40s (0x%08X) %##a\n", + p->ifa_name ? p->ifa_name : "", p->ifa_extra.index, p->ifa_addr ); + + err = SetupInterface( inMDNS, p, &ifd ); + require_noerr( err, exit ); + + // If this guy is point-to-point (ifd->interfaceInfo.McastTxRx == 0 ) we still want to + // register him, but we also want to note that we haven't found a v4 interface + // so that we register loopback so same host operations work + + if ( ifd->interfaceInfo.McastTxRx == mDNStrue ) + { + foundv6 = mDNStrue; + } + + // If we're on a platform that doesn't have WSARecvMsg(), there's no way + // of determing the destination address of a packet that is sent to us. + // For multicast packets, that's easy to determine. But for the unicast + // sockets, we'll fake it by taking the address of the first interface + // that is successfully setup. + + if ( !foundUnicastSock6DestAddr ) + { + inMDNS->p->unicastSock6.addr = ifd->interfaceInfo.ip; + foundUnicastSock6DestAddr = TRUE; + } + + *next = ifd; + next = &ifd->next; + ++inMDNS->p->interfaceCount; + } + } + +#endif + + // If there are no real interfaces, but there is a loopback interface, use that so same-machine operations work. + +#if( !MDNS_WINDOWS_ENABLE_IPV4 && !MDNS_WINDOWS_ENABLE_IPV6 ) + + flagMask |= IFF_LOOPBACK; + flagTest |= IFF_LOOPBACK; + + for( p = addrs; p; p = p->ifa_next ) + { + if( !p->ifa_addr || ( ( p->ifa_flags & flagMask ) != flagTest ) ) + { + continue; + } + if( ( p->ifa_addr->sa_family != AF_INET ) && ( p->ifa_addr->sa_family != AF_INET6 ) ) + { + continue; + } + + v4loopback = p; + break; + } + +#endif + + if ( !foundv4 && loopbackv4 ) + { + dlog( kDebugLevelInfo, DEBUG_NAME "Interface %40s (0x%08X) %##a\n", + loopbackv4->ifa_name ? loopbackv4->ifa_name : "", loopbackv4->ifa_extra.index, loopbackv4->ifa_addr ); + + err = SetupInterface( inMDNS, loopbackv4, &ifd ); + require_noerr( err, exit ); + + inMDNS->p->registeredLoopback4 = mDNStrue; + +#if( MDNS_WINDOWS_ENABLE_IPV4 ) + + // If we're on a platform that doesn't have WSARecvMsg(), there's no way + // of determing the destination address of a packet that is sent to us. + // For multicast packets, that's easy to determine. But for the unicast + // sockets, we'll fake it by taking the address of the first interface + // that is successfully setup. + + if ( !foundUnicastSock4DestAddr ) + { + inMDNS->p->unicastSock4.addr = ifd->sock.addr; + foundUnicastSock4DestAddr = TRUE; + } +#endif + + *next = ifd; + next = &ifd->next; + ++inMDNS->p->interfaceCount; + } + + if ( !foundv6 && loopbackv6 ) + { + dlog( kDebugLevelInfo, DEBUG_NAME "Interface %40s (0x%08X) %##a\n", + loopbackv6->ifa_name ? loopbackv6->ifa_name : "", loopbackv6->ifa_extra.index, loopbackv6->ifa_addr ); + + err = SetupInterface( inMDNS, loopbackv6, &ifd ); + require_noerr( err, exit ); + +#if( MDNS_WINDOWS_ENABLE_IPV6 ) + + if ( gEnableIPv6 ) + { + // If we're on a platform that doesn't have WSARecvMsg(), there's no way + // of determing the destination address of a packet that is sent to us. + // For multicast packets, that's easy to determine. But for the unicast + // sockets, we'll fake it by taking the address of the first interface + // that is successfully setup. + + if ( !foundUnicastSock6DestAddr ) + { + inMDNS->p->unicastSock6.addr = ifd->sock.addr; + foundUnicastSock6DestAddr = TRUE; + } + } + +#endif + + *next = ifd; + next = &ifd->next; + ++inMDNS->p->interfaceCount; + } + + CheckFileShares( inMDNS ); + +exit: + if( err ) + { + TearDownInterfaceList( inMDNS ); + } + if( addrs ) + { + freeifaddrs( addrs ); + } + dlog( kDebugLevelTrace, DEBUG_NAME "setting up interface list done (err=%d %m)\n", err, err ); + return( err ); +} + +//=========================================================================================================================== +// TearDownInterfaceList +//=========================================================================================================================== + +mStatus TearDownInterfaceList( mDNS * const inMDNS ) +{ + mDNSInterfaceData ** p; + mDNSInterfaceData * ifd; + + dlog( kDebugLevelTrace, DEBUG_NAME "tearing down interface list\n" ); + check( inMDNS ); + check( inMDNS->p ); + + // Free any interfaces that were previously marked inactive and are no longer referenced by the mDNS cache. + // Interfaces are marked inactive, but not deleted immediately if they were still referenced by the mDNS cache + // so that remove events that occur after an interface goes away can still report the correct interface. + + p = &inMDNS->p->inactiveInterfaceList; + while( *p ) + { + ifd = *p; + if( NumCacheRecordsForInterfaceID( inMDNS, (mDNSInterfaceID) ifd ) > 0 ) + { + p = &ifd->next; + continue; + } + + dlog( kDebugLevelInfo, DEBUG_NAME "freeing unreferenced, inactive interface %#p %#a\n", ifd, &ifd->interfaceInfo.ip ); + *p = ifd->next; + + QueueUserAPC( ( PAPCFUNC ) FreeInterface, inMDNS->p->mainThread, ( ULONG_PTR ) ifd ); + } + + // Tear down all the interfaces. + + while( inMDNS->p->interfaceList ) + { + ifd = inMDNS->p->interfaceList; + inMDNS->p->interfaceList = ifd->next; + + TearDownInterface( inMDNS, ifd ); + } + inMDNS->p->interfaceCount = 0; + + dlog( kDebugLevelTrace, DEBUG_NAME "tearing down interface list done\n" ); + return( mStatus_NoError ); +} + +//=========================================================================================================================== +// SetupInterface +//=========================================================================================================================== + +mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inIFA, mDNSInterfaceData **outIFD ) +{ + mDNSInterfaceData * ifd; + mDNSInterfaceData * p; + mStatus err; + + ifd = NULL; + dlog( kDebugLevelTrace, DEBUG_NAME "setting up interface\n" ); + check( inMDNS ); + check( inMDNS->p ); + check( inIFA ); + check( inIFA->ifa_addr ); + check( outIFD ); + + // Allocate memory for the interface and initialize it. + + ifd = (mDNSInterfaceData *) calloc( 1, sizeof( *ifd ) ); + require_action( ifd, exit, err = mStatus_NoMemoryErr ); + ifd->sock.fd = kInvalidSocketRef; + ifd->sock.ifd = ifd; + ifd->sock.next = NULL; + ifd->sock.m = inMDNS; + ifd->index = inIFA->ifa_extra.index; + ifd->scopeID = inIFA->ifa_extra.index; + check( strlen( inIFA->ifa_name ) < sizeof( ifd->name ) ); + strncpy( ifd->name, inIFA->ifa_name, sizeof( ifd->name ) - 1 ); + ifd->name[ sizeof( ifd->name ) - 1 ] = '\0'; + + strncpy(ifd->interfaceInfo.ifname, inIFA->ifa_name, sizeof(ifd->interfaceInfo.ifname)); + ifd->interfaceInfo.ifname[sizeof(ifd->interfaceInfo.ifname)-1] = 0; + + // We always send and receive using IPv4, but to reduce traffic, we send and receive using IPv6 only on interfaces + // that have no routable IPv4 address. Having a routable IPv4 address assigned is a reasonable indicator of being + // on a large configured network, which means there's a good chance that most or all the other devices on that + // network should also have v4. By doing this we lose the ability to talk to true v6-only devices on that link, + // but we cut the packet rate in half. At this time, reducing the packet rate is more important than v6-only + // devices on a large configured network, so we are willing to make that sacrifice. + + ifd->interfaceInfo.McastTxRx = ( ( inIFA->ifa_flags & IFF_MULTICAST ) && !( inIFA->ifa_flags & IFF_POINTTOPOINT ) ) ? mDNStrue : mDNSfalse; + ifd->interfaceInfo.InterfaceID = NULL; + + for( p = inMDNS->p->interfaceList; p; p = p->next ) + { + if ( strcmp( p->name, ifd->name ) == 0 ) + { + if (!ifd->interfaceInfo.InterfaceID) + { + ifd->interfaceInfo.InterfaceID = (mDNSInterfaceID) p; + } + + if ( ( inIFA->ifa_addr->sa_family != AF_INET ) && + ( p->interfaceInfo.ip.type == mDNSAddrType_IPv4 ) && + ( p->interfaceInfo.ip.ip.v4.b[ 0 ] != 169 || p->interfaceInfo.ip.ip.v4.b[ 1 ] != 254 ) ) + { + ifd->interfaceInfo.McastTxRx = mDNSfalse; + } + + break; + } + } + + if ( !ifd->interfaceInfo.InterfaceID ) + { + ifd->interfaceInfo.InterfaceID = (mDNSInterfaceID) ifd; + } + + // Set up a socket for this interface (if needed). + + if( ifd->interfaceInfo.McastTxRx ) + { + DWORD size; + + err = SetupSocket( inMDNS, inIFA->ifa_addr, MulticastDNSPort, &ifd->sock.fd ); + require_noerr( err, exit ); + ifd->sock.addr = ( inIFA->ifa_addr->sa_family == AF_INET6 ) ? AllDNSLinkGroup_v6 : AllDNSLinkGroup_v4; + ifd->sock.port = MulticastDNSPort; + + // Get a ptr to the WSARecvMsg function, if supported. Otherwise, we'll fallback to recvfrom. + + err = WSAIoctl( ifd->sock.fd, SIO_GET_EXTENSION_FUNCTION_POINTER, &kWSARecvMsgGUID, sizeof( kWSARecvMsgGUID ), &ifd->sock.recvMsgPtr, sizeof( ifd->sock.recvMsgPtr ), &size, NULL, NULL ); + + if ( err ) + { + ifd->sock.recvMsgPtr = NULL; + } + } + + if ( inIFA->ifa_dhcpEnabled && ( inIFA->ifa_dhcpLeaseExpires < inMDNS->p->nextDHCPLeaseExpires ) ) + { + inMDNS->p->nextDHCPLeaseExpires = inIFA->ifa_dhcpLeaseExpires; + } + + ifd->interfaceInfo.NetWake = inIFA->ifa_womp; + + // Register this interface with mDNS. + + err = SockAddrToMDNSAddr( inIFA->ifa_addr, &ifd->interfaceInfo.ip, NULL ); + require_noerr( err, exit ); + + err = SockAddrToMDNSAddr( inIFA->ifa_netmask, &ifd->interfaceInfo.mask, NULL ); + require_noerr( err, exit ); + + memcpy( ifd->interfaceInfo.MAC.b, inIFA->ifa_physaddr, sizeof( ifd->interfaceInfo.MAC.b ) ); + + ifd->interfaceInfo.Advertise = ( mDNSu8 ) inMDNS->AdvertiseLocalAddresses; + + if ( ifd->sock.fd != kInvalidSocketRef ) + { + err = mDNSPollRegisterSocket( ifd->sock.fd, FD_READ, UDPSocketNotification, &ifd->sock ); + require_noerr( err, exit ); + } + + err = mDNS_RegisterInterface( inMDNS, &ifd->interfaceInfo, mDNSfalse ); + require_noerr( err, exit ); + ifd->hostRegistered = mDNStrue; + + dlog( kDebugLevelInfo, DEBUG_NAME "Registered interface %##a with mDNS\n", inIFA->ifa_addr ); + + // Success! + + *outIFD = ifd; + ifd = NULL; + +exit: + + if( ifd ) + { + TearDownInterface( inMDNS, ifd ); + } + dlog( kDebugLevelTrace, DEBUG_NAME "setting up interface done (err=%d %m)\n", err, err ); + return( err ); +} + +//=========================================================================================================================== +// TearDownInterface +//=========================================================================================================================== + +mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, mDNSInterfaceData *inIFD ) +{ + check( inMDNS ); + check( inIFD ); + + // Deregister this interface with mDNS. + + dlog( kDebugLevelInfo, DEBUG_NAME "Deregistering interface %#a with mDNS\n", &inIFD->interfaceInfo.ip ); + + if( inIFD->hostRegistered ) + { + inIFD->hostRegistered = mDNSfalse; + mDNS_DeregisterInterface( inMDNS, &inIFD->interfaceInfo, mDNSfalse ); + } + + // Tear down the multicast socket. + + UDPCloseSocket( &inIFD->sock ); + + // If the interface is still referenced by items in the mDNS cache then put it on the inactive list. This keeps + // the InterfaceID valid so remove events report the correct interface. If it is no longer referenced, free it. + + if( NumCacheRecordsForInterfaceID( inMDNS, (mDNSInterfaceID) inIFD ) > 0 ) + { + inIFD->next = inMDNS->p->inactiveInterfaceList; + inMDNS->p->inactiveInterfaceList = inIFD; + dlog( kDebugLevelInfo, DEBUG_NAME "deferring free of interface %#p %#a\n", inIFD, &inIFD->interfaceInfo.ip ); + } + else + { + dlog( kDebugLevelInfo, DEBUG_NAME "freeing interface %#p %#a immediately\n", inIFD, &inIFD->interfaceInfo.ip ); + QueueUserAPC( ( PAPCFUNC ) FreeInterface, inMDNS->p->mainThread, ( ULONG_PTR ) inIFD ); + } + + return( mStatus_NoError ); +} + +mDNSlocal void CALLBACK FreeInterface( mDNSInterfaceData *inIFD ) +{ + free( inIFD ); +} + +//=========================================================================================================================== +// SetupSocket +//=========================================================================================================================== + +mDNSlocal mStatus SetupSocket( mDNS * const inMDNS, const struct sockaddr *inAddr, mDNSIPPort port, SocketRef *outSocketRef ) +{ + mStatus err; + SocketRef sock; + int option; + DWORD bytesReturned = 0; + BOOL behavior = FALSE; + + DEBUG_UNUSED( inMDNS ); + + dlog( kDebugLevelTrace, DEBUG_NAME "setting up socket %##a\n", inAddr ); + check( inMDNS ); + check( outSocketRef ); + + // Set up an IPv4 or IPv6 UDP socket. + + sock = socket( inAddr->sa_family, SOCK_DGRAM, IPPROTO_UDP ); + err = translate_errno( IsValidSocket( sock ), errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + // Turn on reuse address option so multiple servers can listen for Multicast DNS packets, + // if we're creating a multicast socket + + if ( !mDNSIPPortIsZero( port ) ) + { + option = 1; + err = setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, (char *) &option, sizeof( option ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + } + + // Bonjour for Windows broken on Windows XP + // + // Not sure why, but the default behavior for sockets is to behave incorrectly + // when using them in Overlapped I/O mode on XP. According to MSDN: + // + // SIO_UDP_CONNRESET (opcode setting: I, T==3) + // Windows XP: Controls whether UDP PORT_UNREACHABLE messages are reported. Set to TRUE to enable reporting. + // Set to FALSE to disable reporting. + // + // Packet traces from misbehaving Bonjour installations showed that ICMP port unreachable + // messages were being sent to us after we sent out packets to a multicast address. This is clearly + // incorrect behavior, but should be harmless. However, after receiving a port unreachable error, WinSock + // will no longer receive any packets from that socket, which is not harmless. This behavior is only + // seen on XP. + // + // So we turn off port unreachable reporting to make sure our sockets that are reading + // multicast packets function correctly under all circumstances. + + err = WSAIoctl( sock, SIO_UDP_CONNRESET, &behavior, sizeof(behavior), NULL, 0, &bytesReturned, NULL, NULL ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + if( inAddr->sa_family == AF_INET ) + { + mDNSv4Addr ipv4; + struct sockaddr_in sa4; + struct ip_mreq mreqv4; + + // Bind the socket to the desired port + + ipv4.NotAnInteger = ( (const struct sockaddr_in *) inAddr )->sin_addr.s_addr; + mDNSPlatformMemZero( &sa4, sizeof( sa4 ) ); + sa4.sin_family = AF_INET; + sa4.sin_port = port.NotAnInteger; + sa4.sin_addr.s_addr = ipv4.NotAnInteger; + + err = bind( sock, (struct sockaddr *) &sa4, sizeof( sa4 ) ); + check_translated_errno( err == 0, errno_compat(), kUnknownErr ); + + // Turn on option to receive destination addresses and receiving interface. + + option = 1; + err = setsockopt( sock, IPPROTO_IP, IP_PKTINFO, (char *) &option, sizeof( option ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + if ( !mDNSIPPortIsZero( port ) ) + { + // Join the all-DNS multicast group so we receive Multicast DNS packets + + mreqv4.imr_multiaddr.s_addr = AllDNSLinkGroup_v4.ip.v4.NotAnInteger; + mreqv4.imr_interface.s_addr = ipv4.NotAnInteger; + err = setsockopt( sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreqv4, sizeof( mreqv4 ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + // Specify the interface to send multicast packets on this socket. + + sa4.sin_addr.s_addr = ipv4.NotAnInteger; + err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_IF, (char *) &sa4.sin_addr, sizeof( sa4.sin_addr ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + // Enable multicast loopback so we receive multicast packets we send (for same-machine operations). + + option = 1; + err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &option, sizeof( option ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + } + + // Send unicast packets with TTL 255 (helps against spoofing). + + option = 255; + err = setsockopt( sock, IPPROTO_IP, IP_TTL, (char *) &option, sizeof( option ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + // Send multicast packets with TTL 255 (helps against spoofing). + + option = 255; + err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &option, sizeof( option ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + } + else if( inAddr->sa_family == AF_INET6 ) + { + struct sockaddr_in6 * sa6p; + struct sockaddr_in6 sa6; + struct ipv6_mreq mreqv6; + + sa6p = (struct sockaddr_in6 *) inAddr; + + // Bind the socket to the desired port + + mDNSPlatformMemZero( &sa6, sizeof( sa6 ) ); + sa6.sin6_family = AF_INET6; + sa6.sin6_port = port.NotAnInteger; + sa6.sin6_flowinfo = 0; + sa6.sin6_addr = sa6p->sin6_addr; + sa6.sin6_scope_id = sa6p->sin6_scope_id; + + err = bind( sock, (struct sockaddr *) &sa6, sizeof( sa6 ) ); + check_translated_errno( err == 0, errno_compat(), kUnknownErr ); + + // Turn on option to receive destination addresses and receiving interface. + + option = 1; + err = setsockopt( sock, IPPROTO_IPV6, IPV6_PKTINFO, (char *) &option, sizeof( option ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + // We only want to receive IPv6 packets (not IPv4-mapped IPv6 addresses) because we have a separate socket + // for IPv4, but the IPv6 stack in Windows currently doesn't support IPv4-mapped IPv6 addresses and doesn't + // support the IPV6_V6ONLY socket option so the following code would typically not be executed (or needed). + + #if( defined( IPV6_V6ONLY ) && ! defined( WIN_32 ) ) + option = 1; + err = setsockopt( sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &option, sizeof( option ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + #endif + + if ( !mDNSIPPortIsZero( port ) ) + { + // Join the all-DNS multicast group so we receive Multicast DNS packets. + + mreqv6.ipv6mr_multiaddr = *( (struct in6_addr *) &AllDNSLinkGroup_v6.ip.v6 ); + mreqv6.ipv6mr_interface = sa6p->sin6_scope_id; + err = setsockopt( sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *) &mreqv6, sizeof( mreqv6 ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + // Specify the interface to send multicast packets on this socket. + + option = (int) sa6p->sin6_scope_id; + err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char *) &option, sizeof( option ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + // Enable multicast loopback so we receive multicast packets we send (for same-machine operations). + + option = 1; + err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &option, sizeof( option ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + } + + // Send unicast packets with TTL 255 (helps against spoofing). + + option = 255; + err = setsockopt( sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char *) &option, sizeof( option ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + // Send multicast packets with TTL 255 (helps against spoofing). + + option = 255; + err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *) &option, sizeof( option ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + } + else + { + dlog( kDebugLevelError, DEBUG_NAME "%s: unsupport socket family (%d)\n", __ROUTINE__, inAddr->sa_family ); + err = kUnsupportedErr; + goto exit; + } + + // Success! + + *outSocketRef = sock; + sock = kInvalidSocketRef; + err = mStatus_NoError; + +exit: + if( IsValidSocket( sock ) ) + { + close_compat( sock ); + } + return( err ); +} + +//=========================================================================================================================== +// SetupSocket +//=========================================================================================================================== + +mDNSlocal mStatus SockAddrToMDNSAddr( const struct sockaddr * const inSA, mDNSAddr *outIP, mDNSIPPort *outPort ) +{ + mStatus err; + + check( inSA ); + check( outIP ); + + if( inSA->sa_family == AF_INET ) + { + struct sockaddr_in * sa4; + + sa4 = (struct sockaddr_in *) inSA; + outIP->type = mDNSAddrType_IPv4; + outIP->ip.v4.NotAnInteger = sa4->sin_addr.s_addr; + if( outPort ) + { + outPort->NotAnInteger = sa4->sin_port; + } + err = mStatus_NoError; + } + else if( inSA->sa_family == AF_INET6 ) + { + struct sockaddr_in6 * sa6; + + sa6 = (struct sockaddr_in6 *) inSA; + outIP->type = mDNSAddrType_IPv6; + outIP->ip.v6 = *( (mDNSv6Addr *) &sa6->sin6_addr ); + if( IN6_IS_ADDR_LINKLOCAL( &sa6->sin6_addr ) ) + { + outIP->ip.v6.w[ 1 ] = 0; + } + if( outPort ) + { + outPort->NotAnInteger = sa6->sin6_port; + } + err = mStatus_NoError; + } + else + { + dlog( kDebugLevelError, DEBUG_NAME "%s: invalid sa_family %d", __ROUTINE__, inSA->sa_family ); + err = mStatus_BadParamErr; + } + return( err ); +} + + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// UDPSocketNotification +//=========================================================================================================================== + +mDNSlocal void CALLBACK +UDPSocketNotification( SOCKET sock, LPWSANETWORKEVENTS event, void *context ) +{ + UDPSocket *udpSock = ( UDPSocket* ) context; + WSAMSG wmsg; + WSABUF wbuf; + struct sockaddr_storage sockSrcAddr; // This is filled in by the WSARecv* function + INT sockSrcAddrLen; // See above + mDNSAddr srcAddr; + mDNSInterfaceID iid; + mDNSIPPort srcPort; + mDNSAddr dstAddr; + mDNSIPPort dstPort; + uint8_t controlBuffer[ 128 ]; + mDNSu8 * end; + int num; + DWORD numTries; + mStatus err; + + DEBUG_UNUSED( sock ); + DEBUG_UNUSED( event ); + + require_action( udpSock != NULL, exit, err = mStatus_BadStateErr ); + + dlog( kDebugLevelChatty, DEBUG_NAME "%s: sock = %d\n", __ROUTINE__, udpSock->fd ); + + // Initialize the buffer structure + + wbuf.buf = (char *) &udpSock->packet; + wbuf.len = (u_long) sizeof( udpSock->packet ); + sockSrcAddrLen = sizeof( sockSrcAddr ); + + numTries = 0; + + do + { + if ( udpSock->recvMsgPtr ) + { + DWORD size; + + wmsg.name = ( LPSOCKADDR ) &sockSrcAddr; + wmsg.namelen = sockSrcAddrLen; + wmsg.lpBuffers = &wbuf; + wmsg.dwBufferCount = 1; + wmsg.Control.buf = ( CHAR* ) controlBuffer; + wmsg.Control.len = sizeof( controlBuffer ); + wmsg.dwFlags = 0; + + err = udpSock->recvMsgPtr( udpSock->fd, &wmsg, &size, NULL, NULL ); + err = translate_errno( ( err == 0 ), (OSStatus) WSAGetLastError(), kUnknownErr ); + num = ( int ) size; + + // iTunes 9.1 fails to install with Bonjour service on Windows 7 Ultimate + // + // There seems to be a bug in some network device drivers that involves calling WSARecvMsg(). + // Although all the parameters to WSARecvMsg() are correct, it returns a + // WSAEFAULT error code when there is no actual error. We have found experientially that falling + // back to using WSARecvFrom() when this happens will work correctly. + + if ( err == WSAEFAULT ) udpSock->recvMsgPtr = NULL; + } + else + { + DWORD flags = 0; + + num = WSARecvFrom( udpSock->fd, &wbuf, 1, NULL, &flags, ( LPSOCKADDR ) &sockSrcAddr, &sockSrcAddrLen, NULL, NULL ); + err = translate_errno( ( num >= 0 ), ( OSStatus ) WSAGetLastError(), kUnknownErr ); + } + + // According to MSDN : + // + // "WSAECONNRESET: For a UDP datagram socket, this error would indicate that a previous + // send operation resulted in an ICMP "Port Unreachable" message." + // + // Because this is the case, we want to ignore this error and try again. Just in case + // this is some kind of pathological condition, we'll break out of the retry loop + // after 100 iterations + + require_action( !err || ( err == WSAECONNRESET ) || ( err == WSAEFAULT ), exit, err = WSAGetLastError() ); + } + while ( ( ( err == WSAECONNRESET ) || ( err == WSAEFAULT ) ) && ( numTries++ < 100 ) ); + + require_noerr( err, exit ); + + // Translate the source of this packet into mDNS data types + + SockAddrToMDNSAddr( (struct sockaddr* ) &sockSrcAddr, &srcAddr, &srcPort ); + + // Initialize the destination of this packet. Just in case + // we can't determine this info because we couldn't call + // WSARecvMsg (recvMsgPtr) + + dstAddr = udpSock->addr; + dstPort = udpSock->port; + + if ( udpSock->recvMsgPtr ) + { + LPWSACMSGHDR header; + LPWSACMSGHDR last = NULL; + int count = 0; + + // Parse the control information. Reject packets received on the wrong interface. + + // INSTALL: Bonjour 2.0 on Windows can not start / stop + // + // There seems to be an interaction between Bullguard and this next bit of code. + // When a user's machine is running Bullguard, the control information that is + // returned is corrupted, and the code would go into an infinite loop. We'll add + // two bits of defensive coding here. The first will check that each pointer to + // the LPWSACMSGHDR that is returned in the for loop is different than the last. + // This fixes the problem with Bullguard. The second will break out of this loop + // after 100 iterations, just in case the corruption isn't caught by the first + // check. + + for ( header = WSA_CMSG_FIRSTHDR( &wmsg ); header; header = WSA_CMSG_NXTHDR( &wmsg, header ) ) + { + if ( ( header != last ) && ( ++count < 100 ) ) + { + last = header; + + if ( ( header->cmsg_level == IPPROTO_IP ) && ( header->cmsg_type == IP_PKTINFO ) ) + { + IN_PKTINFO * ipv4PacketInfo; + + ipv4PacketInfo = (IN_PKTINFO *) WSA_CMSG_DATA( header ); + + if ( udpSock->ifd != NULL ) + { + require_action( ipv4PacketInfo->ipi_ifindex == udpSock->ifd->index, exit, err = ( DWORD ) kMismatchErr ); + } + + dstAddr.type = mDNSAddrType_IPv4; + dstAddr.ip.v4.NotAnInteger = ipv4PacketInfo->ipi_addr.s_addr; + } + else if( ( header->cmsg_level == IPPROTO_IPV6 ) && ( header->cmsg_type == IPV6_PKTINFO ) ) + { + IN6_PKTINFO * ipv6PacketInfo; + + ipv6PacketInfo = (IN6_PKTINFO *) WSA_CMSG_DATA( header ); + + if ( udpSock->ifd != NULL ) + { + require_action( ipv6PacketInfo->ipi6_ifindex == ( udpSock->ifd->index - kIPv6IfIndexBase ), exit, err = ( DWORD ) kMismatchErr ); + } + + dstAddr.type = mDNSAddrType_IPv6; + dstAddr.ip.v6 = *( (mDNSv6Addr *) &ipv6PacketInfo->ipi6_addr ); + } + } + else + { + static BOOL loggedMessage = FALSE; + + if ( !loggedMessage ) + { + LogMsg( "UDPEndRecv: WSARecvMsg control information error." ); + loggedMessage = TRUE; + } + + break; + } + } + } + + dlog( kDebugLevelChatty, DEBUG_NAME "packet received\n" ); + dlog( kDebugLevelChatty, DEBUG_NAME " size = %d\n", num ); + dlog( kDebugLevelChatty, DEBUG_NAME " src = %#a:%u\n", &srcAddr, ntohs( srcPort.NotAnInteger ) ); + dlog( kDebugLevelChatty, DEBUG_NAME " dst = %#a:%u\n", &dstAddr, ntohs( dstPort.NotAnInteger ) ); + + if ( udpSock->ifd != NULL ) + { + dlog( kDebugLevelChatty, DEBUG_NAME " interface = %#a (index=0x%08X)\n", &udpSock->ifd->interfaceInfo.ip, udpSock->ifd->index ); + } + + dlog( kDebugLevelChatty, DEBUG_NAME "\n" ); + + iid = udpSock->ifd ? udpSock->ifd->interfaceInfo.InterfaceID : NULL; + end = ( (mDNSu8 *) &udpSock->packet ) + num; + + mDNSCoreReceive( udpSock->m, &udpSock->packet, end, &srcAddr, srcPort, &dstAddr, dstPort, iid ); + +exit: + + return; +} + + +//=========================================================================================================================== +// InterfaceListDidChange +//=========================================================================================================================== +void InterfaceListDidChange( mDNS * const inMDNS ) +{ + mStatus err; + + dlog( kDebugLevelInfo, DEBUG_NAME "interface list changed\n" ); + check( inMDNS ); + + // Tear down the existing interfaces and set up new ones using the new IP info. + + err = TearDownInterfaceList( inMDNS ); + check_noerr( err ); + + err = SetupInterfaceList( inMDNS ); + check_noerr( err ); + + err = uDNS_SetupDNSConfig( inMDNS ); + check_noerr( err ); + + // Inform clients of the change. + + mDNS_ConfigChanged(inMDNS); + + // Force mDNS to update. + + mDNSCoreMachineSleep( inMDNS, mDNSfalse ); // What is this for? Mac OS X does not do this +} + + +//=========================================================================================================================== +// ComputerDescriptionDidChange +//=========================================================================================================================== +void ComputerDescriptionDidChange( mDNS * const inMDNS ) +{ + dlog( kDebugLevelInfo, DEBUG_NAME "computer description has changed\n" ); + check( inMDNS ); + + // redo the names + SetupNiceName( inMDNS ); +} + + +//=========================================================================================================================== +// TCPIPConfigDidChange +//=========================================================================================================================== +void TCPIPConfigDidChange( mDNS * const inMDNS ) +{ + mStatus err; + + dlog( kDebugLevelInfo, DEBUG_NAME "TCP/IP config has changed\n" ); + check( inMDNS ); + + err = uDNS_SetupDNSConfig( inMDNS ); + check_noerr( err ); +} + + +//=========================================================================================================================== +// DynDNSConfigDidChange +//=========================================================================================================================== +void DynDNSConfigDidChange( mDNS * const inMDNS ) +{ + mStatus err; + + dlog( kDebugLevelInfo, DEBUG_NAME "DynDNS config has changed\n" ); + check( inMDNS ); + + SetDomainSecrets( inMDNS ); + + err = uDNS_SetupDNSConfig( inMDNS ); + check_noerr( err ); +} + + +//=========================================================================================================================== +// FileSharingDidChange +//=========================================================================================================================== +void FileSharingDidChange( mDNS * const inMDNS ) +{ + dlog( kDebugLevelInfo, DEBUG_NAME "File shares has changed\n" ); + check( inMDNS ); + + CheckFileShares( inMDNS ); +} + + +//=========================================================================================================================== +// FilewallDidChange +//=========================================================================================================================== +void FirewallDidChange( mDNS * const inMDNS ) +{ + dlog( kDebugLevelInfo, DEBUG_NAME "Firewall has changed\n" ); + check( inMDNS ); + + CheckFileShares( inMDNS ); +} + + +#if 0 +#pragma mark - +#pragma mark == Utilities == +#endif + +//=========================================================================================================================== +// getifaddrs +//=========================================================================================================================== + +mDNSlocal int getifaddrs( struct ifaddrs **outAddrs ) +{ + int err; + +#if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS ) + + // Try to the load the GetAdaptersAddresses function from the IP Helpers DLL. This API is only available on Windows + // XP or later. Looking up the symbol at runtime allows the code to still work on older systems without that API. + + if( !gIPHelperLibraryInstance ) + { + gIPHelperLibraryInstance = LoadLibrary( TEXT( "Iphlpapi" ) ); + if( gIPHelperLibraryInstance ) + { + gGetAdaptersAddressesFunctionPtr = + (GetAdaptersAddressesFunctionPtr) GetProcAddress( gIPHelperLibraryInstance, "GetAdaptersAddresses" ); + if( !gGetAdaptersAddressesFunctionPtr ) + { + BOOL ok; + + ok = FreeLibrary( gIPHelperLibraryInstance ); + check_translated_errno( ok, GetLastError(), kUnknownErr ); + gIPHelperLibraryInstance = NULL; + } + } + } + + // Use the new IPv6-capable routine if supported. Otherwise, fall back to the old and compatible IPv4-only code. + // Fall back to using getifaddrs_ipv4 if getifaddrs_ipv6 fails + // Fall back to using getifaddrs_ipv4 if getifaddrs_ipv6 returns no addrs + + if( !gGetAdaptersAddressesFunctionPtr || ( ( ( err = getifaddrs_ipv6( outAddrs ) ) != mStatus_NoError ) || ( ( outAddrs != NULL ) && ( *outAddrs == NULL ) ) ) ) + { + err = getifaddrs_ipv4( outAddrs ); + require_noerr( err, exit ); + } + +#else + + err = getifaddrs_ipv4( outAddrs ); + require_noerr( err, exit ); + +#endif + +exit: + return( err ); +} + +#if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS ) +//=========================================================================================================================== +// getifaddrs_ipv6 +//=========================================================================================================================== + +mDNSlocal int getifaddrs_ipv6( struct ifaddrs **outAddrs ) +{ + DWORD err; + int i; + DWORD flags; + struct ifaddrs * head; + struct ifaddrs ** next; + IP_ADAPTER_ADDRESSES * iaaList; + ULONG iaaListSize; + IP_ADAPTER_ADDRESSES * iaa; + size_t size; + struct ifaddrs * ifa; + + check( gGetAdaptersAddressesFunctionPtr ); + + head = NULL; + next = &head; + iaaList = NULL; + + // Get the list of interfaces. The first call gets the size and the second call gets the actual data. + // This loops to handle the case where the interface changes in the window after getting the size, but before the + // second call completes. A limit of 100 retries is enforced to prevent infinite loops if something else is wrong. + + flags = GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME; + i = 0; + for( ;; ) + { + iaaListSize = 0; + err = gGetAdaptersAddressesFunctionPtr( AF_UNSPEC, flags, NULL, NULL, &iaaListSize ); + check( err == ERROR_BUFFER_OVERFLOW ); + check( iaaListSize >= sizeof( IP_ADAPTER_ADDRESSES ) ); + + iaaList = (IP_ADAPTER_ADDRESSES *) malloc( iaaListSize ); + require_action( iaaList, exit, err = ERROR_NOT_ENOUGH_MEMORY ); + + err = gGetAdaptersAddressesFunctionPtr( AF_UNSPEC, flags, NULL, iaaList, &iaaListSize ); + if( err == ERROR_SUCCESS ) break; + + free( iaaList ); + iaaList = NULL; + ++i; + require( i < 100, exit ); + dlog( kDebugLevelWarning, "%s: retrying GetAdaptersAddresses after %d failure(s) (%d %m)\n", __ROUTINE__, i, err, err ); + } + + for( iaa = iaaList; iaa; iaa = iaa->Next ) + { + int addrIndex; + IP_ADAPTER_UNICAST_ADDRESS * addr; + DWORD ipv6IfIndex; + IP_ADAPTER_PREFIX * firstPrefix; + + if( iaa->IfIndex > 0xFFFFFF ) + { + dlog( kDebugLevelAlert, DEBUG_NAME "%s: IPv4 ifindex out-of-range (0x%08X)\n", __ROUTINE__, iaa->IfIndex ); + } + if( iaa->Ipv6IfIndex > 0xFF ) + { + dlog( kDebugLevelAlert, DEBUG_NAME "%s: IPv6 ifindex out-of-range (0x%08X)\n", __ROUTINE__, iaa->Ipv6IfIndex ); + } + + // For IPv4 interfaces, there seems to be a bug in iphlpapi.dll that causes the + // following code to crash when iterating through the prefix list. This seems + // to occur when iaa->Ipv6IfIndex != 0 when IPv6 is not installed on the host. + // This shouldn't happen according to Microsoft docs which states: + // + // "Ipv6IfIndex contains 0 if IPv6 is not available on the interface." + // + // So the data structure seems to be corrupted when we return from + // GetAdaptersAddresses(). The bug seems to occur when iaa->Length < + // sizeof(IP_ADAPTER_ADDRESSES), so when that happens, we'll manually + // modify iaa to have the correct values. + + if ( iaa->Length >= sizeof( IP_ADAPTER_ADDRESSES ) ) + { + ipv6IfIndex = iaa->Ipv6IfIndex; + firstPrefix = iaa->FirstPrefix; + } + else + { + ipv6IfIndex = 0; + firstPrefix = NULL; + } + + // Skip pseudo and tunnel interfaces. + + if( ( ( ipv6IfIndex == 1 ) && ( iaa->IfType != IF_TYPE_SOFTWARE_LOOPBACK ) ) || ( iaa->IfType == IF_TYPE_TUNNEL ) ) + { + continue; + } + + // Add each address as a separate interface to emulate the way getifaddrs works. + + for( addrIndex = 0, addr = iaa->FirstUnicastAddress; addr; ++addrIndex, addr = addr->Next ) + { + int family; + IP_ADAPTER_PREFIX * prefix; + uint32_t ipv4Index; + struct sockaddr_in ipv4Netmask; + + family = addr->Address.lpSockaddr->sa_family; + if( ( family != AF_INET ) && ( family != AF_INET6 ) ) continue; + + // iTunes 8: Bonjour doesn't work after upgrading iTunes 8 + // Seems as if the problem here is a buggy implementation of some network interface + // driver. It is reporting that is has a link-local address when it is actually + // disconnected. This was causing a problem in AddressToIndexAndMask. + // The solution is to call AddressToIndexAndMask first, and if unable to lookup + // the address, to ignore that address. + + ipv4Index = 0; + memset( &ipv4Netmask, 0, sizeof( ipv4Netmask ) ); + + if ( family == AF_INET ) + { + err = AddressToIndexAndMask( addr->Address.lpSockaddr, &ipv4Index, ( struct sockaddr* ) &ipv4Netmask ); + + if ( err ) + { + err = 0; + continue; + } + } + + ifa = (struct ifaddrs *) calloc( 1, sizeof( struct ifaddrs ) ); + require_action( ifa, exit, err = WSAENOBUFS ); + + *next = ifa; + next = &ifa->ifa_next; + + // Get the name. + + size = strlen( iaa->AdapterName ) + 1; + ifa->ifa_name = (char *) malloc( size ); + require_action( ifa->ifa_name, exit, err = WSAENOBUFS ); + memcpy( ifa->ifa_name, iaa->AdapterName, size ); + + // Get interface flags. + + ifa->ifa_flags = 0; + if( iaa->OperStatus == IfOperStatusUp ) ifa->ifa_flags |= IFF_UP; + if( iaa->IfType == IF_TYPE_SOFTWARE_LOOPBACK ) ifa->ifa_flags |= IFF_LOOPBACK; + else if ( IsPointToPoint( addr ) ) ifa->ifa_flags |= IFF_POINTTOPOINT; + if( !( iaa->Flags & IP_ADAPTER_NO_MULTICAST ) ) ifa->ifa_flags |= IFF_MULTICAST; + + + // Interface index being returned is 512 + // + // Windows does not have a uniform scheme for IPv4 and IPv6 interface indexes. + // This code used to shift the IPv4 index up to ensure uniqueness between + // it and IPv6 indexes. Although this worked, it was somewhat confusing to developers, who + // then see interface indexes passed back that don't correspond to anything + // that is seen in Win32 APIs or command line tools like "route". As a relatively + // small percentage of developers are actively using IPv6, it seems to + // make sense to make our use of IPv4 as confusion free as possible. + // So now, IPv6 interface indexes will be shifted up by a + // constant value which will serve to uniquely identify them, and we will + // leave IPv4 interface indexes unmodified. + + switch( family ) + { + case AF_INET: ifa->ifa_extra.index = iaa->IfIndex; break; + case AF_INET6: ifa->ifa_extra.index = ipv6IfIndex + kIPv6IfIndexBase; break; + default: break; + } + + // Get lease lifetime + + if ( ( iaa->IfType != IF_TYPE_SOFTWARE_LOOPBACK ) && ( addr->LeaseLifetime != 0 ) && ( addr->ValidLifetime != 0xFFFFFFFF ) ) + { + ifa->ifa_dhcpEnabled = TRUE; + ifa->ifa_dhcpLeaseExpires = time( NULL ) + addr->ValidLifetime; + } + else + { + ifa->ifa_dhcpEnabled = FALSE; + ifa->ifa_dhcpLeaseExpires = 0; + } + + if ( iaa->PhysicalAddressLength == sizeof( ifa->ifa_physaddr ) ) + { + memcpy( ifa->ifa_physaddr, iaa->PhysicalAddress, iaa->PhysicalAddressLength ); + } + + // Because we don't get notified of womp changes, we're going to just assume + // that all wired interfaces have it enabled. Before we go to sleep, we'll check + // if the interface actually supports it, and update mDNS->SystemWakeOnLANEnabled + // accordingly + + ifa->ifa_womp = ( iaa->IfType == IF_TYPE_ETHERNET_CSMACD ) ? mDNStrue : mDNSfalse; + + // Get address. + + switch( family ) + { + case AF_INET: + case AF_INET6: + ifa->ifa_addr = (struct sockaddr *) calloc( 1, (size_t) addr->Address.iSockaddrLength ); + require_action( ifa->ifa_addr, exit, err = WSAENOBUFS ); + memcpy( ifa->ifa_addr, addr->Address.lpSockaddr, (size_t) addr->Address.iSockaddrLength ); + break; + + default: + break; + } + check( ifa->ifa_addr ); + + // Get subnet mask (IPv4)/link prefix (IPv6). It is specified as a bit length (e.g. 24 for 255.255.255.0). + + switch ( family ) + { + case AF_INET: + { + struct sockaddr_in * sa4; + + sa4 = (struct sockaddr_in *) calloc( 1, sizeof( *sa4 ) ); + require_action( sa4, exit, err = WSAENOBUFS ); + sa4->sin_family = AF_INET; + sa4->sin_addr.s_addr = ipv4Netmask.sin_addr.s_addr; + + dlog( kDebugLevelInfo, DEBUG_NAME "%s: IPv4 mask = %s\n", __ROUTINE__, inet_ntoa( sa4->sin_addr ) ); + ifa->ifa_netmask = (struct sockaddr *) sa4; + break; + } + + case AF_INET6: + { + struct sockaddr_in6 *sa6; + char buf[ 256 ] = { 0 }; + DWORD buflen = sizeof( buf ); + + sa6 = (struct sockaddr_in6 *) calloc( 1, sizeof( *sa6 ) ); + require_action( sa6, exit, err = WSAENOBUFS ); + sa6->sin6_family = AF_INET6; + memset( sa6->sin6_addr.s6_addr, 0xFF, sizeof( sa6->sin6_addr.s6_addr ) ); + ifa->ifa_netmask = (struct sockaddr *) sa6; + + for ( prefix = firstPrefix; prefix; prefix = prefix->Next ) + { + IN6_ADDR mask; + IN6_ADDR maskedAddr; + int maskIndex; + DWORD len; + + // According to MSDN: + // "On Windows Vista and later, the linked IP_ADAPTER_PREFIX structures pointed to by the FirstPrefix member + // include three IP adapter prefixes for each IP address assigned to the adapter. These include the host IP address prefix, + // the subnet IP address prefix, and the subnet broadcast IP address prefix. + // In addition, for each adapter there is a multicast address prefix and a broadcast address prefix. + // On Windows XP with SP1 and later prior to Windows Vista, the linked IP_ADAPTER_PREFIX structures pointed to by the FirstPrefix member + // include only a single IP adapter prefix for each IP address assigned to the adapter." + + // We're only interested in the subnet IP address prefix. We'll determine if the prefix is the + // subnet prefix by masking our address with a mask (computed from the prefix length) and see if that is the same + // as the prefix address. + + if ( ( prefix->PrefixLength == 0 ) || + ( prefix->PrefixLength > 128 ) || + ( addr->Address.iSockaddrLength != prefix->Address.iSockaddrLength ) || + ( memcmp( addr->Address.lpSockaddr, prefix->Address.lpSockaddr, addr->Address.iSockaddrLength ) == 0 ) ) + { + continue; + } + + // Compute the mask + + memset( mask.s6_addr, 0, sizeof( mask.s6_addr ) ); + + for ( len = (int) prefix->PrefixLength, maskIndex = 0; len > 0; len -= 8 ) + { + uint8_t maskByte = ( len >= 8 ) ? 0xFF : (uint8_t)( ( 0xFFU << ( 8 - len ) ) & 0xFFU ); + mask.s6_addr[ maskIndex++ ] = maskByte; + } + + // Apply the mask + + for ( i = 0; i < 16; i++ ) + { + maskedAddr.s6_addr[ i ] = ( ( struct sockaddr_in6* ) addr->Address.lpSockaddr )->sin6_addr.s6_addr[ i ] & mask.s6_addr[ i ]; + } + + // Compare + + if ( memcmp( ( ( struct sockaddr_in6* ) prefix->Address.lpSockaddr )->sin6_addr.s6_addr, maskedAddr.s6_addr, sizeof( maskedAddr.s6_addr ) ) == 0 ) + { + memcpy( sa6->sin6_addr.s6_addr, mask.s6_addr, sizeof( mask.s6_addr ) ); + break; + } + } + + WSAAddressToStringA( ( LPSOCKADDR ) sa6, sizeof( struct sockaddr_in6 ), NULL, buf, &buflen ); + dlog( kDebugLevelInfo, DEBUG_NAME "%s: IPv6 mask = %s\n", __ROUTINE__, buf ); + + break; + } + + default: + break; + } + } + } + + // Success! + + if( outAddrs ) + { + *outAddrs = head; + head = NULL; + } + err = ERROR_SUCCESS; + +exit: + if( head ) + { + freeifaddrs( head ); + } + if( iaaList ) + { + free( iaaList ); + } + return( (int) err ); +} + +#endif // MDNS_WINDOWS_USE_IPV6_IF_ADDRS + +//=========================================================================================================================== +// getifaddrs_ipv4 +//=========================================================================================================================== + +mDNSlocal int getifaddrs_ipv4( struct ifaddrs **outAddrs ) +{ + int err; + SOCKET sock; + DWORD size; + DWORD actualSize; + INTERFACE_INFO * buffer; + INTERFACE_INFO * tempBuffer; + INTERFACE_INFO * ifInfo; + int n; + int i; + struct ifaddrs * head; + struct ifaddrs ** next; + struct ifaddrs * ifa; + + sock = INVALID_SOCKET; + buffer = NULL; + head = NULL; + next = &head; + + // Get the interface list. WSAIoctl is called with SIO_GET_INTERFACE_LIST, but since this does not provide a + // way to determine the size of the interface list beforehand, we have to start with an initial size guess and + // call WSAIoctl repeatedly with increasing buffer sizes until it succeeds. Limit this to 100 tries for safety. + + sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); + err = translate_errno( IsValidSocket( sock ), errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + n = 0; + size = 16 * sizeof( INTERFACE_INFO ); + for( ;; ) + { + tempBuffer = (INTERFACE_INFO *) realloc( buffer, size ); + require_action( tempBuffer, exit, err = WSAENOBUFS ); + buffer = tempBuffer; + + err = WSAIoctl( sock, SIO_GET_INTERFACE_LIST, NULL, 0, buffer, size, &actualSize, NULL, NULL ); + if( err == 0 ) + { + break; + } + + ++n; + require_action( n < 100, exit, err = WSAEADDRNOTAVAIL ); + + size += ( 16 * sizeof( INTERFACE_INFO ) ); + } + check( actualSize <= size ); + check( ( actualSize % sizeof( INTERFACE_INFO ) ) == 0 ); + n = (int)( actualSize / sizeof( INTERFACE_INFO ) ); + + // Process the raw interface list and build a linked list of IPv4 interfaces. + + for( i = 0; i < n; ++i ) + { + uint32_t ifIndex; + struct sockaddr_in netmask; + + ifInfo = &buffer[ i ]; + if( ifInfo->iiAddress.Address.sa_family != AF_INET ) + { + continue; + } + + // iTunes 8: Bonjour doesn't work after upgrading iTunes 8 + // See comment in getifaddrs_ipv6 + + ifIndex = 0; + memset( &netmask, 0, sizeof( netmask ) ); + err = AddressToIndexAndMask( ( struct sockaddr* ) &ifInfo->iiAddress.AddressIn, &ifIndex, ( struct sockaddr* ) &netmask ); + + if ( err ) + { + continue; + } + + ifa = (struct ifaddrs *) calloc( 1, sizeof( struct ifaddrs ) ); + require_action( ifa, exit, err = WSAENOBUFS ); + + *next = ifa; + next = &ifa->ifa_next; + + // Get the name. + + ifa->ifa_name = (char *) malloc( 16 ); + require_action( ifa->ifa_name, exit, err = WSAENOBUFS ); + sprintf( ifa->ifa_name, "%d", i + 1 ); + + // Get interface flags. + + ifa->ifa_flags = (u_int) ifInfo->iiFlags; + + // Get addresses. + + if ( ifInfo->iiAddress.Address.sa_family == AF_INET ) + { + struct sockaddr_in * sa4; + + sa4 = &ifInfo->iiAddress.AddressIn; + ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sa4 ) ); + require_action( ifa->ifa_addr, exit, err = WSAENOBUFS ); + memcpy( ifa->ifa_addr, sa4, sizeof( *sa4 ) ); + + ifa->ifa_netmask = (struct sockaddr*) calloc(1, sizeof( *sa4 ) ); + require_action( ifa->ifa_netmask, exit, err = WSAENOBUFS ); + + // Service won't start on Win2K. The address + // family field was not being initialized. + + ifa->ifa_netmask->sa_family = AF_INET; + ( ( struct sockaddr_in* ) ifa->ifa_netmask )->sin_addr = netmask.sin_addr; + ifa->ifa_extra.index = ifIndex; + } + else + { + // Emulate an interface index. + + ifa->ifa_extra.index = (uint32_t)( i + 1 ); + } + } + + // Success! + + if( outAddrs ) + { + *outAddrs = head; + head = NULL; + } + err = 0; + +exit: + + if( head ) + { + freeifaddrs( head ); + } + if( buffer ) + { + free( buffer ); + } + if( sock != INVALID_SOCKET ) + { + closesocket( sock ); + } + return( err ); +} + +//=========================================================================================================================== +// freeifaddrs +//=========================================================================================================================== + +mDNSlocal void freeifaddrs( struct ifaddrs *inIFAs ) +{ + struct ifaddrs * p; + struct ifaddrs * q; + + // Free each piece of the structure. Set to null after freeing to handle macro-aliased fields. + + for( p = inIFAs; p; p = q ) + { + q = p->ifa_next; + + if( p->ifa_name ) + { + free( p->ifa_name ); + p->ifa_name = NULL; + } + if( p->ifa_addr ) + { + free( p->ifa_addr ); + p->ifa_addr = NULL; + } + if( p->ifa_netmask ) + { + free( p->ifa_netmask ); + p->ifa_netmask = NULL; + } + if( p->ifa_broadaddr ) + { + free( p->ifa_broadaddr ); + p->ifa_broadaddr = NULL; + } + if( p->ifa_dstaddr ) + { + free( p->ifa_dstaddr ); + p->ifa_dstaddr = NULL; + } + if( p->ifa_data ) + { + free( p->ifa_data ); + p->ifa_data = NULL; + } + free( p ); + } +} + + +//=========================================================================================================================== +// GetPrimaryInterface +//=========================================================================================================================== + +mDNSlocal DWORD +GetPrimaryInterface() +{ + PMIB_IPFORWARDTABLE pIpForwardTable = NULL; + DWORD dwSize = 0; + BOOL bOrder = FALSE; + OSStatus err; + DWORD index = 0; + DWORD metric = 0; + unsigned long int i; + + // Find out how big our buffer needs to be. + + err = GetIpForwardTable(NULL, &dwSize, bOrder); + require_action( err == ERROR_INSUFFICIENT_BUFFER, exit, err = kUnknownErr ); + + // Allocate the memory for the table + + pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc( dwSize ); + require_action( pIpForwardTable, exit, err = kNoMemoryErr ); + + // Now get the table. + + err = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder); + require_noerr( err, exit ); + + + // Search for the row in the table we want. + + for ( i = 0; i < pIpForwardTable->dwNumEntries; i++) + { + // Look for a default route + + if ( pIpForwardTable->table[i].dwForwardDest == 0 ) + { + if ( index && ( pIpForwardTable->table[i].dwForwardMetric1 >= metric ) ) + { + continue; + } + + index = pIpForwardTable->table[i].dwForwardIfIndex; + metric = pIpForwardTable->table[i].dwForwardMetric1; + } + } + +exit: + + if ( pIpForwardTable != NULL ) + { + free( pIpForwardTable ); + } + + return index; +} + + +//=========================================================================================================================== +// AddressToIndexAndMask +//=========================================================================================================================== + +mDNSlocal mStatus +AddressToIndexAndMask( struct sockaddr * addr, uint32_t * ifIndex, struct sockaddr * mask ) +{ + // Before calling AddIPAddress we use GetIpAddrTable to get + // an adapter to which we can add the IP. + + PMIB_IPADDRTABLE pIPAddrTable = NULL; + DWORD dwSize = 0; + mStatus err = mStatus_UnknownErr; + DWORD i; + + // For now, this is only for IPv4 addresses. That is why we can safely cast + // addr's to sockaddr_in. + + require_action( addr->sa_family == AF_INET, exit, err = mStatus_UnknownErr ); + + // Make an initial call to GetIpAddrTable to get the + // necessary size into the dwSize variable + + for ( i = 0; i < 100; i++ ) + { + err = GetIpAddrTable( pIPAddrTable, &dwSize, 0 ); + + if ( err != ERROR_INSUFFICIENT_BUFFER ) + { + break; + } + + pIPAddrTable = (MIB_IPADDRTABLE *) realloc( pIPAddrTable, dwSize ); + require_action( pIPAddrTable, exit, err = WSAENOBUFS ); + } + + require_noerr( err, exit ); + err = mStatus_UnknownErr; + + for ( i = 0; i < pIPAddrTable->dwNumEntries; i++ ) + { + if ( ( ( struct sockaddr_in* ) addr )->sin_addr.s_addr == pIPAddrTable->table[i].dwAddr ) + { + *ifIndex = pIPAddrTable->table[i].dwIndex; + ( ( struct sockaddr_in*) mask )->sin_addr.s_addr = pIPAddrTable->table[i].dwMask; + err = mStatus_NoError; + break; + } + } + +exit: + + if ( pIPAddrTable ) + { + free( pIPAddrTable ); + } + + return err; +} + + +//=========================================================================================================================== +// CanReceiveUnicast +//=========================================================================================================================== + +mDNSlocal mDNSBool CanReceiveUnicast( void ) +{ + mDNSBool ok; + SocketRef sock; + struct sockaddr_in addr; + + // Try to bind to the port without the SO_REUSEADDR option to test if someone else has already bound to it. + + sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); + check_translated_errno( IsValidSocket( sock ), errno_compat(), kUnknownErr ); + ok = IsValidSocket( sock ); + if( ok ) + { + mDNSPlatformMemZero( &addr, sizeof( addr ) ); + addr.sin_family = AF_INET; + addr.sin_port = MulticastDNSPort.NotAnInteger; + addr.sin_addr.s_addr = htonl( INADDR_ANY ); + + ok = ( bind( sock, (struct sockaddr *) &addr, sizeof( addr ) ) == 0 ); + close_compat( sock ); + } + + dlog( kDebugLevelInfo, DEBUG_NAME "Unicast UDP responses %s\n", ok ? "okay" : "*not allowed*" ); + return( ok ); +} + + +//=========================================================================================================================== +// IsPointToPoint +//=========================================================================================================================== + +mDNSlocal mDNSBool IsPointToPoint( IP_ADAPTER_UNICAST_ADDRESS * addr ) +{ + struct ifaddrs * addrs = NULL; + struct ifaddrs * p = NULL; + OSStatus err; + mDNSBool ret = mDNSfalse; + + // For now, only works for IPv4 interfaces + + if ( addr->Address.lpSockaddr->sa_family == AF_INET ) + { + // The getifaddrs_ipv4 call will give us correct information regarding IFF_POINTTOPOINT flags. + + err = getifaddrs_ipv4( &addrs ); + require_noerr( err, exit ); + + for ( p = addrs; p; p = p->ifa_next ) + { + if ( ( addr->Address.lpSockaddr->sa_family == p->ifa_addr->sa_family ) && + ( ( ( struct sockaddr_in* ) addr->Address.lpSockaddr )->sin_addr.s_addr == ( ( struct sockaddr_in* ) p->ifa_addr )->sin_addr.s_addr ) ) + { + ret = ( p->ifa_flags & IFF_POINTTOPOINT ) ? mDNStrue : mDNSfalse; + break; + } + } + } + +exit: + + if ( addrs ) + { + freeifaddrs( addrs ); + } + + return ret; +} + + +//=========================================================================================================================== +// GetWindowsVersionString +//=========================================================================================================================== + +mDNSlocal OSStatus GetWindowsVersionString( char *inBuffer, size_t inBufferSize ) +{ +#if( !defined( VER_PLATFORM_WIN32_CE ) ) + #define VER_PLATFORM_WIN32_CE 3 +#endif + + OSStatus err; + OSVERSIONINFO osInfo; + BOOL ok; + const char * versionString; + DWORD platformID; + DWORD majorVersion; + DWORD minorVersion; + DWORD buildNumber; + + versionString = "unknown Windows version"; + + osInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO ); + ok = GetVersionEx( &osInfo ); + err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + platformID = osInfo.dwPlatformId; + majorVersion = osInfo.dwMajorVersion; + minorVersion = osInfo.dwMinorVersion; + buildNumber = osInfo.dwBuildNumber & 0xFFFF; + + if( ( platformID == VER_PLATFORM_WIN32_WINDOWS ) && ( majorVersion == 4 ) ) + { + if( ( minorVersion < 10 ) && ( buildNumber == 950 ) ) + { + versionString = "Windows 95"; + } + else if( ( minorVersion < 10 ) && ( ( buildNumber > 950 ) && ( buildNumber <= 1080 ) ) ) + { + versionString = "Windows 95 SP1"; + } + else if( ( minorVersion < 10 ) && ( buildNumber > 1080 ) ) + { + versionString = "Windows 95 OSR2"; + } + else if( ( minorVersion == 10 ) && ( buildNumber == 1998 ) ) + { + versionString = "Windows 98"; + } + else if( ( minorVersion == 10 ) && ( ( buildNumber > 1998 ) && ( buildNumber < 2183 ) ) ) + { + versionString = "Windows 98 SP1"; + } + else if( ( minorVersion == 10 ) && ( buildNumber >= 2183 ) ) + { + versionString = "Windows 98 SE"; + } + else if( minorVersion == 90 ) + { + versionString = "Windows ME"; + } + } + else if( platformID == VER_PLATFORM_WIN32_NT ) + { + if( ( majorVersion == 3 ) && ( minorVersion == 51 ) ) + { + versionString = "Windows NT 3.51"; + } + else if( ( majorVersion == 4 ) && ( minorVersion == 0 ) ) + { + versionString = "Windows NT 4"; + } + else if( ( majorVersion == 5 ) && ( minorVersion == 0 ) ) + { + versionString = "Windows 2000"; + } + else if( ( majorVersion == 5 ) && ( minorVersion == 1 ) ) + { + versionString = "Windows XP"; + } + else if( ( majorVersion == 5 ) && ( minorVersion == 2 ) ) + { + versionString = "Windows Server 2003"; + } + } + else if( platformID == VER_PLATFORM_WIN32_CE ) + { + versionString = "Windows CE"; + } + +exit: + if( inBuffer && ( inBufferSize > 0 ) ) + { + inBufferSize -= 1; + strncpy( inBuffer, versionString, inBufferSize ); + inBuffer[ inBufferSize ] = '\0'; + } + return( err ); +} + + +//=========================================================================================================================== +// RegQueryString +//=========================================================================================================================== + +mDNSlocal mStatus +RegQueryString( HKEY key, LPCSTR valueName, LPSTR * string, DWORD * stringLen, DWORD * enabled ) +{ + DWORD type; + int i; + mStatus err; + + *stringLen = MAX_ESCAPED_DOMAIN_NAME; + *string = NULL; + i = 0; + + do + { + if ( *string ) + { + free( *string ); + } + + *string = (char*) malloc( *stringLen ); + require_action( *string, exit, err = mStatus_NoMemoryErr ); + + err = RegQueryValueExA( key, valueName, 0, &type, (LPBYTE) *string, stringLen ); + + i++; + } + while ( ( err == ERROR_MORE_DATA ) && ( i < 100 ) ); + + require_noerr_quiet( err, exit ); + + if ( enabled ) + { + DWORD dwSize = sizeof( DWORD ); + + err = RegQueryValueEx( key, TEXT("Enabled"), NULL, NULL, (LPBYTE) enabled, &dwSize ); + check_noerr( err ); + + err = kNoErr; + } + +exit: + + return err; +} + + +//=========================================================================================================================== +// StringToAddress +//=========================================================================================================================== + +mDNSlocal mStatus StringToAddress( mDNSAddr * ip, LPSTR string ) +{ + struct sockaddr_in6 sa6; + struct sockaddr_in sa4; + INT dwSize; + mStatus err; + + sa6.sin6_family = AF_INET6; + dwSize = sizeof( sa6 ); + + err = WSAStringToAddressA( string, AF_INET6, NULL, (struct sockaddr*) &sa6, &dwSize ); + + if ( err == mStatus_NoError ) + { + err = SetupAddr( ip, (struct sockaddr*) &sa6 ); + require_noerr( err, exit ); + } + else + { + sa4.sin_family = AF_INET; + dwSize = sizeof( sa4 ); + + err = WSAStringToAddressA( string, AF_INET, NULL, (struct sockaddr*) &sa4, &dwSize ); + err = translate_errno( err == 0, WSAGetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + err = SetupAddr( ip, (struct sockaddr*) &sa4 ); + require_noerr( err, exit ); + } + +exit: + + return err; +} + + +//=========================================================================================================================== +// myGetIfAddrs +//=========================================================================================================================== + +mDNSlocal struct ifaddrs* +myGetIfAddrs(int refresh) +{ + static struct ifaddrs *ifa = NULL; + + if (refresh && ifa) + { + freeifaddrs(ifa); + ifa = NULL; + } + + if (ifa == NULL) + { + getifaddrs(&ifa); + } + + return ifa; +} + + +//=========================================================================================================================== +// TCHARtoUTF8 +//=========================================================================================================================== + +mDNSlocal OSStatus +TCHARtoUTF8( const TCHAR *inString, char *inBuffer, size_t inBufferSize ) +{ +#if( defined( UNICODE ) || defined( _UNICODE ) ) + OSStatus err; + int len; + + len = WideCharToMultiByte( CP_UTF8, 0, inString, -1, inBuffer, (int) inBufferSize, NULL, NULL ); + err = translate_errno( len > 0, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + +exit: + return( err ); +#else + return( WindowsLatin1toUTF8( inString, inBuffer, inBufferSize ) ); +#endif +} + + +//=========================================================================================================================== +// WindowsLatin1toUTF8 +//=========================================================================================================================== + +mDNSlocal OSStatus +WindowsLatin1toUTF8( const char *inString, char *inBuffer, size_t inBufferSize ) +{ + OSStatus err; + WCHAR * utf16; + int len; + + utf16 = NULL; + + // Windows doesn't support going directly from Latin-1 to UTF-8 so we have to go from Latin-1 to UTF-16 first. + + len = MultiByteToWideChar( CP_ACP, 0, inString, -1, NULL, 0 ); + err = translate_errno( len > 0, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + utf16 = (WCHAR *) malloc( len * sizeof( *utf16 ) ); + require_action( utf16, exit, err = kNoMemoryErr ); + + len = MultiByteToWideChar( CP_ACP, 0, inString, -1, utf16, len ); + err = translate_errno( len > 0, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + // Now convert the temporary UTF-16 to UTF-8. + + len = WideCharToMultiByte( CP_UTF8, 0, utf16, -1, inBuffer, (int) inBufferSize, NULL, NULL ); + err = translate_errno( len > 0, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + +exit: + if( utf16 ) free( utf16 ); + return( err ); +} + + +//=========================================================================================================================== +// TCPCloseSocket +//=========================================================================================================================== + +mDNSlocal void +TCPCloseSocket( TCPSocket * sock ) +{ + dlog( kDebugLevelChatty, DEBUG_NAME "closing TCPSocket 0x%x:%d\n", sock, sock->fd ); + + if ( sock->fd != INVALID_SOCKET ) + { + closesocket( sock->fd ); + sock->fd = INVALID_SOCKET; + } +} + + +//=========================================================================================================================== +// UDPCloseSocket +//=========================================================================================================================== + +mDNSlocal void +UDPCloseSocket( UDPSocket * sock ) +{ + dlog( kDebugLevelChatty, DEBUG_NAME "closing UDPSocket %d\n", sock->fd ); + + if ( sock->fd != INVALID_SOCKET ) + { + mDNSPollUnregisterSocket( sock->fd ); + closesocket( sock->fd ); + sock->fd = INVALID_SOCKET; + } +} + + +//=========================================================================================================================== +// SetupAddr +//=========================================================================================================================== + +mDNSlocal mStatus SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa) + { + if (!sa) { LogMsg("SetupAddr ERROR: NULL sockaddr"); return(mStatus_Invalid); } + + if (sa->sa_family == AF_INET) + { + struct sockaddr_in *ifa_addr = (struct sockaddr_in *)sa; + ip->type = mDNSAddrType_IPv4; + ip->ip.v4.NotAnInteger = ifa_addr->sin_addr.s_addr; + return(mStatus_NoError); + } + + if (sa->sa_family == AF_INET6) + { + struct sockaddr_in6 *ifa_addr = (struct sockaddr_in6 *)sa; + ip->type = mDNSAddrType_IPv6; + if (IN6_IS_ADDR_LINKLOCAL(&ifa_addr->sin6_addr)) ifa_addr->sin6_addr.u.Word[1] = 0; + ip->ip.v6 = *(mDNSv6Addr*)&ifa_addr->sin6_addr; + return(mStatus_NoError); + } + + LogMsg("SetupAddr invalid sa_family %d", sa->sa_family); + return(mStatus_Invalid); + } + + +mDNSlocal void GetDDNSFQDN( domainname *const fqdn ) +{ + LPSTR name = NULL; + DWORD dwSize; + DWORD enabled; + HKEY key = NULL; + OSStatus err; + + check( fqdn ); + + // Initialize + + fqdn->c[0] = '\0'; + + // Get info from Bonjour registry key + + err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode TEXT("\\DynDNS\\Setup\\") kServiceDynDNSHostNames, &key ); + require_noerr( err, exit ); + + err = RegQueryString( key, "", &name, &dwSize, &enabled ); + if ( !err && ( name[0] != '\0' ) && enabled ) + { + if ( !MakeDomainNameFromDNSNameString( fqdn, name ) || !fqdn->c[0] ) + { + dlog( kDebugLevelError, "bad DDNS host name in registry: %s", name[0] ? name : "(unknown)"); + } + } + +exit: + + if ( key ) + { + RegCloseKey( key ); + key = NULL; + } + + if ( name ) + { + free( name ); + name = NULL; + } +} + + +#ifdef UNICODE +mDNSlocal void GetDDNSDomains( DNameListElem ** domains, LPCWSTR lpSubKey ) +#else +mDNSlocal void GetDDNSConfig( DNameListElem ** domains, LPCSTR lpSubKey ) +#endif +{ + char subKeyName[kRegistryMaxKeyLength + 1]; + DWORD cSubKeys = 0; + DWORD cbMaxSubKey; + DWORD cchMaxClass; + DWORD dwSize; + HKEY key = NULL; + HKEY subKey = NULL; + domainname dname; + DWORD i; + OSStatus err; + + check( domains ); + + // Initialize + + *domains = NULL; + + err = RegCreateKey( HKEY_LOCAL_MACHINE, lpSubKey, &key ); + require_noerr( err, exit ); + + // Get information about this node + + err = RegQueryInfoKey( key, NULL, NULL, NULL, &cSubKeys, &cbMaxSubKey, &cchMaxClass, NULL, NULL, NULL, NULL, NULL ); + require_noerr( err, exit ); + + for ( i = 0; i < cSubKeys; i++) + { + DWORD enabled; + + dwSize = kRegistryMaxKeyLength; + + err = RegEnumKeyExA( key, i, subKeyName, &dwSize, NULL, NULL, NULL, NULL ); + + if ( !err ) + { + err = RegOpenKeyExA( key, subKeyName, 0, KEY_READ, &subKey ); + require_noerr( err, exit ); + + dwSize = sizeof( DWORD ); + err = RegQueryValueExA( subKey, "Enabled", NULL, NULL, (LPBYTE) &enabled, &dwSize ); + + if ( !err && ( subKeyName[0] != '\0' ) && enabled ) + { + if ( !MakeDomainNameFromDNSNameString( &dname, subKeyName ) || !dname.c[0] ) + { + dlog( kDebugLevelError, "bad DDNS domain in registry: %s", subKeyName[0] ? subKeyName : "(unknown)"); + } + else + { + DNameListElem * domain = (DNameListElem*) malloc( sizeof( DNameListElem ) ); + require_action( domain, exit, err = mStatus_NoMemoryErr ); + + AssignDomainName(&domain->name, &dname); + domain->next = *domains; + + *domains = domain; + } + } + + RegCloseKey( subKey ); + subKey = NULL; + } + } + +exit: + + if ( subKey ) + { + RegCloseKey( subKey ); + } + + if ( key ) + { + RegCloseKey( key ); + } +} + + +mDNSlocal void SetDomainSecret( mDNS * const m, const domainname * inDomain ) +{ + char domainUTF8[ 256 ]; + DomainAuthInfo *foundInList; + DomainAuthInfo *ptr; + char outDomain[ 256 ]; + char outKey[ 256 ]; + char outSecret[ 256 ]; + OSStatus err; + + ConvertDomainNameToCString( inDomain, domainUTF8 ); + + // If we're able to find a secret for this domain + + if ( LsaGetSecret( domainUTF8, outDomain, sizeof( outDomain ), outKey, sizeof( outKey ), outSecret, sizeof( outSecret ) ) ) + { + domainname domain; + domainname key; + + // Tell the core about this secret + + MakeDomainNameFromDNSNameString( &domain, outDomain ); + MakeDomainNameFromDNSNameString( &key, outKey ); + + for (foundInList = m->AuthInfoList; foundInList; foundInList = foundInList->next) + if (SameDomainName(&foundInList->domain, &domain ) ) break; + + ptr = foundInList; + + if (!ptr) + { + ptr = (DomainAuthInfo*)malloc(sizeof(DomainAuthInfo)); + require_action( ptr, exit, err = mStatus_NoMemoryErr ); + } + + err = mDNS_SetSecretForDomain(m, ptr, &domain, &key, outSecret, NULL, NULL, FALSE ); + require_action( err != mStatus_BadParamErr, exit, if (!foundInList ) mDNSPlatformMemFree( ptr ) ); + + debugf("Setting shared secret for zone %s with key %##s", outDomain, key.c); + } + +exit: + + return; +} + + +mDNSlocal VOID CALLBACK +CheckFileSharesProc( LPVOID arg, DWORD dwTimerLowValue, DWORD dwTimerHighValue ) +{ + mDNS * const m = ( mDNS * const ) arg; + + ( void ) dwTimerLowValue; + ( void ) dwTimerHighValue; + + CheckFileShares( m ); +} + + +mDNSlocal unsigned __stdcall +SMBRegistrationThread( void * arg ) +{ + mDNS * const m = ( mDNS * const ) arg; + DNSServiceRef sref = NULL; + HANDLE handles[ 3 ]; + mDNSu8 txtBuf[ 256 ]; + mDNSu8 * txtPtr; + size_t keyLen; + size_t valLen; + mDNSIPPort port = { { SMBPortAsNumber >> 8, SMBPortAsNumber & 0xFF } }; + DNSServiceErrorType err; + + DEBUG_UNUSED( arg ); + + handles[ 0 ] = gSMBThreadStopEvent; + handles[ 1 ] = gSMBThreadRegisterEvent; + handles[ 2 ] = gSMBThreadDeregisterEvent; + + memset( txtBuf, 0, sizeof( txtBuf ) ); + txtPtr = txtBuf; + keyLen = strlen( "netbios=" ); + valLen = strlen( m->p->nbname ); + require_action( valLen < 32, exit, err = kUnknownErr ); // This should never happen, but check to avoid further memory corruption + *txtPtr++ = ( mDNSu8 ) ( keyLen + valLen ); + memcpy( txtPtr, "netbios=", keyLen ); + txtPtr += keyLen; + if ( valLen ) { memcpy( txtPtr, m->p->nbname, valLen ); txtPtr += ( mDNSu8 ) valLen; } + keyLen = strlen( "domain=" ); + valLen = strlen( m->p->nbdomain ); + require_action( valLen < 32, exit, err = kUnknownErr ); // This should never happen, but check to avoid further memory corruption + *txtPtr++ = ( mDNSu8 )( keyLen + valLen ); + memcpy( txtPtr, "domain=", keyLen ); + txtPtr += keyLen; + if ( valLen ) { memcpy( txtPtr, m->p->nbdomain, valLen ); txtPtr += valLen; } + + for ( ;; ) + { + DWORD ret; + + ret = WaitForMultipleObjects( 3, handles, FALSE, INFINITE ); + + if ( ret != WAIT_FAILED ) + { + if ( ret == kSMBStopEvent ) + { + break; + } + else if ( ret == kSMBRegisterEvent ) + { + err = gDNSServiceRegister( &sref, 0, 0, NULL, "_smb._tcp,_file", NULL, NULL, ( uint16_t ) port.NotAnInteger, ( mDNSu16 )( txtPtr - txtBuf ), txtBuf, NULL, NULL ); + + if ( err ) + { + LogMsg( "SMBRegistrationThread: DNSServiceRegister returned %d\n", err ); + sref = NULL; + break; + } + } + else if ( ret == kSMBDeregisterEvent ) + { + if ( sref ) + { + gDNSServiceRefDeallocate( sref ); + sref = NULL; + } + } + } + else + { + LogMsg( "SMBRegistrationThread: WaitForMultipleObjects returned %d\n", GetLastError() ); + break; + } + } + +exit: + + if ( sref != NULL ) + { + gDNSServiceRefDeallocate( sref ); + sref = NULL; + } + + SetEvent( gSMBThreadQuitEvent ); + _endthreadex( 0 ); + return 0; +} + + +mDNSlocal void +CheckFileShares( mDNS * const m ) +{ + PSHARE_INFO_1 bufPtr = ( PSHARE_INFO_1 ) NULL; + DWORD entriesRead = 0; + DWORD totalEntries = 0; + DWORD resume = 0; + mDNSBool advertise = mDNSfalse; + mDNSBool fileSharing = mDNSfalse; + mDNSBool printSharing = mDNSfalse; + HKEY key = NULL; + BOOL retry = FALSE; + NET_API_STATUS res; + mStatus err; + + check( m ); + + // Only do this if we're not shutting down + + require_action_quiet( m->AdvertiseLocalAddresses && !m->ShutdownTime, exit, err = kNoErr ); + + err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\Services\\SMB", &key ); + + if ( !err ) + { + DWORD dwSize = sizeof( DWORD ); + RegQueryValueEx( key, L"Advertise", NULL, NULL, (LPBYTE) &advertise, &dwSize ); + } + + if ( advertise && mDNSIsFileAndPrintSharingEnabled( &retry ) ) + { + dlog( kDebugLevelTrace, DEBUG_NAME "Sharing is enabled\n" ); + + res = NetShareEnum( NULL, 1, ( LPBYTE* )&bufPtr, MAX_PREFERRED_LENGTH, &entriesRead, &totalEntries, &resume ); + + if ( ( res == ERROR_SUCCESS ) || ( res == ERROR_MORE_DATA ) ) + { + PSHARE_INFO_1 p = bufPtr; + DWORD i; + + for( i = 0; i < entriesRead; i++ ) + { + // We are only interested if the user is sharing anything other + // than the built-in "print$" source + + if ( ( p->shi1_type == STYPE_DISKTREE ) && ( wcscmp( p->shi1_netname, TEXT( "print$" ) ) != 0 ) ) + { + fileSharing = mDNStrue; + } + else if ( p->shi1_type == STYPE_PRINTQ ) + { + printSharing = mDNStrue; + } + + p++; + } + + NetApiBufferFree( bufPtr ); + bufPtr = NULL; + retry = FALSE; + } + else if ( res == NERR_ServerNotStarted ) + { + retry = TRUE; + } + } + + if ( retry ) + { + __int64 qwTimeout; + LARGE_INTEGER liTimeout; + + qwTimeout = -m->p->checkFileSharesTimeout * 10000000; + liTimeout.LowPart = ( DWORD )( qwTimeout & 0xFFFFFFFF ); + liTimeout.HighPart = ( LONG )( qwTimeout >> 32 ); + + SetWaitableTimer( m->p->checkFileSharesTimer, &liTimeout, 0, CheckFileSharesProc, m, FALSE ); + } + + if ( !m->p->smbFileSharing && fileSharing ) + { + if ( !gSMBThread ) + { + if ( !gDNSSDLibrary ) + { + gDNSSDLibrary = LoadLibrary( TEXT( "dnssd.dll" ) ); + require_action( gDNSSDLibrary, exit, err = GetLastError() ); + } + + if ( !gDNSServiceRegister ) + { + gDNSServiceRegister = ( DNSServiceRegisterFunc ) GetProcAddress( gDNSSDLibrary, "DNSServiceRegister" ); + require_action( gDNSServiceRegister, exit, err = GetLastError() ); + } + + if ( !gDNSServiceRefDeallocate ) + { + gDNSServiceRefDeallocate = ( DNSServiceRefDeallocateFunc ) GetProcAddress( gDNSSDLibrary, "DNSServiceRefDeallocate" ); + require_action( gDNSServiceRefDeallocate, exit, err = GetLastError() ); + } + + if ( !gSMBThreadRegisterEvent ) + { + gSMBThreadRegisterEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + require_action( gSMBThreadRegisterEvent != NULL, exit, err = GetLastError() ); + } + + if ( !gSMBThreadDeregisterEvent ) + { + gSMBThreadDeregisterEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + require_action( gSMBThreadDeregisterEvent != NULL, exit, err = GetLastError() ); + } + + if ( !gSMBThreadStopEvent ) + { + gSMBThreadStopEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + require_action( gSMBThreadStopEvent != NULL, exit, err = GetLastError() ); + } + + if ( !gSMBThreadQuitEvent ) + { + gSMBThreadQuitEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + require_action( gSMBThreadQuitEvent != NULL, exit, err = GetLastError() ); + } + + gSMBThread = ( HANDLE ) _beginthreadex( NULL, 0, SMBRegistrationThread, m, 0, NULL ); + require_action( gSMBThread != NULL, exit, err = GetLastError() ); + } + + SetEvent( gSMBThreadRegisterEvent ); + + m->p->smbFileSharing = mDNStrue; + } + else if ( m->p->smbFileSharing && !fileSharing ) + { + dlog( kDebugLevelTrace, DEBUG_NAME "deregistering smb type\n" ); + + if ( gSMBThreadDeregisterEvent != NULL ) + { + SetEvent( gSMBThreadDeregisterEvent ); + } + + m->p->smbFileSharing = mDNSfalse; + } + +exit: + + if ( key ) + { + RegCloseKey( key ); + } +} + + +BOOL +IsWOMPEnabled( mDNS * const m ) +{ + BOOL enabled; + + mDNSInterfaceData * ifd; + + enabled = FALSE; + + for( ifd = m->p->interfaceList; ifd; ifd = ifd->next ) + { + if ( IsWOMPEnabledForAdapter( ifd->name ) ) + { + enabled = TRUE; + break; + } + } + + return enabled; +} + + +mDNSlocal mDNSu8 +IsWOMPEnabledForAdapter( const char * adapterName ) +{ + char fileName[80]; + NDIS_OID oid; + DWORD count; + HANDLE handle = INVALID_HANDLE_VALUE; + NDIS_PNP_CAPABILITIES * pNPC = NULL; + int err; + mDNSu8 ok = TRUE; + + require_action( adapterName != NULL, exit, ok = FALSE ); + + dlog( kDebugLevelTrace, DEBUG_NAME "IsWOMPEnabledForAdapter: %s\n", adapterName ); + + // Construct a device name to pass to CreateFile + + strncpy_s( fileName, sizeof( fileName ), DEVICE_PREFIX, strlen( DEVICE_PREFIX ) ); + strcat_s( fileName, sizeof( fileName ), adapterName ); + handle = CreateFileA( fileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, INVALID_HANDLE_VALUE ); + require_action ( handle != INVALID_HANDLE_VALUE, exit, ok = FALSE ); + + // We successfully opened the driver, format the IOCTL to pass the driver. + + oid = OID_PNP_CAPABILITIES; + pNPC = ( NDIS_PNP_CAPABILITIES * ) malloc( sizeof( NDIS_PNP_CAPABILITIES ) ); + require_action( pNPC != NULL, exit, ok = FALSE ); + ok = ( mDNSu8 ) DeviceIoControl( handle, IOCTL_NDIS_QUERY_GLOBAL_STATS, &oid, sizeof( oid ), pNPC, sizeof( NDIS_PNP_CAPABILITIES ), &count, NULL ); + err = translate_errno( ok, GetLastError(), kUnknownErr ); + require_action( !err, exit, ok = FALSE ); + ok = ( mDNSu8 ) ( ( count == sizeof( NDIS_PNP_CAPABILITIES ) ) && ( pNPC->Flags & NDIS_DEVICE_WAKE_ON_MAGIC_PACKET_ENABLE ) ); + +exit: + + if ( pNPC != NULL ) + { + free( pNPC ); + } + + if ( handle != INVALID_HANDLE_VALUE ) + { + CloseHandle( handle ); + } + + dlog( kDebugLevelTrace, DEBUG_NAME "IsWOMPEnabledForAdapter returns %s\n", ok ? "true" : "false" ); + + return ( mDNSu8 ) ok; +} + + +mDNSlocal void +SendWakeupPacket( mDNS * const inMDNS, LPSOCKADDR addr, INT addrlen, const char * buf, INT buflen, INT numTries, INT msecSleep ) +{ + mDNSBool repeat = ( numTries == 1 ) ? mDNStrue : mDNSfalse; + SOCKET sock; + int num; + mStatus err; + + ( void ) inMDNS; + + sock = socket( addr->sa_family, SOCK_DGRAM, IPPROTO_UDP ); + require_action( sock != INVALID_SOCKET, exit, err = mStatus_UnknownErr ); + + while ( numTries-- ) + { + num = sendto( sock, ( const char* ) buf, buflen, 0, addr, addrlen ); + + if ( num != buflen ) + { + LogMsg( "SendWakeupPacket error: sent %d bytes: %d\n", num, WSAGetLastError() ); + } + + if ( repeat ) + { + num = sendto( sock, buf, buflen, 0, addr, addrlen ); + + if ( num != buflen ) + { + LogMsg( "SendWakeupPacket error: sent %d bytes: %d\n", num, WSAGetLastError() ); + } + } + + if ( msecSleep ) + { + Sleep( msecSleep ); + } + } + +exit: + + if ( sock != INVALID_SOCKET ) + { + closesocket( sock ); + } +} + + +mDNSlocal void _cdecl +SendMulticastWakeupPacket( void *arg ) +{ + MulticastWakeupStruct *info = ( MulticastWakeupStruct* ) arg; + + if ( info ) + { + SendWakeupPacket( info->inMDNS, ( LPSOCKADDR ) &info->addr, sizeof( info->addr ), ( const char* ) info->data, sizeof( info->data ), info->numTries, info->msecSleep ); + free( info ); + } + + _endthread(); +} + + +mDNSexport void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result) +{ + DEBUG_UNUSED( m ); + DEBUG_UNUSED( rr ); + DEBUG_UNUSED( result ); +} diff --git a/src/tools/mdnssd/mDNSWin32.h b/src/tools/mdnssd/mDNSWin32.h new file mode 100644 index 00000000000..6b5b4356648 --- /dev/null +++ b/src/tools/mdnssd/mDNSWin32.h @@ -0,0 +1,163 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MDNS_WIN32__ +#define __MDNS_WIN32__ + +#include "CommonServices.h" + +#if( !defined( _WIN32_WCE ) ) + #include +#endif + +#include "mDNSEmbeddedAPI.h" +#include "uDNS.h" + +#ifdef __cplusplus + extern "C" { +#endif + + +typedef void ( *TCPUserCallback )(); + +struct TCPSocket_struct +{ + TCPSocketFlags flags; // MUST BE FIRST FIELD -- mDNSCore expects every TCPSocket_struct to begin with TCPSocketFlags flags + SOCKET fd; + BOOL connected; + TCPUserCallback userCallback; + void * userContext; + BOOL closed; + mDNS * m; +}; + + +struct UDPSocket_struct +{ + mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port + mDNSAddr addr; // This is initialized by our code. If we don't get the + // dstAddr from WSARecvMsg we use this value instead. + SOCKET fd; + LPFN_WSARECVMSG recvMsgPtr; + DNSMessage packet; + struct mDNSInterfaceData *ifd; + UDPSocket *next; + mDNS *m; +}; + + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @struct mDNSInterfaceData + + @abstract Structure containing interface-specific data. +*/ + +typedef struct mDNSInterfaceData mDNSInterfaceData; +struct mDNSInterfaceData +{ + char name[ 128 ]; + uint32_t index; + uint32_t scopeID; + struct UDPSocket_struct sock; + NetworkInterfaceInfo interfaceInfo; + mDNSBool hostRegistered; + mDNSInterfaceData * next; +}; + + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef ReportStatusFunc +*/ +typedef void (*ReportStatusFunc)(int inType, const char *inFormat, ...); + + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @struct mDNS_PlatformSupport_struct + + @abstract Structure containing platform-specific data. +*/ + +struct mDNS_PlatformSupport_struct +{ + HANDLE mainThread; + HANDLE checkFileSharesTimer; + mDNSs32 checkFileSharesTimeout; + ReportStatusFunc reportStatusFunc; + time_t nextDHCPLeaseExpires; + char nbname[ 32 ]; + char nbdomain[ 32 ]; + mDNSBool smbFileSharing; + mDNSBool smbPrintSharing; + ServiceRecordSet smbSRS; + AuthRecord smbSubTypes[ 2 ]; + mDNSBool registeredLoopback4; + int interfaceCount; + mDNSInterfaceData * interfaceList; + mDNSInterfaceData * inactiveInterfaceList; + struct UDPSocket_struct unicastSock4; + struct UDPSocket_struct unicastSock6; + DWORD osMajorVersion; + DWORD osMinorVersion; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @struct ifaddrs + + @abstract Interface information +*/ + +struct ifaddrs +{ + struct ifaddrs * ifa_next; + char * ifa_name; + u_int ifa_flags; + struct sockaddr * ifa_addr; + struct sockaddr * ifa_netmask; + struct sockaddr * ifa_broadaddr; + struct sockaddr * ifa_dstaddr; + BYTE ifa_physaddr[6]; + BOOL ifa_dhcpEnabled; + time_t ifa_dhcpLeaseExpires; + mDNSu8 ifa_womp; + void * ifa_data; + + struct + { + uint32_t index; + + } ifa_extra; +}; + + +extern void InterfaceListDidChange( mDNS * const inMDNS ); +extern void ComputerDescriptionDidChange( mDNS * const inMDNS ); +extern void TCPIPConfigDidChange( mDNS * const inMDNS ); +extern void DynDNSConfigDidChange( mDNS * const inMDNS ); +extern void FileSharingDidChange( mDNS * const inMDNS ); +extern void FirewallDidChange( mDNS * const inMDNS ); +extern mStatus TCPAddSocket( mDNS * const inMDNS, TCPSocket *sock ); +extern mStatus SetupInterfaceList( mDNS * const inMDNS ); +extern mStatus TearDownInterfaceList( mDNS * const inMDNS ); +extern BOOL IsWOMPEnabled(); +extern void DispatchSocketEvents( mDNS * const inMDNS ); + + +#ifdef __cplusplus + } +#endif + +#endif // __MDNS_WIN32__ diff --git a/src/tools/mdnssd/main.c b/src/tools/mdnssd/main.c new file mode 100644 index 00000000000..e83d01dff5c --- /dev/null +++ b/src/tools/mdnssd/main.c @@ -0,0 +1,38 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Service.h" +#include "Shellapi.h" + +//=========================================================================================================================== +// main +//=========================================================================================================================== +int APIENTRY wWinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPTSTR lpCmdLine, + int nCmdShow) +{ + LPWSTR *argv; + int argc, res; + + argv = CommandLineToArgvW(lpCmdLine, &argc); + if (argv == NULL) + argc = 0; + res = Main( argc, argv ); + LocalFree(argv); + return res; +} diff --git a/src/tools/mdnssd/mdnssd.pro b/src/tools/mdnssd/mdnssd.pro index 2272a5bf617..aaef6adba21 100644 --- a/src/tools/mdnssd/mdnssd.pro +++ b/src/tools/mdnssd/mdnssd.pro @@ -1,19 +1,12 @@ -#------------------------------------------------- -# -# Project created by QtCreator 2011-10-14T10:22:27 -# -#------------------------------------------------- - QT -= gui -QT += core - +TEST = 0 +include(../../../qtcreator.pri) +CONFIG -= console testlib TEST TARGET = mdnssd -CONFIG += console CONFIG -= app_bundle TEMPLATE = app -include(../../../qtcreator.pri) DESTDIR = $$IDE_BIN_PATH DEFINES += PID_FILE=\\\"/var/run/mdnsd.pid\\\" MDNS_UDS_SERVERPATH=\\\"/var/run/mdnsd\\\" MDNS_DEBUGMSGS=0 @@ -21,10 +14,6 @@ DEFINES += PID_FILE=\\\"/var/run/mdnsd.pid\\\" MDNS_UDS_SERVERPATH=\\\"/var/run/ SOURCES += \ uds_daemon.c \ uDNS.c \ - PosixDaemon.c \ - PlatformCommon.c \ - mDNSUNP.c \ - mDNSPosix.c \ mDNSDebug.c \ mDNS.c \ GenLinkedList.c \ @@ -35,9 +24,7 @@ SOURCES += \ HEADERS += \ uds_daemon.h \ uDNS.h \ - PlatformCommon.h \ mDNSUNP.h \ - mDNSPosix.h \ mDNSEmbeddedAPI.h \ mDNSDebug.h \ GenLinkedList.h \ @@ -46,14 +33,70 @@ HEADERS += \ DebugServices.h \ dns_sd.h +linux-* { +SOURCES += mDNSPosix.c \ + PlatformCommon.c \ + PosixDaemon.c \ + mDNSUNP.c + +HEADERS +=\ + PlatformCommon.h \ + mDNSPosix.h +} + *-g++ { QMAKE_CFLAGS += -Wno-unused-but-set-variable QMAKE_CXXFLAGS += -Wno-unused-but-set-variable } + linux-* { DEFINES += _GNU_SOURCE HAVE_IPV6 NOT_HAVE_SA_LEN USES_NETLINK HAVE_LINUX TARGET_OS_LINUX } + macx { DEFINES += HAVE_IPV6 __MAC_OS_X_VERSION_MIN_REQUIRED=__MAC_OS_X_VERSION_10_4 __APPLE_USE_RFC_2292 } +win32 { + HEADERS += \ + CommonServices.h \ + DebugServices.h \ + Firewall.h \ + mDNSWin32.h \ + Poll.h \ + resource.h \ + Secret.h \ + Service.h \ + RegNames.h + + SOURCES += \ + DebugServices.c \ + Firewall.cpp \ + LegacyNATTraversal.c \ + main.c \ + mDNSWin32.c \ + Poll.c \ + Secret.c \ + Service.c + + RC_FILE = Service.rc + + MC_FILES += \ + EventLog.mc + + OTHER_FILES += \ + $$MC_FILES \ + Service.rc + + DEFINES += HAVE_IPV6 _WIN32_WINNT=0x0501 NDEBUG MDNS_DEBUGMSGS=0 TARGET_OS_WIN32 WIN32_LEAN_AND_MEAN USE_TCP_LOOPBACK PLATFORM_NO_STRSEP PLATFORM_NO_EPIPE PLATFORM_NO_RLIMIT UNICODE _UNICODE _CRT_SECURE_NO_DEPRECATE _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1 _LEGACY_NAT_TRAVERSAL_ _USE_32BIT_TIME_T + LIBS += ws2_32.lib advapi32.lib ole32.lib oleaut32.lib iphlpapi.lib netapi32.lib user32.lib powrprof.lib shell32.lib + + mc.output = ${QMAKE_FILE_BASE}.h + mc.commands = mc ${QMAKE_FILE_NAME} + mc.input = MC_FILES + mc.CONFIG += no_link target_predeps explicit_dependencies + QMAKE_EXTRA_COMPILERS += mc +} + +target.path=/bin +INSTALLS+=target diff --git a/src/tools/mdnssd/resource.h b/src/tools/mdnssd/resource.h new file mode 100644 index 00000000000..d968af909df --- /dev/null +++ b/src/tools/mdnssd/resource.h @@ -0,0 +1,17 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Service.rc +// + +#define IDS_SERVICE_DESCRIPTION 100 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/tools/mdnssd/uDNS.c b/src/tools/mdnssd/uDNS.c index 57cfc1a689e..a36338c9064 100755 --- a/src/tools/mdnssd/uDNS.c +++ b/src/tools/mdnssd/uDNS.c @@ -101,7 +101,7 @@ mDNSlocal void SetRecordRetry(mDNS *const m, AuthRecord *rr, mDNSu32 random) #pragma mark - Name Server List Management #endif -mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout, mDNSBool cellIntf) +mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface1, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout, mDNSBool cellIntf) { DNSServer **p = &m->DNSServers; DNSServer *tmp = mDNSNULL; @@ -114,16 +114,16 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons if (!d) d = (const domainname *)""; - LogInfo("mDNS_AddDNSServer: Adding %#a for %##s, InterfaceID %p, scoped %d", addr, d->c, interface, scoped); + LogInfo("mDNS_AddDNSServer: Adding %#a for %##s, InterfaceID %p, scoped %d", addr, d->c, interface1, scoped); if (m->mDNS_busy != m->mDNS_reentrancy+1) LogMsg("mDNS_AddDNSServer: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); while (*p) // Check if we already have this {interface,address,port,domain} tuple registered { - if ((*p)->scoped == scoped && (*p)->interface == interface && (*p)->teststate != DNSServer_Disabled && + if ((*p)->scoped == scoped && (*p)->interface1 == interface1 && (*p)->teststate != DNSServer_Disabled && mDNSSameAddress(&(*p)->addr, addr) && mDNSSameIPPort((*p)->port, port) && SameDomainName(&(*p)->domain, d)) { - if (!((*p)->flags & DNSServer_FlagDelete)) debugf("Note: DNS Server %#a:%d for domain %##s (%p) registered more than once", addr, mDNSVal16(port), d->c, interface); + if (!((*p)->flags & DNSServer_FlagDelete)) debugf("Note: DNS Server %#a:%d for domain %##s (%p) registered more than once", addr, mDNSVal16(port), d->c, interface1); (*p)->flags &= ~DNSServer_FlagDelete; tmp = *p; *p = tmp->next; @@ -143,7 +143,7 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons { NumUnicastDNSServers++; (*p)->scoped = scoped; - (*p)->interface = interface; + (*p)->interface1 = interface1; (*p)->addr = *addr; (*p)->port = port; (*p)->flags = DNSServer_FlagNew; @@ -4215,7 +4215,7 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort), q->unansweredQueries); if (!q->LocalSocket) q->LocalSocket = mDNSPlatformUDPSocket(m, zeroIPPort); if (!q->LocalSocket) err = mStatus_NoMemoryErr; // If failed to make socket (should be very rare), we'll try again next time - else err = mDNSSendDNSMessage(m, &m->omsg, end, q->qDNSServer->interface, q->LocalSocket, &q->qDNSServer->addr, q->qDNSServer->port, mDNSNULL, mDNSNULL); + else err = mDNSSendDNSMessage(m, &m->omsg, end, q->qDNSServer->interface1, q->LocalSocket, &q->qDNSServer->addr, q->qDNSServer->port, mDNSNULL, mDNSNULL); } } diff --git a/src/tools/mdnssd/uds_daemon.c b/src/tools/mdnssd/uds_daemon.c index c962173498e..d2c2a24efb6 100644 --- a/src/tools/mdnssd/uds_daemon.c +++ b/src/tools/mdnssd/uds_daemon.c @@ -1,13 +1,13 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2003-2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2003-2011 Apple Computer, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,7 +15,8 @@ * limitations under the License. */ -#if defined(_WIN32) +#if defined(WIN32) +#include #include #define usleep(X) Sleep(((X)+999)/1000) #else @@ -76,166 +77,166 @@ int WCFNameResolvesToName(WCFConnection *conn, char* fromName, char* toName, uid #endif typedef enum - { - t_uninitialized, - t_morecoming, - t_complete, - t_error, - t_terminated - } transfer_state; + { + t_uninitialized, + t_morecoming, + t_complete, + t_error, + t_terminated + } transfer_state; typedef struct request_state request_state; typedef void (*req_termination_fn)(request_state *request); typedef struct registered_record_entry - { - struct registered_record_entry *next; - mDNSu32 key; - client_context_t regrec_client_context; - request_state *request; - mDNSBool external_advertise; - mDNSInterfaceID origInterfaceID; - AuthRecord *rr; // Pointer to variable-sized AuthRecord (Why a pointer? Why not just embed it here?) - } registered_record_entry; + { + struct registered_record_entry *next; + mDNSu32 key; + client_context_t regrec_client_context; + request_state *request; + mDNSBool external_advertise; + mDNSInterfaceID origInterfaceID; + AuthRecord *rr; // Pointer to variable-sized AuthRecord (Why a pointer? Why not just embed it here?) + } registered_record_entry; // A single registered service: ServiceRecordSet + bookkeeping // Note that we duplicate some fields from parent service_info object // to facilitate cleanup, when instances and parent may be deallocated at different times. typedef struct service_instance - { - struct service_instance *next; - request_state *request; - AuthRecord *subtypes; - mDNSBool renameonmemfree; // Set on config change when we deregister original name + { + struct service_instance *next; + request_state *request; + AuthRecord *subtypes; + mDNSBool renameonmemfree; // Set on config change when we deregister original name mDNSBool clientnotified; // Has client been notified of successful registration yet? - mDNSBool default_local; // is this the "local." from an empty-string registration? - mDNSBool external_advertise; // is this is being advertised externally? - domainname domain; - ServiceRecordSet srs; // note -- variable-sized object -- must be last field in struct - } service_instance; + mDNSBool default_local; // is this the "local." from an empty-string registration? + mDNSBool external_advertise; // is this is being advertised externally? + domainname domain; + ServiceRecordSet srs; // note -- variable-sized object -- must be last field in struct + } service_instance; // for multi-domain default browsing typedef struct browser_t - { - struct browser_t *next; - domainname domain; - DNSQuestion q; - } browser_t; + { + struct browser_t *next; + domainname domain; + DNSQuestion q; + } browser_t; struct request_state - { - request_state *next; - request_state *primary; // If this operation is on a shared socket, pointer to primary - // request_state for the original DNSServiceCreateConnection() operation - dnssd_sock_t sd; - dnssd_sock_t errsd; - mDNSu32 uid; - void * platform_data; + { + request_state *next; + request_state *primary; // If this operation is on a shared socket, pointer to primary + // request_state for the original DNSServiceCreateConnection() operation + dnssd_sock_t sd; + dnssd_sock_t errsd; + mDNSu32 uid; + void * platform_data; - // Note: On a shared connection these fields in the primary structure, including hdr, are re-used - // for each new request. This is because, until we've read the ipc_msg_hdr to find out what the - // operation is, we don't know if we're going to need to allocate a new request_state or not. - transfer_state ts; - mDNSu32 hdr_bytes; // bytes of header already read - ipc_msg_hdr hdr; - mDNSu32 data_bytes; // bytes of message data already read - char *msgbuf; // pointer to data storage to pass to free() - const char *msgptr; // pointer to data to be read from (may be modified) - char *msgend; // pointer to byte after last byte of message + // Note: On a shared connection these fields in the primary structure, including hdr, are re-used + // for each new request. This is because, until we've read the ipc_msg_hdr to find out what the + // operation is, we don't know if we're going to need to allocate a new request_state or not. + transfer_state ts; + mDNSu32 hdr_bytes; // bytes of header already read + ipc_msg_hdr hdr; + mDNSu32 data_bytes; // bytes of message data already read + char *msgbuf; // pointer to data storage to pass to free() + const char *msgptr; // pointer to data to be read from (may be modified) + char *msgend; // pointer to byte after last byte of message - // reply, termination, error, and client context info - int no_reply; // don't send asynchronous replies to client - mDNSs32 time_blocked; // record time of a blocked client - int unresponsiveness_reports; - struct reply_state *replies; // corresponding (active) reply list - req_termination_fn terminate; - DNSServiceFlags flags; + // reply, termination, error, and client context info + int no_reply; // don't send asynchronous replies to client + mDNSs32 time_blocked; // record time of a blocked client + int unresponsiveness_reports; + struct reply_state *replies; // corresponding (active) reply list + req_termination_fn terminate; + DNSServiceFlags flags; - union - { - registered_record_entry *reg_recs; // list of registrations for a connection-oriented request - struct - { - mDNSInterfaceID interface_id; - mDNSBool default_domain; - mDNSBool ForceMCast; - domainname regtype; - browser_t *browsers; - } browser; - struct - { - mDNSInterfaceID InterfaceID; - mDNSu16 txtlen; - void *txtdata; - mDNSIPPort port; - domainlabel name; - char type_as_string[MAX_ESCAPED_DOMAIN_NAME]; - domainname type; - mDNSBool default_domain; - domainname host; - mDNSBool autoname; // Set if this name is tied to the Computer Name - mDNSBool autorename; // Set if this client wants us to automatically rename on conflict - mDNSBool allowremotequery; // Respond to unicast queries from outside the local link? - int num_subtypes; - service_instance *instances; - } servicereg; - struct - { - mDNSInterfaceID interface_id; - mDNSu32 flags; - mDNSu32 protocol; - DNSQuestion q4; - DNSQuestion *q42; - DNSQuestion q6; - DNSQuestion *q62; - } addrinfo; - struct - { - mDNSIPPort ReqExt; // External port we originally requested, for logging purposes - NATTraversalInfo NATinfo; - } pm; - struct - { + union + { + registered_record_entry *reg_recs; // list of registrations for a connection-oriented request + struct + { + mDNSInterfaceID interface_id; + mDNSBool default_domain; + mDNSBool ForceMCast; + domainname regtype; + browser_t *browsers; + } browser; + struct + { + mDNSInterfaceID InterfaceID; + mDNSu16 txtlen; + void *txtdata; + mDNSIPPort port; + domainlabel name; + char type_as_string[MAX_ESCAPED_DOMAIN_NAME]; + domainname type; + mDNSBool default_domain; + domainname host; + mDNSBool autoname; // Set if this name is tied to the Computer Name + mDNSBool autorename; // Set if this client wants us to automatically rename on conflict + mDNSBool allowremotequery; // Respond to unicast queries from outside the local link? + int num_subtypes; + service_instance *instances; + } servicereg; + struct + { + mDNSInterfaceID interface_id; + mDNSu32 flags; + mDNSu32 protocol; + DNSQuestion q4; + DNSQuestion *q42; + DNSQuestion q6; + DNSQuestion *q62; + } addrinfo; + struct + { + mDNSIPPort ReqExt; // External port we originally requested, for logging purposes + NATTraversalInfo NATinfo; + } pm; + struct + { #if 0 - DNSServiceFlags flags; + DNSServiceFlags flags; #endif - DNSQuestion q_all; - DNSQuestion q_default; - } enumeration; - struct - { - DNSQuestion q; - DNSQuestion *q2; - } queryrecord; - struct - { - DNSQuestion qtxt; - DNSQuestion qsrv; - const ResourceRecord *txt; - const ResourceRecord *srv; - mDNSs32 ReportTime; - mDNSBool external_advertise; - } resolve; - } u; - }; + DNSQuestion q_all; + DNSQuestion q_default; + } enumeration; + struct + { + DNSQuestion q; + DNSQuestion *q2; + } queryrecord; + struct + { + DNSQuestion qtxt; + DNSQuestion qsrv; + const ResourceRecord *txt; + const ResourceRecord *srv; + mDNSs32 ReportTime; + mDNSBool external_advertise; + } resolve; + } u; + }; // struct physically sits between ipc message header and call-specific fields in the message buffer typedef struct - { - DNSServiceFlags flags; // Note: This field is in NETWORK byte order - mDNSu32 ifi; // Note: This field is in NETWORK byte order - DNSServiceErrorType error; // Note: This field is in NETWORK byte order - } reply_hdr; + { + DNSServiceFlags flags; // Note: This field is in NETWORK byte order + mDNSu32 ifi; // Note: This field is in NETWORK byte order + DNSServiceErrorType error; // Note: This field is in NETWORK byte order + } reply_hdr; typedef struct reply_state - { - struct reply_state *next; // If there are multiple unsent replies - mDNSu32 totallen; - mDNSu32 nwriten; - ipc_msg_hdr mhdr[1]; - reply_hdr rhdr[1]; - } reply_state; + { + struct reply_state *next; // If there are multiple unsent replies + mDNSu32 totallen; + mDNSu32 nwriten; + ipc_msg_hdr mhdr[1]; + reply_hdr rhdr[1]; + } reply_state; // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -266,7 +267,7 @@ static ARListElem *LocalDomainEnumRecords; // List of locally-generated PTR mDNSexport DNameListElem *AutoBrowseDomains; // List created from those local-only PTR records plus records we get from the network #define MSG_PAD_BYTES 5 // pad message buffer (read from client) with n zero'd bytes to guarantee - // n get_string() calls w/o buffer overrun + // n get_string() calls w/o buffer overrun // initialization, setup/teardown functions // If a platform specifies its own PID file name, we use that @@ -281,317 +282,318 @@ mDNSexport DNameListElem *AutoBrowseDomains; // List created from those local-o #endif mDNSlocal void FatalError(char *errmsg) - { - LogMsg("%s: %s", errmsg, dnssd_strerror(dnssd_errno)); - *(long*)0 = 0; // On OS X abort() doesn't generate a crash log, but writing to zero does - abort(); // On platforms where writing to zero doesn't generate an exception, abort instead - } + { + char* ptr = NULL; + LogMsg("%s: %s", errmsg, dnssd_strerror(dnssd_errno)); + *ptr = 0; // On OS X abort() doesn't generate a crash log, but writing to zero does + abort(); // On platforms where writing to zero doesn't generate an exception, abort instead + } mDNSlocal mDNSu32 dnssd_htonl(mDNSu32 l) - { - mDNSu32 ret; - char *data = (char*) &ret; - put_uint32(l, &data); - return ret; - } + { + mDNSu32 ret; + char *data = (char*) &ret; + put_uint32(l, &data); + return ret; + } // hack to search-replace perror's to LogMsg's mDNSlocal void my_perror(char *errmsg) - { - LogMsg("%s: %d (%s)", errmsg, dnssd_errno, dnssd_strerror(dnssd_errno)); - } + { + LogMsg("%s: %d (%s)", errmsg, dnssd_errno, dnssd_strerror(dnssd_errno)); + } mDNSlocal void abort_request(request_state *req) - { - if (req->terminate == (req_termination_fn)~0) - { LogMsg("abort_request: ERROR: Attempt to abort operation %p with req->terminate %p", req, req->terminate); return; } - - // First stop whatever mDNSCore operation we were doing - // If this is actually a shared connection operation, then its req->terminate function will scan - // the all_requests list and terminate any subbordinate operations sharing this file descriptor - if (req->terminate) req->terminate(req); + { + if (req->terminate == (req_termination_fn)~0) + { LogMsg("abort_request: ERROR: Attempt to abort operation %p with req->terminate %p", req, req->terminate); return; } - if (!dnssd_SocketValid(req->sd)) - { LogMsg("abort_request: ERROR: Attempt to abort operation %p with invalid fd %d", req, req->sd); return; } - - // Now, if this request_state is not subordinate to some other primary, close file descriptor and discard replies - if (!req->primary) - { - if (req->errsd != req->sd) LogOperation("%3d: Removing FD and closing errsd %d", req->sd, req->errsd); - else LogOperation("%3d: Removing FD", req->sd); - udsSupportRemoveFDFromEventLoop(req->sd, req->platform_data); // Note: This also closes file descriptor req->sd for us - if (req->errsd != req->sd) { dnssd_close(req->errsd); req->errsd = req->sd; } + // First stop whatever mDNSCore operation we were doing + // If this is actually a shared connection operation, then its req->terminate function will scan + // the all_requests list and terminate any subbordinate operations sharing this file descriptor + if (req->terminate) req->terminate(req); - while (req->replies) // free pending replies - { - reply_state *ptr = req->replies; - req->replies = req->replies->next; - freeL("reply_state (abort)", ptr); - } - } + if (!dnssd_SocketValid(req->sd)) + { LogMsg("abort_request: ERROR: Attempt to abort operation %p with invalid fd %d", req, req->sd); return; } - // Set req->sd to something invalid, so that udsserver_idle knows to unlink and free this structure + // Now, if this request_state is not subordinate to some other primary, close file descriptor and discard replies + if (!req->primary) + { + if (req->errsd != req->sd) LogOperation("%3d: Removing FD and closing errsd %d", req->sd, req->errsd); + else LogOperation("%3d: Removing FD", req->sd); + udsSupportRemoveFDFromEventLoop(req->sd, req->platform_data); // Note: This also closes file descriptor req->sd for us + if (req->errsd != req->sd) { dnssd_close(req->errsd); req->errsd = req->sd; } + + while (req->replies) // free pending replies + { + reply_state *ptr = req->replies; + req->replies = req->replies->next; + freeL("reply_state (abort)", ptr); + } + } + + // Set req->sd to something invalid, so that udsserver_idle knows to unlink and free this structure #if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING - // Don't use dnssd_InvalidSocket (-1) because that's the sentinel value MACOSX_MDNS_MALLOC_DEBUGGING uses - // for detecting when the memory for an object is inadvertently freed while the object is still on some list - req->sd = req->errsd = -2; + // Don't use dnssd_InvalidSocket (-1) because that's the sentinel value MACOSX_MDNS_MALLOC_DEBUGGING uses + // for detecting when the memory for an object is inadvertently freed while the object is still on some list + req->sd = req->errsd = -2; #else - req->sd = req->errsd = dnssd_InvalidSocket; + req->sd = req->errsd = dnssd_InvalidSocket; #endif - // We also set req->terminate to a bogus value so we know if abort_request() gets called again for this request - req->terminate = (req_termination_fn)~0; - } + // We also set req->terminate to a bogus value so we know if abort_request() gets called again for this request + req->terminate = (req_termination_fn)~0; + } mDNSlocal void AbortUnlinkAndFree(request_state *req) - { - request_state **p = &all_requests; - abort_request(req); - while (*p && *p != req) p=&(*p)->next; - if (*p) { *p = req->next; freeL("request_state/AbortUnlinkAndFree", req); } - else LogMsg("AbortUnlinkAndFree: ERROR: Attempt to abort operation %p not in list", req); - } + { + request_state **p = &all_requests; + abort_request(req); + while (*p && *p != req) p=&(*p)->next; + if (*p) { *p = req->next; freeL("request_state/AbortUnlinkAndFree", req); } + else LogMsg("AbortUnlinkAndFree: ERROR: Attempt to abort operation %p not in list", req); + } mDNSlocal reply_state *create_reply(const reply_op_t op, const size_t datalen, request_state *const request) - { - reply_state *reply; + { + reply_state *reply; - if ((unsigned)datalen < sizeof(reply_hdr)) - { - LogMsg("ERROR: create_reply - data length less than length of required fields"); - return NULL; - } + if ((unsigned)datalen < sizeof(reply_hdr)) + { + LogMsg("ERROR: create_reply - data length less than length of required fields"); + return NULL; + } - reply = mallocL("reply_state", sizeof(reply_state) + datalen - sizeof(reply_hdr)); - if (!reply) FatalError("ERROR: malloc"); - - reply->next = mDNSNULL; - reply->totallen = (mDNSu32)datalen + sizeof(ipc_msg_hdr); - reply->nwriten = 0; + reply = mallocL("reply_state", sizeof(reply_state) + datalen - sizeof(reply_hdr)); + if (!reply) FatalError("ERROR: malloc"); - reply->mhdr->version = VERSION; - reply->mhdr->datalen = (mDNSu32)datalen; - reply->mhdr->ipc_flags = 0; - reply->mhdr->op = op; - reply->mhdr->client_context = request->hdr.client_context; - reply->mhdr->reg_index = 0; + reply->next = mDNSNULL; + reply->totallen = (mDNSu32)datalen + sizeof(ipc_msg_hdr); + reply->nwriten = 0; - return reply; - } + reply->mhdr->version = VERSION; + reply->mhdr->datalen = (mDNSu32)datalen; + reply->mhdr->ipc_flags = 0; + reply->mhdr->op = op; + reply->mhdr->client_context = request->hdr.client_context; + reply->mhdr->reg_index = 0; + + return reply; + } // Append a reply to the list in a request object // If our request is sharing a connection, then we append our reply_state onto the primary's list mDNSlocal void append_reply(request_state *req, reply_state *rep) - { - request_state *r = req->primary ? req->primary : req; - reply_state **ptr = &r->replies; - while (*ptr) ptr = &(*ptr)->next; - *ptr = rep; - rep->next = NULL; - } + { + request_state *r = req->primary ? req->primary : req; + reply_state **ptr = &r->replies; + while (*ptr) ptr = &(*ptr)->next; + *ptr = rep; + rep->next = NULL; + } // Generates a response message giving name, type, domain, plus interface index, // suitable for a browse result or service registration result. // On successful completion rep is set to point to a malloc'd reply_state struct mDNSlocal mStatus GenerateNTDResponse(const domainname *const servicename, const mDNSInterfaceID id, - request_state *const request, reply_state **const rep, reply_op_t op, DNSServiceFlags flags, mStatus err) - { - domainlabel name; - domainname type, dom; - *rep = NULL; - if (!DeconstructServiceName(servicename, &name, &type, &dom)) - return kDNSServiceErr_Invalid; - else - { - char namestr[MAX_DOMAIN_LABEL+1]; - char typestr[MAX_ESCAPED_DOMAIN_NAME]; - char domstr [MAX_ESCAPED_DOMAIN_NAME]; - int len; - char *data; + request_state *const request, reply_state **const rep, reply_op_t op, DNSServiceFlags flags, mStatus err) + { + domainlabel name; + domainname type, dom; + *rep = NULL; + if (!DeconstructServiceName(servicename, &name, &type, &dom)) + return kDNSServiceErr_Invalid; + else + { + char namestr[MAX_DOMAIN_LABEL+1]; + char typestr[MAX_ESCAPED_DOMAIN_NAME]; + char domstr [MAX_ESCAPED_DOMAIN_NAME]; + int len; + char *data; - ConvertDomainLabelToCString_unescaped(&name, namestr); - ConvertDomainNameToCString(&type, typestr); - ConvertDomainNameToCString(&dom, domstr); + ConvertDomainLabelToCString_unescaped(&name, namestr); + ConvertDomainNameToCString(&type, typestr); + ConvertDomainNameToCString(&dom, domstr); - // Calculate reply data length - len = sizeof(DNSServiceFlags); - len += sizeof(mDNSu32); // if index - len += sizeof(DNSServiceErrorType); - len += (int) (strlen(namestr) + 1); - len += (int) (strlen(typestr) + 1); - len += (int) (strlen(domstr) + 1); + // Calculate reply data length + len = sizeof(DNSServiceFlags); + len += sizeof(mDNSu32); // if index + len += sizeof(DNSServiceErrorType); + len += (int) (strlen(namestr) + 1); + len += (int) (strlen(typestr) + 1); + len += (int) (strlen(domstr) + 1); - // Build reply header - *rep = create_reply(op, len, request); - (*rep)->rhdr->flags = dnssd_htonl(flags); - (*rep)->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id, mDNSfalse)); - (*rep)->rhdr->error = dnssd_htonl(err); + // Build reply header + *rep = create_reply(op, len, request); + (*rep)->rhdr->flags = dnssd_htonl(flags); + (*rep)->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id, mDNSfalse)); + (*rep)->rhdr->error = dnssd_htonl(err); - // Build reply body - data = (char *)&(*rep)->rhdr[1]; - put_string(namestr, &data); - put_string(typestr, &data); - put_string(domstr, &data); + // Build reply body + data = (char *)&(*rep)->rhdr[1]; + put_string(namestr, &data); + put_string(typestr, &data); + put_string(domstr, &data); - return mStatus_NoError; - } - } + return mStatus_NoError; + } + } // Special support to enable the DNSServiceBrowse call made by Bonjour Browser // Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse mDNSlocal void GenerateBonjourBrowserResponse(const domainname *const servicename, const mDNSInterfaceID id, - request_state *const request, reply_state **const rep, reply_op_t op, DNSServiceFlags flags, mStatus err) - { - char namestr[MAX_DOMAIN_LABEL+1]; - char typestr[MAX_ESCAPED_DOMAIN_NAME]; - static const char domstr[] = "."; - int len; - char *data; + request_state *const request, reply_state **const rep, reply_op_t op, DNSServiceFlags flags, mStatus err) + { + char namestr[MAX_DOMAIN_LABEL+1]; + char typestr[MAX_ESCAPED_DOMAIN_NAME]; + static const char domstr[] = "."; + int len; + char *data; - *rep = NULL; + *rep = NULL; - // 1. Put first label in namestr - ConvertDomainLabelToCString_unescaped((const domainlabel *)servicename, namestr); + // 1. Put first label in namestr + ConvertDomainLabelToCString_unescaped((const domainlabel *)servicename, namestr); - // 2. Put second label and "local" into typestr - mDNS_snprintf(typestr, sizeof(typestr), "%#s.local.", SecondLabel(servicename)); + // 2. Put second label and "local" into typestr + mDNS_snprintf(typestr, sizeof(typestr), "%#s.local.", SecondLabel(servicename)); - // Calculate reply data length - len = sizeof(DNSServiceFlags); - len += sizeof(mDNSu32); // if index - len += sizeof(DNSServiceErrorType); - len += (int) (strlen(namestr) + 1); - len += (int) (strlen(typestr) + 1); - len += (int) (strlen(domstr) + 1); + // Calculate reply data length + len = sizeof(DNSServiceFlags); + len += sizeof(mDNSu32); // if index + len += sizeof(DNSServiceErrorType); + len += (int) (strlen(namestr) + 1); + len += (int) (strlen(typestr) + 1); + len += (int) (strlen(domstr) + 1); - // Build reply header - *rep = create_reply(op, len, request); - (*rep)->rhdr->flags = dnssd_htonl(flags); - (*rep)->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id, mDNSfalse)); - (*rep)->rhdr->error = dnssd_htonl(err); + // Build reply header + *rep = create_reply(op, len, request); + (*rep)->rhdr->flags = dnssd_htonl(flags); + (*rep)->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id, mDNSfalse)); + (*rep)->rhdr->error = dnssd_htonl(err); - // Build reply body - data = (char *)&(*rep)->rhdr[1]; - put_string(namestr, &data); - put_string(typestr, &data); - put_string(domstr, &data); - } + // Build reply body + data = (char *)&(*rep)->rhdr[1]; + put_string(namestr, &data); + put_string(typestr, &data); + put_string(domstr, &data); + } // Returns a resource record (allocated w/ malloc) containing the data found in an IPC message // Data must be in the following format: flags, interfaceIndex, name, rrtype, rrclass, rdlen, rdata, (optional) ttl // (ttl only extracted/set if ttl argument is non-zero). Returns NULL for a bad-parameter error mDNSlocal AuthRecord *read_rr_from_ipc_msg(request_state *request, int GetTTL, int validate_flags) - { - DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); - mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); - char name[256]; - int str_err = get_string(&request->msgptr, request->msgend, name, sizeof(name)); - mDNSu16 type = get_uint16(&request->msgptr, request->msgend); - mDNSu16 class = get_uint16(&request->msgptr, request->msgend); - mDNSu16 rdlen = get_uint16(&request->msgptr, request->msgend); - const char *rdata = get_rdata (&request->msgptr, request->msgend, rdlen); - mDNSu32 ttl = GetTTL ? get_uint32(&request->msgptr, request->msgend) : 0; - int storage_size = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody); - AuthRecord *rr; - mDNSInterfaceID InterfaceID; - AuthRecType artype; + { + DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); + mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); + char name[256]; + int str_err = get_string(&request->msgptr, request->msgend, name, sizeof(name)); + mDNSu16 type = get_uint16(&request->msgptr, request->msgend); + mDNSu16 class = get_uint16(&request->msgptr, request->msgend); + mDNSu16 rdlen = get_uint16(&request->msgptr, request->msgend); + const char *rdata = get_rdata (&request->msgptr, request->msgend, rdlen); + mDNSu32 ttl = GetTTL ? get_uint32(&request->msgptr, request->msgend) : 0; + int storage_size = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody); + AuthRecord *rr; + mDNSInterfaceID InterfaceID; + AuthRecType artype; - request->flags = flags; + request->flags = flags; - if (str_err) { LogMsg("ERROR: read_rr_from_ipc_msg - get_string"); return NULL; } + if (str_err) { LogMsg("ERROR: read_rr_from_ipc_msg - get_string"); return NULL; } - if (!request->msgptr) { LogMsg("Error reading Resource Record from client"); return NULL; } + if (!request->msgptr) { LogMsg("Error reading Resource Record from client"); return NULL; } - if (validate_flags && - !((flags & kDNSServiceFlagsShared) == kDNSServiceFlagsShared) && - !((flags & kDNSServiceFlagsUnique) == kDNSServiceFlagsUnique)) - { - LogMsg("ERROR: Bad resource record flags (must be kDNSServiceFlagsShared or kDNSServiceFlagsUnique)"); - return NULL; - } + if (validate_flags && + !((flags & kDNSServiceFlagsShared) == kDNSServiceFlagsShared) && + !((flags & kDNSServiceFlagsUnique) == kDNSServiceFlagsUnique)) + { + LogMsg("ERROR: Bad resource record flags (must be kDNSServiceFlagsShared or kDNSServiceFlagsUnique)"); + return NULL; + } - rr = mallocL("AuthRecord/read_rr_from_ipc_msg", sizeof(AuthRecord) - sizeof(RDataBody) + storage_size); - if (!rr) FatalError("ERROR: malloc"); + rr = mallocL("AuthRecord/read_rr_from_ipc_msg", sizeof(AuthRecord) - sizeof(RDataBody) + storage_size); + if (!rr) FatalError("ERROR: malloc"); - InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); - if (InterfaceID == mDNSInterface_LocalOnly) - artype = AuthRecordLocalOnly; - else if (InterfaceID == mDNSInterface_P2P) - artype = AuthRecordP2P; - else if ((InterfaceID == mDNSInterface_Any) && (flags & kDNSServiceFlagsIncludeP2P)) - artype = AuthRecordAnyIncludeP2P; - else - artype = AuthRecordAny; + InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); + if (InterfaceID == mDNSInterface_LocalOnly) + artype = AuthRecordLocalOnly; + else if (InterfaceID == mDNSInterface_P2P) + artype = AuthRecordP2P; + else if ((InterfaceID == mDNSInterface_Any) && (flags & kDNSServiceFlagsIncludeP2P)) + artype = AuthRecordAnyIncludeP2P; + else + artype = AuthRecordAny; - mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, type, 0, - (mDNSu8) ((flags & kDNSServiceFlagsShared) ? kDNSRecordTypeShared : kDNSRecordTypeUnique), artype, mDNSNULL, mDNSNULL); + mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, type, 0, + (mDNSu8) ((flags & kDNSServiceFlagsShared) ? kDNSRecordTypeShared : kDNSRecordTypeUnique), artype, mDNSNULL, mDNSNULL); - if (!MakeDomainNameFromDNSNameString(&rr->namestorage, name)) - { - LogMsg("ERROR: bad name: %s", name); - freeL("AuthRecord/read_rr_from_ipc_msg", rr); - return NULL; - } + if (!MakeDomainNameFromDNSNameString(&rr->namestorage, name)) + { + LogMsg("ERROR: bad name: %s", name); + freeL("AuthRecord/read_rr_from_ipc_msg", rr); + return NULL; + } - if (flags & kDNSServiceFlagsAllowRemoteQuery) rr->AllowRemoteQuery = mDNStrue; - rr->resrec.rrclass = class; - rr->resrec.rdlength = rdlen; - rr->resrec.rdata->MaxRDLength = rdlen; - mDNSPlatformMemCopy(rr->resrec.rdata->u.data, rdata, rdlen); - if (GetTTL) rr->resrec.rroriginalttl = ttl; - rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); - SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rr->rdatahash for us - return rr; - } + if (flags & kDNSServiceFlagsAllowRemoteQuery) rr->AllowRemoteQuery = mDNStrue; + rr->resrec.rrclass = class; + rr->resrec.rdlength = rdlen; + rr->resrec.rdata->MaxRDLength = rdlen; + mDNSPlatformMemCopy(rr->resrec.rdata->u.data, rdata, rdlen); + if (GetTTL) rr->resrec.rroriginalttl = ttl; + rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); + SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rr->rdatahash for us + return rr; + } mDNSlocal int build_domainname_from_strings(domainname *srv, char *name, char *regtype, char *domain) - { - domainlabel n; - domainname d, t; + { + domainlabel n; + domainname d, t; - if (!MakeDomainLabelFromLiteralString(&n, name)) return -1; - if (!MakeDomainNameFromDNSNameString(&t, regtype)) return -1; - if (!MakeDomainNameFromDNSNameString(&d, domain)) return -1; - if (!ConstructServiceName(srv, &n, &t, &d)) return -1; - return 0; - } + if (!MakeDomainLabelFromLiteralString(&n, name)) return -1; + if (!MakeDomainNameFromDNSNameString(&t, regtype)) return -1; + if (!MakeDomainNameFromDNSNameString(&d, domain)) return -1; + if (!ConstructServiceName(srv, &n, &t, &d)) return -1; + return 0; + } mDNSlocal void send_all(dnssd_sock_t s, const char *ptr, int len) - { - int n = send(s, ptr, len, 0); - // On a freshly-created Unix Domain Socket, the kernel should *never* fail to buffer a small write for us - // (four bytes for a typical error code return, 12 bytes for DNSServiceGetProperty(DaemonVersion)). - // If it does fail, we don't attempt to handle this failure, but we do log it so we know something is wrong. - if (n < len) - LogMsg("ERROR: send_all(%d) wrote %d of %d errno %d (%s)", - s, n, len, dnssd_errno, dnssd_strerror(dnssd_errno)); - } + { + int n = send(s, ptr, len, 0); + // On a freshly-created Unix Domain Socket, the kernel should *never* fail to buffer a small write for us + // (four bytes for a typical error code return, 12 bytes for DNSServiceGetProperty(DaemonVersion)). + // If it does fail, we don't attempt to handle this failure, but we do log it so we know something is wrong. + if (n < len) + LogMsg("ERROR: send_all(%d) wrote %d of %d errno %d (%s)", + s, n, len, dnssd_errno, dnssd_strerror(dnssd_errno)); + } #if 0 mDNSlocal mDNSBool AuthorizedDomain(const request_state * const request, const domainname * const d, const DNameListElem * const doms) { - const DNameListElem *delem = mDNSNULL; - int bestDelta = -1; // the delta of the best match, lower is better - int dLabels = 0; - mDNSBool allow = mDNSfalse; - - if (SystemUID(request->uid)) return mDNStrue; - - dLabels = CountLabels(d); - for (delem = doms; delem; delem = delem->next) - { - if (delem->uid) - { - int delemLabels = CountLabels(&delem->name); - int delta = dLabels - delemLabels; - if ((bestDelta == -1 || delta <= bestDelta) && SameDomainName(&delem->name, SkipLeadingLabels(d, delta))) - { - bestDelta = delta; - allow = (allow || (delem->uid == request->uid)); - } - } - } - - return bestDelta == -1 ? mDNStrue : allow; + const DNameListElem *delem = mDNSNULL; + int bestDelta = -1; // the delta of the best match, lower is better + int dLabels = 0; + mDNSBool allow = mDNSfalse; + + if (SystemUID(request->uid)) return mDNStrue; + + dLabels = CountLabels(d); + for (delem = doms; delem; delem = delem->next) + { + if (delem->uid) + { + int delemLabels = CountLabels(&delem->name); + int delta = dLabels - delemLabels; + if ((bestDelta == -1 || delta <= bestDelta) && SameDomainName(&delem->name, SkipLeadingLabels(d, delta))) + { + bestDelta = delta; + allow = (allow || (delem->uid == request->uid)); + } + } + } + + return bestDelta == -1 ? mDNStrue : allow; } #endif @@ -602,60 +604,60 @@ mDNSlocal mDNSBool AuthorizedDomain(const request_state * const request, const d #endif mDNSlocal void external_start_advertising_helper(service_instance *const instance) - { - AuthRecord *st = instance->subtypes; - ExtraResourceRecord *e; - int i; - - if (mDNSIPPortIsZero(instance->request->u.servicereg.port)) - { - LogInfo("external_start_advertising_helper: Not registering service with port number zero"); - return; - } + { + AuthRecord *st = instance->subtypes; + ExtraResourceRecord *e; + int i; + + if (mDNSIPPortIsZero(instance->request->u.servicereg.port)) + { + LogInfo("external_start_advertising_helper: Not registering service with port number zero"); + return; + } #if APPLE_OSX_mDNSResponder - // Update packet filter if p2p interface already exists, otherwise, - // if will be updated when we get the KEV_DL_IF_ATTACHED event for - // the interface. Called here since we don't call external_start_advertising_service() - // with the SRV record when advertising a service. - mDNSInitPacketFilter(); + // Update packet filter if p2p interface already exists, otherwise, + // if will be updated when we get the KEV_DL_IF_ATTACHED event for + // the interface. Called here since we don't call external_start_advertising_service() + // with the SRV record when advertising a service. + mDNSInitPacketFilter(); #endif // APPLE_OSX_mDNSResponder - if (instance->external_advertise) LogMsg("external_start_advertising_helper: external_advertise already set!"); - - for ( i = 0; i < instance->request->u.servicereg.num_subtypes; i++) - external_start_advertising_service(&st[i].resrec); - - external_start_advertising_service(&instance->srs.RR_PTR.resrec); - external_start_advertising_service(&instance->srs.RR_TXT.resrec); - - for (e = instance->srs.Extras; e; e = e->next) - external_start_advertising_service(&e->r.resrec); - - instance->external_advertise = mDNStrue; - } + if (instance->external_advertise) LogMsg("external_start_advertising_helper: external_advertise already set!"); + + for ( i = 0; i < instance->request->u.servicereg.num_subtypes; i++) + external_start_advertising_service(&st[i].resrec); + + external_start_advertising_service(&instance->srs.RR_PTR.resrec); + external_start_advertising_service(&instance->srs.RR_TXT.resrec); + + for (e = instance->srs.Extras; e; e = e->next) + external_start_advertising_service(&e->r.resrec); + + instance->external_advertise = mDNStrue; + } mDNSlocal void external_stop_advertising_helper(service_instance *const instance) - { - AuthRecord *st = instance->subtypes; - ExtraResourceRecord *e; - int i; - - if (!instance->external_advertise) return; + { + AuthRecord *st = instance->subtypes; + ExtraResourceRecord *e; + int i; - LogInfo("external_stop_advertising_helper: calling external_stop_advertising_service"); - - for ( i = 0; i < instance->request->u.servicereg.num_subtypes; i++) - external_stop_advertising_service(&st[i].resrec); - - external_stop_advertising_service(&instance->srs.RR_PTR.resrec); - external_stop_advertising_service(&instance->srs.RR_TXT.resrec); - - for (e = instance->srs.Extras; e; e = e->next) - external_stop_advertising_service(&e->r.resrec); - - instance->external_advertise = mDNSfalse; - } + if (!instance->external_advertise) return; + + LogInfo("external_stop_advertising_helper: calling external_stop_advertising_service"); + + for ( i = 0; i < instance->request->u.servicereg.num_subtypes; i++) + external_stop_advertising_service(&st[i].resrec); + + external_stop_advertising_service(&instance->srs.RR_PTR.resrec); + external_stop_advertising_service(&instance->srs.RR_TXT.resrec); + + for (e = instance->srs.Extras; e; e = e->next) + external_stop_advertising_service(&e->r.resrec); + + instance->external_advertise = mDNSfalse; + } // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -664,1062 +666,1062 @@ mDNSlocal void external_stop_advertising_helper(service_instance *const instance #endif mDNSexport void FreeExtraRR(mDNS *const m, AuthRecord *const rr, mStatus result) - { - ExtraResourceRecord *extra = (ExtraResourceRecord *)rr->RecordContext; - (void)m; // Unused + { + ExtraResourceRecord *extra = (ExtraResourceRecord *)rr->RecordContext; + (void)m; // Unused - if (result != mStatus_MemFree) { LogMsg("Error: FreeExtraRR invoked with unexpected error %d", result); return; } + if (result != mStatus_MemFree) { LogMsg("Error: FreeExtraRR invoked with unexpected error %d", result); return; } - LogInfo(" FreeExtraRR %s", RRDisplayString(m, &rr->resrec)); + LogInfo(" FreeExtraRR %s", RRDisplayString(m, &rr->resrec)); - if (rr->resrec.rdata != &rr->rdatastorage) - freeL("Extra RData", rr->resrec.rdata); - freeL("ExtraResourceRecord/FreeExtraRR", extra); - } + if (rr->resrec.rdata != &rr->rdatastorage) + freeL("Extra RData", rr->resrec.rdata); + freeL("ExtraResourceRecord/FreeExtraRR", extra); + } mDNSlocal void unlink_and_free_service_instance(service_instance *srv) - { - ExtraResourceRecord *e = srv->srs.Extras, *tmp; + { + ExtraResourceRecord *e = srv->srs.Extras, *tmp; - external_stop_advertising_helper(srv); + external_stop_advertising_helper(srv); - // clear pointers from parent struct - if (srv->request) - { - service_instance **p = &srv->request->u.servicereg.instances; - while (*p) - { - if (*p == srv) { *p = (*p)->next; break; } - p = &(*p)->next; - } - } + // clear pointers from parent struct + if (srv->request) + { + service_instance **p = &srv->request->u.servicereg.instances; + while (*p) + { + if (*p == srv) { *p = (*p)->next; break; } + p = &(*p)->next; + } + } - while (e) - { - e->r.RecordContext = e; - tmp = e; - e = e->next; - FreeExtraRR(&mDNSStorage, &tmp->r, mStatus_MemFree); - } + while (e) + { + e->r.RecordContext = e; + tmp = e; + e = e->next; + FreeExtraRR(&mDNSStorage, &tmp->r, mStatus_MemFree); + } - if (srv->srs.RR_TXT.resrec.rdata != &srv->srs.RR_TXT.rdatastorage) - freeL("TXT RData", srv->srs.RR_TXT.resrec.rdata); + if (srv->srs.RR_TXT.resrec.rdata != &srv->srs.RR_TXT.rdatastorage) + freeL("TXT RData", srv->srs.RR_TXT.resrec.rdata); - if (srv->subtypes) { freeL("ServiceSubTypes", srv->subtypes); srv->subtypes = NULL; } - freeL("service_instance", srv); - } + if (srv->subtypes) { freeL("ServiceSubTypes", srv->subtypes); srv->subtypes = NULL; } + freeL("service_instance", srv); + } // Count how many other service records we have locally with the same name, but different rdata. // For auto-named services, we can have at most one per machine -- if we allowed two auto-named services of // the same type on the same machine, we'd get into an infinite autoimmune-response loop of continuous renaming. mDNSexport int CountPeerRegistrations(mDNS *const m, ServiceRecordSet *const srs) - { - int count = 0; - ResourceRecord *r = &srs->RR_SRV.resrec; - AuthRecord *rr; + { + int count = 0; + ResourceRecord *r = &srs->RR_SRV.resrec; + AuthRecord *rr; - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.rrtype == kDNSType_SRV && SameDomainName(rr->resrec.name, r->name) && !IdenticalSameNameRecord(&rr->resrec, r)) - count++; + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.rrtype == kDNSType_SRV && SameDomainName(rr->resrec.name, r->name) && !IdenticalSameNameRecord(&rr->resrec, r)) + count++; - verbosedebugf("%d peer registrations for %##s", count, r->name->c); - return(count); - } + verbosedebugf("%d peer registrations for %##s", count, r->name->c); + return(count); + } mDNSexport int CountExistingRegistrations(domainname *srv, mDNSIPPort port) - { - int count = 0; - AuthRecord *rr; - for (rr = mDNSStorage.ResourceRecords; rr; rr=rr->next) - if (rr->resrec.rrtype == kDNSType_SRV && - mDNSSameIPPort(rr->resrec.rdata->u.srv.port, port) && - SameDomainName(rr->resrec.name, srv)) - count++; - return(count); - } + { + int count = 0; + AuthRecord *rr; + for (rr = mDNSStorage.ResourceRecords; rr; rr=rr->next) + if (rr->resrec.rrtype == kDNSType_SRV && + mDNSSameIPPort(rr->resrec.rdata->u.srv.port, port) && + SameDomainName(rr->resrec.name, srv)) + count++; + return(count); + } mDNSlocal void SendServiceRemovalNotification(ServiceRecordSet *const srs) - { - reply_state *rep; - service_instance *instance = srs->ServiceContext; - if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, 0, mStatus_NoError) != mStatus_NoError) - LogMsg("%3d: SendServiceRemovalNotification: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c); - else { append_reply(instance->request, rep); instance->clientnotified = mDNSfalse; } - } + { + reply_state *rep; + service_instance *instance = srs->ServiceContext; + if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, 0, mStatus_NoError) != mStatus_NoError) + LogMsg("%3d: SendServiceRemovalNotification: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c); + else { append_reply(instance->request, rep); instance->clientnotified = mDNSfalse; } + } // service registration callback performs three duties - frees memory for deregistered services, // handles name conflicts, and delivers completed registration information to the client mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result) - { - mStatus err; - mDNSBool SuppressError = mDNSfalse; - service_instance *instance; - reply_state *rep; - (void)m; // Unused + { + mStatus err; + mDNSBool SuppressError = mDNSfalse; + service_instance *instance; + reply_state *rep; + (void)m; // Unused - if (!srs) { LogMsg("regservice_callback: srs is NULL %d", result); return; } + if (!srs) { LogMsg("regservice_callback: srs is NULL %d", result); return; } - instance = srs->ServiceContext; - if (!instance) { LogMsg("regservice_callback: srs->ServiceContext is NULL %d", result); return; } + instance = srs->ServiceContext; + if (!instance) { LogMsg("regservice_callback: srs->ServiceContext is NULL %d", result); return; } - // don't send errors up to client for wide-area, empty-string registrations - if (instance->request && - instance->request->u.servicereg.default_domain && - !instance->default_local) - SuppressError = mDNStrue; + // don't send errors up to client for wide-area, empty-string registrations + if (instance->request && + instance->request->u.servicereg.default_domain && + !instance->default_local) + SuppressError = mDNStrue; - if (mDNS_LoggingEnabled) - { - const char *const fmt = - (result == mStatus_NoError) ? "%s DNSServiceRegister(%##s, %u) REGISTERED" : - (result == mStatus_MemFree) ? "%s DNSServiceRegister(%##s, %u) DEREGISTERED" : - (result == mStatus_NameConflict) ? "%s DNSServiceRegister(%##s, %u) NAME CONFLICT" : - "%s DNSServiceRegister(%##s, %u) %s %d"; - char prefix[16] = "---:"; - if (instance->request) mDNS_snprintf(prefix, sizeof(prefix), "%3d:", instance->request->sd); - LogOperation(fmt, prefix, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port), - SuppressError ? "suppressed error" : "CALLBACK", result); - } + if (mDNS_LoggingEnabled) + { + const char *const fmt = + (result == mStatus_NoError) ? "%s DNSServiceRegister(%##s, %u) REGISTERED" : + (result == mStatus_MemFree) ? "%s DNSServiceRegister(%##s, %u) DEREGISTERED" : + (result == mStatus_NameConflict) ? "%s DNSServiceRegister(%##s, %u) NAME CONFLICT" : + "%s DNSServiceRegister(%##s, %u) %s %d"; + char prefix[16] = "---:"; + if (instance->request) mDNS_snprintf(prefix, sizeof(prefix), "%3d:", instance->request->sd); + LogOperation(fmt, prefix, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port), + SuppressError ? "suppressed error" : "CALLBACK", result); + } - if (!instance->request && result != mStatus_MemFree) { LogMsg("regservice_callback: instance->request is NULL %d", result); return; } + if (!instance->request && result != mStatus_MemFree) { LogMsg("regservice_callback: instance->request is NULL %d", result); return; } - if (result == mStatus_NoError) - { - if (instance->request->u.servicereg.allowremotequery) - { - ExtraResourceRecord *e; - srs->RR_ADV.AllowRemoteQuery = mDNStrue; - srs->RR_PTR.AllowRemoteQuery = mDNStrue; - srs->RR_SRV.AllowRemoteQuery = mDNStrue; - srs->RR_TXT.AllowRemoteQuery = mDNStrue; - for (e = instance->srs.Extras; e; e = e->next) e->r.AllowRemoteQuery = mDNStrue; - } + if (result == mStatus_NoError) + { + if (instance->request->u.servicereg.allowremotequery) + { + ExtraResourceRecord *e; + srs->RR_ADV.AllowRemoteQuery = mDNStrue; + srs->RR_PTR.AllowRemoteQuery = mDNStrue; + srs->RR_SRV.AllowRemoteQuery = mDNStrue; + srs->RR_TXT.AllowRemoteQuery = mDNStrue; + for (e = instance->srs.Extras; e; e = e->next) e->r.AllowRemoteQuery = mDNStrue; + } - if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError) - LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c); - else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; } + if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError) + LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c); + else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; } - if (instance->request->u.servicereg.InterfaceID == mDNSInterface_P2P || (!instance->request->u.servicereg.InterfaceID && SameDomainName(&instance->domain, &localdomain) && (instance->request->flags & kDNSServiceFlagsIncludeP2P))) - { - LogInfo("regservice_callback: calling external_start_advertising_helper()"); - external_start_advertising_helper(instance); - } - if (instance->request->u.servicereg.autoname && CountPeerRegistrations(m, srs) == 0) - RecordUpdatedNiceLabel(m, 0); // Successfully got new name, tell user immediately - } - else if (result == mStatus_MemFree) - { - if (instance->request && instance->renameonmemfree) - { - external_stop_advertising_helper(instance); - instance->renameonmemfree = 0; - err = mDNS_RenameAndReregisterService(m, srs, &instance->request->u.servicereg.name); - if (err) LogMsg("ERROR: regservice_callback - RenameAndReregisterService returned %d", err); - // error should never happen - safest to log and continue - } - else - unlink_and_free_service_instance(instance); - } - else if (result == mStatus_NameConflict) - { - if (instance->request->u.servicereg.autorename) - { - external_stop_advertising_helper(instance); - if (instance->request->u.servicereg.autoname && CountPeerRegistrations(m, srs) == 0) - { - // On conflict for an autoname service, rename and reregister *all* autoname services - IncrementLabelSuffix(&m->nicelabel, mDNStrue); - mDNS_ConfigChanged(m); // Will call back into udsserver_handle_configchange() - } - else // On conflict for a non-autoname service, rename and reregister just that one service - { - if (instance->clientnotified) SendServiceRemovalNotification(srs); - mDNS_RenameAndReregisterService(m, srs, mDNSNULL); - } - } - else - { - if (!SuppressError) - { - if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError) - LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c); - else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; } - } - unlink_and_free_service_instance(instance); - } - } - else // Not mStatus_NoError, mStatus_MemFree, or mStatus_NameConflict - { - if (!SuppressError) - { - if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError) - LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c); - else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; } - } - } - } + if (instance->request->u.servicereg.InterfaceID == mDNSInterface_P2P || (!instance->request->u.servicereg.InterfaceID && SameDomainName(&instance->domain, &localdomain) && (instance->request->flags & kDNSServiceFlagsIncludeP2P))) + { + LogInfo("regservice_callback: calling external_start_advertising_helper()"); + external_start_advertising_helper(instance); + } + if (instance->request->u.servicereg.autoname && CountPeerRegistrations(m, srs) == 0) + RecordUpdatedNiceLabel(m, 0); // Successfully got new name, tell user immediately + } + else if (result == mStatus_MemFree) + { + if (instance->request && instance->renameonmemfree) + { + external_stop_advertising_helper(instance); + instance->renameonmemfree = 0; + err = mDNS_RenameAndReregisterService(m, srs, &instance->request->u.servicereg.name); + if (err) LogMsg("ERROR: regservice_callback - RenameAndReregisterService returned %d", err); + // error should never happen - safest to log and continue + } + else + unlink_and_free_service_instance(instance); + } + else if (result == mStatus_NameConflict) + { + if (instance->request->u.servicereg.autorename) + { + external_stop_advertising_helper(instance); + if (instance->request->u.servicereg.autoname && CountPeerRegistrations(m, srs) == 0) + { + // On conflict for an autoname service, rename and reregister *all* autoname services + IncrementLabelSuffix(&m->nicelabel, mDNStrue); + mDNS_ConfigChanged(m); // Will call back into udsserver_handle_configchange() + } + else // On conflict for a non-autoname service, rename and reregister just that one service + { + if (instance->clientnotified) SendServiceRemovalNotification(srs); + mDNS_RenameAndReregisterService(m, srs, mDNSNULL); + } + } + else + { + if (!SuppressError) + { + if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError) + LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c); + else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; } + } + unlink_and_free_service_instance(instance); + } + } + else // Not mStatus_NoError, mStatus_MemFree, or mStatus_NameConflict + { + if (!SuppressError) + { + if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError) + LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c); + else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; } + } + } + } mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord *rr, mStatus result) - { - (void)m; // Unused - if (!rr->RecordContext) // parent struct already freed by termination callback - { - if (result == mStatus_NoError) - LogMsg("Error: regrecord_callback: successful registration of orphaned record %s", ARDisplayString(m, rr)); - else - { - if (result != mStatus_MemFree) LogMsg("regrecord_callback: error %d received after parent termination", result); + { + (void)m; // Unused + if (!rr->RecordContext) // parent struct already freed by termination callback + { + if (result == mStatus_NoError) + LogMsg("Error: regrecord_callback: successful registration of orphaned record %s", ARDisplayString(m, rr)); + else + { + if (result != mStatus_MemFree) LogMsg("regrecord_callback: error %d received after parent termination", result); - // We come here when the record is being deregistered either from DNSServiceRemoveRecord or connection_termination. - // If the record has been updated, we need to free the rdata. Everytime we call mDNS_Update, it calls update_callback - // with the old rdata (so that we can free it) and stores the new rdata in "rr->resrec.rdata". This means, we need - // to free the latest rdata for which the update_callback was never called with. - if (rr->resrec.rdata != &rr->rdatastorage) freeL("RData/regrecord_callback", rr->resrec.rdata); - freeL("AuthRecord/regrecord_callback", rr); - } - } - else - { - registered_record_entry *re = rr->RecordContext; - request_state *request = re->request; + // We come here when the record is being deregistered either from DNSServiceRemoveRecord or connection_termination. + // If the record has been updated, we need to free the rdata. Everytime we call mDNS_Update, it calls update_callback + // with the old rdata (so that we can free it) and stores the new rdata in "rr->resrec.rdata". This means, we need + // to free the latest rdata for which the update_callback was never called with. + if (rr->resrec.rdata != &rr->rdatastorage) freeL("RData/regrecord_callback", rr->resrec.rdata); + freeL("AuthRecord/regrecord_callback", rr); + } + } + else + { + registered_record_entry *re = rr->RecordContext; + request_state *request = re->request; - if (mDNS_LoggingEnabled) - { - char *fmt = (result == mStatus_NoError) ? "%3d: DNSServiceRegisterRecord(%u %s) REGISTERED" : - (result == mStatus_MemFree) ? "%3d: DNSServiceRegisterRecord(%u %s) DEREGISTERED" : - (result == mStatus_NameConflict) ? "%3d: DNSServiceRegisterRecord(%u %s) NAME CONFLICT" : - "%3d: DNSServiceRegisterRecord(%u %s) %d"; - LogOperation(fmt, request->sd, re->key, RRDisplayString(m, &rr->resrec), result); - } + if (mDNS_LoggingEnabled) + { + char *fmt = (result == mStatus_NoError) ? "%3d: DNSServiceRegisterRecord(%u %s) REGISTERED" : + (result == mStatus_MemFree) ? "%3d: DNSServiceRegisterRecord(%u %s) DEREGISTERED" : + (result == mStatus_NameConflict) ? "%3d: DNSServiceRegisterRecord(%u %s) NAME CONFLICT" : + "%3d: DNSServiceRegisterRecord(%u %s) %d"; + LogOperation(fmt, request->sd, re->key, RRDisplayString(m, &rr->resrec), result); + } - if (result != mStatus_MemFree) - { - int len = sizeof(DNSServiceFlags) + sizeof(mDNSu32) + sizeof(DNSServiceErrorType); - reply_state *reply = create_reply(reg_record_reply_op, len, request); - reply->mhdr->client_context = re->regrec_client_context; - reply->rhdr->flags = dnssd_htonl(0); - reply->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, rr->resrec.InterfaceID, mDNSfalse)); - reply->rhdr->error = dnssd_htonl(result); - append_reply(request, reply); - } + if (result != mStatus_MemFree) + { + int len = sizeof(DNSServiceFlags) + sizeof(mDNSu32) + sizeof(DNSServiceErrorType); + reply_state *reply = create_reply(reg_record_reply_op, len, request); + reply->mhdr->client_context = re->regrec_client_context; + reply->rhdr->flags = dnssd_htonl(0); + reply->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, rr->resrec.InterfaceID, mDNSfalse)); + reply->rhdr->error = dnssd_htonl(result); + append_reply(request, reply); + } - if (result) - { - // unlink from list, free memory - registered_record_entry **ptr = &request->u.reg_recs; - while (*ptr && (*ptr) != re) ptr = &(*ptr)->next; - if (!*ptr) { LogMsg("regrecord_callback - record not in list!"); return; } - *ptr = (*ptr)->next; - freeL("registered_record_entry AuthRecord regrecord_callback", re->rr); - freeL("registered_record_entry regrecord_callback", re); - } - else - { - if (re->external_advertise) LogMsg("regrecord_callback: external_advertise already set!"); + if (result) + { + // unlink from list, free memory + registered_record_entry **ptr = &request->u.reg_recs; + while (*ptr && (*ptr) != re) ptr = &(*ptr)->next; + if (!*ptr) { LogMsg("regrecord_callback - record not in list!"); return; } + *ptr = (*ptr)->next; + freeL("registered_record_entry AuthRecord regrecord_callback", re->rr); + freeL("registered_record_entry regrecord_callback", re); + } + else + { + if (re->external_advertise) LogMsg("regrecord_callback: external_advertise already set!"); - if (re->origInterfaceID == mDNSInterface_P2P || (!re->origInterfaceID && IsLocalDomain(&rr->namestorage) && (request->flags & kDNSServiceFlagsIncludeP2P))) - { - LogInfo("regrecord_callback: calling external_start_advertising_service"); - external_start_advertising_service(&rr->resrec); - re->external_advertise = mDNStrue; - } - } - } - } + if (re->origInterfaceID == mDNSInterface_P2P || (!re->origInterfaceID && IsLocalDomain(&rr->namestorage) && (request->flags & kDNSServiceFlagsIncludeP2P))) + { + LogInfo("regrecord_callback: calling external_start_advertising_service"); + external_start_advertising_service(&rr->resrec); + re->external_advertise = mDNStrue; + } + } + } + } mDNSlocal void connection_termination(request_state *request) - { - // When terminating a shared connection, we need to scan the all_requests list - // and terminate any subbordinate operations sharing this file descriptor - request_state **req = &all_requests; - - LogOperation("%3d: DNSServiceCreateConnection STOP", request->sd); - - while (*req) - { - if ((*req)->primary == request) - { - // Since we're already doing a list traversal, we unlink the request directly instead of using AbortUnlinkAndFree() - request_state *tmp = *req; - if (tmp->primary == tmp) LogMsg("connection_termination ERROR (*req)->primary == *req for %p %d", tmp, tmp->sd); - if (tmp->replies) LogMsg("connection_termination ERROR How can subordinate req %p %d have replies queued?", tmp, tmp->sd); - abort_request(tmp); - *req = tmp->next; - freeL("request_state/connection_termination", tmp); - } - else - req = &(*req)->next; - } + { + // When terminating a shared connection, we need to scan the all_requests list + // and terminate any subbordinate operations sharing this file descriptor + request_state **req = &all_requests; - while (request->u.reg_recs) - { - registered_record_entry *ptr = request->u.reg_recs; - LogOperation("%3d: DNSServiceRegisterRecord(%u %s) STOP", request->sd, ptr->key, RRDisplayString(&mDNSStorage, &ptr->rr->resrec)); - request->u.reg_recs = request->u.reg_recs->next; - ptr->rr->RecordContext = NULL; - if (ptr->external_advertise) - { - ptr->external_advertise = mDNSfalse; - external_stop_advertising_service(&ptr->rr->resrec); - } - mDNS_Deregister(&mDNSStorage, ptr->rr); // Will free ptr->rr for us - freeL("registered_record_entry/connection_termination", ptr); - } - } + LogOperation("%3d: DNSServiceCreateConnection STOP", request->sd); + + while (*req) + { + if ((*req)->primary == request) + { + // Since we're already doing a list traversal, we unlink the request directly instead of using AbortUnlinkAndFree() + request_state *tmp = *req; + if (tmp->primary == tmp) LogMsg("connection_termination ERROR (*req)->primary == *req for %p %d", tmp, tmp->sd); + if (tmp->replies) LogMsg("connection_termination ERROR How can subordinate req %p %d have replies queued?", tmp, tmp->sd); + abort_request(tmp); + *req = tmp->next; + freeL("request_state/connection_termination", tmp); + } + else + req = &(*req)->next; + } + + while (request->u.reg_recs) + { + registered_record_entry *ptr = request->u.reg_recs; + LogOperation("%3d: DNSServiceRegisterRecord(%u %s) STOP", request->sd, ptr->key, RRDisplayString(&mDNSStorage, &ptr->rr->resrec)); + request->u.reg_recs = request->u.reg_recs->next; + ptr->rr->RecordContext = NULL; + if (ptr->external_advertise) + { + ptr->external_advertise = mDNSfalse; + external_stop_advertising_service(&ptr->rr->resrec); + } + mDNS_Deregister(&mDNSStorage, ptr->rr); // Will free ptr->rr for us + freeL("registered_record_entry/connection_termination", ptr); + } + } mDNSlocal void handle_cancel_request(request_state *request) - { - request_state **req = &all_requests; - LogOperation("%3d: Cancel %08X %08X", request->sd, request->hdr.client_context.u32[1], request->hdr.client_context.u32[0]); - while (*req) - { - if ((*req)->primary == request && - (*req)->hdr.client_context.u32[0] == request->hdr.client_context.u32[0] && - (*req)->hdr.client_context.u32[1] == request->hdr.client_context.u32[1]) - { - // Since we're already doing a list traversal, we unlink the request directly instead of using AbortUnlinkAndFree() - request_state *tmp = *req; - abort_request(tmp); - *req = tmp->next; - freeL("request_state/handle_cancel_request", tmp); - } - else - req = &(*req)->next; - } - } + { + request_state **req = &all_requests; + LogOperation("%3d: Cancel %08X %08X", request->sd, request->hdr.client_context.u32[1], request->hdr.client_context.u32[0]); + while (*req) + { + if ((*req)->primary == request && + (*req)->hdr.client_context.u32[0] == request->hdr.client_context.u32[0] && + (*req)->hdr.client_context.u32[1] == request->hdr.client_context.u32[1]) + { + // Since we're already doing a list traversal, we unlink the request directly instead of using AbortUnlinkAndFree() + request_state *tmp = *req; + abort_request(tmp); + *req = tmp->next; + freeL("request_state/handle_cancel_request", tmp); + } + else + req = &(*req)->next; + } + } mDNSlocal mStatus handle_regrecord_request(request_state *request) - { - mStatus err = mStatus_BadParamErr; - AuthRecord *rr = read_rr_from_ipc_msg(request, 1, 1); - if (rr) - { - registered_record_entry *re; - // Don't allow non-local domains to be regsitered as LocalOnly. Allowing this would permit - // clients to register records such as www.bigbank.com A w.x.y.z to redirect Safari. - if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly && !IsLocalDomain(rr->resrec.name) && - rr->resrec.rrclass == kDNSClass_IN && (rr->resrec.rrtype == kDNSType_A || rr->resrec.rrtype == kDNSType_AAAA || - rr->resrec.rrtype == kDNSType_CNAME)) - { - freeL("AuthRecord/handle_regrecord_request", rr); - return (mStatus_BadParamErr); - } - // allocate registration entry, link into list - re = mallocL("registered_record_entry", sizeof(registered_record_entry)); - if (!re) FatalError("ERROR: malloc"); - re->key = request->hdr.reg_index; - re->rr = rr; - re->regrec_client_context = request->hdr.client_context; - re->request = request; - re->external_advertise = mDNSfalse; - rr->RecordContext = re; - rr->RecordCallback = regrecord_callback; + { + mStatus err = mStatus_BadParamErr; + AuthRecord *rr = read_rr_from_ipc_msg(request, 1, 1); + if (rr) + { + registered_record_entry *re; + // Don't allow non-local domains to be regsitered as LocalOnly. Allowing this would permit + // clients to register records such as www.bigbank.com A w.x.y.z to redirect Safari. + if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly && !IsLocalDomain(rr->resrec.name) && + rr->resrec.rrclass == kDNSClass_IN && (rr->resrec.rrtype == kDNSType_A || rr->resrec.rrtype == kDNSType_AAAA || + rr->resrec.rrtype == kDNSType_CNAME)) + { + freeL("AuthRecord/handle_regrecord_request", rr); + return (mStatus_BadParamErr); + } + // allocate registration entry, link into list + re = mallocL("registered_record_entry", sizeof(registered_record_entry)); + if (!re) FatalError("ERROR: malloc"); + re->key = request->hdr.reg_index; + re->rr = rr; + re->regrec_client_context = request->hdr.client_context; + re->request = request; + re->external_advertise = mDNSfalse; + rr->RecordContext = re; + rr->RecordCallback = regrecord_callback; - re->origInterfaceID = rr->resrec.InterfaceID; - if (rr->resrec.InterfaceID == mDNSInterface_P2P) rr->resrec.InterfaceID = mDNSInterface_Any; + re->origInterfaceID = rr->resrec.InterfaceID; + if (rr->resrec.InterfaceID == mDNSInterface_P2P) rr->resrec.InterfaceID = mDNSInterface_Any; #if 0 - if (!AuthorizedDomain(request, rr->resrec.name, AutoRegistrationDomains)) return (mStatus_NoError); + if (!AuthorizedDomain(request, rr->resrec.name, AutoRegistrationDomains)) return (mStatus_NoError); #endif - if (rr->resrec.rroriginalttl == 0) - rr->resrec.rroriginalttl = DefaultTTLforRRType(rr->resrec.rrtype); - - LogOperation("%3d: DNSServiceRegisterRecord(%u %s) START", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec)); - err = mDNS_Register(&mDNSStorage, rr); - if (err) - { - LogOperation("%3d: DNSServiceRegisterRecord(%u %s) ERROR (%d)", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec), err); - freeL("registered_record_entry", re); - freeL("registered_record_entry/AuthRecord", rr); - } - else - { - re->next = request->u.reg_recs; - request->u.reg_recs = re; - } - } - return(err); - } + if (rr->resrec.rroriginalttl == 0) + rr->resrec.rroriginalttl = DefaultTTLforRRType(rr->resrec.rrtype); + + LogOperation("%3d: DNSServiceRegisterRecord(%u %s) START", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec)); + err = mDNS_Register(&mDNSStorage, rr); + if (err) + { + LogOperation("%3d: DNSServiceRegisterRecord(%u %s) ERROR (%d)", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec), err); + freeL("registered_record_entry", re); + freeL("registered_record_entry/AuthRecord", rr); + } + else + { + re->next = request->u.reg_recs; + request->u.reg_recs = re; + } + } + return(err); + } mDNSlocal void UpdateDeviceInfoRecord(mDNS *const m); mDNSlocal void regservice_termination_callback(request_state *request) - { - if (!request) { LogMsg("regservice_termination_callback context is NULL"); return; } - while (request->u.servicereg.instances) - { - service_instance *p = request->u.servicereg.instances; - request->u.servicereg.instances = request->u.servicereg.instances->next; - // only safe to free memory if registration is not valid, i.e. deregister fails (which invalidates p) - LogOperation("%3d: DNSServiceRegister(%##s, %u) STOP", - request->sd, p->srs.RR_SRV.resrec.name->c, mDNSVal16(p->srs.RR_SRV.resrec.rdata->u.srv.port)); - - external_stop_advertising_helper(p); + { + if (!request) { LogMsg("regservice_termination_callback context is NULL"); return; } + while (request->u.servicereg.instances) + { + service_instance *p = request->u.servicereg.instances; + request->u.servicereg.instances = request->u.servicereg.instances->next; + // only safe to free memory if registration is not valid, i.e. deregister fails (which invalidates p) + LogOperation("%3d: DNSServiceRegister(%##s, %u) STOP", + request->sd, p->srs.RR_SRV.resrec.name->c, mDNSVal16(p->srs.RR_SRV.resrec.rdata->u.srv.port)); - // Clear backpointer *before* calling mDNS_DeregisterService/unlink_and_free_service_instance - // We don't need unlink_and_free_service_instance to cut its element from the list, because we're already advancing - // request->u.servicereg.instances as we work our way through the list, implicitly cutting one element at a time - // We can't clear p->request *after* the calling mDNS_DeregisterService/unlink_and_free_service_instance - // because by then we might have already freed p - p->request = NULL; - if (mDNS_DeregisterService(&mDNSStorage, &p->srs)) unlink_and_free_service_instance(p); - // Don't touch service_instance *p after this -- it's likely to have been freed already - } - if (request->u.servicereg.txtdata) - { freeL("service_info txtdata", request->u.servicereg.txtdata); request->u.servicereg.txtdata = NULL; } - if (request->u.servicereg.autoname) - { - // Clear autoname before calling UpdateDeviceInfoRecord() so it doesn't mistakenly include this in its count of active autoname registrations - request->u.servicereg.autoname = mDNSfalse; - UpdateDeviceInfoRecord(&mDNSStorage); - } - } + external_stop_advertising_helper(p); + + // Clear backpointer *before* calling mDNS_DeregisterService/unlink_and_free_service_instance + // We don't need unlink_and_free_service_instance to cut its element from the list, because we're already advancing + // request->u.servicereg.instances as we work our way through the list, implicitly cutting one element at a time + // We can't clear p->request *after* the calling mDNS_DeregisterService/unlink_and_free_service_instance + // because by then we might have already freed p + p->request = NULL; + if (mDNS_DeregisterService(&mDNSStorage, &p->srs)) unlink_and_free_service_instance(p); + // Don't touch service_instance *p after this -- it's likely to have been freed already + } + if (request->u.servicereg.txtdata) + { freeL("service_info txtdata", request->u.servicereg.txtdata); request->u.servicereg.txtdata = NULL; } + if (request->u.servicereg.autoname) + { + // Clear autoname before calling UpdateDeviceInfoRecord() so it doesn't mistakenly include this in its count of active autoname registrations + request->u.servicereg.autoname = mDNSfalse; + UpdateDeviceInfoRecord(&mDNSStorage); + } + } mDNSlocal request_state *LocateSubordinateRequest(request_state *request) - { - request_state *req; - for (req = all_requests; req; req = req->next) - if (req->primary == request && - req->hdr.client_context.u32[0] == request->hdr.client_context.u32[0] && - req->hdr.client_context.u32[1] == request->hdr.client_context.u32[1]) return(req); - return(request); - } + { + request_state *req; + for (req = all_requests; req; req = req->next) + if (req->primary == request && + req->hdr.client_context.u32[0] == request->hdr.client_context.u32[0] && + req->hdr.client_context.u32[1] == request->hdr.client_context.u32[1]) return(req); + return(request); + } mDNSlocal mStatus add_record_to_service(request_state *request, service_instance *instance, mDNSu16 rrtype, mDNSu16 rdlen, const char *rdata, mDNSu32 ttl) - { - ServiceRecordSet *srs = &instance->srs; - mStatus result; - int size = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody); - ExtraResourceRecord *extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size); - if (!extra) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; } + { + ServiceRecordSet *srs = &instance->srs; + mStatus result; + int size = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody); + ExtraResourceRecord *extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size); + if (!extra) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; } - mDNSPlatformMemZero(extra, sizeof(ExtraResourceRecord)); // OK if oversized rdata not zero'd - extra->r.resrec.rrtype = rrtype; - extra->r.rdatastorage.MaxRDLength = (mDNSu16) size; - extra->r.resrec.rdlength = rdlen; - mDNSPlatformMemCopy(&extra->r.rdatastorage.u.data, rdata, rdlen); + mDNSPlatformMemZero(extra, sizeof(ExtraResourceRecord)); // OK if oversized rdata not zero'd + extra->r.resrec.rrtype = rrtype; + extra->r.rdatastorage.MaxRDLength = (mDNSu16) size; + extra->r.resrec.rdlength = rdlen; + mDNSPlatformMemCopy(&extra->r.rdatastorage.u.data, rdata, rdlen); - result = mDNS_AddRecordToService(&mDNSStorage, srs, extra, &extra->r.rdatastorage, ttl, - (request->flags & kDNSServiceFlagsIncludeP2P) ? 1: 0); - if (result) { freeL("ExtraResourceRecord/add_record_to_service", extra); return result; } + result = mDNS_AddRecordToService(&mDNSStorage, srs, extra, &extra->r.rdatastorage, ttl, + (request->flags & kDNSServiceFlagsIncludeP2P) ? 1: 0); + if (result) { freeL("ExtraResourceRecord/add_record_to_service", extra); return result; } - extra->ClientID = request->hdr.reg_index; - if (instance->external_advertise && (instance->request->u.servicereg.InterfaceID == mDNSInterface_P2P || (!instance->request->u.servicereg.InterfaceID && SameDomainName(&instance->domain, &localdomain) && (instance->request->flags & kDNSServiceFlagsIncludeP2P)))) - { - LogInfo("add_record_to_service: calling external_start_advertising_service"); - external_start_advertising_service(&extra->r.resrec); - } - return result; - } + extra->ClientID = request->hdr.reg_index; + if (instance->external_advertise && (instance->request->u.servicereg.InterfaceID == mDNSInterface_P2P || (!instance->request->u.servicereg.InterfaceID && SameDomainName(&instance->domain, &localdomain) && (instance->request->flags & kDNSServiceFlagsIncludeP2P)))) + { + LogInfo("add_record_to_service: calling external_start_advertising_service"); + external_start_advertising_service(&extra->r.resrec); + } + return result; + } mDNSlocal mStatus handle_add_request(request_state *request) - { - service_instance *i; - mStatus result = mStatus_UnknownErr; - DNSServiceFlags flags = get_flags (&request->msgptr, request->msgend); - mDNSu16 rrtype = get_uint16(&request->msgptr, request->msgend); - mDNSu16 rdlen = get_uint16(&request->msgptr, request->msgend); - const char *rdata = get_rdata (&request->msgptr, request->msgend, rdlen); - mDNSu32 ttl = get_uint32(&request->msgptr, request->msgend); - if (!ttl) ttl = DefaultTTLforRRType(rrtype); - (void)flags; // Unused + { + service_instance *i; + mStatus result = mStatus_UnknownErr; + DNSServiceFlags flags = get_flags (&request->msgptr, request->msgend); + mDNSu16 rrtype = get_uint16(&request->msgptr, request->msgend); + mDNSu16 rdlen = get_uint16(&request->msgptr, request->msgend); + const char *rdata = get_rdata (&request->msgptr, request->msgend, rdlen); + mDNSu32 ttl = get_uint32(&request->msgptr, request->msgend); + if (!ttl) ttl = DefaultTTLforRRType(rrtype); + (void)flags; // Unused - if (!request->msgptr) { LogMsg("%3d: DNSServiceAddRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } + if (!request->msgptr) { LogMsg("%3d: DNSServiceAddRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } - // If this is a shared connection, check if the operation actually applies to a subordinate request_state object - if (request->terminate == connection_termination) request = LocateSubordinateRequest(request); + // If this is a shared connection, check if the operation actually applies to a subordinate request_state object + if (request->terminate == connection_termination) request = LocateSubordinateRequest(request); - if (request->terminate != regservice_termination_callback) - { LogMsg("%3d: DNSServiceAddRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); } + if (request->terminate != regservice_termination_callback) + { LogMsg("%3d: DNSServiceAddRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); } - // For a service registered with zero port, don't allow adding records. This mostly happens due to a bug - // in the application. See radar://9165807. - if (mDNSIPPortIsZero(request->u.servicereg.port)) - { LogMsg("%3d: DNSServiceAddRecord: adding record to a service registered with zero port", request->sd); return(mStatus_BadParamErr); } + // For a service registered with zero port, don't allow adding records. This mostly happens due to a bug + // in the application. See radar://9165807. + if (mDNSIPPortIsZero(request->u.servicereg.port)) + { LogMsg("%3d: DNSServiceAddRecord: adding record to a service registered with zero port", request->sd); return(mStatus_BadParamErr); } - LogOperation("%3d: DNSServiceAddRecord(%X, %##s, %s, %d)", request->sd, flags, - (request->u.servicereg.instances) ? request->u.servicereg.instances->srs.RR_SRV.resrec.name->c : NULL, DNSTypeName(rrtype), rdlen); + LogOperation("%3d: DNSServiceAddRecord(%X, %##s, %s, %d)", request->sd, flags, + (request->u.servicereg.instances) ? request->u.servicereg.instances->srs.RR_SRV.resrec.name->c : NULL, DNSTypeName(rrtype), rdlen); - for (i = request->u.servicereg.instances; i; i = i->next) - { - result = add_record_to_service(request, i, rrtype, rdlen, rdata, ttl); - if (result && i->default_local) break; - else result = mStatus_NoError; // suppress non-local default errors - } + for (i = request->u.servicereg.instances; i; i = i->next) + { + result = add_record_to_service(request, i, rrtype, rdlen, rdata, ttl); + if (result && i->default_local) break; + else result = mStatus_NoError; // suppress non-local default errors + } - return(result); - } + return(result); + } mDNSlocal void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd, mDNSu16 oldrdlen) - { - mDNSBool external_advertise = (rr->UpdateContext) ? *((mDNSBool *)rr->UpdateContext) : mDNSfalse; - (void)m; // Unused - - // There are three cases. - // - // 1. We have updated the primary TXT record of the service - // 2. We have updated the TXT record that was added to the service using DNSServiceAddRecord - // 3. We have updated the TXT record that was registered using DNSServiceRegisterRecord - // - // external_advertise is set if we have advertised at least once during the initial addition - // of the record in all of the three cases above. We should have checked for InterfaceID/LocalDomain - // checks during the first time and hence we don't do any checks here - if (external_advertise) - { - ResourceRecord ext = rr->resrec; - if (ext.rdlength == oldrdlen && mDNSPlatformMemSame(&ext.rdata->u, &oldrd->u, oldrdlen)) goto exit; - SetNewRData(&ext, oldrd, oldrdlen); - external_stop_advertising_service(&ext); - LogInfo("update_callback: calling external_start_advertising_service"); - external_start_advertising_service(&rr->resrec); - } + { + mDNSBool external_advertise = (rr->UpdateContext) ? *((mDNSBool *)rr->UpdateContext) : mDNSfalse; + (void)m; // Unused + + // There are three cases. + // + // 1. We have updated the primary TXT record of the service + // 2. We have updated the TXT record that was added to the service using DNSServiceAddRecord + // 3. We have updated the TXT record that was registered using DNSServiceRegisterRecord + // + // external_advertise is set if we have advertised at least once during the initial addition + // of the record in all of the three cases above. We should have checked for InterfaceID/LocalDomain + // checks during the first time and hence we don't do any checks here + if (external_advertise) + { + ResourceRecord ext = rr->resrec; + if (ext.rdlength == oldrdlen && mDNSPlatformMemSame(&ext.rdata->u, &oldrd->u, oldrdlen)) goto exit; + SetNewRData(&ext, oldrd, oldrdlen); + external_stop_advertising_service(&ext); + LogInfo("update_callback: calling external_start_advertising_service"); + external_start_advertising_service(&rr->resrec); + } exit: - if (oldrd != &rr->rdatastorage) freeL("RData/update_callback", oldrd); - } + if (oldrd != &rr->rdatastorage) freeL("RData/update_callback", oldrd); + } mDNSlocal mStatus update_record(AuthRecord *rr, mDNSu16 rdlen, const char *rdata, mDNSu32 ttl, const mDNSBool *const external_advertise) - { - mStatus result; - const int rdsize = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody); - RData *newrd = mallocL("RData/update_record", sizeof(RData) - sizeof(RDataBody) + rdsize); - if (!newrd) FatalError("ERROR: malloc"); - newrd->MaxRDLength = (mDNSu16) rdsize; - mDNSPlatformMemCopy(&newrd->u, rdata, rdlen); + { + mStatus result; + const int rdsize = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody); + RData *newrd = mallocL("RData/update_record", sizeof(RData) - sizeof(RDataBody) + rdsize); + if (!newrd) FatalError("ERROR: malloc"); + newrd->MaxRDLength = (mDNSu16) rdsize; + mDNSPlatformMemCopy(&newrd->u, rdata, rdlen); - // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct, - // since RFC 1035 specifies a TXT record as "One or more s", not "Zero or more s". - // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here. - if (rr->resrec.rrtype == kDNSType_TXT && rdlen == 0) { rdlen = 1; newrd->u.txt.c[0] = 0; } - - if (external_advertise) rr->UpdateContext = (void *)external_advertise; - - result = mDNS_Update(&mDNSStorage, rr, ttl, rdlen, newrd, update_callback); - if (result) { LogMsg("update_record: Error %d for %s", (int)result, ARDisplayString(&mDNSStorage, rr)); freeL("RData/update_record", newrd); } - return result; - } + // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct, + // since RFC 1035 specifies a TXT record as "One or more s", not "Zero or more s". + // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here. + if (rr->resrec.rrtype == kDNSType_TXT && rdlen == 0) { rdlen = 1; newrd->u.txt.c[0] = 0; } + + if (external_advertise) rr->UpdateContext = (void *)external_advertise; + + result = mDNS_Update(&mDNSStorage, rr, ttl, rdlen, newrd, update_callback); + if (result) { LogMsg("update_record: Error %d for %s", (int)result, ARDisplayString(&mDNSStorage, rr)); freeL("RData/update_record", newrd); } + return result; + } mDNSlocal mStatus handle_update_request(request_state *request) - { - const ipc_msg_hdr *const hdr = &request->hdr; - mStatus result = mStatus_BadReferenceErr; - service_instance *i; - AuthRecord *rr = NULL; + { + const ipc_msg_hdr *const hdr = &request->hdr; + mStatus result = mStatus_BadReferenceErr; + service_instance *i; + AuthRecord *rr = NULL; - // get the message data - DNSServiceFlags flags = get_flags (&request->msgptr, request->msgend); // flags unused - mDNSu16 rdlen = get_uint16(&request->msgptr, request->msgend); - const char *rdata = get_rdata (&request->msgptr, request->msgend, rdlen); - mDNSu32 ttl = get_uint32(&request->msgptr, request->msgend); - (void)flags; // Unused + // get the message data + DNSServiceFlags flags = get_flags (&request->msgptr, request->msgend); // flags unused + mDNSu16 rdlen = get_uint16(&request->msgptr, request->msgend); + const char *rdata = get_rdata (&request->msgptr, request->msgend, rdlen); + mDNSu32 ttl = get_uint32(&request->msgptr, request->msgend); + (void)flags; // Unused - if (!request->msgptr) { LogMsg("%3d: DNSServiceUpdateRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } + if (!request->msgptr) { LogMsg("%3d: DNSServiceUpdateRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } - // If this is a shared connection, check if the operation actually applies to a subordinate request_state object - if (request->terminate == connection_termination) request = LocateSubordinateRequest(request); + // If this is a shared connection, check if the operation actually applies to a subordinate request_state object + if (request->terminate == connection_termination) request = LocateSubordinateRequest(request); - if (request->terminate == connection_termination) - { - // update an individually registered record - registered_record_entry *reptr; - for (reptr = request->u.reg_recs; reptr; reptr = reptr->next) - { - if (reptr->key == hdr->reg_index) - { - result = update_record(reptr->rr, rdlen, rdata, ttl, &reptr->external_advertise); - LogOperation("%3d: DNSServiceUpdateRecord(%##s, %s)", - request->sd, reptr->rr->resrec.name->c, reptr->rr ? DNSTypeName(reptr->rr->resrec.rrtype) : ""); - goto end; - } - } - result = mStatus_BadReferenceErr; - goto end; - } + if (request->terminate == connection_termination) + { + // update an individually registered record + registered_record_entry *reptr; + for (reptr = request->u.reg_recs; reptr; reptr = reptr->next) + { + if (reptr->key == hdr->reg_index) + { + result = update_record(reptr->rr, rdlen, rdata, ttl, &reptr->external_advertise); + LogOperation("%3d: DNSServiceUpdateRecord(%##s, %s)", + request->sd, reptr->rr->resrec.name->c, reptr->rr ? DNSTypeName(reptr->rr->resrec.rrtype) : ""); + goto end; + } + } + result = mStatus_BadReferenceErr; + goto end; + } - if (request->terminate != regservice_termination_callback) - { LogMsg("%3d: DNSServiceUpdateRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); } + if (request->terminate != regservice_termination_callback) + { LogMsg("%3d: DNSServiceUpdateRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); } - // For a service registered with zero port, only SRV record is initialized. Don't allow any updates. - if (mDNSIPPortIsZero(request->u.servicereg.port)) - { LogMsg("%3d: DNSServiceUpdateRecord: updating the record of a service registered with zero port", request->sd); return(mStatus_BadParamErr); } + // For a service registered with zero port, only SRV record is initialized. Don't allow any updates. + if (mDNSIPPortIsZero(request->u.servicereg.port)) + { LogMsg("%3d: DNSServiceUpdateRecord: updating the record of a service registered with zero port", request->sd); return(mStatus_BadParamErr); } - // update the saved off TXT data for the service - if (hdr->reg_index == TXT_RECORD_INDEX) - { - if (request->u.servicereg.txtdata) - { freeL("service_info txtdata", request->u.servicereg.txtdata); request->u.servicereg.txtdata = NULL; } - if (rdlen > 0) - { - request->u.servicereg.txtdata = mallocL("service_info txtdata", rdlen); - if (!request->u.servicereg.txtdata) FatalError("ERROR: handle_update_request - malloc"); - mDNSPlatformMemCopy(request->u.servicereg.txtdata, rdata, rdlen); - } - request->u.servicereg.txtlen = rdlen; - } + // update the saved off TXT data for the service + if (hdr->reg_index == TXT_RECORD_INDEX) + { + if (request->u.servicereg.txtdata) + { freeL("service_info txtdata", request->u.servicereg.txtdata); request->u.servicereg.txtdata = NULL; } + if (rdlen > 0) + { + request->u.servicereg.txtdata = mallocL("service_info txtdata", rdlen); + if (!request->u.servicereg.txtdata) FatalError("ERROR: handle_update_request - malloc"); + mDNSPlatformMemCopy(request->u.servicereg.txtdata, rdata, rdlen); + } + request->u.servicereg.txtlen = rdlen; + } - // update a record from a service record set - for (i = request->u.servicereg.instances; i; i = i->next) - { - if (hdr->reg_index == TXT_RECORD_INDEX) rr = &i->srs.RR_TXT; - else - { - ExtraResourceRecord *e; - for (e = i->srs.Extras; e; e = e->next) - if (e->ClientID == hdr->reg_index) { rr = &e->r; break; } - } + // update a record from a service record set + for (i = request->u.servicereg.instances; i; i = i->next) + { + if (hdr->reg_index == TXT_RECORD_INDEX) rr = &i->srs.RR_TXT; + else + { + ExtraResourceRecord *e; + for (e = i->srs.Extras; e; e = e->next) + if (e->ClientID == hdr->reg_index) { rr = &e->r; break; } + } - if (!rr) { result = mStatus_BadReferenceErr; goto end; } - result = update_record(rr, rdlen, rdata, ttl, &i->external_advertise); - if (result && i->default_local) goto end; - else result = mStatus_NoError; // suppress non-local default errors - } + if (!rr) { result = mStatus_BadReferenceErr; goto end; } + result = update_record(rr, rdlen, rdata, ttl, &i->external_advertise); + if (result && i->default_local) goto end; + else result = mStatus_NoError; // suppress non-local default errors + } end: - if (request->terminate == regservice_termination_callback) - LogOperation("%3d: DNSServiceUpdateRecord(%##s, %s)", request->sd, - (request->u.servicereg.instances) ? request->u.servicereg.instances->srs.RR_SRV.resrec.name->c : NULL, - rr ? DNSTypeName(rr->resrec.rrtype) : ""); + if (request->terminate == regservice_termination_callback) + LogOperation("%3d: DNSServiceUpdateRecord(%##s, %s)", request->sd, + (request->u.servicereg.instances) ? request->u.servicereg.instances->srs.RR_SRV.resrec.name->c : NULL, + rr ? DNSTypeName(rr->resrec.rrtype) : ""); - return(result); - } + return(result); + } // remove a resource record registered via DNSServiceRegisterRecord() mDNSlocal mStatus remove_record(request_state *request) - { - mStatus err = mStatus_UnknownErr; - registered_record_entry *e, **ptr = &request->u.reg_recs; + { + mStatus err = mStatus_UnknownErr; + registered_record_entry *e, **ptr = &request->u.reg_recs; - while (*ptr && (*ptr)->key != request->hdr.reg_index) ptr = &(*ptr)->next; - if (!*ptr) { LogMsg("%3d: DNSServiceRemoveRecord(%u) not found", request->sd, request->hdr.reg_index); return mStatus_BadReferenceErr; } - e = *ptr; - *ptr = e->next; // unlink + while (*ptr && (*ptr)->key != request->hdr.reg_index) ptr = &(*ptr)->next; + if (!*ptr) { LogMsg("%3d: DNSServiceRemoveRecord(%u) not found", request->sd, request->hdr.reg_index); return mStatus_BadReferenceErr; } + e = *ptr; + *ptr = e->next; // unlink - LogOperation("%3d: DNSServiceRemoveRecord(%u %s)", request->sd, e->key, RRDisplayString(&mDNSStorage, &e->rr->resrec)); - e->rr->RecordContext = NULL; - if (e->external_advertise) - { - external_stop_advertising_service(&e->rr->resrec); - e->external_advertise = mDNSfalse; - } - err = mDNS_Deregister(&mDNSStorage, e->rr); // Will free e->rr for us; we're responsible for freeing e - if (err) - { - LogMsg("ERROR: remove_record, mDNS_Deregister: %d", err); - freeL("registered_record_entry AuthRecord remove_record", e->rr); - } - - freeL("registered_record_entry remove_record", e); - return err; - } + LogOperation("%3d: DNSServiceRemoveRecord(%u %s)", request->sd, e->key, RRDisplayString(&mDNSStorage, &e->rr->resrec)); + e->rr->RecordContext = NULL; + if (e->external_advertise) + { + external_stop_advertising_service(&e->rr->resrec); + e->external_advertise = mDNSfalse; + } + err = mDNS_Deregister(&mDNSStorage, e->rr); // Will free e->rr for us; we're responsible for freeing e + if (err) + { + LogMsg("ERROR: remove_record, mDNS_Deregister: %d", err); + freeL("registered_record_entry AuthRecord remove_record", e->rr); + } + + freeL("registered_record_entry remove_record", e); + return err; + } mDNSlocal mStatus remove_extra(const request_state *const request, service_instance *const serv, mDNSu16 *const rrtype) - { - mStatus err = mStatus_BadReferenceErr; - ExtraResourceRecord *ptr; + { + mStatus err = mStatus_BadReferenceErr; + ExtraResourceRecord *ptr; - for (ptr = serv->srs.Extras; ptr; ptr = ptr->next) - { - if (ptr->ClientID == request->hdr.reg_index) // found match - { - *rrtype = ptr->r.resrec.rrtype; - if (serv->external_advertise) external_stop_advertising_service(&ptr->r.resrec); - err = mDNS_RemoveRecordFromService(&mDNSStorage, &serv->srs, ptr, FreeExtraRR, ptr); - break; - } - } - return err; - } + for (ptr = serv->srs.Extras; ptr; ptr = ptr->next) + { + if (ptr->ClientID == request->hdr.reg_index) // found match + { + *rrtype = ptr->r.resrec.rrtype; + if (serv->external_advertise) external_stop_advertising_service(&ptr->r.resrec); + err = mDNS_RemoveRecordFromService(&mDNSStorage, &serv->srs, ptr, FreeExtraRR, ptr); + break; + } + } + return err; + } mDNSlocal mStatus handle_removerecord_request(request_state *request) - { - mStatus err = mStatus_BadReferenceErr; - get_flags(&request->msgptr, request->msgend); // flags unused + { + mStatus err = mStatus_BadReferenceErr; + get_flags(&request->msgptr, request->msgend); // flags unused - if (!request->msgptr) { LogMsg("%3d: DNSServiceRemoveRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } + if (!request->msgptr) { LogMsg("%3d: DNSServiceRemoveRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } - // If this is a shared connection, check if the operation actually applies to a subordinate request_state object - if (request->terminate == connection_termination) request = LocateSubordinateRequest(request); + // If this is a shared connection, check if the operation actually applies to a subordinate request_state object + if (request->terminate == connection_termination) request = LocateSubordinateRequest(request); - if (request->terminate == connection_termination) - err = remove_record(request); // remove individually registered record - else if (request->terminate != regservice_termination_callback) - { LogMsg("%3d: DNSServiceRemoveRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); } - else - { - service_instance *i; - mDNSu16 rrtype = 0; - LogOperation("%3d: DNSServiceRemoveRecord(%##s, %s)", request->sd, - (request->u.servicereg.instances) ? request->u.servicereg.instances->srs.RR_SRV.resrec.name->c : NULL, - rrtype ? DNSTypeName(rrtype) : ""); - for (i = request->u.servicereg.instances; i; i = i->next) - { - err = remove_extra(request, i, &rrtype); - if (err && i->default_local) break; - else err = mStatus_NoError; // suppress non-local default errors - } - } + if (request->terminate == connection_termination) + err = remove_record(request); // remove individually registered record + else if (request->terminate != regservice_termination_callback) + { LogMsg("%3d: DNSServiceRemoveRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); } + else + { + service_instance *i; + mDNSu16 rrtype = 0; + LogOperation("%3d: DNSServiceRemoveRecord(%##s, %s)", request->sd, + (request->u.servicereg.instances) ? request->u.servicereg.instances->srs.RR_SRV.resrec.name->c : NULL, + rrtype ? DNSTypeName(rrtype) : ""); + for (i = request->u.servicereg.instances; i; i = i->next) + { + err = remove_extra(request, i, &rrtype); + if (err && i->default_local) break; + else err = mStatus_NoError; // suppress non-local default errors + } + } - return(err); - } + return(err); + } // If there's a comma followed by another character, // FindFirstSubType overwrites the comma with a nul and returns the pointer to the next character. // Otherwise, it returns a pointer to the final nul at the end of the string mDNSlocal char *FindFirstSubType(char *p) - { - while (*p) - { - if (p[0] == '\\' && p[1]) p += 2; - else if (p[0] == ',' && p[1]) { *p++ = 0; return(p); } - else p++; - } - return(p); - } + { + while (*p) + { + if (p[0] == '\\' && p[1]) p += 2; + else if (p[0] == ',' && p[1]) { *p++ = 0; return(p); } + else p++; + } + return(p); + } // If there's a comma followed by another character, // FindNextSubType overwrites the comma with a nul and returns the pointer to the next character. // If it finds an illegal unescaped dot in the subtype name, it returns mDNSNULL // Otherwise, it returns a pointer to the final nul at the end of the string mDNSlocal char *FindNextSubType(char *p) - { - while (*p) - { - if (p[0] == '\\' && p[1]) // If escape character - p += 2; // ignore following character - else if (p[0] == ',') // If we found a comma - { - if (p[1]) *p++ = 0; - return(p); - } - else if (p[0] == '.') - return(mDNSNULL); - else p++; - } - return(p); - } + { + while (*p) + { + if (p[0] == '\\' && p[1]) // If escape character + p += 2; // ignore following character + else if (p[0] == ',') // If we found a comma + { + if (p[1]) *p++ = 0; + return(p); + } + else if (p[0] == '.') + return(mDNSNULL); + else p++; + } + return(p); + } // Returns -1 if illegal subtype found mDNSexport mDNSs32 ChopSubTypes(char *regtype) - { - mDNSs32 NumSubTypes = 0; - char *stp = FindFirstSubType(regtype); - while (stp && *stp) // If we found a comma... - { - if (*stp == ',') return(-1); - NumSubTypes++; - stp = FindNextSubType(stp); - } - if (!stp) return(-1); - return(NumSubTypes); - } + { + mDNSs32 NumSubTypes = 0; + char *stp = FindFirstSubType(regtype); + while (stp && *stp) // If we found a comma... + { + if (*stp == ',') return(-1); + NumSubTypes++; + stp = FindNextSubType(stp); + } + if (!stp) return(-1); + return(NumSubTypes); + } mDNSexport AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p) - { - AuthRecord *st = mDNSNULL; - if (NumSubTypes) - { - mDNSs32 i; - st = mallocL("ServiceSubTypes", NumSubTypes * sizeof(AuthRecord)); - if (!st) return(mDNSNULL); - for (i = 0; i < NumSubTypes; i++) - { - mDNS_SetupResourceRecord(&st[i], mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, AuthRecordAny, mDNSNULL, mDNSNULL); - while (*p) p++; - p++; - if (!MakeDomainNameFromDNSNameString(&st[i].namestorage, p)) - { freeL("ServiceSubTypes", st); return(mDNSNULL); } - } - } - return(st); - } + { + AuthRecord *st = mDNSNULL; + if (NumSubTypes) + { + mDNSs32 i; + st = mallocL("ServiceSubTypes", NumSubTypes * sizeof(AuthRecord)); + if (!st) return(mDNSNULL); + for (i = 0; i < NumSubTypes; i++) + { + mDNS_SetupResourceRecord(&st[i], mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, AuthRecordAny, mDNSNULL, mDNSNULL); + while (*p) p++; + p++; + if (!MakeDomainNameFromDNSNameString(&st[i].namestorage, p)) + { freeL("ServiceSubTypes", st); return(mDNSNULL); } + } + } + return(st); + } mDNSlocal mStatus register_service_instance(request_state *request, const domainname *domain) - { - service_instance **ptr, *instance; - const int extra_size = (request->u.servicereg.txtlen > sizeof(RDataBody)) ? (request->u.servicereg.txtlen - sizeof(RDataBody)) : 0; - const mDNSBool DomainIsLocal = SameDomainName(domain, &localdomain); - mStatus result; - mDNSInterfaceID interfaceID = request->u.servicereg.InterfaceID; - mDNSu32 regFlags = 0; + { + service_instance **ptr, *instance; + const int extra_size = (request->u.servicereg.txtlen > sizeof(RDataBody)) ? (request->u.servicereg.txtlen - sizeof(RDataBody)) : 0; + const mDNSBool DomainIsLocal = SameDomainName(domain, &localdomain); + mStatus result; + mDNSInterfaceID interfaceID = request->u.servicereg.InterfaceID; + mDNSu32 regFlags = 0; - if (interfaceID == mDNSInterface_P2P) - { - interfaceID = mDNSInterface_Any; - regFlags |= regFlagIncludeP2P; - } - else if (request->flags & kDNSServiceFlagsIncludeP2P) - regFlags |= regFlagIncludeP2P; + if (interfaceID == mDNSInterface_P2P) + { + interfaceID = mDNSInterface_Any; + regFlags |= regFlagIncludeP2P; + } + else if (request->flags & kDNSServiceFlagsIncludeP2P) + regFlags |= regFlagIncludeP2P; - // client guarantees that record names are unique - if (request->flags & kDNSServiceFlagsForce) - regFlags |= regFlagKnownUnique; + // client guarantees that record names are unique + if (request->flags & kDNSServiceFlagsForce) + regFlags |= regFlagKnownUnique; - // If the client specified an interface, but no domain, then we honor the specified interface for the "local" (mDNS) - // registration but for the wide-area registrations we don't (currently) have any concept of a wide-area unicast - // registrations scoped to a specific interface, so for the automatic domains we add we must *not* specify an interface. - // (Specifying an interface with an apparently wide-area domain (i.e. something other than "local") - // currently forces the registration to use mDNS multicast despite the apparently wide-area domain.) - if (request->u.servicereg.default_domain && !DomainIsLocal) interfaceID = mDNSInterface_Any; + // If the client specified an interface, but no domain, then we honor the specified interface for the "local" (mDNS) + // registration but for the wide-area registrations we don't (currently) have any concept of a wide-area unicast + // registrations scoped to a specific interface, so for the automatic domains we add we must *not* specify an interface. + // (Specifying an interface with an apparently wide-area domain (i.e. something other than "local") + // currently forces the registration to use mDNS multicast despite the apparently wide-area domain.) + if (request->u.servicereg.default_domain && !DomainIsLocal) interfaceID = mDNSInterface_Any; - for (ptr = &request->u.servicereg.instances; *ptr; ptr = &(*ptr)->next) - { - if (SameDomainName(&(*ptr)->domain, domain)) - { - LogMsg("register_service_instance: domain %##s already registered for %#s.%##s", - domain->c, &request->u.servicereg.name, &request->u.servicereg.type); - return mStatus_AlreadyRegistered; - } - } + for (ptr = &request->u.servicereg.instances; *ptr; ptr = &(*ptr)->next) + { + if (SameDomainName(&(*ptr)->domain, domain)) + { + LogMsg("register_service_instance: domain %##s already registered for %#s.%##s", + domain->c, &request->u.servicereg.name, &request->u.servicereg.type); + return mStatus_AlreadyRegistered; + } + } - if (mDNSStorage.KnownBugs & mDNS_KnownBug_LimitedIPv6) - { - // Special-case hack: On Mac OS X 10.6.x and earlier we don't advertise SMB service in AutoTunnel domains, - // because AutoTunnel services have to support IPv6, and in Mac OS X 10.6.x the SMB server does not. - // BTMM: Don't advertise SMB with BTMM because it doesn't support IPv6 - if (SameDomainName(&request->u.servicereg.type, (const domainname *) "\x4" "_smb" "\x4" "_tcp")) - { - DomainAuthInfo *AuthInfo = GetAuthInfoForName(&mDNSStorage, domain); - if (AuthInfo && AuthInfo->AutoTunnel) return(kDNSServiceErr_Unsupported); - } - } + if (mDNSStorage.KnownBugs & mDNS_KnownBug_LimitedIPv6) + { + // Special-case hack: On Mac OS X 10.6.x and earlier we don't advertise SMB service in AutoTunnel domains, + // because AutoTunnel services have to support IPv6, and in Mac OS X 10.6.x the SMB server does not. + // BTMM: Don't advertise SMB with BTMM because it doesn't support IPv6 + if (SameDomainName(&request->u.servicereg.type, (const domainname *) "\x4" "_smb" "\x4" "_tcp")) + { + DomainAuthInfo *AuthInfo = GetAuthInfoForName(&mDNSStorage, domain); + if (AuthInfo && AuthInfo->AutoTunnel) return(kDNSServiceErr_Unsupported); + } + } - instance = mallocL("service_instance", sizeof(*instance) + extra_size); - if (!instance) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; } + instance = mallocL("service_instance", sizeof(*instance) + extra_size); + if (!instance) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; } - instance->next = mDNSNULL; - instance->request = request; - instance->subtypes = AllocateSubTypes(request->u.servicereg.num_subtypes, request->u.servicereg.type_as_string); - instance->renameonmemfree = 0; - instance->clientnotified = mDNSfalse; - instance->default_local = (request->u.servicereg.default_domain && DomainIsLocal); - instance->external_advertise = mDNSfalse; - AssignDomainName(&instance->domain, domain); + instance->next = mDNSNULL; + instance->request = request; + instance->subtypes = AllocateSubTypes(request->u.servicereg.num_subtypes, request->u.servicereg.type_as_string); + instance->renameonmemfree = 0; + instance->clientnotified = mDNSfalse; + instance->default_local = (request->u.servicereg.default_domain && DomainIsLocal); + instance->external_advertise = mDNSfalse; + AssignDomainName(&instance->domain, domain); - if (request->u.servicereg.num_subtypes && !instance->subtypes) - { unlink_and_free_service_instance(instance); instance = NULL; FatalError("ERROR: malloc"); } + if (request->u.servicereg.num_subtypes && !instance->subtypes) + { unlink_and_free_service_instance(instance); instance = NULL; FatalError("ERROR: malloc"); } - result = mDNS_RegisterService(&mDNSStorage, &instance->srs, - &request->u.servicereg.name, &request->u.servicereg.type, domain, - request->u.servicereg.host.c[0] ? &request->u.servicereg.host : NULL, - request->u.servicereg.port, - request->u.servicereg.txtdata, request->u.servicereg.txtlen, - instance->subtypes, request->u.servicereg.num_subtypes, - interfaceID, regservice_callback, instance, regFlags); + result = mDNS_RegisterService(&mDNSStorage, &instance->srs, + &request->u.servicereg.name, &request->u.servicereg.type, domain, + request->u.servicereg.host.c[0] ? &request->u.servicereg.host : NULL, + request->u.servicereg.port, + request->u.servicereg.txtdata, request->u.servicereg.txtlen, + instance->subtypes, request->u.servicereg.num_subtypes, + interfaceID, regservice_callback, instance, regFlags); - if (!result) - { - *ptr = instance; // Append this to the end of our request->u.servicereg.instances list - LogOperation("%3d: DNSServiceRegister(%##s, %u) ADDED", - instance->request->sd, instance->srs.RR_SRV.resrec.name->c, mDNSVal16(request->u.servicereg.port)); - } - else - { - LogMsg("register_service_instance %#s.%##s%##s error %d", - &request->u.servicereg.name, &request->u.servicereg.type, domain->c, result); - unlink_and_free_service_instance(instance); - } + if (!result) + { + *ptr = instance; // Append this to the end of our request->u.servicereg.instances list + LogOperation("%3d: DNSServiceRegister(%##s, %u) ADDED", + instance->request->sd, instance->srs.RR_SRV.resrec.name->c, mDNSVal16(request->u.servicereg.port)); + } + else + { + LogMsg("register_service_instance %#s.%##s%##s error %d", + &request->u.servicereg.name, &request->u.servicereg.type, domain->c, result); + unlink_and_free_service_instance(instance); + } - return result; - } + return result; + } mDNSlocal void udsserver_default_reg_domain_changed(const DNameListElem *const d, const mDNSBool add) - { - request_state *request; + { + request_state *request; #if APPLE_OSX_mDNSResponder - machserver_automatic_registration_domain_changed(&d->name, add); + machserver_automatic_registration_domain_changed(&d->name, add); #endif // APPLE_OSX_mDNSResponder - LogMsg("%s registration domain %##s", add ? "Adding" : "Removing", d->name.c); - for (request = all_requests; request; request = request->next) - { - if (request->terminate != regservice_termination_callback) continue; - if (!request->u.servicereg.default_domain) continue; - if (!d->uid || SystemUID(request->uid) || request->uid == d->uid) - { - service_instance **ptr = &request->u.servicereg.instances; - while (*ptr && !SameDomainName(&(*ptr)->domain, &d->name)) ptr = &(*ptr)->next; - if (add) - { - // If we don't already have this domain in our list for this registration, add it now - if (!*ptr) register_service_instance(request, &d->name); - else debugf("udsserver_default_reg_domain_changed %##s already in list, not re-adding", &d->name); - } - else - { - // Normally we should not fail to find the specified instance - // One case where this can happen is if a uDNS update fails for some reason, - // and regservice_callback then calls unlink_and_free_service_instance and disposes of that instance. - if (!*ptr) - LogMsg("udsserver_default_reg_domain_changed domain %##s not found for service %#s type %s", - &d->name, request->u.servicereg.name.c, request->u.servicereg.type_as_string); - else - { - DNameListElem *p; - for (p = AutoRegistrationDomains; p; p=p->next) - if (!p->uid || SystemUID(request->uid) || request->uid == p->uid) - if (SameDomainName(&d->name, &p->name)) break; - if (p) debugf("udsserver_default_reg_domain_changed %##s still in list, not removing", &d->name); - else - { - mStatus err; - service_instance *si = *ptr; - *ptr = si->next; - if (si->clientnotified) SendServiceRemovalNotification(&si->srs); // Do this *before* clearing si->request backpointer - // Now that we've cut this service_instance from the list, we MUST clear the si->request backpointer. - // Otherwise what can happen is this: While our mDNS_DeregisterService is in the - // process of completing asynchronously, the client cancels the entire operation, so - // regservice_termination_callback then runs through the whole list deregistering each - // instance, clearing the backpointers, and then disposing the parent request_state object. - // However, because this service_instance isn't in the list any more, regservice_termination_callback - // has no way to find it and clear its backpointer, and then when our mDNS_DeregisterService finally - // completes later with a mStatus_MemFree message, it calls unlink_and_free_service_instance() with - // a service_instance with a stale si->request backpointer pointing to memory that's already been freed. - si->request = NULL; - err = mDNS_DeregisterService(&mDNSStorage, &si->srs); - if (err) { LogMsg("udsserver_default_reg_domain_changed err %d", err); unlink_and_free_service_instance(si); } - } - } - } - } - } - } + LogMsg("%s registration domain %##s", add ? "Adding" : "Removing", d->name.c); + for (request = all_requests; request; request = request->next) + { + if (request->terminate != regservice_termination_callback) continue; + if (!request->u.servicereg.default_domain) continue; + if (!d->uid || SystemUID(request->uid) || request->uid == d->uid) + { + service_instance **ptr = &request->u.servicereg.instances; + while (*ptr && !SameDomainName(&(*ptr)->domain, &d->name)) ptr = &(*ptr)->next; + if (add) + { + // If we don't already have this domain in our list for this registration, add it now + if (!*ptr) register_service_instance(request, &d->name); + else debugf("udsserver_default_reg_domain_changed %##s already in list, not re-adding", &d->name); + } + else + { + // Normally we should not fail to find the specified instance + // One case where this can happen is if a uDNS update fails for some reason, + // and regservice_callback then calls unlink_and_free_service_instance and disposes of that instance. + if (!*ptr) + LogMsg("udsserver_default_reg_domain_changed domain %##s not found for service %#s type %s", + &d->name, request->u.servicereg.name.c, request->u.servicereg.type_as_string); + else + { + DNameListElem *p; + for (p = AutoRegistrationDomains; p; p=p->next) + if (!p->uid || SystemUID(request->uid) || request->uid == p->uid) + if (SameDomainName(&d->name, &p->name)) break; + if (p) debugf("udsserver_default_reg_domain_changed %##s still in list, not removing", &d->name); + else + { + mStatus err; + service_instance *si = *ptr; + *ptr = si->next; + if (si->clientnotified) SendServiceRemovalNotification(&si->srs); // Do this *before* clearing si->request backpointer + // Now that we've cut this service_instance from the list, we MUST clear the si->request backpointer. + // Otherwise what can happen is this: While our mDNS_DeregisterService is in the + // process of completing asynchronously, the client cancels the entire operation, so + // regservice_termination_callback then runs through the whole list deregistering each + // instance, clearing the backpointers, and then disposing the parent request_state object. + // However, because this service_instance isn't in the list any more, regservice_termination_callback + // has no way to find it and clear its backpointer, and then when our mDNS_DeregisterService finally + // completes later with a mStatus_MemFree message, it calls unlink_and_free_service_instance() with + // a service_instance with a stale si->request backpointer pointing to memory that's already been freed. + si->request = NULL; + err = mDNS_DeregisterService(&mDNSStorage, &si->srs); + if (err) { LogMsg("udsserver_default_reg_domain_changed err %d", err); unlink_and_free_service_instance(si); } + } + } + } + } + } + } mDNSlocal mStatus handle_regservice_request(request_state *request) - { - char name[256]; // Lots of spare space for extra-long names that we'll auto-truncate down to 63 bytes - char domain[MAX_ESCAPED_DOMAIN_NAME], host[MAX_ESCAPED_DOMAIN_NAME]; - char type_as_string[MAX_ESCAPED_DOMAIN_NAME]; - domainname d, srv; - mStatus err; + { + char name[256]; // Lots of spare space for extra-long names that we'll auto-truncate down to 63 bytes + char domain[MAX_ESCAPED_DOMAIN_NAME], host[MAX_ESCAPED_DOMAIN_NAME]; + char type_as_string[MAX_ESCAPED_DOMAIN_NAME]; + domainname d, srv; + mStatus err; - DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); - mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); - mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); - if (interfaceIndex && !InterfaceID) - { LogMsg("ERROR: handle_regservice_request - Couldn't find interfaceIndex %d", interfaceIndex); return(mStatus_BadParamErr); } + DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); + mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); + mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); + if (interfaceIndex && !InterfaceID) + { LogMsg("ERROR: handle_regservice_request - Couldn't find interfaceIndex %d", interfaceIndex); return(mStatus_BadParamErr); } - if (get_string(&request->msgptr, request->msgend, name, sizeof(name)) < 0 || - get_string(&request->msgptr, request->msgend, type_as_string, MAX_ESCAPED_DOMAIN_NAME) < 0 || - get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0 || - get_string(&request->msgptr, request->msgend, host, MAX_ESCAPED_DOMAIN_NAME) < 0) - { LogMsg("ERROR: handle_regservice_request - Couldn't read name/regtype/domain"); return(mStatus_BadParamErr); } + if (get_string(&request->msgptr, request->msgend, name, sizeof(name)) < 0 || + get_string(&request->msgptr, request->msgend, type_as_string, MAX_ESCAPED_DOMAIN_NAME) < 0 || + get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0 || + get_string(&request->msgptr, request->msgend, host, MAX_ESCAPED_DOMAIN_NAME) < 0) + { LogMsg("ERROR: handle_regservice_request - Couldn't read name/regtype/domain"); return(mStatus_BadParamErr); } - request->flags = flags; - request->u.servicereg.InterfaceID = InterfaceID; - request->u.servicereg.instances = NULL; - request->u.servicereg.txtlen = 0; - request->u.servicereg.txtdata = NULL; - mDNSPlatformStrCopy(request->u.servicereg.type_as_string, type_as_string); + request->flags = flags; + request->u.servicereg.InterfaceID = InterfaceID; + request->u.servicereg.instances = NULL; + request->u.servicereg.txtlen = 0; + request->u.servicereg.txtdata = NULL; + mDNSPlatformStrCopy(request->u.servicereg.type_as_string, type_as_string); - if (request->msgptr + 2 > request->msgend) request->msgptr = NULL; - else - { - request->u.servicereg.port.b[0] = *request->msgptr++; - request->u.servicereg.port.b[1] = *request->msgptr++; - } + if (request->msgptr + 2 > request->msgend) request->msgptr = NULL; + else + { + request->u.servicereg.port.b[0] = *request->msgptr++; + request->u.servicereg.port.b[1] = *request->msgptr++; + } - request->u.servicereg.txtlen = get_uint16(&request->msgptr, request->msgend); - if (request->u.servicereg.txtlen) - { - request->u.servicereg.txtdata = mallocL("service_info txtdata", request->u.servicereg.txtlen); - if (!request->u.servicereg.txtdata) FatalError("ERROR: handle_regservice_request - malloc"); - mDNSPlatformMemCopy(request->u.servicereg.txtdata, get_rdata(&request->msgptr, request->msgend, request->u.servicereg.txtlen), request->u.servicereg.txtlen); - } + request->u.servicereg.txtlen = get_uint16(&request->msgptr, request->msgend); + if (request->u.servicereg.txtlen) + { + request->u.servicereg.txtdata = mallocL("service_info txtdata", request->u.servicereg.txtlen); + if (!request->u.servicereg.txtdata) FatalError("ERROR: handle_regservice_request - malloc"); + mDNSPlatformMemCopy(request->u.servicereg.txtdata, get_rdata(&request->msgptr, request->msgend, request->u.servicereg.txtlen), request->u.servicereg.txtlen); + } - if (!request->msgptr) { LogMsg("%3d: DNSServiceRegister(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } + if (!request->msgptr) { LogMsg("%3d: DNSServiceRegister(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } - // Check for sub-types after the service type - request->u.servicereg.num_subtypes = ChopSubTypes(request->u.servicereg.type_as_string); // Note: Modifies regtype string to remove trailing subtypes - if (request->u.servicereg.num_subtypes < 0) - { LogMsg("ERROR: handle_regservice_request - ChopSubTypes failed %s", request->u.servicereg.type_as_string); return(mStatus_BadParamErr); } + // Check for sub-types after the service type + request->u.servicereg.num_subtypes = ChopSubTypes(request->u.servicereg.type_as_string); // Note: Modifies regtype string to remove trailing subtypes + if (request->u.servicereg.num_subtypes < 0) + { LogMsg("ERROR: handle_regservice_request - ChopSubTypes failed %s", request->u.servicereg.type_as_string); return(mStatus_BadParamErr); } - // Don't try to construct "domainname t" until *after* ChopSubTypes has worked its magic - if (!*request->u.servicereg.type_as_string || !MakeDomainNameFromDNSNameString(&request->u.servicereg.type, request->u.servicereg.type_as_string)) - { LogMsg("ERROR: handle_regservice_request - type_as_string bad %s", request->u.servicereg.type_as_string); return(mStatus_BadParamErr); } + // Don't try to construct "domainname t" until *after* ChopSubTypes has worked its magic + if (!*request->u.servicereg.type_as_string || !MakeDomainNameFromDNSNameString(&request->u.servicereg.type, request->u.servicereg.type_as_string)) + { LogMsg("ERROR: handle_regservice_request - type_as_string bad %s", request->u.servicereg.type_as_string); return(mStatus_BadParamErr); } - if (!name[0]) - { - request->u.servicereg.name = mDNSStorage.nicelabel; - request->u.servicereg.autoname = mDNStrue; - } - else - { - // If the client is allowing AutoRename, then truncate name to legal length before converting it to a DomainLabel - if ((flags & kDNSServiceFlagsNoAutoRename) == 0) - { - int newlen = TruncateUTF8ToLength((mDNSu8*)name, mDNSPlatformStrLen(name), MAX_DOMAIN_LABEL); - name[newlen] = 0; - } - if (!MakeDomainLabelFromLiteralString(&request->u.servicereg.name, name)) - { LogMsg("ERROR: handle_regservice_request - name bad %s", name); return(mStatus_BadParamErr); } - request->u.servicereg.autoname = mDNSfalse; - } + if (!name[0]) + { + request->u.servicereg.name = mDNSStorage.nicelabel; + request->u.servicereg.autoname = mDNStrue; + } + else + { + // If the client is allowing AutoRename, then truncate name to legal length before converting it to a DomainLabel + if ((flags & kDNSServiceFlagsNoAutoRename) == 0) + { + int newlen = TruncateUTF8ToLength((mDNSu8*)name, mDNSPlatformStrLen(name), MAX_DOMAIN_LABEL); + name[newlen] = 0; + } + if (!MakeDomainLabelFromLiteralString(&request->u.servicereg.name, name)) + { LogMsg("ERROR: handle_regservice_request - name bad %s", name); return(mStatus_BadParamErr); } + request->u.servicereg.autoname = mDNSfalse; + } - if (*domain) - { - request->u.servicereg.default_domain = mDNSfalse; - if (!MakeDomainNameFromDNSNameString(&d, domain)) - { LogMsg("ERROR: handle_regservice_request - domain bad %s", domain); return(mStatus_BadParamErr); } - } - else - { - request->u.servicereg.default_domain = mDNStrue; - MakeDomainNameFromDNSNameString(&d, "local."); - } + if (*domain) + { + request->u.servicereg.default_domain = mDNSfalse; + if (!MakeDomainNameFromDNSNameString(&d, domain)) + { LogMsg("ERROR: handle_regservice_request - domain bad %s", domain); return(mStatus_BadParamErr); } + } + else + { + request->u.servicereg.default_domain = mDNStrue; + MakeDomainNameFromDNSNameString(&d, "local."); + } - if (!ConstructServiceName(&srv, &request->u.servicereg.name, &request->u.servicereg.type, &d)) - { + if (!ConstructServiceName(&srv, &request->u.servicereg.name, &request->u.servicereg.type, &d)) + { LogMsg("ERROR: handle_regservice_request - Couldn't ConstructServiceName from, '%#s' '%##s' '%##s'", - request->u.servicereg.name.c, request->u.servicereg.type.c, d.c); return(mStatus_BadParamErr); - } + request->u.servicereg.name.c, request->u.servicereg.type.c, d.c); return(mStatus_BadParamErr); + } - if (!MakeDomainNameFromDNSNameString(&request->u.servicereg.host, host)) - { LogMsg("ERROR: handle_regservice_request - host bad %s", host); return(mStatus_BadParamErr); } - request->u.servicereg.autorename = (flags & kDNSServiceFlagsNoAutoRename ) == 0; - request->u.servicereg.allowremotequery = (flags & kDNSServiceFlagsAllowRemoteQuery) != 0; + if (!MakeDomainNameFromDNSNameString(&request->u.servicereg.host, host)) + { LogMsg("ERROR: handle_regservice_request - host bad %s", host); return(mStatus_BadParamErr); } + request->u.servicereg.autorename = (flags & kDNSServiceFlagsNoAutoRename ) == 0; + request->u.servicereg.allowremotequery = (flags & kDNSServiceFlagsAllowRemoteQuery) != 0; - // Some clients use mDNS for lightweight copy protection, registering a pseudo-service with - // a port number of zero. When two instances of the protected client are allowed to run on one - // machine, we don't want to see misleading "Bogus client" messages in syslog and the console. - if (!mDNSIPPortIsZero(request->u.servicereg.port)) - { - int count = CountExistingRegistrations(&srv, request->u.servicereg.port); - if (count) - LogMsg("Client application registered %d identical instances of service %##s port %u.", - count+1, srv.c, mDNSVal16(request->u.servicereg.port)); - } + // Some clients use mDNS for lightweight copy protection, registering a pseudo-service with + // a port number of zero. When two instances of the protected client are allowed to run on one + // machine, we don't want to see misleading "Bogus client" messages in syslog and the console. + if (!mDNSIPPortIsZero(request->u.servicereg.port)) + { + int count = CountExistingRegistrations(&srv, request->u.servicereg.port); + if (count) + LogMsg("Client application registered %d identical instances of service %##s port %u.", + count+1, srv.c, mDNSVal16(request->u.servicereg.port)); + } - LogOperation("%3d: DNSServiceRegister(%X, %d, \"%s\", \"%s\", \"%s\", \"%s\", %u) START", - request->sd, flags, interfaceIndex, name, request->u.servicereg.type_as_string, domain, host, mDNSVal16(request->u.servicereg.port)); + LogOperation("%3d: DNSServiceRegister(%X, %d, \"%s\", \"%s\", \"%s\", \"%s\", %u) START", + request->sd, flags, interfaceIndex, name, request->u.servicereg.type_as_string, domain, host, mDNSVal16(request->u.servicereg.port)); - // We need to unconditionally set request->terminate, because even if we didn't successfully - // start any registrations right now, subsequent configuration changes may cause successful - // registrations to be added, and we'll need to cancel them before freeing this memory. - // We also need to set request->terminate first, before adding additional service instances, - // because the uds_validatelists uses the request->terminate function pointer to determine - // what kind of request this is, and therefore what kind of list validation is required. - request->terminate = regservice_termination_callback; + // We need to unconditionally set request->terminate, because even if we didn't successfully + // start any registrations right now, subsequent configuration changes may cause successful + // registrations to be added, and we'll need to cancel them before freeing this memory. + // We also need to set request->terminate first, before adding additional service instances, + // because the uds_validatelists uses the request->terminate function pointer to determine + // what kind of request this is, and therefore what kind of list validation is required. + request->terminate = regservice_termination_callback; - err = register_service_instance(request, &d); + err = register_service_instance(request, &d); #if 0 - err = AuthorizedDomain(request, &d, AutoRegistrationDomains) ? register_service_instance(request, &d) : mStatus_NoError; + err = AuthorizedDomain(request, &d, AutoRegistrationDomains) ? register_service_instance(request, &d) : mStatus_NoError; #endif - if (!err) - { - if (request->u.servicereg.autoname) UpdateDeviceInfoRecord(&mDNSStorage); + if (!err) + { + if (request->u.servicereg.autoname) UpdateDeviceInfoRecord(&mDNSStorage); - if (!*domain) - { - DNameListElem *ptr; - // Note that we don't report errors for non-local, non-explicit domains - for (ptr = AutoRegistrationDomains; ptr; ptr = ptr->next) - if (!ptr->uid || SystemUID(request->uid) || request->uid == ptr->uid) - register_service_instance(request, &ptr->name); - } - } + if (!*domain) + { + DNameListElem *ptr; + // Note that we don't report errors for non-local, non-explicit domains + for (ptr = AutoRegistrationDomains; ptr; ptr = ptr->next) + if (!ptr->uid || SystemUID(request->uid) || request->uid == ptr->uid) + register_service_instance(request, &ptr->name); + } + } - return(err); - } + return(err); + } // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -1728,462 +1730,462 @@ mDNSlocal mStatus handle_regservice_request(request_state *request) #endif mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) - { - const DNSServiceFlags flags = AddRecord ? kDNSServiceFlagsAdd : 0; - request_state *req = question->QuestionContext; - reply_state *rep; - (void)m; // Unused + { + const DNSServiceFlags flags = AddRecord ? kDNSServiceFlagsAdd : 0; + request_state *req = question->QuestionContext; + reply_state *rep; + (void)m; // Unused - if (answer->rrtype != kDNSType_PTR) - { LogMsg("%3d: FoundInstance: Should not be called with rrtype %d (not a PTR record)", req->sd, answer->rrtype); return; } + if (answer->rrtype != kDNSType_PTR) + { LogMsg("%3d: FoundInstance: Should not be called with rrtype %d (not a PTR record)", req->sd, answer->rrtype); return; } - if (GenerateNTDResponse(&answer->rdata->u.name, answer->InterfaceID, req, &rep, browse_reply_op, flags, mStatus_NoError) != mStatus_NoError) - { - if (SameDomainName(&req->u.browser.regtype, (const domainname*)"\x09_services\x07_dns-sd\x04_udp")) - { - // Special support to enable the DNSServiceBrowse call made by Bonjour Browser - // Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse - GenerateBonjourBrowserResponse(&answer->rdata->u.name, answer->InterfaceID, req, &rep, browse_reply_op, flags, mStatus_NoError); - goto bonjourbrowserhack; - } + if (GenerateNTDResponse(&answer->rdata->u.name, answer->InterfaceID, req, &rep, browse_reply_op, flags, mStatus_NoError) != mStatus_NoError) + { + if (SameDomainName(&req->u.browser.regtype, (const domainname*)"\x09_services\x07_dns-sd\x04_udp")) + { + // Special support to enable the DNSServiceBrowse call made by Bonjour Browser + // Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse + GenerateBonjourBrowserResponse(&answer->rdata->u.name, answer->InterfaceID, req, &rep, browse_reply_op, flags, mStatus_NoError); + goto bonjourbrowserhack; + } - LogMsg("%3d: FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer", - req->sd, answer->name->c, answer->rdata->u.name.c); - return; - } + LogMsg("%3d: FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer", + req->sd, answer->name->c, answer->rdata->u.name.c); + return; + } bonjourbrowserhack: - LogOperation("%3d: DNSServiceBrowse(%##s, %s) RESULT %s %d: %s", - req->sd, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "Add" : "Rmv", - mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNSfalse), RRDisplayString(m, answer)); + LogOperation("%3d: DNSServiceBrowse(%##s, %s) RESULT %s %d: %s", + req->sd, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "Add" : "Rmv", + mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNSfalse), RRDisplayString(m, answer)); - append_reply(req, rep); - } + append_reply(req, rep); + } mDNSlocal mStatus add_domain_to_browser(request_state *info, const domainname *d) - { - browser_t *b, *p; - mStatus err; + { + browser_t *b, *p; + mStatus err; - for (p = info->u.browser.browsers; p; p = p->next) - { - if (SameDomainName(&p->domain, d)) - { debugf("add_domain_to_browser %##s already in list", d->c); return mStatus_AlreadyRegistered; } - } + for (p = info->u.browser.browsers; p; p = p->next) + { + if (SameDomainName(&p->domain, d)) + { debugf("add_domain_to_browser %##s already in list", d->c); return mStatus_AlreadyRegistered; } + } - b = mallocL("browser_t", sizeof(*b)); - if (!b) return mStatus_NoMemoryErr; - AssignDomainName(&b->domain, d); - err = mDNS_StartBrowse(&mDNSStorage, &b->q, - &info->u.browser.regtype, d, info->u.browser.interface_id, info->u.browser.ForceMCast, FoundInstance, info); - if (err) - { - LogMsg("mDNS_StartBrowse returned %d for type %##s domain %##s", err, info->u.browser.regtype.c, d->c); - freeL("browser_t/add_domain_to_browser", b); - } - else - { - b->next = info->u.browser.browsers; - info->u.browser.browsers = b; - LogOperation("%3d: DNSServiceBrowse(%##s) START", info->sd, b->q.qname.c); - if (info->u.browser.interface_id == mDNSInterface_P2P || (!info->u.browser.interface_id && SameDomainName(&b->domain, &localdomain) && (info->flags & kDNSServiceFlagsIncludeP2P))) - { - domainname tmp; - ConstructServiceName(&tmp, NULL, &info->u.browser.regtype, &b->domain); - LogInfo("add_domain_to_browser: calling external_start_browsing_for_service()"); - external_start_browsing_for_service(&mDNSStorage, &tmp, kDNSType_PTR); - } - } - return err; - } + b = mallocL("browser_t", sizeof(*b)); + if (!b) return mStatus_NoMemoryErr; + AssignDomainName(&b->domain, d); + err = mDNS_StartBrowse(&mDNSStorage, &b->q, + &info->u.browser.regtype, d, info->u.browser.interface_id, info->u.browser.ForceMCast, FoundInstance, info); + if (err) + { + LogMsg("mDNS_StartBrowse returned %d for type %##s domain %##s", err, info->u.browser.regtype.c, d->c); + freeL("browser_t/add_domain_to_browser", b); + } + else + { + b->next = info->u.browser.browsers; + info->u.browser.browsers = b; + LogOperation("%3d: DNSServiceBrowse(%##s) START", info->sd, b->q.qname.c); + if (info->u.browser.interface_id == mDNSInterface_P2P || (!info->u.browser.interface_id && SameDomainName(&b->domain, &localdomain) && (info->flags & kDNSServiceFlagsIncludeP2P))) + { + domainname tmp; + ConstructServiceName(&tmp, NULL, &info->u.browser.regtype, &b->domain); + LogInfo("add_domain_to_browser: calling external_start_browsing_for_service()"); + external_start_browsing_for_service(&mDNSStorage, &tmp, kDNSType_PTR); + } + } + return err; + } mDNSlocal void browse_termination_callback(request_state *info) - { - while (info->u.browser.browsers) - { - browser_t *ptr = info->u.browser.browsers; - - if (info->u.browser.interface_id == mDNSInterface_P2P || (!info->u.browser.interface_id && SameDomainName(&ptr->domain, &localdomain) && (info->flags & kDNSServiceFlagsIncludeP2P))) - { - domainname tmp; - ConstructServiceName(&tmp, NULL, &info->u.browser.regtype, &ptr->domain); - LogInfo("browse_termination_callback: calling external_stop_browsing_for_service()"); - external_stop_browsing_for_service(&mDNSStorage, &tmp, kDNSType_PTR); - } - - info->u.browser.browsers = ptr->next; - LogOperation("%3d: DNSServiceBrowse(%##s) STOP", info->sd, ptr->q.qname.c); - mDNS_StopBrowse(&mDNSStorage, &ptr->q); // no need to error-check result - freeL("browser_t/browse_termination_callback", ptr); - } - } + { + while (info->u.browser.browsers) + { + browser_t *ptr = info->u.browser.browsers; + + if (info->u.browser.interface_id == mDNSInterface_P2P || (!info->u.browser.interface_id && SameDomainName(&ptr->domain, &localdomain) && (info->flags & kDNSServiceFlagsIncludeP2P))) + { + domainname tmp; + ConstructServiceName(&tmp, NULL, &info->u.browser.regtype, &ptr->domain); + LogInfo("browse_termination_callback: calling external_stop_browsing_for_service()"); + external_stop_browsing_for_service(&mDNSStorage, &tmp, kDNSType_PTR); + } + + info->u.browser.browsers = ptr->next; + LogOperation("%3d: DNSServiceBrowse(%##s) STOP", info->sd, ptr->q.qname.c); + mDNS_StopBrowse(&mDNSStorage, &ptr->q); // no need to error-check result + freeL("browser_t/browse_termination_callback", ptr); + } + } mDNSlocal void udsserver_automatic_browse_domain_changed(const DNameListElem *const d, const mDNSBool add) - { - request_state *request; - debugf("udsserver_automatic_browse_domain_changed: %s default browse domain %##s", add ? "Adding" : "Removing", d->name.c); + { + request_state *request; + debugf("udsserver_automatic_browse_domain_changed: %s default browse domain %##s", add ? "Adding" : "Removing", d->name.c); #if APPLE_OSX_mDNSResponder - machserver_automatic_browse_domain_changed(&d->name, add); + machserver_automatic_browse_domain_changed(&d->name, add); #endif // APPLE_OSX_mDNSResponder - for (request = all_requests; request; request = request->next) - { - if (request->terminate != browse_termination_callback) continue; // Not a browse operation - if (!request->u.browser.default_domain) continue; // Not an auto-browse operation - if (!d->uid || SystemUID(request->uid) || request->uid == d->uid) - { - browser_t **ptr = &request->u.browser.browsers; - while (*ptr && !SameDomainName(&(*ptr)->domain, &d->name)) ptr = &(*ptr)->next; - if (add) - { - // If we don't already have this domain in our list for this browse operation, add it now - if (!*ptr) add_domain_to_browser(request, &d->name); - else debugf("udsserver_automatic_browse_domain_changed %##s already in list, not re-adding", &d->name); - } - else - { - if (!*ptr) LogMsg("udsserver_automatic_browse_domain_changed ERROR %##s not found", &d->name); - else - { - DNameListElem *p; - for (p = AutoBrowseDomains; p; p=p->next) - if (!p->uid || SystemUID(request->uid) || request->uid == p->uid) - if (SameDomainName(&d->name, &p->name)) break; - if (p) debugf("udsserver_automatic_browse_domain_changed %##s still in list, not removing", &d->name); - else - { - browser_t *rem = *ptr; - *ptr = (*ptr)->next; - mDNS_StopQueryWithRemoves(&mDNSStorage, &rem->q); - freeL("browser_t/udsserver_automatic_browse_domain_changed", rem); - } - } - } - } - } - } + for (request = all_requests; request; request = request->next) + { + if (request->terminate != browse_termination_callback) continue; // Not a browse operation + if (!request->u.browser.default_domain) continue; // Not an auto-browse operation + if (!d->uid || SystemUID(request->uid) || request->uid == d->uid) + { + browser_t **ptr = &request->u.browser.browsers; + while (*ptr && !SameDomainName(&(*ptr)->domain, &d->name)) ptr = &(*ptr)->next; + if (add) + { + // If we don't already have this domain in our list for this browse operation, add it now + if (!*ptr) add_domain_to_browser(request, &d->name); + else debugf("udsserver_automatic_browse_domain_changed %##s already in list, not re-adding", &d->name); + } + else + { + if (!*ptr) LogMsg("udsserver_automatic_browse_domain_changed ERROR %##s not found", &d->name); + else + { + DNameListElem *p; + for (p = AutoBrowseDomains; p; p=p->next) + if (!p->uid || SystemUID(request->uid) || request->uid == p->uid) + if (SameDomainName(&d->name, &p->name)) break; + if (p) debugf("udsserver_automatic_browse_domain_changed %##s still in list, not removing", &d->name); + else + { + browser_t *rem = *ptr; + *ptr = (*ptr)->next; + mDNS_StopQueryWithRemoves(&mDNSStorage, &rem->q); + freeL("browser_t/udsserver_automatic_browse_domain_changed", rem); + } + } + } + } + } + } mDNSlocal void FreeARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result) - { - (void)m; // unused - if (result == mStatus_MemFree) - { - // On shutdown, mDNS_Close automatically deregisters all records - // Since in this case no one has called DeregisterLocalOnlyDomainEnumPTR to cut the record - // from the LocalDomainEnumRecords list, we do this here before we free the memory. - // (This should actually no longer be necessary, now that we do the proper cleanup in - // udsserver_exit. To confirm this, we'll log an error message if we do find a record that - // hasn't been cut from the list yet. If these messages don't appear, we can delete this code.) - ARListElem **ptr = &LocalDomainEnumRecords; - while (*ptr && &(*ptr)->ar != rr) ptr = &(*ptr)->next; - if (*ptr) { *ptr = (*ptr)->next; LogMsg("FreeARElemCallback: Have to cut %s", ARDisplayString(m, rr)); } - mDNSPlatformMemFree(rr->RecordContext); - } - } + { + (void)m; // unused + if (result == mStatus_MemFree) + { + // On shutdown, mDNS_Close automatically deregisters all records + // Since in this case no one has called DeregisterLocalOnlyDomainEnumPTR to cut the record + // from the LocalDomainEnumRecords list, we do this here before we free the memory. + // (This should actually no longer be necessary, now that we do the proper cleanup in + // udsserver_exit. To confirm this, we'll log an error message if we do find a record that + // hasn't been cut from the list yet. If these messages don't appear, we can delete this code.) + ARListElem **ptr = &LocalDomainEnumRecords; + while (*ptr && &(*ptr)->ar != rr) ptr = &(*ptr)->next; + if (*ptr) { *ptr = (*ptr)->next; LogMsg("FreeARElemCallback: Have to cut %s", ARDisplayString(m, rr)); } + mDNSPlatformMemFree(rr->RecordContext); + } + } // RegisterLocalOnlyDomainEnumPTR and DeregisterLocalOnlyDomainEnumPTR largely duplicate code in // "FoundDomain" in uDNS.c for creating and destroying these special mDNSInterface_LocalOnly records. // We may want to turn the common code into a subroutine. mDNSlocal void RegisterLocalOnlyDomainEnumPTR(mDNS *m, const domainname *d, int type) - { - // allocate/register legacy and non-legacy _browse PTR record - mStatus err; - ARListElem *ptr = mDNSPlatformMemAllocate(sizeof(*ptr)); + { + // allocate/register legacy and non-legacy _browse PTR record + mStatus err; + ARListElem *ptr = mDNSPlatformMemAllocate(sizeof(*ptr)); - debugf("Incrementing %s refcount for %##s", - (type == mDNS_DomainTypeBrowse ) ? "browse domain " : - (type == mDNS_DomainTypeRegistration ) ? "registration dom" : - (type == mDNS_DomainTypeBrowseAutomatic) ? "automatic browse" : "?", d->c); + debugf("Incrementing %s refcount for %##s", + (type == mDNS_DomainTypeBrowse ) ? "browse domain " : + (type == mDNS_DomainTypeRegistration ) ? "registration dom" : + (type == mDNS_DomainTypeBrowseAutomatic) ? "automatic browse" : "?", d->c); - mDNS_SetupResourceRecord(&ptr->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, AuthRecordLocalOnly, FreeARElemCallback, ptr); - MakeDomainNameFromDNSNameString(&ptr->ar.namestorage, mDNS_DomainTypeNames[type]); - AppendDNSNameString (&ptr->ar.namestorage, "local"); - AssignDomainName(&ptr->ar.resrec.rdata->u.name, d); - err = mDNS_Register(m, &ptr->ar); - if (err) - { - LogMsg("SetSCPrefsBrowseDomain: mDNS_Register returned error %d", err); - mDNSPlatformMemFree(ptr); - } - else - { - ptr->next = LocalDomainEnumRecords; - LocalDomainEnumRecords = ptr; - } - } + mDNS_SetupResourceRecord(&ptr->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, AuthRecordLocalOnly, FreeARElemCallback, ptr); + MakeDomainNameFromDNSNameString(&ptr->ar.namestorage, mDNS_DomainTypeNames[type]); + AppendDNSNameString (&ptr->ar.namestorage, "local"); + AssignDomainName(&ptr->ar.resrec.rdata->u.name, d); + err = mDNS_Register(m, &ptr->ar); + if (err) + { + LogMsg("SetSCPrefsBrowseDomain: mDNS_Register returned error %d", err); + mDNSPlatformMemFree(ptr); + } + else + { + ptr->next = LocalDomainEnumRecords; + LocalDomainEnumRecords = ptr; + } + } mDNSlocal void DeregisterLocalOnlyDomainEnumPTR(mDNS *m, const domainname *d, int type) - { - ARListElem **ptr = &LocalDomainEnumRecords; - domainname lhs; // left-hand side of PTR, for comparison + { + ARListElem **ptr = &LocalDomainEnumRecords; + domainname lhs; // left-hand side of PTR, for comparison - debugf("Decrementing %s refcount for %##s", - (type == mDNS_DomainTypeBrowse ) ? "browse domain " : - (type == mDNS_DomainTypeRegistration ) ? "registration dom" : - (type == mDNS_DomainTypeBrowseAutomatic) ? "automatic browse" : "?", d->c); + debugf("Decrementing %s refcount for %##s", + (type == mDNS_DomainTypeBrowse ) ? "browse domain " : + (type == mDNS_DomainTypeRegistration ) ? "registration dom" : + (type == mDNS_DomainTypeBrowseAutomatic) ? "automatic browse" : "?", d->c); - MakeDomainNameFromDNSNameString(&lhs, mDNS_DomainTypeNames[type]); - AppendDNSNameString (&lhs, "local"); + MakeDomainNameFromDNSNameString(&lhs, mDNS_DomainTypeNames[type]); + AppendDNSNameString (&lhs, "local"); - while (*ptr) - { - if (SameDomainName(&(*ptr)->ar.resrec.rdata->u.name, d) && SameDomainName((*ptr)->ar.resrec.name, &lhs)) - { - ARListElem *rem = *ptr; - *ptr = (*ptr)->next; - mDNS_Deregister(m, &rem->ar); - return; - } - else ptr = &(*ptr)->next; - } - } + while (*ptr) + { + if (SameDomainName(&(*ptr)->ar.resrec.rdata->u.name, d) && SameDomainName((*ptr)->ar.resrec.name, &lhs)) + { + ARListElem *rem = *ptr; + *ptr = (*ptr)->next; + mDNS_Deregister(m, &rem->ar); + return; + } + else ptr = &(*ptr)->next; + } + } mDNSlocal void AddAutoBrowseDomain(const mDNSu32 uid, const domainname *const name) - { - DNameListElem *new = mDNSPlatformMemAllocate(sizeof(DNameListElem)); - if (!new) { LogMsg("ERROR: malloc"); return; } - AssignDomainName(&new->name, name); - new->uid = uid; - new->next = AutoBrowseDomains; - AutoBrowseDomains = new; - udsserver_automatic_browse_domain_changed(new, mDNStrue); - } + { + DNameListElem *new = mDNSPlatformMemAllocate(sizeof(DNameListElem)); + if (!new) { LogMsg("ERROR: malloc"); return; } + AssignDomainName(&new->name, name); + new->uid = uid; + new->next = AutoBrowseDomains; + AutoBrowseDomains = new; + udsserver_automatic_browse_domain_changed(new, mDNStrue); + } mDNSlocal void RmvAutoBrowseDomain(const mDNSu32 uid, const domainname *const name) - { - DNameListElem **p = &AutoBrowseDomains; - while (*p && (!SameDomainName(&(*p)->name, name) || (*p)->uid != uid)) p = &(*p)->next; - if (!*p) LogMsg("RmvAutoBrowseDomain: Got remove event for domain %##s not in list", name->c); - else - { - DNameListElem *ptr = *p; - *p = ptr->next; - udsserver_automatic_browse_domain_changed(ptr, mDNSfalse); - mDNSPlatformMemFree(ptr); - } - } + { + DNameListElem **p = &AutoBrowseDomains; + while (*p && (!SameDomainName(&(*p)->name, name) || (*p)->uid != uid)) p = &(*p)->next; + if (!*p) LogMsg("RmvAutoBrowseDomain: Got remove event for domain %##s not in list", name->c); + else + { + DNameListElem *ptr = *p; + *p = ptr->next; + udsserver_automatic_browse_domain_changed(ptr, mDNSfalse); + mDNSPlatformMemFree(ptr); + } + } mDNSlocal void SetPrefsBrowseDomains(mDNS *m, DNameListElem *browseDomains, mDNSBool add) - { - DNameListElem *d; - for (d = browseDomains; d; d = d->next) - { - if (add) - { - RegisterLocalOnlyDomainEnumPTR(m, &d->name, mDNS_DomainTypeBrowse); - AddAutoBrowseDomain(d->uid, &d->name); - } - else - { - DeregisterLocalOnlyDomainEnumPTR(m, &d->name, mDNS_DomainTypeBrowse); - RmvAutoBrowseDomain(d->uid, &d->name); - } - } - } + { + DNameListElem *d; + for (d = browseDomains; d; d = d->next) + { + if (add) + { + RegisterLocalOnlyDomainEnumPTR(m, &d->name, mDNS_DomainTypeBrowse); + AddAutoBrowseDomain(d->uid, &d->name); + } + else + { + DeregisterLocalOnlyDomainEnumPTR(m, &d->name, mDNS_DomainTypeBrowse); + RmvAutoBrowseDomain(d->uid, &d->name); + } + } + } mDNSlocal void UpdateDeviceInfoRecord(mDNS *const m) - { - int num_autoname = 0; - request_state *req; - for (req = all_requests; req; req = req->next) - if (req->terminate == regservice_termination_callback && req->u.servicereg.autoname) - num_autoname++; + { + int num_autoname = 0; + request_state *req; + for (req = all_requests; req; req = req->next) + if (req->terminate == regservice_termination_callback && req->u.servicereg.autoname) + num_autoname++; - // If DeviceInfo record is currently registered, see if we need to deregister it - if (m->DeviceInfo.resrec.RecordType != kDNSRecordTypeUnregistered) - if (num_autoname == 0 || !SameDomainLabelCS(m->DeviceInfo.resrec.name->c, m->nicelabel.c)) - { - LogOperation("UpdateDeviceInfoRecord Deregister %##s", m->DeviceInfo.resrec.name); - mDNS_Deregister(m, &m->DeviceInfo); - } + // If DeviceInfo record is currently registered, see if we need to deregister it + if (m->DeviceInfo.resrec.RecordType != kDNSRecordTypeUnregistered) + if (num_autoname == 0 || !SameDomainLabelCS(m->DeviceInfo.resrec.name->c, m->nicelabel.c)) + { + LogOperation("UpdateDeviceInfoRecord Deregister %##s", m->DeviceInfo.resrec.name); + mDNS_Deregister(m, &m->DeviceInfo); + } - // If DeviceInfo record is not currently registered, see if we need to register it - if (m->DeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered) - if (num_autoname > 0) - { - mDNSu8 len = m->HIHardware.c[0] < 255 - 6 ? m->HIHardware.c[0] : 255 - 6; - mDNS_SetupResourceRecord(&m->DeviceInfo, mDNSNULL, mDNSNULL, kDNSType_TXT, kStandardTTL, kDNSRecordTypeAdvisory, AuthRecordAny, mDNSNULL, mDNSNULL); - ConstructServiceName(&m->DeviceInfo.namestorage, &m->nicelabel, &DeviceInfoName, &localdomain); - mDNSPlatformMemCopy(m->DeviceInfo.resrec.rdata->u.data + 1, "model=", 6); - mDNSPlatformMemCopy(m->DeviceInfo.resrec.rdata->u.data + 7, m->HIHardware.c + 1, len); - m->DeviceInfo.resrec.rdata->u.data[0] = 6 + len; // "model=" plus the device string - m->DeviceInfo.resrec.rdlength = 7 + len; // One extra for the length byte at the start of the string - LogOperation("UpdateDeviceInfoRecord Register %##s", m->DeviceInfo.resrec.name); - mDNS_Register(m, &m->DeviceInfo); - } - } + // If DeviceInfo record is not currently registered, see if we need to register it + if (m->DeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered) + if (num_autoname > 0) + { + mDNSu8 len = m->HIHardware.c[0] < 255 - 6 ? m->HIHardware.c[0] : 255 - 6; + mDNS_SetupResourceRecord(&m->DeviceInfo, mDNSNULL, mDNSNULL, kDNSType_TXT, kStandardTTL, kDNSRecordTypeAdvisory, AuthRecordAny, mDNSNULL, mDNSNULL); + ConstructServiceName(&m->DeviceInfo.namestorage, &m->nicelabel, &DeviceInfoName, &localdomain); + mDNSPlatformMemCopy(m->DeviceInfo.resrec.rdata->u.data + 1, "model=", 6); + mDNSPlatformMemCopy(m->DeviceInfo.resrec.rdata->u.data + 7, m->HIHardware.c + 1, len); + m->DeviceInfo.resrec.rdata->u.data[0] = 6 + len; // "model=" plus the device string + m->DeviceInfo.resrec.rdlength = 7 + len; // One extra for the length byte at the start of the string + LogOperation("UpdateDeviceInfoRecord Register %##s", m->DeviceInfo.resrec.name); + mDNS_Register(m, &m->DeviceInfo); + } + } mDNSexport void udsserver_handle_configchange(mDNS *const m) - { - request_state *req; - service_instance *ptr; - DNameListElem *RegDomains = NULL; - DNameListElem *BrowseDomains = NULL; - DNameListElem *p; + { + request_state *req; + service_instance *ptr; + DNameListElem *RegDomains = NULL; + DNameListElem *BrowseDomains = NULL; + DNameListElem *p; - UpdateDeviceInfoRecord(m); + UpdateDeviceInfoRecord(m); - // For autoname services, see if the default service name has changed, necessitating an automatic update - for (req = all_requests; req; req = req->next) - if (req->terminate == regservice_termination_callback) - if (req->u.servicereg.autoname && !SameDomainLabelCS(req->u.servicereg.name.c, m->nicelabel.c)) - { - req->u.servicereg.name = m->nicelabel; - for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next) - { - ptr->renameonmemfree = 1; - if (ptr->clientnotified) SendServiceRemovalNotification(&ptr->srs); - LogInfo("udsserver_handle_configchange: Calling deregister for Service %##s", ptr->srs.RR_PTR.resrec.name->c); - if (mDNS_DeregisterService_drt(m, &ptr->srs, mDNS_Dereg_rapid)) - regservice_callback(m, &ptr->srs, mStatus_MemFree); // If service deregistered already, we can re-register immediately - } - } + // For autoname services, see if the default service name has changed, necessitating an automatic update + for (req = all_requests; req; req = req->next) + if (req->terminate == regservice_termination_callback) + if (req->u.servicereg.autoname && !SameDomainLabelCS(req->u.servicereg.name.c, m->nicelabel.c)) + { + req->u.servicereg.name = m->nicelabel; + for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next) + { + ptr->renameonmemfree = 1; + if (ptr->clientnotified) SendServiceRemovalNotification(&ptr->srs); + LogInfo("udsserver_handle_configchange: Calling deregister for Service %##s", ptr->srs.RR_PTR.resrec.name->c); + if (mDNS_DeregisterService_drt(m, &ptr->srs, mDNS_Dereg_rapid)) + regservice_callback(m, &ptr->srs, mStatus_MemFree); // If service deregistered already, we can re-register immediately + } + } - // Let the platform layer get the current DNS information - mDNS_Lock(m); - mDNSPlatformSetDNSConfig(m, mDNSfalse, mDNSfalse, mDNSNULL, &RegDomains, &BrowseDomains); - mDNS_Unlock(m); + // Let the platform layer get the current DNS information + mDNS_Lock(m); + mDNSPlatformSetDNSConfig(m, mDNSfalse, mDNSfalse, mDNSNULL, &RegDomains, &BrowseDomains); + mDNS_Unlock(m); - // Any automatic registration domains are also implicitly automatic browsing domains - if (RegDomains) SetPrefsBrowseDomains(m, RegDomains, mDNStrue); // Add the new list first - if (AutoRegistrationDomains) SetPrefsBrowseDomains(m, AutoRegistrationDomains, mDNSfalse); // Then clear the old list + // Any automatic registration domains are also implicitly automatic browsing domains + if (RegDomains) SetPrefsBrowseDomains(m, RegDomains, mDNStrue); // Add the new list first + if (AutoRegistrationDomains) SetPrefsBrowseDomains(m, AutoRegistrationDomains, mDNSfalse); // Then clear the old list - // Add any new domains not already in our AutoRegistrationDomains list - for (p=RegDomains; p; p=p->next) - { - DNameListElem **pp = &AutoRegistrationDomains; - while (*pp && ((*pp)->uid != p->uid || !SameDomainName(&(*pp)->name, &p->name))) pp = &(*pp)->next; - if (!*pp) // If not found in our existing list, this is a new default registration domain - { - RegisterLocalOnlyDomainEnumPTR(m, &p->name, mDNS_DomainTypeRegistration); - udsserver_default_reg_domain_changed(p, mDNStrue); - } - else // else found same domainname in both old and new lists, so no change, just delete old copy - { - DNameListElem *del = *pp; - *pp = (*pp)->next; - mDNSPlatformMemFree(del); - } - } + // Add any new domains not already in our AutoRegistrationDomains list + for (p=RegDomains; p; p=p->next) + { + DNameListElem **pp = &AutoRegistrationDomains; + while (*pp && ((*pp)->uid != p->uid || !SameDomainName(&(*pp)->name, &p->name))) pp = &(*pp)->next; + if (!*pp) // If not found in our existing list, this is a new default registration domain + { + RegisterLocalOnlyDomainEnumPTR(m, &p->name, mDNS_DomainTypeRegistration); + udsserver_default_reg_domain_changed(p, mDNStrue); + } + else // else found same domainname in both old and new lists, so no change, just delete old copy + { + DNameListElem *del = *pp; + *pp = (*pp)->next; + mDNSPlatformMemFree(del); + } + } - // Delete any domains in our old AutoRegistrationDomains list that are now gone - while (AutoRegistrationDomains) - { - DNameListElem *del = AutoRegistrationDomains; - AutoRegistrationDomains = AutoRegistrationDomains->next; // Cut record from list FIRST, - DeregisterLocalOnlyDomainEnumPTR(m, &del->name, mDNS_DomainTypeRegistration); - udsserver_default_reg_domain_changed(del, mDNSfalse); // before calling udsserver_default_reg_domain_changed() - mDNSPlatformMemFree(del); - } + // Delete any domains in our old AutoRegistrationDomains list that are now gone + while (AutoRegistrationDomains) + { + DNameListElem *del = AutoRegistrationDomains; + AutoRegistrationDomains = AutoRegistrationDomains->next; // Cut record from list FIRST, + DeregisterLocalOnlyDomainEnumPTR(m, &del->name, mDNS_DomainTypeRegistration); + udsserver_default_reg_domain_changed(del, mDNSfalse); // before calling udsserver_default_reg_domain_changed() + mDNSPlatformMemFree(del); + } - // Now we have our new updated automatic registration domain list - AutoRegistrationDomains = RegDomains; + // Now we have our new updated automatic registration domain list + AutoRegistrationDomains = RegDomains; - // Add new browse domains to internal list - if (BrowseDomains) SetPrefsBrowseDomains(m, BrowseDomains, mDNStrue); + // Add new browse domains to internal list + if (BrowseDomains) SetPrefsBrowseDomains(m, BrowseDomains, mDNStrue); - // Remove old browse domains from internal list - if (SCPrefBrowseDomains) - { - SetPrefsBrowseDomains(m, SCPrefBrowseDomains, mDNSfalse); - while (SCPrefBrowseDomains) - { - DNameListElem *fptr = SCPrefBrowseDomains; - SCPrefBrowseDomains = SCPrefBrowseDomains->next; - mDNSPlatformMemFree(fptr); - } - } + // Remove old browse domains from internal list + if (SCPrefBrowseDomains) + { + SetPrefsBrowseDomains(m, SCPrefBrowseDomains, mDNSfalse); + while (SCPrefBrowseDomains) + { + DNameListElem *fptr = SCPrefBrowseDomains; + SCPrefBrowseDomains = SCPrefBrowseDomains->next; + mDNSPlatformMemFree(fptr); + } + } - // Replace the old browse domains array with the new array - SCPrefBrowseDomains = BrowseDomains; - } + // Replace the old browse domains array with the new array + SCPrefBrowseDomains = BrowseDomains; + } mDNSlocal void AutomaticBrowseDomainChange(mDNS *const m, DNSQuestion *q, const ResourceRecord *const answer, QC_result AddRecord) - { - (void)m; // unused; - (void)q; // unused + { + (void)m; // unused; + (void)q; // unused - LogOperation("AutomaticBrowseDomainChange: %s automatic browse domain %##s", - AddRecord ? "Adding" : "Removing", answer->rdata->u.name.c); + LogOperation("AutomaticBrowseDomainChange: %s automatic browse domain %##s", + AddRecord ? "Adding" : "Removing", answer->rdata->u.name.c); - if (AddRecord) AddAutoBrowseDomain(0, &answer->rdata->u.name); - else RmvAutoBrowseDomain(0, &answer->rdata->u.name); - } + if (AddRecord) AddAutoBrowseDomain(0, &answer->rdata->u.name); + else RmvAutoBrowseDomain(0, &answer->rdata->u.name); + } mDNSlocal mStatus handle_browse_request(request_state *request) - { - char regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME]; - domainname typedn, d, temp; - mDNSs32 NumSubTypes; - mStatus err = mStatus_NoError; + { + char regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME]; + domainname typedn, d, temp; + mDNSs32 NumSubTypes; + mStatus err = mStatus_NoError; - DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); - mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); - mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); - if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr); + DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); + mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); + mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); + if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr); - if (get_string(&request->msgptr, request->msgend, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 || - get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0) return(mStatus_BadParamErr); + if (get_string(&request->msgptr, request->msgend, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 || + get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0) return(mStatus_BadParamErr); - if (!request->msgptr) { LogMsg("%3d: DNSServiceBrowse(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } + if (!request->msgptr) { LogMsg("%3d: DNSServiceBrowse(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } - if (domain[0] == '\0') uDNS_SetupSearchDomains(&mDNSStorage, UDNS_START_WAB_QUERY); + if (domain[0] == '\0') uDNS_SetupSearchDomains(&mDNSStorage, UDNS_START_WAB_QUERY); - request->flags = flags; - typedn.c[0] = 0; - NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes - if (NumSubTypes < 0 || NumSubTypes > 1) return(mStatus_BadParamErr); - if (NumSubTypes == 1 && !AppendDNSNameString(&typedn, regtype + strlen(regtype) + 1)) return(mStatus_BadParamErr); + request->flags = flags; + typedn.c[0] = 0; + NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes + if (NumSubTypes < 0 || NumSubTypes > 1) return(mStatus_BadParamErr); + if (NumSubTypes == 1 && !AppendDNSNameString(&typedn, regtype + strlen(regtype) + 1)) return(mStatus_BadParamErr); - if (!regtype[0] || !AppendDNSNameString(&typedn, regtype)) return(mStatus_BadParamErr); + if (!regtype[0] || !AppendDNSNameString(&typedn, regtype)) return(mStatus_BadParamErr); - if (!MakeDomainNameFromDNSNameString(&temp, regtype)) return(mStatus_BadParamErr); - // For over-long service types, we only allow domain "local" - if (temp.c[0] > 15 && domain[0] == 0) mDNSPlatformStrCopy(domain, "local."); + if (!MakeDomainNameFromDNSNameString(&temp, regtype)) return(mStatus_BadParamErr); + // For over-long service types, we only allow domain "local" + if (temp.c[0] > 15 && domain[0] == 0) mDNSPlatformStrCopy(domain, "local."); - // Set up browser info - request->u.browser.ForceMCast = (flags & kDNSServiceFlagsForceMulticast) != 0; - request->u.browser.interface_id = InterfaceID; - AssignDomainName(&request->u.browser.regtype, &typedn); - request->u.browser.default_domain = !domain[0]; - request->u.browser.browsers = NULL; + // Set up browser info + request->u.browser.ForceMCast = (flags & kDNSServiceFlagsForceMulticast) != 0; + request->u.browser.interface_id = InterfaceID; + AssignDomainName(&request->u.browser.regtype, &typedn); + request->u.browser.default_domain = !domain[0]; + request->u.browser.browsers = NULL; - LogOperation("%3d: DNSServiceBrowse(%X, %d, \"%##s\", \"%s\") START", - request->sd, request->flags, interfaceIndex, request->u.browser.regtype.c, domain); + LogOperation("%3d: DNSServiceBrowse(%X, %d, \"%##s\", \"%s\") START", + request->sd, request->flags, interfaceIndex, request->u.browser.regtype.c, domain); - // We need to unconditionally set request->terminate, because even if we didn't successfully - // start any browses right now, subsequent configuration changes may cause successful - // browses to be added, and we'll need to cancel them before freeing this memory. - request->terminate = browse_termination_callback; + // We need to unconditionally set request->terminate, because even if we didn't successfully + // start any browses right now, subsequent configuration changes may cause successful + // browses to be added, and we'll need to cancel them before freeing this memory. + request->terminate = browse_termination_callback; - if (domain[0]) - { - if (!MakeDomainNameFromDNSNameString(&d, domain)) return(mStatus_BadParamErr); - err = add_domain_to_browser(request, &d); + if (domain[0]) + { + if (!MakeDomainNameFromDNSNameString(&d, domain)) return(mStatus_BadParamErr); + err = add_domain_to_browser(request, &d); #if 0 - err = AuthorizedDomain(request, &d, AutoBrowseDomains) ? add_domain_to_browser(request, &d) : mStatus_NoError; + err = AuthorizedDomain(request, &d, AutoBrowseDomains) ? add_domain_to_browser(request, &d) : mStatus_NoError; #endif - } - else - { - DNameListElem *sdom; - for (sdom = AutoBrowseDomains; sdom; sdom = sdom->next) - if (!sdom->uid || SystemUID(request->uid) || request->uid == sdom->uid) - { - err = add_domain_to_browser(request, &sdom->name); - if (err) - { - if (SameDomainName(&sdom->name, &localdomain)) break; - else err = mStatus_NoError; // suppress errors for non-local "default" domains - } - } - } + } + else + { + DNameListElem *sdom; + for (sdom = AutoBrowseDomains; sdom; sdom = sdom->next) + if (!sdom->uid || SystemUID(request->uid) || request->uid == sdom->uid) + { + err = add_domain_to_browser(request, &sdom->name); + if (err) + { + if (SameDomainName(&sdom->name, &localdomain)) break; + else err = mStatus_NoError; // suppress errors for non-local "default" domains + } + } + } - return(err); - } + return(err); + } // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -2192,169 +2194,169 @@ mDNSlocal mStatus handle_browse_request(request_state *request) #endif mDNSlocal void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) - { - size_t len = 0; - char fullname[MAX_ESCAPED_DOMAIN_NAME], target[MAX_ESCAPED_DOMAIN_NAME]; - char *data; - reply_state *rep; - request_state *req = question->QuestionContext; - (void)m; // Unused + { + size_t len = 0; + char fullname[MAX_ESCAPED_DOMAIN_NAME], target[MAX_ESCAPED_DOMAIN_NAME]; + char *data; + reply_state *rep; + request_state *req = question->QuestionContext; + (void)m; // Unused - LogOperation("%3d: DNSServiceResolve(%##s) %s %s", req->sd, question->qname.c, AddRecord ? "ADD" : "RMV", RRDisplayString(m, answer)); + LogOperation("%3d: DNSServiceResolve(%##s) %s %s", req->sd, question->qname.c, AddRecord ? "ADD" : "RMV", RRDisplayString(m, answer)); - if (!AddRecord) - { - if (req->u.resolve.srv == answer) req->u.resolve.srv = mDNSNULL; - if (req->u.resolve.txt == answer) req->u.resolve.txt = mDNSNULL; - return; - } + if (!AddRecord) + { + if (req->u.resolve.srv == answer) req->u.resolve.srv = mDNSNULL; + if (req->u.resolve.txt == answer) req->u.resolve.txt = mDNSNULL; + return; + } - if (answer->rrtype == kDNSType_SRV) req->u.resolve.srv = answer; - if (answer->rrtype == kDNSType_TXT) req->u.resolve.txt = answer; + if (answer->rrtype == kDNSType_SRV) req->u.resolve.srv = answer; + if (answer->rrtype == kDNSType_TXT) req->u.resolve.txt = answer; - if (!req->u.resolve.txt || !req->u.resolve.srv) return; // only deliver result to client if we have both answers + if (!req->u.resolve.txt || !req->u.resolve.srv) return; // only deliver result to client if we have both answers - ConvertDomainNameToCString(answer->name, fullname); - ConvertDomainNameToCString(&req->u.resolve.srv->rdata->u.srv.target, target); + ConvertDomainNameToCString(answer->name, fullname); + ConvertDomainNameToCString(&req->u.resolve.srv->rdata->u.srv.target, target); - // calculate reply length - len += sizeof(DNSServiceFlags); - len += sizeof(mDNSu32); // interface index - len += sizeof(DNSServiceErrorType); - len += strlen(fullname) + 1; - len += strlen(target) + 1; - len += 2 * sizeof(mDNSu16); // port, txtLen - len += req->u.resolve.txt->rdlength; + // calculate reply length + len += sizeof(DNSServiceFlags); + len += sizeof(mDNSu32); // interface index + len += sizeof(DNSServiceErrorType); + len += strlen(fullname) + 1; + len += strlen(target) + 1; + len += 2 * sizeof(mDNSu16); // port, txtLen + len += req->u.resolve.txt->rdlength; - // allocate/init reply header - rep = create_reply(resolve_reply_op, len, req); - rep->rhdr->flags = dnssd_htonl(0); - rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNSfalse)); - rep->rhdr->error = dnssd_htonl(kDNSServiceErr_NoError); + // allocate/init reply header + rep = create_reply(resolve_reply_op, len, req); + rep->rhdr->flags = dnssd_htonl(0); + rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNSfalse)); + rep->rhdr->error = dnssd_htonl(kDNSServiceErr_NoError); - data = (char *)&rep->rhdr[1]; + data = (char *)&rep->rhdr[1]; - // write reply data to message - put_string(fullname, &data); - put_string(target, &data); - *data++ = req->u.resolve.srv->rdata->u.srv.port.b[0]; - *data++ = req->u.resolve.srv->rdata->u.srv.port.b[1]; - put_uint16(req->u.resolve.txt->rdlength, &data); - put_rdata (req->u.resolve.txt->rdlength, req->u.resolve.txt->rdata->u.data, &data); + // write reply data to message + put_string(fullname, &data); + put_string(target, &data); + *data++ = req->u.resolve.srv->rdata->u.srv.port.b[0]; + *data++ = req->u.resolve.srv->rdata->u.srv.port.b[1]; + put_uint16(req->u.resolve.txt->rdlength, &data); + put_rdata (req->u.resolve.txt->rdlength, req->u.resolve.txt->rdata->u.data, &data); - LogOperation("%3d: DNSServiceResolve(%s) RESULT %s:%d", req->sd, fullname, target, mDNSVal16(req->u.resolve.srv->rdata->u.srv.port)); - append_reply(req, rep); - } + LogOperation("%3d: DNSServiceResolve(%s) RESULT %s:%d", req->sd, fullname, target, mDNSVal16(req->u.resolve.srv->rdata->u.srv.port)); + append_reply(req, rep); + } mDNSlocal void resolve_termination_callback(request_state *request) - { - LogOperation("%3d: DNSServiceResolve(%##s) STOP", request->sd, request->u.resolve.qtxt.qname.c); - mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qtxt); - mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv); - if (request->u.resolve.external_advertise) external_stop_resolving_service(&request->u.resolve.qsrv.qname); - } + { + LogOperation("%3d: DNSServiceResolve(%##s) STOP", request->sd, request->u.resolve.qtxt.qname.c); + mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qtxt); + mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv); + if (request->u.resolve.external_advertise) external_stop_resolving_service(&request->u.resolve.qsrv.qname); + } mDNSlocal mStatus handle_resolve_request(request_state *request) - { - char name[256], regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME]; - domainname fqdn; - mStatus err; + { + char name[256], regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME]; + domainname fqdn; + mStatus err; - // extract the data from the message - DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); - mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); - mDNSInterfaceID InterfaceID; - mDNSBool wasP2P = (interfaceIndex == kDNSServiceInterfaceIndexP2P); - - - request->flags = flags; - if (wasP2P) interfaceIndex = kDNSServiceInterfaceIndexAny; - - InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); - if (interfaceIndex && !InterfaceID) - { LogMsg("ERROR: handle_resolve_request bad interfaceIndex %d", interfaceIndex); return(mStatus_BadParamErr); } + // extract the data from the message + DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); + mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); + mDNSInterfaceID InterfaceID; + mDNSBool wasP2P = (interfaceIndex == kDNSServiceInterfaceIndexP2P); - if (get_string(&request->msgptr, request->msgend, name, 256) < 0 || - get_string(&request->msgptr, request->msgend, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 || - get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0) - { LogMsg("ERROR: handle_resolve_request - Couldn't read name/regtype/domain"); return(mStatus_BadParamErr); } - if (!request->msgptr) { LogMsg("%3d: DNSServiceResolve(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } + request->flags = flags; + if (wasP2P) interfaceIndex = kDNSServiceInterfaceIndexAny; - if (build_domainname_from_strings(&fqdn, name, regtype, domain) < 0) + InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); + if (interfaceIndex && !InterfaceID) + { LogMsg("ERROR: handle_resolve_request bad interfaceIndex %d", interfaceIndex); return(mStatus_BadParamErr); } + + if (get_string(&request->msgptr, request->msgend, name, 256) < 0 || + get_string(&request->msgptr, request->msgend, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 || + get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0) + { LogMsg("ERROR: handle_resolve_request - Couldn't read name/regtype/domain"); return(mStatus_BadParamErr); } + + if (!request->msgptr) { LogMsg("%3d: DNSServiceResolve(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } + + if (build_domainname_from_strings(&fqdn, name, regtype, domain) < 0) { LogMsg("ERROR: handle_resolve_request bad '%s' '%s' '%s'", name, regtype, domain); return(mStatus_BadParamErr); } - mDNSPlatformMemZero(&request->u.resolve, sizeof(request->u.resolve)); + mDNSPlatformMemZero(&request->u.resolve, sizeof(request->u.resolve)); - // format questions - request->u.resolve.qsrv.InterfaceID = InterfaceID; - request->u.resolve.qsrv.Target = zeroAddr; - AssignDomainName(&request->u.resolve.qsrv.qname, &fqdn); - request->u.resolve.qsrv.qtype = kDNSType_SRV; - request->u.resolve.qsrv.qclass = kDNSClass_IN; - request->u.resolve.qsrv.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0; - request->u.resolve.qsrv.ExpectUnique = mDNStrue; - request->u.resolve.qsrv.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; - request->u.resolve.qsrv.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; - request->u.resolve.qsrv.SuppressUnusable = mDNSfalse; - request->u.resolve.qsrv.SearchListIndex = 0; - request->u.resolve.qsrv.AppendSearchDomains = 0; - request->u.resolve.qsrv.RetryWithSearchDomains = mDNSfalse; - request->u.resolve.qsrv.TimeoutQuestion = 0; - request->u.resolve.qsrv.WakeOnResolve = (flags & kDNSServiceFlagsWakeOnResolve) != 0; - request->u.resolve.qsrv.qnameOrig = mDNSNULL; - request->u.resolve.qsrv.QuestionCallback = resolve_result_callback; - request->u.resolve.qsrv.QuestionContext = request; + // format questions + request->u.resolve.qsrv.InterfaceID = InterfaceID; + request->u.resolve.qsrv.Target = zeroAddr; + AssignDomainName(&request->u.resolve.qsrv.qname, &fqdn); + request->u.resolve.qsrv.qtype = kDNSType_SRV; + request->u.resolve.qsrv.qclass = kDNSClass_IN; + request->u.resolve.qsrv.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0; + request->u.resolve.qsrv.ExpectUnique = mDNStrue; + request->u.resolve.qsrv.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; + request->u.resolve.qsrv.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; + request->u.resolve.qsrv.SuppressUnusable = mDNSfalse; + request->u.resolve.qsrv.SearchListIndex = 0; + request->u.resolve.qsrv.AppendSearchDomains = 0; + request->u.resolve.qsrv.RetryWithSearchDomains = mDNSfalse; + request->u.resolve.qsrv.TimeoutQuestion = 0; + request->u.resolve.qsrv.WakeOnResolve = (flags & kDNSServiceFlagsWakeOnResolve) != 0; + request->u.resolve.qsrv.qnameOrig = mDNSNULL; + request->u.resolve.qsrv.QuestionCallback = resolve_result_callback; + request->u.resolve.qsrv.QuestionContext = request; - request->u.resolve.qtxt.InterfaceID = InterfaceID; - request->u.resolve.qtxt.Target = zeroAddr; - AssignDomainName(&request->u.resolve.qtxt.qname, &fqdn); - request->u.resolve.qtxt.qtype = kDNSType_TXT; - request->u.resolve.qtxt.qclass = kDNSClass_IN; - request->u.resolve.qtxt.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0; - request->u.resolve.qtxt.ExpectUnique = mDNStrue; - request->u.resolve.qtxt.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; - request->u.resolve.qtxt.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; - request->u.resolve.qtxt.SuppressUnusable = mDNSfalse; - request->u.resolve.qtxt.SearchListIndex = 0; - request->u.resolve.qtxt.AppendSearchDomains = 0; - request->u.resolve.qtxt.RetryWithSearchDomains = mDNSfalse; - request->u.resolve.qtxt.TimeoutQuestion = 0; - request->u.resolve.qtxt.WakeOnResolve = 0; - request->u.resolve.qtxt.qnameOrig = mDNSNULL; - request->u.resolve.qtxt.QuestionCallback = resolve_result_callback; - request->u.resolve.qtxt.QuestionContext = request; + request->u.resolve.qtxt.InterfaceID = InterfaceID; + request->u.resolve.qtxt.Target = zeroAddr; + AssignDomainName(&request->u.resolve.qtxt.qname, &fqdn); + request->u.resolve.qtxt.qtype = kDNSType_TXT; + request->u.resolve.qtxt.qclass = kDNSClass_IN; + request->u.resolve.qtxt.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0; + request->u.resolve.qtxt.ExpectUnique = mDNStrue; + request->u.resolve.qtxt.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; + request->u.resolve.qtxt.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; + request->u.resolve.qtxt.SuppressUnusable = mDNSfalse; + request->u.resolve.qtxt.SearchListIndex = 0; + request->u.resolve.qtxt.AppendSearchDomains = 0; + request->u.resolve.qtxt.RetryWithSearchDomains = mDNSfalse; + request->u.resolve.qtxt.TimeoutQuestion = 0; + request->u.resolve.qtxt.WakeOnResolve = 0; + request->u.resolve.qtxt.qnameOrig = mDNSNULL; + request->u.resolve.qtxt.QuestionCallback = resolve_result_callback; + request->u.resolve.qtxt.QuestionContext = request; - request->u.resolve.ReportTime = NonZeroTime(mDNS_TimeNow(&mDNSStorage) + 130 * mDNSPlatformOneSecond); + request->u.resolve.ReportTime = NonZeroTime(mDNS_TimeNow(&mDNSStorage) + 130 * mDNSPlatformOneSecond); - request->u.resolve.external_advertise = mDNSfalse; + request->u.resolve.external_advertise = mDNSfalse; #if 0 - if (!AuthorizedDomain(request, &fqdn, AutoBrowseDomains)) return(mStatus_NoError); + if (!AuthorizedDomain(request, &fqdn, AutoBrowseDomains)) return(mStatus_NoError); #endif - // ask the questions - LogOperation("%3d: DNSServiceResolve(%##s) START", request->sd, request->u.resolve.qsrv.qname.c); - err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qsrv); - if (!err) - { - err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qtxt); - if (err) mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv); - else - { - request->terminate = resolve_termination_callback; - // If the user explicitly passed in P2P, we don't restrict the domain in which we resolve. - if (wasP2P || (!InterfaceID && IsLocalDomain(&fqdn) && (request->flags & kDNSServiceFlagsIncludeP2P))) - { - request->u.resolve.external_advertise = mDNStrue; - LogInfo("handle_resolve_request: calling external_start_resolving_service()"); - external_start_resolving_service(&fqdn); - } - } - } + // ask the questions + LogOperation("%3d: DNSServiceResolve(%X %d %##s) START", request->sd, flags, interfaceIndex, request->u.resolve.qsrv.qname.c); + err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qsrv); + if (!err) + { + err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qtxt); + if (err) mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv); + else + { + request->terminate = resolve_termination_callback; + // If the user explicitly passed in P2P, we don't restrict the domain in which we resolve. + if (wasP2P || (!InterfaceID && IsLocalDomain(&fqdn) && (request->flags & kDNSServiceFlagsIncludeP2P))) + { + request->u.resolve.external_advertise = mDNStrue; + LogInfo("handle_resolve_request: calling external_start_resolving_service()"); + external_start_resolving_service(&fqdn); + } + } + } - return(err); - } + return(err); + } // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -2367,659 +2369,659 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) // to the mDNSCore routine) that sends results back to the client, and a termination routine that aborts // the mDNSCore operation if the client dies or closes its socket. -// Returns -1 to tell the caller that it should not try to reissue the query anymore +// Returns -1 to tell the caller that it should not try to reissue the query anymore // Returns 1 on successfully appending a search domain and the caller should reissue the new query // Returns 0 when there are no more search domains and the caller should reissue the query mDNSlocal int AppendNewSearchDomain(mDNS *const m, DNSQuestion *question) - { - domainname *sd; - mStatus err; + { + domainname *sd; + mStatus err; - // Sanity check: The caller already checks this. We use -1 to indicate that we have searched all - // the domains and should try the single label query directly on the wire. - if (question->SearchListIndex == -1) - { - LogMsg("AppendNewSearchDomain: question %##s (%s) SearchListIndex is -1", question->qname.c, DNSTypeName(question->qtype)); - return -1; - } + // Sanity check: The caller already checks this. We use -1 to indicate that we have searched all + // the domains and should try the single label query directly on the wire. + if (question->SearchListIndex == -1) + { + LogMsg("AppendNewSearchDomain: question %##s (%s) SearchListIndex is -1", question->qname.c, DNSTypeName(question->qtype)); + return -1; + } - if (!question->AppendSearchDomains) - { - LogMsg("AppendNewSearchDomain: question %##s (%s) AppendSearchDoamins is 0", question->qname.c, DNSTypeName(question->qtype)); - return -1; - } + if (!question->AppendSearchDomains) + { + LogMsg("AppendNewSearchDomain: question %##s (%s) AppendSearchDoamins is 0", question->qname.c, DNSTypeName(question->qtype)); + return -1; + } - // Save the original name, before we modify them below. - if (!question->qnameOrig) - { - question->qnameOrig = mallocL("AppendNewSearchDomain", sizeof(domainname)); - if (!question->qnameOrig) { LogMsg("AppendNewSearchDomain: ERROR!! malloc failure"); return -1; } - question->qnameOrig->c[0] = 0; - AssignDomainName(question->qnameOrig, &question->qname); - LogInfo("AppendSearchDomain: qnameOrig %##s", question->qnameOrig->c); - } + // Save the original name, before we modify them below. + if (!question->qnameOrig) + { + question->qnameOrig = mallocL("AppendNewSearchDomain", sizeof(domainname)); + if (!question->qnameOrig) { LogMsg("AppendNewSearchDomain: ERROR!! malloc failure"); return -1; } + question->qnameOrig->c[0] = 0; + AssignDomainName(question->qnameOrig, &question->qname); + LogInfo("AppendSearchDomain: qnameOrig %##s", question->qnameOrig->c); + } - sd = uDNS_GetNextSearchDomain(m, question->InterfaceID, &question->SearchListIndex, !question->AppendLocalSearchDomains); - // We use -1 to indicate that we have searched all the domains and should try the single label - // query directly on the wire. uDNS_GetNextSearchDomain should never return a negative value - if (question->SearchListIndex == -1) - { - LogMsg("AppendNewSearchDomain: ERROR!! uDNS_GetNextSearchDomain returned -1"); - return -1; - } + sd = uDNS_GetNextSearchDomain(m, question->InterfaceID, &question->SearchListIndex, !question->AppendLocalSearchDomains); + // We use -1 to indicate that we have searched all the domains and should try the single label + // query directly on the wire. uDNS_GetNextSearchDomain should never return a negative value + if (question->SearchListIndex == -1) + { + LogMsg("AppendNewSearchDomain: ERROR!! uDNS_GetNextSearchDomain returned -1"); + return -1; + } - // Not a common case. Perhaps, we should try the next search domain if it exceeds ? - if (sd && (DomainNameLength(question->qnameOrig) + DomainNameLength(sd)) > MAX_DOMAIN_NAME) - { - LogMsg("AppendNewSearchDomain: ERROR!! exceeding max domain length for %##s (%s) SearchDomain %##s length %d, Question name length %d", question->qnameOrig->c, DNSTypeName(question->qtype), sd->c, DomainNameLength(question->qnameOrig), DomainNameLength(sd)); - return -1; - } + // Not a common case. Perhaps, we should try the next search domain if it exceeds ? + if (sd && (DomainNameLength(question->qnameOrig) + DomainNameLength(sd)) > MAX_DOMAIN_NAME) + { + LogMsg("AppendNewSearchDomain: ERROR!! exceeding max domain length for %##s (%s) SearchDomain %##s length %d, Question name length %d", question->qnameOrig->c, DNSTypeName(question->qtype), sd->c, DomainNameLength(question->qnameOrig), DomainNameLength(sd)); + return -1; + } - // if there are no more search domains and we have already tried this question - // without appending search domains, then we are done. - if (!sd && !ApplySearchDomainsFirst(question)) - { - LogInfo("AppnedNewSearchDomain: No more search domains for question with name %##s (%s), not trying anymore", question->qname.c, DNSTypeName(question->qtype)); - return -1; - } + // if there are no more search domains and we have already tried this question + // without appending search domains, then we are done. + if (!sd && !ApplySearchDomainsFirst(question)) + { + LogInfo("AppnedNewSearchDomain: No more search domains for question with name %##s (%s), not trying anymore", question->qname.c, DNSTypeName(question->qtype)); + return -1; + } - // Stop the question before changing the name as negative cache entries could be pointing at this question. - // Even if we don't change the question in the case of returning 0, the caller is going to restart the - // question. - err = mDNS_StopQuery(&mDNSStorage, question); - if (err) { LogMsg("AppendNewSearchDomain: ERROR!! %##s %s mDNS_StopQuery: %d, while retrying with search domains", question->qname.c, DNSTypeName(question->qtype), (int)err); } + // Stop the question before changing the name as negative cache entries could be pointing at this question. + // Even if we don't change the question in the case of returning 0, the caller is going to restart the + // question. + err = mDNS_StopQuery(&mDNSStorage, question); + if (err) { LogMsg("AppendNewSearchDomain: ERROR!! %##s %s mDNS_StopQuery: %d, while retrying with search domains", question->qname.c, DNSTypeName(question->qtype), (int)err); } - AssignDomainName(&question->qname, question->qnameOrig); - if (sd) - { - AppendDomainName(&question->qname, sd); - LogInfo("AppnedNewSearchDomain: Returning question with name %##s, SearchListIndex %d", question->qname.c, question->SearchListIndex); - return 1; - } + AssignDomainName(&question->qname, question->qnameOrig); + if (sd) + { + AppendDomainName(&question->qname, sd); + LogInfo("AppnedNewSearchDomain: Returning question with name %##s, SearchListIndex %d", question->qname.c, question->SearchListIndex); + return 1; + } - // Try the question as single label - LogInfo("AppnedNewSearchDomain: No more search domains for question with name %##s (%s), trying one last time", question->qname.c, DNSTypeName(question->qtype)); - return 0; - } + // Try the question as single label + LogInfo("AppnedNewSearchDomain: No more search domains for question with name %##s (%s), trying one last time", question->qname.c, DNSTypeName(question->qtype)); + return 0; + } #if APPLE_OSX_mDNSResponder mDNSlocal mDNSBool DomainInSearchList(domainname *domain) - { - const SearchListElem *s; - for (s=SearchList; s; s=s->next) - if (SameDomainName(&s->domain, domain)) return mDNStrue; - return mDNSfalse; - } + { + const SearchListElem *s; + for (s=SearchList; s; s=s->next) + if (SameDomainName(&s->domain, domain)) return mDNStrue; + return mDNSfalse; + } // Workaround for networks using Microsoft Active Directory using "local" as a private internal // top-level domain mDNSlocal mStatus SendAdditionalQuery(DNSQuestion *q, request_state *request, mStatus err) - { - extern domainname ActiveDirectoryPrimaryDomain; - DNSQuestion **question2; - #define VALID_MSAD_SRV_TRANSPORT(T) (SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_tcp") || SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_udp")) - #define VALID_MSAD_SRV(Q) ((Q)->qtype == kDNSType_SRV && VALID_MSAD_SRV_TRANSPORT(SecondLabel(&(Q)->qname))) + { + extern domainname ActiveDirectoryPrimaryDomain; + DNSQuestion **question2; + #define VALID_MSAD_SRV_TRANSPORT(T) (SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_tcp") || SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_udp")) + #define VALID_MSAD_SRV(Q) ((Q)->qtype == kDNSType_SRV && VALID_MSAD_SRV_TRANSPORT(SecondLabel(&(Q)->qname))) - question2 = mDNSNULL; - if (request->hdr.op == query_request) - question2 = &request->u.queryrecord.q2; - else if (request->hdr.op == addrinfo_request) - { - if (q->qtype == kDNSType_A) - question2 = &request->u.addrinfo.q42; - else if (q->qtype == kDNSType_AAAA) - question2 = &request->u.addrinfo.q62; - } - if (!question2) - { - LogMsg("SendAdditionalQuery: question2 NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - return mStatus_BadParamErr; - } + question2 = mDNSNULL; + if (request->hdr.op == query_request) + question2 = &request->u.queryrecord.q2; + else if (request->hdr.op == addrinfo_request) + { + if (q->qtype == kDNSType_A) + question2 = &request->u.addrinfo.q42; + else if (q->qtype == kDNSType_AAAA) + question2 = &request->u.addrinfo.q62; + } + if (!question2) + { + LogMsg("SendAdditionalQuery: question2 NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + return mStatus_BadParamErr; + } - // Sanity check: If we already sent an additonal query, we don't need to send one more. - // - // 1. When the application calls DNSServiceQueryRecord or DNSServiceGetAddrInfo with a .local name, this function - // is called to see whether a unicast query should be sent or not. - // - // 2. As a result of appending search domains, the question may be end up with a .local suffix even though it - // was not a .local name to start with. In that case, queryrecord_result_callback calls this function to - // send the additional query. - // - // Thus, it should not be called more than once. - if (*question2) - { - LogInfo("SendAdditionalQuery: question2 already sent for %##s (%s), no more q2", q->qname.c, DNSTypeName(q->qtype)); - return err; - } + // Sanity check: If we already sent an additonal query, we don't need to send one more. + // + // 1. When the application calls DNSServiceQueryRecord or DNSServiceGetAddrInfo with a .local name, this function + // is called to see whether a unicast query should be sent or not. + // + // 2. As a result of appending search domains, the question may be end up with a .local suffix even though it + // was not a .local name to start with. In that case, queryrecord_result_callback calls this function to + // send the additional query. + // + // Thus, it should not be called more than once. + if (*question2) + { + LogInfo("SendAdditionalQuery: question2 already sent for %##s (%s), no more q2", q->qname.c, DNSTypeName(q->qtype)); + return err; + } - if (!q->ForceMCast && SameDomainLabel(LastLabel(&q->qname), (const mDNSu8 *)&localdomain)) - if (q->qtype == kDNSType_A || q->qtype == kDNSType_AAAA || VALID_MSAD_SRV(q)) - { - DNSQuestion *q2; - int labels = CountLabels(&q->qname); - q2 = mallocL("DNSQuestion", sizeof(DNSQuestion)); - if (!q2) FatalError("ERROR: SendAdditionalQuery malloc"); - *question2 = q2; - *q2 = *q; - q2->InterfaceID = mDNSInterface_Unicast; - q2->ExpectUnique = mDNStrue; - // If the query starts as a single label e.g., somehost, and we have search domains with .local, - // queryrecord_result_callback calls this function when .local is appended to "somehost". - // At that time, the name in "q" is pointing at somehost.local and its qnameOrig pointing at - // "somehost". We need to copy that information so that when we retry with a different search - // domain e.g., mycompany.local, we get "somehost.mycompany.local". - if (q->qnameOrig) - { - (*question2)->qnameOrig = mallocL("SendAdditionalQuery", DomainNameLength(q->qnameOrig)); - if (!(*question2)->qnameOrig) { LogMsg("SendAdditionalQuery: ERROR!! malloc failure"); return mStatus_NoMemoryErr; } - (*question2)->qnameOrig->c[0] = 0; - AssignDomainName((*question2)->qnameOrig, q->qnameOrig); - LogInfo("SendAdditionalQuery: qnameOrig %##s", (*question2)->qnameOrig->c); - } - // For names of the form ".bar.local." we always do a second unicast query in parallel. - // For names of the form ".local." it's less clear whether we should do a unicast query. - // If the name being queried is exactly the same as the name in the DHCP "domain" option (e.g. the DHCP - // "domain" is my-small-company.local, and the user types "my-small-company.local" into their web browser) - // then that's a hint that it's worth doing a unicast query. Otherwise, we first check to see if the - // site's DNS server claims there's an SOA record for "local", and if so, that's also a hint that queries - // for names in the "local" domain will be safely answered privately before they hit the root name servers. - // Note that in the "my-small-company.local" example above there will typically be an SOA record for - // "my-small-company.local" but *not* for "local", which is why the "local SOA" check would fail in that case. - // We need to check against both ActiveDirectoryPrimaryDomain and SearchList. If it matches against either - // of those, we don't want do the SOA check for the local - if (labels == 2 && !SameDomainName(&q->qname, &ActiveDirectoryPrimaryDomain) && !DomainInSearchList(&q->qname)) - { - AssignDomainName(&q2->qname, &localdomain); - q2->qtype = kDNSType_SOA; - q2->LongLived = mDNSfalse; - q2->ForceMCast = mDNSfalse; - q2->ReturnIntermed = mDNStrue; - // Don't append search domains for the .local SOA query - q2->AppendSearchDomains = 0; - q2->AppendLocalSearchDomains = 0; - q2->RetryWithSearchDomains = mDNSfalse; - q2->SearchListIndex = 0; - q2->TimeoutQuestion = 0; - } - LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast", request->sd, q2->qname.c, DNSTypeName(q2->qtype)); - err = mDNS_StartQuery(&mDNSStorage, q2); - if (err) LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q2->qname.c, DNSTypeName(q2->qtype), (int)err); - } - return(err); - } + if (!q->ForceMCast && SameDomainLabel(LastLabel(&q->qname), (const mDNSu8 *)&localdomain)) + if (q->qtype == kDNSType_A || q->qtype == kDNSType_AAAA || VALID_MSAD_SRV(q)) + { + DNSQuestion *q2; + int labels = CountLabels(&q->qname); + q2 = mallocL("DNSQuestion", sizeof(DNSQuestion)); + if (!q2) FatalError("ERROR: SendAdditionalQuery malloc"); + *question2 = q2; + *q2 = *q; + q2->InterfaceID = mDNSInterface_Unicast; + q2->ExpectUnique = mDNStrue; + // If the query starts as a single label e.g., somehost, and we have search domains with .local, + // queryrecord_result_callback calls this function when .local is appended to "somehost". + // At that time, the name in "q" is pointing at somehost.local and its qnameOrig pointing at + // "somehost". We need to copy that information so that when we retry with a different search + // domain e.g., mycompany.local, we get "somehost.mycompany.local". + if (q->qnameOrig) + { + (*question2)->qnameOrig = mallocL("SendAdditionalQuery", DomainNameLength(q->qnameOrig)); + if (!(*question2)->qnameOrig) { LogMsg("SendAdditionalQuery: ERROR!! malloc failure"); return mStatus_NoMemoryErr; } + (*question2)->qnameOrig->c[0] = 0; + AssignDomainName((*question2)->qnameOrig, q->qnameOrig); + LogInfo("SendAdditionalQuery: qnameOrig %##s", (*question2)->qnameOrig->c); + } + // For names of the form ".bar.local." we always do a second unicast query in parallel. + // For names of the form ".local." it's less clear whether we should do a unicast query. + // If the name being queried is exactly the same as the name in the DHCP "domain" option (e.g. the DHCP + // "domain" is my-small-company.local, and the user types "my-small-company.local" into their web browser) + // then that's a hint that it's worth doing a unicast query. Otherwise, we first check to see if the + // site's DNS server claims there's an SOA record for "local", and if so, that's also a hint that queries + // for names in the "local" domain will be safely answered privately before they hit the root name servers. + // Note that in the "my-small-company.local" example above there will typically be an SOA record for + // "my-small-company.local" but *not* for "local", which is why the "local SOA" check would fail in that case. + // We need to check against both ActiveDirectoryPrimaryDomain and SearchList. If it matches against either + // of those, we don't want do the SOA check for the local + if (labels == 2 && !SameDomainName(&q->qname, &ActiveDirectoryPrimaryDomain) && !DomainInSearchList(&q->qname)) + { + AssignDomainName(&q2->qname, &localdomain); + q2->qtype = kDNSType_SOA; + q2->LongLived = mDNSfalse; + q2->ForceMCast = mDNSfalse; + q2->ReturnIntermed = mDNStrue; + // Don't append search domains for the .local SOA query + q2->AppendSearchDomains = 0; + q2->AppendLocalSearchDomains = 0; + q2->RetryWithSearchDomains = mDNSfalse; + q2->SearchListIndex = 0; + q2->TimeoutQuestion = 0; + } + LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast", request->sd, q2->qname.c, DNSTypeName(q2->qtype)); + err = mDNS_StartQuery(&mDNSStorage, q2); + if (err) LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q2->qname.c, DNSTypeName(q2->qtype), (int)err); + } + return(err); + } #endif // APPLE_OSX_mDNSResponder // This function tries to append a search domain if valid and possible. If so, returns true. mDNSlocal mDNSBool RetryQuestionWithSearchDomains(mDNS *const m, DNSQuestion *question, request_state *req) - { - int result; - // RetryWithSearchDomains tells the core to call us back so that we can retry with search domains if there is no - // answer in the cache or /etc/hosts. In the first call back from the core, we clear RetryWithSearchDomains so - // that we don't get called back repeatedly. If we got an answer from the cache or /etc/hosts, we don't touch - // RetryWithSearchDomains which may or may not be set. - // - // If we get e.g., NXDOMAIN and the query is neither suppressed nor exhausted the domain search list and - // is a valid question for appending search domains, retry by appending domains + { + int result; + // RetryWithSearchDomains tells the core to call us back so that we can retry with search domains if there is no + // answer in the cache or /etc/hosts. In the first call back from the core, we clear RetryWithSearchDomains so + // that we don't get called back repeatedly. If we got an answer from the cache or /etc/hosts, we don't touch + // RetryWithSearchDomains which may or may not be set. + // + // If we get e.g., NXDOMAIN and the query is neither suppressed nor exhausted the domain search list and + // is a valid question for appending search domains, retry by appending domains - if (!question->SuppressQuery && question->SearchListIndex != -1 && question->AppendSearchDomains) - { - question->RetryWithSearchDomains = 0; - result = AppendNewSearchDomain(m, question); - // As long as the result is either zero or 1, we retry the question. If we exahaust the search - // domains (result is zero) we try the original query (as it was before appending the search - // domains) as such on the wire as a last resort if we have not tried them before. For queries - // with more than one label, we have already tried them before appending search domains and - // hence don't retry again - if (result != -1) - { - mStatus err; - err = mDNS_StartQuery(m, question); - if (!err) - { - LogOperation("%3d: RetryQuestionWithSearchDomains(%##s, %s), retrying after appending search domain", req->sd, question->qname.c, DNSTypeName(question->qtype)); - // If the result was zero, it meant that there are no search domains and we just retried the question - // as a single label and we should not retry with search domains anymore. - if (!result) question->SearchListIndex = -1; - return mDNStrue; - } - else - { - LogMsg("%3d: ERROR: RetryQuestionWithSearchDomains %##s %s mDNS_StartQuery: %d, while retrying with search domains", req->sd, question->qname.c, DNSTypeName(question->qtype), (int)err); - // We have already stopped the query and could not restart. Reset the appropriate pointers - // so that we don't call stop again when the question terminates - question->QuestionContext = mDNSNULL; - } - } - } - else - { - LogInfo("%3d: RetryQuestionWithSearchDomains: Not appending search domains - SuppressQuery %d, SearchListIndex %d, AppendSearchDomains %d", req->sd, question->SuppressQuery, question->SearchListIndex, question->AppendSearchDomains); - } - return mDNSfalse; - } + if (!question->SuppressQuery && question->SearchListIndex != -1 && question->AppendSearchDomains) + { + question->RetryWithSearchDomains = 0; + result = AppendNewSearchDomain(m, question); + // As long as the result is either zero or 1, we retry the question. If we exahaust the search + // domains (result is zero) we try the original query (as it was before appending the search + // domains) as such on the wire as a last resort if we have not tried them before. For queries + // with more than one label, we have already tried them before appending search domains and + // hence don't retry again + if (result != -1) + { + mStatus err; + err = mDNS_StartQuery(m, question); + if (!err) + { + LogOperation("%3d: RetryQuestionWithSearchDomains(%##s, %s), retrying after appending search domain", req->sd, question->qname.c, DNSTypeName(question->qtype)); + // If the result was zero, it meant that there are no search domains and we just retried the question + // as a single label and we should not retry with search domains anymore. + if (!result) question->SearchListIndex = -1; + return mDNStrue; + } + else + { + LogMsg("%3d: ERROR: RetryQuestionWithSearchDomains %##s %s mDNS_StartQuery: %d, while retrying with search domains", req->sd, question->qname.c, DNSTypeName(question->qtype), (int)err); + // We have already stopped the query and could not restart. Reset the appropriate pointers + // so that we don't call stop again when the question terminates + question->QuestionContext = mDNSNULL; + } + } + } + else + { + LogInfo("%3d: RetryQuestionWithSearchDomains: Not appending search domains - SuppressQuery %d, SearchListIndex %d, AppendSearchDomains %d", req->sd, question->SuppressQuery, question->SearchListIndex, question->AppendSearchDomains); + } + return mDNSfalse; + } mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) - { - char name[MAX_ESCAPED_DOMAIN_NAME]; - request_state *req = question->QuestionContext; - reply_state *rep; - char *data; - size_t len; - DNSServiceErrorType error = kDNSServiceErr_NoError; - DNSQuestion *q = mDNSNULL; + { + char name[MAX_ESCAPED_DOMAIN_NAME]; + request_state *req = question->QuestionContext; + reply_state *rep; + char *data; + size_t len; + DNSServiceErrorType error = kDNSServiceErr_NoError; + DNSQuestion *q = mDNSNULL; #if APPLE_OSX_mDNSResponder - { - // Sanity check: QuestionContext is set to NULL after we stop the question and hence we should not - // get any callbacks from the core after this. - if (!req) - { - LogMsg("queryrecord_result_callback: ERROR!! QuestionContext NULL for %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); - return; - } - if (req->hdr.op == query_request && question == req->u.queryrecord.q2) - q = &req->u.queryrecord.q; - else if (req->hdr.op == addrinfo_request && question == req->u.addrinfo.q42) - q = &req->u.addrinfo.q4; - else if (req->hdr.op == addrinfo_request && question == req->u.addrinfo.q62) - q = &req->u.addrinfo.q6; - - if (q && question->qtype != q->qtype && !SameDomainName(&question->qname, &q->qname)) - { - mStatus err; - domainname *orig = question->qnameOrig; + { + // Sanity check: QuestionContext is set to NULL after we stop the question and hence we should not + // get any callbacks from the core after this. + if (!req) + { + LogMsg("queryrecord_result_callback: ERROR!! QuestionContext NULL for %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + return; + } + if (req->hdr.op == query_request && question == req->u.queryrecord.q2) + q = &req->u.queryrecord.q; + else if (req->hdr.op == addrinfo_request && question == req->u.addrinfo.q42) + q = &req->u.addrinfo.q4; + else if (req->hdr.op == addrinfo_request && question == req->u.addrinfo.q62) + q = &req->u.addrinfo.q6; - LogInfo("queryrecord_result_callback: Stopping q2 local %##s", question->qname.c); - mDNS_StopQuery(m, question); - question->QuestionContext = mDNSNULL; + if (q && question->qtype != q->qtype && !SameDomainName(&question->qname, &q->qname)) + { + mStatus err; + domainname *orig = question->qnameOrig; - // We got a negative response for the SOA record indicating that .local does not exist. - // But we might have other search domains (that does not end in .local) that can be - // appended to this question. In that case, we want to retry the question. Otherwise, - // we don't want to try this question as unicast. - if (answer->RecordType == kDNSRecordTypePacketNegative && !q->AppendSearchDomains) - { - LogInfo("queryrecord_result_callback: question %##s AppendSearchDomains zero", q->qname.c); - return; - } + LogInfo("queryrecord_result_callback: Stopping q2 local %##s", question->qname.c); + mDNS_StopQuery(m, question); + question->QuestionContext = mDNSNULL; - // If we got a non-negative answer for our "local SOA" test query, start an additional parallel unicast query - // - // Note: When we copy the original question, we copy everything including the AppendSearchDomains, - // RetryWithSearchDomains except for qnameOrig which can be non-NULL if the original question is - // e.g., somehost and then we appended e.g., ".local" and retried that question. See comment in - // SendAdditionalQuery as to how qnameOrig gets initialized. - *question = *q; - question->InterfaceID = mDNSInterface_Unicast; - question->ExpectUnique = mDNStrue; - question->qnameOrig = orig; + // We got a negative response for the SOA record indicating that .local does not exist. + // But we might have other search domains (that does not end in .local) that can be + // appended to this question. In that case, we want to retry the question. Otherwise, + // we don't want to try this question as unicast. + if (answer->RecordType == kDNSRecordTypePacketNegative && !q->AppendSearchDomains) + { + LogInfo("queryrecord_result_callback: question %##s AppendSearchDomains zero", q->qname.c); + return; + } - LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast, context %p", req->sd, question->qname.c, DNSTypeName(question->qtype), question->QuestionContext); + // If we got a non-negative answer for our "local SOA" test query, start an additional parallel unicast query + // + // Note: When we copy the original question, we copy everything including the AppendSearchDomains, + // RetryWithSearchDomains except for qnameOrig which can be non-NULL if the original question is + // e.g., somehost and then we appended e.g., ".local" and retried that question. See comment in + // SendAdditionalQuery as to how qnameOrig gets initialized. + *question = *q; + question->InterfaceID = mDNSInterface_Unicast; + question->ExpectUnique = mDNStrue; + question->qnameOrig = orig; - // If the original question timed out, its QuestionContext would already be set to NULL and that's what we copied above. - // Hence, we need to set it explicitly here. - question->QuestionContext = req; - err = mDNS_StartQuery(m, question); - if (err) LogMsg("%3d: ERROR: queryrecord_result_callback %##s %s mDNS_StartQuery: %d", req->sd, question->qname.c, DNSTypeName(question->qtype), (int)err); + LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast, context %p", req->sd, question->qname.c, DNSTypeName(question->qtype), question->QuestionContext); - // If we got a positive response to local SOA, then try the .local question as unicast - if (answer->RecordType != kDNSRecordTypePacketNegative) return; + // If the original question timed out, its QuestionContext would already be set to NULL and that's what we copied above. + // Hence, we need to set it explicitly here. + question->QuestionContext = req; + err = mDNS_StartQuery(m, question); + if (err) LogMsg("%3d: ERROR: queryrecord_result_callback %##s %s mDNS_StartQuery: %d", req->sd, question->qname.c, DNSTypeName(question->qtype), (int)err); - // Fall through and get the next search domain. The question is pointing at .local - // and we don't want to try that. Try the next search domain. Don't try with local - // search domains for the unicast question anymore. - // - // Note: we started the question above which will be stopped immediately (never sent on the wire) - // before we pick the next search domain below. RetryQuestionWithSearchDomains assumes that the - // question has already started. - question->AppendLocalSearchDomains = 0; - } + // If we got a positive response to local SOA, then try the .local question as unicast + if (answer->RecordType != kDNSRecordTypePacketNegative) return; - if (q && AddRecord && (question->InterfaceID == mDNSInterface_Unicast) && !answer->rdlength) - { - // If we get a negative response to the unicast query that we sent above, retry after appending search domains - // Note: We could have appended search domains below (where do it for regular unicast questions) instead of doing it here. - // As we ignore negative unicast answers below, we would never reach the code where the search domains are appended. - // To keep things simple, we handle unicast ".local" separately here. - LogInfo("queryrecord_result_callback: Retrying .local question %##s (%s) as unicast after appending search domains", question->qname.c, DNSTypeName(question->qtype)); - if (RetryQuestionWithSearchDomains(m, question, req)) - return; - if (question->AppendSearchDomains && !question->AppendLocalSearchDomains && IsLocalDomain(&question->qname)) - { - // If "local" is the last search domain, we need to stop the question so that we don't send the "local" - // question on the wire as we got a negative response for the local SOA. But, we can't stop the question - // yet as we may have to timeout the question (done by the "core") for which we need to leave the question - // in the list. We leave it disabled so that it does not hit the wire. - LogInfo("queryrecord_result_callback: Disabling .local question %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); - question->ThisQInterval = 0; - } - } - // If we are here it means that either "question" is not "q2" OR we got a positive response for "q2" OR we have no more search - // domains to append for "q2". In all cases, fall through and deliver the response - } + // Fall through and get the next search domain. The question is pointing at .local + // and we don't want to try that. Try the next search domain. Don't try with local + // search domains for the unicast question anymore. + // + // Note: we started the question above which will be stopped immediately (never sent on the wire) + // before we pick the next search domain below. RetryQuestionWithSearchDomains assumes that the + // question has already started. + question->AppendLocalSearchDomains = 0; + } + + if (q && AddRecord && (question->InterfaceID == mDNSInterface_Unicast) && !answer->rdlength) + { + // If we get a negative response to the unicast query that we sent above, retry after appending search domains + // Note: We could have appended search domains below (where do it for regular unicast questions) instead of doing it here. + // As we ignore negative unicast answers below, we would never reach the code where the search domains are appended. + // To keep things simple, we handle unicast ".local" separately here. + LogInfo("queryrecord_result_callback: Retrying .local question %##s (%s) as unicast after appending search domains", question->qname.c, DNSTypeName(question->qtype)); + if (RetryQuestionWithSearchDomains(m, question, req)) + return; + if (question->AppendSearchDomains && !question->AppendLocalSearchDomains && IsLocalDomain(&question->qname)) + { + // If "local" is the last search domain, we need to stop the question so that we don't send the "local" + // question on the wire as we got a negative response for the local SOA. But, we can't stop the question + // yet as we may have to timeout the question (done by the "core") for which we need to leave the question + // in the list. We leave it disabled so that it does not hit the wire. + LogInfo("queryrecord_result_callback: Disabling .local question %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + question->ThisQInterval = 0; + } + } + // If we are here it means that either "question" is not "q2" OR we got a positive response for "q2" OR we have no more search + // domains to append for "q2". In all cases, fall through and deliver the response + } #endif // APPLE_OSX_mDNSResponder - if (answer->RecordType == kDNSRecordTypePacketNegative) - { - // If this question needs to be timed out and we have reached the stop time, mark - // the error as timeout. It is possible that we might get a negative response from an - // external DNS server at the same time when this question reaches its stop time. We - // can't tell the difference as there is no indication in the callback. This should - // be okay as we will be timing out this query anyway. - mDNS_Lock(m); - if (question->TimeoutQuestion) - { - if ((m->timenow - question->StopTime) >= 0) - { - LogInfo("queryrecord_result_callback:Question %##s (%s) timing out, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID); - error = kDNSServiceErr_Timeout; - } - } - mDNS_Unlock(m); - // When we're doing parallel unicast and multicast queries for dot-local names (for supporting Microsoft - // Active Directory sites) we need to ignore negative unicast answers. Otherwise we'll generate negative - // answers for just about every single multicast name we ever look up, since the Microsoft Active Directory - // server is going to assert that pretty much every single multicast name doesn't exist. - // - // If we are timing out this query, we need to deliver the negative answer to the application - if (error != kDNSServiceErr_Timeout) - { - if (!answer->InterfaceID && IsLocalDomain(answer->name)) - { - LogInfo("queryrecord_result_callback:Question %##s (%s) answering local with unicast", question->qname.c, DNSTypeName(question->qtype)); - return; - } - error = kDNSServiceErr_NoSuchRecord; - } - AddRecord = mDNStrue; - } - // If we get a negative answer, try appending search domains. Don't append search domains - // - if we are timing out this question - // - if the negative response was received as a result of a multicast query - // - if this is an additional query (q2), we already appended search domains above (indicated by "!q" below) - if (error != kDNSServiceErr_Timeout) - { - if (!q && !answer->InterfaceID && !answer->rdlength && AddRecord) - { - // If the original question did not end in .local, we did not send an SOA query - // to figure out whether we should send an additional unicast query or not. If we just - // appended .local, we need to see if we need to send an additional query. This should - // normally happen just once because after we append .local, we ignore all negative - // responses for .local above. - LogInfo("queryrecord_result_callback: Retrying question %##s (%s) after appending search domains", question->qname.c, DNSTypeName(question->qtype)); - if (RetryQuestionWithSearchDomains(m, question, req)) - { - // Note: We need to call SendAdditionalQuery every time after appending a search domain as .local could - // be anywhere in the search domain list. + if (answer->RecordType == kDNSRecordTypePacketNegative) + { + // If this question needs to be timed out and we have reached the stop time, mark + // the error as timeout. It is possible that we might get a negative response from an + // external DNS server at the same time when this question reaches its stop time. We + // can't tell the difference as there is no indication in the callback. This should + // be okay as we will be timing out this query anyway. + mDNS_Lock(m); + if (question->TimeoutQuestion) + { + if ((m->timenow - question->StopTime) >= 0) + { + LogInfo("queryrecord_result_callback:Question %##s (%s) timing out, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID); + error = kDNSServiceErr_Timeout; + } + } + mDNS_Unlock(m); + // When we're doing parallel unicast and multicast queries for dot-local names (for supporting Microsoft + // Active Directory sites) we need to ignore negative unicast answers. Otherwise we'll generate negative + // answers for just about every single multicast name we ever look up, since the Microsoft Active Directory + // server is going to assert that pretty much every single multicast name doesn't exist. + // + // If we are timing out this query, we need to deliver the negative answer to the application + if (error != kDNSServiceErr_Timeout) + { + if (!answer->InterfaceID && IsLocalDomain(answer->name)) + { + LogInfo("queryrecord_result_callback:Question %##s (%s) answering local with unicast", question->qname.c, DNSTypeName(question->qtype)); + return; + } + error = kDNSServiceErr_NoSuchRecord; + } + AddRecord = mDNStrue; + } + // If we get a negative answer, try appending search domains. Don't append search domains + // - if we are timing out this question + // - if the negative response was received as a result of a multicast query + // - if this is an additional query (q2), we already appended search domains above (indicated by "!q" below) + if (error != kDNSServiceErr_Timeout) + { + if (!q && !answer->InterfaceID && !answer->rdlength && AddRecord) + { + // If the original question did not end in .local, we did not send an SOA query + // to figure out whether we should send an additional unicast query or not. If we just + // appended .local, we need to see if we need to send an additional query. This should + // normally happen just once because after we append .local, we ignore all negative + // responses for .local above. + LogInfo("queryrecord_result_callback: Retrying question %##s (%s) after appending search domains", question->qname.c, DNSTypeName(question->qtype)); + if (RetryQuestionWithSearchDomains(m, question, req)) + { + // Note: We need to call SendAdditionalQuery every time after appending a search domain as .local could + // be anywhere in the search domain list. #if APPLE_OSX_mDNSResponder - mStatus err = mStatus_NoError; - err = SendAdditionalQuery(question, req, err); - if (err) LogMsg("queryrecord_result_callback: Sending .local SOA query failed, after appending domains"); + mStatus err = mStatus_NoError; + err = SendAdditionalQuery(question, req, err); + if (err) LogMsg("queryrecord_result_callback: Sending .local SOA query failed, after appending domains"); #endif // APPLE_OSX_mDNSResponder - return; - } - } - } + return; + } + } + } - ConvertDomainNameToCString(answer->name, name); + ConvertDomainNameToCString(answer->name, name); - LogOperation("%3d: %s(%##s, %s) %s %s", req->sd, - req->hdr.op == query_request ? "DNSServiceQueryRecord" : "DNSServiceGetAddrInfo", - question->qname.c, DNSTypeName(question->qtype), AddRecord ? "ADD" : "RMV", RRDisplayString(m, answer)); + LogOperation("%3d: %s(%##s, %s) %s %s", req->sd, + req->hdr.op == query_request ? "DNSServiceQueryRecord" : "DNSServiceGetAddrInfo", + question->qname.c, DNSTypeName(question->qtype), AddRecord ? "ADD" : "RMV", RRDisplayString(m, answer)); - len = sizeof(DNSServiceFlags); // calculate reply data length - len += sizeof(mDNSu32); // interface index - len += sizeof(DNSServiceErrorType); - len += strlen(name) + 1; - len += 3 * sizeof(mDNSu16); // type, class, rdlen - len += answer->rdlength; - len += sizeof(mDNSu32); // TTL + len = sizeof(DNSServiceFlags); // calculate reply data length + len += sizeof(mDNSu32); // interface index + len += sizeof(DNSServiceErrorType); + len += strlen(name) + 1; + len += 3 * sizeof(mDNSu16); // type, class, rdlen + len += answer->rdlength; + len += sizeof(mDNSu32); // TTL - rep = create_reply(req->hdr.op == query_request ? query_reply_op : addrinfo_reply_op, len, req); + rep = create_reply(req->hdr.op == query_request ? query_reply_op : addrinfo_reply_op, len, req); - rep->rhdr->flags = dnssd_htonl(AddRecord ? kDNSServiceFlagsAdd : 0); - // Call mDNSPlatformInterfaceIndexfromInterfaceID, but suppressNetworkChange (last argument). Otherwise, if the - // InterfaceID is not valid, then it simulates a "NetworkChanged" which in turn makes questions - // to be stopped and started including *this* one. Normally the InterfaceID is valid. But when we - // are using the /etc/hosts entries to answer a question, the InterfaceID may not be known to the - // mDNS core . Eventually, we should remove the calls to "NetworkChanged" in - // mDNSPlatformInterfaceIndexfromInterfaceID when it can't find InterfaceID as ResourceRecords - // should not have existed to answer this question if the corresponding interface is not valid. - rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNStrue)); - rep->rhdr->error = dnssd_htonl(error); + rep->rhdr->flags = dnssd_htonl(AddRecord ? kDNSServiceFlagsAdd : 0); + // Call mDNSPlatformInterfaceIndexfromInterfaceID, but suppressNetworkChange (last argument). Otherwise, if the + // InterfaceID is not valid, then it simulates a "NetworkChanged" which in turn makes questions + // to be stopped and started including *this* one. Normally the InterfaceID is valid. But when we + // are using the /etc/hosts entries to answer a question, the InterfaceID may not be known to the + // mDNS core . Eventually, we should remove the calls to "NetworkChanged" in + // mDNSPlatformInterfaceIndexfromInterfaceID when it can't find InterfaceID as ResourceRecords + // should not have existed to answer this question if the corresponding interface is not valid. + rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNStrue)); + rep->rhdr->error = dnssd_htonl(error); - data = (char *)&rep->rhdr[1]; + data = (char *)&rep->rhdr[1]; - put_string(name, &data); - put_uint16(answer->rrtype, &data); - put_uint16(answer->rrclass, &data); - put_uint16(answer->rdlength, &data); - // We need to use putRData here instead of the crude put_rdata function, because the crude put_rdata - // function just does a blind memory copy without regard to structures that may have holes in them. - if (answer->rdlength) - if (!putRData(mDNSNULL, (mDNSu8 *)data, (mDNSu8 *)rep->rhdr + len, answer)) - LogMsg("queryrecord_result_callback putRData failed %d", (mDNSu8 *)rep->rhdr + len - (mDNSu8 *)data); - data += answer->rdlength; - put_uint32(AddRecord ? answer->rroriginalttl : 0, &data); + put_string(name, &data); + put_uint16(answer->rrtype, &data); + put_uint16(answer->rrclass, &data); + put_uint16(answer->rdlength, &data); + // We need to use putRData here instead of the crude put_rdata function, because the crude put_rdata + // function just does a blind memory copy without regard to structures that may have holes in them. + if (answer->rdlength) + if (!putRData(mDNSNULL, (mDNSu8 *)data, (mDNSu8 *)rep->rhdr + len, answer)) + LogMsg("queryrecord_result_callback putRData failed %d", (mDNSu8 *)rep->rhdr + len - (mDNSu8 *)data); + data += answer->rdlength; + put_uint32(AddRecord ? answer->rroriginalttl : 0, &data); - append_reply(req, rep); - // Stop the question, if we just timed out - if (error == kDNSServiceErr_Timeout) - { - mDNS_StopQuery(m, question); - // Reset the pointers so that we don't call stop on termination - question->QuestionContext = mDNSNULL; - } + append_reply(req, rep); + // Stop the question, if we just timed out + if (error == kDNSServiceErr_Timeout) + { + mDNS_StopQuery(m, question); + // Reset the pointers so that we don't call stop on termination + question->QuestionContext = mDNSNULL; + } #if APPLE_OSX_mDNSResponder #if ! NO_WCF - CHECK_WCF_FUNCTION(WCFIsServerRunning) - { - struct xucred x; - socklen_t xucredlen = sizeof(x); - - if (WCFIsServerRunning((WCFConnection *)m->WCF) && answer->rdlength != 0) - { - if (getsockopt(req->sd, 0, LOCAL_PEERCRED, &x, &xucredlen) >= 0 && - (x.cr_version == XUCRED_VERSION)) - { - struct sockaddr_storage addr; - const RDataBody2 *const rdb = (RDataBody2 *)answer->rdata->u.data; - addr.ss_len = 0; - if (answer->rrtype == kDNSType_A || answer->rrtype == kDNSType_AAAA) - { - if (answer->rrtype == kDNSType_A) - { - struct sockaddr_in *sin = (struct sockaddr_in *)&addr; - sin->sin_port = 0; - if (!putRData(mDNSNULL, (mDNSu8 *)&sin->sin_addr, (mDNSu8 *)(&sin->sin_addr + sizeof(rdb->ipv4)), answer)) - LogMsg("queryrecord_result_callback: WCF AF_INET putRData failed"); - else - { - addr.ss_len = sizeof (struct sockaddr_in); - addr.ss_family = AF_INET; - } - } - else if (answer->rrtype == kDNSType_AAAA) - { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr; - sin6->sin6_port = 0; - if (!putRData(mDNSNULL, (mDNSu8 *)&sin6->sin6_addr, (mDNSu8 *)(&sin6->sin6_addr + sizeof(rdb->ipv6)), answer)) - LogMsg("queryrecord_result_callback: WCF AF_INET6 putRData failed"); - else - { - addr.ss_len = sizeof (struct sockaddr_in6); - addr.ss_family = AF_INET6; - } - } - if (addr.ss_len) - { - debugf("queryrecord_result_callback: Name %s, uid %u, addr length %d", name, x.cr_uid, addr.ss_len); - CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr) - { - WCFNameResolvesToAddr(m->WCF, name, (struct sockaddr *)&addr, x.cr_uid); - } - } - } - else if (answer->rrtype == kDNSType_CNAME) - { - domainname cname; - char cname_cstr[MAX_ESCAPED_DOMAIN_NAME]; - if (!putRData(mDNSNULL, cname.c, (mDNSu8 *)(cname.c + MAX_DOMAIN_NAME), answer)) - LogMsg("queryrecord_result_callback: WCF CNAME putRData failed"); - else - { - ConvertDomainNameToCString(&cname, cname_cstr); - CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr) - { - WCFNameResolvesToName(m->WCF, name, cname_cstr, x.cr_uid); - } - } - } - } - else my_perror("queryrecord_result_callback: ERROR: getsockopt LOCAL_PEERCRED"); - } - } + CHECK_WCF_FUNCTION(WCFIsServerRunning) + { + struct xucred x; + socklen_t xucredlen = sizeof(x); + + if (WCFIsServerRunning((WCFConnection *)m->WCF) && answer->rdlength != 0) + { + if (getsockopt(req->sd, 0, LOCAL_PEERCRED, &x, &xucredlen) >= 0 && + (x.cr_version == XUCRED_VERSION)) + { + struct sockaddr_storage addr; + const RDataBody2 *const rdb = (RDataBody2 *)answer->rdata->u.data; + addr.ss_len = 0; + if (answer->rrtype == kDNSType_A || answer->rrtype == kDNSType_AAAA) + { + if (answer->rrtype == kDNSType_A) + { + struct sockaddr_in *sin = (struct sockaddr_in *)&addr; + sin->sin_port = 0; + if (!putRData(mDNSNULL, (mDNSu8 *)&sin->sin_addr, (mDNSu8 *)(&sin->sin_addr + sizeof(rdb->ipv4)), answer)) + LogMsg("queryrecord_result_callback: WCF AF_INET putRData failed"); + else + { + addr.ss_len = sizeof (struct sockaddr_in); + addr.ss_family = AF_INET; + } + } + else if (answer->rrtype == kDNSType_AAAA) + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr; + sin6->sin6_port = 0; + if (!putRData(mDNSNULL, (mDNSu8 *)&sin6->sin6_addr, (mDNSu8 *)(&sin6->sin6_addr + sizeof(rdb->ipv6)), answer)) + LogMsg("queryrecord_result_callback: WCF AF_INET6 putRData failed"); + else + { + addr.ss_len = sizeof (struct sockaddr_in6); + addr.ss_family = AF_INET6; + } + } + if (addr.ss_len) + { + debugf("queryrecord_result_callback: Name %s, uid %u, addr length %d", name, x.cr_uid, addr.ss_len); + CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr) + { + WCFNameResolvesToAddr(m->WCF, name, (struct sockaddr *)&addr, x.cr_uid); + } + } + } + else if (answer->rrtype == kDNSType_CNAME) + { + domainname cname; + char cname_cstr[MAX_ESCAPED_DOMAIN_NAME]; + if (!putRData(mDNSNULL, cname.c, (mDNSu8 *)(cname.c + MAX_DOMAIN_NAME), answer)) + LogMsg("queryrecord_result_callback: WCF CNAME putRData failed"); + else + { + ConvertDomainNameToCString(&cname, cname_cstr); + CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr) + { + WCFNameResolvesToName(m->WCF, name, cname_cstr, x.cr_uid); + } + } + } + } + else my_perror("queryrecord_result_callback: ERROR: getsockopt LOCAL_PEERCRED"); + } + } #endif #endif - } + } mDNSlocal void queryrecord_termination_callback(request_state *request) - { - LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) STOP", - request->sd, request->u.queryrecord.q.qname.c, DNSTypeName(request->u.queryrecord.q.qtype)); - if (request->u.queryrecord.q.QuestionContext) - { - mDNS_StopQuery(&mDNSStorage, &request->u.queryrecord.q); // no need to error check - request->u.queryrecord.q.QuestionContext = mDNSNULL; - } - else - { - DNSQuestion *question = &request->u.queryrecord.q; - LogInfo("queryrecord_termination_callback: question %##s (%s) already stopped, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID); - } + { + LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) STOP", + request->sd, request->u.queryrecord.q.qname.c, DNSTypeName(request->u.queryrecord.q.qtype)); + if (request->u.queryrecord.q.QuestionContext) + { + mDNS_StopQuery(&mDNSStorage, &request->u.queryrecord.q); // no need to error check + request->u.queryrecord.q.QuestionContext = mDNSNULL; + } + else + { + DNSQuestion *question = &request->u.queryrecord.q; + LogInfo("queryrecord_termination_callback: question %##s (%s) already stopped, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID); + } - if (request->u.queryrecord.q.qnameOrig) - { - freeL("QueryTermination", request->u.queryrecord.q.qnameOrig); - request->u.queryrecord.q.qnameOrig = mDNSNULL; - } - if (request->u.queryrecord.q.InterfaceID == mDNSInterface_P2P || (!request->u.queryrecord.q.InterfaceID && SameDomainName((const domainname *)LastLabel(&request->u.queryrecord.q.qname), &localdomain) && (request->flags & kDNSServiceFlagsIncludeP2P))) - { - LogInfo("queryrecord_termination_callback: calling external_stop_browsing_for_service()"); - external_stop_browsing_for_service(&mDNSStorage, &request->u.queryrecord.q.qname, request->u.queryrecord.q.qtype); - } - if (request->u.queryrecord.q2) - { - if (request->u.queryrecord.q2->QuestionContext) - { - LogInfo("queryrecord_termination_callback: Stopping q2 %##s", request->u.queryrecord.q2->qname.c); - mDNS_StopQuery(&mDNSStorage, request->u.queryrecord.q2); - } - else - { - DNSQuestion *question = request->u.queryrecord.q2; - LogInfo("queryrecord_termination_callback: q2 %##s (%s) already stopped, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID); - } - if (request->u.queryrecord.q2->qnameOrig) - { - LogInfo("queryrecord_termination_callback: freeing q2 qnameOrig %##s", request->u.queryrecord.q2->qnameOrig->c); - freeL("QueryTermination q2", request->u.queryrecord.q2->qnameOrig); - request->u.queryrecord.q2->qnameOrig = mDNSNULL; - } - freeL("queryrecord Q2", request->u.queryrecord.q2); - request->u.queryrecord.q2 = mDNSNULL; - } - } + if (request->u.queryrecord.q.qnameOrig) + { + freeL("QueryTermination", request->u.queryrecord.q.qnameOrig); + request->u.queryrecord.q.qnameOrig = mDNSNULL; + } + if (request->u.queryrecord.q.InterfaceID == mDNSInterface_P2P || (!request->u.queryrecord.q.InterfaceID && SameDomainName((const domainname *)LastLabel(&request->u.queryrecord.q.qname), &localdomain) && (request->flags & kDNSServiceFlagsIncludeP2P))) + { + LogInfo("queryrecord_termination_callback: calling external_stop_browsing_for_service()"); + external_stop_browsing_for_service(&mDNSStorage, &request->u.queryrecord.q.qname, request->u.queryrecord.q.qtype); + } + if (request->u.queryrecord.q2) + { + if (request->u.queryrecord.q2->QuestionContext) + { + LogInfo("queryrecord_termination_callback: Stopping q2 %##s", request->u.queryrecord.q2->qname.c); + mDNS_StopQuery(&mDNSStorage, request->u.queryrecord.q2); + } + else + { + DNSQuestion *question = request->u.queryrecord.q2; + LogInfo("queryrecord_termination_callback: q2 %##s (%s) already stopped, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID); + } + if (request->u.queryrecord.q2->qnameOrig) + { + LogInfo("queryrecord_termination_callback: freeing q2 qnameOrig %##s", request->u.queryrecord.q2->qnameOrig->c); + freeL("QueryTermination q2", request->u.queryrecord.q2->qnameOrig); + request->u.queryrecord.q2->qnameOrig = mDNSNULL; + } + freeL("queryrecord Q2", request->u.queryrecord.q2); + request->u.queryrecord.q2 = mDNSNULL; + } + } mDNSlocal mStatus handle_queryrecord_request(request_state *request) - { - DNSQuestion *const q = &request->u.queryrecord.q; - char name[256]; - mDNSu16 rrtype, rrclass; - mStatus err; + { + DNSQuestion *const q = &request->u.queryrecord.q; + char name[256]; + mDNSu16 rrtype, rrclass; + mStatus err; - DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); - mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); - mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); - if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr); + DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); + mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); + mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); + if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr); - if (get_string(&request->msgptr, request->msgend, name, 256) < 0) return(mStatus_BadParamErr); - rrtype = get_uint16(&request->msgptr, request->msgend); - rrclass = get_uint16(&request->msgptr, request->msgend); + if (get_string(&request->msgptr, request->msgend, name, 256) < 0) return(mStatus_BadParamErr); + rrtype = get_uint16(&request->msgptr, request->msgend); + rrclass = get_uint16(&request->msgptr, request->msgend); - if (!request->msgptr) - { LogMsg("%3d: DNSServiceQueryRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } + if (!request->msgptr) + { LogMsg("%3d: DNSServiceQueryRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } - request->flags = flags; - mDNSPlatformMemZero(&request->u.queryrecord, sizeof(request->u.queryrecord)); + request->flags = flags; + mDNSPlatformMemZero(&request->u.queryrecord, sizeof(request->u.queryrecord)); - q->InterfaceID = InterfaceID; - q->Target = zeroAddr; - if (!MakeDomainNameFromDNSNameString(&q->qname, name)) return(mStatus_BadParamErr); + q->InterfaceID = InterfaceID; + q->Target = zeroAddr; + if (!MakeDomainNameFromDNSNameString(&q->qname, name)) return(mStatus_BadParamErr); #if 0 - if (!AuthorizedDomain(request, &q->qname, AutoBrowseDomains)) return (mStatus_NoError); + if (!AuthorizedDomain(request, &q->qname, AutoBrowseDomains)) return (mStatus_NoError); #endif - q->qtype = rrtype; - q->qclass = rrclass; - q->LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0; - q->ExpectUnique = mDNSfalse; - q->ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; - q->ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; - q->SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable ) != 0; - q->TimeoutQuestion = (flags & kDNSServiceFlagsTimeout ) != 0; - q->WakeOnResolve = 0; - q->QuestionCallback = queryrecord_result_callback; - q->QuestionContext = request; - q->SearchListIndex = 0; + q->qtype = rrtype; + q->qclass = rrclass; + q->LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0; + q->ExpectUnique = mDNSfalse; + q->ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; + q->ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; + q->SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable ) != 0; + q->TimeoutQuestion = (flags & kDNSServiceFlagsTimeout ) != 0; + q->WakeOnResolve = 0; + q->QuestionCallback = queryrecord_result_callback; + q->QuestionContext = request; + q->SearchListIndex = 0; - // Don't append search domains for fully qualified domain names including queries - // such as e.g., "abc." that has only one label. We convert all names to FQDNs as internally - // we only deal with FQDNs. Hence, we cannot look at qname to figure out whether we should - // append search domains or not. So, we record that information in AppendSearchDomains. - // - // We append search domains only for queries that are a single label. If overriden using - // command line argument "AlwaysAppendSearchDomains", then we do it for any query which - // is not fully qualified. + // Don't append search domains for fully qualified domain names including queries + // such as e.g., "abc." that has only one label. We convert all names to FQDNs as internally + // we only deal with FQDNs. Hence, we cannot look at qname to figure out whether we should + // append search domains or not. So, we record that information in AppendSearchDomains. + // + // We append search domains only for queries that are a single label. If overriden using + // command line argument "AlwaysAppendSearchDomains", then we do it for any query which + // is not fully qualified. - if ((rrtype == kDNSType_A || rrtype == kDNSType_AAAA) && name[strlen(name) - 1] != '.' && - (AlwaysAppendSearchDomains || CountLabels(&q->qname) == 1)) - { - q->AppendSearchDomains = 1; - q->AppendLocalSearchDomains = 1; - } - else - { - q->AppendSearchDomains = 0; - q->AppendLocalSearchDomains = 0; - } + if ((rrtype == kDNSType_A || rrtype == kDNSType_AAAA) && name[strlen(name) - 1] != '.' && + (AlwaysAppendSearchDomains || CountLabels(&q->qname) == 1)) + { + q->AppendSearchDomains = 1; + q->AppendLocalSearchDomains = 1; + } + else + { + q->AppendSearchDomains = 0; + q->AppendLocalSearchDomains = 0; + } - // For single label queries that are not fully qualified, look at /etc/hosts, cache and try - // search domains before trying them on the wire as a single label query. RetryWithSearchDomains - // tell the core to call back into the UDS layer if there is no valid response in /etc/hosts or - // the cache - q->RetryWithSearchDomains = ApplySearchDomainsFirst(q) ? 1 : 0; - q->qnameOrig = mDNSNULL; + // For single label queries that are not fully qualified, look at /etc/hosts, cache and try + // search domains before trying them on the wire as a single label query. RetryWithSearchDomains + // tell the core to call back into the UDS layer if there is no valid response in /etc/hosts or + // the cache + q->RetryWithSearchDomains = ApplySearchDomainsFirst(q) ? 1 : 0; + q->qnameOrig = mDNSNULL; - LogOperation("%3d: DNSServiceQueryRecord(%X, %d, %##s, %s) START", request->sd, flags, interfaceIndex, q->qname.c, DNSTypeName(q->qtype)); - err = mDNS_StartQuery(&mDNSStorage, q); - if (err) LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q->qname.c, DNSTypeName(q->qtype), (int)err); - else - { - request->terminate = queryrecord_termination_callback; - if (q->InterfaceID == mDNSInterface_P2P || (!q->InterfaceID && SameDomainName((const domainname *)LastLabel(&q->qname), &localdomain) && (flags & kDNSServiceFlagsIncludeP2P))) - { - LogInfo("handle_queryrecord_request: calling external_start_browsing_for_service()"); - external_start_browsing_for_service(&mDNSStorage, &q->qname, q->qtype); - } - } + LogOperation("%3d: DNSServiceQueryRecord(%X, %d, %##s, %s) START", request->sd, flags, interfaceIndex, q->qname.c, DNSTypeName(q->qtype)); + err = mDNS_StartQuery(&mDNSStorage, q); + if (err) LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q->qname.c, DNSTypeName(q->qtype), (int)err); + else + { + request->terminate = queryrecord_termination_callback; + if (q->InterfaceID == mDNSInterface_P2P || (!q->InterfaceID && SameDomainName((const domainname *)LastLabel(&q->qname), &localdomain) && (flags & kDNSServiceFlagsIncludeP2P))) + { + LogInfo("handle_queryrecord_request: calling external_start_browsing_for_service()"); + external_start_browsing_for_service(&mDNSStorage, &q->qname, q->qtype); + } + } #if APPLE_OSX_mDNSResponder - err = SendAdditionalQuery(q, request, err); + err = SendAdditionalQuery(q, request, err); #endif // APPLE_OSX_mDNSResponder - return(err); - } + return(err); + } // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -3028,113 +3030,113 @@ mDNSlocal mStatus handle_queryrecord_request(request_state *request) #endif mDNSlocal reply_state *format_enumeration_reply(request_state *request, - const char *domain, DNSServiceFlags flags, mDNSu32 ifi, DNSServiceErrorType err) - { - size_t len; - reply_state *reply; - char *data; + const char *domain, DNSServiceFlags flags, mDNSu32 ifi, DNSServiceErrorType err) + { + size_t len; + reply_state *reply; + char *data; - len = sizeof(DNSServiceFlags); - len += sizeof(mDNSu32); - len += sizeof(DNSServiceErrorType); - len += strlen(domain) + 1; + len = sizeof(DNSServiceFlags); + len += sizeof(mDNSu32); + len += sizeof(DNSServiceErrorType); + len += strlen(domain) + 1; - reply = create_reply(enumeration_reply_op, len, request); - reply->rhdr->flags = dnssd_htonl(flags); - reply->rhdr->ifi = dnssd_htonl(ifi); - reply->rhdr->error = dnssd_htonl(err); - data = (char *)&reply->rhdr[1]; - put_string(domain, &data); - return reply; - } + reply = create_reply(enumeration_reply_op, len, request); + reply->rhdr->flags = dnssd_htonl(flags); + reply->rhdr->ifi = dnssd_htonl(ifi); + reply->rhdr->error = dnssd_htonl(err); + data = (char *)&reply->rhdr[1]; + put_string(domain, &data); + return reply; + } mDNSlocal void enum_termination_callback(request_state *request) - { - mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_all); - mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_default); - } + { + mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_all); + mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_default); + } mDNSlocal void enum_result_callback(mDNS *const m, - DNSQuestion *const question, const ResourceRecord *const answer, QC_result AddRecord) - { - char domain[MAX_ESCAPED_DOMAIN_NAME]; - request_state *request = question->QuestionContext; - DNSServiceFlags flags = 0; - reply_state *reply; - (void)m; // Unused + DNSQuestion *const question, const ResourceRecord *const answer, QC_result AddRecord) + { + char domain[MAX_ESCAPED_DOMAIN_NAME]; + request_state *request = question->QuestionContext; + DNSServiceFlags flags = 0; + reply_state *reply; + (void)m; // Unused - if (answer->rrtype != kDNSType_PTR) return; + if (answer->rrtype != kDNSType_PTR) return; #if 0 - if (!AuthorizedDomain(request, &answer->rdata->u.name, request->u.enumeration.flags ? AutoRegistrationDomains : AutoBrowseDomains)) return; + if (!AuthorizedDomain(request, &answer->rdata->u.name, request->u.enumeration.flags ? AutoRegistrationDomains : AutoBrowseDomains)) return; #endif - // We only return add/remove events for the browse and registration lists - // For the default browse and registration answers, we only give an "ADD" event - if (question == &request->u.enumeration.q_default && !AddRecord) return; + // We only return add/remove events for the browse and registration lists + // For the default browse and registration answers, we only give an "ADD" event + if (question == &request->u.enumeration.q_default && !AddRecord) return; - if (AddRecord) - { - flags |= kDNSServiceFlagsAdd; - if (question == &request->u.enumeration.q_default) flags |= kDNSServiceFlagsDefault; - } + if (AddRecord) + { + flags |= kDNSServiceFlagsAdd; + if (question == &request->u.enumeration.q_default) flags |= kDNSServiceFlagsDefault; + } - ConvertDomainNameToCString(&answer->rdata->u.name, domain); - // Note that we do NOT propagate specific interface indexes to the client - for example, a domain we learn from - // a machine's system preferences may be discovered on the LocalOnly interface, but should be browsed on the - // network, so we just pass kDNSServiceInterfaceIndexAny - reply = format_enumeration_reply(request, domain, flags, kDNSServiceInterfaceIndexAny, kDNSServiceErr_NoError); - if (!reply) { LogMsg("ERROR: enum_result_callback, format_enumeration_reply"); return; } + ConvertDomainNameToCString(&answer->rdata->u.name, domain); + // Note that we do NOT propagate specific interface indexes to the client - for example, a domain we learn from + // a machine's system preferences may be discovered on the LocalOnly interface, but should be browsed on the + // network, so we just pass kDNSServiceInterfaceIndexAny + reply = format_enumeration_reply(request, domain, flags, kDNSServiceInterfaceIndexAny, kDNSServiceErr_NoError); + if (!reply) { LogMsg("ERROR: enum_result_callback, format_enumeration_reply"); return; } - LogOperation("%3d: DNSServiceEnumerateDomains(%#2s) RESULT %s: %s", request->sd, question->qname.c, AddRecord ? "Add" : "Rmv", domain); + LogOperation("%3d: DNSServiceEnumerateDomains(%#2s) RESULT %s: %s", request->sd, question->qname.c, AddRecord ? "Add" : "Rmv", domain); - append_reply(request, reply); - } + append_reply(request, reply); + } mDNSlocal mStatus handle_enum_request(request_state *request) - { - mStatus err; - DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); - DNSServiceFlags reg = flags & kDNSServiceFlagsRegistrationDomains; - mDNS_DomainType t_all = reg ? mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse; - mDNS_DomainType t_default = reg ? mDNS_DomainTypeRegistrationDefault : mDNS_DomainTypeBrowseDefault; - mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); - mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); - if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr); + { + mStatus err; + DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); + DNSServiceFlags reg = flags & kDNSServiceFlagsRegistrationDomains; + mDNS_DomainType t_all = reg ? mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse; + mDNS_DomainType t_default = reg ? mDNS_DomainTypeRegistrationDefault : mDNS_DomainTypeBrowseDefault; + mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); + mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); + if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr); - if (!request->msgptr) - { LogMsg("%3d: DNSServiceEnumerateDomains(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } + if (!request->msgptr) + { LogMsg("%3d: DNSServiceEnumerateDomains(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } - // allocate context structures - uDNS_SetupSearchDomains(&mDNSStorage, UDNS_START_WAB_QUERY); + // allocate context structures + uDNS_SetupSearchDomains(&mDNSStorage, UDNS_START_WAB_QUERY); #if 0 - // mark which kind of enumeration we're doing so we can (de)authorize certain domains - request->u.enumeration.flags = reg; + // mark which kind of enumeration we're doing so we can (de)authorize certain domains + request->u.enumeration.flags = reg; #endif - // enumeration requires multiple questions, so we must link all the context pointers so that - // necessary context can be reached from the callbacks - request->u.enumeration.q_all .QuestionContext = request; - request->u.enumeration.q_default.QuestionContext = request; - - // if the caller hasn't specified an explicit interface, we use local-only to get the system-wide list. - if (!InterfaceID) InterfaceID = mDNSInterface_LocalOnly; + // enumeration requires multiple questions, so we must link all the context pointers so that + // necessary context can be reached from the callbacks + request->u.enumeration.q_all .QuestionContext = request; + request->u.enumeration.q_default.QuestionContext = request; - // make the calls - LogOperation("%3d: DNSServiceEnumerateDomains(%X=%s)", request->sd, flags, - (flags & kDNSServiceFlagsBrowseDomains ) ? "kDNSServiceFlagsBrowseDomains" : - (flags & kDNSServiceFlagsRegistrationDomains) ? "kDNSServiceFlagsRegistrationDomains" : "<>"); - err = mDNS_GetDomains(&mDNSStorage, &request->u.enumeration.q_all, t_all, NULL, InterfaceID, enum_result_callback, request); - if (!err) - { - err = mDNS_GetDomains(&mDNSStorage, &request->u.enumeration.q_default, t_default, NULL, InterfaceID, enum_result_callback, request); - if (err) mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_all); - else request->terminate = enum_termination_callback; - } + // if the caller hasn't specified an explicit interface, we use local-only to get the system-wide list. + if (!InterfaceID) InterfaceID = mDNSInterface_LocalOnly; - return(err); - } + // make the calls + LogOperation("%3d: DNSServiceEnumerateDomains(%X=%s)", request->sd, flags, + (flags & kDNSServiceFlagsBrowseDomains ) ? "kDNSServiceFlagsBrowseDomains" : + (flags & kDNSServiceFlagsRegistrationDomains) ? "kDNSServiceFlagsRegistrationDomains" : "<>"); + err = mDNS_GetDomains(&mDNSStorage, &request->u.enumeration.q_all, t_all, NULL, InterfaceID, enum_result_callback, request); + if (!err) + { + err = mDNS_GetDomains(&mDNSStorage, &request->u.enumeration.q_default, t_default, NULL, InterfaceID, enum_result_callback, request); + if (err) mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_all); + else request->terminate = enum_termination_callback; + } + + return(err); + } // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -3143,62 +3145,62 @@ mDNSlocal mStatus handle_enum_request(request_state *request) #endif mDNSlocal mStatus handle_reconfirm_request(request_state *request) - { - mStatus status = mStatus_BadParamErr; - AuthRecord *rr = read_rr_from_ipc_msg(request, 0, 0); - if (rr) - { - status = mDNS_ReconfirmByValue(&mDNSStorage, &rr->resrec); - LogOperation( - (status == mStatus_NoError) ? - "%3d: DNSServiceReconfirmRecord(%s) interface %d initiated" : - "%3d: DNSServiceReconfirmRecord(%s) interface %d failed: %d", - request->sd, RRDisplayString(&mDNSStorage, &rr->resrec), - mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, rr->resrec.InterfaceID, mDNSfalse), status); - freeL("AuthRecord/handle_reconfirm_request", rr); - } - return(status); - } + { + mStatus status = mStatus_BadParamErr; + AuthRecord *rr = read_rr_from_ipc_msg(request, 0, 0); + if (rr) + { + status = mDNS_ReconfirmByValue(&mDNSStorage, &rr->resrec); + LogOperation( + (status == mStatus_NoError) ? + "%3d: DNSServiceReconfirmRecord(%s) interface %d initiated" : + "%3d: DNSServiceReconfirmRecord(%s) interface %d failed: %d", + request->sd, RRDisplayString(&mDNSStorage, &rr->resrec), + mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, rr->resrec.InterfaceID, mDNSfalse), status); + freeL("AuthRecord/handle_reconfirm_request", rr); + } + return(status); + } mDNSlocal mStatus handle_setdomain_request(request_state *request) - { - char domainstr[MAX_ESCAPED_DOMAIN_NAME]; - domainname domain; - DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); - (void)flags; // Unused - if (get_string(&request->msgptr, request->msgend, domainstr, MAX_ESCAPED_DOMAIN_NAME) < 0 || - !MakeDomainNameFromDNSNameString(&domain, domainstr)) - { LogMsg("%3d: DNSServiceSetDefaultDomainForUser(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } + { + char domainstr[MAX_ESCAPED_DOMAIN_NAME]; + domainname domain; + DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); + (void)flags; // Unused + if (get_string(&request->msgptr, request->msgend, domainstr, MAX_ESCAPED_DOMAIN_NAME) < 0 || + !MakeDomainNameFromDNSNameString(&domain, domainstr)) + { LogMsg("%3d: DNSServiceSetDefaultDomainForUser(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } - LogOperation("%3d: DNSServiceSetDefaultDomainForUser(%##s)", request->sd, domain.c); - return(mStatus_NoError); - } + LogOperation("%3d: DNSServiceSetDefaultDomainForUser(%##s)", request->sd, domain.c); + return(mStatus_NoError); + } typedef packedstruct - { - mStatus err; - mDNSu32 len; - mDNSu32 vers; - } DaemonVersionReply; + { + mStatus err; + mDNSu32 len; + mDNSu32 vers; + } DaemonVersionReply; mDNSlocal void handle_getproperty_request(request_state *request) - { - const mStatus BadParamErr = dnssd_htonl((mDNSu32)mStatus_BadParamErr); - char prop[256]; - if (get_string(&request->msgptr, request->msgend, prop, sizeof(prop)) >= 0) - { - LogOperation("%3d: DNSServiceGetProperty(%s)", request->sd, prop); - if (!strcmp(prop, kDNSServiceProperty_DaemonVersion)) - { - DaemonVersionReply x = { 0, dnssd_htonl(4), dnssd_htonl(_DNS_SD_H) }; - send_all(request->sd, (const char *)&x, sizeof(x)); - return; - } - } + { + const mStatus BadParamErr = dnssd_htonl((mDNSu32)mStatus_BadParamErr); + char prop[256]; + if (get_string(&request->msgptr, request->msgend, prop, sizeof(prop)) >= 0) + { + LogOperation("%3d: DNSServiceGetProperty(%s)", request->sd, prop); + if (!strcmp(prop, kDNSServiceProperty_DaemonVersion)) + { + DaemonVersionReply x = { 0, dnssd_htonl(4), dnssd_htonl(_DNS_SD_H) }; + send_all(request->sd, (const char *)&x, sizeof(x)); + return; + } + } - // If we didn't recogize the requested property name, return BadParamErr - send_all(request->sd, (const char *)&BadParamErr, sizeof(BadParamErr)); - } + // If we didn't recogize the requested property name, return BadParamErr + send_all(request->sd, (const char *)&BadParamErr, sizeof(BadParamErr)); + } // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -3209,106 +3211,106 @@ mDNSlocal void handle_getproperty_request(request_state *request) #define DNSServiceProtocol(X) ((X) == NATOp_AddrRequest ? 0 : (X) == NATOp_MapUDP ? kDNSServiceProtocol_UDP : kDNSServiceProtocol_TCP) mDNSlocal void port_mapping_termination_callback(request_state *request) - { - LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) STOP", request->sd, - DNSServiceProtocol(request->u.pm.NATinfo.Protocol), - mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease); - mDNS_StopNATOperation(&mDNSStorage, &request->u.pm.NATinfo); - } + { + LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) STOP", request->sd, + DNSServiceProtocol(request->u.pm.NATinfo.Protocol), + mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease); + mDNS_StopNATOperation(&mDNSStorage, &request->u.pm.NATinfo); + } // Called via function pointer when we get a NAT-PMP address request or port mapping response mDNSlocal void port_mapping_create_request_callback(mDNS *m, NATTraversalInfo *n) - { - request_state *request = (request_state *)n->clientContext; - reply_state *rep; - int replyLen; - char *data; + { + request_state *request = (request_state *)n->clientContext; + reply_state *rep; + int replyLen; + char *data; - if (!request) { LogMsg("port_mapping_create_request_callback called with unknown request_state object"); return; } + if (!request) { LogMsg("port_mapping_create_request_callback called with unknown request_state object"); return; } - // calculate reply data length - replyLen = sizeof(DNSServiceFlags); - replyLen += 3 * sizeof(mDNSu32); // if index + addr + ttl - replyLen += sizeof(DNSServiceErrorType); - replyLen += 2 * sizeof(mDNSu16); // Internal Port + External Port - replyLen += sizeof(mDNSu8); // protocol + // calculate reply data length + replyLen = sizeof(DNSServiceFlags); + replyLen += 3 * sizeof(mDNSu32); // if index + addr + ttl + replyLen += sizeof(DNSServiceErrorType); + replyLen += 2 * sizeof(mDNSu16); // Internal Port + External Port + replyLen += sizeof(mDNSu8); // protocol - rep = create_reply(port_mapping_reply_op, replyLen, request); + rep = create_reply(port_mapping_reply_op, replyLen, request); - rep->rhdr->flags = dnssd_htonl(0); - rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, n->InterfaceID, mDNSfalse)); - rep->rhdr->error = dnssd_htonl(n->Result); + rep->rhdr->flags = dnssd_htonl(0); + rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, n->InterfaceID, mDNSfalse)); + rep->rhdr->error = dnssd_htonl(n->Result); - data = (char *)&rep->rhdr[1]; + data = (char *)&rep->rhdr[1]; - *data++ = request->u.pm.NATinfo.ExternalAddress.b[0]; - *data++ = request->u.pm.NATinfo.ExternalAddress.b[1]; - *data++ = request->u.pm.NATinfo.ExternalAddress.b[2]; - *data++ = request->u.pm.NATinfo.ExternalAddress.b[3]; - *data++ = DNSServiceProtocol(request->u.pm.NATinfo.Protocol); - *data++ = request->u.pm.NATinfo.IntPort.b[0]; - *data++ = request->u.pm.NATinfo.IntPort.b[1]; - *data++ = request->u.pm.NATinfo.ExternalPort.b[0]; - *data++ = request->u.pm.NATinfo.ExternalPort.b[1]; - put_uint32(request->u.pm.NATinfo.Lifetime, &data); + *data++ = request->u.pm.NATinfo.ExternalAddress.b[0]; + *data++ = request->u.pm.NATinfo.ExternalAddress.b[1]; + *data++ = request->u.pm.NATinfo.ExternalAddress.b[2]; + *data++ = request->u.pm.NATinfo.ExternalAddress.b[3]; + *data++ = DNSServiceProtocol(request->u.pm.NATinfo.Protocol); + *data++ = request->u.pm.NATinfo.IntPort.b[0]; + *data++ = request->u.pm.NATinfo.IntPort.b[1]; + *data++ = request->u.pm.NATinfo.ExternalPort.b[0]; + *data++ = request->u.pm.NATinfo.ExternalPort.b[1]; + put_uint32(request->u.pm.NATinfo.Lifetime, &data); - LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) RESULT %.4a:%u TTL %u", request->sd, - DNSServiceProtocol(request->u.pm.NATinfo.Protocol), - mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease, - &request->u.pm.NATinfo.ExternalAddress, mDNSVal16(request->u.pm.NATinfo.ExternalPort), request->u.pm.NATinfo.Lifetime); + LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) RESULT %.4a:%u TTL %u", request->sd, + DNSServiceProtocol(request->u.pm.NATinfo.Protocol), + mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease, + &request->u.pm.NATinfo.ExternalAddress, mDNSVal16(request->u.pm.NATinfo.ExternalPort), request->u.pm.NATinfo.Lifetime); - append_reply(request, rep); - } + append_reply(request, rep); + } mDNSlocal mStatus handle_port_mapping_request(request_state *request) - { - mDNSu32 ttl = 0; - mStatus err = mStatus_NoError; + { + mDNSu32 ttl = 0; + mStatus err = mStatus_NoError; - DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); - mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); - mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); - mDNSu8 protocol = (mDNSu8)get_uint32(&request->msgptr, request->msgend); - (void)flags; // Unused - if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr); - if (request->msgptr + 8 > request->msgend) request->msgptr = NULL; - else - { - request->u.pm.NATinfo.IntPort.b[0] = *request->msgptr++; - request->u.pm.NATinfo.IntPort.b[1] = *request->msgptr++; - request->u.pm.ReqExt.b[0] = *request->msgptr++; - request->u.pm.ReqExt.b[1] = *request->msgptr++; - ttl = get_uint32(&request->msgptr, request->msgend); - } + DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); + mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); + mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); + mDNSu8 protocol = (mDNSu8)get_uint32(&request->msgptr, request->msgend); + (void)flags; // Unused + if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr); + if (request->msgptr + 8 > request->msgend) request->msgptr = NULL; + else + { + request->u.pm.NATinfo.IntPort.b[0] = *request->msgptr++; + request->u.pm.NATinfo.IntPort.b[1] = *request->msgptr++; + request->u.pm.ReqExt.b[0] = *request->msgptr++; + request->u.pm.ReqExt.b[1] = *request->msgptr++; + ttl = get_uint32(&request->msgptr, request->msgend); + } - if (!request->msgptr) - { LogMsg("%3d: DNSServiceNATPortMappingCreate(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } + if (!request->msgptr) + { LogMsg("%3d: DNSServiceNATPortMappingCreate(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } - if (protocol == 0) // If protocol == 0 (i.e. just request public address) then IntPort, ExtPort, ttl must be zero too - { - if (!mDNSIPPortIsZero(request->u.pm.NATinfo.IntPort) || !mDNSIPPortIsZero(request->u.pm.ReqExt) || ttl) return(mStatus_BadParamErr); - } - else - { - if (mDNSIPPortIsZero(request->u.pm.NATinfo.IntPort)) return(mStatus_BadParamErr); - if (!(protocol & (kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP))) return(mStatus_BadParamErr); - } + if (protocol == 0) // If protocol == 0 (i.e. just request public address) then IntPort, ExtPort, ttl must be zero too + { + if (!mDNSIPPortIsZero(request->u.pm.NATinfo.IntPort) || !mDNSIPPortIsZero(request->u.pm.ReqExt) || ttl) return(mStatus_BadParamErr); + } + else + { + if (mDNSIPPortIsZero(request->u.pm.NATinfo.IntPort)) return(mStatus_BadParamErr); + if (!(protocol & (kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP))) return(mStatus_BadParamErr); + } - request->u.pm.NATinfo.Protocol = !protocol ? NATOp_AddrRequest : (protocol == kDNSServiceProtocol_UDP) ? NATOp_MapUDP : NATOp_MapTCP; - // u.pm.NATinfo.IntPort = already set above - request->u.pm.NATinfo.RequestedPort = request->u.pm.ReqExt; - request->u.pm.NATinfo.NATLease = ttl; - request->u.pm.NATinfo.clientCallback = port_mapping_create_request_callback; - request->u.pm.NATinfo.clientContext = request; + request->u.pm.NATinfo.Protocol = !protocol ? NATOp_AddrRequest : (protocol == kDNSServiceProtocol_UDP) ? NATOp_MapUDP : NATOp_MapTCP; + // u.pm.NATinfo.IntPort = already set above + request->u.pm.NATinfo.RequestedPort = request->u.pm.ReqExt; + request->u.pm.NATinfo.NATLease = ttl; + request->u.pm.NATinfo.clientCallback = port_mapping_create_request_callback; + request->u.pm.NATinfo.clientContext = request; - LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) START", request->sd, - protocol, mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease); - err = mDNS_StartNATOperation(&mDNSStorage, &request->u.pm.NATinfo); - if (err) LogMsg("ERROR: mDNS_StartNATOperation: %d", (int)err); - else request->terminate = port_mapping_termination_callback; + LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) START", request->sd, + protocol, mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease); + err = mDNS_StartNATOperation(&mDNSStorage, &request->u.pm.NATinfo); + if (err) LogMsg("ERROR: mDNS_StartNATOperation: %d", (int)err); + else request->terminate = port_mapping_termination_callback; - return(err); - } + return(err); + } // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -3317,184 +3319,184 @@ mDNSlocal mStatus handle_port_mapping_request(request_state *request) #endif mDNSlocal void addrinfo_termination_callback(request_state *request) - { - LogOperation("%3d: DNSServiceGetAddrInfo(%##s) STOP", request->sd, request->u.addrinfo.q4.qname.c); + { + LogOperation("%3d: DNSServiceGetAddrInfo(%##s) STOP", request->sd, request->u.addrinfo.q4.qname.c); - if (request->u.addrinfo.q4.QuestionContext) - { - mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q4); - request->u.addrinfo.q4.QuestionContext = mDNSNULL; - } - if (request->u.addrinfo.q4.qnameOrig) - { - freeL("QueryTermination", request->u.addrinfo.q4.qnameOrig); - request->u.addrinfo.q4.qnameOrig = mDNSNULL; - } - if (request->u.addrinfo.q42) - { - if (request->u.addrinfo.q42->QuestionContext) - { - LogInfo("addrinfo_termination_callback: Stopping q42 %##s", request->u.addrinfo.q42->qname.c); - mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q42); - } - if (request->u.addrinfo.q42->qnameOrig) - { - LogInfo("addrinfo_termination_callback: freeing q42 qnameOrig %##s", request->u.addrinfo.q42->qnameOrig->c); - freeL("QueryTermination q42", request->u.addrinfo.q42->qnameOrig); - request->u.addrinfo.q42->qnameOrig = mDNSNULL; - } - freeL("addrinfo Q42", request->u.addrinfo.q42); - request->u.addrinfo.q42 = mDNSNULL; - } + if (request->u.addrinfo.q4.QuestionContext) + { + mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q4); + request->u.addrinfo.q4.QuestionContext = mDNSNULL; + } + if (request->u.addrinfo.q4.qnameOrig) + { + freeL("QueryTermination", request->u.addrinfo.q4.qnameOrig); + request->u.addrinfo.q4.qnameOrig = mDNSNULL; + } + if (request->u.addrinfo.q42) + { + if (request->u.addrinfo.q42->QuestionContext) + { + LogInfo("addrinfo_termination_callback: Stopping q42 %##s", request->u.addrinfo.q42->qname.c); + mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q42); + } + if (request->u.addrinfo.q42->qnameOrig) + { + LogInfo("addrinfo_termination_callback: freeing q42 qnameOrig %##s", request->u.addrinfo.q42->qnameOrig->c); + freeL("QueryTermination q42", request->u.addrinfo.q42->qnameOrig); + request->u.addrinfo.q42->qnameOrig = mDNSNULL; + } + freeL("addrinfo Q42", request->u.addrinfo.q42); + request->u.addrinfo.q42 = mDNSNULL; + } - if (request->u.addrinfo.q6.QuestionContext) - { - mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q6); - request->u.addrinfo.q6.QuestionContext = mDNSNULL; - } - if (request->u.addrinfo.q6.qnameOrig) - { - freeL("QueryTermination", request->u.addrinfo.q6.qnameOrig); - request->u.addrinfo.q6.qnameOrig = mDNSNULL; - } - if (request->u.addrinfo.q62) - { - if (request->u.addrinfo.q62->QuestionContext) - { - LogInfo("addrinfo_termination_callback: Stopping q62 %##s", request->u.addrinfo.q62->qname.c); - mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q62); - } - if (request->u.addrinfo.q62->qnameOrig) - { - LogInfo("addrinfo_termination_callback: freeing q62 qnameOrig %##s", request->u.addrinfo.q62->qnameOrig->c); - freeL("QueryTermination q62", request->u.addrinfo.q62->qnameOrig); - request->u.addrinfo.q62->qnameOrig = mDNSNULL; - } - freeL("addrinfo Q62", request->u.addrinfo.q62); - request->u.addrinfo.q62 = mDNSNULL; - } - } + if (request->u.addrinfo.q6.QuestionContext) + { + mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q6); + request->u.addrinfo.q6.QuestionContext = mDNSNULL; + } + if (request->u.addrinfo.q6.qnameOrig) + { + freeL("QueryTermination", request->u.addrinfo.q6.qnameOrig); + request->u.addrinfo.q6.qnameOrig = mDNSNULL; + } + if (request->u.addrinfo.q62) + { + if (request->u.addrinfo.q62->QuestionContext) + { + LogInfo("addrinfo_termination_callback: Stopping q62 %##s", request->u.addrinfo.q62->qname.c); + mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q62); + } + if (request->u.addrinfo.q62->qnameOrig) + { + LogInfo("addrinfo_termination_callback: freeing q62 qnameOrig %##s", request->u.addrinfo.q62->qnameOrig->c); + freeL("QueryTermination q62", request->u.addrinfo.q62->qnameOrig); + request->u.addrinfo.q62->qnameOrig = mDNSNULL; + } + freeL("addrinfo Q62", request->u.addrinfo.q62); + request->u.addrinfo.q62 = mDNSNULL; + } + } mDNSlocal mStatus handle_addrinfo_request(request_state *request) - { - char hostname[256]; - domainname d; - mStatus err = 0; + { + char hostname[256]; + domainname d; + mStatus err = 0; - DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); - mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); + DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); + mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); - mDNSPlatformMemZero(&request->u.addrinfo, sizeof(request->u.addrinfo)); - request->u.addrinfo.interface_id = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); - request->u.addrinfo.flags = flags; - request->u.addrinfo.protocol = get_uint32(&request->msgptr, request->msgend); + mDNSPlatformMemZero(&request->u.addrinfo, sizeof(request->u.addrinfo)); + request->u.addrinfo.interface_id = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); + request->u.addrinfo.flags = flags; + request->u.addrinfo.protocol = get_uint32(&request->msgptr, request->msgend); - if (interfaceIndex && !request->u.addrinfo.interface_id) return(mStatus_BadParamErr); - if (request->u.addrinfo.protocol > (kDNSServiceProtocol_IPv4|kDNSServiceProtocol_IPv6)) return(mStatus_BadParamErr); + if (interfaceIndex && !request->u.addrinfo.interface_id) return(mStatus_BadParamErr); + if (request->u.addrinfo.protocol > (kDNSServiceProtocol_IPv4|kDNSServiceProtocol_IPv6)) return(mStatus_BadParamErr); - if (get_string(&request->msgptr, request->msgend, hostname, 256) < 0) return(mStatus_BadParamErr); + if (get_string(&request->msgptr, request->msgend, hostname, 256) < 0) return(mStatus_BadParamErr); - if (!request->msgptr) { LogMsg("%3d: DNSServiceGetAddrInfo(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } + if (!request->msgptr) { LogMsg("%3d: DNSServiceGetAddrInfo(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } - if (!MakeDomainNameFromDNSNameString(&d, hostname)) - { LogMsg("ERROR: handle_addrinfo_request: bad hostname: %s", hostname); return(mStatus_BadParamErr); } + if (!MakeDomainNameFromDNSNameString(&d, hostname)) + { LogMsg("ERROR: handle_addrinfo_request: bad hostname: %s", hostname); return(mStatus_BadParamErr); } #if 0 - if (!AuthorizedDomain(request, &d, AutoBrowseDomains)) return (mStatus_NoError); + if (!AuthorizedDomain(request, &d, AutoBrowseDomains)) return (mStatus_NoError); #endif - if (!request->u.addrinfo.protocol) - { - flags |= kDNSServiceFlagsSuppressUnusable; - request->u.addrinfo.protocol = (kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6); - } + if (!request->u.addrinfo.protocol) + { + flags |= kDNSServiceFlagsSuppressUnusable; + request->u.addrinfo.protocol = (kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6); + } - request->u.addrinfo.q4.InterfaceID = request->u.addrinfo.q6.InterfaceID = request->u.addrinfo.interface_id; - request->u.addrinfo.q4.Target = request->u.addrinfo.q6.Target = zeroAddr; - request->u.addrinfo.q4.qname = request->u.addrinfo.q6.qname = d; - request->u.addrinfo.q4.qclass = request->u.addrinfo.q6.qclass = kDNSServiceClass_IN; - request->u.addrinfo.q4.LongLived = request->u.addrinfo.q6.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0; - request->u.addrinfo.q4.ExpectUnique = request->u.addrinfo.q6.ExpectUnique = mDNSfalse; - request->u.addrinfo.q4.ForceMCast = request->u.addrinfo.q6.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; - request->u.addrinfo.q4.ReturnIntermed = request->u.addrinfo.q6.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; - request->u.addrinfo.q4.SuppressUnusable = request->u.addrinfo.q6.SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable ) != 0; - request->u.addrinfo.q4.TimeoutQuestion = request->u.addrinfo.q6.TimeoutQuestion = (flags & kDNSServiceFlagsTimeout ) != 0; - request->u.addrinfo.q4.WakeOnResolve = request->u.addrinfo.q6.WakeOnResolve = 0; - request->u.addrinfo.q4.qnameOrig = request->u.addrinfo.q6.qnameOrig = mDNSNULL; + request->u.addrinfo.q4.InterfaceID = request->u.addrinfo.q6.InterfaceID = request->u.addrinfo.interface_id; + request->u.addrinfo.q4.Target = request->u.addrinfo.q6.Target = zeroAddr; + request->u.addrinfo.q4.qname = request->u.addrinfo.q6.qname = d; + request->u.addrinfo.q4.qclass = request->u.addrinfo.q6.qclass = kDNSServiceClass_IN; + request->u.addrinfo.q4.LongLived = request->u.addrinfo.q6.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0; + request->u.addrinfo.q4.ExpectUnique = request->u.addrinfo.q6.ExpectUnique = mDNSfalse; + request->u.addrinfo.q4.ForceMCast = request->u.addrinfo.q6.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; + request->u.addrinfo.q4.ReturnIntermed = request->u.addrinfo.q6.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; + request->u.addrinfo.q4.SuppressUnusable = request->u.addrinfo.q6.SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable ) != 0; + request->u.addrinfo.q4.TimeoutQuestion = request->u.addrinfo.q6.TimeoutQuestion = (flags & kDNSServiceFlagsTimeout ) != 0; + request->u.addrinfo.q4.WakeOnResolve = request->u.addrinfo.q6.WakeOnResolve = 0; + request->u.addrinfo.q4.qnameOrig = request->u.addrinfo.q6.qnameOrig = mDNSNULL; - if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4) - { - request->u.addrinfo.q4.qtype = kDNSServiceType_A; - request->u.addrinfo.q4.SearchListIndex = 0; + if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4) + { + request->u.addrinfo.q4.qtype = kDNSServiceType_A; + request->u.addrinfo.q4.SearchListIndex = 0; - // We append search domains only for queries that are a single label. If overriden using - // command line argument "AlwaysAppendSearchDomains", then we do it for any query which - // is not fully qualified. - if (hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1)) - { - request->u.addrinfo.q4.AppendSearchDomains = 1; - request->u.addrinfo.q4.AppendLocalSearchDomains = 1; - } - else - { - request->u.addrinfo.q4.AppendSearchDomains = 0; - request->u.addrinfo.q4.AppendLocalSearchDomains = 0; - } - request->u.addrinfo.q4.RetryWithSearchDomains = (ApplySearchDomainsFirst(&request->u.addrinfo.q4) ? 1 : 0); - request->u.addrinfo.q4.QuestionCallback = queryrecord_result_callback; - request->u.addrinfo.q4.QuestionContext = request; - err = mDNS_StartQuery(&mDNSStorage, &request->u.addrinfo.q4); - if (err != mStatus_NoError) - { - LogMsg("ERROR: mDNS_StartQuery: %d", (int)err); - request->u.addrinfo.q4.QuestionContext = mDNSNULL; - } - #if APPLE_OSX_mDNSResponder - err = SendAdditionalQuery(&request->u.addrinfo.q4, request, err); - #endif // APPLE_OSX_mDNSResponder - } + // We append search domains only for queries that are a single label. If overriden using + // command line argument "AlwaysAppendSearchDomains", then we do it for any query which + // is not fully qualified. + if (hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1)) + { + request->u.addrinfo.q4.AppendSearchDomains = 1; + request->u.addrinfo.q4.AppendLocalSearchDomains = 1; + } + else + { + request->u.addrinfo.q4.AppendSearchDomains = 0; + request->u.addrinfo.q4.AppendLocalSearchDomains = 0; + } + request->u.addrinfo.q4.RetryWithSearchDomains = (ApplySearchDomainsFirst(&request->u.addrinfo.q4) ? 1 : 0); + request->u.addrinfo.q4.QuestionCallback = queryrecord_result_callback; + request->u.addrinfo.q4.QuestionContext = request; + err = mDNS_StartQuery(&mDNSStorage, &request->u.addrinfo.q4); + if (err != mStatus_NoError) + { + LogMsg("ERROR: mDNS_StartQuery: %d", (int)err); + request->u.addrinfo.q4.QuestionContext = mDNSNULL; + } + #if APPLE_OSX_mDNSResponder + err = SendAdditionalQuery(&request->u.addrinfo.q4, request, err); + #endif // APPLE_OSX_mDNSResponder + } - if (!err && (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6)) - { - request->u.addrinfo.q6.qtype = kDNSServiceType_AAAA; - request->u.addrinfo.q6.SearchListIndex = 0; - if (hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1)) - { - request->u.addrinfo.q6.AppendSearchDomains = 1; - request->u.addrinfo.q6.AppendLocalSearchDomains = 1; - } - else - { - request->u.addrinfo.q6.AppendSearchDomains = 0; - request->u.addrinfo.q6.AppendLocalSearchDomains = 0; - } - request->u.addrinfo.q6.RetryWithSearchDomains = (ApplySearchDomainsFirst(&request->u.addrinfo.q6) ? 1 : 0); - request->u.addrinfo.q6.QuestionCallback = queryrecord_result_callback; - request->u.addrinfo.q6.QuestionContext = request; - err = mDNS_StartQuery(&mDNSStorage, &request->u.addrinfo.q6); - if (err != mStatus_NoError) - { - LogMsg("ERROR: mDNS_StartQuery: %d", (int)err); - request->u.addrinfo.q6.QuestionContext = mDNSNULL; - if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4) - { - // If we started a query for IPv4, we need to cancel it - mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q4); - request->u.addrinfo.q4.QuestionContext = mDNSNULL; - } - } - #if APPLE_OSX_mDNSResponder - err = SendAdditionalQuery(&request->u.addrinfo.q6, request, err); - #endif // APPLE_OSX_mDNSResponder - } + if (!err && (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6)) + { + request->u.addrinfo.q6.qtype = kDNSServiceType_AAAA; + request->u.addrinfo.q6.SearchListIndex = 0; + if (hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1)) + { + request->u.addrinfo.q6.AppendSearchDomains = 1; + request->u.addrinfo.q6.AppendLocalSearchDomains = 1; + } + else + { + request->u.addrinfo.q6.AppendSearchDomains = 0; + request->u.addrinfo.q6.AppendLocalSearchDomains = 0; + } + request->u.addrinfo.q6.RetryWithSearchDomains = (ApplySearchDomainsFirst(&request->u.addrinfo.q6) ? 1 : 0); + request->u.addrinfo.q6.QuestionCallback = queryrecord_result_callback; + request->u.addrinfo.q6.QuestionContext = request; + err = mDNS_StartQuery(&mDNSStorage, &request->u.addrinfo.q6); + if (err != mStatus_NoError) + { + LogMsg("ERROR: mDNS_StartQuery: %d", (int)err); + request->u.addrinfo.q6.QuestionContext = mDNSNULL; + if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4) + { + // If we started a query for IPv4, we need to cancel it + mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q4); + request->u.addrinfo.q4.QuestionContext = mDNSNULL; + } + } + #if APPLE_OSX_mDNSResponder + err = SendAdditionalQuery(&request->u.addrinfo.q6, request, err); + #endif // APPLE_OSX_mDNSResponder + } - LogOperation("%3d: DNSServiceGetAddrInfo(%X, %d, %d, %##s) START", - request->sd, flags, interfaceIndex, request->u.addrinfo.protocol, d.c); + LogOperation("%3d: DNSServiceGetAddrInfo(%X, %d, %d, %##s) START", + request->sd, flags, interfaceIndex, request->u.addrinfo.protocol, d.c); - if (!err) request->terminate = addrinfo_termination_callback; + if (!err) request->terminate = addrinfo_termination_callback; - return(err); - } + return(err); + } // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -3503,1214 +3505,1214 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request) #endif mDNSlocal request_state *NewRequest(void) - { - request_state **p = &all_requests; - while (*p) p=&(*p)->next; - *p = mallocL("request_state", sizeof(request_state)); - if (!*p) FatalError("ERROR: malloc"); - mDNSPlatformMemZero(*p, sizeof(request_state)); - return(*p); - } + { + request_state **p = &all_requests; + while (*p) p=&(*p)->next; + *p = mallocL("request_state", sizeof(request_state)); + if (!*p) FatalError("ERROR: malloc"); + mDNSPlatformMemZero(*p, sizeof(request_state)); + return(*p); + } // read_msg may be called any time when the transfer state (req->ts) is t_morecoming. // if there is no data on the socket, the socket will be closed and t_terminated will be returned mDNSlocal void read_msg(request_state *req) - { - if (req->ts == t_terminated || req->ts == t_error) - { LogMsg("%3d: ERROR: read_msg called with transfer state terminated or error", req->sd); req->ts = t_error; return; } + { + if (req->ts == t_terminated || req->ts == t_error) + { LogMsg("%3d: ERROR: read_msg called with transfer state terminated or error", req->sd); req->ts = t_error; return; } - if (req->ts == t_complete) // this must be death or something is wrong - { - char buf[4]; // dummy for death notification - int nread = udsSupportReadFD(req->sd, buf, 4, 0, req->platform_data); - if (!nread) { req->ts = t_terminated; return; } - if (nread < 0) goto rerror; - LogMsg("%3d: ERROR: read data from a completed request", req->sd); - req->ts = t_error; - return; - } + if (req->ts == t_complete) // this must be death or something is wrong + { + char buf[4]; // dummy for death notification + int nread = udsSupportReadFD(req->sd, buf, 4, 0, req->platform_data); + if (!nread) { req->ts = t_terminated; return; } + if (nread < 0) goto rerror; + LogMsg("%3d: ERROR: read data from a completed request", req->sd); + req->ts = t_error; + return; + } - if (req->ts != t_morecoming) - { LogMsg("%3d: ERROR: read_msg called with invalid transfer state (%d)", req->sd, req->ts); req->ts = t_error; return; } + if (req->ts != t_morecoming) + { LogMsg("%3d: ERROR: read_msg called with invalid transfer state (%d)", req->sd, req->ts); req->ts = t_error; return; } - if (req->hdr_bytes < sizeof(ipc_msg_hdr)) - { - mDNSu32 nleft = sizeof(ipc_msg_hdr) - req->hdr_bytes; - int nread = udsSupportReadFD(req->sd, (char *)&req->hdr + req->hdr_bytes, nleft, 0, req->platform_data); - if (nread == 0) { req->ts = t_terminated; return; } - if (nread < 0) goto rerror; - req->hdr_bytes += nread; - if (req->hdr_bytes > sizeof(ipc_msg_hdr)) - { LogMsg("%3d: ERROR: read_msg - read too many header bytes", req->sd); req->ts = t_error; return; } + if (req->hdr_bytes < sizeof(ipc_msg_hdr)) + { + mDNSu32 nleft = sizeof(ipc_msg_hdr) - req->hdr_bytes; + int nread = udsSupportReadFD(req->sd, (char *)&req->hdr + req->hdr_bytes, nleft, 0, req->platform_data); + if (nread == 0) { req->ts = t_terminated; return; } + if (nread < 0) goto rerror; + req->hdr_bytes += nread; + if (req->hdr_bytes > sizeof(ipc_msg_hdr)) + { LogMsg("%3d: ERROR: read_msg - read too many header bytes", req->sd); req->ts = t_error; return; } - // only read data if header is complete - if (req->hdr_bytes == sizeof(ipc_msg_hdr)) - { - ConvertHeaderBytes(&req->hdr); - if (req->hdr.version != VERSION) - { LogMsg("%3d: ERROR: client version 0x%08X daemon version 0x%08X", req->sd, req->hdr.version, VERSION); req->ts = t_error; return; } + // only read data if header is complete + if (req->hdr_bytes == sizeof(ipc_msg_hdr)) + { + ConvertHeaderBytes(&req->hdr); + if (req->hdr.version != VERSION) + { LogMsg("%3d: ERROR: client version 0x%08X daemon version 0x%08X", req->sd, req->hdr.version, VERSION); req->ts = t_error; return; } - // Largest conceivable single request is a DNSServiceRegisterRecord() or DNSServiceAddRecord() - // with 64kB of rdata. Adding 1009 byte for a maximal domain name, plus a safety margin - // for other overhead, this means any message above 70kB is definitely bogus. - if (req->hdr.datalen > 70000) - { LogMsg("%3d: ERROR: read_msg: hdr.datalen %u (0x%X) > 70000", req->sd, req->hdr.datalen, req->hdr.datalen); req->ts = t_error; return; } - req->msgbuf = mallocL("request_state msgbuf", req->hdr.datalen + MSG_PAD_BYTES); - if (!req->msgbuf) { my_perror("ERROR: malloc"); req->ts = t_error; return; } - req->msgptr = req->msgbuf; - req->msgend = req->msgbuf + req->hdr.datalen; - mDNSPlatformMemZero(req->msgbuf, req->hdr.datalen + MSG_PAD_BYTES); - } - } + // Largest conceivable single request is a DNSServiceRegisterRecord() or DNSServiceAddRecord() + // with 64kB of rdata. Adding 1009 byte for a maximal domain name, plus a safety margin + // for other overhead, this means any message above 70kB is definitely bogus. + if (req->hdr.datalen > 70000) + { LogMsg("%3d: ERROR: read_msg: hdr.datalen %u (0x%X) > 70000", req->sd, req->hdr.datalen, req->hdr.datalen); req->ts = t_error; return; } + req->msgbuf = mallocL("request_state msgbuf", req->hdr.datalen + MSG_PAD_BYTES); + if (!req->msgbuf) { my_perror("ERROR: malloc"); req->ts = t_error; return; } + req->msgptr = req->msgbuf; + req->msgend = req->msgbuf + req->hdr.datalen; + mDNSPlatformMemZero(req->msgbuf, req->hdr.datalen + MSG_PAD_BYTES); + } + } - // If our header is complete, but we're still needing more body data, then try to read it now - // Note: For cancel_request req->hdr.datalen == 0, but there's no error return socket for cancel_request - // Any time we need to get the error return socket we know we'll have at least one data byte - // (even if only the one-byte empty C string placeholder for the old ctrl_path parameter) - if (req->hdr_bytes == sizeof(ipc_msg_hdr) && req->data_bytes < req->hdr.datalen) - { - mDNSu32 nleft = req->hdr.datalen - req->data_bytes; - int nread; + // If our header is complete, but we're still needing more body data, then try to read it now + // Note: For cancel_request req->hdr.datalen == 0, but there's no error return socket for cancel_request + // Any time we need to get the error return socket we know we'll have at least one data byte + // (even if only the one-byte empty C string placeholder for the old ctrl_path parameter) + if (req->hdr_bytes == sizeof(ipc_msg_hdr) && req->data_bytes < req->hdr.datalen) + { + mDNSu32 nleft = req->hdr.datalen - req->data_bytes; + int nread; #if !defined(_WIN32) - struct iovec vec = { req->msgbuf + req->data_bytes, nleft }; // Tell recvmsg where we want the bytes put - struct msghdr msg; - struct cmsghdr *cmsg; - char cbuf[CMSG_SPACE(sizeof(dnssd_sock_t))]; - msg.msg_name = 0; - msg.msg_namelen = 0; - msg.msg_iov = &vec; - msg.msg_iovlen = 1; - msg.msg_control = cbuf; - msg.msg_controllen = sizeof(cbuf); - msg.msg_flags = 0; - nread = recvmsg(req->sd, &msg, 0); + struct iovec vec = { req->msgbuf + req->data_bytes, nleft }; // Tell recvmsg where we want the bytes put + struct msghdr msg; + struct cmsghdr *cmsg; + char cbuf[CMSG_SPACE(sizeof(dnssd_sock_t))]; + msg.msg_name = 0; + msg.msg_namelen = 0; + msg.msg_iov = &vec; + msg.msg_iovlen = 1; + msg.msg_control = cbuf; + msg.msg_controllen = sizeof(cbuf); + msg.msg_flags = 0; + nread = recvmsg(req->sd, &msg, 0); #else - nread = udsSupportReadFD(req->sd, (char *)req->msgbuf + req->data_bytes, nleft, 0, req->platform_data); + nread = udsSupportReadFD(req->sd, (char *)req->msgbuf + req->data_bytes, nleft, 0, req->platform_data); #endif - if (nread == 0) { req->ts = t_terminated; return; } - if (nread < 0) goto rerror; - req->data_bytes += nread; - if (req->data_bytes > req->hdr.datalen) - { LogMsg("%3d: ERROR: read_msg - read too many data bytes", req->sd); req->ts = t_error; return; } + if (nread == 0) { req->ts = t_terminated; return; } + if (nread < 0) goto rerror; + req->data_bytes += nread; + if (req->data_bytes > req->hdr.datalen) + { LogMsg("%3d: ERROR: read_msg - read too many data bytes", req->sd); req->ts = t_error; return; } #if !defined(_WIN32) - cmsg = CMSG_FIRSTHDR(&msg); + cmsg = CMSG_FIRSTHDR(&msg); #if DEBUG_64BIT_SCM_RIGHTS - LogMsg("%3d: Expecting %d %d %d %d", req->sd, sizeof(cbuf), sizeof(cbuf), SOL_SOCKET, SCM_RIGHTS); - LogMsg("%3d: Got %d %d %d %d", req->sd, msg.msg_controllen, cmsg->cmsg_len, cmsg->cmsg_level, cmsg->cmsg_type); + LogMsg("%3d: Expecting %d %d %d %d", req->sd, sizeof(cbuf), sizeof(cbuf), SOL_SOCKET, SCM_RIGHTS); + LogMsg("%3d: Got %d %d %d %d", req->sd, msg.msg_controllen, cmsg->cmsg_len, cmsg->cmsg_level, cmsg->cmsg_type); #endif // DEBUG_64BIT_SCM_RIGHTS - if (msg.msg_controllen == sizeof(cbuf) && - cmsg->cmsg_len == CMSG_LEN(sizeof(dnssd_sock_t)) && - cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_RIGHTS) - { + if (msg.msg_controllen == sizeof(cbuf) && + cmsg->cmsg_len == CMSG_LEN(sizeof(dnssd_sock_t)) && + cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) + { #if APPLE_OSX_mDNSResponder - // Strictly speaking BPF_fd belongs solely in the platform support layer, but because - // of privilege separation on Mac OS X we need to get BPF_fd from mDNSResponderHelper, - // and it's convenient to repurpose the existing fd-passing code here for that task - if (req->hdr.op == send_bpf) - { - dnssd_sock_t x = *(dnssd_sock_t *)CMSG_DATA(cmsg); - LogOperation("%3d: Got BPF %d", req->sd, x); - mDNSPlatformReceiveBPF_fd(&mDNSStorage, x); - } - else + // Strictly speaking BPF_fd belongs solely in the platform support layer, but because + // of privilege separation on Mac OS X we need to get BPF_fd from mDNSResponderHelper, + // and it's convenient to repurpose the existing fd-passing code here for that task + if (req->hdr.op == send_bpf) + { + dnssd_sock_t x = *(dnssd_sock_t *)CMSG_DATA(cmsg); + LogOperation("%3d: Got BPF %d", req->sd, x); + mDNSPlatformReceiveBPF_fd(&mDNSStorage, x); + } + else #endif // APPLE_OSX_mDNSResponder - req->errsd = *(dnssd_sock_t *)CMSG_DATA(cmsg); + req->errsd = *(dnssd_sock_t *)CMSG_DATA(cmsg); #if DEBUG_64BIT_SCM_RIGHTS - LogMsg("%3d: read req->errsd %d", req->sd, req->errsd); + LogMsg("%3d: read req->errsd %d", req->sd, req->errsd); #endif // DEBUG_64BIT_SCM_RIGHTS - if (req->data_bytes < req->hdr.datalen) - { - LogMsg("%3d: Client sent error socket %d via SCM_RIGHTS with req->data_bytes %d < req->hdr.datalen %d", - req->sd, req->errsd, req->data_bytes, req->hdr.datalen); - req->ts = t_error; - return; - } - } + if (req->data_bytes < req->hdr.datalen) + { + LogMsg("%3d: Client sent error socket %d via SCM_RIGHTS with req->data_bytes %d < req->hdr.datalen %d", + req->sd, req->errsd, req->data_bytes, req->hdr.datalen); + req->ts = t_error; + return; + } + } #endif - } + } - // If our header and data are both complete, see if we need to make our separate error return socket - if (req->hdr_bytes == sizeof(ipc_msg_hdr) && req->data_bytes == req->hdr.datalen) - { - if (req->terminate && req->hdr.op != cancel_request) - { - dnssd_sockaddr_t cliaddr; + // If our header and data are both complete, see if we need to make our separate error return socket + if (req->hdr_bytes == sizeof(ipc_msg_hdr) && req->data_bytes == req->hdr.datalen) + { + if (req->terminate && req->hdr.op != cancel_request) + { + dnssd_sockaddr_t cliaddr; #if defined(USE_TCP_LOOPBACK) - mDNSOpaque16 port; - u_long opt = 1; - port.b[0] = req->msgptr[0]; - port.b[1] = req->msgptr[1]; - req->msgptr += 2; - cliaddr.sin_family = AF_INET; - cliaddr.sin_port = port.NotAnInteger; - cliaddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR); + mDNSOpaque16 port; + u_long opt = 1; + port.b[0] = req->msgptr[0]; + port.b[1] = req->msgptr[1]; + req->msgptr += 2; + cliaddr.sin_family = AF_INET; + cliaddr.sin_port = port.NotAnInteger; + cliaddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR); #else - char ctrl_path[MAX_CTLPATH]; - get_string(&req->msgptr, req->msgend, ctrl_path, MAX_CTLPATH); // path is first element in message buffer - mDNSPlatformMemZero(&cliaddr, sizeof(cliaddr)); - cliaddr.sun_family = AF_LOCAL; - mDNSPlatformStrCopy(cliaddr.sun_path, ctrl_path); - // If the error return path UDS name is empty string, that tells us - // that this is a new version of the library that's going to pass us - // the error return path socket via sendmsg/recvmsg - if (ctrl_path[0] == 0) - { - if (req->errsd == req->sd) - { LogMsg("%3d: read_msg: ERROR failed to get errsd via SCM_RIGHTS", req->sd); req->ts = t_error; return; } - goto got_errfd; - } + char ctrl_path[MAX_CTLPATH]; + get_string(&req->msgptr, req->msgend, ctrl_path, MAX_CTLPATH); // path is first element in message buffer + mDNSPlatformMemZero(&cliaddr, sizeof(cliaddr)); + cliaddr.sun_family = AF_LOCAL; + mDNSPlatformStrCopy(cliaddr.sun_path, ctrl_path); + // If the error return path UDS name is empty string, that tells us + // that this is a new version of the library that's going to pass us + // the error return path socket via sendmsg/recvmsg + if (ctrl_path[0] == 0) + { + if (req->errsd == req->sd) + { LogMsg("%3d: read_msg: ERROR failed to get errsd via SCM_RIGHTS", req->sd); req->ts = t_error; return; } + goto got_errfd; + } #endif - - req->errsd = socket(AF_DNSSD, SOCK_STREAM, 0); - if (!dnssd_SocketValid(req->errsd)) { my_perror("ERROR: socket"); req->ts = t_error; return; } - if (connect(req->errsd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)) < 0) - { + req->errsd = socket(AF_DNSSD, SOCK_STREAM, 0); + if (!dnssd_SocketValid(req->errsd)) { my_perror("ERROR: socket"); req->ts = t_error; return; } + + if (connect(req->errsd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)) < 0) + { #if !defined(USE_TCP_LOOPBACK) - struct stat sb; + struct stat sb; LogMsg("%3d: read_msg: Couldn't connect to error return path socket '%s' errno %d (%s)", - req->sd, cliaddr.sun_path, dnssd_errno, dnssd_strerror(dnssd_errno)); - if (stat(cliaddr.sun_path, &sb) < 0) + req->sd, cliaddr.sun_path, dnssd_errno, dnssd_strerror(dnssd_errno)); + if (stat(cliaddr.sun_path, &sb) < 0) LogMsg("%3d: read_msg: stat failed '%s' errno %d (%s)", req->sd, cliaddr.sun_path, dnssd_errno, dnssd_strerror(dnssd_errno)); - else + else LogMsg("%3d: read_msg: file '%s' mode %o (octal) uid %d gid %d", req->sd, cliaddr.sun_path, sb.st_mode, sb.st_uid, sb.st_gid); #endif - req->ts = t_error; - return; - } - + req->ts = t_error; + return; + } + #if !defined(USE_TCP_LOOPBACK) got_errfd: #endif - LogOperation("%3d: Error socket %d created %08X %08X", req->sd, req->errsd, req->hdr.client_context.u32[1], req->hdr.client_context.u32[0]); + LogOperation("%3d: Error socket %d created %08X %08X", req->sd, req->errsd, req->hdr.client_context.u32[1], req->hdr.client_context.u32[0]); #if defined(_WIN32) - if (ioctlsocket(req->errsd, FIONBIO, &opt) != 0) + if (ioctlsocket(req->errsd, FIONBIO, &opt) != 0) #else - if (fcntl(req->errsd, F_SETFL, fcntl(req->errsd, F_GETFL, 0) | O_NONBLOCK) != 0) + if (fcntl(req->errsd, F_SETFL, fcntl(req->errsd, F_GETFL, 0) | O_NONBLOCK) != 0) #endif - { - LogMsg("%3d: ERROR: could not set control socket to non-blocking mode errno %d (%s)", - req->sd, dnssd_errno, dnssd_strerror(dnssd_errno)); - req->ts = t_error; - return; - } - } - - req->ts = t_complete; - } + { + LogMsg("%3d: ERROR: could not set control socket to non-blocking mode errno %d (%s)", + req->sd, dnssd_errno, dnssd_strerror(dnssd_errno)); + req->ts = t_error; + return; + } + } - return; + req->ts = t_complete; + } + + return; rerror: - if (dnssd_errno == dnssd_EWOULDBLOCK || dnssd_errno == dnssd_EINTR) return; - LogMsg("%3d: ERROR: read_msg errno %d (%s)", req->sd, dnssd_errno, dnssd_strerror(dnssd_errno)); - req->ts = t_error; - } + if (dnssd_errno == dnssd_EWOULDBLOCK || dnssd_errno == dnssd_EINTR) return; + LogMsg("%3d: ERROR: read_msg errno %d (%s)", req->sd, dnssd_errno, dnssd_strerror(dnssd_errno)); + req->ts = t_error; + } #define RecordOrientedOp(X) \ - ((X) == reg_record_request || (X) == add_record_request || (X) == update_record_request || (X) == remove_record_request) + ((X) == reg_record_request || (X) == add_record_request || (X) == update_record_request || (X) == remove_record_request) // The lightweight operations are the ones that don't need a dedicated request_state structure allocated for them #define LightweightOp(X) (RecordOrientedOp(X) || (X) == cancel_request) mDNSlocal void request_callback(int fd, short filter, void *info) - { - mStatus err = 0; - request_state *req = info; - mDNSs32 min_size = sizeof(DNSServiceFlags); - (void)fd; // Unused - (void)filter; // Unused + { + mStatus err = 0; + request_state *req = info; + mDNSs32 min_size = sizeof(DNSServiceFlags); + (void)fd; // Unused + (void)filter; // Unused - for (;;) - { - read_msg(req); - if (req->ts == t_morecoming) return; - if (req->ts == t_terminated || req->ts == t_error) { AbortUnlinkAndFree(req); return; } - if (req->ts != t_complete) { LogMsg("req->ts %d != t_complete", req->ts); AbortUnlinkAndFree(req); return; } + for (;;) + { + read_msg(req); + if (req->ts == t_morecoming) return; + if (req->ts == t_terminated || req->ts == t_error) { AbortUnlinkAndFree(req); return; } + if (req->ts != t_complete) { LogMsg("req->ts %d != t_complete", req->ts); AbortUnlinkAndFree(req); return; } - if (req->hdr.version != VERSION) - { - LogMsg("ERROR: client version %d incompatible with daemon version %d", req->hdr.version, VERSION); - AbortUnlinkAndFree(req); - return; - } + if (req->hdr.version != VERSION) + { + LogMsg("ERROR: client version %d incompatible with daemon version %d", req->hdr.version, VERSION); + AbortUnlinkAndFree(req); + return; + } - switch(req->hdr.op) // Interface + other data - { - case connection_request: min_size = 0; break; - case reg_service_request: min_size += sizeof(mDNSu32) + 4 /* name, type, domain, host */ + 4 /* port, textlen */; break; - case add_record_request: min_size += 4 /* type, rdlen */ + 4 /* ttl */; break; - case update_record_request: min_size += 2 /* rdlen */ + 4 /* ttl */; break; - case remove_record_request: break; - case browse_request: min_size += sizeof(mDNSu32) + 2 /* type, domain */; break; - case resolve_request: min_size += sizeof(mDNSu32) + 3 /* type, type, domain */; break; - case query_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 4 /* type, class*/; break; - case enumeration_request: min_size += sizeof(mDNSu32); break; - case reg_record_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */ + 4 /* ttl */; break; - case reconfirm_record_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */; break; - case setdomain_request: min_size += 1 /* domain */; break; - case getproperty_request: min_size = 2; break; - case port_mapping_request: min_size += sizeof(mDNSu32) + 4 /* udp/tcp */ + 4 /* int/ext port */ + 4 /* ttl */; break; - case addrinfo_request: min_size += sizeof(mDNSu32) + 4 /* v4/v6 */ + 1 /* hostname */; break; - case send_bpf: // Same as cancel_request below - case cancel_request: min_size = 0; break; - default: LogMsg("ERROR: validate_message - unsupported req type: %d", req->hdr.op); min_size = -1; break; - } + switch(req->hdr.op) // Interface + other data + { + case connection_request: min_size = 0; break; + case reg_service_request: min_size += sizeof(mDNSu32) + 4 /* name, type, domain, host */ + 4 /* port, textlen */; break; + case add_record_request: min_size += 4 /* type, rdlen */ + 4 /* ttl */; break; + case update_record_request: min_size += 2 /* rdlen */ + 4 /* ttl */; break; + case remove_record_request: break; + case browse_request: min_size += sizeof(mDNSu32) + 2 /* type, domain */; break; + case resolve_request: min_size += sizeof(mDNSu32) + 3 /* type, type, domain */; break; + case query_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 4 /* type, class*/; break; + case enumeration_request: min_size += sizeof(mDNSu32); break; + case reg_record_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */ + 4 /* ttl */; break; + case reconfirm_record_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */; break; + case setdomain_request: min_size += 1 /* domain */; break; + case getproperty_request: min_size = 2; break; + case port_mapping_request: min_size += sizeof(mDNSu32) + 4 /* udp/tcp */ + 4 /* int/ext port */ + 4 /* ttl */; break; + case addrinfo_request: min_size += sizeof(mDNSu32) + 4 /* v4/v6 */ + 1 /* hostname */; break; + case send_bpf: // Same as cancel_request below + case cancel_request: min_size = 0; break; + default: LogMsg("ERROR: validate_message - unsupported req type: %d", req->hdr.op); min_size = -1; break; + } - if ((mDNSs32)req->data_bytes < min_size) - { LogMsg("Invalid message %d bytes; min for %d is %d", req->data_bytes, req->hdr.op, min_size); AbortUnlinkAndFree(req); return; } + if ((mDNSs32)req->data_bytes < min_size) + { LogMsg("Invalid message %d bytes; min for %d is %d", req->data_bytes, req->hdr.op, min_size); AbortUnlinkAndFree(req); return; } - if (LightweightOp(req->hdr.op) && !req->terminate) - { LogMsg("Reg/Add/Update/Remove %d require existing connection", req->hdr.op); AbortUnlinkAndFree(req); return; } + if (LightweightOp(req->hdr.op) && !req->terminate) + { LogMsg("Reg/Add/Update/Remove %d require existing connection", req->hdr.op); AbortUnlinkAndFree(req); return; } - // check if client wants silent operation - if (req->hdr.ipc_flags & IPC_FLAGS_NOREPLY) req->no_reply = 1; + // check if client wants silent operation + if (req->hdr.ipc_flags & IPC_FLAGS_NOREPLY) req->no_reply = 1; - // If req->terminate is already set, this means this operation is sharing an existing connection - if (req->terminate && !LightweightOp(req->hdr.op)) - { - request_state *newreq = NewRequest(); - newreq->primary = req; - newreq->sd = req->sd; - newreq->errsd = req->errsd; - newreq->uid = req->uid; - newreq->hdr = req->hdr; - newreq->msgbuf = req->msgbuf; - newreq->msgptr = req->msgptr; - newreq->msgend = req->msgend; - req = newreq; - } + // If req->terminate is already set, this means this operation is sharing an existing connection + if (req->terminate && !LightweightOp(req->hdr.op)) + { + request_state *newreq = NewRequest(); + newreq->primary = req; + newreq->sd = req->sd; + newreq->errsd = req->errsd; + newreq->uid = req->uid; + newreq->hdr = req->hdr; + newreq->msgbuf = req->msgbuf; + newreq->msgptr = req->msgptr; + newreq->msgend = req->msgend; + req = newreq; + } - // If we're shutting down, don't allow new client requests - // We do allow "cancel" and "getproperty" during shutdown - if (mDNSStorage.ShutdownTime && req->hdr.op != cancel_request && req->hdr.op != getproperty_request) - { - err = mStatus_ServiceNotRunning; - } - else switch(req->hdr.op) - { - // These are all operations that have their own first-class request_state object - case connection_request: LogOperation("%3d: DNSServiceCreateConnection START", req->sd); - req->terminate = connection_termination; break; - case resolve_request: err = handle_resolve_request (req); break; - case query_request: err = handle_queryrecord_request (req); break; - case browse_request: err = handle_browse_request (req); break; - case reg_service_request: err = handle_regservice_request (req); break; - case enumeration_request: err = handle_enum_request (req); break; - case reconfirm_record_request: err = handle_reconfirm_request (req); break; - case setdomain_request: err = handle_setdomain_request (req); break; - case getproperty_request: handle_getproperty_request (req); break; - case port_mapping_request: err = handle_port_mapping_request(req); break; - case addrinfo_request: err = handle_addrinfo_request (req); break; - case send_bpf: /* Do nothing for send_bpf */ break; + // If we're shutting down, don't allow new client requests + // We do allow "cancel" and "getproperty" during shutdown + if (mDNSStorage.ShutdownTime && req->hdr.op != cancel_request && req->hdr.op != getproperty_request) + { + err = mStatus_ServiceNotRunning; + } + else switch(req->hdr.op) + { + // These are all operations that have their own first-class request_state object + case connection_request: LogOperation("%3d: DNSServiceCreateConnection START", req->sd); + req->terminate = connection_termination; break; + case resolve_request: err = handle_resolve_request (req); break; + case query_request: err = handle_queryrecord_request (req); break; + case browse_request: err = handle_browse_request (req); break; + case reg_service_request: err = handle_regservice_request (req); break; + case enumeration_request: err = handle_enum_request (req); break; + case reconfirm_record_request: err = handle_reconfirm_request (req); break; + case setdomain_request: err = handle_setdomain_request (req); break; + case getproperty_request: handle_getproperty_request (req); break; + case port_mapping_request: err = handle_port_mapping_request(req); break; + case addrinfo_request: err = handle_addrinfo_request (req); break; + case send_bpf: /* Do nothing for send_bpf */ break; - // These are all operations that work with an existing request_state object - case reg_record_request: err = handle_regrecord_request (req); break; - case add_record_request: err = handle_add_request (req); break; - case update_record_request: err = handle_update_request (req); break; - case remove_record_request: err = handle_removerecord_request(req); break; - case cancel_request: handle_cancel_request (req); break; - default: LogMsg("%3d: ERROR: Unsupported UDS req: %d", req->sd, req->hdr.op); - } + // These are all operations that work with an existing request_state object + case reg_record_request: err = handle_regrecord_request (req); break; + case add_record_request: err = handle_add_request (req); break; + case update_record_request: err = handle_update_request (req); break; + case remove_record_request: err = handle_removerecord_request(req); break; + case cancel_request: handle_cancel_request (req); break; + default: LogMsg("%3d: ERROR: Unsupported UDS req: %d", req->sd, req->hdr.op); + } - // req->msgbuf may be NULL, e.g. for connection_request or remove_record_request - if (req->msgbuf) freeL("request_state msgbuf", req->msgbuf); + // req->msgbuf may be NULL, e.g. for connection_request or remove_record_request + if (req->msgbuf) freeL("request_state msgbuf", req->msgbuf); - // There's no return data for a cancel request (DNSServiceRefDeallocate returns no result) - // For a DNSServiceGetProperty call, the handler already generated the response, so no need to do it again here - if (req->hdr.op != cancel_request && req->hdr.op != getproperty_request && req->hdr.op != send_bpf) - { - const mStatus err_netorder = dnssd_htonl(err); - send_all(req->errsd, (const char *)&err_netorder, sizeof(err_netorder)); - if (req->errsd != req->sd) - { - LogOperation("%3d: Error socket %d closed %08X %08X (%d)", - req->sd, req->errsd, req->hdr.client_context.u32[1], req->hdr.client_context.u32[0], err); - dnssd_close(req->errsd); - req->errsd = req->sd; - // Also need to reset the parent's errsd, if this is a subordinate operation - if (req->primary) req->primary->errsd = req->primary->sd; - } - } + // There's no return data for a cancel request (DNSServiceRefDeallocate returns no result) + // For a DNSServiceGetProperty call, the handler already generated the response, so no need to do it again here + if (req->hdr.op != cancel_request && req->hdr.op != getproperty_request && req->hdr.op != send_bpf) + { + const mStatus err_netorder = dnssd_htonl(err); + send_all(req->errsd, (const char *)&err_netorder, sizeof(err_netorder)); + if (req->errsd != req->sd) + { + LogOperation("%3d: Error socket %d closed %08X %08X (%d)", + req->sd, req->errsd, req->hdr.client_context.u32[1], req->hdr.client_context.u32[0], err); + dnssd_close(req->errsd); + req->errsd = req->sd; + // Also need to reset the parent's errsd, if this is a subordinate operation + if (req->primary) req->primary->errsd = req->primary->sd; + } + } - // Reset ready to accept the next req on this pipe - if (req->primary) req = req->primary; - req->ts = t_morecoming; - req->hdr_bytes = 0; - req->data_bytes = 0; - req->msgbuf = mDNSNULL; - req->msgptr = mDNSNULL; - req->msgend = 0; - } - } + // Reset ready to accept the next req on this pipe + if (req->primary) req = req->primary; + req->ts = t_morecoming; + req->hdr_bytes = 0; + req->data_bytes = 0; + req->msgbuf = mDNSNULL; + req->msgptr = mDNSNULL; + req->msgend = 0; + } + } mDNSlocal void connect_callback(int fd, short filter, void *info) - { - dnssd_sockaddr_t cliaddr; - dnssd_socklen_t len = (dnssd_socklen_t) sizeof(cliaddr); - dnssd_sock_t sd = accept(fd, (struct sockaddr*) &cliaddr, &len); + { + dnssd_sockaddr_t cliaddr; + dnssd_socklen_t len = (dnssd_socklen_t) sizeof(cliaddr); + dnssd_sock_t sd = accept(fd, (struct sockaddr*) &cliaddr, &len); #if defined(SO_NOSIGPIPE) || defined(_WIN32) - unsigned long optval = 1; + unsigned long optval = 1; #endif - (void)filter; // Unused - (void)info; // Unused + (void)filter; // Unused + (void)info; // Unused - if (!dnssd_SocketValid(sd)) - { - if (dnssd_errno != dnssd_EWOULDBLOCK) my_perror("ERROR: accept"); - return; - } + if (!dnssd_SocketValid(sd)) + { + if (dnssd_errno != dnssd_EWOULDBLOCK) my_perror("ERROR: accept"); + return; + } #ifdef SO_NOSIGPIPE - // Some environments (e.g. OS X) support turning off SIGPIPE for a socket - if (setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0) - LogMsg("%3d: WARNING: setsockopt - SO_NOSIGPIPE %d (%s)", sd, dnssd_errno, dnssd_strerror(dnssd_errno)); + // Some environments (e.g. OS X) support turning off SIGPIPE for a socket + if (setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0) + LogMsg("%3d: WARNING: setsockopt - SO_NOSIGPIPE %d (%s)", sd, dnssd_errno, dnssd_strerror(dnssd_errno)); #endif #if defined(_WIN32) - if (ioctlsocket(sd, FIONBIO, &optval) != 0) + if (ioctlsocket(sd, FIONBIO, &optval) != 0) #else - if (fcntl(sd, F_SETFL, fcntl(sd, F_GETFL, 0) | O_NONBLOCK) != 0) + if (fcntl(sd, F_SETFL, fcntl(sd, F_GETFL, 0) | O_NONBLOCK) != 0) #endif - { - my_perror("ERROR: fcntl(sd, F_SETFL, O_NONBLOCK) - aborting client"); - dnssd_close(sd); - return; - } - else - { - request_state *request = NewRequest(); - request->ts = t_morecoming; - request->sd = sd; - request->errsd = sd; + { + my_perror("ERROR: fcntl(sd, F_SETFL, O_NONBLOCK) - aborting client"); + dnssd_close(sd); + return; + } + else + { + request_state *request = NewRequest(); + request->ts = t_morecoming; + request->sd = sd; + request->errsd = sd; #if APPLE_OSX_mDNSResponder - struct xucred x; - socklen_t xucredlen = sizeof(x); - if (getsockopt(sd, 0, LOCAL_PEERCRED, &x, &xucredlen) >= 0 && x.cr_version == XUCRED_VERSION) request->uid = x.cr_uid; - else my_perror("ERROR: getsockopt, LOCAL_PEERCRED"); - debugf("LOCAL_PEERCRED %d %u %u %d", xucredlen, x.cr_version, x.cr_uid, x.cr_ngroups); + struct xucred x; + socklen_t xucredlen = sizeof(x); + if (getsockopt(sd, 0, LOCAL_PEERCRED, &x, &xucredlen) >= 0 && x.cr_version == XUCRED_VERSION) request->uid = x.cr_uid; + else my_perror("ERROR: getsockopt, LOCAL_PEERCRED"); + debugf("LOCAL_PEERCRED %d %u %u %d", xucredlen, x.cr_version, x.cr_uid, x.cr_ngroups); #endif // APPLE_OSX_mDNSResponder - LogOperation("%3d: Adding FD for uid %u", request->sd, request->uid); - udsSupportAddFDToEventLoop(sd, request_callback, request, &request->platform_data); - } - } + LogOperation("%3d: Adding FD for uid %u", request->sd, request->uid); + udsSupportAddFDToEventLoop(sd, request_callback, request, &request->platform_data); + } + } mDNSlocal mDNSBool uds_socket_setup(dnssd_sock_t skt) - { + { #if defined(SO_NP_EXTENSIONS) - struct so_np_extensions sonpx; - socklen_t optlen = sizeof(struct so_np_extensions); - sonpx.npx_flags = SONPX_SETOPTSHUT; - sonpx.npx_mask = SONPX_SETOPTSHUT; - if (setsockopt(skt, SOL_SOCKET, SO_NP_EXTENSIONS, &sonpx, optlen) < 0) - my_perror("WARNING: could not set sockopt - SO_NP_EXTENSIONS"); + struct so_np_extensions sonpx; + socklen_t optlen = sizeof(struct so_np_extensions); + sonpx.npx_flags = SONPX_SETOPTSHUT; + sonpx.npx_mask = SONPX_SETOPTSHUT; + if (setsockopt(skt, SOL_SOCKET, SO_NP_EXTENSIONS, &sonpx, optlen) < 0) + my_perror("WARNING: could not set sockopt - SO_NP_EXTENSIONS"); #endif #if defined(_WIN32) - // SEH: do we even need to do this on windows? - // This socket will be given to WSAEventSelect which will automatically set it to non-blocking - u_long opt = 1; - if (ioctlsocket(skt, FIONBIO, &opt) != 0) + // SEH: do we even need to do this on windows? + // This socket will be given to WSAEventSelect which will automatically set it to non-blocking + u_long opt = 1; + if (ioctlsocket(skt, FIONBIO, &opt) != 0) #else - if (fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK) != 0) + if (fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK) != 0) #endif - { - my_perror("ERROR: could not set listen socket to non-blocking mode"); - return mDNSfalse; - } + { + my_perror("ERROR: could not set listen socket to non-blocking mode"); + return mDNSfalse; + } - if (listen(skt, LISTENQ) != 0) - { - my_perror("ERROR: could not listen on listen socket"); - return mDNSfalse; - } + if (listen(skt, LISTENQ) != 0) + { + my_perror("ERROR: could not listen on listen socket"); + return mDNSfalse; + } - if (mStatus_NoError != udsSupportAddFDToEventLoop(skt, connect_callback, (void *) NULL, (void **) NULL)) - { - my_perror("ERROR: could not add listen socket to event loop"); - return mDNSfalse; - } - else LogOperation("%3d: Listening for incoming Unix Domain Socket client requests", skt); - - return mDNStrue; - } + if (mStatus_NoError != udsSupportAddFDToEventLoop(skt, connect_callback, (void *) NULL, (void **) NULL)) + { + my_perror("ERROR: could not add listen socket to event loop"); + return mDNSfalse; + } + else LogOperation("%3d: Listening for incoming Unix Domain Socket client requests", skt); + + return mDNStrue; + } mDNSexport int udsserver_init(dnssd_sock_t skts[], mDNSu32 count) - { - dnssd_sockaddr_t laddr; - int ret; - mDNSu32 i = 0; + { + dnssd_sockaddr_t laddr; + int ret; + mDNSu32 i = 0; - LogInfo("udsserver_init"); + LogInfo("udsserver_init"); - // If a particular platform wants to opt out of having a PID file, define PID_FILE to be "" - if (PID_FILE[0]) - { - FILE *fp = fopen(PID_FILE, "w"); - if (fp != NULL) - { - fprintf(fp, "%d\n", getpid()); - fclose(fp); - } - } + // If a particular platform wants to opt out of having a PID file, define PID_FILE to be "" + if (PID_FILE[0]) + { + FILE *fp = fopen(PID_FILE, "w"); + if (fp != NULL) + { + fprintf(fp, "%d\n", getpid()); + fclose(fp); + } + } - if (skts) - { - for (i = 0; i < count; i++) - if (dnssd_SocketValid(skts[i]) && !uds_socket_setup(skts[i])) - goto error; - } - else - { - listenfd = socket(AF_DNSSD, SOCK_STREAM, 0); - if (!dnssd_SocketValid(listenfd)) - { - my_perror("ERROR: socket(AF_DNSSD, SOCK_STREAM, 0); failed"); - goto error; - } + if (skts) + { + for (i = 0; i < count; i++) + if (dnssd_SocketValid(skts[i]) && !uds_socket_setup(skts[i])) + goto error; + } + else + { + listenfd = socket(AF_DNSSD, SOCK_STREAM, 0); + if (!dnssd_SocketValid(listenfd)) + { + my_perror("ERROR: socket(AF_DNSSD, SOCK_STREAM, 0); failed"); + goto error; + } - mDNSPlatformMemZero(&laddr, sizeof(laddr)); + mDNSPlatformMemZero(&laddr, sizeof(laddr)); - #if defined(USE_TCP_LOOPBACK) - { - laddr.sin_family = AF_INET; - laddr.sin_port = htons(MDNS_TCP_SERVERPORT); - laddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR); - ret = bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); - if (ret < 0) - { - my_perror("ERROR: bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); failed"); - goto error; - } - } - #else - { - mode_t mask = umask(0); - unlink(MDNS_UDS_SERVERPATH); // OK if this fails - laddr.sun_family = AF_LOCAL; - #ifndef NOT_HAVE_SA_LEN - // According to Stevens (section 3.2), there is no portable way to - // determine whether sa_len is defined on a particular platform. - laddr.sun_len = sizeof(struct sockaddr_un); - #endif - mDNSPlatformStrCopy(laddr.sun_path, MDNS_UDS_SERVERPATH); - ret = bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); - umask(mask); - if (ret < 0) - { - my_perror("ERROR: bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); failed"); - goto error; - } - } - #endif - - if (!uds_socket_setup(listenfd)) goto error; - } + #if defined(USE_TCP_LOOPBACK) + { + laddr.sin_family = AF_INET; + laddr.sin_port = htons(MDNS_TCP_SERVERPORT); + laddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR); + ret = bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); + if (ret < 0) + { + my_perror("ERROR: bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); failed"); + goto error; + } + } + #else + { + mode_t mask = umask(0); + unlink(MDNS_UDS_SERVERPATH); // OK if this fails + laddr.sun_family = AF_LOCAL; + #ifndef NOT_HAVE_SA_LEN + // According to Stevens (section 3.2), there is no portable way to + // determine whether sa_len is defined on a particular platform. + laddr.sun_len = sizeof(struct sockaddr_un); + #endif + mDNSPlatformStrCopy(laddr.sun_path, MDNS_UDS_SERVERPATH); + ret = bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); + umask(mask); + if (ret < 0) + { + my_perror("ERROR: bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); failed"); + goto error; + } + } + #endif + + if (!uds_socket_setup(listenfd)) goto error; + } #if !defined(PLATFORM_NO_RLIMIT) - { - // Set maximum number of open file descriptors - #define MIN_OPENFILES 10240 - struct rlimit maxfds, newfds; + { + // Set maximum number of open file descriptors + #define MIN_OPENFILES 10240 + struct rlimit maxfds, newfds; - // Due to bugs in OS X (, , ) - // you have to get and set rlimits once before getrlimit will return sensible values - if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; } - if (setrlimit(RLIMIT_NOFILE, &maxfds) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit"); + // Due to bugs in OS X (, , ) + // you have to get and set rlimits once before getrlimit will return sensible values + if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; } + if (setrlimit(RLIMIT_NOFILE, &maxfds) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit"); - if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; } - newfds.rlim_max = (maxfds.rlim_max > MIN_OPENFILES) ? maxfds.rlim_max : MIN_OPENFILES; - newfds.rlim_cur = (maxfds.rlim_cur > MIN_OPENFILES) ? maxfds.rlim_cur : MIN_OPENFILES; - if (newfds.rlim_max != maxfds.rlim_max || newfds.rlim_cur != maxfds.rlim_cur) - if (setrlimit(RLIMIT_NOFILE, &newfds) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit"); + if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; } + newfds.rlim_max = (maxfds.rlim_max > MIN_OPENFILES) ? maxfds.rlim_max : MIN_OPENFILES; + newfds.rlim_cur = (maxfds.rlim_cur > MIN_OPENFILES) ? maxfds.rlim_cur : MIN_OPENFILES; + if (newfds.rlim_max != maxfds.rlim_max || newfds.rlim_cur != maxfds.rlim_cur) + if (setrlimit(RLIMIT_NOFILE, &newfds) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit"); - if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; } - debugf("maxfds.rlim_max %d", (long)maxfds.rlim_max); - debugf("maxfds.rlim_cur %d", (long)maxfds.rlim_cur); - } + if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; } + debugf("maxfds.rlim_max %d", (long)maxfds.rlim_max); + debugf("maxfds.rlim_cur %d", (long)maxfds.rlim_cur); + } #endif - // We start a "LocalOnly" query looking for Automatic Browse Domain records. - // When Domain Enumeration in uDNS.c finds an "lb" record from the network, its "FoundDomain" routine - // creates a "LocalOnly" record, which results in our AutomaticBrowseDomainChange callback being invoked - mDNS_GetDomains(&mDNSStorage, &mDNSStorage.AutomaticBrowseDomainQ, mDNS_DomainTypeBrowseAutomatic, - mDNSNULL, mDNSInterface_LocalOnly, AutomaticBrowseDomainChange, mDNSNULL); + // We start a "LocalOnly" query looking for Automatic Browse Domain records. + // When Domain Enumeration in uDNS.c finds an "lb" record from the network, its "FoundDomain" routine + // creates a "LocalOnly" record, which results in our AutomaticBrowseDomainChange callback being invoked + mDNS_GetDomains(&mDNSStorage, &mDNSStorage.AutomaticBrowseDomainQ, mDNS_DomainTypeBrowseAutomatic, + mDNSNULL, mDNSInterface_LocalOnly, AutomaticBrowseDomainChange, mDNSNULL); - // Add "local" as recommended registration domain ("dns-sd -E"), recommended browsing domain ("dns-sd -F"), and automatic browsing domain - RegisterLocalOnlyDomainEnumPTR(&mDNSStorage, &localdomain, mDNS_DomainTypeRegistration); - RegisterLocalOnlyDomainEnumPTR(&mDNSStorage, &localdomain, mDNS_DomainTypeBrowse); - AddAutoBrowseDomain(0, &localdomain); + // Add "local" as recommended registration domain ("dns-sd -E"), recommended browsing domain ("dns-sd -F"), and automatic browsing domain + RegisterLocalOnlyDomainEnumPTR(&mDNSStorage, &localdomain, mDNS_DomainTypeRegistration); + RegisterLocalOnlyDomainEnumPTR(&mDNSStorage, &localdomain, mDNS_DomainTypeBrowse); + AddAutoBrowseDomain(0, &localdomain); - udsserver_handle_configchange(&mDNSStorage); - return 0; + udsserver_handle_configchange(&mDNSStorage); + return 0; error: - my_perror("ERROR: udsserver_init"); - return -1; - } + my_perror("ERROR: udsserver_init"); + return -1; + } mDNSexport int udsserver_exit(void) - { - // Cancel all outstanding client requests - while (all_requests) AbortUnlinkAndFree(all_requests); + { + // Cancel all outstanding client requests + while (all_requests) AbortUnlinkAndFree(all_requests); - // Clean up any special mDNSInterface_LocalOnly records we created, both the entries for "local" we - // created in udsserver_init, and others we created as a result of reading local configuration data - while (LocalDomainEnumRecords) - { - ARListElem *rem = LocalDomainEnumRecords; - LocalDomainEnumRecords = LocalDomainEnumRecords->next; - mDNS_Deregister(&mDNSStorage, &rem->ar); - } + // Clean up any special mDNSInterface_LocalOnly records we created, both the entries for "local" we + // created in udsserver_init, and others we created as a result of reading local configuration data + while (LocalDomainEnumRecords) + { + ARListElem *rem = LocalDomainEnumRecords; + LocalDomainEnumRecords = LocalDomainEnumRecords->next; + mDNS_Deregister(&mDNSStorage, &rem->ar); + } - // If the launching environment created no listening socket, - // that means we created it ourselves, so we should clean it up on exit - if (dnssd_SocketValid(listenfd)) - { - dnssd_close(listenfd); + // If the launching environment created no listening socket, + // that means we created it ourselves, so we should clean it up on exit + if (dnssd_SocketValid(listenfd)) + { + dnssd_close(listenfd); #if !defined(USE_TCP_LOOPBACK) - // Currently, we're unable to remove /var/run/mdnsd because we've changed to userid "nobody" - // to give up unnecessary privilege, but we need to be root to remove this Unix Domain Socket. - // It would be nice if we could find a solution to this problem - if (unlink(MDNS_UDS_SERVERPATH)) - debugf("Unable to remove %s", MDNS_UDS_SERVERPATH); + // Currently, we're unable to remove /var/run/mdnsd because we've changed to userid "nobody" + // to give up unnecessary privilege, but we need to be root to remove this Unix Domain Socket. + // It would be nice if we could find a solution to this problem + if (unlink(MDNS_UDS_SERVERPATH)) + debugf("Unable to remove %s", MDNS_UDS_SERVERPATH); #endif - } + } - if (PID_FILE[0]) unlink(PID_FILE); + if (PID_FILE[0]) unlink(PID_FILE); - return 0; - } + return 0; + } mDNSlocal void LogClientInfo(mDNS *const m, const request_state *req) - { - char prefix[16]; - if (req->primary) mDNS_snprintf(prefix, sizeof(prefix), " -> "); - else mDNS_snprintf(prefix, sizeof(prefix), "%3d:", req->sd); + { + char prefix[16]; + if (req->primary) mDNS_snprintf(prefix, sizeof(prefix), " -> "); + else mDNS_snprintf(prefix, sizeof(prefix), "%3d:", req->sd); - usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); + usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); - if (!req->terminate) - LogMsgNoIdent("%s No operation yet on this socket", prefix); - else if (req->terminate == connection_termination) - { - int num_records = 0, num_ops = 0; - const registered_record_entry *p; - const request_state *r; - for (p = req->u.reg_recs; p; p=p->next) num_records++; - for (r = req->next; r; r=r->next) if (r->primary == req) num_ops++; - LogMsgNoIdent("%s DNSServiceCreateConnection: %d registered record%s, %d kDNSServiceFlagsShareConnection operation%s", prefix, - num_records, num_records != 1 ? "s" : "", - num_ops, num_ops != 1 ? "s" : ""); - for (p = req->u.reg_recs; p; p=p->next) - LogMsgNoIdent(" -> DNSServiceRegisterRecord %3d %s", p->key, ARDisplayString(m, p->rr)); - for (r = req->next; r; r=r->next) if (r->primary == req) LogClientInfo(m, r); - } - else if (req->terminate == regservice_termination_callback) - { - service_instance *ptr; - for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next) - LogMsgNoIdent("%s DNSServiceRegister %##s %u/%u", - (ptr == req->u.servicereg.instances) ? prefix : " ", - ptr->srs.RR_SRV.resrec.name->c, mDNSVal16(req->u.servicereg.port), SRS_PORT(&ptr->srs)); - } - else if (req->terminate == browse_termination_callback) - { - browser_t *blist; - for (blist = req->u.browser.browsers; blist; blist = blist->next) - LogMsgNoIdent("%s DNSServiceBrowse %##s", (blist == req->u.browser.browsers) ? prefix : " ", blist->q.qname.c); - } - else if (req->terminate == resolve_termination_callback) - LogMsgNoIdent("%s DNSServiceResolve %##s", prefix, req->u.resolve.qsrv.qname.c); - else if (req->terminate == queryrecord_termination_callback) - LogMsgNoIdent("%s DNSServiceQueryRecord %##s (%s)", prefix, req->u.queryrecord.q.qname.c, DNSTypeName(req->u.queryrecord.q.qtype)); - else if (req->terminate == enum_termination_callback) - LogMsgNoIdent("%s DNSServiceEnumerateDomains %##s", prefix, req->u.enumeration.q_all.qname.c); - else if (req->terminate == port_mapping_termination_callback) - LogMsgNoIdent("%s DNSServiceNATPortMapping %.4a %s%s Int %d Req %d Ext %d Req TTL %d Granted TTL %d", - prefix, - &req->u.pm.NATinfo.ExternalAddress, - req->u.pm.NATinfo.Protocol & NATOp_MapTCP ? "TCP" : " ", - req->u.pm.NATinfo.Protocol & NATOp_MapUDP ? "UDP" : " ", - mDNSVal16(req->u.pm.NATinfo.IntPort), - mDNSVal16(req->u.pm.ReqExt), - mDNSVal16(req->u.pm.NATinfo.ExternalPort), - req->u.pm.NATinfo.NATLease, - req->u.pm.NATinfo.Lifetime); - else if (req->terminate == addrinfo_termination_callback) - LogMsgNoIdent("%s DNSServiceGetAddrInfo %s%s %##s", prefix, - req->u.addrinfo.protocol & kDNSServiceProtocol_IPv4 ? "v4" : " ", - req->u.addrinfo.protocol & kDNSServiceProtocol_IPv6 ? "v6" : " ", - req->u.addrinfo.q4.qname.c); - else - LogMsgNoIdent("%s Unrecognized operation %p", prefix, req->terminate); - } + if (!req->terminate) + LogMsgNoIdent("%s No operation yet on this socket", prefix); + else if (req->terminate == connection_termination) + { + int num_records = 0, num_ops = 0; + const registered_record_entry *p; + const request_state *r; + for (p = req->u.reg_recs; p; p=p->next) num_records++; + for (r = req->next; r; r=r->next) if (r->primary == req) num_ops++; + LogMsgNoIdent("%s DNSServiceCreateConnection: %d registered record%s, %d kDNSServiceFlagsShareConnection operation%s", prefix, + num_records, num_records != 1 ? "s" : "", + num_ops, num_ops != 1 ? "s" : ""); + for (p = req->u.reg_recs; p; p=p->next) + LogMsgNoIdent(" -> DNSServiceRegisterRecord %3d %s", p->key, ARDisplayString(m, p->rr)); + for (r = req->next; r; r=r->next) if (r->primary == req) LogClientInfo(m, r); + } + else if (req->terminate == regservice_termination_callback) + { + service_instance *ptr; + for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next) + LogMsgNoIdent("%s DNSServiceRegister %##s %u/%u", + (ptr == req->u.servicereg.instances) ? prefix : " ", + ptr->srs.RR_SRV.resrec.name->c, mDNSVal16(req->u.servicereg.port), SRS_PORT(&ptr->srs)); + } + else if (req->terminate == browse_termination_callback) + { + browser_t *blist; + for (blist = req->u.browser.browsers; blist; blist = blist->next) + LogMsgNoIdent("%s DNSServiceBrowse %##s", (blist == req->u.browser.browsers) ? prefix : " ", blist->q.qname.c); + } + else if (req->terminate == resolve_termination_callback) + LogMsgNoIdent("%s DNSServiceResolve %##s", prefix, req->u.resolve.qsrv.qname.c); + else if (req->terminate == queryrecord_termination_callback) + LogMsgNoIdent("%s DNSServiceQueryRecord %##s (%s)", prefix, req->u.queryrecord.q.qname.c, DNSTypeName(req->u.queryrecord.q.qtype)); + else if (req->terminate == enum_termination_callback) + LogMsgNoIdent("%s DNSServiceEnumerateDomains %##s", prefix, req->u.enumeration.q_all.qname.c); + else if (req->terminate == port_mapping_termination_callback) + LogMsgNoIdent("%s DNSServiceNATPortMapping %.4a %s%s Int %d Req %d Ext %d Req TTL %d Granted TTL %d", + prefix, + &req->u.pm.NATinfo.ExternalAddress, + req->u.pm.NATinfo.Protocol & NATOp_MapTCP ? "TCP" : " ", + req->u.pm.NATinfo.Protocol & NATOp_MapUDP ? "UDP" : " ", + mDNSVal16(req->u.pm.NATinfo.IntPort), + mDNSVal16(req->u.pm.ReqExt), + mDNSVal16(req->u.pm.NATinfo.ExternalPort), + req->u.pm.NATinfo.NATLease, + req->u.pm.NATinfo.Lifetime); + else if (req->terminate == addrinfo_termination_callback) + LogMsgNoIdent("%s DNSServiceGetAddrInfo %s%s %##s", prefix, + req->u.addrinfo.protocol & kDNSServiceProtocol_IPv4 ? "v4" : " ", + req->u.addrinfo.protocol & kDNSServiceProtocol_IPv6 ? "v6" : " ", + req->u.addrinfo.q4.qname.c); + else + LogMsgNoIdent("%s Unrecognized operation %p", prefix, req->terminate); + } mDNSlocal char *RecordTypeName(mDNSu8 rtype) - { - switch (rtype) - { - case kDNSRecordTypeUnregistered: return ("Unregistered "); - case kDNSRecordTypeDeregistering: return ("Deregistering"); - case kDNSRecordTypeUnique: return ("Unique "); - case kDNSRecordTypeAdvisory: return ("Advisory "); - case kDNSRecordTypeShared: return ("Shared "); - case kDNSRecordTypeVerified: return ("Verified "); - case kDNSRecordTypeKnownUnique: return ("KnownUnique "); - default: return("Unknown"); - } - } + { + switch (rtype) + { + case kDNSRecordTypeUnregistered: return ("Unregistered "); + case kDNSRecordTypeDeregistering: return ("Deregistering"); + case kDNSRecordTypeUnique: return ("Unique "); + case kDNSRecordTypeAdvisory: return ("Advisory "); + case kDNSRecordTypeShared: return ("Shared "); + case kDNSRecordTypeVerified: return ("Verified "); + case kDNSRecordTypeKnownUnique: return ("KnownUnique "); + default: return("Unknown"); + } + } mDNSlocal void LogEtcHosts(mDNS *const m) - { - mDNSBool showheader = mDNStrue; - const AuthRecord *ar; - mDNSu32 slot; - AuthGroup *ag; - int count = 0; - int authslot = 0; - mDNSBool truncated = 0; + { + mDNSBool showheader = mDNStrue; + const AuthRecord *ar; + mDNSu32 slot; + AuthGroup *ag; + int count = 0; + int authslot = 0; + mDNSBool truncated = 0; - for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) - { - if (m->rrauth.rrauth_hash[slot]) authslot++; - for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next) - for (ar = ag->members; ar; ar = ar->next) - { - if (ar->RecordCallback != FreeEtcHosts) continue; - if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" State Interface"); } - - // Print a maximum of 50 records - if (count++ >= 50) { truncated = mDNStrue; continue; } - if (ar->ARType == AuthRecordLocalOnly) - { - if (ar->resrec.InterfaceID == mDNSInterface_LocalOnly) - LogMsgNoIdent(" %s LO %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar)); - else - { - mDNSu32 scopeid = (mDNSu32)(uintptr_t)ar->resrec.InterfaceID; - LogMsgNoIdent(" %s %u %s", RecordTypeName(ar->resrec.RecordType), scopeid, ARDisplayString(m, ar)); - } - } - usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); - } - } + for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) + { + if (m->rrauth.rrauth_hash[slot]) authslot++; + for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next) + for (ar = ag->members; ar; ar = ar->next) + { + if (ar->RecordCallback != FreeEtcHosts) continue; + if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" State Interface"); } - if (showheader) LogMsgNoIdent(""); - else if (truncated) LogMsgNoIdent("", count, m->rrauth.rrauth_totalused, authslot); - } + // Print a maximum of 50 records + if (count++ >= 50) { truncated = mDNStrue; continue; } + if (ar->ARType == AuthRecordLocalOnly) + { + if (ar->resrec.InterfaceID == mDNSInterface_LocalOnly) + LogMsgNoIdent(" %s LO %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar)); + else + { + mDNSu32 scopeid = (mDNSu32)(uintptr_t)ar->resrec.InterfaceID; + LogMsgNoIdent(" %s %u %s", RecordTypeName(ar->resrec.RecordType), scopeid, ARDisplayString(m, ar)); + } + } + usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); + } + } + + if (showheader) LogMsgNoIdent(""); + else if (truncated) LogMsgNoIdent("", count, m->rrauth.rrauth_totalused, authslot); + } mDNSlocal void LogLocalOnlyAuthRecords(mDNS *const m) - { - mDNSBool showheader = mDNStrue; - const AuthRecord *ar; - mDNSu32 slot; - AuthGroup *ag; + { + mDNSBool showheader = mDNStrue; + const AuthRecord *ar; + mDNSu32 slot; + AuthGroup *ag; - for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) - { - for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next) - for (ar = ag->members; ar; ar = ar->next) - { - if (ar->RecordCallback == FreeEtcHosts) continue; - if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" State Interface"); } - - // Print a maximum of 400 records - if (ar->ARType == AuthRecordLocalOnly) - LogMsgNoIdent(" %s LO %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar)); - else if (ar->ARType == AuthRecordP2P) - LogMsgNoIdent(" %s PP %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar)); - usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); - } - } + for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) + { + for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next) + for (ar = ag->members; ar; ar = ar->next) + { + if (ar->RecordCallback == FreeEtcHosts) continue; + if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" State Interface"); } - if (showheader) LogMsgNoIdent(""); - } + // Print a maximum of 400 records + if (ar->ARType == AuthRecordLocalOnly) + LogMsgNoIdent(" %s LO %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar)); + else if (ar->ARType == AuthRecordP2P) + LogMsgNoIdent(" %s PP %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar)); + usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); + } + } + + if (showheader) LogMsgNoIdent(""); + } mDNSlocal void LogAuthRecords(mDNS *const m, const mDNSs32 now, AuthRecord *ResourceRecords, int *proxy) - { - mDNSBool showheader = mDNStrue; - const AuthRecord *ar; - OwnerOptData owner = zeroOwner; - for (ar = ResourceRecords; ar; ar=ar->next) - { - const char *const ifname = InterfaceNameForID(m, ar->resrec.InterfaceID); - if ((ar->WakeUp.HMAC.l[0] != 0) == (proxy != mDNSNULL)) - { - if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" Int Next Expire State"); } - if (proxy) (*proxy)++; - if (!mDNSPlatformMemSame(&owner, &ar->WakeUp, sizeof(owner))) - { - owner = ar->WakeUp; - if (owner.password.l[0]) - LogMsgNoIdent("Proxying for H-MAC %.6a I-MAC %.6a Password %.6a seq %d", &owner.HMAC, &owner.IMAC, &owner.password, owner.seq); - else if (!mDNSSameEthAddress(&owner.HMAC, &owner.IMAC)) - LogMsgNoIdent("Proxying for H-MAC %.6a I-MAC %.6a seq %d", &owner.HMAC, &owner.IMAC, owner.seq); - else - LogMsgNoIdent("Proxying for %.6a seq %d", &owner.HMAC, owner.seq); - } - if (AuthRecord_uDNS(ar)) - LogMsgNoIdent("%7d %7d %7d %7d %s", - ar->ThisAPInterval / mDNSPlatformOneSecond, - (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond, - ar->expire ? (ar->expire - now) / mDNSPlatformOneSecond : 0, - ar->state, ARDisplayString(m, ar)); - else if (ar->ARType == AuthRecordLocalOnly) - LogMsgNoIdent(" LO %s", ARDisplayString(m, ar)); - else if (ar->ARType == AuthRecordP2P) - LogMsgNoIdent(" PP %s", ARDisplayString(m, ar)); - else - LogMsgNoIdent("%7d %7d %7d %7s %s", - ar->ThisAPInterval / mDNSPlatformOneSecond, - ar->AnnounceCount ? (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond : 0, - ar->TimeExpire ? (ar->TimeExpire - now) / mDNSPlatformOneSecond : 0, - ifname ? ifname : "ALL", - ARDisplayString(m, ar)); - usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); - } - } - if (showheader) LogMsgNoIdent(""); - } + { + mDNSBool showheader = mDNStrue; + const AuthRecord *ar; + OwnerOptData owner = zeroOwner; + for (ar = ResourceRecords; ar; ar=ar->next) + { + const char *const ifname = InterfaceNameForID(m, ar->resrec.InterfaceID); + if ((ar->WakeUp.HMAC.l[0] != 0) == (proxy != mDNSNULL)) + { + if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" Int Next Expire State"); } + if (proxy) (*proxy)++; + if (!mDNSPlatformMemSame(&owner, &ar->WakeUp, sizeof(owner))) + { + owner = ar->WakeUp; + if (owner.password.l[0]) + LogMsgNoIdent("Proxying for H-MAC %.6a I-MAC %.6a Password %.6a seq %d", &owner.HMAC, &owner.IMAC, &owner.password, owner.seq); + else if (!mDNSSameEthAddress(&owner.HMAC, &owner.IMAC)) + LogMsgNoIdent("Proxying for H-MAC %.6a I-MAC %.6a seq %d", &owner.HMAC, &owner.IMAC, owner.seq); + else + LogMsgNoIdent("Proxying for %.6a seq %d", &owner.HMAC, owner.seq); + } + if (AuthRecord_uDNS(ar)) + LogMsgNoIdent("%7d %7d %7d %7d %s", + ar->ThisAPInterval / mDNSPlatformOneSecond, + (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond, + ar->expire ? (ar->expire - now) / mDNSPlatformOneSecond : 0, + ar->state, ARDisplayString(m, ar)); + else if (ar->ARType == AuthRecordLocalOnly) + LogMsgNoIdent(" LO %s", ARDisplayString(m, ar)); + else if (ar->ARType == AuthRecordP2P) + LogMsgNoIdent(" PP %s", ARDisplayString(m, ar)); + else + LogMsgNoIdent("%7d %7d %7d %7s %s", + ar->ThisAPInterval / mDNSPlatformOneSecond, + ar->AnnounceCount ? (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond : 0, + ar->TimeExpire ? (ar->TimeExpire - now) / mDNSPlatformOneSecond : 0, + ifname ? ifname : "ALL", + ARDisplayString(m, ar)); + usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); + } + } + if (showheader) LogMsgNoIdent(""); + } mDNSexport void udsserver_info(mDNS *const m) - { - const mDNSs32 now = mDNS_TimeNow(m); - mDNSu32 CacheUsed = 0, CacheActive = 0, slot; - int ProxyA = 0, ProxyD = 0; - const CacheGroup *cg; - const CacheRecord *cr; - const DNSQuestion *q; - const DNameListElem *d; - const SearchListElem *s; + { + const mDNSs32 now = mDNS_TimeNow(m); + mDNSu32 CacheUsed = 0, CacheActive = 0, slot; + int ProxyA = 0, ProxyD = 0; + const CacheGroup *cg; + const CacheRecord *cr; + const DNSQuestion *q; + const DNameListElem *d; + const SearchListElem *s; - LogMsgNoIdent("Timenow 0x%08lX (%d)", (mDNSu32)now, now); + LogMsgNoIdent("Timenow 0x%08lX (%d)", (mDNSu32)now, now); - LogMsgNoIdent("------------ Cache -------------"); - LogMsgNoIdent("Slt Q TTL if U Type rdlen"); - for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) - for (cg = m->rrcache_hash[slot]; cg; cg=cg->next) - { - CacheUsed++; // Count one cache entity for the CacheGroup object - for (cr = cg->members; cr; cr=cr->next) - { - const mDNSs32 remain = cr->resrec.rroriginalttl - (now - cr->TimeRcvd) / mDNSPlatformOneSecond; - const char *ifname; - mDNSInterfaceID InterfaceID = cr->resrec.InterfaceID; - if (!InterfaceID && cr->resrec.rDNSServer) - InterfaceID = cr->resrec.rDNSServer->interface; - ifname = InterfaceNameForID(m, InterfaceID); - CacheUsed++; - if (cr->CRActiveQuestion) CacheActive++; - LogMsgNoIdent("%3d %s%8ld %-7s%s %-6s%s", - slot, - cr->CRActiveQuestion ? "*" : " ", - remain, - ifname ? ifname : "-U-", - (cr->resrec.RecordType == kDNSRecordTypePacketNegative) ? "-" : - (cr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? " " : "+", - DNSTypeName(cr->resrec.rrtype), - CRDisplayString(m, cr)); - usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); - } - } + LogMsgNoIdent("------------ Cache -------------"); + LogMsgNoIdent("Slt Q TTL if U Type rdlen"); + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + for (cg = m->rrcache_hash[slot]; cg; cg=cg->next) + { + CacheUsed++; // Count one cache entity for the CacheGroup object + for (cr = cg->members; cr; cr=cr->next) + { + const mDNSs32 remain = cr->resrec.rroriginalttl - (now - cr->TimeRcvd) / mDNSPlatformOneSecond; + const char *ifname; + mDNSInterfaceID InterfaceID = cr->resrec.InterfaceID; + if (!InterfaceID && cr->resrec.rDNSServer) + InterfaceID = cr->resrec.rDNSServer->interface1; + ifname = InterfaceNameForID(m, InterfaceID); + CacheUsed++; + if (cr->CRActiveQuestion) CacheActive++; + LogMsgNoIdent("%3d %s%8ld %-7s%s %-6s%s", + slot, + cr->CRActiveQuestion ? "*" : " ", + remain, + ifname ? ifname : "-U-", + (cr->resrec.RecordType == kDNSRecordTypePacketNegative) ? "-" : + (cr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? " " : "+", + DNSTypeName(cr->resrec.rrtype), + CRDisplayString(m, cr)); + usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); + } + } - if (m->rrcache_totalused != CacheUsed) - LogMsgNoIdent("Cache use mismatch: rrcache_totalused is %lu, true count %lu", m->rrcache_totalused, CacheUsed); - if (m->rrcache_active != CacheActive) - LogMsgNoIdent("Cache use mismatch: rrcache_active is %lu, true count %lu", m->rrcache_active, CacheActive); - LogMsgNoIdent("Cache currently contains %lu entities; %lu referenced by active questions", CacheUsed, CacheActive); + if (m->rrcache_totalused != CacheUsed) + LogMsgNoIdent("Cache use mismatch: rrcache_totalused is %lu, true count %lu", m->rrcache_totalused, CacheUsed); + if (m->rrcache_active != CacheActive) + LogMsgNoIdent("Cache use mismatch: rrcache_active is %lu, true count %lu", m->rrcache_active, CacheActive); + LogMsgNoIdent("Cache currently contains %lu entities; %lu referenced by active questions", CacheUsed, CacheActive); - LogMsgNoIdent("--------- Auth Records ---------"); - LogAuthRecords(m, now, m->ResourceRecords, mDNSNULL); + LogMsgNoIdent("--------- Auth Records ---------"); + LogAuthRecords(m, now, m->ResourceRecords, mDNSNULL); - LogMsgNoIdent("--------- LocalOnly, P2P Auth Records ---------"); - LogLocalOnlyAuthRecords(m); + LogMsgNoIdent("--------- LocalOnly, P2P Auth Records ---------"); + LogLocalOnlyAuthRecords(m); - LogMsgNoIdent("--------- /etc/hosts ---------"); - LogEtcHosts(m); + LogMsgNoIdent("--------- /etc/hosts ---------"); + LogEtcHosts(m); - LogMsgNoIdent("------ Duplicate Records -------"); - LogAuthRecords(m, now, m->DuplicateRecords, mDNSNULL); + LogMsgNoIdent("------ Duplicate Records -------"); + LogAuthRecords(m, now, m->DuplicateRecords, mDNSNULL); - LogMsgNoIdent("----- Auth Records Proxied -----"); - LogAuthRecords(m, now, m->ResourceRecords, &ProxyA); + LogMsgNoIdent("----- Auth Records Proxied -----"); + LogAuthRecords(m, now, m->ResourceRecords, &ProxyA); - LogMsgNoIdent("-- Duplicate Records Proxied ---"); - LogAuthRecords(m, now, m->DuplicateRecords, &ProxyD); + LogMsgNoIdent("-- Duplicate Records Proxied ---"); + LogAuthRecords(m, now, m->DuplicateRecords, &ProxyD); - LogMsgNoIdent("---------- Questions -----------"); - if (!m->Questions) LogMsgNoIdent(""); - else - { - CacheUsed = 0; - CacheActive = 0; - LogMsgNoIdent(" Int Next if T NumAns VDNS Qptr DupOf SU SQ Type Name"); - for (q = m->Questions; q; q=q->next) - { - mDNSs32 i = q->ThisQInterval / mDNSPlatformOneSecond; - mDNSs32 n = (NextQSendTime(q) - now) / mDNSPlatformOneSecond; - char *ifname = InterfaceNameForID(m, q->InterfaceID); - CacheUsed++; - if (q->ThisQInterval) CacheActive++; - LogMsgNoIdent("%6d%6d %-7s%s%s %5d 0x%x%x 0x%p 0x%p %1d %2d %-5s%##s%s", - i, n, - ifname ? ifname : mDNSOpaque16IsZero(q->TargetQID) ? "" : "-U-", - mDNSOpaque16IsZero(q->TargetQID) ? (q->LongLived ? "l" : " ") : (q->LongLived ? "L" : "O"), - PrivateQuery(q) ? "P" : " ", - q->CurrentAnswers, q->validDNSServers.l[1], q->validDNSServers.l[0], q, q->DuplicateOf, - q->SuppressUnusable, q->SuppressQuery, DNSTypeName(q->qtype), q->qname.c, q->DuplicateOf ? " (dup)" : ""); - usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); - } - LogMsgNoIdent("%lu question%s; %lu active", CacheUsed, CacheUsed > 1 ? "s" : "", CacheActive); - } + LogMsgNoIdent("---------- Questions -----------"); + if (!m->Questions) LogMsgNoIdent(""); + else + { + CacheUsed = 0; + CacheActive = 0; + LogMsgNoIdent(" Int Next if T NumAns VDNS Qptr DupOf SU SQ Type Name"); + for (q = m->Questions; q; q=q->next) + { + mDNSs32 i = q->ThisQInterval / mDNSPlatformOneSecond; + mDNSs32 n = (NextQSendTime(q) - now) / mDNSPlatformOneSecond; + char *ifname = InterfaceNameForID(m, q->InterfaceID); + CacheUsed++; + if (q->ThisQInterval) CacheActive++; + LogMsgNoIdent("%6d%6d %-7s%s%s %5d 0x%x%x 0x%p 0x%p %1d %2d %-5s%##s%s", + i, n, + ifname ? ifname : mDNSOpaque16IsZero(q->TargetQID) ? "" : "-U-", + mDNSOpaque16IsZero(q->TargetQID) ? (q->LongLived ? "l" : " ") : (q->LongLived ? "L" : "O"), + PrivateQuery(q) ? "P" : " ", + q->CurrentAnswers, q->validDNSServers.l[1], q->validDNSServers.l[0], q, q->DuplicateOf, + q->SuppressUnusable, q->SuppressQuery, DNSTypeName(q->qtype), q->qname.c, q->DuplicateOf ? " (dup)" : ""); + usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); + } + LogMsgNoIdent("%lu question%s; %lu active", CacheUsed, CacheUsed > 1 ? "s" : "", CacheActive); + } - LogMsgNoIdent("----- Local-Only Questions -----"); - if (!m->LocalOnlyQuestions) LogMsgNoIdent(""); - else for (q = m->LocalOnlyQuestions; q; q=q->next) - LogMsgNoIdent(" %5d %-6s%##s%s", - q->CurrentAnswers, DNSTypeName(q->qtype), q->qname.c, q->DuplicateOf ? " (dup)" : ""); + LogMsgNoIdent("----- Local-Only Questions -----"); + if (!m->LocalOnlyQuestions) LogMsgNoIdent(""); + else for (q = m->LocalOnlyQuestions; q; q=q->next) + LogMsgNoIdent(" %5d %-6s%##s%s", + q->CurrentAnswers, DNSTypeName(q->qtype), q->qname.c, q->DuplicateOf ? " (dup)" : ""); - LogMsgNoIdent("---- Active Client Requests ----"); - if (!all_requests) LogMsgNoIdent(""); - else - { - const request_state *req, *r; - for (req = all_requests; req; req=req->next) - { - if (req->primary) // If this is a subbordinate operation, check that the parent is in the list - { - for (r = all_requests; r && r != req; r=r->next) if (r == req->primary) goto foundparent; - LogMsgNoIdent("%3d: Orhpan operation %p; parent %p not found in request list", req->sd); - } - // For non-subbordinate operations, and subbordinate operations that have lost their parent, write out their info - LogClientInfo(m, req); - foundparent:; - } - } + LogMsgNoIdent("---- Active Client Requests ----"); + if (!all_requests) LogMsgNoIdent(""); + else + { + const request_state *req, *r; + for (req = all_requests; req; req=req->next) + { + if (req->primary) // If this is a subbordinate operation, check that the parent is in the list + { + for (r = all_requests; r && r != req; r=r->next) if (r == req->primary) goto foundparent; + LogMsgNoIdent("%3d: Orhpan operation %p; parent %p not found in request list", req->sd); + } + // For non-subbordinate operations, and subbordinate operations that have lost their parent, write out their info + LogClientInfo(m, req); + foundparent:; + } + } - LogMsgNoIdent("-------- NAT Traversals --------"); - if (!m->NATTraversals) LogMsgNoIdent(""); - else - { - const NATTraversalInfo *nat; - for (nat = m->NATTraversals; nat; nat=nat->next) - { - if (nat->Protocol) - LogMsgNoIdent("%p %s Int %5d Ext %5d Err %d Retry %5d Interval %5d Expire %5d", - nat, nat->Protocol == NATOp_MapTCP ? "TCP" : "UDP", - mDNSVal16(nat->IntPort), mDNSVal16(nat->ExternalPort), nat->Result, - nat->retryPortMap ? (nat->retryPortMap - now) / mDNSPlatformOneSecond : 0, - nat->retryInterval / mDNSPlatformOneSecond, - nat->ExpiryTime ? (nat->ExpiryTime - now) / mDNSPlatformOneSecond : 0); - else - LogMsgNoIdent("%p Address Request Retry %5d Interval %5d", nat, - (m->retryGetAddr - now) / mDNSPlatformOneSecond, - m->retryIntervalGetAddr / mDNSPlatformOneSecond); - usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); - } - } + LogMsgNoIdent("-------- NAT Traversals --------"); + if (!m->NATTraversals) LogMsgNoIdent(""); + else + { + const NATTraversalInfo *nat; + for (nat = m->NATTraversals; nat; nat=nat->next) + { + if (nat->Protocol) + LogMsgNoIdent("%p %s Int %5d Ext %5d Err %d Retry %5d Interval %5d Expire %5d", + nat, nat->Protocol == NATOp_MapTCP ? "TCP" : "UDP", + mDNSVal16(nat->IntPort), mDNSVal16(nat->ExternalPort), nat->Result, + nat->retryPortMap ? (nat->retryPortMap - now) / mDNSPlatformOneSecond : 0, + nat->retryInterval / mDNSPlatformOneSecond, + nat->ExpiryTime ? (nat->ExpiryTime - now) / mDNSPlatformOneSecond : 0); + else + LogMsgNoIdent("%p Address Request Retry %5d Interval %5d", nat, + (m->retryGetAddr - now) / mDNSPlatformOneSecond, + m->retryIntervalGetAddr / mDNSPlatformOneSecond); + usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); + } + } - LogMsgNoIdent("--------- AuthInfoList ---------"); - if (!m->AuthInfoList) LogMsgNoIdent(""); - else - { - const DomainAuthInfo *a; - for (a = m->AuthInfoList; a; a = a->next) - LogMsgNoIdent("%##s %##s %##s %d %s", a->domain.c, a->keyname.c, a->hostname.c, (a->port.b[0] << 8 | a->port.b[1]), a->AutoTunnel ? a->AutoTunnel : ""); - } + LogMsgNoIdent("--------- AuthInfoList ---------"); + if (!m->AuthInfoList) LogMsgNoIdent(""); + else + { + const DomainAuthInfo *a; + for (a = m->AuthInfoList; a; a = a->next) + LogMsgNoIdent("%##s %##s %##s %d %s", a->domain.c, a->keyname.c, a->hostname.c, (a->port.b[0] << 8 | a->port.b[1]), a->AutoTunnel ? a->AutoTunnel : ""); + } - #if APPLE_OSX_mDNSResponder - LogMsgNoIdent("--------- TunnelClients --------"); - if (!m->TunnelClients) LogMsgNoIdent(""); - else - { - const ClientTunnel *c; - for (c = m->TunnelClients; c; c = c->next) - LogMsgNoIdent("%s %##s local %.16a %.4a %.16a remote %.16a %.4a %5d %.16a interval %d", - c->prefix, c->dstname.c, &c->loc_inner, &c->loc_outer, &c->loc_outer6, &c->rmt_inner, &c->rmt_outer, mDNSVal16(c->rmt_outer_port), &c->rmt_outer6, c->q.ThisQInterval); - } - #endif // APPLE_OSX_mDNSResponder + #if APPLE_OSX_mDNSResponder + LogMsgNoIdent("--------- TunnelClients --------"); + if (!m->TunnelClients) LogMsgNoIdent(""); + else + { + const ClientTunnel *c; + for (c = m->TunnelClients; c; c = c->next) + LogMsgNoIdent("%s %##s local %.16a %.4a %.16a remote %.16a %.4a %5d %.16a interval %d", + c->prefix, c->dstname.c, &c->loc_inner, &c->loc_outer, &c->loc_outer6, &c->rmt_inner, &c->rmt_outer, mDNSVal16(c->rmt_outer_port), &c->rmt_outer6, c->q.ThisQInterval); + } + #endif // APPLE_OSX_mDNSResponder - LogMsgNoIdent("---------- Misc State ----------"); + LogMsgNoIdent("---------- Misc State ----------"); - LogMsgNoIdent("PrimaryMAC: %.6a", &m->PrimaryMAC); + LogMsgNoIdent("PrimaryMAC: %.6a", &m->PrimaryMAC); - LogMsgNoIdent("m->SleepState %d (%s) seq %d", - m->SleepState, - m->SleepState == SleepState_Awake ? "Awake" : - m->SleepState == SleepState_Transferring ? "Transferring" : - m->SleepState == SleepState_Sleeping ? "Sleeping" : "?", - m->SleepSeqNum); + LogMsgNoIdent("m->SleepState %d (%s) seq %d", + m->SleepState, + m->SleepState == SleepState_Awake ? "Awake" : + m->SleepState == SleepState_Transferring ? "Transferring" : + m->SleepState == SleepState_Sleeping ? "Sleeping" : "?", + m->SleepSeqNum); - if (!m->SPSSocket) LogMsgNoIdent("Not offering Sleep Proxy Service"); - else LogMsgNoIdent("Offering Sleep Proxy Service: %#s", m->SPSRecords.RR_SRV.resrec.name->c); + if (!m->SPSSocket) LogMsgNoIdent("Not offering Sleep Proxy Service"); + else LogMsgNoIdent("Offering Sleep Proxy Service: %#s", m->SPSRecords.RR_SRV.resrec.name->c); - if (m->ProxyRecords == ProxyA + ProxyD) LogMsgNoIdent("ProxyRecords: %d + %d = %d", ProxyA, ProxyD, ProxyA + ProxyD); + if (m->ProxyRecords == ProxyA + ProxyD) LogMsgNoIdent("ProxyRecords: %d + %d = %d", ProxyA, ProxyD, ProxyA + ProxyD); else LogMsgNoIdent("ProxyRecords: MISMATCH %d + %d = %d != %d", ProxyA, ProxyD, ProxyA + ProxyD, m->ProxyRecords); - LogMsgNoIdent("------ Auto Browse Domains -----"); - if (!AutoBrowseDomains) LogMsgNoIdent(""); - else for (d=AutoBrowseDomains; d; d=d->next) LogMsgNoIdent("%##s", d->name.c); + LogMsgNoIdent("------ Auto Browse Domains -----"); + if (!AutoBrowseDomains) LogMsgNoIdent(""); + else for (d=AutoBrowseDomains; d; d=d->next) LogMsgNoIdent("%##s", d->name.c); - LogMsgNoIdent("--- Auto Registration Domains --"); - if (!AutoRegistrationDomains) LogMsgNoIdent(""); - else for (d=AutoRegistrationDomains; d; d=d->next) LogMsgNoIdent("%##s", d->name.c); + LogMsgNoIdent("--- Auto Registration Domains --"); + if (!AutoRegistrationDomains) LogMsgNoIdent(""); + else for (d=AutoRegistrationDomains; d; d=d->next) LogMsgNoIdent("%##s", d->name.c); - LogMsgNoIdent("--- Search Domains --"); - if (!SearchList) LogMsgNoIdent(""); - else - { - for (s=SearchList; s; s=s->next) - { - char *ifname = InterfaceNameForID(m, s->InterfaceID); - LogMsgNoIdent("%##s %s", s->domain.c, ifname ? ifname : ""); - } - } + LogMsgNoIdent("--- Search Domains --"); + if (!SearchList) LogMsgNoIdent(""); + else + { + for (s=SearchList; s; s=s->next) + { + char *ifname = InterfaceNameForID(m, s->InterfaceID); + LogMsgNoIdent("%##s %s", s->domain.c, ifname ? ifname : ""); + } + } - LogMsgNoIdent("---- Task Scheduling Timers ----"); + LogMsgNoIdent("---- Task Scheduling Timers ----"); - if (!m->NewQuestions) - LogMsgNoIdent("NewQuestion "); - else - LogMsgNoIdent("NewQuestion DelayAnswering %d %d %##s (%s)", - m->NewQuestions->DelayAnswering, m->NewQuestions->DelayAnswering-now, - m->NewQuestions->qname.c, DNSTypeName(m->NewQuestions->qtype)); + if (!m->NewQuestions) + LogMsgNoIdent("NewQuestion "); + else + LogMsgNoIdent("NewQuestion DelayAnswering %d %d %##s (%s)", + m->NewQuestions->DelayAnswering, m->NewQuestions->DelayAnswering-now, + m->NewQuestions->qname.c, DNSTypeName(m->NewQuestions->qtype)); - if (!m->NewLocalOnlyQuestions) - LogMsgNoIdent("NewLocalOnlyQuestions "); - else - LogMsgNoIdent("NewLocalOnlyQuestions %##s (%s)", - m->NewLocalOnlyQuestions->qname.c, DNSTypeName(m->NewLocalOnlyQuestions->qtype)); + if (!m->NewLocalOnlyQuestions) + LogMsgNoIdent("NewLocalOnlyQuestions "); + else + LogMsgNoIdent("NewLocalOnlyQuestions %##s (%s)", + m->NewLocalOnlyQuestions->qname.c, DNSTypeName(m->NewLocalOnlyQuestions->qtype)); - if (!m->NewLocalRecords) - LogMsgNoIdent("NewLocalRecords "); - else - LogMsgNoIdent("NewLocalRecords %02X %s", m->NewLocalRecords->resrec.RecordType, ARDisplayString(m, m->NewLocalRecords)); + if (!m->NewLocalRecords) + LogMsgNoIdent("NewLocalRecords "); + else + LogMsgNoIdent("NewLocalRecords %02X %s", m->NewLocalRecords->resrec.RecordType, ARDisplayString(m, m->NewLocalRecords)); - LogMsgNoIdent("SPSProxyListChanged%s", m->SPSProxyListChanged ? "" : " "); - LogMsgNoIdent("LocalRemoveEvents%s", m->LocalRemoveEvents ? "" : " "); - LogMsgNoIdent("m->RegisterAutoTunnel6 %08X", m->RegisterAutoTunnel6); - LogMsgNoIdent("m->AutoTunnelRelayAddrIn %.16a", &m->AutoTunnelRelayAddrIn); - LogMsgNoIdent("m->AutoTunnelRelayAddrOut %.16a", &m->AutoTunnelRelayAddrOut); + LogMsgNoIdent("SPSProxyListChanged%s", m->SPSProxyListChanged ? "" : " "); + LogMsgNoIdent("LocalRemoveEvents%s", m->LocalRemoveEvents ? "" : " "); + LogMsgNoIdent("m->RegisterAutoTunnel6 %08X", m->RegisterAutoTunnel6); + LogMsgNoIdent("m->AutoTunnelRelayAddrIn %.16a", &m->AutoTunnelRelayAddrIn); + LogMsgNoIdent("m->AutoTunnelRelayAddrOut %.16a", &m->AutoTunnelRelayAddrOut); #define LogTimer(MSG,T) LogMsgNoIdent( MSG " %08X %11d %08X %11d", (T), (T), (T)-now, (T)-now) - LogMsgNoIdent(" ABS (hex) ABS (dec) REL (hex) REL (dec)"); - LogMsgNoIdent("m->timenow %08X %11d", now, now); - LogMsgNoIdent("m->timenow_adjust %08X %11d", m->timenow_adjust, m->timenow_adjust); - LogTimer("m->NextScheduledEvent ", m->NextScheduledEvent); + LogMsgNoIdent(" ABS (hex) ABS (dec) REL (hex) REL (dec)"); + LogMsgNoIdent("m->timenow %08X %11d", now, now); + LogMsgNoIdent("m->timenow_adjust %08X %11d", m->timenow_adjust, m->timenow_adjust); + LogTimer("m->NextScheduledEvent ", m->NextScheduledEvent); #ifndef UNICAST_DISABLED - LogTimer("m->NextuDNSEvent ", m->NextuDNSEvent); - LogTimer("m->NextSRVUpdate ", m->NextSRVUpdate); - LogTimer("m->NextScheduledNATOp ", m->NextScheduledNATOp); - LogTimer("m->retryGetAddr ", m->retryGetAddr); + LogTimer("m->NextuDNSEvent ", m->NextuDNSEvent); + LogTimer("m->NextSRVUpdate ", m->NextSRVUpdate); + LogTimer("m->NextScheduledNATOp ", m->NextScheduledNATOp); + LogTimer("m->retryGetAddr ", m->retryGetAddr); #endif - LogTimer("m->NextCacheCheck ", m->NextCacheCheck); - LogTimer("m->NextScheduledSPS ", m->NextScheduledSPS); - LogTimer("m->NextScheduledSPRetry ", m->NextScheduledSPRetry); - LogTimer("m->DelaySleep ", m->DelaySleep); + LogTimer("m->NextCacheCheck ", m->NextCacheCheck); + LogTimer("m->NextScheduledSPS ", m->NextScheduledSPS); + LogTimer("m->NextScheduledSPRetry ", m->NextScheduledSPRetry); + LogTimer("m->DelaySleep ", m->DelaySleep); - LogTimer("m->NextScheduledQuery ", m->NextScheduledQuery); - LogTimer("m->NextScheduledProbe ", m->NextScheduledProbe); - LogTimer("m->NextScheduledResponse", m->NextScheduledResponse); + LogTimer("m->NextScheduledQuery ", m->NextScheduledQuery); + LogTimer("m->NextScheduledProbe ", m->NextScheduledProbe); + LogTimer("m->NextScheduledResponse", m->NextScheduledResponse); - LogTimer("m->SuppressSending ", m->SuppressSending); - LogTimer("m->SuppressProbes ", m->SuppressProbes); - LogTimer("m->ProbeFailTime ", m->ProbeFailTime); - LogTimer("m->DelaySleep ", m->DelaySleep); - LogTimer("m->SleepLimit ", m->SleepLimit); - LogTimer("m->NextScheduledStopTime ", m->NextScheduledStopTime); - } + LogTimer("m->SuppressSending ", m->SuppressSending); + LogTimer("m->SuppressProbes ", m->SuppressProbes); + LogTimer("m->ProbeFailTime ", m->ProbeFailTime); + LogTimer("m->DelaySleep ", m->DelaySleep); + LogTimer("m->SleepLimit ", m->SleepLimit); + LogTimer("m->NextScheduledStopTime ", m->NextScheduledStopTime); + } #if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING mDNSexport void uds_validatelists(void) - { - const request_state *req, *p; - for (req = all_requests; req; req=req->next) - { - if (req->next == (request_state *)~0 || (req->sd < 0 && req->sd != -2)) - LogMemCorruption("UDS request list: %p is garbage (%d)", req, req->sd); + { + const request_state *req, *p; + for (req = all_requests; req; req=req->next) + { + if (req->next == (request_state *)~0 || (req->sd < 0 && req->sd != -2)) + LogMemCorruption("UDS request list: %p is garbage (%d)", req, req->sd); - if (req->primary == req) - LogMemCorruption("UDS request list: req->primary should not point to self %p/%d", req, req->sd); + if (req->primary == req) + LogMemCorruption("UDS request list: req->primary should not point to self %p/%d", req, req->sd); - if (req->primary && req->replies) - LogMemCorruption("UDS request list: Subordinate request %p/%d/%p should not have replies (%p)", - req, req->sd, req->primary && req->replies); + if (req->primary && req->replies) + LogMemCorruption("UDS request list: Subordinate request %p/%d/%p should not have replies (%p)", + req, req->sd, req->primary && req->replies); - p = req->primary; - if ((long)p & 3) - LogMemCorruption("UDS request list: req %p primary %p is misaligned (%d)", req, p, req->sd); - else if (p && (p->next == (request_state *)~0 || (p->sd < 0 && p->sd != -2))) - LogMemCorruption("UDS request list: req %p primary %p is garbage (%d)", req, p, p->sd); + p = req->primary; + if ((long)p & 3) + LogMemCorruption("UDS request list: req %p primary %p is misaligned (%d)", req, p, req->sd); + else if (p && (p->next == (request_state *)~0 || (p->sd < 0 && p->sd != -2))) + LogMemCorruption("UDS request list: req %p primary %p is garbage (%d)", req, p, p->sd); - reply_state *rep; - for (rep = req->replies; rep; rep=rep->next) - if (rep->next == (reply_state *)~0) - LogMemCorruption("UDS req->replies: %p is garbage", rep); + reply_state *rep; + for (rep = req->replies; rep; rep=rep->next) + if (rep->next == (reply_state *)~0) + LogMemCorruption("UDS req->replies: %p is garbage", rep); - if (req->terminate == connection_termination) - { - registered_record_entry *r; - for (r = req->u.reg_recs; r; r=r->next) - if (r->next == (registered_record_entry *)~0) - LogMemCorruption("UDS req->u.reg_recs: %p is garbage", r); - } - else if (req->terminate == regservice_termination_callback) - { - service_instance *s; - for (s = req->u.servicereg.instances; s; s=s->next) - if (s->next == (service_instance *)~0) - LogMemCorruption("UDS req->u.servicereg.instances: %p is garbage", s); - } - else if (req->terminate == browse_termination_callback) - { - browser_t *b; - for (b = req->u.browser.browsers; b; b=b->next) - if (b->next == (browser_t *)~0) - LogMemCorruption("UDS req->u.browser.browsers: %p is garbage", b); - } - } + if (req->terminate == connection_termination) + { + registered_record_entry *r; + for (r = req->u.reg_recs; r; r=r->next) + if (r->next == (registered_record_entry *)~0) + LogMemCorruption("UDS req->u.reg_recs: %p is garbage", r); + } + else if (req->terminate == regservice_termination_callback) + { + service_instance *s; + for (s = req->u.servicereg.instances; s; s=s->next) + if (s->next == (service_instance *)~0) + LogMemCorruption("UDS req->u.servicereg.instances: %p is garbage", s); + } + else if (req->terminate == browse_termination_callback) + { + browser_t *b; + for (b = req->u.browser.browsers; b; b=b->next) + if (b->next == (browser_t *)~0) + LogMemCorruption("UDS req->u.browser.browsers: %p is garbage", b); + } + } - DNameListElem *d; - for (d = SCPrefBrowseDomains; d; d=d->next) - if (d->next == (DNameListElem *)~0 || d->name.c[0] > 63) - LogMemCorruption("SCPrefBrowseDomains: %p is garbage (%d)", d, d->name.c[0]); + DNameListElem *d; + for (d = SCPrefBrowseDomains; d; d=d->next) + if (d->next == (DNameListElem *)~0 || d->name.c[0] > 63) + LogMemCorruption("SCPrefBrowseDomains: %p is garbage (%d)", d, d->name.c[0]); - ARListElem *b; - for (b = LocalDomainEnumRecords; b; b=b->next) - if (b->next == (ARListElem *)~0 || b->ar.resrec.name->c[0] > 63) - LogMemCorruption("LocalDomainEnumRecords: %p is garbage (%d)", b, b->ar.resrec.name->c[0]); + ARListElem *b; + for (b = LocalDomainEnumRecords; b; b=b->next) + if (b->next == (ARListElem *)~0 || b->ar.resrec.name->c[0] > 63) + LogMemCorruption("LocalDomainEnumRecords: %p is garbage (%d)", b, b->ar.resrec.name->c[0]); - for (d = AutoBrowseDomains; d; d=d->next) - if (d->next == (DNameListElem *)~0 || d->name.c[0] > 63) - LogMemCorruption("AutoBrowseDomains: %p is garbage (%d)", d, d->name.c[0]); + for (d = AutoBrowseDomains; d; d=d->next) + if (d->next == (DNameListElem *)~0 || d->name.c[0] > 63) + LogMemCorruption("AutoBrowseDomains: %p is garbage (%d)", d, d->name.c[0]); - for (d = AutoRegistrationDomains; d; d=d->next) - if (d->next == (DNameListElem *)~0 || d->name.c[0] > 63) - LogMemCorruption("AutoRegistrationDomains: %p is garbage (%d)", d, d->name.c[0]); - } + for (d = AutoRegistrationDomains; d; d=d->next) + if (d->next == (DNameListElem *)~0 || d->name.c[0] > 63) + LogMemCorruption("AutoRegistrationDomains: %p is garbage (%d)", d, d->name.c[0]); + } #endif // APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING mDNSlocal int send_msg(request_state *const req) - { - reply_state *const rep = req->replies; // Send the first waiting reply - ssize_t nwriten; - if (req->no_reply) return(t_complete); + { + reply_state *const rep = req->replies; // Send the first waiting reply + ssize_t nwriten; + if (req->no_reply) return(t_complete); - ConvertHeaderBytes(rep->mhdr); - nwriten = send(req->sd, (char *)&rep->mhdr + rep->nwriten, rep->totallen - rep->nwriten, 0); - ConvertHeaderBytes(rep->mhdr); + ConvertHeaderBytes(rep->mhdr); + nwriten = send(req->sd, (char *)&rep->mhdr + rep->nwriten, rep->totallen - rep->nwriten, 0); + ConvertHeaderBytes(rep->mhdr); - if (nwriten < 0) - { - if (dnssd_errno == dnssd_EINTR || dnssd_errno == dnssd_EWOULDBLOCK) nwriten = 0; - else - { + if (nwriten < 0) + { + if (dnssd_errno == dnssd_EINTR || dnssd_errno == dnssd_EWOULDBLOCK) nwriten = 0; + else + { #if !defined(PLATFORM_NO_EPIPE) - if (dnssd_errno == EPIPE) - return(req->ts = t_terminated); - else + if (dnssd_errno == EPIPE) + return(req->ts = t_terminated); + else #endif - { - LogMsg("send_msg ERROR: failed to write %d of %d bytes to fd %d errno %d (%s)", - rep->totallen - rep->nwriten, rep->totallen, req->sd, dnssd_errno, dnssd_strerror(dnssd_errno)); - return(t_error); - } - } - } - rep->nwriten += nwriten; - return (rep->nwriten == rep->totallen) ? t_complete : t_morecoming; - } + { + LogMsg("send_msg ERROR: failed to write %d of %d bytes to fd %d errno %d (%s)", + rep->totallen - rep->nwriten, rep->totallen, req->sd, dnssd_errno, dnssd_strerror(dnssd_errno)); + return(t_error); + } + } + } + rep->nwriten += nwriten; + return (rep->nwriten == rep->totallen) ? t_complete : t_morecoming; + } mDNSexport mDNSs32 udsserver_idle(mDNSs32 nextevent) - { - mDNSs32 now = mDNS_TimeNow(&mDNSStorage); - request_state **req = &all_requests; + { + mDNSs32 now = mDNS_TimeNow(&mDNSStorage); + request_state **req = &all_requests; - while (*req) - { - request_state *const r = *req; + while (*req) + { + request_state *const r = *req; - if (r->terminate == resolve_termination_callback) - if (r->u.resolve.ReportTime && now - r->u.resolve.ReportTime >= 0) - { - r->u.resolve.ReportTime = 0; - LogMsgNoIdent("Client application bug: DNSServiceResolve(%##s) active for over two minutes. " - "This places considerable burden on the network.", r->u.resolve.qsrv.qname.c); - } + if (r->terminate == resolve_termination_callback) + if (r->u.resolve.ReportTime && now - r->u.resolve.ReportTime >= 0) + { + r->u.resolve.ReportTime = 0; + LogMsgNoIdent("Client application bug: DNSServiceResolve(%##s) active for over two minutes. " + "This places considerable burden on the network.", r->u.resolve.qsrv.qname.c); + } - // Note: Only primary req's have reply lists, not subordinate req's. - while (r->replies) // Send queued replies - { - transfer_state result; - if (r->replies->next) r->replies->rhdr->flags |= dnssd_htonl(kDNSServiceFlagsMoreComing); - result = send_msg(r); // Returns t_morecoming if buffer full because client is not reading - if (result == t_complete) - { - reply_state *fptr = r->replies; - r->replies = r->replies->next; - freeL("reply_state/udsserver_idle", fptr); - r->time_blocked = 0; // reset failure counter after successful send - r->unresponsiveness_reports = 0; - continue; - } - else if (result == t_terminated || result == t_error) - { - LogMsg("%3d: Could not write data to client because of error - aborting connection", r->sd); - LogClientInfo(&mDNSStorage, r); - abort_request(r); - } - break; - } + // Note: Only primary req's have reply lists, not subordinate req's. + while (r->replies) // Send queued replies + { + transfer_state result; + if (r->replies->next) r->replies->rhdr->flags |= dnssd_htonl(kDNSServiceFlagsMoreComing); + result = send_msg(r); // Returns t_morecoming if buffer full because client is not reading + if (result == t_complete) + { + reply_state *fptr = r->replies; + r->replies = r->replies->next; + freeL("reply_state/udsserver_idle", fptr); + r->time_blocked = 0; // reset failure counter after successful send + r->unresponsiveness_reports = 0; + continue; + } + else if (result == t_terminated || result == t_error) + { + LogMsg("%3d: Could not write data to client because of error - aborting connection", r->sd); + LogClientInfo(&mDNSStorage, r); + abort_request(r); + } + break; + } - if (r->replies) // If we failed to send everything, check our time_blocked timer - { - if (nextevent - now > mDNSPlatformOneSecond) nextevent = now + mDNSPlatformOneSecond; + if (r->replies) // If we failed to send everything, check our time_blocked timer + { + if (nextevent - now > mDNSPlatformOneSecond) nextevent = now + mDNSPlatformOneSecond; - if (mDNSStorage.SleepState != SleepState_Awake) r->time_blocked = 0; - else if (!r->time_blocked) r->time_blocked = NonZeroTime(now); - else if (now - r->time_blocked >= 10 * mDNSPlatformOneSecond * (r->unresponsiveness_reports+1)) - { - int num = 0; - struct reply_state *x = r->replies; - while (x) { num++; x=x->next; } - LogMsg("%3d: Could not write data to client after %ld seconds, %d repl%s waiting", - r->sd, (now - r->time_blocked) / mDNSPlatformOneSecond, num, num == 1 ? "y" : "ies"); - if (++r->unresponsiveness_reports >= 60) - { - LogMsg("%3d: Client unresponsive; aborting connection", r->sd); - LogClientInfo(&mDNSStorage, r); - abort_request(r); - } - } - } + if (mDNSStorage.SleepState != SleepState_Awake) r->time_blocked = 0; + else if (!r->time_blocked) r->time_blocked = NonZeroTime(now); + else if (now - r->time_blocked >= 10 * mDNSPlatformOneSecond * (r->unresponsiveness_reports+1)) + { + int num = 0; + struct reply_state *x = r->replies; + while (x) { num++; x=x->next; } + LogMsg("%3d: Could not write data to client after %ld seconds, %d repl%s waiting", + r->sd, (now - r->time_blocked) / mDNSPlatformOneSecond, num, num == 1 ? "y" : "ies"); + if (++r->unresponsiveness_reports >= 60) + { + LogMsg("%3d: Client unresponsive; aborting connection", r->sd); + LogClientInfo(&mDNSStorage, r); + abort_request(r); + } + } + } - if (!dnssd_SocketValid(r->sd)) // If this request is finished, unlink it from the list and free the memory - { - // Since we're already doing a list traversal, we unlink the request directly instead of using AbortUnlinkAndFree() - *req = r->next; - freeL("request_state/udsserver_idle", r); - } - else - req = &r->next; - } - return nextevent; - } + if (!dnssd_SocketValid(r->sd)) // If this request is finished, unlink it from the list and free the memory + { + // Since we're already doing a list traversal, we unlink the request directly instead of using AbortUnlinkAndFree() + *req = r->next; + freeL("request_state/udsserver_idle", r); + } + else + req = &r->next; + } + return nextevent; + } struct CompileTimeAssertionChecks_uds_daemon - { - // Check our structures are reasonable sizes. Including overly-large buffers, or embedding - // other overly-large structures instead of having a pointer to them, can inadvertently - // cause structure sizes (and therefore memory usage) to balloon unreasonably. - char sizecheck_request_state [(sizeof(request_state) <= 1784) ? 1 : -1]; - char sizecheck_registered_record_entry[(sizeof(registered_record_entry) <= 60) ? 1 : -1]; - char sizecheck_service_instance [(sizeof(service_instance) <= 6552) ? 1 : -1]; - char sizecheck_browser_t [(sizeof(browser_t) <= 1050) ? 1 : -1]; - char sizecheck_reply_hdr [(sizeof(reply_hdr) <= 12) ? 1 : -1]; - char sizecheck_reply_state [(sizeof(reply_state) <= 64) ? 1 : -1]; - }; + { + // Check our structures are reasonable sizes. Including overly-large buffers, or embedding + // other overly-large structures instead of having a pointer to them, can inadvertently + // cause structure sizes (and therefore memory usage) to balloon unreasonably. + char sizecheck_request_state [(sizeof(request_state) <= 2000) ? 1 : -1]; + char sizecheck_registered_record_entry[(sizeof(registered_record_entry) <= 60) ? 1 : -1]; + char sizecheck_service_instance [(sizeof(service_instance) <= 6552) ? 1 : -1]; + char sizecheck_browser_t [(sizeof(browser_t) <= 1050) ? 1 : -1]; + char sizecheck_reply_hdr [(sizeof(reply_hdr) <= 12) ? 1 : -1]; + char sizecheck_reply_state [(sizeof(reply_state) <= 64) ? 1 : -1]; + }; diff --git a/src/tools/tools.pro b/src/tools/tools.pro index b6b159f591d..956babc589f 100644 --- a/src/tools/tools.pro +++ b/src/tools/tools.pro @@ -5,6 +5,7 @@ SUBDIRS = qtpromaker \ win32 { SUBDIRS += qtcdebugger + SUBDIRS += mdnssd # win64interrupt only make sense for 64bit builds ENV_CPU=$$(CPU) ENV_LIBPATH=$$(LIBPATH)