[PATCH 02/10] mDNSResponder: Update to v878.50.17
Sebastian Huber
sebastian.huber at embedded-brains.de
Fri Jun 19 11:02:16 UTC 2020
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 <CoreUtils/CommandLineUtils.h>
#include <CoreUtils/DataBufferUtils.h>
#include <CoreUtils/DebugServices.h>
+#include <CoreUtils/HTTPUtils.h>
#include <CoreUtils/MiscUtils.h>
#include <CoreUtils/NetUtils.h>
#include <CoreUtils/PrintFUtils.h>
#include <CoreUtils/RandomNumberUtils.h>
+#include <CoreUtils/SoftLinking.h>
#include <CoreUtils/StringUtils.h>
#include <CoreUtils/TickUtils.h>
#include <dns_sd.h>
@@ -21,6 +23,7 @@
#if( TARGET_OS_DARWIN )
#include <dnsinfo.h>
#include <libproc.h>
+ #include <netdb.h>
#include <sys/proc_info.h>
#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> string:'\\x09color=red'\n" \
- "Hexadecimal string hex:<hex string> hex:c0a80101 or hex:'C0 A8 01 01'\n" \
- "TXT record keys and values txt:<comma-delimited keys and values> txt:'key1=x,key2=y\\,z,key3'\n" \
- "File containing raw record data file:<file path> file:dir/record_data.bin\n"
+ "Format Syntax Example\n" \
+ "Domain name domain:<domain name> domain:demo._test._tcp.local\n" \
+ "File containing record data file:<file path> file:/path/to/rdata.bin\n" \
+ "Hexadecimal string hex:<hex string> hex:c0000201 or hex:'C0 00 02 01'\n" \
+ "IPv4 address ipv4:<IPv4 address> ipv4:192.0.2.1\n" \
+ "IPv6 address ipv6:<IPv6 address> ipv6:2001:db8::1\n" \
+ "SRV record srv:<priority>,<weight>,<port>,<target> srv:0,0,64206,example.local\n" \
+ "String (w/escaped hex bytes) string:<string> string:'\\x09color=red'\n" \
+ "TXT record keys and values txt:<comma-delimited keys and values> 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 : "<NULL>" );
+ 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 <https://tools.ietf.org/html/rfc1035>:
+ // 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; i<DupSuppressInfoSize; i++)
@@ -11826,8 +11815,6 @@ mDNSlocal mDNSBool InitCommonState(mDNS *const m, DNSQuestion *const question)
if (question->DelayAnswering)
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;
};
--
2.26.2
More information about the devel
mailing list