]> git.saurik.com Git - apple/mdnsresponder.git/blobdiff - mDNSWindows/DNSServiceTest/Tool.c
mDNSResponder-87.tar.gz
[apple/mdnsresponder.git] / mDNSWindows / DNSServiceTest / Tool.c
diff --git a/mDNSWindows/DNSServiceTest/Tool.c b/mDNSWindows/DNSServiceTest/Tool.c
new file mode 100644 (file)
index 0000000..258363e
--- /dev/null
@@ -0,0 +1,1076 @@
+/*
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+
+    Change History (most recent first):
+       
+$Log: Tool.c,v $
+Revision 1.3  2004/09/16 01:58:25  cheshire
+Fix compiler warnings
+
+Revision 1.2  2004/07/13 21:24:28  rpantos
+Fix for <rdar://problem/3701120>.
+
+Revision 1.1  2004/06/18 04:07:54  rpantos
+Move up one level
+
+Revision 1.12  2004/04/09 21:03:15  bradley
+Changed port numbers to use network byte order for consistency with other platforms.
+
+Revision 1.11  2004/01/30 03:04:32  bradley
+Updated for latest changes to mDNSWindows.
+
+Revision 1.10  2003/10/31 12:18:31  bradley
+Added display of the resolved host name. Show separate TXT record entries on separate lines.
+
+Revision 1.9  2003/10/22 02:00:20  bradley
+Fixed proxy IP setup to be in network byte order so it works on Mac and Windows.
+
+Revision 1.8  2003/10/04 04:47:08  bradley
+Changed DNSServiceRegistrationCreate to treat the port in network byte order for end-to-end consistency.
+
+Revision 1.7  2003/08/20 07:06:34  bradley
+Update to APSL 2.0. Updated change history to match other mDNSResponder files.
+
+Revision 1.6  2003/08/20 06:50:55  bradley
+Updated to latest internal version of the mDNSCore code: Re-did everything to support
+the latest DNSServices APIs (proxies, record updates, etc.); Added support for testing the platform
+neutral DNSServices-based emulation layer for the Mac OS X DNSServiceDiscovery API.
+
+*/
+
+#if( defined( _MSC_VER ) )
+       #pragma warning( disable:4068 )                 // Disable "unknown pragma" warning for "pragma unused".
+       #pragma warning( disable:4127 )                 // Disable "conditional expression is constant" warning for debug macros.
+       #pragma warning( disable:4311 )                 // Disable "type cast : pointer truncation from void *const to int".
+       
+       // No stdint.h with Visual C++ so emulate it here.
+       
+       typedef signed char                     int8_t;         // C99 stdint.h not supported in VC++/VS.NET yet.
+       typedef unsigned char           uint8_t;        // C99 stdint.h not supported in VC++/VS.NET yet.
+       typedef signed short            int16_t;        // C99 stdint.h not supported in VC++/VS.NET yet.
+       typedef unsigned short          uint16_t;       // C99 stdint.h not supported in VC++/VS.NET yet.
+       typedef signed long                     int32_t;        // C99 stdint.h not supported in VC++/VS.NET yet.
+       typedef unsigned long           uint32_t;       // C99 stdint.h not supported in VC++/VS.NET yet.
+#else
+       #include        <stdint.h>
+#endif
+
+#include       <stdio.h>
+#include       <stdlib.h>
+
+#if( __MACH__ )
+       #include        <sys/types.h>
+       #include        <sys/socket.h>
+       #include        <netinet/in.h>
+       
+       #include        <signal.h>
+       #include        <unistd.h>
+       
+       #include        <CoreServices/CoreServices.h>
+#else
+       #define WIN32_LEAN_AND_MEAN
+
+       #include        <winsock2.h>
+       #include        <windows.h>
+#endif
+
+#include       "DNSServices.h"
+#include       "DNSServiceDiscovery.h"
+
+//===========================================================================================================================
+//     Macros
+//===========================================================================================================================
+
+#if( !TARGET_OS_MAC )
+       #define require_action_string( X, LABEL, ACTION, STR )                          \
+               do                                                                                                                              \
+               {                                                                                                                               \
+                       if( !( X ) )                                                                                            \
+                       {                                                                                                                       \
+                               fprintf( stderr, "%s\n", ( STR ) );                                             \
+                               { ACTION; }                                                                                             \
+                               goto LABEL;                                                                                             \
+                       }                                                                                                                       \
+               } while( 0 )
+
+       #define require_string( X, LABEL, STR )                                                         \
+               do                                                                                                                              \
+               {                                                                                                                               \
+                       if( !( X ) )                                                                                            \
+                       {                                                                                                                       \
+                               fprintf( stderr, "%s\n", ( STR ) );                                             \
+                               goto LABEL;                                                                                             \
+                                                                                                                                               \
+                       }                                                                                                                       \
+               } while( 0 )
+
+       #define require_noerr_string( ERR, LABEL, STR )                                         \
+               do                                                                                                                              \
+               {                                                                                                                               \
+                       if( ( ERR ) != 0 )                                                                                      \
+                       {                                                                                                                       \
+                               fprintf( stderr, "%s (%ld)\n", ( STR ), ( ERR ) );              \
+                               goto LABEL;                                                                                             \
+                       }                                                                                                                       \
+               } while( 0 )
+#endif
+
+//===========================================================================================================================
+//     Prototypes
+//===========================================================================================================================
+
+int                            main( int argc, char* argv[] );
+static void                    Usage( void );
+static int                     ProcessArgs( int argc, char* argv[] );
+static DNSStatus       ProcessPreset( int inPreset );
+
+#if( __MACH__ )
+       static void     SigIntHandler( int inSignalNumber );
+#endif
+
+#if( defined( WINVER ) )
+       static BOOL WINAPI      ConsoleControlHandler( DWORD inControlEvent );
+#endif
+
+static void    BrowserCallBack( void *inContext, DNSBrowserRef inRef, DNSStatus inStatusCode, const DNSBrowserEvent *inEvent );
+static void ResolverCallBack( void *inContext, DNSResolverRef inRef, DNSStatus inStatusCode, const DNSResolverEvent *inEvent );
+
+static void
+       RegistrationCallBack( 
+               void *                                                  inContext, 
+               DNSRegistrationRef                              inRef, 
+               DNSStatus                                               inStatusCode, 
+               const DNSRegistrationEvent *    inEvent );
+
+static void
+       HostRegistrationCallBack( 
+               void *                                  inContext, 
+               DNSHostRegistrationRef  inRef, 
+               DNSStatus                               inStatusCode, 
+               void *                                  inData );
+
+static void
+       EmulatedBrowserCallBack(
+               DNSServiceBrowserReplyResultType        inResult, 
+               const char *                                            inName,
+               const char *                                            inType,
+               const char *                                            inDomain,
+               DNSServiceDiscoveryReplyFlags           inFlags,
+               void *                                                          inContext );
+
+static void
+       EmulatedDomainEnumerationCallBack(
+               DNSServiceDomainEnumerationReplyResultType      inResult, 
+               const char *                                                            inDomain,
+               DNSServiceDiscoveryReplyFlags                           inFlags,
+               void *                                                                          inContext );
+
+static void
+       EmulatedResolverCallBack(
+               struct sockaddr *                               inInterfaceAddr, 
+               struct sockaddr *                               inAddr,
+               const char *                                    inTextRecord,
+               DNSServiceDiscoveryReplyFlags   inFlags, 
+               void *                                                  inContext );
+
+static void    EmulatedRegistrationCallBack( DNSServiceRegistrationReplyErrorType inResult, void *inContext );
+
+static char *  IPv4ToString( DNSOpaque32 inIP, char *outString );
+
+//===========================================================================================================================
+//     Globals
+//===========================================================================================================================
+
+#if( defined( WINVER ) )
+       static volatile int             gQuit = 0;
+#endif
+
+static int                                     gPrintTXTRecords = 1;
+
+// Presets
+
+typedef struct PresetData      PresetData;
+struct PresetData
+{
+       int                     argc;
+       char *          argv[ 16 ];
+};
+
+#if 0
+#pragma mark == Presets ==
+#endif
+
+static const PresetData                gPresets[] = 
+{
+       /* 01 */        { 2, { "DNSServiceTest", "-bbd" } },
+       /* 02 */        { 4, { "DNSServiceTest", "-bs",  "_airport._tcp",               "local."  } }, 
+       /* 03 */        { 4, { "DNSServiceTest", "-bs",  "_xserveraid._tcp",    "local."  } }, 
+       /* 04 */        { 3, { "DNSServiceTest", "-rdb", "apple.com" } }, 
+       /* 05 */        { 7, { "DNSServiceTest", "-rs",  "My Fake AirPort",     "_airport._tcp",        "local.",       "1234", "My Fake Info"  } }, 
+       /* 06 */        { 7, { "DNSServiceTest", "-rs",  "My Fake Xserve RAID", "_xserveraid._tcp", "local.",   "1234", "My Fake Info"  } }, 
+       /* 07 */        { 7, { "DNSServiceTest", "-rs",  "My Fake Web Server",  "_http._tcp",           "local.",       "8080", "index.html"  } }, 
+       /* 08 */        { 9, { "DNSServiceTest", "-rps", "www.apple.com", "17.254.0.91", "Apple Web Server", "_http._tcp", "local.", "80", "index.html"  } }, 
+};
+
+const int                                      gPresetsCount = sizeof( gPresets ) / sizeof( gPresets[ 0 ] );
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+//     main
+//===========================================================================================================================
+
+int main( int argc, char* argv[] )
+{      
+       DNSStatus               err;
+       
+       // Set up DNS Services and install a Console Control Handler to handle things like control-c signals.
+       
+       err = DNSServicesInitialize( kDNSFlagAdvertise, 0 );
+       require_noerr_string( err, exit, "could not initialize DNSServiceTest" );
+
+#if( __MACH__ )
+       signal( SIGINT, SigIntHandler );
+#endif
+
+#if( defined( WINVER ) )
+       SetConsoleCtrlHandler( ConsoleControlHandler, TRUE );
+#endif
+
+       ProcessArgs( argc, argv );
+               
+exit:
+       DNSServicesFinalize();
+       return( err );
+}
+
+//===========================================================================================================================
+//     Usage
+//===========================================================================================================================
+
+static void    Usage( void )
+{
+       fprintf( stderr, "\n" );
+       fprintf( stderr, "DNSServiceTest - DNS-SD Test Tool 1.0d1\n" );
+       fprintf( stderr, "\n" );
+       fprintf( stderr, "  -bbd                                                    'b'rowse for 'b'rowsing 'd'omains\n" );
+       fprintf( stderr, "  -brd                                                    'b'rowse for 'r'egistration 'd'omains\n" );
+       fprintf( stderr, "  -bs <type> <domain>                                     'b'rowse for 's'ervices\n" );
+       fprintf( stderr, "  -lsi <name> <type> <domain>                             'l'ookup 's'ervice 'i'nstance\n" );
+       fprintf( stderr, "  -rdb[d] <domain>                                        'r'egister 'd'omain for 'b'rowsing ['d'efault]\n" );
+       fprintf( stderr, "  -rdr[d] <domain>                                        'r'egister 'd'omain for 'r'egistration ['d'efault]\n" );
+       fprintf( stderr, "  -rs <name> <type> <domain> <port> <txt>                 'r'egister 's'ervice\n" );
+       fprintf( stderr, "  -rps <host> <ip> <name> <type> <domain> <port> <txt>    'r'egister 'p'roxy 's'ervice\n" );
+       fprintf( stderr, "  -rnss <name> <type> <domain>                            'r'egister 'n'o 's'uch 's'ervice\n" );
+       
+       fprintf( stderr, "  -ebs <type> <domain>                                    'e'mulated 'b'rowse for 's'ervices\n" );
+       fprintf( stderr, "  -ebd <registration/browse>                              'e'mulated 'b'rowse for 'd'omains\n" );
+       fprintf( stderr, "  -elsi <name> <type> <domain>                            'e'mulated 'l'ookup 's'ervice 'i'nstance\n" );
+       fprintf( stderr, "  -ers <name> <type> <domain> <port> <txt>                'e'mulated 'r'egister 's'ervice\n" );
+       
+       fprintf( stderr, "  -h[elp]                                                 'h'elp\n" );
+       fprintf( stderr, "\n" );
+       
+       fprintf( stderr, "  -1 Preset 1 (browse for browsing domains)    DNSServiceTest -bbd\n" );
+       fprintf( stderr, "  -2 Preset 2 (browse for AirPort)             DNSServiceTest -bs \"_airport._tcp\" \"local.\"\n" );
+       fprintf( stderr, "  -3 Preset 3 (browse for Xserve RAID)         DNSServiceTest -bs \"_xserveraid._tcp\" \"local.\"\n" );
+       fprintf( stderr, "  -4 Preset 4 (register apple.com domain)      DNSServiceTest -rdb \"apple.com\"\n" );
+       fprintf( stderr, "  -5 Preset 5 (register fake AirPort)          DNSServiceTest -rs \"My Fake AirPort\" \"_airport._tcp\" \"local.\" 1234 \"My Fake Info\"\n" );
+       fprintf( stderr, "  -6 Preset 6 (register fake Xserve RAID)      DNSServiceTest -rs \"My Fake Xserve RAID\" \"_xserveraid._tcp\" \"local.\" 1234 \"My Fake Info\"\n" ); 
+       fprintf( stderr, "  -7 Preset 7 (register fake web server)       DNSServiceTest -rs \"My Fake Web Server\" \"_http._tcp\" \"local.\" 8080 \"index.html\"\n" );
+       fprintf( stderr, "\n" );
+}
+
+//===========================================================================================================================
+//     ProcessArgs
+//===========================================================================================================================
+
+static int ProcessArgs( int argc, char* argv[] )
+{      
+       DNSStatus                                               err;
+       int                                                             i;
+       const char *                                    name;
+       const char *                                    type;
+       const char *                                    domain;
+       uint16_t                                                port;
+       const char *                                    text;
+       size_t                                                  textSize;
+       DNSBrowserRef                                   browser;
+       DNSResolverFlags                                resolverFlags;
+       DNSDomainRegistrationType               domainType;
+       const char *                                    label;
+       const char *                                    host;
+       const char *                                    ip;
+       unsigned int                                    b[ 4 ];
+       DNSNetworkAddress                               addr;
+       dns_service_discovery_ref               emulatedRef;
+       
+       // Parse the command line arguments (ignore first argument since it's just the program name).
+       
+       require_action_string( argc >= 2, exit, err = kDNSBadParamErr, "no arguments specified" );
+       
+       for( i = 1; i < argc; ++i )
+       {
+               if( strcmp( argv[ i ], "-bbd" ) == 0 )
+               {
+                       // 'b'rowse for 'b'rowsing 'd'omains
+                       
+                       fprintf( stdout, "browsing for browsing domains\n" );
+                       
+                       err = DNSBrowserCreate( 0, BrowserCallBack, NULL, &browser );
+                       require_noerr_string( err, exit, "create browser failed" );
+                       
+                       err = DNSBrowserStartDomainSearch( browser, 0 );
+                       require_noerr_string( err, exit, "start domain search failed" );
+               }
+               else if( strcmp( argv[ i ], "-brd" ) == 0 )
+               {
+                       // 'b'rowse for 'r'egistration 'd'omains
+                       
+                       fprintf( stdout, "browsing for registration domains\n" );
+                       
+                       err = DNSBrowserCreate( 0, BrowserCallBack, NULL, &browser );
+                       require_noerr_string( err, exit, "create browser failed" );
+                       
+                       err = DNSBrowserStartDomainSearch( browser, kDNSBrowserFlagRegistrationDomainsOnly );
+                       require_noerr_string( err, exit, "start domain search failed" );
+               }
+               else if( strcmp( argv[ i ], "-bs" ) == 0 )
+               {
+                       // 'b'rowse for 's'ervices <type> <domain>
+                                               
+                       require_action_string( argc > ( i + 2 ), exit, err = kDNSBadParamErr, "missing arguments" );
+                       ++i;
+                       type    = argv[ i++ ];
+                       domain  = argv[ i ];
+                       if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) )
+                       {
+                               domain = "local.";
+                       }
+                       fprintf( stdout, "browsing for \"%s.%s\"\n", type, domain );
+                       
+                       err = DNSBrowserCreate( 0, BrowserCallBack, NULL, &browser );
+                       require_noerr_string( err, exit, "create browser failed" );
+                       
+                       err = DNSBrowserStartServiceSearch( browser, kDNSBrowserFlagAutoResolve, type, domain );
+                       require_noerr_string( err, exit, "start service search failed" );
+               }
+               else if( strcmp( argv[ i ], "-lsi" ) == 0 )
+               {
+                       // 'l'ookup 's'ervice 'i'nstance <name> <type> <domain>
+                       
+                       require_action_string( argc > ( i + 3 ), exit, err = kDNSBadParamErr, "missing arguments" );
+                       ++i;
+                       name    = argv[ i++ ];
+                       type    = argv[ i++ ];
+                       domain  = argv[ i ];
+                       if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) )
+                       {
+                               domain = "local.";
+                       }
+                       fprintf( stdout, "resolving \"%s.%s.%s\"\n", name, type, domain );
+                       
+                       resolverFlags = kDNSResolverFlagOnlyIfUnique | 
+                                                       kDNSResolverFlagAutoReleaseByName;
+                       err = DNSResolverCreate( resolverFlags, name, type, domain, ResolverCallBack, 0, NULL, NULL );
+                       require_noerr_string( err, exit, "create resolver failed" );
+               }
+               else if( ( strcmp( argv[ i ], "-rdb" ) == 0 ) || ( strcmp( argv[ i ], "-rdbd" ) == 0 ) )
+               {
+                       // 'r'egister 'd'omain for 'b'rowsing ['d'efault] <domain>
+                                               
+                       require_action_string( argc > ( i + 1 ), exit, err = kDNSBadParamErr, "missing arguments" );
+                       if( strcmp( argv[ i ], "-rdb" ) == 0 )
+                       {
+                               domainType = kDNSDomainRegistrationTypeBrowse;
+                               label = "";
+                       }
+                       else
+                       {
+                               domainType = kDNSDomainRegistrationTypeBrowseDefault;
+                               label = "default ";
+                       }
+                       ++i;
+                       domain = argv[ i ];
+                       if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) )
+                       {
+                               domain = "local.";
+                       }
+                       fprintf( stdout, "registering \"%s\" as %sbrowse domain\n", domain, label );
+                       
+                       err = DNSDomainRegistrationCreate( 0, domain, domainType, NULL );
+                       require_noerr_string( err, exit, "create domain registration failed" );
+               }
+               else if( ( strcmp( argv[ i ], "-rdr" ) == 0 ) || ( strcmp( argv[ i ], "-rdrd" ) == 0 ) )
+               {
+                       // 'r'egister 'd'omain for 'r'egistration ['d'efault] <domain>
+                       
+                       require_action_string( argc > ( i + 1 ), exit, err = kDNSBadParamErr, "missing arguments" );
+                       if( strcmp( argv[ i ], "-rdr" ) == 0 )
+                       {
+                               domainType = kDNSDomainRegistrationTypeRegistration;
+                               label = "";
+                       }
+                       else
+                       {
+                               domainType = kDNSDomainRegistrationTypeRegistrationDefault;
+                               label = "default ";
+                       }
+                       ++i;
+                       domain = argv[ i ];
+                       if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) )
+                       {
+                               domain = "local.";
+                       }
+                       fprintf( stdout, "registering \"%s\" as %sregistration domain\n", domain, label );
+                       
+                       err = DNSDomainRegistrationCreate( 0, domain, domainType, NULL );
+                       require_noerr_string( err, exit, "create domain registration failed" );
+               }
+               else if( strcmp( argv[ i ], "-rs" ) == 0 )
+               {
+                       // 'r'egister 's'ervice <name> <type> <domain> <port> <txt>
+                                               
+                       require_action_string( argc > ( i + 5 ), exit, err = kDNSBadParamErr, "missing arguments" );
+                       ++i;
+                       name            = argv[ i++ ];
+                       type            = argv[ i++ ];
+                       domain          = argv[ i++ ];
+                       port            = (uint16_t) atoi( argv[ i++ ] );
+                       text            = argv[ i ];
+                       textSize        = strlen( text );
+                       if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) )
+                       {
+                               domain = "local.";
+                       }
+                       fprintf( stdout, "registering service \"%s.%s.%s\" port %d text \"%s\"\n", name, type, domain, port, text );
+                       
+                       err = DNSRegistrationCreate( 0, name, type, domain, (DNSPort) port, text, (DNSCount) textSize, NULL, NULL, 
+                                                                                RegistrationCallBack, NULL, NULL );
+                       require_noerr_string( err, exit, "create registration failed" );
+               }
+               else if( strcmp( argv[ i ], "-rps" ) == 0 )
+               {
+                       DNSHostRegistrationFlags                hostFlags;
+                       
+                       // 'r'egister 'p'roxy 's'ervice <host> <ip> <name> <type> <domain> <port> <txt>
+                                               
+                       require_action_string( argc > ( i + 7 ), exit, err = kDNSBadParamErr, "missing arguments" );
+                       ++i;
+                       host            = argv[ i++ ];
+                       ip                      = argv[ i++ ];
+                       name            = argv[ i++ ];
+                       type            = argv[ i++ ];
+                       domain          = argv[ i++ ];
+                       port            = (uint16_t) atoi( argv[ i++ ] );
+                       text            = argv[ i ];
+                       textSize        = strlen( text );
+                       if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) )
+                       {
+                               domain = "local.";
+                       }
+                       
+                       sscanf( ip, "%u.%u.%u.%u", &b[ 0 ], &b[ 1 ], &b[ 2 ], &b[ 3 ] );
+                       addr.addressType                        = kDNSNetworkAddressTypeIPv4;
+                       addr.u.ipv4.addr.v8[ 0 ]        = (DNSUInt8) b[ 0 ];
+                       addr.u.ipv4.addr.v8[ 1 ]        = (DNSUInt8) b[ 1 ];
+                       addr.u.ipv4.addr.v8[ 2 ]        = (DNSUInt8) b[ 2 ];
+                       addr.u.ipv4.addr.v8[ 3 ]        = (DNSUInt8) b[ 3 ];
+                       
+                       fprintf( stdout, "registering proxy service \"%s.%s.%s\" port %d text \"%s\"\n", name, type, domain, port, text );
+                       
+                       hostFlags = kDNSHostRegistrationFlagOnlyIfNotFound | kDNSHostRegistrationFlagAutoRenameOnConflict;
+                       err = DNSHostRegistrationCreate( hostFlags, host, domain, &addr, NULL, 
+                                                                                        HostRegistrationCallBack, NULL, NULL );
+                       require_noerr_string( err, exit, "create host registration failed" );
+                       
+                       err = DNSRegistrationCreate( 0, name, type, domain, (DNSPort) port, text, (DNSCount) textSize, host, NULL, 
+                                                                                RegistrationCallBack, NULL, NULL );
+                       require_noerr_string( err, exit, "create registration failed" );                        
+               }
+               else if( strcmp( argv[ i ], "-rnss" ) == 0 )
+               {
+                       // 'r'egister 'n'o 's'uch 's'ervice <name> <type> <domain>
+                                               
+                       require_action_string( argc > ( i + 3 ), exit, err = kDNSBadParamErr, "missing arguments" );
+                       ++i;
+                       name            = argv[ i++ ];
+                       type            = argv[ i++ ];
+                       domain          = argv[ i++ ];
+                       if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) )
+                       {
+                               domain = "local.";
+                       }
+                       fprintf( stdout, "registering no-such-service \"%s.%s.%s\"\n", name, type, domain );
+                       
+                       err = DNSNoSuchServiceRegistrationCreate( 0, name, type, domain, NULL, RegistrationCallBack, NULL, NULL );
+                       require_noerr_string( err, exit, "create no-such-service registration failed" );
+               }
+               else if( strcmp( argv[ i ], "-ebs" ) == 0 )
+               {
+                       // 'e'mulated 'b'rowse for 's'ervices <type> <domain>
+                                               
+                       require_action_string( argc > ( i + 2 ), exit, err = kDNSBadParamErr, "missing arguments" );
+                       ++i;
+                       type    = argv[ i++ ];
+                       domain  = argv[ i ];
+                       if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) )
+                       {
+                               domain = "local.";
+                       }
+                       fprintf( stdout, "emulated browsing for \"%s.%s\"\n", type, domain );
+                       
+                       emulatedRef = DNSServiceBrowserCreate( type, domain, EmulatedBrowserCallBack, NULL );
+                       require_action_string( emulatedRef, exit, err = kDNSUnknownErr, "create emulated browser failed" );
+               }
+               else if( strcmp( argv[ i ], "-ebd" ) == 0 )
+               {
+                       int             registrationOnly;
+                       
+                       // 'e'mulated 'b'rowse for 'd'omains <registration/browse>
+                       
+                       require_action_string( argc > ( i + 1 ), exit, err = kDNSBadParamErr, "missing arguments" );
+                       ++i;
+                       type = argv[ i++ ];
+                       if( strcmp( type, "registration" ) == 0 )
+                       {
+                               registrationOnly = 1;
+                       }
+                       else if( strcmp( type, "browse" ) == 0 )
+                       {
+                               registrationOnly = 0;
+                       }
+                       else
+                       {
+                               require_action_string( 0, exit, err = kDNSBadParamErr, "invalid browse type" );
+                       }
+                       fprintf( stdout, "emulated browsing for %s domains\n", type );
+                       
+                       emulatedRef = DNSServiceDomainEnumerationCreate( registrationOnly, EmulatedDomainEnumerationCallBack, NULL );
+                       require_action_string( emulatedRef, exit, err = kDNSUnknownErr, "create emulated domain browser failed" );
+               }
+               else if( strcmp( argv[ i ], "-elsi" ) == 0 )
+               {
+                       // 'e'mulated 'l'ookup 's'ervice 'i'nstance <name> <type> <domain>
+                       
+                       require_action_string( argc > ( i + 3 ), exit, err = kDNSBadParamErr, "missing arguments" );
+                       ++i;
+                       name    = argv[ i++ ];
+                       type    = argv[ i++ ];
+                       domain  = argv[ i ];
+                       if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) )
+                       {
+                               domain = "local.";
+                       }
+                       fprintf( stdout, "emulated resolving \"%s.%s.%s\"\n", name, type, domain );
+                       
+                       emulatedRef = DNSServiceResolverResolve( name, type, domain, EmulatedResolverCallBack, NULL );
+                       require_action_string( emulatedRef, exit, err = kDNSUnknownErr, "create emulated resolver failed" );
+               }
+               else if( strcmp( argv[ i ], "-ers" ) == 0 )
+               {
+                       // 'e'mulated 'r'egister 's'ervice <name> <type> <domain> <port> <txt>
+                                               
+                       require_action_string( argc > ( i + 5 ), exit, err = kDNSBadParamErr, "missing arguments" );
+                       ++i;
+                       name            = argv[ i++ ];
+                       type            = argv[ i++ ];
+                       domain          = argv[ i++ ];
+                       port            = (uint16_t) atoi( argv[ i++ ] );
+                       text            = argv[ i ];
+                       textSize        = strlen( text );
+                       if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) )
+                       {
+                               domain = "local.";
+                       }
+                       fprintf( stdout, "registering service \"%s.%s.%s\" port %d text \"%s\"\n", name, type, domain, port, text );
+                       
+                       emulatedRef = DNSServiceRegistrationCreate( name, type, domain, htons( port ), text, 
+                                                                                                               EmulatedRegistrationCallBack, NULL );
+                       require_action_string( emulatedRef, exit, err = kDNSUnknownErr, "create emulated registration failed" );
+               }
+               else if( ( argv[ i ][ 0 ] == '-' ) && isdigit( argv[ i ][ 1 ] ) )
+               {
+                       // Preset
+                       
+                       ProcessPreset( atoi( &argv[ i ][ 1 ] ) );
+                       err = 0;
+                       goto exit;
+               }
+               else if( strcmp( argv[ i ], "-q" ) == 0 )
+               {
+                       // Quiet (no text records)
+                       
+                       gPrintTXTRecords = 0;
+               }
+               else if( ( strcmp( argv[ i ], "-help" ) == 0 ) || ( strcmp( argv[ i ], "-h" ) == 0 ) )
+               {
+                       // Help
+                       
+                       Usage();
+                       err = 0;
+                       goto exit;
+               }
+               else
+               {
+                       // Unknown parameter.
+                       
+                       require_action_string( 0, exit, err = kDNSBadParamErr, "unknown parameter" );
+                       goto exit;
+               }
+       }
+       
+       // Run until control-C'd.
+       
+       #if( __MACH__ )
+               CFRunLoopRun();
+       #endif
+       
+       #if( defined( WINVER ) )
+               while( !gQuit )
+               {
+                       Sleep( 200 );
+               }
+       #endif
+       
+       err = kDNSNoErr;
+       
+exit:
+       if( err )
+       {
+               Usage();
+       }
+       return( err );
+}
+
+//===========================================================================================================================
+//     ProcessPreset
+//===========================================================================================================================
+
+static DNSStatus       ProcessPreset( int inPreset )
+{
+       DNSStatus               err;
+       
+       require_action_string( ( inPreset > 0 ) && ( inPreset <= gPresetsCount ), exit, err = kDNSBadParamErr, "invalid preset" );
+       
+       err = ProcessArgs( gPresets[ inPreset - 1 ].argc, (char **) gPresets[ inPreset - 1 ].argv );
+       
+exit:
+       return( err );
+}
+
+#if( __MACH__ )
+//===========================================================================================================================
+//     SigIntHandler
+//===========================================================================================================================
+
+static void    SigIntHandler( int inSignalNumber )
+{
+       DNS_UNUSED( inSignalNumber );
+       
+       signal( SIGINT, SIG_DFL );
+       CFRunLoopStop( CFRunLoopGetCurrent() );
+}
+#endif
+
+#if( defined( WINVER ) )
+//===========================================================================================================================
+//     ConsoleControlHandler
+//===========================================================================================================================
+
+static BOOL WINAPI     ConsoleControlHandler( DWORD inControlEvent )
+{
+       BOOL            handled;
+       
+       handled = 0;
+       switch( inControlEvent )
+       {
+               case CTRL_C_EVENT:
+               case CTRL_BREAK_EVENT:
+               case CTRL_CLOSE_EVENT:
+               case CTRL_LOGOFF_EVENT:
+               case CTRL_SHUTDOWN_EVENT:
+                       gQuit = 1;
+                       handled = 1;
+                       break;
+               
+               default:
+                       break;
+       }
+       return( handled );
+}
+#endif
+
+//===========================================================================================================================
+//     BrowserCallBack
+//===========================================================================================================================
+
+static void BrowserCallBack( void *inContext, DNSBrowserRef inRef, DNSStatus inStatusCode, const DNSBrowserEvent *inEvent )
+{
+       char            ifIP[ 32 ];
+       char            ip[ 32 ];
+
+       DNS_UNUSED( inContext );
+       DNS_UNUSED( inRef );
+       DNS_UNUSED( inStatusCode );
+       
+       switch( inEvent->type )
+       {
+               case kDNSBrowserEventTypeRelease:
+                       break;
+                       
+               case kDNSBrowserEventTypeAddDomain:                     
+                       fprintf( stdout, "domain         \"%s\" added on interface 0x%p (%s)\n", 
+                                        inEvent->data.addDomain.domain, 
+                                        (int) inEvent->data.addDomain.interfaceID, 
+                                        IPv4ToString( inEvent->data.addDomain.interfaceIP.u.ipv4.addr, ifIP ) );
+                       break;
+               
+               case kDNSBrowserEventTypeAddDefaultDomain:
+                       fprintf( stdout, "default domain \"%s\" added on interface 0x%p (%s)\n", 
+                                        inEvent->data.addDefaultDomain.domain, 
+                                        (int) inEvent->data.addDefaultDomain.interfaceID, 
+                                        IPv4ToString( inEvent->data.addDefaultDomain.interfaceIP.u.ipv4.addr, ifIP ) );
+                       break;
+               
+               case kDNSBrowserEventTypeRemoveDomain:
+                       fprintf( stdout, "domain         \"%s\" removed on interface 0x%p (%s)\n", 
+                                        inEvent->data.removeDomain.domain, 
+                                        (int) inEvent->data.removeDomain.interfaceID, 
+                                        IPv4ToString( inEvent->data.removeDomain.interfaceIP.u.ipv4.addr, ifIP ) );
+                       break;
+               
+               case kDNSBrowserEventTypeAddService:
+                       fprintf( stdout, "service        \"%s.%s%s\" added on interface 0x%p (%s)\n", 
+                                        inEvent->data.addService.name, 
+                                        inEvent->data.addService.type, 
+                                        inEvent->data.addService.domain, 
+                                        (int) inEvent->data.addService.interfaceID, 
+                                        IPv4ToString( inEvent->data.addService.interfaceIP.u.ipv4.addr, ifIP ) );
+                       break;
+               
+               case kDNSBrowserEventTypeRemoveService:
+                       fprintf( stdout, "service        \"%s.%s%s\" removed on interface 0x%p (%s)\n", 
+                                        inEvent->data.removeService.name, 
+                                        inEvent->data.removeService.type, 
+                                        inEvent->data.removeService.domain, 
+                                        (int) inEvent->data.removeService.interfaceID, 
+                                        IPv4ToString( inEvent->data.removeService.interfaceIP.u.ipv4.addr, ifIP ) );
+                       break;
+               
+               case kDNSBrowserEventTypeResolved:
+               {
+                       const uint8_t *         p;
+                       const uint8_t *         end;
+                       int                                     i;
+                       
+                       fprintf( stdout, "resolved       \"%s.%s%s\" to \"%s\" (%s:%u) on interface 0x%p (%s)%s\n", 
+                                        inEvent->data.resolved->name, 
+                                        inEvent->data.resolved->type, 
+                                        inEvent->data.resolved->domain, 
+                                        inEvent->data.resolved->hostName, 
+                                        IPv4ToString( inEvent->data.resolved->address.u.ipv4.addr, ip ), 
+                                        ( inEvent->data.resolved->address.u.ipv4.port.v8[ 0 ] << 8 ) | 
+                                          inEvent->data.resolved->address.u.ipv4.port.v8[ 1 ], 
+                                        (int) inEvent->data.resolved->interfaceID, 
+                                        IPv4ToString( inEvent->data.resolved->interfaceIP.u.ipv4.addr, ifIP ), 
+                                        ( inEvent->data.resolved->textRecordRawSize > 0 ) ? " with text:" : "" );
+                       
+                       p       = (const uint8_t *) inEvent->data.resolved->textRecordRaw;
+                       end = p + inEvent->data.resolved->textRecordRawSize;
+                       i       = 0;
+                       
+                       if( gPrintTXTRecords )
+                       {
+                               while( p < end )
+                               {
+                                       uint8_t         size;
+                                               
+                                       size = *p++;
+                                       if( ( p + size ) > end )
+                                       {
+                                               fprintf( stdout, "\n### MALFORMED TXT RECORD (length byte too big for record)\n\n" );
+                                               break;
+                                       }
+                                       fprintf( stdout, "%5d (%3d bytes): \"%.*s\"\n", i, size, size, p );
+                                       p += size;
+                                       ++i;
+                               }
+                               fprintf( stdout, "\n" );
+                       }
+                       break;
+               }
+               
+               default:
+                       break;
+       }
+}
+
+//===========================================================================================================================
+//     ResolverCallBack
+//===========================================================================================================================
+
+static void ResolverCallBack( void *inContext, DNSResolverRef inRef, DNSStatus inStatusCode, const DNSResolverEvent *inEvent )
+{
+       char            ifIP[ 32 ];
+       char            ip[ 32 ];
+
+       DNS_UNUSED( inContext );
+       DNS_UNUSED( inRef );
+       DNS_UNUSED( inStatusCode );
+
+       switch( inEvent->type )
+       {
+               case kDNSResolverEventTypeResolved:
+               {
+                       const uint8_t *         p;
+                       const uint8_t *         end;
+                       int                                     i;
+                       
+                       fprintf( stdout, "resolved       \"%s.%s%s\" to \"%s\" (%s:%u) on interface 0x%p (%s)%s\n", 
+                                        inEvent->data.resolved.name, 
+                                        inEvent->data.resolved.type, 
+                                        inEvent->data.resolved.domain, 
+                                        inEvent->data.resolved.hostName, 
+                                        IPv4ToString( inEvent->data.resolved.address.u.ipv4.addr, ip ), 
+                                        ( inEvent->data.resolved.address.u.ipv4.port.v8[ 0 ] << 8 ) | 
+                                          inEvent->data.resolved.address.u.ipv4.port.v8[ 1 ], 
+                                        (int) inEvent->data.resolved.interfaceID, 
+                                        IPv4ToString( inEvent->data.resolved.interfaceIP.u.ipv4.addr, ifIP ), 
+                                        ( inEvent->data.resolved.textRecordRawSize > 0 ) ? " with text:" : "" );
+                       
+                       p       = (const uint8_t *) inEvent->data.resolved.textRecordRaw;
+                       end = p + inEvent->data.resolved.textRecordRawSize;
+                       i       = 0;
+                       
+                       if( gPrintTXTRecords )
+                       {
+                               while( p < end )
+                               {
+                                       uint8_t         size;
+                                       
+                                       size = *p++;
+                                       if( ( p + size ) > end )
+                                       {
+                                               fprintf( stdout, "\n### MALFORMED TXT RECORD (length byte too big for record)\n\n" );
+                                               break;
+                                       }
+                                       fprintf( stdout, "%5d (%3d bytes): \"%.*s\"\n", i, size, size, p );
+                                       p += size;
+                                       ++i;
+                               }
+                               fprintf( stdout, "\n" );
+                       }
+                       break;
+               }
+
+               case kDNSResolverEventTypeRelease:
+                       break;
+               
+               default:
+                       break;
+       }
+}
+
+//===========================================================================================================================
+//     RegistrationCallBack
+//===========================================================================================================================
+
+static void
+       RegistrationCallBack( 
+               void *                                                  inContext, 
+               DNSRegistrationRef                              inRef, 
+               DNSStatus                                               inStatusCode, 
+               const DNSRegistrationEvent *    inEvent )
+{
+       DNS_UNUSED( inContext );
+       DNS_UNUSED( inRef );
+       DNS_UNUSED( inStatusCode );
+       
+       switch( inEvent->type )
+       {
+               case kDNSRegistrationEventTypeRelease:  
+                       break;
+               
+               case kDNSRegistrationEventTypeRegistered:
+                       fprintf( stdout, "name registered and active\n" );
+                       break;
+
+               case kDNSRegistrationEventTypeNameCollision:
+                       fprintf( stdout, "name in use, please choose another name\n" );
+                       break;
+               
+               default:
+                       break;
+       }
+}
+
+//===========================================================================================================================
+//     HostRegistrationCallBack
+//===========================================================================================================================
+
+static void
+       HostRegistrationCallBack( 
+               void *                                  inContext, 
+               DNSHostRegistrationRef  inRef, 
+               DNSStatus                               inStatusCode, 
+               void *                                  inData )
+{
+       DNS_UNUSED( inContext );
+       DNS_UNUSED( inRef );
+       DNS_UNUSED( inData );
+       
+       if( inStatusCode == kDNSNoErr )
+       {
+               fprintf( stdout, "host name registered and active\n" );
+       }
+       else if( inStatusCode == kDNSNameConflictErr )
+       {
+               fprintf( stdout, "host name in use, please choose another name\n" );
+       }
+       else
+       {
+               fprintf( stdout, "unknown host registration status (%ld)\n", inStatusCode );
+       }
+}
+
+//===========================================================================================================================
+//     EmulatedBrowserCallBack
+//===========================================================================================================================
+
+static void
+       EmulatedBrowserCallBack(
+               DNSServiceBrowserReplyResultType        inResult, 
+               const char *                                            inName,
+               const char *                                            inType,
+               const char *                                            inDomain,
+               DNSServiceDiscoveryReplyFlags           inFlags,
+               void *                                                          inContext )
+{
+       DNS_UNUSED( inFlags );
+       DNS_UNUSED( inContext );
+       
+       if( inResult == DNSServiceBrowserReplyAddInstance )
+       {
+               fprintf( stdout, "\"%s.%s%s\" service added emulated\n", inName, inType, inDomain );
+       }
+       else if( inResult == DNSServiceBrowserReplyRemoveInstance )
+       {
+               fprintf( stdout, "\"%s.%s%s\" service removed emulated\n", inName, inType, inDomain );
+       }
+       else
+       {
+               fprintf( stdout, "### unknown emulated browser callback result (%d)\n", inResult );
+       }
+}
+
+//===========================================================================================================================
+//     EmulatedDomainEnumerationCallBack
+//===========================================================================================================================
+
+static void
+       EmulatedDomainEnumerationCallBack(
+               DNSServiceDomainEnumerationReplyResultType      inResult, 
+               const char *                                                            inDomain,
+               DNSServiceDiscoveryReplyFlags                           inFlags,
+               void *                                                                          inContext )
+{
+       DNS_UNUSED( inFlags );
+       DNS_UNUSED( inContext );
+       
+       if( inResult == DNSServiceDomainEnumerationReplyAddDomain )
+       {
+               fprintf( stdout, "\"%s\" domain added emulated\n", inDomain );
+       }
+       else if( inResult == DNSServiceDomainEnumerationReplyAddDomainDefault )
+       {
+               fprintf( stdout, "\"%s\" default domain added emulated\n", inDomain );
+       }
+       else if( inResult == DNSServiceDomainEnumerationReplyRemoveDomain )
+       {
+               fprintf( stdout, "\"%s\" domain removed emulated\n", inDomain );
+       }
+       else
+       {
+               fprintf( stdout, "### unknown emulated domain enumeration callback result (%d)\n", inResult );
+       }
+}
+
+//===========================================================================================================================
+//     EmulatedResolverCallBack
+//===========================================================================================================================
+
+static void
+       EmulatedResolverCallBack(
+               struct sockaddr *                               inInterfaceAddr, 
+               struct sockaddr *                               inAddr,
+               const char *                                    inTextRecord,
+               DNSServiceDiscoveryReplyFlags   inFlags, 
+               void *                                                  inContext )
+{
+       struct sockaddr_in *            ifSin4;
+       struct sockaddr_in *            sin4;
+       char                                            ifIP[ 64 ];
+       char                                            ip[ 64 ];
+       
+       DNS_UNUSED( inFlags );
+       DNS_UNUSED( inContext );
+       
+       ifSin4  = (struct sockaddr_in *) inInterfaceAddr;
+       sin4    = (struct sockaddr_in *) inAddr;
+
+       fprintf( stdout, "service resolved to %s:%d on interface %s with text \"%s\"\n", 
+                        IPv4ToString( *( (DNSOpaque32 *) &sin4->sin_addr.s_addr ), ip ), 
+                        ntohs( sin4->sin_port ), 
+                        IPv4ToString( *( (DNSOpaque32 *) &ifSin4->sin_addr.s_addr ), ifIP ), 
+                        inTextRecord ? inTextRecord : "" );
+}
+
+//===========================================================================================================================
+//     EmulatedResolverCallBack
+//===========================================================================================================================
+
+static void    EmulatedRegistrationCallBack( DNSServiceRegistrationReplyErrorType inResult, void *inContext )
+{
+       DNS_UNUSED( inContext );
+       
+       if( inResult == kDNSServiceDiscoveryNoError )
+       {
+               fprintf( stdout, "service name registered successfully\n" );
+       }
+       else
+       {
+               fprintf( stdout, "service registration failed( %d)\n", inResult );
+       }
+}
+
+//===========================================================================================================================
+//     IPv4ToString
+//===========================================================================================================================
+
+static char *  IPv4ToString( DNSOpaque32 inIP, char *outString )
+{
+       sprintf( outString, "%u.%u.%u.%u", inIP.v8[ 0 ], inIP.v8[ 1 ], inIP.v8[ 2 ], inIP.v8[ 3 ] );
+       return( outString );
+}