From 79fabda817f80ea9de9a67d4623cccb882cd0dbb Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Thu, 18 Jun 2020 12:55:21 +0200 Subject: mDNSResponder: Update to v878.50.17 The sources can be obtained via: https://opensource.apple.com/tarballs/mDNSResponder/mDNSResponder-878.50.17.tar.gz Update #4010. --- mDNSResponder/Clients/dnssdutil.c | 1550 +++++++++++++++++++++---- mDNSResponder/Makefile | 2 +- mDNSResponder/mDNSCore/DNSCommon.c | 5 +- mDNSResponder/mDNSCore/mDNS.c | 133 +-- mDNSResponder/mDNSCore/uDNS.c | 52 +- mDNSResponder/mDNSMacOSX/LegacyNATTraversal.c | 3 +- mDNSResponder/mDNSShared/CommonServices.h | 6 +- mDNSResponder/mDNSShared/dns_sd.h | 2 +- mDNSResponder/mDNSShared/dnsextd.conf | 10 +- 9 files changed, 1412 insertions(+), 351 deletions(-) diff --git a/mDNSResponder/Clients/dnssdutil.c b/mDNSResponder/Clients/dnssdutil.c index d1f7c6ca..1bde0daf 100644 --- a/mDNSResponder/Clients/dnssdutil.c +++ b/mDNSResponder/Clients/dnssdutil.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2016-2017 Apple Inc. All rights reserved. + Copyright (c) 2016-2018 Apple Inc. All rights reserved. dnssdutil is a command-line utility for testing the DNS-SD API. */ @@ -9,10 +9,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -21,6 +23,7 @@ #if( TARGET_OS_DARWIN ) #include #include + #include #include #endif @@ -83,6 +86,8 @@ #define kDNSServiceProtocolDescriptors \ "\x00" "IPv4\0" \ "\x01" "IPv6\0" \ + "\x04" "UDP\0" \ + "\x05" "TCP\0" \ "\x00" // (m)DNS @@ -165,14 +170,14 @@ static int gDNSSDFlag_SuppressUnusable = false; static int gDNSSDFlag_Timeout = false; static int gDNSSDFlag_UnicastResponse = false; static int gDNSSDFlag_Unique = false; +static int gDNSSDFlag_WakeOnResolve = false; #define DNSSDFlagsOption() \ IntegerOption( 'f', "flags", &gDNSSDFlags, "flags", \ - "DNSServiceFlags to use. This value is bitwise ORed with other single flag options.", false ) + "DNSServiceFlags as an integer. This value is bitwise ORed with other single flag options.", false ) -#define DNSSDFlagOption( SHORT_CHAR, FLAG_NAME ) \ - BooleanOption( SHORT_CHAR, Stringify( FLAG_NAME ), &gDNSSDFlag_ ## FLAG_NAME, \ - "Use kDNSServiceFlags" Stringify( FLAG_NAME ) "." ) +#define DNSSDFlagOption( SHORT_CHAR, FLAG_NAME ) \ + BooleanOption( SHORT_CHAR, # FLAG_NAME, &gDNSSDFlag_ ## FLAG_NAME, "Use kDNSServiceFlags" # FLAG_NAME "." ) #define DNSSDFlagsOption_DenyCellular() DNSSDFlagOption( 'C', DenyCellular ) #define DNSSDFlagsOption_DenyExpensive() DNSSDFlagOption( 'E', DenyExpensive ) @@ -186,6 +191,7 @@ static int gDNSSDFlag_Unique = false; #define DNSSDFlagsOption_Timeout() DNSSDFlagOption( 'T', Timeout ) #define DNSSDFlagsOption_UnicastResponse() DNSSDFlagOption( 'U', UnicastResponse ) #define DNSSDFlagsOption_Unique() DNSSDFlagOption( 'U', Unique ) +#define DNSSDFlagsOption_WakeOnResolve() DNSSDFlagOption( 'W', WakeOnResolve ) // Interface option @@ -226,7 +232,7 @@ static const char * gConnectionOpt = kConnectionArg_Normal; "\n" \ "to specify the delegator by UUID.\n" \ "\n" \ - "To not use a main connection at all, but instead perform operations on their own connections, use\n" \ + "To not use a main connection at all, but instead perform operations on their own implicit connections, use\n" \ "\n" \ " --no-connection\n" @@ -234,8 +240,12 @@ static const char * gConnectionOpt = kConnectionArg_Normal; // Help text for record data options +#define kRDataArgPrefix_Domain "domain:" #define kRDataArgPrefix_File "file:" #define kRDataArgPrefix_HexString "hex:" +#define kRDataArgPrefix_IPv4 "ipv4:" +#define kRDataArgPrefix_IPv6 "ipv6:" +#define kRDataArgPrefix_SRV "srv:" #define kRDataArgPrefix_String "string:" #define kRDataArgPrefix_TXT "txt:" @@ -243,11 +253,15 @@ static const char * gConnectionOpt = kConnectionArg_Normal; #define kRecordDataSection_Text \ "A record data argument is specified in one of the following formats:\n" \ "\n" \ - "Format Syntax Example\n" \ - "String string: string:'\\x09color=red'\n" \ - "Hexadecimal string hex: hex:c0a80101 or hex:'C0 A8 01 01'\n" \ - "TXT record keys and values txt: txt:'key1=x,key2=y\\,z,key3'\n" \ - "File containing raw record data file: file:dir/record_data.bin\n" + "Format Syntax Example\n" \ + "Domain name domain: domain:demo._test._tcp.local\n" \ + "File containing record data file: file:/path/to/rdata.bin\n" \ + "Hexadecimal string hex: hex:c0000201 or hex:'C0 00 02 01'\n" \ + "IPv4 address ipv4: ipv4:192.0.2.1\n" \ + "IPv6 address ipv6: ipv6:2001:db8::1\n" \ + "SRV record srv:,,, srv:0,0,64206,example.local\n" \ + "String (w/escaped hex bytes) string: string:'\\x09color=red'\n" \ + "TXT record keys and values txt: txt:'vers=1.0,lang=en\\,es\\,fr,passreq'\n" #define RecordDataSection() CLI_SECTION( kRecordDataSection_Name, kRecordDataSection_Text ) @@ -466,6 +480,7 @@ static CLIOption kResolveOpts[] = DNSSDFlagsOption_ForceMulticast(), DNSSDFlagsOption_IncludeAWDL(), DNSSDFlagsOption_ReturnIntermediates(), + DNSSDFlagsOption_WakeOnResolve(), CLI_OPTION_GROUP( "Operation" ), ConnectionOptions(), @@ -519,6 +534,9 @@ static int gGAIPOSIXFlag_V4MappedCFG = false; #if( defined( AI_DEFAULT ) ) static int gGAIPOSIXFlag_Default = false; #endif +#if( defined( AI_UNUSABLE ) ) +static int gGAIPOSIXFlag_Unusable = false; +#endif static CLIOption kGetAddrInfoPOSIXOpts[] = { @@ -544,6 +562,9 @@ static CLIOption kGetAddrInfoPOSIXOpts[] = #if( defined( AI_DEFAULT ) ) BooleanOption( 0 , "flag-default", &gGAIPOSIXFlag_Default, "In hints ai_flags field, set AI_DEFAULT." ), #endif +#if( defined( AI_UNUSABLE ) ) + BooleanOption( 0 , "flag-unusable", &gGAIPOSIXFlag_Unusable, "In hints ai_flags field, set AI_UNUSABLE." ), +#endif CLI_SECTION( "Notes", "See getaddrinfo(3) man page for more details.\n" ), CLI_OPTION_END() @@ -577,6 +598,35 @@ static CLIOption kReverseLookupOpts[] = CLI_OPTION_END() }; +//=========================================================================================================================== +// PortMapping Command Options +//=========================================================================================================================== + +static int gPortMapping_ProtocolTCP = false; +static int gPortMapping_ProtocolUDP = false; +static int gPortMapping_InternalPort = 0; +static int gPortMapping_ExternalPort = 0; +static int gPortMapping_TTL = 0; + +static CLIOption kPortMappingOpts[] = +{ + InterfaceOption(), + BooleanOption( 0, "tcp", &gPortMapping_ProtocolTCP, "Use kDNSServiceProtocol_TCP." ), + BooleanOption( 0, "udp", &gPortMapping_ProtocolUDP, "Use kDNSServiceProtocol_UDP." ), + IntegerOption( 0, "internalPort", &gPortMapping_InternalPort, "port number", "Internal port.", false ), + IntegerOption( 0, "externalPort", &gPortMapping_ExternalPort, "port number", "Requested external port. Use '0' for any external port.", false ), + IntegerOption( 0, "ttl", &gPortMapping_TTL, "seconds", "Requested TTL (renewal period) in seconds. Use '0' for a default value.", false ), + + CLI_OPTION_GROUP( "Flags" ), + DNSSDFlagsOption(), + + CLI_OPTION_GROUP( "Operation" ), + ConnectionOptions(), + + ConnectionSection(), + CLI_OPTION_END() +}; + //=========================================================================================================================== // BrowseAll Command Options //=========================================================================================================================== @@ -586,20 +636,20 @@ static char ** gBrowseAll_ServiceTypes = NULL; static size_t gBrowseAll_ServiceTypesCount = 0; static int gBrowseAll_IncludeAWDL = false; static int gBrowseAll_BrowseTimeSecs = 5; -static int gBrowseAll_ConnectTimeLimitSecs = 5; +static int gBrowseAll_MaxConnectTimeSecs = 0; static CLIOption kBrowseAllOpts[] = { InterfaceOption(), StringOption( 'd', "domain", &gBrowseAll_Domain, "domain", "Domain in which to browse for the service.", false ), - MultiStringOption( 't', "type", &gBrowseAll_ServiceTypes, &gBrowseAll_ServiceTypesCount, "service type", "Service type(s), e.g., \"_ssh._tcp\".", false ), + MultiStringOption( 't', "type", &gBrowseAll_ServiceTypes, &gBrowseAll_ServiceTypesCount, "service type", "Service type(s), e.g., \"_ssh._tcp\". All services are browsed for if none is specified.", false ), CLI_OPTION_GROUP( "Flags" ), DNSSDFlagsOption_IncludeAWDL(), CLI_OPTION_GROUP( "Operation" ), - IntegerOption( 'b', "browseTime", &gBrowseAll_BrowseTimeSecs, "seconds", "Specifies the duration of the browse.", false ), - IntegerOption( 'c', "connectTimeLimit", &gBrowseAll_ConnectTimeLimitSecs, "seconds", "Specifies the max duration of the connect operations.", false ), + IntegerOption( 'b', "browseTime", &gBrowseAll_BrowseTimeSecs, "seconds", "Amount of time to spend browsing. (Default: 5 seconds)", false ), + IntegerOption( 'c', "maxConnectTime", &gBrowseAll_MaxConnectTimeSecs, "seconds", "Max duration of connection attempts. If <= 0, then no connections are attempted. (Default: 0 seconds)", false ), CLI_OPTION_END() }; @@ -731,6 +781,73 @@ static CLIOption kPIDToUUIDOpts[] = CLI_OPTION_END() }; +//=========================================================================================================================== +// SSDP Command Options +//=========================================================================================================================== + +static int gSSDPDiscover_MX = 1; +static const char * gSSDPDiscover_ST = "ssdp:all"; +static int gSSDPDiscover_ReceiveSecs = 1; +static int gSSDPDiscover_UseIPv4 = false; +static int gSSDPDiscover_UseIPv6 = false; +static int gSSDPDiscover_Verbose = false; + +static CLIOption kSSDPDiscoverOpts[] = +{ + StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ), + IntegerOption( 'm', "mx", &gSSDPDiscover_MX, "seconds", "MX value in search request, i.e., max response delay in seconds. (Default: 1 second)", false ), + StringOption( 's', "st", &gSSDPDiscover_ST, "string", "ST value in search request, i.e., the search target. (Default: \"ssdp:all\")", false ), + IntegerOption( 'r', "receiveTime", &gSSDPDiscover_ReceiveSecs, "seconds", "Amount of time to spend receiving responses. -1 means unlimited. (Default: 1 second)", false ), + BooleanOption( 0 , "ipv4", &gSSDPDiscover_UseIPv4, "Use IPv4, i.e., multicast to 239.255.255.250:1900." ), + BooleanOption( 0 , "ipv6", &gSSDPDiscover_UseIPv6, "Use IPv6, i.e., multicast to [ff02::c]:1900" ), + BooleanOption( 'v', "verbose", &gSSDPDiscover_Verbose, "Prints the search request(s) that were sent." ), + CLI_OPTION_END() +}; + +static void SSDPDiscoverCmd( void ); + +static CLIOption kSSDPOpts[] = +{ + Command( "discover", SSDPDiscoverCmd, kSSDPDiscoverOpts, "Crafts and multicasts an SSDP search message.", false ), + CLI_OPTION_END() +}; + +//=========================================================================================================================== +// res_query Command Options +//=========================================================================================================================== + +static const char * gResQuery_Name = NULL; +static const char * gResQuery_Type = NULL; +static const char * gResQuery_Class = NULL; +static int gResQuery_UseLibInfo = false; + +static CLIOption kResQueryOpts[] = +{ + StringOption( 'n', "name", &gResQuery_Name, "domain name", "Full domain name of record to query.", true ), + StringOption( 't', "type", &gResQuery_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ), + StringOption( 'c', "class", &gResQuery_Class, "record class", "Record class by name or number. Default class is IN.", false ), + BooleanOption( 0 , "libinfo", &gResQuery_UseLibInfo, "Use res_query from libinfo instead of libresolv." ), + CLI_OPTION_END() +}; + +//=========================================================================================================================== +// dns_query Command Options +//=========================================================================================================================== + +static const char * gResolvDNSQuery_Name = NULL; +static const char * gResolvDNSQuery_Type = NULL; +static const char * gResolvDNSQuery_Class = NULL; +static const char * gResolvDNSQuery_Path = NULL; + +static CLIOption kResolvDNSQueryOpts[] = +{ + StringOption( 'n', "name", &gResolvDNSQuery_Name, "domain name", "Full domain name of record to query.", true ), + StringOption( 't', "type", &gResolvDNSQuery_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ), + StringOption( 'c', "class", &gResolvDNSQuery_Class, "record class", "Record class by name or number. Default class is IN.", false ), + StringOption( 'p', "path", &gResolvDNSQuery_Path, "file path", "The path argument to pass to dns_open() before calling dns_query(). Default value is NULL.", false ), + CLI_OPTION_END() +}; + //=========================================================================================================================== // Command Table //=========================================================================================================================== @@ -746,6 +863,7 @@ static void ResolveCmd( void ); static void ReconfirmCmd( void ); static void GetAddrInfoPOSIXCmd( void ); static void ReverseLookupCmd( void ); +static void PortMappingCmd( void ); static void BrowseAllCmd( void ); static void GetAddrInfoStressCmd( void ); static void DNSQueryCmd( void ); @@ -754,6 +872,10 @@ static void DNSCryptCmd( void ); #endif static void MDNSQueryCmd( void ); static void PIDToUUIDCmd( void ); +#if( TARGET_OS_DARWIN ) +static void ResQueryCmd( void ); +static void ResolvDNSQueryCmd( void ); +#endif static void DaemonVersionCmd( void ); static CLIOption kGlobalOpts[] = @@ -773,7 +895,8 @@ static CLIOption kGlobalOpts[] = Command( "reconfirm", ReconfirmCmd, kReconfirmOpts, "Uses DNSServiceReconfirmRecord() to reconfirm a record.", false ), Command( "getaddrinfo-posix", GetAddrInfoPOSIXCmd, kGetAddrInfoPOSIXOpts, "Uses getaddrinfo() to resolve a hostname to IP addresses.", false ), Command( "reverseLookup", ReverseLookupCmd, kReverseLookupOpts, "Uses DNSServiceQueryRecord() to perform a reverse IP address lookup.", false ), - Command( "browseAll", BrowseAllCmd, kBrowseAllOpts, "Browse and resolve all, or just some, services.", false ), + Command( "portMapping", PortMappingCmd, kPortMappingOpts, "Uses DNSServiceNATPortMappingCreate() to create a port mapping.", false ), + Command( "browseAll", BrowseAllCmd, kBrowseAllOpts, "Browse and resolve all (or specific) services and, optionally, attempt connections.", false ), // Uncommon commands. @@ -784,6 +907,11 @@ static CLIOption kGlobalOpts[] = #endif Command( "mDNSQuery", MDNSQueryCmd, kMDNSQueryOpts, "Crafts and sends an mDNS query over the specified interface.", true ), Command( "pid2uuid", PIDToUUIDCmd, kPIDToUUIDOpts, "Prints the UUID of a process.", true ), + Command( "ssdp", NULL, kSSDPOpts, "Commands for testing with Simple Service Discovery Protocol (SSDP).", true ), +#if( TARGET_OS_DARWIN ) + Command( "res_query", ResQueryCmd, kResQueryOpts, "Uses res_query() from either libresolv or libinfo to query for a record.", true ), + Command( "dns_query", ResolvDNSQueryCmd, kResolvDNSQueryOpts, "Uses dns_query() from libresolv to query for a record.", true ), +#endif Command( "daemonVersion", DaemonVersionCmd, NULL, "Prints the version of the DNS-SD daemon.", true ), CLI_COMMAND_HELP(), @@ -908,6 +1036,11 @@ static OSStatus const char * inString, uint8_t ** outEndPtr ); static Boolean DomainNameEqual( const uint8_t *inName1, const uint8_t *inName2 ); +static OSStatus + DomainNameFromString( + uint8_t inDomainName[ kDomainNameLengthMax ], + const char * inString, + uint8_t ** outEndPtr ); static OSStatus DomainNameToString( const uint8_t * inDomainName, @@ -2115,7 +2248,7 @@ static void RegisterCmd( void ) // Start operation. err = DNSServiceRegister( &context->opRef, context->flags, context->ifIndex, context->name, context->type, - context->domain, NULL, ntohs( context->port ), (uint16_t) context->txtLen, context->txtPtr, + context->domain, NULL, htons( context->port ), (uint16_t) context->txtLen, context->txtPtr, RegisterCallback, context ); ForgetMem( &context->txtPtr ); require_noerr( err, exit ); @@ -2679,7 +2812,7 @@ static void ReconfirmCmd( void ) require_noerr_quiet( err, exit ); } - // Get record data. + // Get record class. if( gReconfirmRecord_Class ) { @@ -2794,12 +2927,40 @@ static void DNSSD_API ( (X) == AF_UNSPEC ) ? "unspec" : \ "???" ) +typedef struct +{ + unsigned int flag; + const char * str; + +} FlagStringPair; + +#define CaseFlagStringify( X ) { (X), # X } + +const FlagStringPair kGAIPOSIXFlagStringPairs[] = +{ +#if( defined( AI_UNUSABLE ) ) + CaseFlagStringify( AI_UNUSABLE ), +#endif + CaseFlagStringify( AI_NUMERICSERV ), + CaseFlagStringify( AI_V4MAPPED ), + CaseFlagStringify( AI_ADDRCONFIG ), +#if( defined( AI_V4MAPPED_CFG ) ) + CaseFlagStringify( AI_V4MAPPED_CFG ), +#endif + CaseFlagStringify( AI_ALL ), + CaseFlagStringify( AI_NUMERICHOST ), + CaseFlagStringify( AI_CANONNAME ), + CaseFlagStringify( AI_PASSIVE ), + { 0, NULL } +}; + static void GetAddrInfoPOSIXCmd( void ) { OSStatus err; struct addrinfo hints; const struct addrinfo * addrInfo; struct addrinfo * addrInfoList = NULL; + const FlagStringPair * pair; char time[ kTimestampBufLen ]; memset( &hints, 0, sizeof( hints ) ); @@ -2833,6 +2994,9 @@ static void GetAddrInfoPOSIXCmd( void ) #if( defined( AI_DEFAULT ) ) if( gGAIPOSIXFlag_Default ) hints.ai_flags |= AI_DEFAULT; #endif +#if( defined( AI_UNUSABLE ) ) + if( gGAIPOSIXFlag_Unusable ) hints.ai_flags |= AI_UNUSABLE; +#endif // Print prologue. @@ -2840,16 +3004,10 @@ static void GetAddrInfoPOSIXCmd( void ) FPrintF( stdout, "Servname: %s\n", gGAIPOSIX_ServName ); FPrintF( stdout, "Address family: %s\n", AddressFamilyStr( hints.ai_family ) ); FPrintF( stdout, "Flags: 0x%X < ", hints.ai_flags ); - if( hints.ai_flags & AI_NUMERICSERV ) FPrintF( stdout, "AI_NUMERICSERV " ); - if( hints.ai_flags & AI_V4MAPPED ) FPrintF( stdout, "AI_V4MAPPED " ); - if( hints.ai_flags & AI_ADDRCONFIG ) FPrintF( stdout, "AI_ADDRCONFIG " ); -#if( defined( AI_V4MAPPED_CFG ) ) - if( hints.ai_flags & AI_V4MAPPED_CFG ) FPrintF( stdout, "AI_V4MAPPED_CFG " ); -#endif - if( hints.ai_flags & AI_ALL ) FPrintF( stdout, "AI_ALL " ); - if( hints.ai_flags & AI_NUMERICHOST ) FPrintF( stdout, "AI_NUMERICHOST " ); - if( hints.ai_flags & AI_CANONNAME ) FPrintF( stdout, "AI_CANONNAME " ); - if( hints.ai_flags & AI_PASSIVE ) FPrintF( stdout, "AI_PASSIVE " ); + for( pair = kGAIPOSIXFlagStringPairs; pair->str != NULL; ++pair ) + { + if( ( (unsigned int) hints.ai_flags ) & pair->flag ) FPrintF( stdout, "%s ", pair->str ); + } FPrintF( stdout, ">\n" ); FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); FPrintF( stdout, "---\n" ); @@ -2860,7 +3018,7 @@ static void GetAddrInfoPOSIXCmd( void ) GetTimestampStr( time ); if( err ) { - FPrintF( stderr, "Error %#m: %s.\n", err, gai_strerror( err ) ); + FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) ); } else { @@ -2896,6 +3054,7 @@ static void ReverseLookupCmd( void ) uint8_t ipv6Addr[ 16 ]; char recordName[ ( 16 * 4 ) + 9 + 1 ]; int useMainConnection; + const char * endPtr; // Set up SIGINT handler. @@ -2944,16 +3103,16 @@ static void ReverseLookupCmd( void ) // Create reverse lookup record name. err = StringToIPv4Address( gReverseLookup_IPAddr, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix, - &ipv4Addr, NULL, NULL, NULL, NULL ); - if( err ) + &ipv4Addr, NULL, NULL, NULL, &endPtr ); + if( err || ( *endPtr != '\0' ) ) { char * dst; int i; err = StringToIPv6Address( gReverseLookup_IPAddr, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix | kStringToIPAddressFlagsNoScope, - ipv6Addr, NULL, NULL, NULL, NULL ); - if( err ) + ipv6Addr, NULL, NULL, NULL, &endPtr ); + if( err || ( *endPtr != '\0' ) ) { FPrintF( stderr, "Invalid IP address: \"%s\".\n", gReverseLookup_IPAddr ); err = kParamErr; @@ -3019,6 +3178,202 @@ exit: if( err ) exit( 1 ); } +//=========================================================================================================================== +// PortMappingCmd +//=========================================================================================================================== + +typedef struct +{ + DNSServiceRef mainRef; // Main sdRef for shared connection. + DNSServiceRef opRef; // sdRef for the DNSServiceNATPortMappingCreate operation. + DNSServiceFlags flags; // Flags for DNSServiceNATPortMappingCreate operation. + uint32_t ifIndex; // Interface index argument for DNSServiceNATPortMappingCreate operation. + DNSServiceProtocol protocols; // Protocols argument for DNSServiceNATPortMappingCreate operation. + uint32_t ttl; // TTL argument for DNSServiceNATPortMappingCreate operation. + uint16_t internalPort; // Internal port argument for DNSServiceNATPortMappingCreate operation. + uint16_t externalPort; // External port argument for DNSServiceNATPortMappingCreate operation. + Boolean printedHeader; // True if results header was printed. + +} PortMappingContext; + +static void PortMappingPrintPrologue( const PortMappingContext *inContext ); +static void PortMappingContextFree( PortMappingContext *inContext ); +static void DNSSD_API + PortMappingCallback( + DNSServiceRef inSDRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inError, + uint32_t inExternalIPv4Address, + DNSServiceProtocol inProtocol, + uint16_t inInternalPort, + uint16_t inExternalPort, + uint32_t inTTL, + void * inContext ); + +static void PortMappingCmd( void ) +{ + OSStatus err; + PortMappingContext * context = NULL; + DNSServiceRef sdRef; + dispatch_source_t signalSource = NULL; + int useMainConnection; + + // Set up SIGINT handler. + + signal( SIGINT, SIG_IGN ); + err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource ); + require_noerr( err, exit ); + dispatch_resume( signalSource ); + + // Create context. + + context = (PortMappingContext *) calloc( 1, sizeof( *context ) ); + require_action( context, exit, err = kNoMemoryErr ); + + // Check command parameters. + + if( ( gPortMapping_InternalPort < 0 ) || ( gPortMapping_InternalPort > UINT16_MAX ) ) + { + FPrintF( stderr, "Internal port number %d is out-of-range.\n", gPortMapping_InternalPort ); + err = kParamErr; + goto exit; + } + + if( ( gPortMapping_ExternalPort < 0 ) || ( gPortMapping_ExternalPort > UINT16_MAX ) ) + { + FPrintF( stderr, "External port number %d is out-of-range.\n", gPortMapping_ExternalPort ); + err = kParamErr; + goto exit; + } + + // Create main connection. + + if( gConnectionOpt ) + { + err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL ); + require_noerr_quiet( err, exit ); + useMainConnection = true; + } + else + { + useMainConnection = false; + } + + // Get flags. + + context->flags = GetDNSSDFlagsFromOpts(); + if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection; + + // Get interface index. + + err = InterfaceIndexFromArgString( gInterface, &context->ifIndex ); + require_noerr_quiet( err, exit ); + + // Set remaining parameters. + + if( gPortMapping_ProtocolTCP ) context->protocols |= kDNSServiceProtocol_TCP; + if( gPortMapping_ProtocolUDP ) context->protocols |= kDNSServiceProtocol_UDP; + context->ttl = (uint32_t) gPortMapping_TTL; + context->internalPort = (uint16_t) gPortMapping_InternalPort; + context->externalPort = (uint16_t) gPortMapping_ExternalPort; + + // Print prologue. + + PortMappingPrintPrologue( context ); + + // Start operation. + + if( useMainConnection ) sdRef = context->mainRef; + err = DNSServiceNATPortMappingCreate( &sdRef, context->flags, context->ifIndex, context->protocols, + htons( context->internalPort ), htons( context->externalPort ), context->ttl, PortMappingCallback, context ); + require_noerr( err, exit ); + + context->opRef = sdRef; + if( !useMainConnection ) + { + err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() ); + require_noerr( err, exit ); + } + + dispatch_main(); + +exit: + dispatch_source_forget( &signalSource ); + if( context ) PortMappingContextFree( context ); + if( err ) exit( 1 ); +} + +//=========================================================================================================================== +// PortMappingPrintPrologue +//=========================================================================================================================== + +static void PortMappingPrintPrologue( const PortMappingContext *inContext ) +{ + char ifName[ kInterfaceNameBufLen ]; + char time[ kTimestampBufLen ]; + + InterfaceIndexToName( inContext->ifIndex, ifName ); + + FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors ); + FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName ); + FPrintF( stdout, "Protocols: %#{flags}\n", inContext->protocols, kDNSServiceProtocolDescriptors ); + FPrintF( stdout, "Internal Port: %u\n", inContext->internalPort ); + FPrintF( stdout, "External Port: %u\n", inContext->externalPort ); + FPrintF( stdout, "TTL: %u%?s\n", inContext->ttl, !inContext->ttl, " (system will use a default value.)" ); + FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); + FPrintF( stdout, "---\n" ); + +} + +//=========================================================================================================================== +// PortMappingContextFree +//=========================================================================================================================== + +static void PortMappingContextFree( PortMappingContext *inContext ) +{ + DNSServiceForget( &inContext->opRef ); + DNSServiceForget( &inContext->mainRef ); + free( inContext ); +} + +//=========================================================================================================================== +// PortMappingCallback +//=========================================================================================================================== + +static void DNSSD_API + PortMappingCallback( + DNSServiceRef inSDRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inError, + uint32_t inExternalIPv4Address, + DNSServiceProtocol inProtocol, + uint16_t inInternalPort, + uint16_t inExternalPort, + uint32_t inTTL, + void * inContext ) +{ + PortMappingContext * const context = (PortMappingContext *) inContext; + char time[ kTimestampBufLen ]; + char errorStr[ 128 ]; + + Unused( inSDRef ); + Unused( inFlags ); + + GetTimestampStr( time ); + + if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " (error: %#m)", inError ); + if( !context->printedHeader ) + { + FPrintF( stdout, "%-26s IF %7s %15s %7s %6s Protocol\n", "Timestamp", "IntPort", "ExtAddr", "ExtPort", "TTL" ); + context->printedHeader = true; + } + FPrintF( stdout, "%-26s %2u %7u %15.4a %7u %6u %#{flags}%?s\n", + time, inInterfaceIndex, ntohs( inInternalPort), &inExternalIPv4Address, ntohs( inExternalPort ), inTTL, + inProtocol, kDNSServiceProtocolDescriptors, inError, errorStr ); +} + //=========================================================================================================================== // BrowseAllCmd //=========================================================================================================================== @@ -3042,7 +3397,7 @@ typedef struct uint32_t ifIndex; int pendingConnectCount; int browseTimeSecs; - int connectTimeLimitSecs; + int maxConnectTimeSecs; Boolean includeAWDL; Boolean useColoredText; @@ -3276,7 +3631,7 @@ static void BrowseAllCmd( void ) gBrowseAll_ServiceTypes = NULL; gBrowseAll_ServiceTypesCount = 0; context->browseTimeSecs = gBrowseAll_BrowseTimeSecs; - context->connectTimeLimitSecs = gBrowseAll_ConnectTimeLimitSecs; + context->maxConnectTimeSecs = gBrowseAll_MaxConnectTimeSecs; context->includeAWDL = gBrowseAll_IncludeAWDL ? true : false; #if( TARGET_OS_POSIX ) context->useColoredText = isatty( STDOUT_FILENO ) ? true : false; @@ -3332,8 +3687,8 @@ static void BrowseAllPrintPrologue( const BrowseAllContext *inContext ) InterfaceIndexToName( inContext->ifIndex, ifName ); - FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName ); - FPrintF( stdout, "Service types: "); + FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName ); + FPrintF( stdout, "Service types: "); if( inContext->serviceTypesCount > 0 ) { FPrintF( stdout, "%s", inContext->serviceTypes[ 0 ] ); @@ -3344,10 +3699,10 @@ static void BrowseAllPrintPrologue( const BrowseAllContext *inContext ) { FPrintF( stdout, "all services\n" ); } - FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "default domains" ); - FPrintF( stdout, "Browse time: %d second%?c\n", inContext->browseTimeSecs, inContext->browseTimeSecs != 1, 's' ); - FPrintF( stdout, "Connect time limit: %d second%?c\n", - inContext->connectTimeLimitSecs, inContext->connectTimeLimitSecs != 1, 's' ); + FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "default domains" ); + FPrintF( stdout, "Browse time: %d second%?c\n", inContext->browseTimeSecs, inContext->browseTimeSecs != 1, 's' ); + FPrintF( stdout, "Max connect time: %d second%?c\n", + inContext->maxConnectTimeSecs, inContext->maxConnectTimeSecs != 1, 's' ); FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); FPrintF( stdout, "---\n" ); } @@ -3704,10 +4059,10 @@ static void BrowseAllStop( void *inContext ) } DNSServiceForget( &context->mainRef ); - if( ( context->pendingConnectCount > 0 ) && ( context->connectTimeLimitSecs > 0 ) ) + if( ( context->pendingConnectCount > 0 ) && ( context->maxConnectTimeSecs > 0 ) ) { check( !context->exitTimer ); - err = DispatchTimerCreate( dispatch_time_seconds( context->connectTimeLimitSecs ), DISPATCH_TIME_FOREVER, + err = DispatchTimerCreate( dispatch_time_seconds( context->maxConnectTimeSecs ), DISPATCH_TIME_FOREVER, 100 * kNanosecondsPerMillisecond, BrowseAllExit, NULL, context, &context->exitTimer ); require_noerr( err, exit ); dispatch_resume( context->exitTimer ); @@ -3764,7 +4119,6 @@ static void BrowseAllExit( void *inContext ) { char ifname[ IF_NAMESIZE + 1 ]; - FPrintF( stdout, "%*s" "%s via ", Indent( 2 ), instance->name ); FPrintF( stdout, "%*s" "%s via ", Indent( 2 ), instance->name ); if( instance->ifIndex == 0 ) { @@ -3809,31 +4163,35 @@ static void BrowseAllExit( void *inContext ) addr->connectError = kTimeoutErr; } - FPrintF( stdout, "%*s" "%-##47a %4llu ms (", Indent( 4 ), + FPrintF( stdout, "%*s" "%-##47a %4llu ms", Indent( 4 ), &addr->sip.sa, UpTicksToMilliseconds( addr->foundTicks - instance->getAddrStartTicks ) ); + if( context->maxConnectTimeSecs <= 0 ) + { + FPrintF( stdout, "\n" ); + continue; + } switch( addr->connectStatus ) { case kConnectStatus_None: - FPrintF( stdout, "%s", kStatusStr_NoConnectionAttempted ); + FPrintF( stdout, " (%s)\n", kStatusStr_NoConnectionAttempted ); break; case kConnectStatus_Succeeded: - FPrintF( stdout, "%s in %.2f ms", + FPrintF( stdout, " (%s in %.2f ms)\n", context->useColoredText ? kStatusStr_CouldConnectColored : kStatusStr_CouldConnect, addr->connectTimeSecs * 1000 ); break; case kConnectStatus_Failed: - FPrintF( stdout, "%s: %m", + FPrintF( stdout, " (%s: %m)\n", context->useColoredText ? kStatusStr_CouldNotConnectColored : kStatusStr_CouldNotConnect, addr->connectError ); break; default: - FPrintF( stdout, "%s", kStatusStr_Unknown ); + FPrintF( stdout, " (%s)\n", kStatusStr_Unknown ); break; } - FPrintF( stdout, ")\n" ); } FPrintF( stdout, "\n" ); @@ -4230,7 +4588,7 @@ static OSStatus newAddr->foundTicks = nowTicks; SockAddrCopy( inSockAddr, &newAddr->sip.sa ); - if( inInstance->isTCP && ( inInstance->port != kDiscardProtocolPort ) ) + if( ( inContext->maxConnectTimeSecs > 0 ) && inInstance->isTCP && ( inInstance->port != kDiscardProtocolPort ) ) { char destination[ kSockAddrStringMaxSize ]; @@ -5114,8 +5472,7 @@ static void DNSCryptReceiveCertHandler( void *inContext ) err = DNSMessageGetAnswerSection( context->msgBuf, context->msgLen, &ptr ); require_noerr( err, exit ); - targetName[ 0 ] = 0; - err = DomainNameAppendString( targetName, context->providerName, NULL ); + err = DomainNameFromString( targetName, context->providerName, NULL ); require_noerr( err, exit ); answerCount = DNSHeaderGetAnswerCount( hdr ); @@ -5520,7 +5877,7 @@ typedef struct Boolean useIPv4; // True if the query should be sent via IPv4 multicast. Boolean useIPv6; // True if the query should be sent via IPv6 multicast. char ifName[ IF_NAMESIZE + 1 ]; // Name of the interface over which to send the query. - uint8_t qname[ kDomainNameLengthMax ]; // Buffer to hold the QNAME in label format. + uint8_t qname[ kDomainNameLengthMax ]; // Buffer to hold the QNAME in DNS label format. uint8_t msgBuf[ 8940 ]; // Message buffer. 8940 is max size used by mDNSResponder. } MDNSQueryContext; @@ -5594,7 +5951,7 @@ static void MDNSQueryCmd( void ) if( !context->isQU && ( context->localPort == kMDNSPort ) ) { - SocketJoinMulticast( sockV4, &mcastAddr4, ifNamePtr, context->ifIndex ); + err = SocketJoinMulticast( sockV4, &mcastAddr4, ifNamePtr, context->ifIndex ); require_noerr( err, exit ); } } @@ -5625,7 +5982,7 @@ static void MDNSQueryCmd( void ) if( !context->isQU && ( context->localPort == kMDNSPort ) ) { - SocketJoinMulticast( sockV6, &mcastAddr6, ifNamePtr, context->ifIndex ); + err = SocketJoinMulticast( sockV6, &mcastAddr6, ifNamePtr, context->ifIndex ); require_noerr( err, exit ); } } @@ -5837,51 +6194,664 @@ exit: } //=========================================================================================================================== -// DaemonVersionCmd +// SSDPDiscoverCmd //=========================================================================================================================== -static void DaemonVersionCmd( void ) -{ - OSStatus err; - uint32_t size, version; - char strBuf[ 16 ]; - - size = (uint32_t) sizeof( version ); - err = DNSServiceGetProperty( kDNSServiceProperty_DaemonVersion, &version, &size ); - require_noerr( err, exit ); - - FPrintF( stdout, "Daemon version: %s\n", SourceVersionToCString( version, strBuf ) ); - -exit: - if( err ) exit( 1 ); -} - -//=========================================================================================================================== -// Exit -//=========================================================================================================================== +#define kSSDPPort 1900 -static void Exit( void *inContext ) +typedef struct { - const char * const reason = (const char *) inContext; - char time[ kTimestampBufLen ]; + HTTPHeader header; // HTTP header object for sending and receiving. + dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket. + dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket. + int receiveSecs; // After send, the amount of time to spend receiving. + uint32_t ifindex; // Index of the interface over which to send the query. + Boolean useIPv4; // True if the query should be sent via IPv4 multicast. + Boolean useIPv6; // True if the query should be sent via IPv6 multicast. - FPrintF( stdout, "---\n" ); - FPrintF( stdout, "End time: %s\n", GetTimestampStr( time ) ); - if( reason ) FPrintF( stdout, "End reason: %s\n", reason ); - exit( gExitCode ); -} +} SSDPDiscoverContext; -//=========================================================================================================================== -// GetTimestampStr -//=========================================================================================================================== +static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext ); +static void SSDPDiscoverReadHandler( void *inContext ); +static int SocketToPortNumber( SocketRef inSock ); +static OSStatus WriteSSDPSearchRequest( HTTPHeader *inHeader, const void *inHostSA, int inMX, const char *inST ); -static char * GetTimestampStr( char inBuffer[ kTimestampBufLen ] ) +static void SSDPDiscoverCmd( void ) { - struct timeval now; - struct tm * tm; - size_t len; + OSStatus err; + SSDPDiscoverContext * context; + dispatch_source_t signalSource = NULL; + SocketRef sockV4 = kInvalidSocketRef; + SocketRef sockV6 = kInvalidSocketRef; + ssize_t n; + int sendCount; + char time[ kTimestampBufLen ]; - gettimeofday( &now, NULL ); + // Set up SIGINT handler. + + signal( SIGINT, SIG_IGN ); + err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource ); + require_noerr( err, exit ); + dispatch_resume( signalSource ); + + // Check command parameters. + + if( gSSDPDiscover_ReceiveSecs < -1 ) + { + FPrintF( stdout, "Invalid receive time: %d seconds.\n", gSSDPDiscover_ReceiveSecs ); + err = kParamErr; + goto exit; + } + + // Create context. + + context = (SSDPDiscoverContext *) calloc( 1, sizeof( *context ) ); + require_action( context, exit, err = kNoMemoryErr ); + + context->receiveSecs = gSSDPDiscover_ReceiveSecs; + context->useIPv4 = ( gSSDPDiscover_UseIPv4 || !gSSDPDiscover_UseIPv6 ) ? true : false; + context->useIPv6 = ( gSSDPDiscover_UseIPv6 || !gSSDPDiscover_UseIPv4 ) ? true : false; + + err = InterfaceIndexFromArgString( gInterface, &context->ifindex ); + require_noerr_quiet( err, exit ); + + // Set up IPv4 socket. + + if( context->useIPv4 ) + { + int port; + err = UDPClientSocketOpen( AF_INET, NULL, 0, -1, &port, &sockV4 ); + require_noerr( err, exit ); + + err = SocketSetMulticastInterface( sockV4, NULL, context->ifindex ); + require_noerr( err, exit ); + + err = setsockopt( sockV4, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &(uint8_t){ 1 }, (socklen_t) sizeof( uint8_t ) ); + err = map_socket_noerr_errno( sockV4, err ); + require_noerr( err, exit ); + } + + // Set up IPv6 socket. + + if( context->useIPv6 ) + { + err = UDPClientSocketOpen( AF_INET6, NULL, 0, -1, NULL, &sockV6 ); + require_noerr( err, exit ); + + err = SocketSetMulticastInterface( sockV6, NULL, context->ifindex ); + require_noerr( err, exit ); + + err = setsockopt( sockV6, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &(int){ 1 }, (socklen_t) sizeof( int ) ); + err = map_socket_noerr_errno( sockV6, err ); + require_noerr( err, exit ); + } + + // Print prologue. + + SSDPDiscoverPrintPrologue( context ); + + // Send mDNS query message. + + sendCount = 0; + if( IsValidSocket( sockV4 ) ) + { + struct sockaddr_in mcastAddr4; + + memset( &mcastAddr4, 0, sizeof( mcastAddr4 ) ); + SIN_LEN_SET( &mcastAddr4 ); + mcastAddr4.sin_family = AF_INET; + mcastAddr4.sin_port = htons( kSSDPPort ); + mcastAddr4.sin_addr.s_addr = htonl( 0xEFFFFFFA ); // 239.255.255.250 + + err = WriteSSDPSearchRequest( &context->header, &mcastAddr4, gSSDPDiscover_MX, gSSDPDiscover_ST ); + require_noerr( err, exit ); + + n = sendto( sockV4, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr4, + (socklen_t) sizeof( mcastAddr4 ) ); + err = map_socket_value_errno( sockV4, n == (ssize_t) context->header.len, n ); + if( err ) + { + FPrintF( stderr, "*** Failed to send query on IPv4 socket with error %#m\n", err ); + ForgetSocket( &sockV4 ); + } + else + { + if( gSSDPDiscover_Verbose ) + { + GetTimestampStr( time ); + FPrintF( stdout, "---\n" ); + FPrintF( stdout, "Send time: %s\n", time ); + FPrintF( stdout, "Source Port: %d\n", SocketToPortNumber( sockV4 ) ); + FPrintF( stdout, "Destination: %##a\n", &mcastAddr4 ); + FPrintF( stdout, "Message size: %zu\n", context->header.len ); + FPrintF( stdout, "HTTP header:\n%1{text}", context->header.buf, context->header.len ); + } + ++sendCount; + } + } + + if( IsValidSocket( sockV6 ) ) + { + struct sockaddr_in6 mcastAddr6; + + memset( &mcastAddr6, 0, sizeof( mcastAddr6 ) ); + SIN6_LEN_SET( &mcastAddr6 ); + mcastAddr6.sin6_family = AF_INET6; + mcastAddr6.sin6_port = htons( kSSDPPort ); + mcastAddr6.sin6_addr.s6_addr[ 0 ] = 0xFF; // SSDP IPv6 link-local multicast address FF02::C + mcastAddr6.sin6_addr.s6_addr[ 1 ] = 0x02; + mcastAddr6.sin6_addr.s6_addr[ 15 ] = 0x0C; + + err = WriteSSDPSearchRequest( &context->header, &mcastAddr6, gSSDPDiscover_MX, gSSDPDiscover_ST ); + require_noerr( err, exit ); + + n = sendto( sockV6, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr6, + (socklen_t) sizeof( mcastAddr6 ) ); + err = map_socket_value_errno( sockV6, n == (ssize_t) context->header.len, n ); + if( err ) + { + FPrintF( stderr, "*** Failed to send query on IPv6 socket with error %#m\n", err ); + ForgetSocket( &sockV6 ); + } + else + { + if( gSSDPDiscover_Verbose ) + { + GetTimestampStr( time ); + FPrintF( stdout, "---\n" ); + FPrintF( stdout, "Send time: %s\n", time ); + FPrintF( stdout, "Source Port: %d\n", SocketToPortNumber( sockV6 ) ); + FPrintF( stdout, "Destination: %##a\n", &mcastAddr6 ); + FPrintF( stdout, "Message size: %zu\n", context->header.len ); + FPrintF( stdout, "HTTP header:\n%1{text}", context->header.buf, context->header.len ); + } + ++sendCount; + } + } + require_action_quiet( sendCount > 0, exit, err = kUnexpectedErr ); + + // If there's no wait period after the send, then exit. + + if( context->receiveSecs == 0 ) goto exit; + + // Create dispatch read sources for socket(s). + + if( IsValidSocket( sockV4 ) ) + { + SocketContext * sockContext; + + sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) ); + require_action( sockContext, exit, err = kNoMemoryErr ); + + err = DispatchReadSourceCreate( sockV4, SSDPDiscoverReadHandler, SocketContextCancelHandler, sockContext, + &context->readSourceV4 ); + if( err ) ForgetMem( &sockContext ); + require_noerr( err, exit ); + + sockContext->context = context; + sockContext->sock = sockV4; + sockV4 = kInvalidSocketRef; + dispatch_resume( context->readSourceV4 ); + } + + if( IsValidSocket( sockV6 ) ) + { + SocketContext * sockContext; + + sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) ); + require_action( sockContext, exit, err = kNoMemoryErr ); + + err = DispatchReadSourceCreate( sockV6, SSDPDiscoverReadHandler, SocketContextCancelHandler, sockContext, + &context->readSourceV6 ); + if( err ) ForgetMem( &sockContext ); + require_noerr( err, exit ); + + sockContext->context = context; + sockContext->sock = sockV6; + sockV6 = kInvalidSocketRef; + dispatch_resume( context->readSourceV6 ); + } + + if( context->receiveSecs > 0 ) + { + dispatch_after_f( dispatch_time_seconds( context->receiveSecs ), dispatch_get_main_queue(), kExitReason_Timeout, + Exit ); + } + dispatch_main(); + +exit: + ForgetSocket( &sockV4 ); + ForgetSocket( &sockV6 ); + dispatch_source_forget( &signalSource ); + if( err ) exit( 1 ); +} + +static int SocketToPortNumber( SocketRef inSock ) +{ + OSStatus err; + sockaddr_ip sip; + socklen_t len; + + len = (socklen_t) sizeof( sip ); + err = getsockname( inSock, &sip.sa, &len ); + err = map_socket_noerr_errno( inSock, err ); + check_noerr( err ); + return( err ? -1 : SockAddrGetPort( &sip ) ); +} + +static OSStatus WriteSSDPSearchRequest( HTTPHeader *inHeader, const void *inHostSA, int inMX, const char *inST ) +{ + OSStatus err; + + err = HTTPHeader_InitRequest( inHeader, "M-SEARCH", "*", "HTTP/1.1" ); + require_noerr( err, exit ); + + err = HTTPHeader_SetField( inHeader, "Host", "%##a", inHostSA ); + require_noerr( err, exit ); + + err = HTTPHeader_SetField( inHeader, "ST", "%s", inST ? inST : "ssdp:all" ); + require_noerr( err, exit ); + + err = HTTPHeader_SetField( inHeader, "Man", "\"ssdp:discover\"" ); + require_noerr( err, exit ); + + err = HTTPHeader_SetField( inHeader, "MX", "%d", inMX ); + require_noerr( err, exit ); + + err = HTTPHeader_Commit( inHeader ); + require_noerr( err, exit ); + +exit: + return( err ); +} + +//=========================================================================================================================== +// SSDPDiscoverPrintPrologue +//=========================================================================================================================== + +static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext ) +{ + const int receiveSecs = inContext->receiveSecs; + const char * ifName; + char ifNameBuf[ IF_NAMESIZE + 1 ]; + NetTransportType ifType; + char time[ kTimestampBufLen ]; + + ifName = if_indextoname( inContext->ifindex, ifNameBuf ); + + ifType = kNetTransportType_Undefined; + if( ifName ) SocketGetInterfaceInfo( kInvalidSocketRef, ifName, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &ifType ); + + FPrintF( stdout, "Interface: %s/%d/%s\n", + ifName ? ifName : "?", inContext->ifindex, NetTransportTypeToString( ifType ) ); + FPrintF( stdout, "IP protocols: %?s%?s%?s\n", + inContext->useIPv4, "IPv4", ( inContext->useIPv4 && inContext->useIPv6 ), ", ", inContext->useIPv6, "IPv6" ); + FPrintF( stdout, "Receive duration: " ); + if( receiveSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", receiveSecs, receiveSecs != 1, 's' ); + else FPrintF( stdout, "∞\n" ); + FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); +} + +//=========================================================================================================================== +// SSDPDiscoverReadHandler +//=========================================================================================================================== + +static void SSDPDiscoverReadHandler( void *inContext ) +{ + OSStatus err; + SocketContext * const sockContext = (SocketContext *) inContext; + SSDPDiscoverContext * const context = (SSDPDiscoverContext *) sockContext->context; + HTTPHeader * const header = &context->header; + sockaddr_ip fromAddr; + size_t msgLen; + char time[ kTimestampBufLen ]; + + GetTimestampStr( time ); + + err = SocketRecvFrom( sockContext->sock, header->buf, sizeof( header->buf ), &msgLen, &fromAddr, sizeof( fromAddr ), + NULL, NULL, NULL, NULL ); + require_noerr( err, exit ); + + FPrintF( stdout, "---\n" ); + FPrintF( stdout, "Receive time: %s\n", time ); + FPrintF( stdout, "Source: %##a\n", &fromAddr ); + FPrintF( stdout, "Message size: %zu\n", msgLen ); + header->len = msgLen; + if( HTTPHeader_Validate( header ) ) + { + FPrintF( stdout, "HTTP header:\n%1{text}", header->buf, header->len ); + if( header->extraDataLen > 0 ) + { + FPrintF( stdout, "HTTP body: %1.1H", header->extraDataPtr, (int) header->extraDataLen, INT_MAX ); + } + } + else + { + FPrintF( stdout, "Invalid HTTP message:\n%1.1H", header->buf, (int) msgLen, INT_MAX ); + goto exit; + } + +exit: + if( err ) exit( 1 ); +} + +//=========================================================================================================================== +// HTTPHeader_Validate +// +// Parses for the end of an HTTP header and updates the HTTPHeader structure so it's ready to parse. Returns true if valid. +// This assumes the "buf" and "len" fields are set. The other fields are set by this function. +// +// Note: This was copied from CoreUtils because the HTTPHeader_Validate function is currently not exported in the framework. +//=========================================================================================================================== + +Boolean HTTPHeader_Validate( HTTPHeader *inHeader ) +{ + const char * src; + const char * end; + + // Check for interleaved binary data (4 byte header that begins with $). See RFC 2326 section 10.12. + + require( inHeader->len < sizeof( inHeader->buf ), exit ); + src = inHeader->buf; + end = src + inHeader->len; + if( ( ( end - src ) >= 4 ) && ( src[ 0 ] == '$' ) ) + { + src += 4; + } + else + { + // Search for an empty line (HTTP-style header/body separator). CRLFCRLF, LFCRLF, or LFLF accepted. + // $$$ TO DO: Start from the last search location to avoid re-searching the same data over and over. + + for( ;; ) + { + while( ( src < end ) && ( src[ 0 ] != '\n' ) ) ++src; + if( src >= end ) goto exit; + ++src; + if( ( ( end - src ) >= 2 ) && ( src[ 0 ] == '\r' ) && ( src[ 1 ] == '\n' ) ) // CFLFCRLF or LFCRLF + { + src += 2; + break; + } + else if( ( ( end - src ) >= 1 ) && ( src[ 0 ] == '\n' ) ) // LFLF + { + src += 1; + break; + } + } + } + inHeader->extraDataPtr = src; + inHeader->extraDataLen = (size_t)( end - src ); + inHeader->len = (size_t)( src - inHeader->buf ); + return( true ); + +exit: + return( false ); +} + +#if( TARGET_OS_DARWIN ) +//=========================================================================================================================== +// ResQueryCmd +//=========================================================================================================================== + +// res_query() from libresolv is actually called res_9_query (see /usr/include/resolv.h). + +SOFT_LINK_LIBRARY_EX( "/usr/lib", resolv ); +SOFT_LINK_FUNCTION_EX( resolv, res_9_query, + int, + ( const char *dname, int class, int type, u_char *answer, int anslen ), + ( dname, class, type, answer, anslen ) ); + +// res_query() from libinfo + +SOFT_LINK_LIBRARY_EX( "/usr/lib", info ); +SOFT_LINK_FUNCTION_EX( info, res_query, + int, + ( const char *dname, int class, int type, u_char *answer, int anslen ), + ( dname, class, type, answer, anslen ) ); + +typedef int ( *res_query_f )( const char *dname, int class, int type, u_char *answer, int anslen ); + +static void ResQueryCmd( void ) +{ + OSStatus err; + res_query_f res_query_ptr; + int n; + uint16_t type, class; + char time[ kTimestampBufLen ]; + uint8_t answer[ 1024 ]; + + // Get pointer to one of the res_query() functions. + + if( gResQuery_UseLibInfo ) + { + if( !SOFT_LINK_HAS_FUNCTION( info, res_query ) ) + { + FPrintF( stderr, "Failed to soft link res_query from libinfo.\n" ); + err = kNotFoundErr; + goto exit; + } + res_query_ptr = soft_res_query; + } + else + { + if( !SOFT_LINK_HAS_FUNCTION( resolv, res_9_query ) ) + { + FPrintF( stderr, "Failed to soft link res_query from libresolv.\n" ); + err = kNotFoundErr; + goto exit; + } + res_query_ptr = soft_res_9_query; + } + + // Get record type. + + err = RecordTypeFromArgString( gResQuery_Type, &type ); + require_noerr( err, exit ); + + // Get record class. + + if( gResQuery_Class ) + { + err = RecordClassFromArgString( gResQuery_Class, &class ); + require_noerr( err, exit ); + } + else + { + class = kDNSServiceClass_IN; + } + + // Print prologue. + + FPrintF( stdout, "Name: %s\n", gResQuery_Name ); + FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type ); + FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class ); + FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); + FPrintF( stdout, "---\n" ); + + // Call res_query(). + + n = res_query_ptr( gResQuery_Name, class, type, (u_char *) answer, (int) sizeof( answer ) ); + if( n < 0 ) + { + FPrintF( stderr, "res_query() failed with error: %d (%s).\n", h_errno, hstrerror( h_errno ) ); + err = kUnknownErr; + goto exit; + } + + // Print result. + + FPrintF( stdout, "Message size: %d\n\n", n ); + PrintUDNSMessage( answer, (size_t) n, false ); + +exit: + if( err ) exit( 1 ); +} + +//=========================================================================================================================== +// ResolvDNSQueryCmd +//=========================================================================================================================== + +// dns_handle_t is defined as a pointer to a privately-defined struct in /usr/include/dns.h. It's defined as a void * here to +// avoid including the header file. + +typedef void * dns_handle_t; + +SOFT_LINK_FUNCTION_EX( resolv, dns_open, dns_handle_t, ( const char *path ), ( path ) ); +SOFT_LINK_FUNCTION_VOID_RETURN_EX( resolv, dns_free, ( dns_handle_t *dns ), ( dns ) ); +SOFT_LINK_FUNCTION_EX( resolv, dns_query, + int32_t, ( + dns_handle_t dns, + const char * name, + uint32_t dnsclass, + uint32_t dnstype, + char * buf, + uint32_t len, + struct sockaddr * from, + uint32_t * fromlen ), + ( dns, name, dnsclass, dnstype, buf, len, from, fromlen ) ); + +static void ResolvDNSQueryCmd( void ) +{ + OSStatus err; + int n; + dns_handle_t dns = NULL; + uint16_t type, class; + sockaddr_ip from; + uint32_t fromLen; + char time[ kTimestampBufLen ]; + uint8_t answer[ 1024 ]; + + // Make sure that the required symbols are available. + + if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_open ) ) + { + FPrintF( stderr, "Failed to soft link dns_open from libresolv.\n" ); + err = kNotFoundErr; + goto exit; + } + + if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_free ) ) + { + FPrintF( stderr, "Failed to soft link dns_free from libresolv.\n" ); + err = kNotFoundErr; + goto exit; + } + + if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_query ) ) + { + FPrintF( stderr, "Failed to soft link dns_query from libresolv.\n" ); + err = kNotFoundErr; + goto exit; + } + + // Get record type. + + err = RecordTypeFromArgString( gResolvDNSQuery_Type, &type ); + require_noerr( err, exit ); + + // Get record class. + + if( gResolvDNSQuery_Class ) + { + err = RecordClassFromArgString( gResolvDNSQuery_Class, &class ); + require_noerr( err, exit ); + } + else + { + class = kDNSServiceClass_IN; + } + + // Get dns handle. + + dns = soft_dns_open( gResolvDNSQuery_Path ); + if( !dns ) + { + FPrintF( stderr, "dns_open( %s ) failed.\n", gResolvDNSQuery_Path ); + err = kUnknownErr; + goto exit; + } + + // Print prologue. + + FPrintF( stdout, "Name: %s\n", gResolvDNSQuery_Name ); + FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type ); + FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class ); + FPrintF( stdout, "Path: %s\n", gResolvDNSQuery_Path ? gResolvDNSQuery_Name : "" ); + FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); + FPrintF( stdout, "---\n" ); + + // Call dns_query(). + + memset( &from, 0, sizeof( from ) ); + fromLen = (uint32_t) sizeof( from ); + n = soft_dns_query( dns, gResolvDNSQuery_Name, class, type, (char *) answer, (uint32_t) sizeof( answer ), &from.sa, + &fromLen ); + if( n < 0 ) + { + FPrintF( stderr, "dns_query() failed with error: %d (%s).\n", h_errno, hstrerror( h_errno ) ); + err = kUnknownErr; + goto exit; + } + + // Print result. + + FPrintF( stdout, "From: %##a\n", &from ); + FPrintF( stdout, "Message size: %d\n\n", n ); + PrintUDNSMessage( answer, (size_t) n, false ); + +exit: + if( dns ) soft_dns_free( dns ); + if( err ) exit( 1 ); +} +#endif // TARGET_OS_DARWIN + +//=========================================================================================================================== +// DaemonVersionCmd +//=========================================================================================================================== + +static void DaemonVersionCmd( void ) +{ + OSStatus err; + uint32_t size, version; + char strBuf[ 16 ]; + + size = (uint32_t) sizeof( version ); + err = DNSServiceGetProperty( kDNSServiceProperty_DaemonVersion, &version, &size ); + require_noerr( err, exit ); + + FPrintF( stdout, "Daemon version: %s\n", SourceVersionToCString( version, strBuf ) ); + +exit: + if( err ) exit( 1 ); +} + +//=========================================================================================================================== +// Exit +//=========================================================================================================================== + +static void Exit( void *inContext ) +{ + const char * const reason = (const char *) inContext; + char time[ kTimestampBufLen ]; + + FPrintF( stdout, "---\n" ); + FPrintF( stdout, "End time: %s\n", GetTimestampStr( time ) ); + if( reason ) FPrintF( stdout, "End reason: %s\n", reason ); + exit( gExitCode ); +} + +//=========================================================================================================================== +// GetTimestampStr +//=========================================================================================================================== + +static char * GetTimestampStr( char inBuffer[ kTimestampBufLen ] ) +{ + struct timeval now; + struct tm * tm; + size_t len; + + gettimeofday( &now, NULL ); tm = localtime( &now.tv_sec ); require_action( tm, exit, *inBuffer = '\0' ); @@ -5921,6 +6891,7 @@ static DNSServiceFlags GetDNSSDFlagsFromOpts( void ) if( gDNSSDFlag_Timeout ) flags |= kDNSServiceFlagsTimeout; if( gDNSSDFlag_UnicastResponse ) flags |= kDNSServiceFlagsUnicastResponse; if( gDNSSDFlag_Unique ) flags |= kDNSServiceFlagsUnique; + if( gDNSSDFlag_WakeOnResolve ) flags |= kDNSServiceFlagsWakeOnResolve; return( flags ); } @@ -6056,105 +7027,253 @@ exit: #define kRDataMaxLen UINT16_C( 0xFFFF ) +static OSStatus StringToSRVRData( const char *inString, uint8_t **outPtr, size_t *outLen ); +static OSStatus StringToTXTRData( const char *inString, char inDelimiter, uint8_t **outPtr, size_t *outLen ); + static OSStatus RecordDataFromArgString( const char *inString, uint8_t **outDataPtr, size_t *outDataLen ) { OSStatus err; uint8_t * dataPtr = NULL; size_t dataLen; - DataBuffer dataBuf; - DataBuffer_Init( &dataBuf, NULL, 0, kRDataMaxLen ); + if( 0 ) {} + + // Domain name - if( stricmp_prefix( inString, kRDataArgPrefix_String ) == 0 ) + else if( stricmp_prefix( inString, kRDataArgPrefix_Domain ) == 0 ) { - const char * const strPtr = inString + sizeof_string( kRDataArgPrefix_String ); - const size_t strLen = strlen( strPtr ); - size_t copiedLen; - size_t totalLen; + const char * const str = inString + sizeof_string( kRDataArgPrefix_Domain ); + uint8_t * end; + uint8_t dname[ kDomainNameLengthMax ]; - if( strLen > 0 ) - { - require_action( strLen <= kRDataMaxLen, exit, err = kSizeErr ); - dataPtr = (uint8_t *) malloc( strLen ); - require_action( dataPtr, exit, err = kNoMemoryErr ); - - copiedLen = 0; - ParseQuotedEscapedString( strPtr, strPtr + strLen, "", (char *) dataPtr, strLen, &copiedLen, &totalLen, NULL ); - check( copiedLen == totalLen ); - dataLen = copiedLen; - } - else - { - dataPtr = NULL; - dataLen = 0; - } + err = DomainNameFromString( dname, str, &end ); + require_noerr( err, exit ); + + dataLen = (size_t)( end - dname ); + dataPtr = malloc( dataLen ); + require_action( dataPtr, exit, err = kNoMemoryErr ); + + memcpy( dataPtr, dname, dataLen ); } - else if( stricmp_prefix( inString, kRDataArgPrefix_HexString ) == 0 ) + + // File path + + else if( stricmp_prefix( inString, kRDataArgPrefix_File ) == 0 ) { - const char * const strPtr = inString + sizeof_string( kRDataArgPrefix_HexString ); + const char * const path = inString + sizeof_string( kRDataArgPrefix_File ); - err = HexToDataCopy( strPtr, kSizeCString, kHexToData_DefaultFlags, &dataPtr, &dataLen, NULL ); + err = CopyFileDataByPath( path, (char **) &dataPtr, &dataLen ); require_noerr( err, exit ); require_action( dataLen <= kRDataMaxLen, exit, err = kSizeErr ); } - else if( stricmp_prefix( inString, kRDataArgPrefix_File ) == 0 ) + + // Hexadecimal string + + else if( stricmp_prefix( inString, kRDataArgPrefix_HexString ) == 0 ) { - const char * const path = inString + sizeof_string( kRDataArgPrefix_File ); + const char * const str = inString + sizeof_string( kRDataArgPrefix_HexString ); - err = CopyFileDataByPath( path, (char **) &dataPtr, &dataLen ); + err = HexToDataCopy( str, kSizeCString, kHexToData_DefaultFlags, &dataPtr, &dataLen, NULL ); require_noerr( err, exit ); require_action( dataLen <= kRDataMaxLen, exit, err = kSizeErr ); } - else if( stricmp_prefix( inString, kRDataArgPrefix_TXT ) == 0 ) + + // IPv4 address string + + else if( stricmp_prefix( inString, kRDataArgPrefix_IPv4 ) == 0 ) + { + const char * const str = inString + sizeof_string( kRDataArgPrefix_IPv4 ); + const char * end; + + dataLen = 4; + dataPtr = (uint8_t *) malloc( dataLen ); + require_action( dataPtr, exit, err = kNoMemoryErr ); + + err = StringToIPv4Address( str, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix, + (uint32_t *) dataPtr, NULL, NULL, NULL, &end ); + if( !err && ( *end != '\0' ) ) err = kMalformedErr; + require_noerr( err, exit ); + + *( (uint32_t *) dataPtr ) = HostToBig32( *( (uint32_t *) dataPtr ) ); + } + + // IPv6 address string + + else if( stricmp_prefix( inString, kRDataArgPrefix_IPv6 ) == 0 ) + { + const char * const str = inString + sizeof_string( kRDataArgPrefix_IPv6 ); + const char * end; + + dataLen = 16; + dataPtr = (uint8_t *) malloc( dataLen ); + require_action( dataPtr, exit, err = kNoMemoryErr ); + + err = StringToIPv6Address( str, + kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix | kStringToIPAddressFlagsNoScope, + dataPtr, NULL, NULL, NULL, &end ); + if( !err && ( *end != '\0' ) ) err = kMalformedErr; + require_noerr( err, exit ); + } + + // SRV record + + else if( stricmp_prefix( inString, kRDataArgPrefix_SRV ) == 0 ) + { + const char * const str = inString + sizeof_string( kRDataArgPrefix_SRV ); + + err = StringToSRVRData( str, &dataPtr, &dataLen ); + require_noerr( err, exit ); + } + + // String with escaped hex and octal bytes + + else if( stricmp_prefix( inString, kRDataArgPrefix_String ) == 0 ) { - const char * strPtr = inString + sizeof_string( kRDataArgPrefix_TXT ); - const char * const strEnd = strPtr + strlen( strPtr ); + const char * const str = inString + sizeof_string( kRDataArgPrefix_String ); + const char * const end = str + strlen( str ); + size_t copiedLen; + size_t totalLen; + Boolean success; - while( strPtr < strEnd ) + if( str < end ) { - size_t copiedLen, totalLen; - uint8_t kvBuf[ 1 + 255 + 1 ]; // Length byte + max key-value length + 1 for NUL terminator. + success = ParseQuotedEscapedString( str, end, "", NULL, 0, NULL, &totalLen, NULL ); + require_action( success, exit, err = kParamErr ); + require_action( totalLen <= kRDataMaxLen, exit, err = kSizeErr ); - err = ParseEscapedString( strPtr, strEnd, ',', (char *) &kvBuf[ 1 ], sizeof( kvBuf ) - 1, - &copiedLen, &totalLen, &strPtr ); - require_noerr_quiet( err, exit ); - check( copiedLen == totalLen ); - if( totalLen > 255 ) - { - FPrintF( stderr, "TXT key-value pair length %zu is too long (> 255 bytes).\n", totalLen ); - err = kParamErr; - goto exit; - } + dataLen = totalLen; + dataPtr = (uint8_t *) malloc( dataLen ); + require_action( dataPtr, exit, err = kNoMemoryErr ); - kvBuf[ 0 ] = (uint8_t) copiedLen; - err = DataBuffer_Append( &dataBuf, kvBuf, 1 + kvBuf[ 0 ] ); - require_noerr( err, exit ); + success = ParseQuotedEscapedString( str, end, "", (char *) dataPtr, dataLen, &copiedLen, NULL, NULL ); + require_action( success, exit, err = kParamErr ); + check( copiedLen == dataLen ); } + else + { + dataPtr = NULL; + dataLen = 0; + } + } + + // TXT record + + else if( stricmp_prefix( inString, kRDataArgPrefix_TXT ) == 0 ) + { + const char * const str = inString + sizeof_string( kRDataArgPrefix_TXT ); - err = DataBuffer_Commit( &dataBuf, NULL, NULL ); - require_noerr( err, exit ); - - err = DataBuffer_Detach( &dataBuf, &dataPtr, &dataLen ); + err = StringToTXTRData( str, ',', &dataPtr, &dataLen ); require_noerr( err, exit ); } + + // Unrecognized format + else { FPrintF( stderr, "Unrecognized record data string \"%s\".\n", inString ); err = kParamErr; goto exit; } - err = kNoErr; + err = kNoErr; *outDataLen = dataLen; *outDataPtr = dataPtr; dataPtr = NULL; exit: - DataBuffer_Free( &dataBuf ); FreeNullSafe( dataPtr ); return( err ); } +static OSStatus StringToSRVRData( const char *inString, uint8_t **outPtr, size_t *outLen ) +{ + OSStatus err; + DataBuffer dataBuf; + const char * ptr; + int i; + uint8_t * end; + uint8_t target[ kDomainNameLengthMax ]; + + DataBuffer_Init( &dataBuf, NULL, 0, ( 3 * 2 ) + kDomainNameLengthMax ); + + // Parse and set the priority, weight, and port values (all three are unsigned 16-bit values). + + ptr = inString; + for( i = 0; i < 3; ++i ) + { + char * next; + long value; + uint8_t buf[ 2 ]; + + value = strtol( ptr, &next, 0 ); + require_action_quiet( ( next != ptr ) && ( *next == ',' ), exit, err = kMalformedErr ); + require_action_quiet( ( value >= 0 ) && ( value <= UINT16_MAX ), exit, err = kRangeErr ); + ptr = next + 1; + + WriteBig16( buf, value ); + + err = DataBuffer_Append( &dataBuf, buf, sizeof( buf ) ); + require_noerr( err, exit ); + } + + // Set the target domain name. + + err = DomainNameFromString( target, ptr, &end ); + require_noerr_quiet( err, exit ); + + err = DataBuffer_Append( &dataBuf, target, (size_t)( end - target ) ); + require_noerr( err, exit ); + + err = DataBuffer_Detach( &dataBuf, outPtr, outLen ); + require_noerr( err, exit ); + +exit: + DataBuffer_Free( &dataBuf ); + return( err ); +} + +static OSStatus StringToTXTRData( const char *inString, char inDelimiter, uint8_t **outPtr, size_t *outLen ) +{ + OSStatus err; + DataBuffer dataBuf; + const char * src; + uint8_t txtStr[ 256 ]; // Buffer for single TXT string: 1 length byte + up to 255 bytes of data. + + DataBuffer_Init( &dataBuf, NULL, 0, kRDataMaxLen ); + + src = inString; + for( ;; ) + { + uint8_t * dst = &txtStr[ 1 ]; + const uint8_t * const lim = &txtStr[ 256 ]; + int c; + + while( *src && ( *src != inDelimiter ) ) + { + if( ( c = *src++ ) == '\\' ) + { + require_action_quiet( *src != '\0', exit, err = kUnderrunErr ); + c = *src++; + } + require_action_quiet( dst < lim, exit, err = kOverrunErr ); + *dst++ = (uint8_t) c; + } + txtStr[ 0 ] = (uint8_t)( dst - &txtStr[ 1 ] ); + err = DataBuffer_Append( &dataBuf, txtStr, 1 + txtStr[ 0 ] ); + require_noerr( err, exit ); + + if( *src == '\0' ) break; + ++src; + } + + err = DataBuffer_Detach( &dataBuf, outPtr, outLen ); + require_noerr( err, exit ); + +exit: + DataBuffer_Free( &dataBuf ); + return( err ); +} + //=========================================================================================================================== // RecordTypeFromArgString //=========================================================================================================================== @@ -6767,67 +7886,55 @@ static OSStatus { OSStatus err; const char * src; - uint8_t * dst; - const uint8_t * const nameLimit = inDomainName + kDomainNameLengthMax; + uint8_t * root; + const uint8_t * const nameLim = inDomainName + kDomainNameLengthMax; - // Find the root label. + for( root = inDomainName; ( root < nameLim ) && *root; root += ( 1 + *root ) ) {} + require_action_quiet( root < nameLim, exit, err = kMalformedErr ); - for( dst = inDomainName; ( dst < nameLimit ) && *dst; dst += ( 1 + *dst ) ) {} - require_action_quiet( dst < nameLimit, exit, err = kMalformedErr ); - - // Append the string's labels one label at a time. + // If the string is a single dot, denoting the root domain, then there are no non-empty labels. src = inString; + if( ( src[ 0 ] == '.' ) && ( src[ 1 ] == '\0' ) ) ++src; while( *src ) { - uint8_t * const label = dst++; - const uint8_t * const labelLimit = Min( dst + kDomainLabelLengthMax, nameLimit - 1 ); - - // If the first character is a label separator, then the label is empty. Empty non-root labels are not allowed. + uint8_t * const label = root; + const uint8_t * const labelLim = Min( &label[ 1 + kDomainLabelLengthMax ], nameLim - 1 ); + uint8_t * dst; + int c; + size_t labelLen; - require_action_quiet( *src != '.', exit, err = kMalformedErr ); - - // Write the label characters until the end of the label, a separator or NUL character, is encountered, or until no - // more space is available. - - while( ( *src != '.' ) && ( *src != '\0' ) && ( dst < labelLimit ) ) + dst = &label[ 1 ]; + while( *src && ( ( c = *src++ ) != '.' ) ) { - uint8_t value; - - value = (uint8_t) *src++; - if( value == '\\' ) + if( c == '\\' ) { - if( *src == '\0' ) break; - value = (uint8_t) *src++; - if( isdigit_safe( value ) && isdigit_safe( src[ 0 ] ) && isdigit_safe( src[ 1 ] ) ) + require_action_quiet( *src != '\0', exit, err = kUnderrunErr ); + c = *src++; + if( isdigit_safe( c ) && isdigit_safe( src[ 0 ] ) && isdigit_safe( src[ 1 ] ) ) { - int decimalValue; + const int decimal = ( ( c - '0' ) * 100 ) + ( ( src[ 0 ] - '0' ) * 10 ) + ( src[ 1 ] - '0' ); - decimalValue = ( ( value - '0' ) * 100 ) + ( ( src[ 0 ] - '0' ) * 10 ) + ( src[ 1 ] - '0' ); - if( decimalValue <= 255 ) + if( decimal <= 255 ) { - value = (uint8_t) decimalValue; + c = decimal; src += 2; } } } - *dst++ = value; - } - if( ( *src == '.' ) || ( *src == '\0' ) ) - { - label[ 0 ] = (uint8_t)( dst - &label[ 1 ] ); // Write the label length. - if( *src == '.' ) ++src; // Advance the pointer past the label separator. - } - else - { - label[ 0 ] = 0; - err = kOverrunErr; - goto exit; + require_action_quiet( dst < labelLim, exit, err = kOverrunErr ); + *dst++ = (uint8_t) c; } + + labelLen = (size_t)( dst - &label[ 1 ] ); + require_action_quiet( labelLen > 0, exit, err = kMalformedErr ); + + label[ 0 ] = (uint8_t) labelLen; + root = dst; + *root = 0; } - *dst++ = 0; // Write the empty root label. - if( outEndPtr ) *outEndPtr = dst; + if( outEndPtr ) *outEndPtr = root + 1; err = kNoErr; exit: @@ -6856,6 +7963,20 @@ static Boolean DomainNameEqual( const uint8_t *inName1, const uint8_t *inName2 ) return( true ); } +//=========================================================================================================================== +// DomainNameFromString +//=========================================================================================================================== + +static OSStatus + DomainNameFromString( + uint8_t inDomainName[ kDomainNameLengthMax ], + const char * inString, + uint8_t ** outEndPtr ) +{ + inDomainName[ 0 ] = 0; + return( DomainNameAppendString( inDomainName, inString, outEndPtr ) ); +} + //=========================================================================================================================== // DomainNameToString //=========================================================================================================================== @@ -7081,8 +8202,7 @@ static OSStatus WriteBig16( hdr->additionalCount, 0 ); ptr = (uint8_t *)( hdr + 1 ); - ptr[ 0 ] = 0; - err = DomainNameAppendString( ptr, inQName, &ptr ); + err = DomainNameFromString( ptr, inQName, &ptr ); require_noerr_quiet( err, exit ); WriteBig16( ptr, inQType ); @@ -7379,56 +8499,6 @@ exit: return( err ); } -//=========================================================================================================================== -// ParseEscapedString -// -// Note: This was copied from CoreUtils because the ParseEscapedString function is currently not exported in the framework. -//=========================================================================================================================== - -OSStatus - ParseEscapedString( - const char * inSrc, - const char * inEnd, - char inDelimiter, - char * inBuf, - size_t inMaxLen, - size_t * outCopiedLen, - size_t * outTotalLen, - const char ** outSrc ) -{ - OSStatus err; - char c; - char * dst; - char * lim; - size_t len; - - dst = inBuf; - lim = dst + ( ( inMaxLen > 0 ) ? ( inMaxLen - 1 ) : 0 ); // Leave room for null terminator. - len = 0; - while( ( inSrc < inEnd ) && ( ( c = *inSrc++ ) != inDelimiter ) ) - { - if( c == '\\' ) - { - require_action_quiet( inSrc < inEnd, exit, err = kUnderrunErr ); - c = *inSrc++; - } - if( dst < lim ) - { - if( inBuf ) *dst = c; - ++dst; - } - ++len; - } - if( inBuf && ( inMaxLen > 0 ) ) *dst = '\0'; - err = kNoErr; - -exit: - if( outCopiedLen ) *outCopiedLen = (size_t)( dst - inBuf ); - if( outTotalLen ) *outTotalLen = len; - if( outSrc ) *outSrc = inSrc; - return( err ); -} - //=========================================================================================================================== // ParseIPv4Address // diff --git a/mDNSResponder/Makefile b/mDNSResponder/Makefile index 9d975334..fa0a4e49 100644 --- a/mDNSResponder/Makefile +++ b/mDNSResponder/Makefile @@ -16,7 +16,7 @@ include $(MAKEFILEPATH)/pb_makefiles/platform.make -MVERS = "mDNSResponder-878.30.4" +MVERS = "mDNSResponder-878.50.17" VER = ifneq ($(strip $(GCC_VERSION)),) diff --git a/mDNSResponder/mDNSCore/DNSCommon.c b/mDNSResponder/mDNSCore/DNSCommon.c index be8e1065..750c10e6 100644 --- a/mDNSResponder/mDNSCore/DNSCommon.c +++ b/mDNSResponder/mDNSCore/DNSCommon.c @@ -3473,9 +3473,8 @@ mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]); // If mDNS record has cache-flush bit set, we mark it unique - // For uDNS records, all are implicitly deemed unique (a single DNS server is always - // authoritative for the entire RRSet), unless this is a truncated response - if (ptr[2] & (kDNSClass_UniqueRRSet >> 8) || (!InterfaceID && !(msg->h.flags.b[0] & kDNSFlag0_TC))) + // For uDNS records, all are implicitly deemed unique (a single DNS server is always authoritative for the entire RRSet) + if (ptr[2] & (kDNSClass_UniqueRRSet >> 8) || !InterfaceID) RecordType |= kDNSRecordTypePacketUniqueMask; ptr += 10; if (ptr + pktrdlength > end) { debugf("GetLargeResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); } diff --git a/mDNSResponder/mDNSCore/mDNS.c b/mDNSResponder/mDNSCore/mDNS.c index 72375d94..0788ab67 100755 --- a/mDNSResponder/mDNSCore/mDNS.c +++ b/mDNSResponder/mDNSCore/mDNS.c @@ -83,7 +83,7 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m); mDNSlocal void RetrySPSRegistrations(mDNS *const m); mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *EthAddr, mDNSOpaque48 *password, mDNSBool unicastOnly); mDNSlocal mDNSBool LocalRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q); -mDNSlocal void mDNS_PurgeForQuestion(mDNS *const m, DNSQuestion *q); +mDNSlocal void mDNS_PurgeBeforeResolve(mDNS *const m, DNSQuestion *q); mDNSlocal void CheckForDNSSECRecords(mDNS *const m, DNSQuestion *q); mDNSlocal void mDNS_SendKeepalives(mDNS *const m); mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSAddr *laddr, mDNSAddr *raddr, mDNSEthAddr *eth, @@ -4321,56 +4321,17 @@ mDNSlocal void CacheRecordDeferredAdd(mDNS *const m, CacheRecord *rr) m->CurrentQuestion = mDNSNULL; } -mDNSlocal mDNSs32 CheckForSoonToExpireRecords(mDNS *const m, const domainname *const name, const mDNSu32 namehash, mDNSBool *purge) +mDNSlocal mDNSs32 CheckForSoonToExpireRecords(mDNS *const m, const domainname *const name, const mDNSu32 namehash) { - const mDNSs32 threshhold = m->timenow + mDNSPlatformOneSecond; // See if there are any records expiring within one second + const mDNSs32 threshold = m->timenow + mDNSPlatformOneSecond; // See if there are any records expiring within one second const mDNSs32 start = m->timenow - 0x10000000; mDNSs32 delay = start; CacheGroup *cg = CacheGroupForName(m, namehash, name); const CacheRecord *rr; - if (purge) - *purge = mDNSfalse; for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) { - // If there are records that will expire soon, there are cases that need delayed - // delivery of events: - // - // 1) A new cache entry is about to be added as a replacement. The caller needs to - // deliver a RMV (for the current old entry) followed by ADD (for the new entry). - // It needs to schedule the timer for the next cache expiry (ScheduleNextCacheCheckTime), - // so that the cache entry can be purged (purging causes the RMV followed by ADD) - // - // 2) A new question is about to be answered and the caller needs to know whether it's - // scheduling should be delayed so that the question is not answered with this record. - // Instead of delivering an ADD (old entry) followed by RMV (old entry) and another ADD - // (new entry), a single ADD can be delivered by delaying the scheduling of the question - // immediately. - // - // When the unicast cache record is created, it's TTL has been extended beyond its value - // given in the resource record (See RRAdjustTTL). If it is in the "extended" time, the - // cache is already expired and we set "purge" to indicate that. When "purge" is set, the - // return value of the function should be ignored by the callers. - // - // Note: For case (1), "purge" argument is NULL and hence the following checks are skipped. - // It is okay to skip in that case because the cache records have been set to expire almost - // immediately and the extended time does not apply. - // - // Also, if there is already an active question we don't try to optimize as purging the cache - // would end up delivering RMV for the active question and hence we avoid that. - - if (purge && !rr->resrec.InterfaceID && !rr->CRActiveQuestion && rr->resrec.rroriginalttl) - { - mDNSu32 uTTL = RRUnadjustedTTL(rr->resrec.rroriginalttl); - if (m->timenow - (rr->TimeRcvd + ((mDNSs32)uTTL * mDNSPlatformOneSecond)) >= 0) - { - LogInfo("CheckForSoonToExpireRecords: %s: rroriginalttl %u, unadjustedTTL %u, currentTTL %u", - CRDisplayString(m, rr), rr->resrec.rroriginalttl, uTTL, (m->timenow - rr->TimeRcvd)/mDNSPlatformOneSecond); - *purge = mDNStrue; - continue; - } - } - if (threshhold - RRExpireTime(rr) >= 0) // If we have records about to expire within a second + if (threshold - RRExpireTime(rr) >= 0) // If we have records about to expire within a second { if (delay - RRExpireTime(rr) < 0) // then delay until after they've been deleted delay = RRExpireTime(rr); @@ -6827,7 +6788,13 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) #endif mDNS_ReclaimLockAfterCallback(); } - +#ifdef _LEGACY_NAT_TRAVERSAL_ + if (m->SSDPSocket) + { + mDNSPlatformUDPClose(m->SSDPSocket); + m->SSDPSocket = mDNSNULL; + } +#endif m->SleepState = SleepState_Transferring; if (m->SystemWakeOnLANEnabled && m->DelaySleep) { @@ -8963,18 +8930,25 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // abort our TCP connection, and not complete the operation, and end up with an incomplete RRSet in our cache. // Next time there's a query for this RRSet we'll see answers in our cache, and assume we have the whole RRSet already, // and not even do the TCP query. - // Accordingly, if we get a uDNS reply with kDNSFlag0_TC set, we bail out and wait for the TCP response containing the entire RRSet. - if (!InterfaceID && (response->h.flags.b[0] & kDNSFlag0_TC)) return; + // Accordingly, if we get a uDNS reply with kDNSFlag0_TC set, we bail out and wait for the TCP response containing the + // entire RRSet, with the following exception. If the response contains an answer section and one or more records in + // either the authority section or additional section, then that implies that truncation occurred beyond the answer + // section, and the answer section is therefore assumed to be complete. + // + // From section 6.2 of RFC 1035 : + // When a response is so long that truncation is required, the truncation + // should start at the end of the response and work forward in the + // datagram. Thus if there is any data for the authority section, the + // answer section is guaranteed to be unique. + if (!InterfaceID && (response->h.flags.b[0] & kDNSFlag0_TC) && + ((response->h.numAnswers == 0) || ((response->h.numAuthorities == 0) && (response->h.numAdditionals == 0)))) return; if (LLQType == uDNS_LLQ_Ignore) return; // 1. We ignore questions (if any) in mDNS response packets // 2. If this is an LLQ response, we handle it much the same - // 3. If we get a uDNS UDP response with the TC (truncated) bit set, then we can't treat this - // answer as being the authoritative complete RRSet, and respond by deleting all other - // matching cache records that don't appear in this packet. // Otherwise, this is a authoritative uDNS answer, so arrange for any stale records to be purged - if (ResponseMCast || LLQType == uDNS_LLQ_Events || (response->h.flags.b[0] & kDNSFlag0_TC)) + if (ResponseMCast || LLQType == uDNS_LLQ_Events) ptr = LocateAnswers(response, end); // Otherwise, for one-shot queries, any answers in our cache that are not also contained // in this response packet are immediately deemed to be invalid. @@ -9420,7 +9394,7 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, if (AddToCFList) delay = NonZeroTime(m->timenow + mDNSPlatformOneSecond); else - delay = CheckForSoonToExpireRecords(m, m->rec.r.resrec.name, m->rec.r.resrec.namehash, mDNSNULL); + delay = CheckForSoonToExpireRecords(m, m->rec.r.resrec.name, m->rec.r.resrec.namehash); // If unique, assume we may have to delay delivery of this 'add' event. // Below, where we walk the CacheFlushRecords list, we either call CacheRecordDeferredAdd() @@ -9488,6 +9462,7 @@ exit: CacheRecord *r1 = CacheFlushRecords, *r2; const mDNSu32 slot = HashSlotFromNameHash(r1->resrec.namehash); const CacheGroup *cg = CacheGroupForRecord(m, &r1->resrec); + mDNSBool purgedRecords = mDNSfalse; CacheFlushRecords = CacheFlushRecords->NextInCFList; r1->NextInCFList = mDNSNULL; @@ -9569,8 +9544,9 @@ exit: r2->resrec.rroriginalttl = r1->resrec.rroriginalttl; } r2->TimeRcvd = m->timenow; + SetNextCacheCheckTimeForRecord(m, r2); } - else // else, if record is old, mark it to be flushed + else if (r2->resrec.InterfaceID) // else, if record is old, mark it to be flushed { verbosedebugf("Cache flush new %p age %d expire in %d %s", r1, m->timenow - r1->TimeRcvd, RRExpireTime(r1) - m->timenow, CRDisplayString(m, r1)); verbosedebugf("Cache flush old %p age %d expire in %d %s", r2, m->timenow - r2->TimeRcvd, RRExpireTime(r2) - m->timenow, CRDisplayString(m, r2)); @@ -9608,8 +9584,14 @@ exit: // We use (m->timenow - 1) instead of m->timenow, because we use that to identify records // that we marked for deletion via an explicit DE record } + SetNextCacheCheckTimeForRecord(m, r2); + } + else + { + // Old uDNS records are scheduled to be purged instead of given at most one second to live. + mDNS_PurgeCacheResourceRecord(m, r2); + purgedRecords = mDNStrue; } - SetNextCacheCheckTimeForRecord(m, r2); } } @@ -9637,7 +9619,16 @@ exit: NSECRecords = mDNSNULL; NSECCachePtr = mDNSNULL; } - r1->DelayDelivery = CheckForSoonToExpireRecords(m, r1->resrec.name, r1->resrec.namehash, mDNSNULL); + if (r1->resrec.InterfaceID) + { + r1->DelayDelivery = CheckForSoonToExpireRecords(m, r1->resrec.name, r1->resrec.namehash); + } + else + { + // If uDNS records from an older RRset were scheduled to be purged, then delay delivery slightly to allow + // them to be deleted before any ADD events for this record. + r1->DelayDelivery = purgedRecords ? NonZeroTime(m->timenow) : 0; + } // If no longer delaying, deliver answer now, else schedule delivery for the appropriate time if (!r1->DelayDelivery) CacheRecordDeferredAdd(m, r1); else ScheduleNextCacheCheckTime(m, slot, r1->DelayDelivery); @@ -11650,7 +11641,7 @@ mDNSlocal mStatus ValidateParameters(mDNS *const m, DNSQuestion *const question) } // InitDNSConfig() is called by InitCommonState() to initialize the DNS configuration of the Question. -// These are a subset of the internal uDNS fields. Must be done before ShouldSuppressQuery() & mDNS_PurgeForQuestion() +// These are a subset of the internal uDNS fields. Must be done before ShouldSuppressQuery() & mDNS_PurgeBeforeResolve() mDNSlocal void InitDNSConfig(mDNS *const m, DNSQuestion *const question) { // First reset all DNS Configuration @@ -11706,9 +11697,8 @@ mDNSlocal void InitDNSConfig(mDNS *const m, DNSQuestion *const question) // InitCommonState() is called by mDNS_StartQuery_internal() to initialize the common(uDNS/mDNS) internal // state fields of the DNS Question. These are independent of the Client layer. -mDNSlocal mDNSBool InitCommonState(mDNS *const m, DNSQuestion *const question) +mDNSlocal void InitCommonState(mDNS *const m, DNSQuestion *const question) { - mDNSBool purge; int i; mDNSBool isBlocked = mDNSfalse; @@ -11727,7 +11717,7 @@ mDNSlocal mDNSBool InitCommonState(mDNS *const m, DNSQuestion *const question) // turned ON which can allocate memory e.g., base64 encoding, in the case of DNSSEC. question->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question question->qnamehash = DomainNameHashValue(&question->qname); - question->DelayAnswering = CheckForSoonToExpireRecords(m, &question->qname, question->qnamehash, &purge); + question->DelayAnswering = mDNSOpaque16IsZero(question->TargetQID) ? CheckForSoonToExpireRecords(m, &question->qname, question->qnamehash) : 0; question->LastQTime = m->timenow; question->ExpectUnicastResp = 0; question->LastAnswerPktNum = m->PktNum; @@ -11809,7 +11799,6 @@ mDNSlocal mDNSBool InitCommonState(mDNS *const m, DNSQuestion *const question) if (question->WakeOnResolve) { question->WakeOnResolveCount = InitialWakeOnResolveCount; - purge = mDNStrue; } for (i=0; iDelayAnswering) LogInfo("InitCommonState: Delaying answering for %d ticks while cache stabilizes for %##s (%s)", question->DelayAnswering - m->timenow, question->qname.c, DNSTypeName(question->qtype)); - - return(purge); } // Excludes the DNS Config fields which are already handled by InitDNSConfig() @@ -11909,7 +11896,7 @@ mDNSlocal void InitDNSSECProxyState(mDNS *const m, DNSQuestion *const question) // Once the question is completely initialized including the duplicate logic, this function // is called to finalize the unicast question which requires flushing the cache if needed, // activating the query etc. -mDNSlocal void FinalizeUnicastQuestion(mDNS *const m, DNSQuestion *question, mDNSBool purge) +mDNSlocal void FinalizeUnicastQuestion(mDNS *const m, DNSQuestion *question) { // Ensure DNS related info of duplicate question is same as the orig question if (question->DuplicateOf) @@ -11934,14 +11921,7 @@ mDNSlocal void FinalizeUnicastQuestion(mDNS *const m, DNSQuestion *question, mDN ActivateUnicastQuery(m, question, mDNSfalse); - // If purge was set above, flush the cache. Need to do this after we set the - // DNS server on the question - if (purge) - { - question->DelayAnswering = 0; - mDNS_PurgeForQuestion(m, question); - } - else if (!question->DuplicateOf && DNSSECQuestion(question)) + if (!question->DuplicateOf && DNSSECQuestion(question)) { // For DNSSEC questions, we need to have the RRSIGs also for verification. CheckForDNSSECRecords(m, question); @@ -11964,7 +11944,6 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu { DNSQuestion **q; mStatus vStatus; - mDNSBool purge; // First check for cache space (can't do queries if there is no cache space allocated) if (m->rrcache_size == 0) @@ -12012,7 +11991,7 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu // InitCommonState -> InitDNSConfig) as DNS server selection affects DNSSEC // validation. - purge = InitCommonState(m, question); + InitCommonState(m, question); InitWABState(question); InitLLQState(question); #ifdef DNS_PUSH_ENABLED @@ -12045,7 +12024,7 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu // this routine with the question list data structures in an inconsistent state. if (!mDNSOpaque16IsZero(question->TargetQID)) { - FinalizeUnicastQuestion(m, question, purge); + FinalizeUnicastQuestion(m, question); } else { @@ -12065,10 +12044,10 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu } } #endif // BONJOUR_ON_DEMAND - if (purge) + if (question->WakeOnResolve) { LogInfo("mDNS_StartQuery_internal: Purging for %##s", question->qname.c); - mDNS_PurgeForQuestion(m, question); + mDNS_PurgeBeforeResolve(m, question); } } } @@ -14618,7 +14597,7 @@ mDNSlocal void PurgeOrReconfirmCacheRecord(mDNS *const m, CacheRecord *cr, const } } -mDNSlocal void mDNS_PurgeForQuestion(mDNS *const m, DNSQuestion *q) +mDNSlocal void mDNS_PurgeBeforeResolve(mDNS *const m, DNSQuestion *q) { CacheGroup *const cg = CacheGroupForName(m, q->qnamehash, &q->qname); CacheRecord *rp; @@ -14634,7 +14613,7 @@ mDNSlocal void mDNS_PurgeForQuestion(mDNS *const m, DNSQuestion *q) { if (SameNameRecordAnswersQuestion(&rp->resrec, q)) { - LogInfo("mDNS_PurgeForQuestion: Flushing %s", CRDisplayString(m, rp)); + LogInfo("mDNS_PurgeBeforeResolve: Flushing %s", CRDisplayString(m, rp)); mDNS_PurgeCacheResourceRecord(m, rp); } } diff --git a/mDNSResponder/mDNSCore/uDNS.c b/mDNSResponder/mDNSCore/uDNS.c index 4d011427..64dae891 100755 --- a/mDNSResponder/mDNSCore/uDNS.c +++ b/mDNSResponder/mDNSCore/uDNS.c @@ -550,7 +550,7 @@ mDNSlocal mStatus uDNS_RequestAddress(mDNS *m) return err; } -mDNSlocal mStatus uDNS_SendNATMsg(mDNS *m, NATTraversalInfo *info, mDNSBool usePCP) +mDNSlocal mStatus uDNS_SendNATMsg(mDNS *m, NATTraversalInfo *info, mDNSBool usePCP, mDNSBool unmapping) { mStatus err = mStatus_NoError; @@ -649,19 +649,25 @@ mDNSlocal mStatus uDNS_SendNATMsg(mDNS *m, NATTraversalInfo *info, mDNSBool useP info->sentNATPMP = mDNSfalse; #ifdef _LEGACY_NAT_TRAVERSAL_ - if (mDNSIPPortIsZero(m->UPnPRouterPort) || mDNSIPPortIsZero(m->UPnPSOAPPort)) + // If an unmapping is being performed, then don't send an LNT discovery message or an LNT port map request. + if (!unmapping) { - LNT_SendDiscoveryMsg(m); - debugf("uDNS_SendNATMsg: LNT_SendDiscoveryMsg"); - } - else - { - mStatus lnterr = LNT_MapPort(m, info); - if (lnterr) - LogMsg("uDNS_SendNATMsg: LNT_MapPort returned error %d", lnterr); + if (mDNSIPPortIsZero(m->UPnPRouterPort) || mDNSIPPortIsZero(m->UPnPSOAPPort)) + { + LNT_SendDiscoveryMsg(m); + debugf("uDNS_SendNATMsg: LNT_SendDiscoveryMsg"); + } + else + { + mStatus lnterr = LNT_MapPort(m, info); + if (lnterr) + LogMsg("uDNS_SendNATMsg: LNT_MapPort returned error %d", lnterr); - err = err ? err : lnterr; // PCP error takes precedence + err = err ? err : lnterr; // PCP error takes precedence + } } +#else + (void)unmapping; // Unused #endif // _LEGACY_NAT_TRAVERSAL_ } } @@ -925,6 +931,16 @@ mDNSexport mStatus mDNS_StopNATOperation_internal(mDNS *m, NATTraversalInfo *tra } } + // Even if we DIDN'T make a successful UPnP mapping yet, we might still have a partially-open TCP connection we need to clean up + // Before zeroing traversal->RequestedPort below, perform the LNT unmapping, which requires the mapping's external port, + // held by the traversal->RequestedPort variable. + #ifdef _LEGACY_NAT_TRAVERSAL_ + { + mStatus err = LNT_UnmapPort(m, traversal); + if (err) LogMsg("Legacy NAT Traversal - unmap request failed with error %d", err); + } + #endif // _LEGACY_NAT_TRAVERSAL_ + if (traversal->ExpiryTime && unmap) { traversal->NATLease = 0; @@ -946,17 +962,9 @@ mDNSexport mStatus mDNS_StopNATOperation_internal(mDNS *m, NATTraversalInfo *tra traversal->RequestedPort = zeroIPPort; traversal->NewAddress = zerov4Addr; - uDNS_SendNATMsg(m, traversal, traversal->lastSuccessfulProtocol != NATTProtocolNATPMP); + uDNS_SendNATMsg(m, traversal, traversal->lastSuccessfulProtocol != NATTProtocolNATPMP, mDNStrue); } - // Even if we DIDN'T make a successful UPnP mapping yet, we might still have a partially-open TCP connection we need to clean up - #ifdef _LEGACY_NAT_TRAVERSAL_ - { - mStatus err = LNT_UnmapPort(m, traversal); - if (err) LogMsg("Legacy NAT Traversal - unmap request failed with error %d", err); - } - #endif // _LEGACY_NAT_TRAVERSAL_ - return(mStatus_NoError); } @@ -3660,7 +3668,7 @@ mDNSlocal void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID Interface { // Send a NAT-PMP request for this operation as needed // and update the state variables - uDNS_SendNATMsg(m, n, mDNSfalse); + uDNS_SendNATMsg(m, n, mDNSfalse, mDNSfalse); } m->NextScheduledNATOp = m->timenow; @@ -5003,7 +5011,7 @@ mDNSexport void CheckNATMappings(mDNS *m) cur->retryInterval = NATMAP_INIT_RETRY; } - uDNS_SendNATMsg(m, cur, mDNStrue); // Will also do UPnP discovery for us, if necessary + uDNS_SendNATMsg(m, cur, mDNStrue, mDNSfalse); // Will also do UPnP discovery for us, if necessary if (cur->ExpiryTime) // If have active mapping then set next renewal time halfway to expiry NATSetNextRenewalTime(m, cur); diff --git a/mDNSResponder/mDNSMacOSX/LegacyNATTraversal.c b/mDNSResponder/mDNSMacOSX/LegacyNATTraversal.c index 2c90d929..7de031c4 100644 --- a/mDNSResponder/mDNSMacOSX/LegacyNATTraversal.c +++ b/mDNSResponder/mDNSMacOSX/LegacyNATTraversal.c @@ -888,9 +888,10 @@ mDNSexport void LNT_SendDiscoveryMsg(mDNS *m) "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 + mDNSu8 *const buf = (mDNSu8*)&m->omsg; //m->omsg is 8952 bytes, which is plenty unsigned int bufLen; + if (m->SleepState != SleepState_Awake) return; if (!mDNSIPPortIsZero(m->UPnPRouterPort)) { if (m->SSDPSocket) { debugf("LNT_SendDiscoveryMsg destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; } diff --git a/mDNSResponder/mDNSShared/CommonServices.h b/mDNSResponder/mDNSShared/CommonServices.h index 7fceac02..fecfb0f3 100644 --- a/mDNSResponder/mDNSShared/CommonServices.h +++ b/mDNSResponder/mDNSShared/CommonServices.h @@ -879,7 +879,11 @@ typedef unsigned long int uintptr_t; #define false 0 #endif #else - #define COMMON_SERVICES_NEEDS_BOOL ( !defined( __cplusplus ) && !__bool_true_false_are_defined ) + #if ( !defined( __cplusplus ) && !__bool_true_false_are_defined ) + #define COMMON_SERVICES_NEEDS_BOOL 1 + #else + #define COMMON_SERVICES_NEEDS_BOOL 0 + #endif #endif #if ( COMMON_SERVICES_NEEDS_BOOL ) diff --git a/mDNSResponder/mDNSShared/dns_sd.h b/mDNSResponder/mDNSShared/dns_sd.h index 0b47c394..94237fb3 100644 --- a/mDNSResponder/mDNSShared/dns_sd.h +++ b/mDNSResponder/mDNSShared/dns_sd.h @@ -66,7 +66,7 @@ */ #ifndef _DNS_SD_H -#define _DNS_SD_H 8783004 +#define _DNS_SD_H 8785017 #ifdef __cplusplus extern "C" { diff --git a/mDNSResponder/mDNSShared/dnsextd.conf b/mDNSResponder/mDNSShared/dnsextd.conf index 0379580d..d8cac244 100644 --- a/mDNSResponder/mDNSShared/dnsextd.conf +++ b/mDNSResponder/mDNSShared/dnsextd.conf @@ -23,7 +23,7 @@ // network, you might allow anyone to perform updates. To do that, you just // permit any and all updates coming from dnsextd on the same machine: // -// zone "my-dynamic-subdomain.company.com." +// zone "my-dynamic-subdomain.example.com." // { type master; file "db.xxx"; allow-update { 127.0.0.1; }; }; // // On a machine connected to the Internet or other large open network, @@ -32,11 +32,11 @@ // perform updates in your dynamic zone, like this: // // key keyname. { algorithm hmac-md5; secret "abcdefghijklmnopqrstuv=="; }; -// zone "my-dynamic-subdomain.company.com." in +// zone "my-dynamic-subdomain.example.com." in // { // type master; -// file "db.my-dynamic-subdomain.company.com"; -// update-policy { grant * wildcard *.my-dynamic-subdomain.company.com.; }; +// file "db.my-dynamic-subdomain.example.com"; +// update-policy { grant * wildcard *.my-dynamic-subdomain.example.com.; }; // }; // // You could use a single key which you give to all authorized users, but @@ -55,6 +55,6 @@ options { // llq port 5352; }; -zone "my-dynamic-subdomain.company.com." { +zone "my-dynamic-subdomain.example.com." { type public; }; -- cgit v1.2.3