2 Copyright (c) 2016-2019 Apple Inc. All rights reserved.
4 dnssdutil is a command-line utility for testing the DNS-SD API.
7 #include "DNSMessage.h"
9 #include <CoreUtils/CoreUtils.h>
11 #include <dns_sd_private.h>
13 #include CF_RUNTIME_HEADER
15 #if( TARGET_OS_DARWIN )
16 #include <CFNetwork/CFHost.h>
17 #include <CoreFoundation/CoreFoundation.h>
18 #include <SystemConfiguration/SCPrivate.h>
24 #include <sys/proc_info.h>
28 #if( TARGET_OS_POSIX )
29 #include <sys/resource.h>
32 #if( !defined( DNSSDUTIL_INCLUDE_DNSCRYPT ) )
33 #define DNSSDUTIL_INCLUDE_DNSCRYPT 0
36 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
37 #include "tweetnacl.h" // TweetNaCl from <https://tweetnacl.cr.yp.to/software.html>.
40 #if( !defined( MDNSRESPONDER_PROJECT ) )
41 #define MDNSRESPONDER_PROJECT 0
44 #if( MDNSRESPONDER_PROJECT )
45 #include <dns_services.h>
46 #include "mdns_private.h"
47 #include "TestUtils.h"
50 //===========================================================================================================================
52 //===========================================================================================================================
54 #define kDNSSDUtilNumVersion NumVersionBuild( 2, 0, 0, kVersionStageBeta, 0 )
56 #if( !MDNSRESPONDER_PROJECT && !defined( DNSSDUTIL_SOURCE_VERSION ) )
57 #define DNSSDUTIL_SOURCE_VERSION "0.0.0"
60 #define kDNSSDUtilIdentifier "com.apple.dnssdutil"
62 //===========================================================================================================================
64 //===========================================================================================================================
66 // DNS-SD API flag descriptors
68 #define kDNSServiceFlagsDescriptors \
69 "\x00" "AutoTrigger\0" \
72 "\x03" "NoAutoRename\0" \
75 "\x06" "BrowseDomains\0" \
76 "\x07" "RegistrationDomains\0" \
77 "\x08" "LongLivedQuery\0" \
78 "\x09" "AllowRemoteQuery\0" \
79 "\x0A" "ForceMulticast\0" \
80 "\x0B" "KnownUnique\0" \
81 "\x0C" "ReturnIntermediates\0" \
82 "\x0D" "DenyConstrained\0" \
83 "\x0E" "ShareConnection\0" \
84 "\x0F" "SuppressUnusable\0" \
86 "\x11" "IncludeP2P\0" \
87 "\x12" "WakeOnResolve\0" \
88 "\x13" "BackgroundTrafficClass\0" \
89 "\x14" "IncludeAWDL\0" \
91 "\x16" "UnicastResponse\0" \
92 "\x17" "ValidateOptional\0" \
93 "\x18" "WakeOnlyService\0" \
94 "\x19" "ThresholdOne\0" \
95 "\x1A" "ThresholdFinder\0" \
96 "\x1B" "DenyCellular\0" \
97 "\x1C" "ServiceIndex\0" \
98 "\x1D" "DenyExpensive\0" \
99 "\x1E" "PathEvaluationDone\0" \
100 "\x1F" "AllowExpiredAnswers\0" \
103 #define DNSServiceFlagsToAddRmvStr( FLAGS ) ( ( (FLAGS) & kDNSServiceFlagsAdd ) ? "Add" : "Rmv" )
105 #define kDNSServiceProtocolDescriptors \
112 #define kBadDNSServiceRef ( (DNSServiceRef)(intptr_t) -1 )
114 //===========================================================================================================================
116 //===========================================================================================================================
119 #define kDNSMaxUDPMessageSize 512
120 #define kDNSMaxTCPMessageSize UINT16_MAX
122 #define kDNSRecordDataLengthMax UINT16_MAX
124 //===========================================================================================================================
126 //===========================================================================================================================
128 #define kMDNSPort 5353
130 #define kDefaultMDNSMessageID 0
131 #define kDefaultMDNSQueryFlags 0
133 #define kQClassUnicastResponseBit ( 1U << 15 )
134 #define kRRClassCacheFlushBit ( 1U << 15 )
136 // Recommended Resource Record TTL values. See <https://tools.ietf.org/html/rfc6762#section-10>.
138 #define kMDNSRecordTTL_Host 120 // TTL for resource records related to a host name, e.g., A, AAAA, SRV, etc.
139 #define kMDNSRecordTTL_Other 4500 // TTL for other resource records.
141 // Maximum mDNS Message Size. See <https://tools.ietf.org/html/rfc6762#section-17>.
143 #define kMDNSMessageSizeMax 8952 // 9000 B (Ethernet jumbo frame max size) - 40 B (IPv6 header) - 8 B (UDP header)
145 #define kLocalStr "\x05" "local"
146 #define kLocalLabel ( (const uint8_t *) kLocalStr )
147 #define kLocalName ( (const uint8_t *) kLocalStr )
148 #define kLocalNameLen sizeof( kLocalStr )
150 //===========================================================================================================================
151 // Test Address Blocks
152 //===========================================================================================================================
154 // IPv4 address block 203.0.113.0/24 (TEST-NET-3) is reserved for documentation. See <https://tools.ietf.org/html/rfc5737>.
156 #define kDNSServerBaseAddrV4 UINT32_C( 0xCB007100 ) // 203.0.113.0/24
158 // IPv6 address block 2001:db8::/32 is reserved for documentation. See <https://tools.ietf.org/html/rfc3849>.
160 static const uint8_t kDNSServerBaseAddrV6
[] =
162 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 2001:db8:1::/120
165 static const uint8_t kMDNSReplierBaseAddrV6
[] =
167 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 2001:db8:2::/96
170 check_compile_time( sizeof( kDNSServerBaseAddrV6
) == 16 );
171 check_compile_time( sizeof( kMDNSReplierBaseAddrV6
) == 16 );
173 // Bad IPv4 and IPv6 Address Blocks
174 // Used by the DNS server when it needs to respond with intentionally "bad" A/AAAA record data, i.e., IP addresses neither
175 // in 203.0.113.0/24 nor 2001:db8:1::/120.
177 #define kDNSServerBadBaseAddrV4 UINT32_C( 0x00000000 ) // 0.0.0.0/24
179 static const uint8_t kDNSServerBadBaseAddrV6
[] =
181 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 // ::ffff:0:0/120
184 check_compile_time( sizeof( kDNSServerBadBaseAddrV6
) == 16 );
186 //===========================================================================================================================
188 //===========================================================================================================================
190 #if( TARGET_OS_DARWIN )
191 SOFT_LINK_LIBRARY_EX( "/usr/lib/system", system_dnssd
);
192 SOFT_LINK_FUNCTION_EX( system_dnssd
, DNSServiceSleepKeepalive_sockaddr
,
193 DNSServiceErrorType
, (
194 DNSServiceRef
* sdRef
,
195 DNSServiceFlags flags
,
196 const struct sockaddr
* localAddr
,
197 const struct sockaddr
* remoteAddr
,
198 unsigned int timeout
,
199 DNSServiceSleepKeepaliveReply callBack
,
201 ( sdRef
, flags
, localAddr
, remoteAddr
, timeout
, callBack
, context
) );
204 //===========================================================================================================================
206 //===========================================================================================================================
208 #define kLowerAlphaNumericCharSet "abcdefghijklmnopqrstuvwxyz0123456789"
209 #define kLowerAlphaNumericCharSetSize sizeof_string( kLowerAlphaNumericCharSet )
211 #if( !defined( kWhiteSpaceCharSet ) )
212 #define kWhiteSpaceCharSet "\t\n\v\f\r "
215 // Note: strcpy_literal() appears in CoreUtils code, but isn't currently defined in framework headers.
217 #if( !defined( strcpy_literal ) )
218 #define strcpy_literal( DST, SRC ) memcpy( DST, SRC, sizeof( SRC ) )
221 #define _RandomStringExact( CHAR_SET, CHAR_SET_SIZE, CHAR_COUNT, OUT_STRING ) \
222 RandomString( CHAR_SET, CHAR_SET_SIZE, CHAR_COUNT, CHAR_COUNT, OUT_STRING )
224 #define kNoSuchRecordStr "No Such Record"
225 #define kNoSuchRecordAStr "No Such Record (A)"
226 #define kNoSuchRecordAAAAStr "No Such Record (AAAA)"
228 #define kRootLabel ( (const uint8_t *) "" )
230 //===========================================================================================================================
231 // Gerneral Command Options
232 //===========================================================================================================================
234 // Command option macros
236 #define Command( NAME, CALLBACK, SUB_OPTIONS, SHORT_HELP, IS_NOTCOMMON ) \
237 CLI_COMMAND_EX( NAME, CALLBACK, SUB_OPTIONS, (IS_NOTCOMMON) ? kCLIOptionFlags_NotCommon : kCLIOptionFlags_None, \
240 #define kRequiredOptionSuffix " [REQUIRED]"
242 #define MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
243 CLI_OPTION_MULTI_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, \
244 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
245 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
247 #define MultiStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
248 MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
250 #define IntegerOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
251 CLI_OPTION_INTEGER_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
252 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
253 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
255 #define DoubleOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
256 CLI_OPTION_DOUBLE_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
257 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
258 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
260 #define BooleanOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP ) \
261 CLI_OPTION_BOOLEAN( (SHORT_CHAR), (LONG_NAME), (VAL_PTR), (SHORT_HELP), NULL )
263 #define StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
264 CLI_OPTION_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
265 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
266 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
268 #define StringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
269 StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
271 #define CFStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
272 CLI_OPTION_CFSTRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
273 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
274 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
276 // DNS-SD API flag options
278 static int gDNSSDFlags
= 0;
279 static int gDNSSDFlag_AllowExpiredAnswers
= false;
280 static int gDNSSDFlag_BrowseDomains
= false;
281 static int gDNSSDFlag_DenyCellular
= false;
282 static int gDNSSDFlag_DenyConstrained
= false;
283 static int gDNSSDFlag_DenyExpensive
= false;
284 static int gDNSSDFlag_ForceMulticast
= false;
285 static int gDNSSDFlag_IncludeAWDL
= false;
286 static int gDNSSDFlag_KnownUnique
= false;
287 static int gDNSSDFlag_NoAutoRename
= false;
288 static int gDNSSDFlag_PathEvaluationDone
= false;
289 static int gDNSSDFlag_RegistrationDomains
= false;
290 static int gDNSSDFlag_ReturnIntermediates
= false;
291 static int gDNSSDFlag_Shared
= false;
292 static int gDNSSDFlag_SuppressUnusable
= false;
293 static int gDNSSDFlag_Timeout
= false;
294 static int gDNSSDFlag_UnicastResponse
= false;
295 static int gDNSSDFlag_Unique
= false;
296 static int gDNSSDFlag_WakeOnResolve
= false;
298 #define DNSSDFlagsOption() \
299 IntegerOption( 'f', "flags", &gDNSSDFlags, "flags", \
300 "DNSServiceFlags as an integer. This value is bitwise ORed with other single flag options.", false )
302 #define DNSSDFlagOption( SHORT_CHAR, FLAG_NAME ) \
303 BooleanOption( SHORT_CHAR, # FLAG_NAME, &gDNSSDFlag_ ## FLAG_NAME, "Use kDNSServiceFlags" # FLAG_NAME "." )
305 #define DNSSDFlagsOption_AllowExpiredAnswers() DNSSDFlagOption( 'X', AllowExpiredAnswers )
306 #define DNSSDFlagsOption_DenyCellular() DNSSDFlagOption( 'C', DenyCellular )
307 #define DNSSDFlagsOption_DenyConstrained() DNSSDFlagOption( 'R', DenyConstrained)
308 #define DNSSDFlagsOption_DenyExpensive() DNSSDFlagOption( 'E', DenyExpensive )
309 #define DNSSDFlagsOption_ForceMulticast() DNSSDFlagOption( 'M', ForceMulticast )
310 #define DNSSDFlagsOption_IncludeAWDL() DNSSDFlagOption( 'A', IncludeAWDL )
311 #define DNSSDFlagsOption_KnownUnique() DNSSDFlagOption( 'K', KnownUnique )
312 #define DNSSDFlagsOption_NoAutoRename() DNSSDFlagOption( 'N', NoAutoRename )
313 #define DNSSDFlagsOption_PathEvalDone() DNSSDFlagOption( 'P', PathEvaluationDone )
314 #define DNSSDFlagsOption_ReturnIntermediates() DNSSDFlagOption( 'I', ReturnIntermediates )
315 #define DNSSDFlagsOption_Shared() DNSSDFlagOption( 'S', Shared )
316 #define DNSSDFlagsOption_SuppressUnusable() DNSSDFlagOption( 'S', SuppressUnusable )
317 #define DNSSDFlagsOption_Timeout() DNSSDFlagOption( 'T', Timeout )
318 #define DNSSDFlagsOption_UnicastResponse() DNSSDFlagOption( 'U', UnicastResponse )
319 #define DNSSDFlagsOption_Unique() DNSSDFlagOption( 'U', Unique )
320 #define DNSSDFlagsOption_WakeOnResolve() DNSSDFlagOption( 'W', WakeOnResolve )
324 static const char * gInterface
= NULL
;
326 #define InterfaceOption() \
327 StringOption( 'i', "interface", &gInterface, "interface", \
328 "Network interface by name or index. Use index -1 for local-only.", false )
330 // Connection options
332 #define kConnectionArg_Normal ""
333 #define kConnectionArgPrefix_PID "pid:"
334 #define kConnectionArgPrefix_UUID "uuid:"
336 static const char * gConnectionOpt
= kConnectionArg_Normal
;
338 #define ConnectionOptions() \
339 { kCLIOptionType_String, 0, "connection", &gConnectionOpt, NULL, (intptr_t) kConnectionArg_Normal, "type", \
340 kCLIOptionFlags_OptionalArgument, NULL, NULL, NULL, NULL, \
341 "Specifies the type of main connection to use. See " kConnectionSection_Name " below.", NULL }
343 #define kConnectionSection_Name "Connection Option"
344 #define kConnectionSection_Text \
345 "The default behavior is to create a main connection with DNSServiceCreateConnection() and perform operations on\n" \
346 "the main connection using the kDNSServiceFlagsShareConnection flag. This behavior can be explicitly invoked by\n" \
347 "specifying the connection option without an argument, i.e.,\n" \
351 "To instead use a delegate connection created with DNSServiceCreateDelegateConnection(), use\n" \
353 " --connection=pid:<PID>\n" \
355 "to specify the delegator by PID, or use\n" \
357 " --connection=uuid:<UUID>\n" \
359 "to specify the delegator by UUID.\n" \
361 "To not use a main connection at all, but instead perform operations on their own implicit connections, use\n" \
365 #define ConnectionSection() CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
367 // Help text for record data options
369 #define kRDataArgPrefix_Domain "domain:"
370 #define kRDataArgPrefix_File "file:"
371 #define kRDataArgPrefix_HexString "hex:"
372 #define kRDataArgPrefix_IPv4 "ipv4:"
373 #define kRDataArgPrefix_IPv6 "ipv6:"
374 #define kRDataArgPrefix_SRV "srv:"
375 #define kRDataArgPrefix_String "string:"
376 #define kRDataArgPrefix_TXT "txt:"
378 #define kRecordDataSection_Name "Record Data Arguments"
379 #define kRecordDataSection_Text \
380 "A record data argument is specified in one of the following formats:\n" \
382 "Format Syntax Example\n" \
383 "Domain name domain:<domain name> domain:demo._test._tcp.local\n" \
384 "File containing record data file:<file path> file:/path/to/binary-rdata-file\n" \
385 "Hexadecimal string hex:<hex string> hex:c0000201 or hex:'C0 00 02 01'\n" \
386 "IPv4 address ipv4:<IPv4 address> ipv4:192.0.2.1\n" \
387 "IPv6 address ipv6:<IPv6 address> ipv6:2001:db8::1\n" \
388 "SRV record srv:<priority>,<weight>,<port>,<target> srv:0,0,64206,example.local\n" \
389 "String string:<string> string:'\\x09color=red'\n" \
390 "TXT record strings txt:<comma-delimited strings> txt:'vers=1.0,lang=en\\,es\\,fr,passreq'\n" \
392 "Note: The string format converts each \\xHH escape sequence into the octet represented by the HH hex digit pair.\n"
394 #define RecordDataSection() CLI_SECTION( kRecordDataSection_Name, kRecordDataSection_Text )
396 //===========================================================================================================================
398 //===========================================================================================================================
400 #define kOutputFormatStr_JSON "json"
401 #define kOutputFormatStr_XML "xml"
402 #define kOutputFormatStr_Binary "binary"
406 kOutputFormatType_Invalid
= 0,
407 kOutputFormatType_JSON
= 1,
408 kOutputFormatType_XML
= 2,
409 kOutputFormatType_Binary
= 3
413 #define FormatOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP, IS_REQUIRED ) \
414 StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, "format", SHORT_HELP, IS_REQUIRED, \
416 "Use '" kOutputFormatStr_JSON "' for JavaScript Object Notation (JSON).\n" \
417 "Use '" kOutputFormatStr_XML "' for property list XML version 1.0.\n" \
418 "Use '" kOutputFormatStr_Binary "' for property list binary version 1.0.\n" \
422 //===========================================================================================================================
423 // Browse Command Options
424 //===========================================================================================================================
426 static char ** gBrowse_ServiceTypes
= NULL
;
427 static size_t gBrowse_ServiceTypesCount
= 0;
428 static const char * gBrowse_Domain
= NULL
;
429 static int gBrowse_DoResolve
= false;
430 static int gBrowse_QueryTXT
= false;
431 static int gBrowse_TimeLimitSecs
= 0;
433 static CLIOption kBrowseOpts
[] =
436 MultiStringOption( 't', "type", &gBrowse_ServiceTypes
, &gBrowse_ServiceTypesCount
, "service type", "Service type(s), e.g., \"_ssh._tcp\".", true ),
437 StringOption( 'd', "domain", &gBrowse_Domain
, "domain", "Domain in which to browse for the service type(s).", false ),
439 CLI_OPTION_GROUP( "Flags" ),
441 DNSSDFlagsOption_IncludeAWDL(),
443 CLI_OPTION_GROUP( "Operation" ),
445 BooleanOption( 0 , "resolve", &gBrowse_DoResolve
, "Resolve service instances." ),
446 BooleanOption( 0 , "queryTXT", &gBrowse_QueryTXT
, "Query TXT records of service instances." ),
447 IntegerOption( 'l', "timeLimit", &gBrowse_TimeLimitSecs
, "seconds", "Specifies the max duration of the browse operation. Use '0' for no time limit.", false ),
453 //===========================================================================================================================
454 // GetAddrInfo Command Options
455 //===========================================================================================================================
457 static const char * gGetAddrInfo_Name
= NULL
;
458 static int gGetAddrInfo_ProtocolIPv4
= false;
459 static int gGetAddrInfo_ProtocolIPv6
= false;
460 static int gGetAddrInfo_OneShot
= false;
461 static int gGetAddrInfo_TimeLimitSecs
= 0;
463 static CLIOption kGetAddrInfoOpts
[] =
466 StringOption( 'n', "name", &gGetAddrInfo_Name
, "domain name", "Domain name to resolve.", true ),
467 BooleanOption( 0 , "ipv4", &gGetAddrInfo_ProtocolIPv4
, "Use kDNSServiceProtocol_IPv4." ),
468 BooleanOption( 0 , "ipv6", &gGetAddrInfo_ProtocolIPv6
, "Use kDNSServiceProtocol_IPv6." ),
470 CLI_OPTION_GROUP( "Flags" ),
472 DNSSDFlagsOption_AllowExpiredAnswers(),
473 DNSSDFlagsOption_DenyCellular(),
474 DNSSDFlagsOption_DenyConstrained(),
475 DNSSDFlagsOption_DenyExpensive(),
476 DNSSDFlagsOption_PathEvalDone(),
477 DNSSDFlagsOption_ReturnIntermediates(),
478 DNSSDFlagsOption_SuppressUnusable(),
479 DNSSDFlagsOption_Timeout(),
481 CLI_OPTION_GROUP( "Operation" ),
483 BooleanOption( 'o', "oneshot", &gGetAddrInfo_OneShot
, "Finish after first set of results." ),
484 IntegerOption( 'l', "timeLimit", &gGetAddrInfo_TimeLimitSecs
, "seconds", "Maximum duration of the GetAddrInfo operation. Use '0' for no time limit.", false ),
490 //===========================================================================================================================
491 // QueryRecord Command Options
492 //===========================================================================================================================
494 static const char * gQueryRecord_Name
= NULL
;
495 static const char * gQueryRecord_Type
= NULL
;
496 static int gQueryRecord_OneShot
= false;
497 static int gQueryRecord_TimeLimitSecs
= 0;
498 static int gQueryRecord_RawRData
= false;
500 static CLIOption kQueryRecordOpts
[] =
503 StringOption( 'n', "name", &gQueryRecord_Name
, "domain name", "Full domain name of record to query.", true ),
504 StringOption( 't', "type", &gQueryRecord_Type
, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
506 CLI_OPTION_GROUP( "Flags" ),
508 DNSSDFlagsOption_AllowExpiredAnswers(),
509 DNSSDFlagsOption_DenyCellular(),
510 DNSSDFlagsOption_DenyConstrained(),
511 DNSSDFlagsOption_DenyExpensive(),
512 DNSSDFlagsOption_ForceMulticast(),
513 DNSSDFlagsOption_IncludeAWDL(),
514 DNSSDFlagsOption_PathEvalDone(),
515 DNSSDFlagsOption_ReturnIntermediates(),
516 DNSSDFlagsOption_SuppressUnusable(),
517 DNSSDFlagsOption_Timeout(),
518 DNSSDFlagsOption_UnicastResponse(),
520 CLI_OPTION_GROUP( "Operation" ),
522 BooleanOption( 'o', "oneshot", &gQueryRecord_OneShot
, "Finish after first set of results." ),
523 IntegerOption( 'l', "timeLimit", &gQueryRecord_TimeLimitSecs
, "seconds", "Maximum duration of the query record operation. Use '0' for no time limit.", false ),
524 BooleanOption( 0 , "raw", &gQueryRecord_RawRData
, "Show record data as a hexdump." ),
530 //===========================================================================================================================
531 // Register Command Options
532 //===========================================================================================================================
534 static const char * gRegister_Name
= NULL
;
535 static const char * gRegister_Type
= NULL
;
536 static const char * gRegister_Domain
= NULL
;
537 static int gRegister_Port
= 0;
538 static const char * gRegister_TXT
= NULL
;
539 static int gRegister_LifetimeMs
= -1;
540 static const char ** gAddRecord_Types
= NULL
;
541 static size_t gAddRecord_TypesCount
= 0;
542 static const char ** gAddRecord_Data
= NULL
;
543 static size_t gAddRecord_DataCount
= 0;
544 static const char ** gAddRecord_TTLs
= NULL
;
545 static size_t gAddRecord_TTLsCount
= 0;
546 static const char * gUpdateRecord_Data
= NULL
;
547 static int gUpdateRecord_DelayMs
= 0;
548 static int gUpdateRecord_TTL
= 0;
550 static CLIOption kRegisterOpts
[] =
553 StringOption( 'n', "name", &gRegister_Name
, "service name", "Name of service.", false ),
554 StringOption( 't', "type", &gRegister_Type
, "service type", "Service type, e.g., \"_ssh._tcp\".", true ),
555 StringOption( 'd', "domain", &gRegister_Domain
, "domain", "Domain in which to advertise the service.", false ),
556 IntegerOption( 'p', "port", &gRegister_Port
, "port number", "Service's port number.", true ),
557 StringOption( 0 , "txt", &gRegister_TXT
, "record data", "The TXT record data. See " kRecordDataSection_Name
" below.", false ),
559 CLI_OPTION_GROUP( "Flags" ),
561 DNSSDFlagsOption_IncludeAWDL(),
562 DNSSDFlagsOption_KnownUnique(),
563 DNSSDFlagsOption_NoAutoRename(),
565 CLI_OPTION_GROUP( "Operation" ),
566 IntegerOption( 'l', "lifetime", &gRegister_LifetimeMs
, "ms", "Lifetime of the service registration in milliseconds.", false ),
568 CLI_OPTION_GROUP( "Options for updating the registered service's primary TXT record with DNSServiceUpdateRecord()\n" ),
569 StringOption( 0 , "updateData", &gUpdateRecord_Data
, "record data", "Record data for the record update. See " kRecordDataSection_Name
" below.", false ),
570 IntegerOption( 0 , "updateDelay", &gUpdateRecord_DelayMs
, "ms", "Number of milliseconds after registration to wait before record update.", false ),
571 IntegerOption( 0 , "updateTTL", &gUpdateRecord_TTL
, "seconds", "Time-to-live of the updated record.", false ),
573 CLI_OPTION_GROUP( "Options for adding extra record(s) to the registered service with DNSServiceAddRecord()\n" ),
574 MultiStringOption( 0 , "addType", &gAddRecord_Types
, &gAddRecord_TypesCount
, "record type", "Type of additional record by name (e.g., TXT, SRV, etc.) or number.", false ),
575 MultiStringOptionEx( 0 , "addData", &gAddRecord_Data
, &gAddRecord_DataCount
, "record data", "Additional record's data. See " kRecordDataSection_Name
" below.", false, NULL
),
576 MultiStringOption( 0 , "addTTL", &gAddRecord_TTLs
, &gAddRecord_TTLsCount
, "seconds", "Time-to-live of additional record in seconds. Use '0' for default.", false ),
582 //===========================================================================================================================
583 // RegisterRecord Command Options
584 //===========================================================================================================================
586 static const char * gRegisterRecord_Name
= NULL
;
587 static const char * gRegisterRecord_Type
= NULL
;
588 static const char * gRegisterRecord_Data
= NULL
;
589 static int gRegisterRecord_TTL
= 0;
590 static int gRegisterRecord_LifetimeMs
= -1;
591 static const char * gRegisterRecord_UpdateData
= NULL
;
592 static int gRegisterRecord_UpdateDelayMs
= 0;
593 static int gRegisterRecord_UpdateTTL
= 0;
595 static CLIOption kRegisterRecordOpts
[] =
598 StringOption( 'n', "name", &gRegisterRecord_Name
, "record name", "Fully qualified domain name of record.", true ),
599 StringOption( 't', "type", &gRegisterRecord_Type
, "record type", "Record type by name (e.g., TXT, PTR, A) or number.", true ),
600 StringOption( 'd', "data", &gRegisterRecord_Data
, "record data", "The record data. See " kRecordDataSection_Name
" below.", false ),
601 IntegerOption( 0 , "ttl", &gRegisterRecord_TTL
, "seconds", "Time-to-live in seconds. Use '0' for default.", false ),
603 CLI_OPTION_GROUP( "Flags" ),
605 DNSSDFlagsOption_IncludeAWDL(),
606 DNSSDFlagsOption_KnownUnique(),
607 DNSSDFlagsOption_Shared(),
608 DNSSDFlagsOption_Unique(),
610 CLI_OPTION_GROUP( "Operation" ),
611 IntegerOption( 'l', "lifetime", &gRegisterRecord_LifetimeMs
, "ms", "Lifetime of the service registration in milliseconds.", false ),
613 CLI_OPTION_GROUP( "Options for updating the registered record with DNSServiceUpdateRecord()\n" ),
614 StringOption( 0 , "updateData", &gRegisterRecord_UpdateData
, "record data", "Record data for the record update.", false ),
615 IntegerOption( 0 , "updateDelay", &gRegisterRecord_UpdateDelayMs
, "ms", "Number of milliseconds after registration to wait before record update.", false ),
616 IntegerOption( 0 , "updateTTL", &gRegisterRecord_UpdateTTL
, "seconds", "Time-to-live of the updated record.", false ),
622 //===========================================================================================================================
623 // Resolve Command Options
624 //===========================================================================================================================
626 static char * gResolve_Name
= NULL
;
627 static char * gResolve_Type
= NULL
;
628 static char * gResolve_Domain
= NULL
;
629 static int gResolve_TimeLimitSecs
= 0;
631 static CLIOption kResolveOpts
[] =
634 StringOption( 'n', "name", &gResolve_Name
, "service name", "Name of the service instance to resolve.", true ),
635 StringOption( 't', "type", &gResolve_Type
, "service type", "Type of the service instance to resolve.", true ),
636 StringOption( 'd', "domain", &gResolve_Domain
, "domain", "Domain of the service instance to resolve.", true ),
638 CLI_OPTION_GROUP( "Flags" ),
640 DNSSDFlagsOption_ForceMulticast(),
641 DNSSDFlagsOption_IncludeAWDL(),
642 DNSSDFlagsOption_ReturnIntermediates(),
643 DNSSDFlagsOption_WakeOnResolve(),
645 CLI_OPTION_GROUP( "Operation" ),
647 IntegerOption( 'l', "timeLimit", &gResolve_TimeLimitSecs
, "seconds", "Maximum duration of the resolve operation. Use '0' for no time limit.", false ),
653 //===========================================================================================================================
654 // Reconfirm Command Options
655 //===========================================================================================================================
657 static const char * gReconfirmRecord_Name
= NULL
;
658 static const char * gReconfirmRecord_Type
= NULL
;
659 static const char * gReconfirmRecord_Class
= NULL
;
660 static const char * gReconfirmRecord_Data
= NULL
;
662 static CLIOption kReconfirmOpts
[] =
665 StringOption( 'n', "name", &gReconfirmRecord_Name
, "record name", "Full name of the record to reconfirm.", true ),
666 StringOption( 't', "type", &gReconfirmRecord_Type
, "record type", "Type of the record to reconfirm.", true ),
667 StringOption( 'c', "class", &gReconfirmRecord_Class
, "record class", "Class of the record to reconfirm. Default class is IN.", false ),
668 StringOption( 'd', "data", &gReconfirmRecord_Data
, "record data", "The record data. See " kRecordDataSection_Name
" below.", false ),
670 CLI_OPTION_GROUP( "Flags" ),
677 //===========================================================================================================================
678 // getaddrinfo-POSIX Command Options
679 //===========================================================================================================================
681 static const char * gGAIPOSIX_HostName
= NULL
;
682 static const char * gGAIPOSIX_ServName
= NULL
;
683 static const char * gGAIPOSIX_Family
= NULL
;
684 static int gGAIPOSIXFlag_AddrConfig
= false;
685 static int gGAIPOSIXFlag_All
= false;
686 static int gGAIPOSIXFlag_CanonName
= false;
687 static int gGAIPOSIXFlag_NumericHost
= false;
688 static int gGAIPOSIXFlag_NumericServ
= false;
689 static int gGAIPOSIXFlag_Passive
= false;
690 static int gGAIPOSIXFlag_V4Mapped
= false;
691 #if( defined( AI_V4MAPPED_CFG ) )
692 static int gGAIPOSIXFlag_V4MappedCFG
= false;
694 #if( defined( AI_DEFAULT ) )
695 static int gGAIPOSIXFlag_Default
= false;
697 #if( defined( AI_UNUSABLE ) )
698 static int gGAIPOSIXFlag_Unusable
= false;
701 static CLIOption kGetAddrInfoPOSIXOpts
[] =
703 StringOption( 'n', "hostname", &gGAIPOSIX_HostName
, "hostname", "Domain name to resolve or an IPv4 or IPv6 address.", true ),
704 StringOption( 's', "servname", &gGAIPOSIX_ServName
, "servname", "Port number in decimal or service name from services(5).", false ),
706 CLI_OPTION_GROUP( "Hints" ),
707 StringOptionEx( 'f', "family", &gGAIPOSIX_Family
, "address family", "Address family to use for hints ai_family field.", false,
709 "Possible address family values are 'inet' for AF_INET, 'inet6' for AF_INET6, or 'unspec' for AF_UNSPEC. If no\n"
710 "address family is specified, then AF_UNSPEC is used.\n"
712 BooleanOption( 0 , "flag-addrconfig", &gGAIPOSIXFlag_AddrConfig
, "In hints ai_flags field, set AI_ADDRCONFIG." ),
713 BooleanOption( 0 , "flag-all", &gGAIPOSIXFlag_All
, "In hints ai_flags field, set AI_ALL." ),
714 BooleanOption( 0 , "flag-canonname", &gGAIPOSIXFlag_CanonName
, "In hints ai_flags field, set AI_CANONNAME." ),
715 BooleanOption( 0 , "flag-numerichost", &gGAIPOSIXFlag_NumericHost
, "In hints ai_flags field, set AI_NUMERICHOST." ),
716 BooleanOption( 0 , "flag-numericserv", &gGAIPOSIXFlag_NumericServ
, "In hints ai_flags field, set AI_NUMERICSERV." ),
717 BooleanOption( 0 , "flag-passive", &gGAIPOSIXFlag_Passive
, "In hints ai_flags field, set AI_PASSIVE." ),
718 BooleanOption( 0 , "flag-v4mapped", &gGAIPOSIXFlag_V4Mapped
, "In hints ai_flags field, set AI_V4MAPPED." ),
719 #if( defined( AI_V4MAPPED_CFG ) )
720 BooleanOption( 0 , "flag-v4mappedcfg", &gGAIPOSIXFlag_V4MappedCFG
, "In hints ai_flags field, set AI_V4MAPPED_CFG." ),
722 #if( defined( AI_DEFAULT ) )
723 BooleanOption( 0 , "flag-default", &gGAIPOSIXFlag_Default
, "In hints ai_flags field, set AI_DEFAULT." ),
725 #if( defined( AI_UNUSABLE ) )
726 BooleanOption( 0 , "flag-unusable", &gGAIPOSIXFlag_Unusable
, "In hints ai_flags field, set AI_UNUSABLE." ),
729 CLI_SECTION( "Notes", "See getaddrinfo(3) man page for more details.\n" ),
733 //===========================================================================================================================
734 // ReverseLookup Command Options
735 //===========================================================================================================================
737 static const char * gReverseLookup_IPAddr
= NULL
;
738 static int gReverseLookup_OneShot
= false;
739 static int gReverseLookup_TimeLimitSecs
= 0;
741 static CLIOption kReverseLookupOpts
[] =
744 StringOption( 'a', "address", &gReverseLookup_IPAddr
, "IP address", "IPv4 or IPv6 address for which to perform a reverse IP lookup.", true ),
746 CLI_OPTION_GROUP( "Flags" ),
748 DNSSDFlagsOption_ForceMulticast(),
749 DNSSDFlagsOption_ReturnIntermediates(),
750 DNSSDFlagsOption_SuppressUnusable(),
752 CLI_OPTION_GROUP( "Operation" ),
754 BooleanOption( 'o', "oneshot", &gReverseLookup_OneShot
, "Finish after first set of results." ),
755 IntegerOption( 'l', "timeLimit", &gReverseLookup_TimeLimitSecs
, "seconds", "Specifies the max duration of the query record operation. Use '0' for no time limit.", false ),
761 //===========================================================================================================================
762 // PortMapping Command Options
763 //===========================================================================================================================
765 static int gPortMapping_ProtocolTCP
= false;
766 static int gPortMapping_ProtocolUDP
= false;
767 static int gPortMapping_InternalPort
= 0;
768 static int gPortMapping_ExternalPort
= 0;
769 static int gPortMapping_TTL
= 0;
771 static CLIOption kPortMappingOpts
[] =
774 BooleanOption( 0, "tcp", &gPortMapping_ProtocolTCP
, "Use kDNSServiceProtocol_TCP." ),
775 BooleanOption( 0, "udp", &gPortMapping_ProtocolUDP
, "Use kDNSServiceProtocol_UDP." ),
776 IntegerOption( 0, "internalPort", &gPortMapping_InternalPort
, "port number", "Internal port.", false ),
777 IntegerOption( 0, "externalPort", &gPortMapping_ExternalPort
, "port number", "Requested external port. Use '0' for any external port.", false ),
778 IntegerOption( 0, "ttl", &gPortMapping_TTL
, "seconds", "Requested TTL (renewal period) in seconds. Use '0' for a default value.", false ),
780 CLI_OPTION_GROUP( "Flags" ),
783 CLI_OPTION_GROUP( "Operation" ),
790 #if( TARGET_OS_DARWIN )
791 //===========================================================================================================================
792 // RegisterKA Command Options
793 //===========================================================================================================================
795 static const char * gRegisterKA_LocalAddress
= NULL
;
796 static const char * gRegisterKA_RemoteAddress
= NULL
;
797 static int gRegisterKA_Timeout
= 0;
799 static CLIOption kRegisterKA_Opts
[] =
802 StringOption( 'l', "local", &gRegisterKA_LocalAddress
, "IP addr+port", "TCP connection's local IPv4 or IPv6 address and port pair.", true ),
803 StringOption( 'r', "remote", &gRegisterKA_RemoteAddress
, "IP addr+port", "TCP connection's remote IPv4 or IPv6 address and port pair.", true ),
804 IntegerOption( 't', "timeout", &gRegisterKA_Timeout
, "timeout", "Keepalive record's timeout value, i.e., its 't=' value.", false ),
808 static void RegisterKACmd( void );
811 //===========================================================================================================================
812 // BrowseAll Command Options
813 //===========================================================================================================================
815 static const char * gBrowseAll_Domain
= NULL
;
816 static const char ** gBrowseAll_ServiceTypes
= NULL
;
817 static size_t gBrowseAll_ServiceTypesCount
= 0;
818 static int gBrowseAll_BrowseTimeSecs
= 5;
819 static int gBrowseAll_ConnectTimeout
= 0;
821 static CLIOption kBrowseAllOpts
[] =
824 StringOption( 'd', "domain", &gBrowseAll_Domain
, "domain", "Domain in which to browse for the service.", false ),
825 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 ),
827 CLI_OPTION_GROUP( "Flags" ),
828 DNSSDFlagsOption_IncludeAWDL(),
830 CLI_OPTION_GROUP( "Operation" ),
831 IntegerOption( 'b', "browseTime", &gBrowseAll_BrowseTimeSecs
, "seconds", "Amount of time to spend browsing in seconds. (default: 5)", false ),
832 IntegerOption( 'c', "connectTimeout", &gBrowseAll_ConnectTimeout
, "seconds", "Timeout for connection attempts. If <= 0, no connections are attempted. (default: 0)", false ),
836 //===========================================================================================================================
837 // GetNameInfo Command Options
838 //===========================================================================================================================
840 static void GetNameInfoCmd( void );
842 static char * gGetNameInfo_IPAddress
= NULL
;
843 static int gGetNameInfoFlag_DGram
= false;
844 static int gGetNameInfoFlag_NameReqd
= false;
845 static int gGetNameInfoFlag_NoFQDN
= false;
846 static int gGetNameInfoFlag_NumericHost
= false;
847 static int gGetNameInfoFlag_NumericScope
= false;
848 static int gGetNameInfoFlag_NumericServ
= false;
850 static CLIOption kGetNameInfoOpts
[] =
852 StringOption( 'a', "address", &gGetNameInfo_IPAddress
, "IP address", "IPv4 or IPv6 address to use in sockaddr structure.", true ),
854 CLI_OPTION_GROUP( "Flags" ),
855 BooleanOption( 0 , "flag-dgram", &gGetNameInfoFlag_DGram
, "Use NI_DGRAM flag." ),
856 BooleanOption( 0 , "flag-namereqd", &gGetNameInfoFlag_NameReqd
, "Use NI_NAMEREQD flag." ),
857 BooleanOption( 0 , "flag-nofqdn", &gGetNameInfoFlag_NoFQDN
, "Use NI_NOFQDN flag." ),
858 BooleanOption( 0 , "flag-numerichost", &gGetNameInfoFlag_NumericHost
, "Use NI_NUMERICHOST flag." ),
859 BooleanOption( 0 , "flag-numericscope", &gGetNameInfoFlag_NumericScope
, "Use NI_NUMERICSCOPE flag." ),
860 BooleanOption( 0 , "flag-numericserv", &gGetNameInfoFlag_NumericServ
, "Use NI_NUMERICSERV flag." ),
862 CLI_SECTION( "Notes", "See getnameinfo(3) man page for more details.\n" ),
866 //===========================================================================================================================
867 // GetAddrInfoStress Command Options
868 //===========================================================================================================================
870 static int gGAIStress_TestDurationSecs
= 0;
871 static int gGAIStress_ConnectionCount
= 0;
872 static int gGAIStress_DurationMinMs
= 0;
873 static int gGAIStress_DurationMaxMs
= 0;
874 static int gGAIStress_RequestCountMax
= 0;
876 static CLIOption kGetAddrInfoStressOpts
[] =
880 CLI_OPTION_GROUP( "Flags" ),
881 DNSSDFlagsOption_ReturnIntermediates(),
882 DNSSDFlagsOption_SuppressUnusable(),
884 CLI_OPTION_GROUP( "Operation" ),
885 IntegerOption( 0, "testDuration", &gGAIStress_TestDurationSecs
, "seconds", "Stress test duration in seconds. Use '0' for forever.", false ),
886 IntegerOption( 0, "connectionCount", &gGAIStress_ConnectionCount
, "integer", "Number of simultaneous DNS-SD connections.", true ),
887 IntegerOption( 0, "requestDurationMin", &gGAIStress_DurationMinMs
, "ms", "Minimum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
888 IntegerOption( 0, "requestDurationMax", &gGAIStress_DurationMaxMs
, "ms", "Maximum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
889 IntegerOption( 0, "consecutiveRequestMax", &gGAIStress_RequestCountMax
, "integer", "Maximum number of requests on a connection before restarting it.", true ),
893 //===========================================================================================================================
894 // DNSQuery Command Options
895 //===========================================================================================================================
897 static char * gDNSQuery_Name
= NULL
;
898 static char * gDNSQuery_Type
= "A";
899 static char * gDNSQuery_Server
= NULL
;
900 static int gDNSQuery_TimeLimitSecs
= 5;
901 static int gDNSQuery_UseTCP
= false;
902 static int gDNSQuery_Flags
= kDNSHeaderFlag_RecursionDesired
;
903 static int gDNSQuery_RawRData
= false;
904 static int gDNSQuery_Verbose
= false;
906 #if( TARGET_OS_DARWIN )
907 #define kDNSQueryServerOptionIsRequired false
909 #define kDNSQueryServerOptionIsRequired true
912 static CLIOption kDNSQueryOpts
[] =
914 StringOption( 'n', "name", &gDNSQuery_Name
, "name", "Question name (QNAME) to put in DNS query message.", true ),
915 StringOption( 't', "type", &gDNSQuery_Type
, "type", "Question type (QTYPE) to put in DNS query message. Default value is 'A'.", false ),
916 StringOption( 's', "server", &gDNSQuery_Server
, "IP address", "DNS server's IPv4 or IPv6 address.", kDNSQueryServerOptionIsRequired
),
917 IntegerOption( 'l', "timeLimit", &gDNSQuery_TimeLimitSecs
, "seconds", "Specifies query time limit. Use '-1' for no limit and '0' to exit immediately after sending.", false ),
918 BooleanOption( 0 , "tcp", &gDNSQuery_UseTCP
, "Send the DNS query via TCP instead of UDP." ),
919 IntegerOption( 'f', "flags", &gDNSQuery_Flags
, "flags", "16-bit value for DNS header flags/codes field. Default value is 0x0100 (Recursion Desired).", false ),
920 BooleanOption( 0 , "raw", &gDNSQuery_RawRData
, "Present record data as a hexdump." ),
921 BooleanOption( 'v', "verbose", &gDNSQuery_Verbose
, "Prints the DNS message to be sent to the server." ),
925 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
926 //===========================================================================================================================
927 // DNSCrypt Command Options
928 //===========================================================================================================================
930 static char * gDNSCrypt_ProviderName
= NULL
;
931 static char * gDNSCrypt_ProviderKey
= NULL
;
932 static char * gDNSCrypt_Name
= NULL
;
933 static char * gDNSCrypt_Type
= NULL
;
934 static char * gDNSCrypt_Server
= NULL
;
935 static int gDNSCrypt_TimeLimitSecs
= 5;
936 static int gDNSCrypt_RawRData
= false;
937 static int gDNSCrypt_Verbose
= false;
939 static CLIOption kDNSCryptOpts
[] =
941 StringOption( 'p', "providerName", &gDNSCrypt_ProviderName
, "name", "The DNSCrypt provider name.", true ),
942 StringOption( 'k', "providerKey", &gDNSCrypt_ProviderKey
, "hex string", "The DNSCrypt provider's public signing key.", true ),
943 StringOption( 'n', "name", &gDNSCrypt_Name
, "name", "Question name (QNAME) to put in DNS query message.", true ),
944 StringOption( 't', "type", &gDNSCrypt_Type
, "type", "Question type (QTYPE) to put in DNS query message.", true ),
945 StringOption( 's', "server", &gDNSCrypt_Server
, "IP address", "DNS server's IPv4 or IPv6 address.", true ),
946 IntegerOption( 'l', "timeLimit", &gDNSCrypt_TimeLimitSecs
, "seconds", "Specifies query time limit. Use '-1' for no time limit and '0' to exit immediately after sending.", false ),
947 BooleanOption( 0 , "raw", &gDNSCrypt_RawRData
, "Present record data as a hexdump." ),
948 BooleanOption( 'v', "verbose", &gDNSCrypt_Verbose
, "Prints the DNS message to be sent to the server." ),
953 //===========================================================================================================================
954 // MDNSQuery Command Options
955 //===========================================================================================================================
957 static char * gMDNSQuery_Name
= NULL
;
958 static char * gMDNSQuery_Type
= NULL
;
959 static int gMDNSQuery_SourcePort
= 0;
960 static int gMDNSQuery_IsQU
= false;
961 static int gMDNSQuery_RawRData
= false;
962 static int gMDNSQuery_UseIPv4
= false;
963 static int gMDNSQuery_UseIPv6
= false;
964 static int gMDNSQuery_AllResponses
= false;
965 static int gMDNSQuery_ReceiveSecs
= 1;
967 static CLIOption kMDNSQueryOpts
[] =
969 StringOption( 'i', "interface", &gInterface
, "name or index", "Network interface by name or index.", true ),
970 StringOption( 'n', "name", &gMDNSQuery_Name
, "name", "Question name (QNAME) to put in mDNS message.", true ),
971 StringOption( 't', "type", &gMDNSQuery_Type
, "type", "Question type (QTYPE) to put in mDNS message.", true ),
972 IntegerOption( 'p', "sourcePort", &gMDNSQuery_SourcePort
, "port number", "UDP source port to use when sending mDNS messages. Default is 5353 for QM questions.", false ),
973 BooleanOption( 'u', "QU", &gMDNSQuery_IsQU
, "Set the unicast-response bit, i.e., send a QU question." ),
974 BooleanOption( 0 , "raw", &gMDNSQuery_RawRData
, "Present record data as a hexdump." ),
975 BooleanOption( 0 , "ipv4", &gMDNSQuery_UseIPv4
, "Use IPv4." ),
976 BooleanOption( 0 , "ipv6", &gMDNSQuery_UseIPv6
, "Use IPv6." ),
977 BooleanOption( 'a', "allResponses", &gMDNSQuery_AllResponses
, "Print all received mDNS messages, not just those containing answers." ),
978 IntegerOption( 'r', "receiveTime", &gMDNSQuery_ReceiveSecs
, "seconds", "Amount of time to spend receiving messages after the query is sent. The default is one second. Use -1 for unlimited time.", false ),
982 //===========================================================================================================================
983 // MDNSCollider Command Options
984 //===========================================================================================================================
986 #define kMDNSColliderProgramSection_Intro \
987 "Programs dictate when the collider sends out unsolicited response messages for its record and how the collider\n" \
988 "ought to react to probe queries that match its record's name, if at all.\n" \
990 "For example, suppose that the goal is to cause a specific unique record in the verified state to be renamed.\n" \
991 "The collider should be invoked such that its record's name is equal to that of the record being targeted. Also,\n" \
992 "the record's type and data should be such that no record with that name, type, and data combination currently\n" \
993 "exists. If the mDNS responder that owns the record follows sections 8.1 and 9 of RFC 6762, then the goal can be\n" \
994 "accomplished with the following program:\n" \
996 " probes 3r; send; wait 5000\n" \
998 "The first command, 'probes 3r', tells the collider to respond to the next three probe queries that match its\n" \
999 "record's name. The second command, makes the collider send an unsolicited response message that contains its\n" \
1000 "record in the answer section. The third command makes the collider wait for five seconds before exiting, which\n" \
1001 "is more than enough time for the collider to respond to probe queries.\n" \
1003 "The send command will cause the targeted record to go into the probing state per section 9 since the collider's\n" \
1004 "record conflicts with target record. Per the probes command, the subsequent probe query sent during the probing\n" \
1005 "state will be answered by the collider, which will cause the record to be renamed per section 8.1.\n"
1007 #define kMDNSColliderProgramSection_Probes \
1008 "The probes command defines how the collider ought to react to probe queries that match its record's name.\n" \
1010 "Usage: probes [<action-string>]\n" \
1012 "The syntax for an action-string is\n" \
1014 " <action-string> ::= <action> | <action-string> \"-\" <action>\n" \
1015 " <action> ::= [<repeat-count>] <action-code>\n" \
1016 " <repeat-count> ::= \"1\" | \"2\" | ... | \"10\"\n" \
1017 " <action-code> ::= \"n\" | \"r\" | \"u\" | \"m\" | \"p\"\n" \
1019 "An expanded action-string is defined as\n" \
1021 " <expanded-action-string> ::= <action-code> | <expanded-action-string> \"-\" <action-code>\n" \
1023 "The action-string argument is converted into an expanded-action-string by expanding each action with a\n" \
1024 "repeat-count into an expanded-action-string consisting of exactly <repeat-count> <action-code>s. For example,\n" \
1025 "2n-r expands to n-n-r. Action-strings that expand to expanded-action-strings with more than 10 action-codes\n" \
1026 "are not allowed.\n" \
1028 "When the probes command is executed, it does two things. Firstly, it resets to zero the collider's count of\n" \
1029 "probe queries that match its record's name. Secondly, it defines how the collider ought to react to such probe\n" \
1030 "queries based on the action-string argument. Specifically, the nth action-code in the expanded version of the\n" \
1031 "action-string argument defines how the collider ought to react to the nth received probe query:\n" \
1035 " n Do nothing.\n" \
1036 " r Respond to the probe query.\n" \
1037 " u Respond to the probe query via unicast.\n" \
1038 " m Respond to the probe query via multicast.\n" \
1039 " p Multicast own probe query. (Useful for causing simultaneous probe scenarios.)\n" \
1041 "Note: If no action is defined for a received probe query, then the collider does nothing, i.e., it doesn't send\n" \
1042 "a response nor does it multicast its own probe query.\n"
1044 #define kMDNSColliderProgramSection_Send \
1045 "The send command multicasts an unsolicited mDNS response containing the collider's record in the answer\n" \
1046 "section, which can be used to force unique records with the same record name into the probing state.\n" \
1050 #define kMDNSColliderProgramSection_Wait \
1051 "The wait command pauses program execution for the interval of time specified by its argument.\n" \
1053 "Usage: wait <milliseconds>\n"
1055 #define kMDNSColliderProgramSection_Loop \
1056 "The loop command starts a counting loop. The done statement marks the end of the loop body. The loop command's\n" \
1057 "argument specifies the number of loop iterations. Note: Loop nesting is supported up to a depth of 16.\n" \
1059 "Usage: loop <non-zero count>; ... ; done\n" \
1061 "For example, the following program sends three unsolicited responses at an approximate rate of one per second:\n" \
1063 " loop 3; wait 1000; send; done"
1065 #define ConnectionSection() CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
1067 static const char * gMDNSCollider_Name
= NULL
;
1068 static const char * gMDNSCollider_Type
= NULL
;
1069 static const char * gMDNSCollider_RecordData
= NULL
;
1070 static int gMDNSCollider_UseIPv4
= false;
1071 static int gMDNSCollider_UseIPv6
= false;
1072 static const char * gMDNSCollider_Program
= NULL
;
1074 static CLIOption kMDNSColliderOpts
[] =
1076 StringOption( 'i', "interface", &gInterface
, "name or index", "Network interface by name or index.", true ),
1077 StringOption( 'n', "name", &gMDNSCollider_Name
, "name", "Collider's record name.", true ),
1078 StringOption( 't', "type", &gMDNSCollider_Type
, "type", "Collider's record type.", true ),
1079 StringOption( 'd', "data", &gMDNSCollider_RecordData
, "record data", "Collider's record data. See " kRecordDataSection_Name
" below.", true ),
1080 StringOption( 'p', "program", &gMDNSCollider_Program
, "program", "Program to execute. See Program section below.", true ),
1081 BooleanOption( 0 , "ipv4", &gMDNSCollider_UseIPv4
, "Use IPv4." ),
1082 BooleanOption( 0 , "ipv6", &gMDNSCollider_UseIPv6
, "Use IPv6." ),
1084 RecordDataSection(),
1085 CLI_SECTION( "Program", kMDNSColliderProgramSection_Intro
),
1086 CLI_SECTION( "Program Command: probes", kMDNSColliderProgramSection_Probes
),
1087 CLI_SECTION( "Program Command: send", kMDNSColliderProgramSection_Send
),
1088 CLI_SECTION( "Program Command: wait", kMDNSColliderProgramSection_Wait
),
1089 CLI_SECTION( "Program Command: loop", kMDNSColliderProgramSection_Loop
),
1093 static void MDNSColliderCmd( void );
1095 //===========================================================================================================================
1096 // PIDToUUID Command Options
1097 //===========================================================================================================================
1099 static int gPIDToUUID_PID
= 0;
1101 static CLIOption kPIDToUUIDOpts
[] =
1103 IntegerOption( 'p', "pid", &gPIDToUUID_PID
, "PID", "Process ID.", true ),
1107 //===========================================================================================================================
1108 // DNSServer Command Options
1109 //===========================================================================================================================
1111 #define kDNSServerInfoText_Intro \
1112 "The DNS server answers certain queries in the d.test. domain. Responses are dynamically generated based on the\n" \
1113 "presence of special labels in the query's QNAME. There are currently eight types of special labels that can be\n" \
1114 "used to generate specific responses: Alias labels, Alias-TTL labels, Count labels, Tag labels, TTL labels, the\n" \
1115 "IPv4 label, the IPv6 label, and SRV labels.\n" \
1117 "Note: Sub-strings representing integers in domain name labels are in decimal notation and without leading zeros.\n"
1119 #define kDNSServerInfoText_NameExistence \
1120 "A name is considered to exist if it's an Address name or an SRV name.\n" \
1122 "An Address name is defined as a name that ends with d.test., and the other labels, if any, and in no particular\n" \
1123 "order, unless otherwise noted, consist of\n" \
1125 " 1. at most one Alias or Alias-TTL label as the first label;\n" \
1126 " 2. at most one Count label;\n" \
1127 " 3. zero or more Tag labels;\n" \
1128 " 4. at most one TTL label; and\n" \
1129 " 5. at most one IPv4 or IPv6 label.\n" \
1131 "An SRV name is defined as a name with the following form:\n" \
1133 " _<service>._<proto>[.<parent domain>][.<SRV label 1>[.<target 1>][.<SRV label 2>[.<target 2>][...]]].d.test.\n" \
1135 "See \"SRV Names\" for details.\n"
1137 #define kDNSServerInfoText_ResourceRecords \
1138 "Currently, the server only supports CNAME, A, AAAA, and SRV records.\n" \
1140 "Address names that begin with an Alias or Alias-TTL label are aliases of canonical names, i.e., they're the\n" \
1141 "names of CNAME records. See \"Alias Labels\" and \"Alias-TTL Labels\" for details.\n" \
1143 "A canonical Address name can exclusively be the name of one or more A records, can exclusively be the name or\n" \
1144 "one or more AAAA records, or can be the name of both A and AAAA records. Address names that contain an IPv4\n" \
1145 "label have at least one A record, but no AAAA records. Address names that contain an IPv6 label, have at least\n" \
1146 "one AAAA record, but no A records. All other Address names have at least one A record and at least one AAAA\n" \
1147 "record. See \"Count Labels\" for how the number of address records for a given Address name is determined.\n" \
1149 "A records contain IPv4 addresses in the 203.0.113.0/24 block, while AAAA records contain IPv6 addresses in the\n" \
1150 "2001:db8:1::/120 block. Both of these address blocks are reserved for documentation. See\n" \
1151 "<https://tools.ietf.org/html/rfc5737> and <https://tools.ietf.org/html/rfc3849>.\n" \
1153 "SRV names are names of SRV records.\n" \
1155 "Unless otherwise specified, all resource records will use a default TTL. The default TTL can be set with the\n" \
1156 "--defaultTTL option. See \"Alias-TTL Labels\" and \"TTL Labels\" for details on how to query for CNAME, A, and\n" \
1157 "AAAA records with specific TTL values.\n"
1159 #define kDNSServerInfoText_AliasLabel \
1160 "Alias labels are of the form \"alias\" or \"alias-N\", where N is an integer in [2, 2^31 - 1].\n" \
1162 "If QNAME is an Address name and its first label is Alias label \"alias-N\", then the response will contain\n" \
1163 "exactly N CNAME records:\n" \
1165 " 1. For each i in [3, N], the response will contain a CNAME record whose name is identical to QNAME, except\n" \
1166 " that the first label is \"alias-i\" instead, and whose RDATA is the name of the other CNAME record whose\n" \
1167 " name has \"alias-(i - 1)\" as its first label.\n" \
1169 " 2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1170 " is \"alias-2\" instead, and whose RDATA is the name identical to QNAME, except that the first label is\n" \
1171 " \"alias\" instead.\n" \
1173 " 3. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1174 " is \"alias\" instead, and whose RDATA is the name identical to QNAME minus its first label.\n" \
1176 "If QNAME is an Address name and its first label is Alias label \"alias\", then the response will contain a\n" \
1177 "single CNAME record. The CNAME record's name will be equal to QNAME and its RDATA will be the name identical to\n" \
1178 "QNAME minus its first label.\n" \
1180 "Example. A response to a query with a QNAME of alias-3.count-5.d.test will contain the following CNAME\n" \
1183 " alias-4.count-5.d.test. 60 IN CNAME alias-3.count-5.d.test.\n" \
1184 " alias-3.count-5.d.test. 60 IN CNAME alias-2.count-5.d.test.\n" \
1185 " alias-2.count-5.d.test. 60 IN CNAME alias.count-5.d.test.\n" \
1186 " alias.count-5.d.test. 60 IN CNAME count-5.d.test.\n"
1188 #define kDNSServerInfoText_AliasTTLLabel \
1189 "Alias-TTL labels are of the form \"alias-ttl-T_1[-T_2[...-T_N]]\", where each T_i is an integer in\n" \
1190 "[0, 2^31 - 1] and N is a positive integer bounded by the size of the maximum legal label length (63 octets).\n" \
1192 "If QNAME is an Address name and its first label is Alias-TTL label \"alias-ttl-T_1...-T_N\", then the response\n" \
1193 "will contain exactly N CNAME records:\n" \
1195 " 1. For each i in [1, N - 1], the response will contain a CNAME record whose name is identical to QNAME,\n" \
1196 " except that the first label is \"alias-ttl-T_i...-T_N\" instead, whose TTL value is T_i, and whose RDATA\n" \
1197 " is the name of the other CNAME record whose name has \"alias-ttl-T_(i+1)...-T_N\" as its first label.\n" \
1199 " 2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1200 " is \"alias-ttl-T_N\", whose TTL is T_N, and whose RDATA is identical to QNAME stripped of its first\n" \
1203 "Example. A response to a query with a QNAME of alias-ttl-20-40-80.count-5.d.test will contain the following\n" \
1204 "CNAME records:\n" \
1206 " alias-ttl-20-40-80.count-5.d.test. 20 IN CNAME alias-ttl-40-80.count-5.d.test.\n" \
1207 " alias-ttl-40-80.count-5.d.test. 40 IN CNAME alias-ttl-80.count-5.d.test.\n" \
1208 " alias-ttl-80.count-5.d.test. 80 IN CNAME count-5.d.test.\n"
1210 #define kDNSServerInfoText_CountLabel \
1211 "Count labels are of the form \"count-N_1\" or \"count-N_1-N_2\", where N_1 is an integer in [1, 255] and N_2 is\n" \
1212 "an integer in [N_1, 255].\n" \
1214 "If QNAME is an Address name, contains Count label \"count-N\", and has the type of address records specified by\n" \
1215 "QTYPE, then the response will contain exactly N address records:\n" \
1217 " 1. For i in [1, N], the response will contain an address record of type QTYPE whose name is equal to QNAME\n" \
1218 " and whose RDATA is an address equal to a constant base address + i.\n" \
1220 " 2. The address records will be ordered by the address contained in RDATA in ascending order.\n" \
1222 "Example. A response to an A record query with a QNAME of alias.count-3.d.test will contain the following A\n" \
1225 " count-3.d.test. 60 IN A 203.0.113.1\n" \
1226 " count-3.d.test. 60 IN A 203.0.113.2\n" \
1227 " count-3.d.test. 60 IN A 203.0.113.3\n" \
1229 "If QNAME is an Address name, contains Count label \"count-N_1-N_2\", and has the type of address records\n" \
1230 "specified by QTYPE, then the response will contain exactly N_1 address records:\n" \
1232 " 1. Each of the address records will be of type QTYPE, have name equal to QNAME, and have as its RDATA a\n" \
1233 " unique address equal to a constant base address + i, where i is a randomly chosen integer in [1, N_2].\n" \
1235 " 2. The order of the address records will be random.\n" \
1237 "Example. A response to a AAAA record query with a QNAME of count-3-100.ttl-20.d.test could contain the\n" \
1238 "following AAAA records:\n" \
1240 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::c\n" \
1241 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::3a\n" \
1242 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::4f\n" \
1244 "If QNAME is an Address name, but doesn't have the type of address records specified by QTYPE, then the response\n" \
1245 "will contain no address records, regardless of whether it contains a Count label.\n" \
1247 "Address names that don't have a Count label are treated as though they contain a count label equal to\n" \
1250 #define kDNSServerInfoText_TagLabel \
1251 "Tag labels are labels prefixed with \"tag-\" and contain zero or more arbitrary octets after the prefix.\n" \
1253 "This type of label exists to allow testers to \"uniquify\" domain names. Tag labels can also serve as padding\n" \
1254 "to increase the sizes of domain names.\n"
1256 #define kDNSServerInfoText_TTLLabel \
1257 "TTL labels are of the form \"ttl-T\", where T is an integer in [0, 2^31 - 1].\n" \
1259 "If QNAME is an Address name and contains TTL label \"ttl-T\", then all non-CNAME records contained in the\n" \
1260 "response will have a TTL value equal to T.\n"
1262 #define kDNSServerInfoText_IPv4Label \
1263 "The IPv4 label is \"ipv4\". See \"Resource Records\" for the affect of this label.\n"
1265 #define kDNSServerInfoText_IPv6Label \
1266 "The IPv6 label is \"ipv6\". See \"Resource Records\" for the affect of this label.\n"
1268 #define kDNSServerInfoText_SRVNames \
1269 "SRV labels are of the form \"srv-R-W-P\", where R, W, and P are integers in [0, 2^16 - 1].\n" \
1271 "After the first two labels, i.e., the service and protocol labels, the sequence of labels, which may be empty,\n" \
1272 "leading up to the the first SRV label, if one exists, or the d.test. labels will be used as a parent domain for\n" \
1273 "the target hostname of each of the SRV name's SRV records.\n" \
1275 "If QNAME is an SRV name and QTYPE is SRV, then for each SRV label, the response will contain an SRV record with\n" \
1276 "priority R, weight W, port P, and target hostname <target>[.<parent domain>]., where <target> is the sequence\n" \
1277 "of labels, which may be empty, that follows the SRV label leading up to either the next SRV label or the\n" \
1278 "d.test. labels, whichever comes first.\n" \
1280 "Example. A response to an SRV record query with a QNAME of\n" \
1281 "_http._tcp.example.com.srv-0-0-80.www.srv-1-0-8080.www.d.test. will contain the following SRV records:\n" \
1283 "_http._tcp.example.com.srv-0-0-80.www.srv-1-0-8080.www.d.test. 60 IN SRV 0 0 80 www.example.com.\n" \
1284 "_http._tcp.example.com.srv-0-0-80.www.srv-1-0-8080.www.d.test. 60 IN SRV 1 0 8080 www.example.com.\n"
1286 #define kDNSServerInfoText_BadUDPMode \
1287 "The purpose of Bad UDP mode is to test mDNSResponder's TCP fallback mechanism by which mDNSResponder reissues a\n" \
1288 "UDP query as a TCP query if the UDP response contains the expected QNAME, QTYPE, and QCLASS, but a message ID\n" \
1289 "that's not equal to the query's message ID.\n" \
1291 "This mode is identical to the normal mode except that all responses sent via UDP have a message ID equal to the\n" \
1292 "query's message ID plus one. Also, in this mode, to aid in debugging, A records in responses sent via UDP have\n" \
1293 "IPv4 addresses in the 0.0.0.0/24 block instead of the 203.0.113.0/24 block, i.e., 0.0.0.0 is used as the IPv4\n" \
1294 "base address, and AAAA records in responses sent via UDP have IPv6 addresses in the ::ffff:0:0/120 block\n" \
1295 "instead of the 2001:db8:1::/120 block, i.e., ::ffff:0:0 is used as the IPv6 base address.\n"
1297 static int gDNSServer_LoopbackOnly
= false;
1298 static int gDNSServer_Foreground
= false;
1299 static int gDNSServer_ResponseDelayMs
= 0;
1300 static int gDNSServer_DefaultTTL
= 60;
1301 static int gDNSServer_Port
= kDNSPort
;
1302 static const char * gDNSServer_DomainOverride
= NULL
;
1303 #if( TARGET_OS_DARWIN )
1304 static const char * gDNSServer_FollowPID
= NULL
;
1306 static int gDNSServer_BadUDPMode
= false;
1308 static CLIOption kDNSServerOpts
[] =
1310 BooleanOption( 'l', "loopback", &gDNSServer_LoopbackOnly
, "Bind to to the loopback interface." ),
1311 BooleanOption( 'f', "foreground", &gDNSServer_Foreground
, "Direct log output to stdout instead of system logging." ),
1312 IntegerOption( 'd', "responseDelay", &gDNSServer_ResponseDelayMs
, "ms", "The amount of additional delay in milliseconds to apply to responses. (default: 0)", false ),
1313 IntegerOption( 0 , "defaultTTL", &gDNSServer_DefaultTTL
, "seconds", "Resource record TTL value to use when unspecified. (default: 60)", false ),
1314 IntegerOption( 'p', "port", &gDNSServer_Port
, "port number", "UDP/TCP port number to use. Use 0 for any port. (default: 53)", false ),
1315 StringOption( 0 , "domain", &gDNSServer_DomainOverride
, "domain", "Used to override 'd.test.' as the server's domain.", false ),
1316 #if( TARGET_OS_DARWIN )
1317 StringOption( 0 , "follow", &gDNSServer_FollowPID
, "pid", "Exit when the process, usually the parent process, specified by PID exits.", false ),
1319 BooleanOption( 0 , "badUDPMode", &gDNSServer_BadUDPMode
, "Run in Bad UDP mode to trigger mDNSResponder's TCP fallback mechanism." ),
1321 CLI_SECTION( "Intro", kDNSServerInfoText_Intro
),
1322 CLI_SECTION( "Name Existence", kDNSServerInfoText_NameExistence
),
1323 CLI_SECTION( "Resource Records", kDNSServerInfoText_ResourceRecords
),
1324 CLI_SECTION( "Alias Labels", kDNSServerInfoText_AliasLabel
),
1325 CLI_SECTION( "Alias-TTL Labels", kDNSServerInfoText_AliasTTLLabel
),
1326 CLI_SECTION( "Count Labels", kDNSServerInfoText_CountLabel
),
1327 CLI_SECTION( "Tag Labels", kDNSServerInfoText_TagLabel
),
1328 CLI_SECTION( "TTL Labels", kDNSServerInfoText_TTLLabel
),
1329 CLI_SECTION( "IPv4 Label", kDNSServerInfoText_IPv4Label
),
1330 CLI_SECTION( "IPv6 Label", kDNSServerInfoText_IPv6Label
),
1331 CLI_SECTION( "SRV Names", kDNSServerInfoText_SRVNames
),
1332 CLI_SECTION( "Bad UDP Mode", kDNSServerInfoText_BadUDPMode
),
1336 static void DNSServerCmd( void );
1338 //===========================================================================================================================
1339 // MDNSReplier Command Options
1340 //===========================================================================================================================
1342 #define kMDNSReplierPortBase 50000
1344 #define kMDNSReplierInfoText_Intro \
1345 "The mDNS replier answers mDNS queries for its authoritative records. These records are of class IN and of types\n" \
1346 "PTR, SRV, TXT, A, and AAAA as described below.\n" \
1348 "Note: Sub-strings representing integers in domain name labels are in decimal notation and without leading zeros.\n"
1350 #define kMDNSReplierInfoText_Parameters \
1351 "There are five parameters that control the replier's set of authoritative records.\n" \
1353 " 1. <hostname> is the base name used for service instance names and the names of A and AAAA records. This\n" \
1354 " parameter is specified with the --hostname option.\n" \
1355 " 2. <tag> is an arbitrary string used to uniquify service types. This parameter is specified with the --tag\n" \
1357 " 3. N_max in an integer in [1, 65535] and limits service types to those that have no more than N_max\n" \
1358 " instances. It also limits the number of hostnames to N_max, i.e., <hostname>.local.,\n" \
1359 " <hostname>-1.local., ..., <hostname>-N_max.local. This parameter is specified with the\n" \
1360 " --maxInstanceCount option.\n" \
1361 " 4. N_a is an integer in [1, 255] and the number of A records per hostname. This parameter is specified\n" \
1362 " with the --countA option.\n" \
1363 " 5. N_aaaa is an integer in [1, 255] and the number of AAAA records per hostname. This parameter is\n" \
1364 " specified with the --countAAAA option.\n"
1366 #define kMDNSReplierInfoText_PTR \
1367 "The replier's authoritative PTR records have names of the form _t-<tag>-<L>-<N>._tcp.local., where L is an\n" \
1368 "integer in [1, 65535], and N is an integer in [1, N_max].\n" \
1370 "For a given L and N, the replier has exactly N authoritative PTR records:\n" \
1372 " 1. The first PTR record is defined as\n" \
1374 " NAME: _t-<tag>-<L>-<N>._tcp.local.\n" \
1378 " RDATA: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n" \
1380 " 2. For each i in [2, N], there is one PTR record defined as\n" \
1382 " NAME: _t-<tag>-<L>-<N>._tcp.local.\n" \
1386 " RDATA: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n"
1388 #define kMDNSReplierInfoText_SRV \
1389 "The replier's authoritative SRV records have names of the form <instance name>._t-<tag>-<L>-<N>._tcp.local.,\n" \
1390 "where L is an integer in [1, 65535], N is an integer in [1, N_max], and <instance name> is <hostname> or\n" \
1391 "\"<hostname> (<i>)\", where i is in [2, N].\n" \
1393 "For a given L and N, the replier has exactly N authoritative SRV records:\n" \
1395 " 1. The first SRV record is defined as\n" \
1397 " NAME: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n" \
1404 " Port: (50000 + L) mod 2^16\n" \
1405 " Target: <hostname>.local.\n" \
1407 " 2. For each i in [2, N], there is one SRV record defined as:\n" \
1409 " NAME: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n" \
1416 " Port: (50000 + L) mod 2^16\n" \
1417 " Target: <hostname>-<i>.local.\n"
1419 #define kMDNSReplierInfoText_TXT \
1420 "The replier's authoritative TXT records have names of the form <instance name>._t-<tag>-<L>-<N>._tcp.local.,\n" \
1421 "where L is an integer in [1, 65535], N is an integer in [1, N_max], and <instance name> is <hostname> or\n" \
1422 "\"<hostname> (<i>)\", where i is in [2, N].\n" \
1424 "For a given L and N, the replier has exactly N authoritative TXT records:\n" \
1426 " 1. The first TXT record is defined as\n" \
1428 " NAME: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n" \
1433 " RDATA: <one or more strings with an aggregate length of L octets>\n" \
1435 " 2. For each i in [2, N], there is one TXT record:\n" \
1437 " NAME: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n" \
1442 " RDATA: <one or more strings with an aggregate length of L octets>\n" \
1444 "The RDATA of each TXT record is exactly L octets and consists of a repeating series of the 15-byte string\n" \
1445 "\"hash=0x<32-bit FNV-1 hash of the record name as an 8-character hexadecimal string>\". The last instance of\n" \
1446 "the string may be truncated to satisfy the TXT record data's size requirement.\n"
1448 #define kMDNSReplierInfoText_A \
1449 "The replier has exactly N_max x N_a authoritative A records:\n" \
1451 " 1. For each j in [1, N_a], an A record is defined as\n" \
1453 " NAME: <hostname>.local.\n" \
1458 " RDATA: 0.0.1.<j>\n" \
1460 " 2. For each i in [2, N_max], for each j in [1, N_a], an A record is defined as\n" \
1462 " NAME: <hostname>-<i>.local.\n" \
1467 " RDATA: 0.<ceil(i / 256)>.<i mod 256>.<j>\n"
1469 #define kMDNSReplierInfoText_AAAA \
1470 "The replier has exactly N_max x N_aaaa authoritative AAAA records:\n" \
1472 " 1. For each j in [1, N_aaaa], a AAAA record is defined as\n" \
1474 " NAME: <hostname>.local.\n" \
1479 " RDATA: 2001:db8:2::1:<j>\n" \
1481 " 2. For each i in [2, N_max], for each j in [1, N_aaaa], a AAAA record is defined as\n" \
1483 " NAME: <hostname>-<i>.local.\n" \
1488 " RDATA: 2001:db8:2::<i>:<j>\n"
1490 #define kMDNSReplierInfoText_Responses \
1491 "When generating answers for a query message, any two records pertaining to the same hostname will be grouped\n" \
1492 "together in the same response message, and any two records pertaining to different hostnames will be in\n" \
1493 "separate response messages.\n"
1495 static const char * gMDNSReplier_Hostname
= NULL
;
1496 static const char * gMDNSReplier_ServiceTypeTag
= NULL
;
1497 static int gMDNSReplier_MaxInstanceCount
= 1000;
1498 static int gMDNSReplier_NoAdditionals
= false;
1499 static int gMDNSReplier_RecordCountA
= 1;
1500 static int gMDNSReplier_RecordCountAAAA
= 1;
1501 static double gMDNSReplier_UnicastDropRate
= 0.0;
1502 static double gMDNSReplier_MulticastDropRate
= 0.0;
1503 static int gMDNSReplier_MaxDropCount
= 0;
1504 static int gMDNSReplier_UseIPv4
= false;
1505 static int gMDNSReplier_UseIPv6
= false;
1506 static int gMDNSReplier_Foreground
= false;
1507 static const char * gMDNSReplier_FollowPID
= NULL
;
1509 static CLIOption kMDNSReplierOpts
[] =
1511 StringOption( 'i', "interface", &gInterface
, "name or index", "Network interface by name or index.", true ),
1512 StringOption( 'n', "hostname", &gMDNSReplier_Hostname
, "string", "Base name to use for hostnames and service instance names.", true ),
1513 StringOption( 't', "tag", &gMDNSReplier_ServiceTypeTag
, "string", "Tag to use for service types, e.g., _t-<tag>-<TXT size>-<count>._tcp.", true ),
1514 IntegerOption( 'c', "maxInstanceCount", &gMDNSReplier_MaxInstanceCount
, "count", "Maximum number of service instances. (default: 1000)", false ),
1515 BooleanOption( 0 , "noAdditionals", &gMDNSReplier_NoAdditionals
, "When answering queries, don't include any additional records." ),
1516 IntegerOption( 0 , "countA", &gMDNSReplier_RecordCountA
, "count", "Number of A records per hostname. (default: 1)", false ),
1517 IntegerOption( 0 , "countAAAA", &gMDNSReplier_RecordCountAAAA
, "count", "Number of AAAA records per hostname. (default: 1)", false ),
1518 DoubleOption( 0 , "udrop", &gMDNSReplier_UnicastDropRate
, "probability", "Probability of dropping a unicast response. (default: 0.0)", false ),
1519 DoubleOption( 0 , "mdrop", &gMDNSReplier_MulticastDropRate
, "probability", "Probability of dropping a multicast query or response. (default: 0.0)", false ),
1520 IntegerOption( 0 , "maxDropCount", &gMDNSReplier_MaxDropCount
, "count", "If > 0, drop probabilities are limted to first <count> responses from each instance. (default: 0)", false ),
1521 BooleanOption( 0 , "ipv4", &gMDNSReplier_UseIPv4
, "Use IPv4." ),
1522 BooleanOption( 0 , "ipv6", &gMDNSReplier_UseIPv6
, "Use IPv6." ),
1523 BooleanOption( 'f', "foreground", &gMDNSReplier_Foreground
, "Direct log output to stdout instead of system logging." ),
1524 #if( TARGET_OS_DARWIN )
1525 StringOption( 0 , "follow", &gMDNSReplier_FollowPID
, "pid", "Exit when the process, usually the parent process, specified by PID exits.", false ),
1528 CLI_SECTION( "Intro", kMDNSReplierInfoText_Intro
),
1529 CLI_SECTION( "Authoritative Record Parameters", kMDNSReplierInfoText_Parameters
),
1530 CLI_SECTION( "Authoritative PTR Records", kMDNSReplierInfoText_PTR
),
1531 CLI_SECTION( "Authoritative SRV Records", kMDNSReplierInfoText_SRV
),
1532 CLI_SECTION( "Authoritative TXT Records", kMDNSReplierInfoText_TXT
),
1533 CLI_SECTION( "Authoritative A Records", kMDNSReplierInfoText_A
),
1534 CLI_SECTION( "Authoritative AAAA Records", kMDNSReplierInfoText_AAAA
),
1535 CLI_SECTION( "Responses", kMDNSReplierInfoText_Responses
),
1539 static void MDNSReplierCmd( void );
1541 //===========================================================================================================================
1542 // Test Command Options
1543 //===========================================================================================================================
1545 #define kTestExitStatusSection_Name "Exit Status"
1546 #define kTestExitStatusSection_Text \
1547 "This test command can exit with one of three status codes:\n" \
1549 "0 - The test ran to completion and passed.\n" \
1550 "1 - A fatal error prevented the test from completing.\n" \
1551 "2 - The test ran to completion, but it or a subtest failed. See test output for details.\n" \
1553 "Note: The pass/fail status applies to the correctness or results. It does not necessarily imply anything about\n" \
1556 #define TestExitStatusSection() CLI_SECTION( kTestExitStatusSection_Name, kTestExitStatusSection_Text )
1558 #define kGAIPerfTestSuiteName_Basic "basic"
1559 #define kGAIPerfTestSuiteName_Advanced "advanced"
1561 static const char * gGAIPerf_TestSuite
= NULL
;
1562 static int gGAIPerf_CallDelayMs
= 10;
1563 static int gGAIPerf_ServerDelayMs
= 10;
1564 static int gGAIPerf_SkipPathEvalulation
= false;
1565 static int gGAIPerf_BadUDPMode
= false;
1566 static int gGAIPerf_IterationCount
= 100;
1567 static int gGAIPerf_IterationTimeLimitMs
= 100;
1568 static const char * gGAIPerf_OutputFilePath
= NULL
;
1569 static const char * gGAIPerf_OutputFormat
= kOutputFormatStr_JSON
;
1570 static int gGAIPerf_OutputAppendNewline
= false;
1572 static void GAIPerfCmd( void );
1574 #define kGAIPerfSectionText_TestSuiteBasic \
1575 "This test suite consists of the following three test cases:\n" \
1577 "Test Case #1: Resolve a domain name with\n" \
1579 " 2 CNAME records, 4 A records, and 4 AAAA records\n" \
1581 "to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n" \
1582 "server queries.\n" \
1584 "Test Case #2: Resolve a domain name with\n" \
1586 " 2 CNAME records, 4 A records, and 4 AAAA records\n" \
1588 "to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n" \
1589 "requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n" \
1590 "which should ideally require no additional server queries, i.e., the results should come from the cache.\n" \
1592 "Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n" \
1593 "records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n" \
1594 "domain name in the preliminary iteration isn't counted in the performance stats.\n" \
1596 "Test Case #3: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n"
1598 #define kGAIPerfSectionText_TestSuiteAdvanced \
1599 "This test suite consists of 33 test cases. Test cases 1 through 32 can be described in the following way\n" \
1601 "Test Case #N (where N is in [1, 32] and odd): Resolve a domain name with\n" \
1603 " N_c CNAME records, N_a A records, and N_a AAAA records\n" \
1605 "to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n" \
1606 "server queries.\n" \
1608 "Test Case #N (where N is in [1, 32] and even): Resolve a domain name with\n" \
1610 " N_c CNAME records, N_a A records, and N_a AAAA records\n" \
1612 "to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n" \
1613 "requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n" \
1614 "which should ideally require no additional server queries, i.e., the results should come from the cache.\n" \
1616 "Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n" \
1617 "records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n" \
1618 "domain name in the preliminary iteration isn't counted in the performance stats.\n" \
1620 "N_c and N_a take on the following values, depending on the value of N:\n" \
1622 " N_c is 0 if N is in [1, 8].\n" \
1623 " N_c is 1 if N is in [9, 16].\n" \
1624 " N_c is 2 if N is in [17, 24].\n" \
1625 " N_c is 4 if N is in [25, 32].\n" \
1627 " N_a is 1 if N mod 8 is 1 or 2.\n" \
1628 " N_a is 2 if N mod 8 is 3 or 4.\n" \
1629 " N_a is 4 if N mod 8 is 5 or 6.\n" \
1630 " N_a is 8 if N mod 8 is 7 or 0.\n" \
1634 "Test Case #33: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n"
1636 static CLIOption kGAIPerfOpts
[] =
1638 StringOptionEx( 's', "suite", &gGAIPerf_TestSuite
, "name", "Name of the predefined test suite to run.", true,
1640 "There are currently two predefined test suites, '" kGAIPerfTestSuiteName_Basic
"' and '" kGAIPerfTestSuiteName_Advanced
"', which are described below.\n"
1643 StringOption( 'o', "output", &gGAIPerf_OutputFilePath
, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1644 FormatOption( 'f', "format", &gGAIPerf_OutputFormat
, "Specifies the test results output format. (default: " kOutputFormatStr_JSON
")", false ),
1645 BooleanOption( 'n', "appendNewline", &gGAIPerf_OutputAppendNewline
, "If the output format is JSON, output a trailing newline character." ),
1646 IntegerOption( 'i', "iterations", &gGAIPerf_IterationCount
, "count", "The number of iterations per test case. (default: 100)", false ),
1647 IntegerOption( 'l', "timeLimit", &gGAIPerf_IterationTimeLimitMs
, "ms", "Time limit for each DNSServiceGetAddrInfo() operation in milliseconds. (default: 100)", false ),
1648 IntegerOption( 0 , "callDelay", &gGAIPerf_CallDelayMs
, "ms", "Time to wait before calling DNSServiceGetAddrInfo() in milliseconds. (default: 10)", false ),
1649 BooleanOption( 0 , "skipPathEval", &gGAIPerf_SkipPathEvalulation
, "Use kDNSServiceFlagsPathEvaluationDone when calling DNSServiceGetAddrInfo()." ),
1651 CLI_OPTION_GROUP( "DNS Server Options" ),
1652 IntegerOption( 0 , "responseDelay", &gGAIPerf_ServerDelayMs
, "ms", "Additional delay in milliseconds to have the server apply to responses. (default: 10)", false ),
1653 BooleanOption( 0 , "badUDPMode", &gGAIPerf_BadUDPMode
, "Run server in Bad UDP mode to trigger mDNSResponder's TCP fallback mechanism." ),
1655 CLI_SECTION( "Test Suite \"Basic\"", kGAIPerfSectionText_TestSuiteBasic
),
1656 CLI_SECTION( "Test Suite \"Advanced\"", kGAIPerfSectionText_TestSuiteAdvanced
),
1657 TestExitStatusSection(),
1661 static void MDNSDiscoveryTestCmd( void );
1663 static int gMDNSDiscoveryTest_InstanceCount
= 100;
1664 static int gMDNSDiscoveryTest_TXTSize
= 100;
1665 static int gMDNSDiscoveryTest_BrowseTimeSecs
= 2;
1666 static int gMDNSDiscoveryTest_FlushCache
= false;
1667 static char * gMDNSDiscoveryTest_Interface
= NULL
;
1668 static int gMDNSDiscoveryTest_NoAdditionals
= false;
1669 static int gMDNSDiscoveryTest_RecordCountA
= 1;
1670 static int gMDNSDiscoveryTest_RecordCountAAAA
= 1;
1671 static double gMDNSDiscoveryTest_UnicastDropRate
= 0.0;
1672 static double gMDNSDiscoveryTest_MulticastDropRate
= 0.0;
1673 static int gMDNSDiscoveryTest_MaxDropCount
= 0;
1674 static int gMDNSDiscoveryTest_UseIPv4
= false;
1675 static int gMDNSDiscoveryTest_UseIPv6
= false;
1676 static const char * gMDNSDiscoveryTest_OutputFormat
= kOutputFormatStr_JSON
;
1677 static int gMDNSDiscoveryTest_OutputAppendNewline
= false;
1678 static const char * gMDNSDiscoveryTest_OutputFilePath
= NULL
;
1680 static CLIOption kMDNSDiscoveryTestOpts
[] =
1682 IntegerOption( 'c', "instanceCount", &gMDNSDiscoveryTest_InstanceCount
, "count", "Number of service instances to discover. (default: 100)", false ),
1683 IntegerOption( 's', "txtSize", &gMDNSDiscoveryTest_TXTSize
, "bytes", "Desired size of each service instance's TXT record data. (default: 100)", false ),
1684 IntegerOption( 'b', "browseTime", &gMDNSDiscoveryTest_BrowseTimeSecs
, "seconds", "Amount of time to spend browsing in seconds. (default: 2)", false ),
1685 BooleanOption( 0 , "flushCache", &gMDNSDiscoveryTest_FlushCache
, "Flush mDNSResponder's record cache before browsing. Requires root privileges." ),
1687 CLI_OPTION_GROUP( "mDNS Replier Parameters" ),
1688 StringOption( 'i', "interface", &gMDNSDiscoveryTest_Interface
, "name or index", "Network interface. If unspecified, any available mDNS-capable interface will be used.", false ),
1689 BooleanOption( 0 , "noAdditionals", &gMDNSDiscoveryTest_NoAdditionals
, "When answering queries, don't include any additional records." ),
1690 IntegerOption( 0 , "countA", &gMDNSDiscoveryTest_RecordCountA
, "count", "Number of A records per hostname. (default: 1)", false ),
1691 IntegerOption( 0 , "countAAAA", &gMDNSDiscoveryTest_RecordCountAAAA
, "count", "Number of AAAA records per hostname. (default: 1)", false ),
1692 DoubleOption( 0 , "udrop", &gMDNSDiscoveryTest_UnicastDropRate
, "probability", "Probability of dropping a unicast response. (default: 0.0)", false ),
1693 DoubleOption( 0 , "mdrop", &gMDNSDiscoveryTest_MulticastDropRate
, "probability", "Probability of dropping a multicast query or response. (default: 0.0)", false ),
1694 IntegerOption( 0 , "maxDropCount", &gMDNSDiscoveryTest_MaxDropCount
, "count", "If > 0, drop probabilities are limted to first <count> responses from each instance. (default: 0)", false ),
1695 BooleanOption( 0 , "ipv4", &gMDNSDiscoveryTest_UseIPv4
, "Use IPv4." ),
1696 BooleanOption( 0 , "ipv6", &gMDNSDiscoveryTest_UseIPv6
, "Use IPv6." ),
1698 CLI_OPTION_GROUP( "Results" ),
1699 FormatOption( 'f', "format", &gMDNSDiscoveryTest_OutputFormat
, "Specifies the test results output format. (default: " kOutputFormatStr_JSON
")", false ),
1700 StringOption( 'o', "output", &gMDNSDiscoveryTest_OutputFilePath
, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1702 TestExitStatusSection(),
1706 static void DotLocalTestCmd( void );
1708 static const char * gDotLocalTest_Interface
= NULL
;
1709 static const char * gDotLocalTest_OutputFormat
= kOutputFormatStr_JSON
;
1710 static const char * gDotLocalTest_OutputFilePath
= NULL
;
1712 #define kDotLocalTestSubtestDesc_GAIMDNSOnly "GAI for a dotlocal name that has only MDNS A and AAAA records."
1713 #define kDotLocalTestSubtestDesc_GAIDNSOnly "GAI for a dotlocal name that has only DNS A and AAAA records."
1714 #define kDotLocalTestSubtestDesc_GAIBoth "GAI for a dotlocal name that has both mDNS and DNS A and AAAA records."
1715 #define kDotLocalTestSubtestDesc_GAINeither "GAI for a dotlocal name that has no A or AAAA records."
1716 #define kDotLocalTestSubtestDesc_GAINoSuchRecord \
1717 "GAI for a dotlocal name that has no A or AAAA records, but is a subdomain name of a search domain."
1718 #define kDotLocalTestSubtestDesc_QuerySRV "SRV query for a dotlocal name that has only a DNS SRV record."
1720 #define kDotLocalTestSectionText_Description \
1721 "The goal of the dotlocal test is to verify that mDNSResponder properly handles queries for domain names in the\n" \
1722 "local domain when a local SOA record exists. As part of the test setup, a test DNS server and an mdnsreplier are\n" \
1723 "spawned, and a dummy local SOA record is registered with DNSServiceRegisterRecord(). The server is invoked such\n" \
1724 "that its domain is a second-level subdomain of the local domain, i.e., <some label>.local, while the mdnsreplier is\n" \
1725 "invoked such that its base hostname is equal to the server's domain, e.g., if the server's domain is test.local.,\n" \
1726 "then the mdnsreplier's base hostname is test.local.\n" \
1728 "The dotlocal test consists of six subtests that perform either a DNSServiceGetAddrInfo (GAI) operation for a\n" \
1729 "hostname in the local domain or a DNSServiceQueryRecord operation to query for an SRV record in the local domain:\n" \
1731 "1. " kDotLocalTestSubtestDesc_GAIMDNSOnly "\n" \
1732 "2. " kDotLocalTestSubtestDesc_GAIDNSOnly "\n" \
1733 "3. " kDotLocalTestSubtestDesc_GAIBoth "\n" \
1734 "4. " kDotLocalTestSubtestDesc_GAINeither "\n" \
1735 "5. " kDotLocalTestSubtestDesc_GAINoSuchRecord "\n" \
1736 "6. " kDotLocalTestSubtestDesc_QuerySRV "\n" \
1738 "Each subtest runs for five seconds.\n"
1740 static CLIOption kDotLocalTestOpts
[] =
1742 StringOption( 'i', "interface", &gDotLocalTest_Interface
, "name or index", "mdnsreplier's network interface. If not set, any mDNS-capable interface will be used.", false ),
1744 CLI_OPTION_GROUP( "Results" ),
1745 FormatOption( 'f', "format", &gDotLocalTest_OutputFormat
, "Specifies the test results output format. (default: " kOutputFormatStr_JSON
")", false ),
1746 StringOption( 'o', "output", &gDotLocalTest_OutputFilePath
, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1748 CLI_SECTION( "Description", kDotLocalTestSectionText_Description
),
1749 TestExitStatusSection(),
1753 static void ProbeConflictTestCmd( void );
1755 static const char * gProbeConflictTest_Interface
= NULL
;
1756 static int gProbeConflictTest_UseComputerName
= false;
1757 static const char * gProbeConflictTest_OutputFormat
= kOutputFormatStr_JSON
;
1758 static const char * gProbeConflictTest_OutputFilePath
= NULL
;
1760 static CLIOption kProbeConflictTestOpts
[] =
1762 StringOption( 'i', "interface", &gProbeConflictTest_Interface
, "name or index", "mdnsreplier's network interface. If not set, any mDNS-capable interface will be used.", false ),
1763 BooleanOption( 'c', "useComputerName", &gProbeConflictTest_UseComputerName
, "Use the device's \"computer name\" for the test service's name." ),
1765 CLI_OPTION_GROUP( "Results" ),
1766 FormatOption( 'f', "format", &gProbeConflictTest_OutputFormat
, "Specifies the test report output format. (default: " kOutputFormatStr_JSON
")", false ),
1767 StringOption( 'o', "output", &gProbeConflictTest_OutputFilePath
, "path", "Path of the file to write test report to instead of standard output (stdout).", false ),
1769 TestExitStatusSection(),
1773 static void RegistrationTestCmd( void );
1775 static int gRegistrationTest_BATSEnvironment
= false;
1776 static const char * gRegistrationTest_OutputFormat
= kOutputFormatStr_JSON
;
1777 static const char * gRegistrationTest_OutputFilePath
= NULL
;
1779 static CLIOption kRegistrationTestOpts
[] =
1781 CLI_OPTION_BOOLEAN( 0, "bats", &gRegistrationTest_BATSEnvironment
, "Informs the test that it's running in a BATS environment.",
1783 "This option allows the test to take special measures while running in a BATS environment. Currently, this option\n"
1784 "only has an effect on watchOS. Because it has been observed that the Wi-Fi interface sometimes goes down during\n"
1785 "watchOS BATS testing, for watchOS, when a service is registered using kDNSServiceInterfaceIndexAny,\n"
1787 " 1. missing browse and query \"add\" results for Wi-Fi interfaces aren't enough for a subtest to fail; and\n"
1788 " 2. unexpected browse and query results for Wi-Fi interfaces are ignored.\n"
1790 CLI_OPTION_GROUP( "Results" ),
1791 FormatOption( 'f', "format", &gRegistrationTest_OutputFormat
, "Specifies the test results output format. (default: " kOutputFormatStr_JSON
")", false ),
1792 StringOption( 'o', "output", &gRegistrationTest_OutputFilePath
, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1794 TestExitStatusSection(),
1798 static void ExpensiveConstrainedTestCmd( void );
1800 static const char * gExpensiveConstrainedTest_Interface
= NULL
;
1801 static const char * gExpensiveConstrainedTest_Name
= NULL
;
1802 static Boolean gExpensiveConstrainedTest_DenyExpensive
= false;
1803 static Boolean gExpensiveConstrainedTest_DenyConstrained
= false;
1804 static Boolean gExpensiveConstrainedTest_StartFromExpensive
= false;
1805 static int gExpensiveConstrainedTest_ProtocolIPv4
= false;
1806 static int gExpensiveConstrainedTest_ProtocolIPv6
= false;
1807 static const char * gExpensiveConstrainedTest_OutputFormat
= kOutputFormatStr_JSON
;
1808 static const char * gExpensiveConstrainedTest_OutputFilePath
= NULL
;
1810 static CLIOption kExpensiveConstrainedTestOpts
[] =
1812 CLI_OPTION_GROUP( "Results" ),
1813 FormatOption( 'f', "format", &gExpensiveConstrainedTest_OutputFormat
, "Specifies the test results output format. (default: " kOutputFormatStr_JSON
")", false ),
1814 StringOption( 'o', "output", &gExpensiveConstrainedTest_OutputFilePath
, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1816 TestExitStatusSection(),
1820 #if( MDNSRESPONDER_PROJECT )
1821 static void XCTestCmd( void );
1823 static const char * gXCTest_Classname
= NULL
;
1825 static CLIOption kXCTestOpts
[] =
1827 StringOption( 'c', "class", &gXCTest_Classname
, "classname", "The classname of the XCTest to run (from /AppleInternal/XCTests/com.apple.mDNSResponder/Tests.xctest)", true ),
1832 #if( TARGET_OS_DARWIN )
1833 static void KeepAliveTestCmd( void );
1835 static const char * gKeepAliveTest_OutputFormat
= kOutputFormatStr_JSON
;
1836 static const char * gKeepAliveTest_OutputFilePath
= NULL
;
1838 static CLIOption kKeepAliveTestOpts
[] =
1840 CLI_OPTION_GROUP( "Results" ),
1841 FormatOption( 'f', "format", &gKeepAliveTest_OutputFormat
, "Specifies the test results output format. (default: " kOutputFormatStr_JSON
")", false ),
1842 StringOption( 'o', "output", &gKeepAliveTest_OutputFilePath
, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1844 TestExitStatusSection(),
1849 static CLIOption kTestOpts
[] =
1851 Command( "gaiperf", GAIPerfCmd
, kGAIPerfOpts
, "Runs DNSServiceGetAddrInfo() performance tests.", false ),
1852 Command( "mdnsdiscovery", MDNSDiscoveryTestCmd
, kMDNSDiscoveryTestOpts
, "Tests mDNS service discovery for correctness.", false ),
1853 Command( "dotlocal", DotLocalTestCmd
, kDotLocalTestOpts
, "Tests DNS and mDNS queries for domain names in the local domain.", false ),
1854 Command( "probeconflicts", ProbeConflictTestCmd
, kProbeConflictTestOpts
, "Tests various probing conflict scenarios.", false ),
1855 Command( "registration", RegistrationTestCmd
, kRegistrationTestOpts
, "Tests service registrations.", false ),
1856 Command( "expensive_constrained_updates", ExpensiveConstrainedTestCmd
, kExpensiveConstrainedTestOpts
, "Tests if the mDNSResponder can handle expensive and constrained property change correctly", false),
1857 #if( MDNSRESPONDER_PROJECT )
1858 Command( "xctest", XCTestCmd
, kXCTestOpts
, "Run a XCTest from /AppleInternal/XCTests/com.apple.mDNSResponder/Tests.xctest.", true ),
1860 #if( TARGET_OS_DARWIN )
1861 Command( "keepalive", KeepAliveTestCmd
, kKeepAliveTestOpts
, "Tests keepalive record registrations.", false ),
1866 //===========================================================================================================================
1867 // SSDP Command Options
1868 //===========================================================================================================================
1870 static int gSSDPDiscover_MX
= 1;
1871 static const char * gSSDPDiscover_ST
= "ssdp:all";
1872 static int gSSDPDiscover_ReceiveSecs
= 1;
1873 static int gSSDPDiscover_UseIPv4
= false;
1874 static int gSSDPDiscover_UseIPv6
= false;
1875 static int gSSDPDiscover_Verbose
= false;
1877 static CLIOption kSSDPDiscoverOpts
[] =
1879 StringOption( 'i', "interface", &gInterface
, "name or index", "Network interface by name or index.", true ),
1880 IntegerOption( 'm', "mx", &gSSDPDiscover_MX
, "seconds", "MX value in search request, i.e., max response delay in seconds. (Default: 1 second)", false ),
1881 StringOption( 's', "st", &gSSDPDiscover_ST
, "string", "ST value in search request, i.e., the search target. (Default: \"ssdp:all\")", false ),
1882 IntegerOption( 'r', "receiveTime", &gSSDPDiscover_ReceiveSecs
, "seconds", "Amount of time to spend receiving responses. -1 means unlimited. (Default: 1 second)", false ),
1883 BooleanOption( 0 , "ipv4", &gSSDPDiscover_UseIPv4
, "Use IPv4, i.e., multicast to 239.255.255.250:1900." ),
1884 BooleanOption( 0 , "ipv6", &gSSDPDiscover_UseIPv6
, "Use IPv6, i.e., multicast to [ff02::c]:1900" ),
1885 BooleanOption( 'v', "verbose", &gSSDPDiscover_Verbose
, "Prints the search request(s) that were sent." ),
1889 static void SSDPDiscoverCmd( void );
1891 static CLIOption kSSDPOpts
[] =
1893 Command( "discover", SSDPDiscoverCmd
, kSSDPDiscoverOpts
, "Crafts and multicasts an SSDP search message.", false ),
1897 #if( TARGET_OS_DARWIN )
1898 //===========================================================================================================================
1899 // res_query Command Options
1900 //===========================================================================================================================
1902 static void ResQueryCmd( void );
1904 static const char * gResQuery_Name
= NULL
;
1905 static const char * gResQuery_Type
= NULL
;
1906 static const char * gResQuery_Class
= NULL
;
1907 static int gResQuery_UseLibInfo
= false;
1909 static CLIOption kResQueryOpts
[] =
1911 StringOption( 'n', "name", &gResQuery_Name
, "domain name", "Full domain name of record to query.", true ),
1912 StringOption( 't', "type", &gResQuery_Type
, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
1913 StringOption( 'c', "class", &gResQuery_Class
, "record class", "Record class by name or number. Default class is IN.", false ),
1914 BooleanOption( 0 , "libinfo", &gResQuery_UseLibInfo
, "Use res_query from libinfo instead of libresolv." ),
1918 //===========================================================================================================================
1919 // dns_query Command Options
1920 //===========================================================================================================================
1922 static void ResolvDNSQueryCmd( void );
1924 static const char * gResolvDNSQuery_Name
= NULL
;
1925 static const char * gResolvDNSQuery_Type
= NULL
;
1926 static const char * gResolvDNSQuery_Class
= NULL
;
1927 static const char * gResolvDNSQuery_Path
= NULL
;
1929 static CLIOption kResolvDNSQueryOpts
[] =
1931 StringOption( 'n', "name", &gResolvDNSQuery_Name
, "domain name", "Full domain name of record to query.", true ),
1932 StringOption( 't', "type", &gResolvDNSQuery_Type
, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
1933 StringOption( 'c', "class", &gResolvDNSQuery_Class
, "record class", "Record class by name or number. Default class is IN.", false ),
1934 StringOption( 'p', "path", &gResolvDNSQuery_Path
, "file path", "The path argument to pass to dns_open() before calling dns_query(). Default value is NULL.", false ),
1938 //===========================================================================================================================
1939 // CFHost Command Options
1940 //===========================================================================================================================
1942 static void CFHostCmd( void );
1944 static const char * gCFHost_Name
= NULL
;
1945 static int gCFHost_WaitSecs
= 0;
1947 static CLIOption kCFHostOpts
[] =
1949 StringOption( 'n', "name", &gCFHost_Name
, "hostname", "Hostname to resolve.", true ),
1950 IntegerOption( 'w', "wait", &gCFHost_WaitSecs
, "seconds", "Time in seconds to wait before a normal exit. (default: 0)", false ),
1954 static CLIOption kLegacyOpts
[] =
1956 Command( "res_query", ResQueryCmd
, kResQueryOpts
, "Uses res_query() from either libresolv or libinfo to query for a record.", true ),
1957 Command( "dns_query", ResolvDNSQueryCmd
, kResolvDNSQueryOpts
, "Uses dns_query() from libresolv to query for a record.", true ),
1958 Command( "cfhost", CFHostCmd
, kCFHostOpts
, "Uses CFHost to resolve a hostname.", true ),
1962 //===========================================================================================================================
1963 // DNSConfigAdd Command Options
1964 //===========================================================================================================================
1966 static void DNSConfigAddCmd( void );
1968 static CFStringRef gDNSConfigAdd_ID
= NULL
;
1969 static char ** gDNSConfigAdd_IPAddrArray
= NULL
;
1970 static size_t gDNSConfigAdd_IPAddrCount
= 0;
1971 static char ** gDNSConfigAdd_DomainArray
= NULL
;
1972 static size_t gDNSConfigAdd_DomainCount
= 0;
1973 static const char * gDNSConfigAdd_Interface
= NULL
;
1975 static CLIOption kDNSConfigAddOpts
[] =
1977 CFStringOption( 0 , "id", &gDNSConfigAdd_ID
, "ID", "Arbitrary ID to use for resolver entry.", true ),
1978 MultiStringOption( 'a', "address", &gDNSConfigAdd_IPAddrArray
, &gDNSConfigAdd_IPAddrCount
, "IP address", "DNS server IP address(es). Can be specified more than once.", true ),
1979 MultiStringOption( 'd', "domain", &gDNSConfigAdd_DomainArray
, &gDNSConfigAdd_DomainCount
, "domain", "Specific domain(s) for the resolver entry. Can be specified more than once.", false ),
1980 StringOption( 'i', "interface", &gDNSConfigAdd_Interface
, "interface name", "Specific interface for the resolver entry.", false ),
1982 CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ),
1986 //===========================================================================================================================
1987 // DNSConfigRemove Command Options
1988 //===========================================================================================================================
1990 static void DNSConfigRemoveCmd( void );
1992 static CFStringRef gDNSConfigRemove_ID
= NULL
;
1994 static CLIOption kDNSConfigRemoveOpts
[] =
1996 CFStringOption( 0, "id", &gDNSConfigRemove_ID
, "ID", "ID of resolver entry to remove.", true ),
1998 CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ),
2002 static CLIOption kDNSConfigOpts
[] =
2004 Command( "add", DNSConfigAddCmd
, kDNSConfigAddOpts
, "Add a supplemental resolver entry to the system's DNS configuration.", true ),
2005 Command( "remove", DNSConfigRemoveCmd
, kDNSConfigRemoveOpts
, "Remove a supplemental resolver entry from the system's DNS configuration.", true ),
2009 //===========================================================================================================================
2011 //===========================================================================================================================
2013 static void XPCSendCmd( void );
2015 static const char * gXPCSend_ServiceName
= NULL
;
2016 static const char * gXPCSend_MessageStr
= NULL
;
2018 static const char kXPCSendMessageSection_Name
[] = "Message Argument";
2019 static const char kXPCSendMessageSection_Text
[] =
2020 "XPC messages are described as a string using the following syntax.\n"
2022 "With the exception of the top-most XPC message dictionary, dictionaries begin with a '{' and end with a '}'.\n"
2023 "Key-value pairs are of the form <key>=<value>, where <key> is a string and <value> is a value of any of the\n"
2024 "currently supported XPC types.\n"
2026 "Arrays begin with a '[' and end with a ']'.\n"
2028 "The following non-container XPC types are supported:\n"
2030 "Type Syntax Example\n"
2031 "bool bool:<string> bool:true (or yes/y/on/1), bool:false (or no/n/off/0)\n"
2032 "data data:<hex string> data:C0000201\n"
2033 "int64 (signed 64-bit integer) int:<pos. or neg. integer> int:-1\n"
2034 "string string:<string> string:hello\\ world\n"
2035 "uint64 (unsigned 64-bit integer) uint:<pos. integer> uint:1024 or uint:0x400\n"
2036 "UUID uuid:<UUID> uuid:dab10183-84b5-4859-9de6-4bee287cfea3\n"
2038 "Example 1: 'cmd=string:add make=string:Apple model=string:Macintosh aliases=[string:Mac string:Macintosh\\ 128K]'\n"
2039 "Example 2: 'cmd=string:search features={portable=bool:yes solar=bool:no} priceMin=uint:100 priceMax=uint:200'\n";
2041 static CLIOption kXPCSendOpts
[] =
2043 StringOption( 's', "service", &gXPCSend_ServiceName
, "service name", "XPC service name.", true ),
2044 StringOption( 'm', "message", &gXPCSend_MessageStr
, "message", "XPC message as a string.", true ),
2046 CLI_SECTION( kXPCSendMessageSection_Name
, kXPCSendMessageSection_Text
),
2049 #endif // TARGET_OS_DARWIN
2051 #if( MDNSRESPONDER_PROJECT )
2052 //===========================================================================================================================
2054 //===========================================================================================================================
2056 static void InterfaceMonitorCmd( void );
2058 static CLIOption kInterfaceMonitorOpts
[] =
2060 StringOption( 'i', "interface", &gInterface
, "name or index", "Network interface by name or index.", true ),
2064 //===========================================================================================================================
2066 //===========================================================================================================================
2068 static void DNSProxyCmd( void );
2070 static char ** gDNSProxy_InputInterfaces
= NULL
;
2071 static size_t gDNSProxy_InputInterfaceCount
= 0;
2072 static const char * gDNSProxy_OutputInterface
= NULL
;
2074 static CLIOption kDNSProxyOpts
[] =
2076 MultiStringOption( 'i', "inputInterface", &gDNSProxy_InputInterfaces
, &gDNSProxy_InputInterfaceCount
, "name or index", "Interface to accept queries on. Can be specified more than once.", true ),
2077 StringOption( 'o', "outputInterface", &gDNSProxy_OutputInterface
, "name or index", "Interface to forward queries over. Use '0' for primary interface. (default: 0)", false ),
2081 #endif // MDNSRESPONDER_PROJECT
2083 //===========================================================================================================================
2085 //===========================================================================================================================
2087 static OSStatus
VersionOptionCallback( CLIOption
*inOption
, const char *inArg
, int inUnset
);
2089 static void BrowseCmd( void );
2090 static void GetAddrInfoCmd( void );
2091 static void QueryRecordCmd( void );
2092 static void RegisterCmd( void );
2093 static void RegisterRecordCmd( void );
2094 static void ResolveCmd( void );
2095 static void ReconfirmCmd( void );
2096 static void GetAddrInfoPOSIXCmd( void );
2097 static void ReverseLookupCmd( void );
2098 static void PortMappingCmd( void );
2099 static void BrowseAllCmd( void );
2100 static void GetAddrInfoStressCmd( void );
2101 static void DNSQueryCmd( void );
2102 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
2103 static void DNSCryptCmd( void );
2105 static void MDNSQueryCmd( void );
2106 static void PIDToUUIDCmd( void );
2107 static void DaemonVersionCmd( void );
2109 static CLIOption kGlobalOpts
[] =
2111 CLI_OPTION_CALLBACK_EX( 'V', "version", VersionOptionCallback
, NULL
, NULL
,
2112 kCLIOptionFlags_NoArgument
| kCLIOptionFlags_GlobalOnly
, "Displays the version of this tool.", NULL
),
2117 Command( "browse", BrowseCmd
, kBrowseOpts
, "Uses DNSServiceBrowse() to browse for one or more service types.", false ),
2118 Command( "getAddrInfo", GetAddrInfoCmd
, kGetAddrInfoOpts
, "Uses DNSServiceGetAddrInfo() to resolve a hostname to IP addresses.", false ),
2119 Command( "queryRecord", QueryRecordCmd
, kQueryRecordOpts
, "Uses DNSServiceQueryRecord() to query for an arbitrary DNS record.", false ),
2120 Command( "register", RegisterCmd
, kRegisterOpts
, "Uses DNSServiceRegister() to register a service.", false ),
2121 Command( "registerRecord", RegisterRecordCmd
, kRegisterRecordOpts
, "Uses DNSServiceRegisterRecord() to register a record.", false ),
2122 Command( "resolve", ResolveCmd
, kResolveOpts
, "Uses DNSServiceResolve() to resolve a service.", false ),
2123 Command( "reconfirm", ReconfirmCmd
, kReconfirmOpts
, "Uses DNSServiceReconfirmRecord() to reconfirm a record.", false ),
2124 Command( "getaddrinfo-posix", GetAddrInfoPOSIXCmd
, kGetAddrInfoPOSIXOpts
, "Uses getaddrinfo() to resolve a hostname to IP addresses.", false ),
2125 Command( "reverseLookup", ReverseLookupCmd
, kReverseLookupOpts
, "Uses DNSServiceQueryRecord() to perform a reverse IP address lookup.", false ),
2126 Command( "portMapping", PortMappingCmd
, kPortMappingOpts
, "Uses DNSServiceNATPortMappingCreate() to create a port mapping.", false ),
2127 #if( TARGET_OS_DARWIN )
2128 Command( "registerKA", RegisterKACmd
, kRegisterKA_Opts
, "Uses DNSServiceSleepKeepalive_sockaddr() to register a keep alive record.", false ),
2130 Command( "browseAll", BrowseAllCmd
, kBrowseAllOpts
, "Browse and resolve all (or specific) services and, optionally, attempt connections.", false ),
2132 // Uncommon commands.
2134 Command( "getnameinfo", GetNameInfoCmd
, kGetNameInfoOpts
, "Calls getnameinfo() and prints results.", true ),
2135 Command( "getAddrInfoStress", GetAddrInfoStressCmd
, kGetAddrInfoStressOpts
, "Runs DNSServiceGetAddrInfo() stress testing.", true ),
2136 Command( "DNSQuery", DNSQueryCmd
, kDNSQueryOpts
, "Crafts and sends a DNS query.", true ),
2137 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
2138 Command( "DNSCrypt", DNSCryptCmd
, kDNSCryptOpts
, "Crafts and sends a DNSCrypt query.", true ),
2140 Command( "mdnsquery", MDNSQueryCmd
, kMDNSQueryOpts
, "Crafts and sends an mDNS query over the specified interface.", true ),
2141 Command( "mdnscollider", MDNSColliderCmd
, kMDNSColliderOpts
, "Creates record name collision scenarios.", true ),
2142 Command( "pid2uuid", PIDToUUIDCmd
, kPIDToUUIDOpts
, "Prints the UUID of a process.", true ),
2143 Command( "server", DNSServerCmd
, kDNSServerOpts
, "DNS server for testing.", true ),
2144 Command( "mdnsreplier", MDNSReplierCmd
, kMDNSReplierOpts
, "Responds to mDNS queries for a set of authoritative resource records.", true ),
2145 Command( "test", NULL
, kTestOpts
, "Commands for testing DNS-SD.", true ),
2146 Command( "ssdp", NULL
, kSSDPOpts
, "Simple Service Discovery Protocol (SSDP).", true ),
2147 #if( TARGET_OS_DARWIN )
2148 Command( "legacy", NULL
, kLegacyOpts
, "Legacy DNS API.", true ),
2149 Command( "dnsconfig", NULL
, kDNSConfigOpts
, "Add/remove a supplemental resolver entry to/from the system's DNS configuration.", true ),
2150 Command( "xpcsend", XPCSendCmd
, kXPCSendOpts
, "Sends a message to an XPC service.", true ),
2152 #if( MDNSRESPONDER_PROJECT )
2153 Command( "interfaceMonitor", InterfaceMonitorCmd
, kInterfaceMonitorOpts
, "mDNSResponder's interface monitor.", true ),
2154 Command( "dnsproxy", DNSProxyCmd
, kDNSProxyOpts
, "Enables mDNSResponder's DNS proxy.", true ),
2156 Command( "daemonVersion", DaemonVersionCmd
, NULL
, "Prints the version of the DNS-SD daemon.", true ),
2162 //===========================================================================================================================
2163 // Helper Prototypes
2164 //===========================================================================================================================
2166 #define kExitReason_OneShotDone "one-shot done"
2167 #define kExitReason_ReceivedResponse "received response"
2168 #define kExitReason_SIGINT "interrupt signal"
2169 #define kExitReason_Timeout "timeout"
2170 #define kExitReason_TimeLimit "time limit"
2172 static void Exit( void *inContext
) ATTRIBUTE_NORETURN
;
2174 static DNSServiceFlags
GetDNSSDFlagsFromOpts( void );
2178 kConnectionType_None
= 0,
2179 kConnectionType_Normal
= 1,
2180 kConnectionType_DelegatePID
= 2,
2181 kConnectionType_DelegateUUID
= 3
2187 ConnectionType type
;
2198 CreateConnectionFromArgString(
2199 const char * inString
,
2200 dispatch_queue_t inQueue
,
2201 DNSServiceRef
* outSDRef
,
2202 ConnectionDesc
* outDesc
);
2203 static OSStatus
InterfaceIndexFromArgString( const char *inString
, uint32_t *outIndex
);
2204 static OSStatus
RecordDataFromArgString( const char *inString
, uint8_t **outDataPtr
, size_t *outDataLen
);
2205 static OSStatus
RecordTypeFromArgString( const char *inString
, uint16_t *outValue
);
2206 static OSStatus
RecordClassFromArgString( const char *inString
, uint16_t *outValue
);
2208 #define kInterfaceNameBufLen ( Max( IF_NAMESIZE, 16 ) + 1 )
2210 static char * InterfaceIndexToName( uint32_t inIfIndex
, char inNameBuf
[ kInterfaceNameBufLen
] );
2211 static const char * RecordTypeToString( unsigned int inValue
);
2214 DNSRecordDataToString(
2215 const void * inRDataPtr
,
2217 unsigned int inRDataType
,
2218 const void * inMsgPtr
,
2220 char ** outString
);
2224 const uint8_t * inMsgPtr
,
2231 WriteDNSQueryMessage(
2232 uint8_t inMsg
[ kDNSQueryMessageMaxLen
],
2235 const char * inQName
,
2238 size_t * outMsgLen
);
2242 typedef void ( *DispatchHandler
)( void *inContext
);
2245 DispatchSignalSourceCreate(
2247 dispatch_queue_t inQueue
,
2248 DispatchHandler inEventHandler
,
2250 dispatch_source_t
* outSource
);
2252 DispatchSocketSourceCreate(
2254 dispatch_source_type_t inType
,
2255 dispatch_queue_t inQueue
,
2256 DispatchHandler inEventHandler
,
2257 DispatchHandler inCancelHandler
,
2259 dispatch_source_t
* outSource
);
2261 #define DispatchReadSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
2262 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_READ, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
2264 #define DispatchWriteSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
2265 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_WRITE, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
2268 DispatchTimerCreate(
2269 dispatch_time_t inStart
,
2270 uint64_t inIntervalNs
,
2271 uint64_t inLeewayNs
,
2272 dispatch_queue_t inQueue
,
2273 DispatchHandler inEventHandler
,
2274 DispatchHandler inCancelHandler
,
2276 dispatch_source_t
* outTimer
);
2278 #define DispatchTimerOneShotCreate( IN_START, IN_LEEWAY, IN_QUEUE, IN_EVENT_HANDLER, IN_CONTEXT, OUT_TIMER ) \
2279 DispatchTimerCreate( IN_START, DISPATCH_TIME_FOREVER, IN_LEEWAY, IN_QUEUE, IN_EVENT_HANDLER, NULL, IN_CONTEXT, OUT_TIMER )
2282 DispatchProcessMonitorCreate(
2284 unsigned long inFlags
,
2285 dispatch_queue_t inQueue
,
2286 DispatchHandler inEventHandler
,
2287 DispatchHandler inCancelHandler
,
2289 dispatch_source_t
* outMonitor
);
2291 static const char * ServiceTypeDescription( const char *inName
);
2295 SocketRef sock
; // Socket.
2296 void * userContext
; // User context.
2297 int32_t refCount
; // Reference count.
2301 static OSStatus
SocketContextCreate( SocketRef inSock
, void * inUserContext
, SocketContext
**outContext
);
2302 static SocketContext
* SocketContextRetain( SocketContext
*inContext
);
2303 static void SocketContextRelease( SocketContext
*inContext
);
2304 static void SocketContextCancelHandler( void *inContext
);
2306 #define ForgetSocketContext( X ) ForgetCustom( X, SocketContextRelease )
2308 static OSStatus
StringToInt32( const char *inString
, int32_t *outValue
);
2309 static OSStatus
StringToUInt32( const char *inString
, uint32_t *outValue
);
2310 #if( TARGET_OS_DARWIN )
2311 static int64_t _StringToInt64( const char *inString
, OSStatus
*outError
);
2312 static uint64_t _StringToUInt64( const char *inString
, OSStatus
*outError
);
2313 static pid_t
_StringToPID( const char *inString
, OSStatus
*outError
);
2315 _ParseEscapedString(
2318 const char * inDelimiters
,
2321 size_t * outCopiedLen
,
2322 size_t * outActualLen
,
2323 const char ** outPtr
);
2325 static OSStatus
StringToARecordData( const char *inString
, uint8_t **outPtr
, size_t *outLen
);
2326 static OSStatus
StringToAAAARecordData( const char *inString
, uint8_t **outPtr
, size_t *outLen
);
2327 static OSStatus
StringToDomainName( const char *inString
, uint8_t **outPtr
, size_t *outLen
);
2328 #if( TARGET_OS_DARWIN )
2329 static OSStatus
GetDefaultDNSServer( sockaddr_ip
*outAddr
);
2332 _ServerSocketOpenEx2(
2336 const void * inAddr
,
2340 Boolean inNoPortReuse
,
2341 SocketRef
* outSock
);
2343 static const struct sockaddr
* GetMDNSMulticastAddrV4( void );
2344 static const struct sockaddr
* GetMDNSMulticastAddrV6( void );
2347 CreateMulticastSocket(
2348 const struct sockaddr
* inAddr
,
2350 const char * inIfName
,
2354 SocketRef
* outSock
);
2356 static OSStatus
DecimalTextToUInt32( const char *inSrc
, const char *inEnd
, uint32_t *outValue
, const char **outPtr
);
2357 static OSStatus
CheckIntegerArgument( int inArgValue
, const char *inArgName
, int inMin
, int inMax
);
2358 static OSStatus
CheckDoubleArgument( double inArgValue
, const char *inArgName
, double inMin
, double inMax
);
2359 static OSStatus
CheckRootUser( void );
2360 static OSStatus
SpawnCommand( pid_t
*outPID
, const char *inFormat
, ... );
2361 static OSStatus
OutputFormatFromArgString( const char *inArgString
, OutputFormatType
*outFormat
);
2362 static OSStatus
OutputPropertyList( CFPropertyListRef inPList
, OutputFormatType inType
, const char *inOutputFilePath
);
2363 static OSStatus
CreateSRVRecordDataFromString( const char *inString
, uint8_t **outPtr
, size_t *outLen
);
2364 static OSStatus
CreateTXTRecordDataFromString( const char *inString
, int inDelimiter
, uint8_t **outPtr
, size_t *outLen
);
2366 CreateNSECRecordData(
2367 const uint8_t * inNextDomainName
,
2370 unsigned int inTypeCount
,
2375 const uint8_t * inNamePtr
,
2380 const uint8_t * inMName
,
2381 const uint8_t * inRName
,
2386 uint32_t inMinimumTTL
,
2389 CreateSOARecordData(
2390 const uint8_t * inMName
,
2391 const uint8_t * inRName
,
2396 uint32_t inMinimumTTL
,
2400 _DataBuffer_AppendDNSQuestion(
2402 const uint8_t * inNamePtr
,
2407 _DataBuffer_AppendDNSRecord(
2409 const uint8_t * inNamePtr
,
2414 const uint8_t * inRDataPtr
,
2415 size_t inRDataLen
);
2416 static char * _NanoTime64ToTimestamp( NanoTime64 inTime
, char *inBuf
, size_t inMaxLen
);
2418 typedef struct MDNSInterfaceItem MDNSInterfaceItem
;
2419 struct MDNSInterfaceItem
2421 MDNSInterfaceItem
* next
;
2432 kMDNSInterfaceSubset_All
= 0, // All mDNS-capable interfaces.
2433 kMDNSInterfaceSubset_AWDL
= 1, // All mDNS-capable AWDL interfaces.
2434 kMDNSInterfaceSubset_NonAWDL
= 2 // All mDNS-capable non-AWDL iterfaces.
2436 } MDNSInterfaceSubset
;
2438 static OSStatus
_MDNSInterfaceListCreate( MDNSInterfaceSubset inSubset
, size_t inItemSize
, MDNSInterfaceItem
**outList
);
2439 static void _MDNSInterfaceListFree( MDNSInterfaceItem
*inList
);
2440 #define _MDNSInterfaceListForget( X ) ForgetCustom( X, _MDNSInterfaceListFree )
2441 static OSStatus
_MDNSInterfaceGetAny( MDNSInterfaceSubset inSubset
, char inNameBuf
[ IF_NAMESIZE
+ 1 ], uint32_t *outIndex
);
2443 static OSStatus
_SetComputerName( CFStringRef inComputerName
, CFStringEncoding inEncoding
);
2444 static OSStatus
_SetComputerNameWithUTF8CString( const char *inComputerName
);
2445 static OSStatus
_SetLocalHostName( CFStringRef inLocalHostName
);
2446 static OSStatus
_SetLocalHostNameWithUTF8CString( const char *inLocalHostName
);
2448 static OSStatus
_SocketWriteAll( SocketRef inSock
, const void *inData
, size_t inSize
, int32_t inTimeoutSecs
);
2450 _StringToIPv4Address(
2452 StringToIPAddressFlags inFlags
,
2455 uint32_t * outSubnet
,
2456 uint32_t * outRouter
,
2457 const char ** outStr
);
2458 static void _StringArray_Free( char **inArray
, size_t inCount
);
2460 _StringToIPv6Address(
2462 StringToIPAddressFlags inFlags
,
2463 uint8_t outIPv6
[ 16 ],
2464 uint32_t * outScope
,
2467 const char ** outStr
);
2469 _ParseQuotedEscapedString(
2472 const char * inDelimiters
,
2475 size_t * outCopiedLen
,
2476 size_t * outTotalLen
,
2477 const char ** outSrc
);
2478 static void * _memdup( const void *inPtr
, size_t inLen
);
2479 static int _memicmp( const void *inP1
, const void *inP2
, size_t inLen
);
2480 static uint32_t _FNV1( const void *inData
, size_t inSize
);
2482 #define Unused( X ) (void)(X)
2484 //===========================================================================================================================
2486 //===========================================================================================================================
2488 typedef struct MDNSColliderPrivate
* MDNSColliderRef
;
2490 typedef uint32_t MDNSColliderProtocols
;
2491 #define kMDNSColliderProtocol_None 0
2492 #define kMDNSColliderProtocol_IPv4 ( 1 << 0 )
2493 #define kMDNSColliderProtocol_IPv6 ( 1 << 1 )
2495 typedef void ( *MDNSColliderStopHandler_f
)( void *inContext
, OSStatus inError
);
2497 static OSStatus
MDNSColliderCreate( dispatch_queue_t inQueue
, MDNSColliderRef
*outCollider
);
2498 static OSStatus
MDNSColliderStart( MDNSColliderRef inCollider
);
2499 static void MDNSColliderStop( MDNSColliderRef inCollider
);
2500 static void MDNSColliderSetProtocols( MDNSColliderRef inCollider
, MDNSColliderProtocols inProtocols
);
2501 static void MDNSColliderSetInterfaceIndex( MDNSColliderRef inCollider
, uint32_t inInterfaceIndex
);
2502 static OSStatus
MDNSColliderSetProgram( MDNSColliderRef inCollider
, const char *inProgramStr
);
2504 MDNSColliderSetStopHandler(
2505 MDNSColliderRef inCollider
,
2506 MDNSColliderStopHandler_f inStopHandler
,
2507 void * inStopContext
);
2509 MDNSColliderSetRecord(
2510 MDNSColliderRef inCollider
,
2511 const uint8_t * inName
,
2513 const void * inRDataPtr
,
2514 size_t inRDataLen
);
2515 static CFTypeID
MDNSColliderGetTypeID( void );
2517 //===========================================================================================================================
2519 //===========================================================================================================================
2521 typedef struct ServiceBrowserPrivate
* ServiceBrowserRef
;
2522 typedef struct ServiceBrowserResults ServiceBrowserResults
;
2523 typedef struct SBRDomain SBRDomain
;
2524 typedef struct SBRServiceType SBRServiceType
;
2525 typedef struct SBRServiceInstance SBRServiceInstance
;
2526 typedef struct SBRIPAddress SBRIPAddress
;
2528 typedef void ( *ServiceBrowserCallback_f
)( ServiceBrowserResults
*inResults
, OSStatus inError
, void *inContext
);
2530 struct ServiceBrowserResults
2532 SBRDomain
* domainList
; // List of domains in which services were found.
2537 SBRDomain
* next
; // Next domain in list.
2538 char * name
; // Name of domain represented by this object.
2539 SBRServiceType
* typeList
; // List of service types in this domain.
2542 struct SBRServiceType
2544 SBRServiceType
* next
; // Next service type in list.
2545 char * name
; // Name of service type represented by this object.
2546 SBRServiceInstance
* instanceList
; // List of service instances of this service type.
2549 struct SBRServiceInstance
2551 SBRServiceInstance
* next
; // Next service instance in list.
2552 char * name
; // Name of service instance represented by this object.
2553 char * hostname
; // Target from service instance's SRV record.
2554 uint32_t ifIndex
; // Index of interface over which this service instance was discovered.
2555 uint16_t port
; // Port from service instance's SRV record.
2556 uint8_t * txtPtr
; // Service instance's TXT record data.
2557 size_t txtLen
; // Service instance's TXT record data length.
2558 SBRIPAddress
* ipaddrList
; // List of IP addresses that the hostname resolved to.
2559 uint64_t discoverTimeUs
; // Time it took to discover this service instance in microseconds.
2560 uint64_t resolveTimeUs
; // Time it took to resolve this service instance in microseconds.
2565 SBRIPAddress
* next
; // Next IP address in list.
2566 sockaddr_ip sip
; // IPv4 or IPv6 address.
2567 uint64_t resolveTimeUs
; // Time it took to resolve this IP address in microseconds.
2570 static CFTypeID
ServiceBrowserGetTypeID( void );
2572 ServiceBrowserCreate(
2573 dispatch_queue_t inQueue
,
2574 uint32_t inInterfaceIndex
,
2575 const char * inDomain
,
2576 unsigned int inBrowseTimeSecs
,
2577 Boolean inIncludeAWDL
,
2578 ServiceBrowserRef
* outBrowser
);
2579 static void ServiceBrowserStart( ServiceBrowserRef inBrowser
);
2580 static OSStatus
ServiceBrowserAddServiceType( ServiceBrowserRef inBrowser
, const char *inServiceType
);
2582 ServiceBrowserSetCallback(
2583 ServiceBrowserRef inBrowser
,
2584 ServiceBrowserCallback_f inCallback
,
2586 static void ServiceBrowserResultsRetain( ServiceBrowserResults
*inResults
);
2587 static void ServiceBrowserResultsRelease( ServiceBrowserResults
*inResults
);
2589 #define ForgetServiceBrowserResults( X ) ForgetCustom( X, ServiceBrowserResultsRelease )
2591 //===========================================================================================================================
2593 //===========================================================================================================================
2595 #define _PRINTF_EXTENSION_HANDLER_DECLARE( NAME ) \
2597 _PrintFExtension ## NAME ## Handler( \
2598 PrintFContext * inContext, \
2599 PrintFFormat * inFormat, \
2600 PrintFVAList * inArgs, \
2601 void * inUserContext )
2603 _PRINTF_EXTENSION_HANDLER_DECLARE( Timestamp
);
2604 _PRINTF_EXTENSION_HANDLER_DECLARE( DNSMessage
);
2605 _PRINTF_EXTENSION_HANDLER_DECLARE( CallbackFlags
);
2606 _PRINTF_EXTENSION_HANDLER_DECLARE( DNSRecordData
);
2608 int main( int argc
, const char **argv
)
2612 // Route DebugServices logging output to stderr.
2614 dlog_control( "DebugServices:output=file;stderr" );
2616 PrintFRegisterExtension( "du:time", _PrintFExtensionTimestampHandler
, NULL
);
2617 PrintFRegisterExtension( "du:dnsmsg", _PrintFExtensionDNSMessageHandler
, NULL
);
2618 PrintFRegisterExtension( "du:cbflags", _PrintFExtensionCallbackFlagsHandler
, NULL
);
2619 PrintFRegisterExtension( "du:rdata", _PrintFExtensionDNSRecordDataHandler
, NULL
);
2620 CLIInit( argc
, argv
);
2621 err
= CLIParse( kGlobalOpts
, kCLIFlags_None
);
2622 if( err
) exit( 1 );
2624 return( gExitCode
);
2627 //===========================================================================================================================
2628 // VersionOptionCallback
2629 //===========================================================================================================================
2631 static OSStatus
VersionOptionCallback( CLIOption
*inOption
, const char *inArg
, int inUnset
)
2633 const char * srcVers
;
2634 #if( MDNSRESPONDER_PROJECT )
2642 #if( MDNSRESPONDER_PROJECT )
2643 srcVers
= SourceVersionToCString( _DNS_SD_H
, srcStr
);
2645 srcVers
= DNSSDUTIL_SOURCE_VERSION
;
2647 FPrintF( stdout
, "%s version %v (%s)\n", gProgramName
, kDNSSDUtilNumVersion
, srcVers
);
2649 return( kEndingErr
);
2652 //===========================================================================================================================
2654 //===========================================================================================================================
2656 typedef struct BrowseResolveOp BrowseResolveOp
;
2658 struct BrowseResolveOp
2660 BrowseResolveOp
* next
; // Next resolve operation in list.
2661 DNSServiceRef sdRef
; // sdRef of the DNSServiceResolve or DNSServiceQueryRecord operation.
2662 char * fullName
; // Full name of the service to resolve.
2663 uint32_t interfaceIndex
; // Interface index of the DNSServiceResolve or DNSServiceQueryRecord operation.
2668 DNSServiceRef mainRef
; // Main sdRef for shared connection.
2669 DNSServiceRef
* opRefs
; // Array of sdRefs for individual Browse operarions.
2670 size_t opRefsCount
; // Count of array of sdRefs for non-shared connections.
2671 const char * domain
; // Domain for DNSServiceBrowse operation(s).
2672 DNSServiceFlags flags
; // Flags for DNSServiceBrowse operation(s).
2673 char ** serviceTypes
; // Array of service types to browse for.
2674 size_t serviceTypesCount
; // Count of array of service types to browse for.
2675 int timeLimitSecs
; // Time limit of DNSServiceBrowse operation in seconds.
2676 BrowseResolveOp
* resolveList
; // List of resolve and/or TXT record query operations.
2677 uint32_t ifIndex
; // Interface index of DNSServiceBrowse operation(s).
2678 Boolean printedHeader
; // True if results header has been printed.
2679 Boolean doResolve
; // True if service instances are to be resolved.
2680 Boolean doResolveTXTOnly
; // True if TXT records of service instances are to be queried.
2684 static void BrowsePrintPrologue( const BrowseContext
*inContext
);
2685 static void BrowseContextFree( BrowseContext
*inContext
);
2686 static OSStatus
BrowseResolveOpCreate( const char *inFullName
, uint32_t inInterfaceIndex
, BrowseResolveOp
**outOp
);
2687 static void BrowseResolveOpFree( BrowseResolveOp
*inOp
);
2688 static void DNSSD_API
2690 DNSServiceRef inSDRef
,
2691 DNSServiceFlags inFlags
,
2692 uint32_t inInterfaceIndex
,
2693 DNSServiceErrorType inError
,
2694 const char * inName
,
2695 const char * inRegType
,
2696 const char * inDomain
,
2698 static void DNSSD_API
2699 BrowseResolveCallback(
2700 DNSServiceRef inSDRef
,
2701 DNSServiceFlags inFlags
,
2702 uint32_t inInterfaceIndex
,
2703 DNSServiceErrorType inError
,
2704 const char * inFullName
,
2705 const char * inHostname
,
2708 const unsigned char * inTXTPtr
,
2710 static void DNSSD_API
2711 BrowseQueryRecordCallback(
2712 DNSServiceRef inSDRef
,
2713 DNSServiceFlags inFlags
,
2714 uint32_t inInterfaceIndex
,
2715 DNSServiceErrorType inError
,
2716 const char * inFullName
,
2719 uint16_t inRDataLen
,
2720 const void * inRDataPtr
,
2724 static void BrowseCmd( void )
2728 BrowseContext
* context
= NULL
;
2729 dispatch_source_t signalSource
= NULL
;
2730 int useMainConnection
;
2732 // Set up SIGINT handler.
2734 signal( SIGINT
, SIG_IGN
);
2735 err
= DispatchSignalSourceCreate( SIGINT
, dispatch_get_main_queue(), Exit
, kExitReason_SIGINT
, &signalSource
);
2736 require_noerr( err
, exit
);
2737 dispatch_resume( signalSource
);
2741 context
= (BrowseContext
*) calloc( 1, sizeof( *context
) );
2742 require_action( context
, exit
, err
= kNoMemoryErr
);
2744 context
->opRefs
= (DNSServiceRef
*) calloc( gBrowse_ServiceTypesCount
, sizeof( DNSServiceRef
) );
2745 require_action( context
->opRefs
, exit
, err
= kNoMemoryErr
);
2746 context
->opRefsCount
= gBrowse_ServiceTypesCount
;
2748 // Check command parameters.
2750 if( gBrowse_TimeLimitSecs
< 0 )
2752 FPrintF( stderr
, "Invalid time limit: %d seconds.\n", gBrowse_TimeLimitSecs
);
2757 // Create main connection.
2759 if( gConnectionOpt
)
2761 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
2762 require_noerr_quiet( err
, exit
);
2763 useMainConnection
= true;
2767 useMainConnection
= false;
2772 context
->flags
= GetDNSSDFlagsFromOpts();
2773 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
2777 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
2778 require_noerr_quiet( err
, exit
);
2780 // Set remaining parameters.
2782 context
->serviceTypes
= gBrowse_ServiceTypes
;
2783 context
->serviceTypesCount
= gBrowse_ServiceTypesCount
;
2784 context
->domain
= gBrowse_Domain
;
2785 context
->doResolve
= gBrowse_DoResolve
? true : false;
2786 context
->timeLimitSecs
= gBrowse_TimeLimitSecs
;
2787 context
->doResolveTXTOnly
= gBrowse_QueryTXT
? true : false;
2791 BrowsePrintPrologue( context
);
2793 // Start operation(s).
2795 for( i
= 0; i
< context
->serviceTypesCount
; ++i
)
2797 DNSServiceRef sdRef
;
2799 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
2800 err
= DNSServiceBrowse( &sdRef
, context
->flags
, context
->ifIndex
, context
->serviceTypes
[ i
], context
->domain
,
2801 BrowseCallback
, context
);
2802 require_noerr( err
, exit
);
2804 context
->opRefs
[ i
] = sdRef
;
2805 if( !useMainConnection
)
2807 err
= DNSServiceSetDispatchQueue( context
->opRefs
[ i
], dispatch_get_main_queue() );
2808 require_noerr( err
, exit
);
2814 if( context
->timeLimitSecs
> 0 )
2816 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
2817 kExitReason_TimeLimit
, Exit
);
2822 dispatch_source_forget( &signalSource
);
2823 if( context
) BrowseContextFree( context
);
2824 if( err
) exit( 1 );
2827 //===========================================================================================================================
2828 // BrowsePrintPrologue
2829 //===========================================================================================================================
2831 static void BrowsePrintPrologue( const BrowseContext
*inContext
)
2833 const int timeLimitSecs
= inContext
->timeLimitSecs
;
2834 const char * const * ptr
= (const char **) inContext
->serviceTypes
;
2835 const char * const * const end
= (const char **) inContext
->serviceTypes
+ inContext
->serviceTypesCount
;
2836 char ifName
[ kInterfaceNameBufLen
];
2838 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
2840 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
2841 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
2842 FPrintF( stdout
, "Service types: %s", *ptr
++ );
2843 while( ptr
< end
) FPrintF( stdout
, ", %s", *ptr
++ );
2844 FPrintF( stdout
, "\n" );
2845 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
? inContext
->domain
: "<NULL> (default domains)" );
2846 FPrintF( stdout
, "Time limit: " );
2847 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
2848 else FPrintF( stdout
, "∞\n" );
2849 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
2850 FPrintF( stdout
, "---\n" );
2853 //===========================================================================================================================
2854 // BrowseContextFree
2855 //===========================================================================================================================
2857 static void BrowseContextFree( BrowseContext
*inContext
)
2861 for( i
= 0; i
< inContext
->opRefsCount
; ++i
)
2863 DNSServiceForget( &inContext
->opRefs
[ i
] );
2865 if( inContext
->serviceTypes
)
2867 _StringArray_Free( inContext
->serviceTypes
, inContext
->serviceTypesCount
);
2868 inContext
->serviceTypes
= NULL
;
2869 inContext
->serviceTypesCount
= 0;
2871 DNSServiceForget( &inContext
->mainRef
);
2875 //===========================================================================================================================
2876 // BrowseResolveOpCreate
2877 //===========================================================================================================================
2879 static OSStatus
BrowseResolveOpCreate( const char *inFullName
, uint32_t inInterfaceIndex
, BrowseResolveOp
**outOp
)
2882 BrowseResolveOp
* resolveOp
;
2884 resolveOp
= (BrowseResolveOp
*) calloc( 1, sizeof( *resolveOp
) );
2885 require_action( resolveOp
, exit
, err
= kNoMemoryErr
);
2887 resolveOp
->fullName
= strdup( inFullName
);
2888 require_action( resolveOp
->fullName
, exit
, err
= kNoMemoryErr
);
2890 resolveOp
->interfaceIndex
= inInterfaceIndex
;
2897 if( resolveOp
) BrowseResolveOpFree( resolveOp
);
2901 //===========================================================================================================================
2902 // BrowseResolveOpFree
2903 //===========================================================================================================================
2905 static void BrowseResolveOpFree( BrowseResolveOp
*inOp
)
2907 DNSServiceForget( &inOp
->sdRef
);
2908 ForgetMem( &inOp
->fullName
);
2912 //===========================================================================================================================
2914 //===========================================================================================================================
2916 static void DNSSD_API
2918 DNSServiceRef inSDRef
,
2919 DNSServiceFlags inFlags
,
2920 uint32_t inInterfaceIndex
,
2921 DNSServiceErrorType inError
,
2922 const char * inName
,
2923 const char * inRegType
,
2924 const char * inDomain
,
2927 BrowseContext
* const context
= (BrowseContext
*) inContext
;
2929 BrowseResolveOp
* newOp
= NULL
;
2930 BrowseResolveOp
** p
;
2931 char fullName
[ kDNSServiceMaxDomainName
];
2936 gettimeofday( &now
, NULL
);
2939 require_noerr( err
, exit
);
2941 if( !context
->printedHeader
)
2943 FPrintF( stdout
, "%-26s %-16s IF %-20s %-20s Instance Name\n", "Timestamp", "Flags", "Domain", "Service Type" );
2944 context
->printedHeader
= true;
2946 FPrintF( stdout
, "%{du:time} %{du:cbflags} %2d %-20s %-20s %s\n",
2947 &now
, inFlags
, (int32_t) inInterfaceIndex
, inDomain
, inRegType
, inName
);
2949 if( !context
->doResolve
&& !context
->doResolveTXTOnly
) goto exit
;
2951 err
= DNSServiceConstructFullName( fullName
, inName
, inRegType
, inDomain
);
2952 require_noerr( err
, exit
);
2954 if( inFlags
& kDNSServiceFlagsAdd
)
2956 DNSServiceRef sdRef
;
2957 DNSServiceFlags flags
;
2959 err
= BrowseResolveOpCreate( fullName
, inInterfaceIndex
, &newOp
);
2960 require_noerr( err
, exit
);
2962 if( context
->mainRef
)
2964 sdRef
= context
->mainRef
;
2965 flags
= kDNSServiceFlagsShareConnection
;
2971 if( context
->doResolve
)
2973 err
= DNSServiceResolve( &sdRef
, flags
, inInterfaceIndex
, inName
, inRegType
, inDomain
, BrowseResolveCallback
,
2975 require_noerr( err
, exit
);
2979 err
= DNSServiceQueryRecord( &sdRef
, flags
, inInterfaceIndex
, fullName
, kDNSServiceType_TXT
, kDNSServiceClass_IN
,
2980 BrowseQueryRecordCallback
, NULL
);
2981 require_noerr( err
, exit
);
2984 newOp
->sdRef
= sdRef
;
2985 if( !context
->mainRef
)
2987 err
= DNSServiceSetDispatchQueue( newOp
->sdRef
, dispatch_get_main_queue() );
2988 require_noerr( err
, exit
);
2990 for( p
= &context
->resolveList
; *p
; p
= &( *p
)->next
) {}
2996 BrowseResolveOp
* resolveOp
;
2998 for( p
= &context
->resolveList
; ( resolveOp
= *p
) != NULL
; p
= &resolveOp
->next
)
3000 if( ( resolveOp
->interfaceIndex
== inInterfaceIndex
) && ( strcasecmp( resolveOp
->fullName
, fullName
) == 0 ) )
3007 *p
= resolveOp
->next
;
3008 BrowseResolveOpFree( resolveOp
);
3013 if( newOp
) BrowseResolveOpFree( newOp
);
3014 if( err
) exit( 1 );
3017 //===========================================================================================================================
3018 // BrowseQueryRecordCallback
3019 //===========================================================================================================================
3021 static void DNSSD_API
3022 BrowseQueryRecordCallback(
3023 DNSServiceRef inSDRef
,
3024 DNSServiceFlags inFlags
,
3025 uint32_t inInterfaceIndex
,
3026 DNSServiceErrorType inError
,
3027 const char * inFullName
,
3030 uint16_t inRDataLen
,
3031 const void * inRDataPtr
,
3041 Unused( inContext
);
3043 gettimeofday( &now
, NULL
);
3046 require_noerr( err
, exit
);
3047 require_action( inType
== kDNSServiceType_TXT
, exit
, err
= kTypeErr
);
3049 FPrintF( stdout
, "%{du:time} %s %s TXT on interface %d\n TXT: %#{txt}\n",
3050 &now
, DNSServiceFlagsToAddRmvStr( inFlags
), inFullName
, (int32_t) inInterfaceIndex
, inRDataPtr
,
3051 (size_t) inRDataLen
);
3054 if( err
) exit( 1 );
3057 //===========================================================================================================================
3058 // BrowseResolveCallback
3059 //===========================================================================================================================
3061 static void DNSSD_API
3062 BrowseResolveCallback(
3063 DNSServiceRef inSDRef
,
3064 DNSServiceFlags inFlags
,
3065 uint32_t inInterfaceIndex
,
3066 DNSServiceErrorType inError
,
3067 const char * inFullName
,
3068 const char * inHostname
,
3071 const unsigned char * inTXTPtr
,
3075 char errorStr
[ 64 ];
3079 Unused( inContext
);
3081 gettimeofday( &now
, NULL
);
3083 if( inError
) SNPrintF( errorStr
, sizeof( errorStr
), " error %#m", inError
);
3085 FPrintF( stdout
, "%{du:time} %s can be reached at %s:%u (interface %d)%?s\n",
3086 &now
, inFullName
, inHostname
, ntohs( inPort
), (int32_t) inInterfaceIndex
, inError
, errorStr
);
3089 FPrintF( stdout
, " TXT record: %#H\n", inTXTPtr
, (int) inTXTLen
, INT_MAX
);
3093 FPrintF( stdout
, " TXT record: %#{txt}\n", inTXTPtr
, (size_t) inTXTLen
);
3097 //===========================================================================================================================
3099 //===========================================================================================================================
3103 DNSServiceRef mainRef
; // Main sdRef for shared connection.
3104 DNSServiceRef opRef
; // sdRef for the DNSServiceGetAddrInfo operation.
3105 const char * name
; // Hostname to resolve.
3106 DNSServiceFlags flags
; // Flags argument for DNSServiceGetAddrInfo().
3107 DNSServiceProtocol protocols
; // Protocols argument for DNSServiceGetAddrInfo().
3108 uint32_t ifIndex
; // Interface index argument for DNSServiceGetAddrInfo().
3109 int timeLimitSecs
; // Time limit for the DNSServiceGetAddrInfo() operation in seconds.
3110 Boolean printedHeader
; // True if the results header has been printed.
3111 Boolean oneShotMode
; // True if command is done after the first set of results (one-shot mode).
3112 Boolean needIPv4
; // True if in one-shot mode and an IPv4 result is needed.
3113 Boolean needIPv6
; // True if in one-shot mode and an IPv6 result is needed.
3115 } GetAddrInfoContext
;
3117 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext
*inContext
);
3118 static void GetAddrInfoContextFree( GetAddrInfoContext
*inContext
);
3119 static void DNSSD_API
3120 GetAddrInfoCallback(
3121 DNSServiceRef inSDRef
,
3122 DNSServiceFlags inFlags
,
3123 uint32_t inInterfaceIndex
,
3124 DNSServiceErrorType inError
,
3125 const char * inHostname
,
3126 const struct sockaddr
* inSockAddr
,
3130 static void GetAddrInfoCmd( void )
3133 DNSServiceRef sdRef
;
3134 GetAddrInfoContext
* context
= NULL
;
3135 dispatch_source_t signalSource
= NULL
;
3136 int useMainConnection
;
3138 // Set up SIGINT handler.
3140 signal( SIGINT
, SIG_IGN
);
3141 err
= DispatchSignalSourceCreate( SIGINT
, dispatch_get_main_queue(), Exit
, kExitReason_SIGINT
, &signalSource
);
3142 require_noerr( err
, exit
);
3143 dispatch_resume( signalSource
);
3145 // Check command parameters.
3147 if( gGetAddrInfo_TimeLimitSecs
< 0 )
3149 FPrintF( stderr
, "Invalid time limit: %d s.\n", gGetAddrInfo_TimeLimitSecs
);
3156 context
= (GetAddrInfoContext
*) calloc( 1, sizeof( *context
) );
3157 require_action( context
, exit
, err
= kNoMemoryErr
);
3159 // Create main connection.
3161 if( gConnectionOpt
)
3163 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
3164 require_noerr_quiet( err
, exit
);
3165 useMainConnection
= true;
3169 useMainConnection
= false;
3174 context
->flags
= GetDNSSDFlagsFromOpts();
3175 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
3179 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3180 require_noerr_quiet( err
, exit
);
3182 // Set remaining parameters.
3184 context
->name
= gGetAddrInfo_Name
;
3185 context
->timeLimitSecs
= gGetAddrInfo_TimeLimitSecs
;
3186 if( gGetAddrInfo_ProtocolIPv4
) context
->protocols
|= kDNSServiceProtocol_IPv4
;
3187 if( gGetAddrInfo_ProtocolIPv6
) context
->protocols
|= kDNSServiceProtocol_IPv6
;
3188 if( gGetAddrInfo_OneShot
)
3190 context
->oneShotMode
= true;
3191 context
->needIPv4
= ( gGetAddrInfo_ProtocolIPv4
|| !gGetAddrInfo_ProtocolIPv6
) ? true : false;
3192 context
->needIPv6
= ( gGetAddrInfo_ProtocolIPv6
|| !gGetAddrInfo_ProtocolIPv4
) ? true : false;
3197 GetAddrInfoPrintPrologue( context
);
3201 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
3202 err
= DNSServiceGetAddrInfo( &sdRef
, context
->flags
, context
->ifIndex
, context
->protocols
, context
->name
,
3203 GetAddrInfoCallback
, context
);
3204 require_noerr( err
, exit
);
3206 context
->opRef
= sdRef
;
3207 if( !useMainConnection
)
3209 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
3210 require_noerr( err
, exit
);
3215 if( context
->timeLimitSecs
> 0 )
3217 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
3218 kExitReason_TimeLimit
, Exit
);
3223 dispatch_source_forget( &signalSource
);
3224 if( context
) GetAddrInfoContextFree( context
);
3225 if( err
) exit( 1 );
3228 //===========================================================================================================================
3229 // GetAddrInfoPrintPrologue
3230 //===========================================================================================================================
3232 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext
*inContext
)
3234 const int timeLimitSecs
= inContext
->timeLimitSecs
;
3235 char ifName
[ kInterfaceNameBufLen
];
3237 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
3239 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
3240 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
3241 FPrintF( stdout
, "Protocols: %#{flags}\n", inContext
->protocols
, kDNSServiceProtocolDescriptors
);
3242 FPrintF( stdout
, "Name: %s\n", inContext
->name
);
3243 FPrintF( stdout
, "Mode: %s\n", inContext
->oneShotMode
? "one-shot" : "continuous" );
3244 FPrintF( stdout
, "Time limit: " );
3245 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
3246 else FPrintF( stdout
, "∞\n" );
3247 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
3248 FPrintF( stdout
, "---\n" );
3251 //===========================================================================================================================
3252 // GetAddrInfoContextFree
3253 //===========================================================================================================================
3255 static void GetAddrInfoContextFree( GetAddrInfoContext
*inContext
)
3257 DNSServiceForget( &inContext
->opRef
);
3258 DNSServiceForget( &inContext
->mainRef
);
3262 //===========================================================================================================================
3263 // GetAddrInfoCallback
3264 //===========================================================================================================================
3266 static void DNSSD_API
3267 GetAddrInfoCallback(
3268 DNSServiceRef inSDRef
,
3269 DNSServiceFlags inFlags
,
3270 uint32_t inInterfaceIndex
,
3271 DNSServiceErrorType inError
,
3272 const char * inHostname
,
3273 const struct sockaddr
* inSockAddr
,
3277 GetAddrInfoContext
* const context
= (GetAddrInfoContext
*) inContext
;
3280 const char * addrStr
;
3281 char addrStrBuf
[ kSockAddrStringMaxSize
];
3285 gettimeofday( &now
, NULL
);
3289 case kDNSServiceErr_NoError
:
3290 case kDNSServiceErr_NoSuchRecord
:
3294 case kDNSServiceErr_Timeout
:
3295 Exit( kExitReason_Timeout
);
3302 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
3304 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
3311 err
= SockAddrToString( inSockAddr
, kSockAddrStringFlagsNone
, addrStrBuf
);
3312 require_noerr( err
, exit
);
3313 addrStr
= addrStrBuf
;
3317 addrStr
= ( inSockAddr
->sa_family
== AF_INET
) ? kNoSuchRecordAStr
: kNoSuchRecordAAAAStr
;
3320 if( !context
->printedHeader
)
3322 FPrintF( stdout
, "%-26s %-16s IF %-30s %-34s %6s\n", "Timestamp", "Flags", "Hostname", "Address", "TTL" );
3323 context
->printedHeader
= true;
3325 FPrintF( stdout
, "%{du:time} %{du:cbflags} %2d %-30s %-34s %6u\n",
3326 &now
, inFlags
, (int32_t) inInterfaceIndex
, inHostname
, addrStr
, inTTL
);
3328 if( context
->oneShotMode
)
3330 if( inFlags
& kDNSServiceFlagsAdd
)
3332 if( inSockAddr
->sa_family
== AF_INET
) context
->needIPv4
= false;
3333 else context
->needIPv6
= false;
3335 if( !( inFlags
& kDNSServiceFlagsMoreComing
) && !context
->needIPv4
&& !context
->needIPv6
)
3337 Exit( kExitReason_OneShotDone
);
3342 if( err
) exit( 1 );
3345 //===========================================================================================================================
3347 //===========================================================================================================================
3351 DNSServiceRef mainRef
; // Main sdRef for shared connection.
3352 DNSServiceRef opRef
; // sdRef for the DNSServiceQueryRecord operation.
3353 const char * recordName
; // Resource record name argument for DNSServiceQueryRecord().
3354 DNSServiceFlags flags
; // Flags argument for DNSServiceQueryRecord().
3355 uint32_t ifIndex
; // Interface index argument for DNSServiceQueryRecord().
3356 int timeLimitSecs
; // Time limit for the DNSServiceQueryRecord() operation in seconds.
3357 uint16_t recordType
; // Resource record type argument for DNSServiceQueryRecord().
3358 Boolean printedHeader
; // True if the results header was printed.
3359 Boolean oneShotMode
; // True if command is done after the first set of results (one-shot mode).
3360 Boolean gotRecord
; // True if in one-shot mode and received at least one record of the desired type.
3361 Boolean printRawRData
; // True if RDATA results are not to be formatted when printed.
3363 } QueryRecordContext
;
3365 static void QueryRecordPrintPrologue( const QueryRecordContext
*inContext
);
3366 static void QueryRecordContextFree( QueryRecordContext
*inContext
);
3367 static void DNSSD_API
3368 QueryRecordCallback(
3369 DNSServiceRef inSDRef
,
3370 DNSServiceFlags inFlags
,
3371 uint32_t inInterfaceIndex
,
3372 DNSServiceErrorType inError
,
3373 const char * inFullName
,
3376 uint16_t inRDataLen
,
3377 const void * inRDataPtr
,
3381 static void QueryRecordCmd( void )
3384 DNSServiceRef sdRef
;
3385 QueryRecordContext
* context
= NULL
;
3386 dispatch_source_t signalSource
= NULL
;
3387 int useMainConnection
;
3389 // Set up SIGINT handler.
3391 signal( SIGINT
, SIG_IGN
);
3392 err
= DispatchSignalSourceCreate( SIGINT
, dispatch_get_main_queue(), Exit
, kExitReason_SIGINT
, &signalSource
);
3393 require_noerr( err
, exit
);
3394 dispatch_resume( signalSource
);
3398 context
= (QueryRecordContext
*) calloc( 1, sizeof( *context
) );
3399 require_action( context
, exit
, err
= kNoMemoryErr
);
3401 // Check command parameters.
3403 if( gQueryRecord_TimeLimitSecs
< 0 )
3405 FPrintF( stderr
, "Invalid time limit: %d seconds.\n", gQueryRecord_TimeLimitSecs
);
3410 // Create main connection.
3412 if( gConnectionOpt
)
3414 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
3415 require_noerr_quiet( err
, exit
);
3416 useMainConnection
= true;
3420 useMainConnection
= false;
3425 context
->flags
= GetDNSSDFlagsFromOpts();
3426 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
3430 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3431 require_noerr_quiet( err
, exit
);
3435 err
= RecordTypeFromArgString( gQueryRecord_Type
, &context
->recordType
);
3436 require_noerr( err
, exit
);
3438 // Set remaining parameters.
3440 context
->recordName
= gQueryRecord_Name
;
3441 context
->timeLimitSecs
= gQueryRecord_TimeLimitSecs
;
3442 context
->oneShotMode
= gQueryRecord_OneShot
? true : false;
3443 context
->printRawRData
= gQueryRecord_RawRData
? true : false;
3447 QueryRecordPrintPrologue( context
);
3451 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
3452 err
= DNSServiceQueryRecord( &sdRef
, context
->flags
, context
->ifIndex
, context
->recordName
, context
->recordType
,
3453 kDNSServiceClass_IN
, QueryRecordCallback
, context
);
3454 require_noerr( err
, exit
);
3456 context
->opRef
= sdRef
;
3457 if( !useMainConnection
)
3459 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
3460 require_noerr( err
, exit
);
3465 if( context
->timeLimitSecs
> 0 )
3467 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(), kExitReason_TimeLimit
,
3473 dispatch_source_forget( &signalSource
);
3474 if( context
) QueryRecordContextFree( context
);
3475 if( err
) exit( 1 );
3478 //===========================================================================================================================
3479 // QueryRecordContextFree
3480 //===========================================================================================================================
3482 static void QueryRecordContextFree( QueryRecordContext
*inContext
)
3484 DNSServiceForget( &inContext
->opRef
);
3485 DNSServiceForget( &inContext
->mainRef
);
3489 //===========================================================================================================================
3490 // QueryRecordPrintPrologue
3491 //===========================================================================================================================
3493 static void QueryRecordPrintPrologue( const QueryRecordContext
*inContext
)
3495 const int timeLimitSecs
= inContext
->timeLimitSecs
;
3496 char ifName
[ kInterfaceNameBufLen
];
3498 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
3500 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
3501 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
3502 FPrintF( stdout
, "Name: %s\n", inContext
->recordName
);
3503 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->recordType
), inContext
->recordType
);
3504 FPrintF( stdout
, "Mode: %s\n", inContext
->oneShotMode
? "one-shot" : "continuous" );
3505 FPrintF( stdout
, "Time limit: " );
3506 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
3507 else FPrintF( stdout
, "∞\n" );
3508 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
3509 FPrintF( stdout
, "---\n" );
3513 //===========================================================================================================================
3514 // QueryRecordCallback
3515 //===========================================================================================================================
3517 static void DNSSD_API
3518 QueryRecordCallback(
3519 DNSServiceRef inSDRef
,
3520 DNSServiceFlags inFlags
,
3521 uint32_t inInterfaceIndex
,
3522 DNSServiceErrorType inError
,
3523 const char * inFullName
,
3526 uint16_t inRDataLen
,
3527 const void * inRDataPtr
,
3531 QueryRecordContext
* const context
= (QueryRecordContext
*) inContext
;
3534 char * rdataStr
= NULL
;
3538 gettimeofday( &now
, NULL
);
3542 case kDNSServiceErr_NoError
:
3543 case kDNSServiceErr_NoSuchRecord
:
3547 case kDNSServiceErr_Timeout
:
3548 Exit( kExitReason_Timeout
);
3555 if( inError
!= kDNSServiceErr_NoSuchRecord
)
3557 if( !context
->printRawRData
) DNSRecordDataToString( inRDataPtr
, inRDataLen
, inType
, NULL
, 0, &rdataStr
);
3560 ASPrintF( &rdataStr
, "%#H", inRDataPtr
, inRDataLen
, INT_MAX
);
3561 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
3565 if( !context
->printedHeader
)
3567 FPrintF( stdout
, "%-26s %-16s IF %-32s %-5s %-5s %6s RData\n",
3568 "Timestamp", "Flags", "Name", "Type", "Class", "TTL" );
3569 context
->printedHeader
= true;
3571 FPrintF( stdout
, "%{du:time} %{du:cbflags} %2d %-32s %-5s %?-5s%?5u %6u %s\n",
3572 &now
, inFlags
, (int32_t) inInterfaceIndex
, inFullName
, RecordTypeToString( inType
),
3573 ( inClass
== kDNSServiceClass_IN
), "IN", ( inClass
!= kDNSServiceClass_IN
), inClass
, inTTL
,
3574 rdataStr
? rdataStr
: kNoSuchRecordStr
);
3576 if( context
->oneShotMode
)
3578 if( ( inFlags
& kDNSServiceFlagsAdd
) &&
3579 ( ( context
->recordType
== kDNSServiceType_ANY
) || ( context
->recordType
== inType
) ) )
3581 context
->gotRecord
= true;
3583 if( !( inFlags
& kDNSServiceFlagsMoreComing
) && context
->gotRecord
) Exit( kExitReason_OneShotDone
);
3587 FreeNullSafe( rdataStr
);
3588 if( err
) exit( 1 );
3591 //===========================================================================================================================
3593 //===========================================================================================================================
3597 DNSRecordRef recordRef
; // Reference returned by DNSServiceAddRecord().
3598 uint8_t * dataPtr
; // Record data.
3599 size_t dataLen
; // Record data length.
3600 uint32_t ttl
; // Record TTL value.
3601 uint16_t type
; // Record type.
3607 DNSServiceRef opRef
; // sdRef for DNSServiceRegister operation.
3608 const char * name
; // Service name argument for DNSServiceRegister().
3609 const char * type
; // Service type argument for DNSServiceRegister().
3610 const char * domain
; // Domain in which advertise the service.
3611 uint8_t * txtPtr
; // Service TXT record data. (malloc'd)
3612 size_t txtLen
; // Service TXT record data len.
3613 ExtraRecord
* extraRecords
; // Array of extra records to add to registered service.
3614 size_t extraRecordsCount
; // Number of extra records.
3615 uint8_t * updateTXTPtr
; // Pointer to record data for TXT record update. (malloc'd)
3616 size_t updateTXTLen
; // Length of record data for TXT record update.
3617 uint32_t updateTTL
; // TTL of updated TXT record.
3618 int updateDelayMs
; // Post-registration TXT record update delay in milliseconds.
3619 DNSServiceFlags flags
; // Flags argument for DNSServiceRegister().
3620 uint32_t ifIndex
; // Interface index argument for DNSServiceRegister().
3621 int lifetimeMs
; // Lifetime of the record registration in milliseconds.
3622 uint16_t port
; // Service instance's port number.
3623 Boolean printedHeader
; // True if results header was printed.
3624 Boolean didRegister
; // True if service was registered.
3628 static void RegisterPrintPrologue( const RegisterContext
*inContext
);
3629 static void RegisterContextFree( RegisterContext
*inContext
);
3630 static void DNSSD_API
3632 DNSServiceRef inSDRef
,
3633 DNSServiceFlags inFlags
,
3634 DNSServiceErrorType inError
,
3635 const char * inName
,
3636 const char * inType
,
3637 const char * inDomain
,
3639 static void RegisterUpdate( void *inContext
);
3641 static void RegisterCmd( void )
3644 RegisterContext
* context
= NULL
;
3645 dispatch_source_t signalSource
= NULL
;
3647 // Set up SIGINT handler.
3649 signal( SIGINT
, SIG_IGN
);
3650 err
= DispatchSignalSourceCreate( SIGINT
, dispatch_get_main_queue(), Exit
, kExitReason_SIGINT
, &signalSource
);
3651 require_noerr( err
, exit
);
3652 dispatch_resume( signalSource
);
3656 context
= (RegisterContext
*) calloc( 1, sizeof( *context
) );
3657 require_action( context
, exit
, err
= kNoMemoryErr
);
3659 // Check command parameters.
3661 if( ( gRegister_Port
< 0 ) || ( gRegister_Port
> UINT16_MAX
) )
3663 FPrintF( stderr
, "Port number %d is out-of-range.\n", gRegister_Port
);
3668 if( ( gAddRecord_DataCount
!= gAddRecord_TypesCount
) || ( gAddRecord_TTLsCount
!= gAddRecord_TypesCount
) )
3670 FPrintF( stderr
, "There are missing additional record parameters.\n" );
3677 context
->flags
= GetDNSSDFlagsFromOpts();
3679 // Get interface index.
3681 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3682 require_noerr_quiet( err
, exit
);
3684 // Get TXT record data.
3688 err
= RecordDataFromArgString( gRegister_TXT
, &context
->txtPtr
, &context
->txtLen
);
3689 require_noerr_quiet( err
, exit
);
3692 // Set remaining parameters.
3694 context
->name
= gRegister_Name
;
3695 context
->type
= gRegister_Type
;
3696 context
->domain
= gRegister_Domain
;
3697 context
->port
= (uint16_t) gRegister_Port
;
3698 context
->lifetimeMs
= gRegister_LifetimeMs
;
3700 if( gAddRecord_TypesCount
> 0 )
3704 context
->extraRecords
= (ExtraRecord
*) calloc( gAddRecord_TypesCount
, sizeof( ExtraRecord
) );
3705 require_action( context
, exit
, err
= kNoMemoryErr
);
3706 context
->extraRecordsCount
= gAddRecord_TypesCount
;
3708 for( i
= 0; i
< gAddRecord_TypesCount
; ++i
)
3710 ExtraRecord
* const extraRecord
= &context
->extraRecords
[ i
];
3712 err
= RecordTypeFromArgString( gAddRecord_Types
[ i
], &extraRecord
->type
);
3713 require_noerr( err
, exit
);
3715 err
= StringToUInt32( gAddRecord_TTLs
[ i
], &extraRecord
->ttl
);
3718 FPrintF( stderr
, "Invalid TTL value: %s\n", gAddRecord_TTLs
[ i
] );
3723 err
= RecordDataFromArgString( gAddRecord_Data
[ i
], &extraRecord
->dataPtr
, &extraRecord
->dataLen
);
3724 require_noerr_quiet( err
, exit
);
3728 if( gUpdateRecord_Data
)
3730 err
= RecordDataFromArgString( gUpdateRecord_Data
, &context
->updateTXTPtr
, &context
->updateTXTLen
);
3731 require_noerr_quiet( err
, exit
);
3733 context
->updateTTL
= (uint32_t) gUpdateRecord_TTL
;
3734 context
->updateDelayMs
= gUpdateRecord_DelayMs
;
3739 RegisterPrintPrologue( context
);
3743 err
= DNSServiceRegister( &context
->opRef
, context
->flags
, context
->ifIndex
, context
->name
, context
->type
,
3744 context
->domain
, NULL
, htons( context
->port
), (uint16_t) context
->txtLen
, context
->txtPtr
,
3745 RegisterCallback
, context
);
3746 ForgetMem( &context
->txtPtr
);
3747 require_noerr( err
, exit
);
3749 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
3750 require_noerr( err
, exit
);
3755 dispatch_source_forget( &signalSource
);
3756 if( context
) RegisterContextFree( context
);
3757 if( err
) exit( 1 );
3760 //===========================================================================================================================
3761 // RegisterPrintPrologue
3762 //===========================================================================================================================
3764 static void RegisterPrintPrologue( const RegisterContext
*inContext
)
3768 char ifName
[ kInterfaceNameBufLen
];
3770 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
3772 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
3773 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
3774 FPrintF( stdout
, "Name: %s\n", inContext
->name
? inContext
->name
: "<NULL>" );
3775 FPrintF( stdout
, "Type: %s\n", inContext
->type
);
3776 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
? inContext
->domain
: "<NULL> (default domains)" );
3777 FPrintF( stdout
, "Port: %u\n", inContext
->port
);
3778 FPrintF( stdout
, "TXT data: %#{txt}\n", inContext
->txtPtr
, inContext
->txtLen
);
3779 infinite
= ( inContext
->lifetimeMs
< 0 ) ? true : false;
3780 FPrintF( stdout
, "Lifetime: %?s%?d ms\n", infinite
, "∞", !infinite
, inContext
->lifetimeMs
);
3781 if( inContext
->updateTXTPtr
)
3783 FPrintF( stdout
, "\nUpdate record:\n" );
3784 FPrintF( stdout
, " Delay: %d ms\n", ( inContext
->updateDelayMs
> 0 ) ? inContext
->updateDelayMs
: 0 );
3785 FPrintF( stdout
, " TTL: %u%?s\n",
3786 inContext
->updateTTL
, inContext
->updateTTL
== 0, " (system will use a default value.)" );
3787 FPrintF( stdout
, " TXT data: %#{txt}\n", inContext
->updateTXTPtr
, inContext
->updateTXTLen
);
3789 if( inContext
->extraRecordsCount
> 0 ) FPrintF( stdout
, "\n" );
3790 for( i
= 0; i
< inContext
->extraRecordsCount
; ++i
)
3792 const ExtraRecord
* record
= &inContext
->extraRecords
[ i
];
3794 FPrintF( stdout
, "Extra record %zu:\n", i
+ 1 );
3795 FPrintF( stdout
, " Type: %s (%u)\n", RecordTypeToString( record
->type
), record
->type
);
3796 FPrintF( stdout
, " TTL: %u%?s\n", record
->ttl
, record
->ttl
== 0, " (system will use a default value.)" );
3797 FPrintF( stdout
, " RData: %#H\n\n", record
->dataPtr
, (int) record
->dataLen
, INT_MAX
);
3799 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
3800 FPrintF( stdout
, "---\n" );
3803 //===========================================================================================================================
3804 // RegisterContextFree
3805 //===========================================================================================================================
3807 static void RegisterContextFree( RegisterContext
*inContext
)
3809 ExtraRecord
* record
;
3810 const ExtraRecord
* const end
= inContext
->extraRecords
+ inContext
->extraRecordsCount
;
3812 DNSServiceForget( &inContext
->opRef
);
3813 ForgetMem( &inContext
->txtPtr
);
3814 for( record
= inContext
->extraRecords
; record
< end
; ++record
)
3816 check( !record
->recordRef
);
3817 ForgetMem( &record
->dataPtr
);
3819 ForgetMem( &inContext
->extraRecords
);
3820 ForgetMem( &inContext
->updateTXTPtr
);
3824 //===========================================================================================================================
3826 //===========================================================================================================================
3828 static void DNSSD_API
3830 DNSServiceRef inSDRef
,
3831 DNSServiceFlags inFlags
,
3832 DNSServiceErrorType inError
,
3833 const char * inName
,
3834 const char * inType
,
3835 const char * inDomain
,
3838 RegisterContext
* const context
= (RegisterContext
*) inContext
;
3844 gettimeofday( &now
, NULL
);
3846 if( !context
->printedHeader
)
3848 FPrintF( stdout
, "%-26s %-16s Service\n", "Timestamp", "Flags" );
3849 context
->printedHeader
= true;
3851 FPrintF( stdout
, "%{du:time} %{du:cbflags} %s.%s%s %?#m\n", &now
, inFlags
, inName
, inType
, inDomain
, inError
, inError
);
3853 require_noerr_action_quiet( inError
, exit
, err
= inError
);
3855 if( !context
->didRegister
&& ( inFlags
& kDNSServiceFlagsAdd
) )
3857 context
->didRegister
= true;
3858 if( context
->updateTXTPtr
)
3860 if( context
->updateDelayMs
> 0 )
3862 dispatch_after_f( dispatch_time_milliseconds( context
->updateDelayMs
), dispatch_get_main_queue(),
3863 context
, RegisterUpdate
);
3867 RegisterUpdate( context
);
3870 if( context
->extraRecordsCount
> 0 )
3872 ExtraRecord
* record
;
3873 const ExtraRecord
* const end
= context
->extraRecords
+ context
->extraRecordsCount
;
3875 for( record
= context
->extraRecords
; record
< end
; ++record
)
3877 err
= DNSServiceAddRecord( context
->opRef
, &record
->recordRef
, 0, record
->type
,
3878 (uint16_t) record
->dataLen
, record
->dataPtr
, record
->ttl
);
3879 require_noerr( err
, exit
);
3882 if( context
->lifetimeMs
== 0 )
3884 Exit( kExitReason_TimeLimit
);
3886 else if( context
->lifetimeMs
> 0 )
3888 dispatch_after_f( dispatch_time_milliseconds( context
->lifetimeMs
), dispatch_get_main_queue(),
3889 kExitReason_TimeLimit
, Exit
);
3895 if( err
) exit( 1 );
3898 //===========================================================================================================================
3900 //===========================================================================================================================
3902 static void RegisterUpdate( void *inContext
)
3905 RegisterContext
* const context
= (RegisterContext
*) inContext
;
3907 err
= DNSServiceUpdateRecord( context
->opRef
, NULL
, 0, (uint16_t) context
->updateTXTLen
, context
->updateTXTPtr
,
3908 context
->updateTTL
);
3909 require_noerr( err
, exit
);
3912 if( err
) exit( 1 );
3915 //===========================================================================================================================
3916 // RegisterRecordCmd
3917 //===========================================================================================================================
3921 DNSServiceRef conRef
; // sdRef to be initialized by DNSServiceCreateConnection().
3922 DNSRecordRef recordRef
; // Registered record reference.
3923 const char * recordName
; // Name of resource record.
3924 uint8_t * dataPtr
; // Pointer to resource record data.
3925 size_t dataLen
; // Length of resource record data.
3926 uint32_t ttl
; // TTL value of resource record in seconds.
3927 uint32_t ifIndex
; // Interface index argument for DNSServiceRegisterRecord().
3928 DNSServiceFlags flags
; // Flags argument for DNSServiceRegisterRecord().
3929 int lifetimeMs
; // Lifetime of the record registration in milliseconds.
3930 uint16_t recordType
; // Resource record type.
3931 uint8_t * updateDataPtr
; // Pointer to data for record update. (malloc'd)
3932 size_t updateDataLen
; // Length of data for record update.
3933 uint32_t updateTTL
; // TTL for updated record.
3934 int updateDelayMs
; // Post-registration record update delay in milliseconds.
3935 Boolean didRegister
; // True if the record was registered.
3937 } RegisterRecordContext
;
3939 static void RegisterRecordPrintPrologue( const RegisterRecordContext
*inContext
);
3940 static void RegisterRecordContextFree( RegisterRecordContext
*inContext
);
3941 static void DNSSD_API
3942 RegisterRecordCallback(
3943 DNSServiceRef inSDRef
,
3944 DNSRecordRef inRecordRef
,
3945 DNSServiceFlags inFlags
,
3946 DNSServiceErrorType inError
,
3948 static void RegisterRecordUpdate( void *inContext
);
3950 static void RegisterRecordCmd( void )
3953 RegisterRecordContext
* context
= NULL
;
3954 dispatch_source_t signalSource
= NULL
;
3956 // Set up SIGINT handler.
3958 signal( SIGINT
, SIG_IGN
);
3959 err
= DispatchSignalSourceCreate( SIGINT
, dispatch_get_main_queue(), Exit
, kExitReason_SIGINT
, &signalSource
);
3960 require_noerr( err
, exit
);
3961 dispatch_resume( signalSource
);
3965 context
= (RegisterRecordContext
*) calloc( 1, sizeof( *context
) );
3966 require_action( context
, exit
, err
= kNoMemoryErr
);
3968 // Create connection.
3970 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->conRef
, NULL
);
3971 require_noerr_quiet( err
, exit
);
3975 context
->flags
= GetDNSSDFlagsFromOpts();
3979 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3980 require_noerr_quiet( err
, exit
);
3984 err
= RecordTypeFromArgString( gRegisterRecord_Type
, &context
->recordType
);
3985 require_noerr( err
, exit
);
3989 if( gRegisterRecord_Data
)
3991 err
= RecordDataFromArgString( gRegisterRecord_Data
, &context
->dataPtr
, &context
->dataLen
);
3992 require_noerr_quiet( err
, exit
);
3995 // Set remaining parameters.
3997 context
->recordName
= gRegisterRecord_Name
;
3998 context
->ttl
= (uint32_t) gRegisterRecord_TTL
;
3999 context
->lifetimeMs
= gRegisterRecord_LifetimeMs
;
4003 if( gRegisterRecord_UpdateData
)
4005 err
= RecordDataFromArgString( gRegisterRecord_UpdateData
, &context
->updateDataPtr
, &context
->updateDataLen
);
4006 require_noerr_quiet( err
, exit
);
4008 context
->updateTTL
= (uint32_t) gRegisterRecord_UpdateTTL
;
4009 context
->updateDelayMs
= gRegisterRecord_UpdateDelayMs
;
4014 RegisterRecordPrintPrologue( context
);
4018 err
= DNSServiceRegisterRecord( context
->conRef
, &context
->recordRef
, context
->flags
, context
->ifIndex
,
4019 context
->recordName
, context
->recordType
, kDNSServiceClass_IN
, (uint16_t) context
->dataLen
, context
->dataPtr
,
4020 context
->ttl
, RegisterRecordCallback
, context
);
4023 FPrintF( stderr
, "DNSServiceRegisterRecord() returned %#m\n", err
);
4030 dispatch_source_forget( &signalSource
);
4031 if( context
) RegisterRecordContextFree( context
);
4032 if( err
) exit( 1 );
4035 //===========================================================================================================================
4036 // RegisterRecordPrintPrologue
4037 //===========================================================================================================================
4039 static void RegisterRecordPrintPrologue( const RegisterRecordContext
*inContext
)
4042 char ifName
[ kInterfaceNameBufLen
];
4044 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
4046 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
4047 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
4048 FPrintF( stdout
, "Name: %s\n", inContext
->recordName
);
4049 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->recordType
), inContext
->recordType
);
4050 FPrintF( stdout
, "TTL: %u\n", inContext
->ttl
);
4051 FPrintF( stdout
, "Data: %#H\n", inContext
->dataPtr
, (int) inContext
->dataLen
, INT_MAX
);
4052 infinite
= ( inContext
->lifetimeMs
< 0 ) ? true : false;
4053 FPrintF( stdout
, "Lifetime: %?s%?d ms\n", infinite
, "∞", !infinite
, inContext
->lifetimeMs
);
4054 if( inContext
->updateDataPtr
)
4056 FPrintF( stdout
, "\nUpdate record:\n" );
4057 FPrintF( stdout
, " Delay: %d ms\n", ( inContext
->updateDelayMs
>= 0 ) ? inContext
->updateDelayMs
: 0 );
4058 FPrintF( stdout
, " TTL: %u%?s\n",
4059 inContext
->updateTTL
, inContext
->updateTTL
== 0, " (system will use a default value.)" );
4060 FPrintF( stdout
, " RData: %#H\n", inContext
->updateDataPtr
, (int) inContext
->updateDataLen
, INT_MAX
);
4062 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
4063 FPrintF( stdout
, "---\n" );
4066 //===========================================================================================================================
4067 // RegisterRecordContextFree
4068 //===========================================================================================================================
4070 static void RegisterRecordContextFree( RegisterRecordContext
*inContext
)
4072 DNSServiceForget( &inContext
->conRef
);
4073 ForgetMem( &inContext
->dataPtr
);
4074 ForgetMem( &inContext
->updateDataPtr
);
4078 //===========================================================================================================================
4079 // RegisterRecordCallback
4080 //===========================================================================================================================
4083 RegisterRecordCallback(
4084 DNSServiceRef inSDRef
,
4085 DNSRecordRef inRecordRef
,
4086 DNSServiceFlags inFlags
,
4087 DNSServiceErrorType inError
,
4090 RegisterRecordContext
* context
= (RegisterRecordContext
*) inContext
;
4094 Unused( inRecordRef
);
4098 gettimeofday( &now
, NULL
);
4099 FPrintF( stdout
, "%{du:time} Record registration result (error %#m)\n", &now
, inError
);
4101 if( !context
->didRegister
&& !inError
)
4103 context
->didRegister
= true;
4104 if( context
->updateDataPtr
)
4106 if( context
->updateDelayMs
> 0 )
4108 dispatch_after_f( dispatch_time_milliseconds( context
->updateDelayMs
), dispatch_get_main_queue(),
4109 context
, RegisterRecordUpdate
);
4113 RegisterRecordUpdate( context
);
4116 if( context
->lifetimeMs
== 0 )
4118 Exit( kExitReason_TimeLimit
);
4120 else if( context
->lifetimeMs
> 0 )
4122 dispatch_after_f( dispatch_time_milliseconds( context
->lifetimeMs
), dispatch_get_main_queue(),
4123 kExitReason_TimeLimit
, Exit
);
4128 //===========================================================================================================================
4129 // RegisterRecordUpdate
4130 //===========================================================================================================================
4132 static void RegisterRecordUpdate( void *inContext
)
4135 RegisterRecordContext
* const context
= (RegisterRecordContext
*) inContext
;
4137 err
= DNSServiceUpdateRecord( context
->conRef
, context
->recordRef
, 0, (uint16_t) context
->updateDataLen
,
4138 context
->updateDataPtr
, context
->updateTTL
);
4139 require_noerr( err
, exit
);
4142 if( err
) exit( 1 );
4145 //===========================================================================================================================
4147 //===========================================================================================================================
4151 DNSServiceRef mainRef
; // Main sdRef for shared connections.
4152 DNSServiceRef opRef
; // sdRef for the DNSServiceResolve operation.
4153 DNSServiceFlags flags
; // Flags argument for DNSServiceResolve().
4154 const char * name
; // Service name argument for DNSServiceResolve().
4155 const char * type
; // Service type argument for DNSServiceResolve().
4156 const char * domain
; // Domain argument for DNSServiceResolve().
4157 uint32_t ifIndex
; // Interface index argument for DNSServiceResolve().
4158 int timeLimitSecs
; // Time limit for the DNSServiceResolve operation in seconds.
4162 static void ResolvePrintPrologue( const ResolveContext
*inContext
);
4163 static void ResolveContextFree( ResolveContext
*inContext
);
4164 static void DNSSD_API
4166 DNSServiceRef inSDRef
,
4167 DNSServiceFlags inFlags
,
4168 uint32_t inInterfaceIndex
,
4169 DNSServiceErrorType inError
,
4170 const char * inFullName
,
4171 const char * inHostname
,
4174 const unsigned char * inTXTPtr
,
4177 static void ResolveCmd( void )
4180 DNSServiceRef sdRef
;
4181 ResolveContext
* context
= NULL
;
4182 dispatch_source_t signalSource
= NULL
;
4183 int useMainConnection
;
4185 // Set up SIGINT handler.
4187 signal( SIGINT
, SIG_IGN
);
4188 err
= DispatchSignalSourceCreate( SIGINT
, dispatch_get_main_queue(), Exit
, kExitReason_SIGINT
, &signalSource
);
4189 require_noerr( err
, exit
);
4190 dispatch_resume( signalSource
);
4194 context
= (ResolveContext
*) calloc( 1, sizeof( *context
) );
4195 require_action( context
, exit
, err
= kNoMemoryErr
);
4197 // Check command parameters.
4199 if( gResolve_TimeLimitSecs
< 0 )
4201 FPrintF( stderr
, "Invalid time limit: %d seconds.\n", gResolve_TimeLimitSecs
);
4206 // Create main connection.
4208 if( gConnectionOpt
)
4210 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
4211 require_noerr_quiet( err
, exit
);
4212 useMainConnection
= true;
4216 useMainConnection
= false;
4221 context
->flags
= GetDNSSDFlagsFromOpts();
4222 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
4224 // Get interface index.
4226 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
4227 require_noerr_quiet( err
, exit
);
4229 // Set remaining parameters.
4231 context
->name
= gResolve_Name
;
4232 context
->type
= gResolve_Type
;
4233 context
->domain
= gResolve_Domain
;
4234 context
->timeLimitSecs
= gResolve_TimeLimitSecs
;
4238 ResolvePrintPrologue( context
);
4242 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
4243 err
= DNSServiceResolve( &sdRef
, context
->flags
, context
->ifIndex
, context
->name
, context
->type
, context
->domain
,
4244 ResolveCallback
, NULL
);
4245 require_noerr( err
, exit
);
4247 context
->opRef
= sdRef
;
4248 if( !useMainConnection
)
4250 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
4251 require_noerr( err
, exit
);
4256 if( context
->timeLimitSecs
> 0 )
4258 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
4259 kExitReason_TimeLimit
, Exit
);
4264 dispatch_source_forget( &signalSource
);
4265 if( context
) ResolveContextFree( context
);
4266 if( err
) exit( 1 );
4269 //===========================================================================================================================
4271 //===========================================================================================================================
4273 static void ReconfirmCmd( void )
4276 uint8_t * rdataPtr
= NULL
;
4277 size_t rdataLen
= 0;
4278 DNSServiceFlags flags
;
4280 uint16_t type
, class;
4281 char ifName
[ kInterfaceNameBufLen
];
4285 flags
= GetDNSSDFlagsFromOpts();
4287 // Get interface index.
4289 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
4290 require_noerr_quiet( err
, exit
);
4294 err
= RecordTypeFromArgString( gReconfirmRecord_Type
, &type
);
4295 require_noerr( err
, exit
);
4299 if( gReconfirmRecord_Data
)
4301 err
= RecordDataFromArgString( gReconfirmRecord_Data
, &rdataPtr
, &rdataLen
);
4302 require_noerr_quiet( err
, exit
);
4305 // Get record class.
4307 if( gReconfirmRecord_Class
)
4309 err
= RecordClassFromArgString( gReconfirmRecord_Class
, &class );
4310 require_noerr( err
, exit
);
4314 class = kDNSServiceClass_IN
;
4319 FPrintF( stdout
, "Flags: %#{flags}\n", flags
, kDNSServiceFlagsDescriptors
);
4320 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) ifIndex
, InterfaceIndexToName( ifIndex
, ifName
) );
4321 FPrintF( stdout
, "Name: %s\n", gReconfirmRecord_Name
);
4322 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( type
), type
);
4323 FPrintF( stdout
, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN
) ? "IN" : "???", class );
4324 FPrintF( stdout
, "Data: %#H\n", rdataPtr
, (int) rdataLen
, INT_MAX
);
4325 FPrintF( stdout
, "---\n" );
4327 err
= DNSServiceReconfirmRecord( flags
, ifIndex
, gReconfirmRecord_Name
, type
, class, (uint16_t) rdataLen
, rdataPtr
);
4328 FPrintF( stdout
, "Error: %#m\n", err
);
4331 FreeNullSafe( rdataPtr
);
4332 if( err
) exit( 1 );
4335 //===========================================================================================================================
4336 // ResolvePrintPrologue
4337 //===========================================================================================================================
4339 static void ResolvePrintPrologue( const ResolveContext
*inContext
)
4341 const int timeLimitSecs
= inContext
->timeLimitSecs
;
4342 char ifName
[ kInterfaceNameBufLen
];
4344 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
4346 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
4347 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
4348 FPrintF( stdout
, "Name: %s\n", inContext
->name
);
4349 FPrintF( stdout
, "Type: %s\n", inContext
->type
);
4350 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
);
4351 FPrintF( stdout
, "Time limit: " );
4352 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
4353 else FPrintF( stdout
, "∞\n" );
4354 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
4355 FPrintF( stdout
, "---\n" );
4358 //===========================================================================================================================
4359 // ResolveContextFree
4360 //===========================================================================================================================
4362 static void ResolveContextFree( ResolveContext
*inContext
)
4364 DNSServiceForget( &inContext
->opRef
);
4365 DNSServiceForget( &inContext
->mainRef
);
4369 //===========================================================================================================================
4371 //===========================================================================================================================
4373 static void DNSSD_API
4375 DNSServiceRef inSDRef
,
4376 DNSServiceFlags inFlags
,
4377 uint32_t inInterfaceIndex
,
4378 DNSServiceErrorType inError
,
4379 const char * inFullName
,
4380 const char * inHostname
,
4383 const unsigned char * inTXTPtr
,
4387 char errorStr
[ 64 ];
4391 Unused( inContext
);
4393 gettimeofday( &now
, NULL
);
4395 if( inError
) SNPrintF( errorStr
, sizeof( errorStr
), " error %#m", inError
);
4397 FPrintF( stdout
, "%{du:time}: %s can be reached at %s:%u (interface %d)%?s\n",
4398 &now
, inFullName
, inHostname
, ntohs( inPort
), (int32_t) inInterfaceIndex
, inError
, errorStr
);
4401 FPrintF( stdout
, " TXT record: %#H\n", inTXTPtr
, (int) inTXTLen
, INT_MAX
);
4405 FPrintF( stdout
, " TXT record: %#{txt}\n", inTXTPtr
, (size_t) inTXTLen
);
4409 //===========================================================================================================================
4410 // GetAddrInfoPOSIXCmd
4411 //===========================================================================================================================
4413 #define AddressFamilyStr( X ) ( \
4414 ( (X) == AF_INET ) ? "inet" : \
4415 ( (X) == AF_INET6 ) ? "inet6" : \
4416 ( (X) == AF_UNSPEC ) ? "unspec" : \
4426 #define CaseFlagStringify( X ) { (X), # X }
4428 const FlagStringPair kGAIPOSIXFlagStringPairs
[] =
4430 #if( defined( AI_UNUSABLE ) )
4431 CaseFlagStringify( AI_UNUSABLE
),
4433 CaseFlagStringify( AI_NUMERICSERV
),
4434 CaseFlagStringify( AI_V4MAPPED
),
4435 CaseFlagStringify( AI_ADDRCONFIG
),
4436 #if( defined( AI_V4MAPPED_CFG ) )
4437 CaseFlagStringify( AI_V4MAPPED_CFG
),
4439 CaseFlagStringify( AI_ALL
),
4440 CaseFlagStringify( AI_NUMERICHOST
),
4441 CaseFlagStringify( AI_CANONNAME
),
4442 CaseFlagStringify( AI_PASSIVE
),
4446 static void GetAddrInfoPOSIXCmd( void )
4449 struct addrinfo hints
;
4451 const struct addrinfo
* addrInfo
;
4452 struct addrinfo
* addrInfoList
= NULL
;
4453 const FlagStringPair
* pair
;
4455 memset( &hints
, 0, sizeof( hints
) );
4456 hints
.ai_socktype
= SOCK_STREAM
;
4458 // Set hints address family.
4460 if( !gGAIPOSIX_Family
) hints
.ai_family
= AF_UNSPEC
;
4461 else if( strcasecmp( gGAIPOSIX_Family
, "inet" ) == 0 ) hints
.ai_family
= AF_INET
;
4462 else if( strcasecmp( gGAIPOSIX_Family
, "inet6" ) == 0 ) hints
.ai_family
= AF_INET6
;
4463 else if( strcasecmp( gGAIPOSIX_Family
, "unspec" ) == 0 ) hints
.ai_family
= AF_UNSPEC
;
4466 FPrintF( stderr
, "Invalid address family: %s.\n", gGAIPOSIX_Family
);
4473 if( gGAIPOSIXFlag_AddrConfig
) hints
.ai_flags
|= AI_ADDRCONFIG
;
4474 if( gGAIPOSIXFlag_All
) hints
.ai_flags
|= AI_ALL
;
4475 if( gGAIPOSIXFlag_CanonName
) hints
.ai_flags
|= AI_CANONNAME
;
4476 if( gGAIPOSIXFlag_NumericHost
) hints
.ai_flags
|= AI_NUMERICHOST
;
4477 if( gGAIPOSIXFlag_NumericServ
) hints
.ai_flags
|= AI_NUMERICSERV
;
4478 if( gGAIPOSIXFlag_Passive
) hints
.ai_flags
|= AI_PASSIVE
;
4479 if( gGAIPOSIXFlag_V4Mapped
) hints
.ai_flags
|= AI_V4MAPPED
;
4480 #if( defined( AI_V4MAPPED_CFG ) )
4481 if( gGAIPOSIXFlag_V4MappedCFG
) hints
.ai_flags
|= AI_V4MAPPED_CFG
;
4483 #if( defined( AI_DEFAULT ) )
4484 if( gGAIPOSIXFlag_Default
) hints
.ai_flags
|= AI_DEFAULT
;
4486 #if( defined( AI_UNUSABLE ) )
4487 if( gGAIPOSIXFlag_Unusable
) hints
.ai_flags
|= AI_UNUSABLE
;
4492 FPrintF( stdout
, "Hostname: %s\n", gGAIPOSIX_HostName
);
4493 FPrintF( stdout
, "Servname: %s\n", gGAIPOSIX_ServName
);
4494 FPrintF( stdout
, "Address family: %s\n", AddressFamilyStr( hints
.ai_family
) );
4495 FPrintF( stdout
, "Flags: 0x%X < ", hints
.ai_flags
);
4496 for( pair
= kGAIPOSIXFlagStringPairs
; pair
->str
!= NULL
; ++pair
)
4498 if( ( (unsigned int) hints
.ai_flags
) & pair
->flag
) FPrintF( stdout
, "%s ", pair
->str
);
4500 FPrintF( stdout
, ">\n" );
4501 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
4502 FPrintF( stdout
, "---\n" );
4504 // Call getaddrinfo().
4506 err
= getaddrinfo( gGAIPOSIX_HostName
, gGAIPOSIX_ServName
, &hints
, &addrInfoList
);
4507 gettimeofday( &now
, NULL
);
4510 FPrintF( stderr
, "Error %d: %s.\n", err
, gai_strerror( err
) );
4516 for( addrInfo
= addrInfoList
; addrInfo
; addrInfo
= addrInfo
->ai_next
) { ++addrCount
; }
4518 FPrintF( stdout
, "Addresses (%d total):\n", addrCount
);
4519 for( addrInfo
= addrInfoList
; addrInfo
; addrInfo
= addrInfo
->ai_next
)
4521 FPrintF( stdout
, "%##a\n", addrInfo
->ai_addr
);
4524 FPrintF( stdout
, "---\n" );
4525 FPrintF( stdout
, "End time: %{du:time}\n", &now
);
4528 if( addrInfoList
) freeaddrinfo( addrInfoList
);
4529 if( err
) exit( 1 );
4532 //===========================================================================================================================
4534 //===========================================================================================================================
4536 #define kIP6ARPADomainStr "ip6.arpa."
4538 static void ReverseLookupCmd( void )
4541 QueryRecordContext
* context
= NULL
;
4542 DNSServiceRef sdRef
;
4543 dispatch_source_t signalSource
= NULL
;
4545 uint8_t ipv6Addr
[ 16 ];
4546 char recordName
[ ( 16 * 4 ) + sizeof( kIP6ARPADomainStr
) ];
4547 int useMainConnection
;
4548 const char * endPtr
;
4550 // Set up SIGINT handler.
4552 signal( SIGINT
, SIG_IGN
);
4553 err
= DispatchSignalSourceCreate( SIGINT
, dispatch_get_main_queue(), Exit
, kExitReason_SIGINT
, &signalSource
);
4554 require_noerr( err
, exit
);
4555 dispatch_resume( signalSource
);
4559 context
= (QueryRecordContext
*) calloc( 1, sizeof( *context
) );
4560 require_action( context
, exit
, err
= kNoMemoryErr
);
4562 // Check command parameters.
4564 if( gReverseLookup_TimeLimitSecs
< 0 )
4566 FPrintF( stderr
, "Invalid time limit: %d s.\n", gReverseLookup_TimeLimitSecs
);
4571 // Create main connection.
4573 if( gConnectionOpt
)
4575 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
4576 require_noerr_quiet( err
, exit
);
4577 useMainConnection
= true;
4581 useMainConnection
= false;
4586 context
->flags
= GetDNSSDFlagsFromOpts();
4587 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
4589 // Get interface index.
4591 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
4592 require_noerr_quiet( err
, exit
);
4594 // Create reverse lookup record name.
4596 err
= _StringToIPv4Address( gReverseLookup_IPAddr
, kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
,
4597 &ipv4Addr
, NULL
, NULL
, NULL
, &endPtr
);
4598 if( err
|| ( *endPtr
!= '\0' ) )
4603 err
= _StringToIPv6Address( gReverseLookup_IPAddr
,
4604 kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
| kStringToIPAddressFlagsNoScope
,
4605 ipv6Addr
, NULL
, NULL
, NULL
, &endPtr
);
4606 if( err
|| ( *endPtr
!= '\0' ) )
4608 FPrintF( stderr
, "Invalid IP address: \"%s\".\n", gReverseLookup_IPAddr
);
4613 for( i
= 15; i
>= 0; --i
)
4615 *dst
++ = kHexDigitsLowercase
[ ipv6Addr
[ i
] & 0x0F ];
4617 *dst
++ = kHexDigitsLowercase
[ ipv6Addr
[ i
] >> 4 ];
4620 strcpy_literal( dst
, kIP6ARPADomainStr
);
4621 check( ( strlen( recordName
) + 1 ) <= sizeof( recordName
) );
4625 SNPrintF( recordName
, sizeof( recordName
), "%u.%u.%u.%u.in-addr.arpa.",
4627 ( ipv4Addr
>> 8 ) & 0xFF,
4628 ( ipv4Addr
>> 16 ) & 0xFF,
4629 ( ipv4Addr
>> 24 ) & 0xFF );
4632 // Set remaining parameters.
4634 context
->recordName
= recordName
;
4635 context
->recordType
= kDNSServiceType_PTR
;
4636 context
->timeLimitSecs
= gReverseLookup_TimeLimitSecs
;
4637 context
->oneShotMode
= gReverseLookup_OneShot
? true : false;
4641 QueryRecordPrintPrologue( context
);
4645 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
4646 err
= DNSServiceQueryRecord( &sdRef
, context
->flags
, context
->ifIndex
, context
->recordName
, context
->recordType
,
4647 kDNSServiceClass_IN
, QueryRecordCallback
, context
);
4648 require_noerr( err
, exit
);
4650 context
->opRef
= sdRef
;
4651 if( !useMainConnection
)
4653 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
4654 require_noerr( err
, exit
);
4659 if( context
->timeLimitSecs
> 0 )
4661 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
4662 kExitReason_TimeLimit
, Exit
);
4667 dispatch_source_forget( &signalSource
);
4668 if( context
) QueryRecordContextFree( context
);
4669 if( err
) exit( 1 );
4672 //===========================================================================================================================
4674 //===========================================================================================================================
4678 DNSServiceRef mainRef
; // Main sdRef for shared connection.
4679 DNSServiceRef opRef
; // sdRef for the DNSServiceNATPortMappingCreate operation.
4680 DNSServiceFlags flags
; // Flags for DNSServiceNATPortMappingCreate operation.
4681 uint32_t ifIndex
; // Interface index argument for DNSServiceNATPortMappingCreate operation.
4682 DNSServiceProtocol protocols
; // Protocols argument for DNSServiceNATPortMappingCreate operation.
4683 uint32_t ttl
; // TTL argument for DNSServiceNATPortMappingCreate operation.
4684 uint16_t internalPort
; // Internal port argument for DNSServiceNATPortMappingCreate operation.
4685 uint16_t externalPort
; // External port argument for DNSServiceNATPortMappingCreate operation.
4686 Boolean printedHeader
; // True if results header was printed.
4688 } PortMappingContext
;
4690 static void PortMappingPrintPrologue( const PortMappingContext
*inContext
);
4691 static void PortMappingContextFree( PortMappingContext
*inContext
);
4692 static void DNSSD_API
4693 PortMappingCallback(
4694 DNSServiceRef inSDRef
,
4695 DNSServiceFlags inFlags
,
4696 uint32_t inInterfaceIndex
,
4697 DNSServiceErrorType inError
,
4698 uint32_t inExternalIPv4Address
,
4699 DNSServiceProtocol inProtocol
,
4700 uint16_t inInternalPort
,
4701 uint16_t inExternalPort
,
4705 static void PortMappingCmd( void )
4708 PortMappingContext
* context
= NULL
;
4709 DNSServiceRef sdRef
;
4710 dispatch_source_t signalSource
= NULL
;
4711 int useMainConnection
;
4713 // Set up SIGINT handler.
4715 signal( SIGINT
, SIG_IGN
);
4716 err
= DispatchSignalSourceCreate( SIGINT
, dispatch_get_main_queue(), Exit
, kExitReason_SIGINT
, &signalSource
);
4717 require_noerr( err
, exit
);
4718 dispatch_resume( signalSource
);
4722 context
= (PortMappingContext
*) calloc( 1, sizeof( *context
) );
4723 require_action( context
, exit
, err
= kNoMemoryErr
);
4725 // Check command parameters.
4727 if( ( gPortMapping_InternalPort
< 0 ) || ( gPortMapping_InternalPort
> UINT16_MAX
) )
4729 FPrintF( stderr
, "Internal port number %d is out-of-range.\n", gPortMapping_InternalPort
);
4734 if( ( gPortMapping_ExternalPort
< 0 ) || ( gPortMapping_ExternalPort
> UINT16_MAX
) )
4736 FPrintF( stderr
, "External port number %d is out-of-range.\n", gPortMapping_ExternalPort
);
4741 // Create main connection.
4743 if( gConnectionOpt
)
4745 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
4746 require_noerr_quiet( err
, exit
);
4747 useMainConnection
= true;
4751 useMainConnection
= false;
4756 context
->flags
= GetDNSSDFlagsFromOpts();
4757 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
4759 // Get interface index.
4761 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
4762 require_noerr_quiet( err
, exit
);
4764 // Set remaining parameters.
4766 if( gPortMapping_ProtocolTCP
) context
->protocols
|= kDNSServiceProtocol_TCP
;
4767 if( gPortMapping_ProtocolUDP
) context
->protocols
|= kDNSServiceProtocol_UDP
;
4768 context
->ttl
= (uint32_t) gPortMapping_TTL
;
4769 context
->internalPort
= (uint16_t) gPortMapping_InternalPort
;
4770 context
->externalPort
= (uint16_t) gPortMapping_ExternalPort
;
4774 PortMappingPrintPrologue( context
);
4778 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
4779 err
= DNSServiceNATPortMappingCreate( &sdRef
, context
->flags
, context
->ifIndex
, context
->protocols
,
4780 htons( context
->internalPort
), htons( context
->externalPort
), context
->ttl
, PortMappingCallback
, context
);
4781 require_noerr( err
, exit
);
4783 context
->opRef
= sdRef
;
4784 if( !useMainConnection
)
4786 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
4787 require_noerr( err
, exit
);
4793 dispatch_source_forget( &signalSource
);
4794 if( context
) PortMappingContextFree( context
);
4795 if( err
) exit( 1 );
4798 //===========================================================================================================================
4799 // PortMappingPrintPrologue
4800 //===========================================================================================================================
4802 static void PortMappingPrintPrologue( const PortMappingContext
*inContext
)
4804 char ifName
[ kInterfaceNameBufLen
];
4806 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
4808 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
4809 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
4810 FPrintF( stdout
, "Protocols: %#{flags}\n", inContext
->protocols
, kDNSServiceProtocolDescriptors
);
4811 FPrintF( stdout
, "Internal Port: %u\n", inContext
->internalPort
);
4812 FPrintF( stdout
, "External Port: %u\n", inContext
->externalPort
);
4813 FPrintF( stdout
, "TTL: %u%?s\n", inContext
->ttl
, !inContext
->ttl
,
4814 " (system will use a default value.)" );
4815 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
4816 FPrintF( stdout
, "---\n" );
4820 //===========================================================================================================================
4821 // PortMappingContextFree
4822 //===========================================================================================================================
4824 static void PortMappingContextFree( PortMappingContext
*inContext
)
4826 DNSServiceForget( &inContext
->opRef
);
4827 DNSServiceForget( &inContext
->mainRef
);
4831 //===========================================================================================================================
4832 // PortMappingCallback
4833 //===========================================================================================================================
4835 static void DNSSD_API
4836 PortMappingCallback(
4837 DNSServiceRef inSDRef
,
4838 DNSServiceFlags inFlags
,
4839 uint32_t inInterfaceIndex
,
4840 DNSServiceErrorType inError
,
4841 uint32_t inExternalIPv4Address
,
4842 DNSServiceProtocol inProtocol
,
4843 uint16_t inInternalPort
,
4844 uint16_t inExternalPort
,
4848 PortMappingContext
* const context
= (PortMappingContext
*) inContext
;
4850 char errorStr
[ 128 ];
4855 gettimeofday( &now
, NULL
);
4857 if( inError
) SNPrintF( errorStr
, sizeof( errorStr
), " (error: %#m)", inError
);
4858 if( !context
->printedHeader
)
4860 FPrintF( stdout
, "%-26s IF %7s %15s %7s %6s Protocol\n", "Timestamp", "IntPort", "ExtAddr", "ExtPort", "TTL" );
4861 context
->printedHeader
= true;
4863 FPrintF( stdout
, "%{du:time} %2u %7u %15.4a %7u %6u %#{flags}%?s\n",
4864 &now
, inInterfaceIndex
, ntohs( inInternalPort
), &inExternalIPv4Address
, ntohs( inExternalPort
), inTTL
,
4865 inProtocol
, kDNSServiceProtocolDescriptors
, inError
, errorStr
);
4868 #if( TARGET_OS_DARWIN )
4869 //===========================================================================================================================
4871 //===========================================================================================================================
4875 dispatch_queue_t queue
; // Serial queue for command's events.
4876 dispatch_semaphore_t doneSem
; // Semaphore to signal when underlying command operation is done.
4877 sockaddr_ip local
; // Connection's local IP address and port.
4878 sockaddr_ip remote
; // Connection's remote IP address and port.
4879 DNSServiceFlags flags
; // Flags to pass to DNSServiceSleepKeepalive_sockaddr().
4880 unsigned int timeout
; // Timeout to pass to DNSServiceSleepKeepalive_sockaddr().
4881 DNSServiceRef keepalive
; // DNSServiceSleepKeepalive_sockaddr operation.
4882 dispatch_source_t sourceSigInt
; // Dispatch source for SIGINT.
4883 dispatch_source_t sourceSigTerm
; // Dispatch source for SIGTERM.
4884 OSStatus error
; // Command's error.
4886 } RegisterKACmdContext
;
4888 static void _RegisterKACmdFree( RegisterKACmdContext
*inCmd
);
4889 static void _RegisterKACmdStart( void *inContext
);
4890 static OSStatus
_RegisterKACmdGetIPAddressArgument( const char *inArgStr
, const char *inArgName
, sockaddr_ip
*outSA
);
4892 static void RegisterKACmd( void )
4895 RegisterKACmdContext
* cmd
= NULL
;
4897 if( !SOFT_LINK_HAS_FUNCTION( system_dnssd
, DNSServiceSleepKeepalive_sockaddr
) )
4899 FPrintF( stderr
, "error: Failed to soft link DNSServiceSleepKeepalive_sockaddr from libsystem_dnssd.\n" );
4903 cmd
= (RegisterKACmdContext
*) calloc( 1, sizeof( *cmd
) );
4904 require_action( cmd
, exit
, err
= kNoMemoryErr
);
4906 err
= _RegisterKACmdGetIPAddressArgument( gRegisterKA_LocalAddress
, "local IP address", &cmd
->local
);
4907 require_noerr_quiet( err
, exit
);
4909 err
= _RegisterKACmdGetIPAddressArgument( gRegisterKA_RemoteAddress
, "remote IP address", &cmd
->remote
);
4910 require_noerr_quiet( err
, exit
);
4912 err
= CheckIntegerArgument( gRegisterKA_Timeout
, "timeout", 0, INT_MAX
);
4913 require_noerr_quiet( err
, exit
);
4915 cmd
->flags
= GetDNSSDFlagsFromOpts();
4916 cmd
->timeout
= (unsigned int) gRegisterKA_Timeout
;
4920 cmd
->queue
= dispatch_queue_create( "com.apple.dnssdutil.registerka-command", DISPATCH_QUEUE_SERIAL
);
4921 require_action( cmd
->queue
, exit
, err
= kNoResourcesErr
);
4923 cmd
->doneSem
= dispatch_semaphore_create( 0 );
4924 require_action( cmd
->doneSem
, exit
, err
= kNoResourcesErr
);
4926 dispatch_async_f( cmd
->queue
, cmd
, _RegisterKACmdStart
);
4927 dispatch_semaphore_wait( cmd
->doneSem
, DISPATCH_TIME_FOREVER
);
4928 if( cmd
->error
) err
= cmd
->error
;
4930 FPrintF( stdout
, "---\n" );
4931 FPrintF( stdout
, "End time: %{du:time}\n", NULL
);
4934 if( cmd
) _RegisterKACmdFree( cmd
);
4935 gExitCode
= err
? 1 : 0;
4938 //===========================================================================================================================
4940 static void _RegisterKACmdFree( RegisterKACmdContext
*inCmd
)
4942 check( !inCmd
->keepalive
);
4943 check( !inCmd
->sourceSigInt
);
4944 check( !inCmd
->sourceSigTerm
);
4945 dispatch_forget( &inCmd
->queue
);
4946 dispatch_forget( &inCmd
->doneSem
);
4950 //===========================================================================================================================
4952 static void _RegisterKACmdStop( RegisterKACmdContext
*inCmd
, OSStatus inError
);
4953 static void _RegisterKACmdSignalHandler( void *inContext
);
4954 static void DNSSD_API
_RegisterKACmdKeepaliveCallback( DNSServiceRef inSDRef
, DNSServiceErrorType inError
, void *inCtx
);
4956 static void _RegisterKACmdStart( void *inContext
)
4959 RegisterKACmdContext
* const cmd
= (RegisterKACmdContext
*) inContext
;
4961 signal( SIGINT
, SIG_IGN
);
4962 err
= DispatchSignalSourceCreate( SIGINT
, cmd
->queue
, _RegisterKACmdSignalHandler
, cmd
, &cmd
->sourceSigInt
);
4963 require_noerr( err
, exit
);
4964 dispatch_resume( cmd
->sourceSigInt
);
4966 signal( SIGTERM
, SIG_IGN
);
4967 err
= DispatchSignalSourceCreate( SIGTERM
, cmd
->queue
, _RegisterKACmdSignalHandler
, cmd
, &cmd
->sourceSigTerm
);
4968 require_noerr( err
, exit
);
4969 dispatch_resume( cmd
->sourceSigTerm
);
4971 FPrintF( stdout
, "Flags: %#{flags}\n", cmd
->flags
, kDNSServiceFlagsDescriptors
);
4972 FPrintF( stdout
, "Local: %##a\n", &cmd
->local
.sa
);
4973 FPrintF( stdout
, "Remote: %##a\n", &cmd
->remote
.sa
);
4974 FPrintF( stdout
, "Timeout: %u\n", cmd
->timeout
);
4975 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
4976 FPrintF( stdout
, "---\n" );
4978 err
= soft_DNSServiceSleepKeepalive_sockaddr( &cmd
->keepalive
, cmd
->flags
, &cmd
->local
.sa
, &cmd
->remote
.sa
,
4979 cmd
->timeout
, _RegisterKACmdKeepaliveCallback
, cmd
);
4980 require_noerr( err
, exit
);
4982 err
= DNSServiceSetDispatchQueue( cmd
->keepalive
, cmd
->queue
);
4983 require_noerr( err
, exit
);
4986 if( err
) _RegisterKACmdStop( cmd
, err
);
4989 //===========================================================================================================================
4991 static OSStatus
_RegisterKACmdGetIPAddressArgument( const char *inArgStr
, const char *inArgName
, sockaddr_ip
*outSA
)
4996 err
= StringToSockAddr( inArgStr
, &sip
, sizeof( sip
), NULL
);
4997 if( !err
&& ( ( sip
.sa
.sa_family
== AF_INET
) || ( sip
.sa
.sa_family
== AF_INET6
) ) )
4999 if( outSA
) SockAddrCopy( &sip
, outSA
);
5003 FPrintF( stderr
, "error: Invalid %s: '%s'\n", inArgName
, inArgStr
);
5009 //===========================================================================================================================
5011 static void _RegisterKACmdStop( RegisterKACmdContext
*inCmd
, OSStatus inError
)
5013 if( !inCmd
->error
) inCmd
->error
= inError
;
5014 DNSServiceForget( &inCmd
->keepalive
);
5015 dispatch_source_forget( &inCmd
->sourceSigInt
);
5016 dispatch_source_forget( &inCmd
->sourceSigTerm
);
5017 dispatch_semaphore_signal( inCmd
->doneSem
);
5020 //===========================================================================================================================
5022 static void _RegisterKACmdSignalHandler( void *inContext
)
5024 RegisterKACmdContext
* const cmd
= (RegisterKACmdContext
*) inContext
;
5026 _RegisterKACmdStop( cmd
, kNoErr
);
5029 //===========================================================================================================================
5031 static void DNSSD_API
_RegisterKACmdKeepaliveCallback( DNSServiceRef inSDRef
, DNSServiceErrorType inError
, void *inCtx
)
5033 RegisterKACmdContext
* const cmd
= (RegisterKACmdContext
*) inCtx
;
5037 FPrintF( stdout
, "%{du:time} Record registration result: %#m\n", NULL
, inError
);
5038 if( !cmd
->error
) cmd
->error
= inError
;
5040 #endif // TARGET_OS_DARWIN
5042 //===========================================================================================================================
5044 //===========================================================================================================================
5046 typedef struct BrowseAllConnection BrowseAllConnection
;
5050 ServiceBrowserRef browser
; // Service browser.
5051 ServiceBrowserResults
* results
; // Results from the service browser.
5052 BrowseAllConnection
* connectionList
; // List of connections.
5053 dispatch_source_t connectionTimer
; // Timer for connection timeout.
5054 int connectionPendingCount
; // Number of pending connections.
5055 int connectionTimeoutSecs
; // Timeout value for connections in seconds.
5059 struct BrowseAllConnection
5061 BrowseAllConnection
* next
; // Next connection object in list.
5062 sockaddr_ip sip
; // IPv4 or IPv6 address to connect to.
5063 uint16_t port
; // TCP port to connect to.
5064 AsyncConnectionRef asyncCnx
; // AsyncConnection object to handle the actual connection.
5065 OSStatus status
; // Status of connection. NoErr means connection succeeded.
5066 CFTimeInterval connectTimeSecs
; // Time it took to connect in seconds.
5067 int32_t refCount
; // This object's reference count.
5068 BrowseAllContext
* context
; // Back pointer to parent context.
5071 static void _BrowseAllContextFree( BrowseAllContext
*inContext
);
5072 static void _BrowseAllServiceBrowserCallback( ServiceBrowserResults
*inResults
, OSStatus inError
, void *inContext
);
5074 _BrowseAllConnectionCreate(
5075 const struct sockaddr
* inSockAddr
,
5077 BrowseAllContext
* inContext
,
5078 BrowseAllConnection
** outConnection
);
5079 static void _BrowseAllConnectionRetain( BrowseAllConnection
*inConnection
);
5080 static void _BrowseAllConnectionRelease( BrowseAllConnection
*inConnection
);
5081 static void _BrowseAllConnectionProgress( int inPhase
, const void *inDetails
, void *inArg
);
5082 static void _BrowseAllConnectionHandler( SocketRef inSock
, OSStatus inError
, void *inArg
);
5083 static void _BrowseAllExit( void *inContext
);
5085 static Boolean
_IsServiceTypeTCP( const char *inServiceType
);
5087 static void BrowseAllCmd( void )
5090 BrowseAllContext
* context
= NULL
;
5093 char ifName
[ kInterfaceNameBufLen
];
5095 // Check parameters.
5097 if( gBrowseAll_BrowseTimeSecs
<= 0 )
5099 FPrintF( stdout
, "Invalid browse time: %d seconds.\n", gBrowseAll_BrowseTimeSecs
);
5104 context
= (BrowseAllContext
*) calloc( 1, sizeof( *context
) );
5105 require_action( context
, exit
, err
= kNoMemoryErr
);
5107 context
->connectionTimeoutSecs
= gBrowseAll_ConnectTimeout
;
5108 #if( TARGET_OS_POSIX )
5109 // Increase the open file descriptor limit for connection sockets.
5111 if( context
->connectionTimeoutSecs
> 0 )
5113 struct rlimit fdLimits
;
5115 err
= getrlimit( RLIMIT_NOFILE
, &fdLimits
);
5116 err
= map_global_noerr_errno( err
);
5117 require_noerr( err
, exit
);
5119 if( fdLimits
.rlim_cur
< 4096 )
5121 fdLimits
.rlim_cur
= 4096;
5122 err
= setrlimit( RLIMIT_NOFILE
, &fdLimits
);
5123 err
= map_global_noerr_errno( err
);
5124 require_noerr( err
, exit
);
5129 // Get interface index.
5131 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
5132 require_noerr_quiet( err
, exit
);
5136 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) ifIndex
, InterfaceIndexToName( ifIndex
, ifName
) );
5137 FPrintF( stdout
, "Service types: ");
5138 if( gBrowseAll_ServiceTypesCount
> 0 )
5140 FPrintF( stdout
, "%s", gBrowseAll_ServiceTypes
[ 0 ] );
5141 for( i
= 1; i
< gBrowseAll_ServiceTypesCount
; ++i
)
5143 FPrintF( stdout
, ", %s", gBrowseAll_ServiceTypes
[ i
] );
5145 FPrintF( stdout
, "\n" );
5149 FPrintF( stdout
, "all services\n" );
5151 FPrintF( stdout
, "Domain: %s\n", gBrowseAll_Domain
? gBrowseAll_Domain
: "default domains" );
5152 FPrintF( stdout
, "Browse time: %d second%?c\n", gBrowseAll_BrowseTimeSecs
, gBrowseAll_BrowseTimeSecs
!= 1, 's' );
5153 FPrintF( stdout
, "Connect timeout: %d second%?c\n",
5154 context
->connectionTimeoutSecs
, context
->connectionTimeoutSecs
!= 1, 's' );
5155 FPrintF( stdout
, "IncludeAWDL: %s\n", gDNSSDFlag_IncludeAWDL
? "yes" : "no" );
5156 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
5157 FPrintF( stdout
, "---\n" );
5159 err
= ServiceBrowserCreate( dispatch_get_main_queue(), ifIndex
, gBrowseAll_Domain
,
5160 (unsigned int) gBrowseAll_BrowseTimeSecs
, gDNSSDFlag_IncludeAWDL
? true : false, &context
->browser
);
5161 require_noerr( err
, exit
);
5163 for( i
= 0; i
< gBrowseAll_ServiceTypesCount
; ++i
)
5165 err
= ServiceBrowserAddServiceType( context
->browser
, gBrowseAll_ServiceTypes
[ i
] );
5166 require_noerr( err
, exit
);
5168 ServiceBrowserSetCallback( context
->browser
, _BrowseAllServiceBrowserCallback
, context
);
5169 ServiceBrowserStart( context
->browser
);
5173 if( context
) _BrowseAllContextFree( context
);
5174 if( err
) exit( 1 );
5177 //===========================================================================================================================
5178 // _BrowseAllContextFree
5179 //===========================================================================================================================
5181 static void _BrowseAllContextFree( BrowseAllContext
*inContext
)
5183 check( !inContext
->browser
);
5184 check( !inContext
->connectionTimer
);
5185 check( !inContext
->connectionList
);
5186 ForgetServiceBrowserResults( &inContext
->results
);
5190 //===========================================================================================================================
5191 // _BrowseAllServiceBrowserCallback
5192 //===========================================================================================================================
5194 #define kDiscardProtocolPort 9
5196 static void _BrowseAllServiceBrowserCallback( ServiceBrowserResults
*inResults
, OSStatus inError
, void *inContext
)
5199 BrowseAllContext
* const context
= (BrowseAllContext
*) inContext
;
5201 SBRServiceType
* type
;
5202 SBRServiceInstance
* instance
;
5203 SBRIPAddress
* ipaddr
;
5207 require_action( inResults
, exit
, err
= kUnexpectedErr
);
5209 check( !context
->results
);
5210 context
->results
= inResults
;
5211 ServiceBrowserResultsRetain( context
->results
);
5213 check( context
->connectionPendingCount
== 0 );
5214 if( context
->connectionTimeoutSecs
> 0 )
5216 BrowseAllConnection
* connection
;
5217 BrowseAllConnection
** connectionPtr
= &context
->connectionList
;
5218 char destination
[ kSockAddrStringMaxSize
];
5220 for( domain
= context
->results
->domainList
; domain
; domain
= domain
->next
)
5222 for( type
= domain
->typeList
; type
; type
= type
->next
)
5224 if( !_IsServiceTypeTCP( type
->name
) ) continue;
5225 for( instance
= type
->instanceList
; instance
; instance
= instance
->next
)
5227 if( instance
->port
== kDiscardProtocolPort
) continue;
5228 for( ipaddr
= instance
->ipaddrList
; ipaddr
; ipaddr
= ipaddr
->next
)
5230 err
= _BrowseAllConnectionCreate( &ipaddr
->sip
.sa
, instance
->port
, context
, &connection
);
5231 require_noerr( err
, exit
);
5233 *connectionPtr
= connection
;
5234 connectionPtr
= &connection
->next
;
5236 err
= SockAddrToString( &ipaddr
->sip
, kSockAddrStringFlagsNoPort
, destination
);
5240 err
= AsyncConnection_Connect( &connection
->asyncCnx
, destination
, -instance
->port
,
5241 kAsyncConnectionFlag_P2P
, kAsyncConnectionNoTimeout
,
5242 kSocketBufferSize_DontSet
, kSocketBufferSize_DontSet
,
5243 _BrowseAllConnectionProgress
, connection
, _BrowseAllConnectionHandler
, connection
,
5244 dispatch_get_main_queue() );
5249 _BrowseAllConnectionRetain( connection
);
5250 connection
->status
= kInProgressErr
;
5251 ++context
->connectionPendingCount
;
5255 connection
->status
= err
;
5263 if( context
->connectionPendingCount
> 0 )
5265 check( !context
->connectionTimer
);
5266 err
= DispatchTimerCreate( dispatch_time_seconds( context
->connectionTimeoutSecs
), DISPATCH_TIME_FOREVER
,
5267 100 * kNanosecondsPerMillisecond
, NULL
, _BrowseAllExit
, NULL
, context
, &context
->connectionTimer
);
5268 require_noerr( err
, exit
);
5269 dispatch_resume( context
->connectionTimer
);
5273 dispatch_async_f( dispatch_get_main_queue(), context
, _BrowseAllExit
);
5278 ForgetCF( &context
->browser
);
5279 if( err
) exit( 1 );
5282 //===========================================================================================================================
5283 // _BrowseAllConnectionCreate
5284 //===========================================================================================================================
5287 _BrowseAllConnectionCreate(
5288 const struct sockaddr
* inSockAddr
,
5290 BrowseAllContext
* inContext
,
5291 BrowseAllConnection
** outConnection
)
5294 BrowseAllConnection
* obj
;
5296 obj
= (BrowseAllConnection
*) calloc( 1, sizeof( *obj
) );
5297 require_action( obj
, exit
, err
= kNoMemoryErr
);
5300 SockAddrCopy( inSockAddr
, &obj
->sip
);
5302 obj
->context
= inContext
;
5304 *outConnection
= obj
;
5311 //===========================================================================================================================
5312 // _BrowseAllConnectionRetain
5313 //===========================================================================================================================
5315 static void _BrowseAllConnectionRetain( BrowseAllConnection
*inConnection
)
5317 ++inConnection
->refCount
;
5320 //===========================================================================================================================
5321 // _BrowseAllConnectionRelease
5322 //===========================================================================================================================
5324 static void _BrowseAllConnectionRelease( BrowseAllConnection
*inConnection
)
5326 if( --inConnection
->refCount
== 0 ) free( inConnection
);
5329 //===========================================================================================================================
5330 // _BrowseAllConnectionProgress
5331 //===========================================================================================================================
5333 static void _BrowseAllConnectionProgress( int inPhase
, const void *inDetails
, void *inArg
)
5335 BrowseAllConnection
* const connection
= (BrowseAllConnection
*) inArg
;
5337 if( inPhase
== kAsyncConnectionPhase_Connected
)
5339 const AsyncConnectedInfo
* const info
= (AsyncConnectedInfo
*) inDetails
;
5341 connection
->connectTimeSecs
= info
->connectSecs
;
5345 //===========================================================================================================================
5346 // _BrowseAllConnectionHandler
5347 //===========================================================================================================================
5349 static void _BrowseAllConnectionHandler( SocketRef inSock
, OSStatus inError
, void *inArg
)
5351 BrowseAllConnection
* const connection
= (BrowseAllConnection
*) inArg
;
5352 BrowseAllContext
* const context
= connection
->context
;
5354 connection
->status
= inError
;
5355 ForgetSocket( &inSock
);
5358 check( context
->connectionPendingCount
> 0 );
5359 if( ( --context
->connectionPendingCount
== 0 ) && context
->connectionTimer
)
5361 dispatch_source_forget( &context
->connectionTimer
);
5362 dispatch_async_f( dispatch_get_main_queue(), context
, _BrowseAllExit
);
5365 _BrowseAllConnectionRelease( connection
);
5368 //===========================================================================================================================
5370 //===========================================================================================================================
5372 #define Indent( X ) ( (X) * 4 ), ""
5374 static void _BrowseAllExit( void *inContext
)
5376 BrowseAllContext
* const context
= (BrowseAllContext
*) inContext
;
5378 SBRServiceType
* type
;
5379 SBRServiceInstance
* instance
;
5380 SBRIPAddress
* ipaddr
;
5381 char textBuf
[ 512 ];
5382 #if( TARGET_OS_POSIX )
5383 const Boolean useColor
= isatty( STDOUT_FILENO
) ? true : false;
5386 dispatch_source_forget( &context
->connectionTimer
);
5388 for( domain
= context
->results
->domainList
; domain
; domain
= domain
->next
)
5390 FPrintF( stdout
, "%s\n\n", domain
->name
);
5392 for( type
= domain
->typeList
; type
; type
= type
->next
)
5394 const char * description
;
5395 const Boolean serviceTypeIsTCP
= _IsServiceTypeTCP( type
->name
);
5397 description
= ServiceTypeDescription( type
->name
);
5398 if( description
) FPrintF( stdout
, "%*s" "%s (%s)\n\n", Indent( 1 ), description
, type
->name
);
5399 else FPrintF( stdout
, "%*s" "%s\n\n", Indent( 1 ), type
->name
);
5401 for( instance
= type
->instanceList
; instance
; instance
= instance
->next
)
5403 char * dst
= textBuf
;
5404 char * const lim
= &textBuf
[ countof( textBuf
) ];
5405 char ifname
[ IF_NAMESIZE
+ 1 ];
5407 SNPrintF_Add( &dst
, lim
, "%s via ", instance
->name
);
5408 if( instance
->ifIndex
== 0 )
5410 SNPrintF_Add( &dst
, lim
, "the Internet" );
5412 else if( if_indextoname( instance
->ifIndex
, ifname
) )
5414 NetTransportType netType
;
5416 SocketGetInterfaceInfo( kInvalidSocketRef
, ifname
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &netType
);
5417 SNPrintF_Add( &dst
, lim
, "%s (%s)",
5418 ( netType
== kNetTransportType_Ethernet
) ? "Ethernet" : NetTransportTypeToString( netType
),
5423 SNPrintF_Add( &dst
, lim
, "interface index %u", instance
->ifIndex
);
5425 FPrintF( stdout
, "%*s" "%-55s %4llu.%03llu ms\n\n",
5426 Indent( 2 ), textBuf
, instance
->discoverTimeUs
/ 1000, instance
->discoverTimeUs
% 1000 );
5428 if( instance
->hostname
)
5430 SNPrintF( textBuf
, sizeof( textBuf
), "%s:%u", instance
->hostname
, instance
->port
);
5431 FPrintF( stdout
, "%*s" "%-51s %4llu.%03llu ms\n",
5432 Indent( 3 ), textBuf
, instance
->resolveTimeUs
/ 1000, instance
->resolveTimeUs
% 1000 );
5436 FPrintF( stdout
, "%*s" "%s:%u\n", Indent( 3 ), instance
->hostname
, instance
->port
);
5439 for( ipaddr
= instance
->ipaddrList
; ipaddr
; ipaddr
= ipaddr
->next
)
5441 BrowseAllConnection
* conn
;
5442 BrowseAllConnection
** connPtr
;
5444 FPrintF( stdout
, "%*s" "%-##47a %4llu.%03llu ms",
5445 Indent( 4 ), &ipaddr
->sip
.sa
, ipaddr
->resolveTimeUs
/ 1000, ipaddr
->resolveTimeUs
% 1000 );
5448 if( serviceTypeIsTCP
&& ( instance
->port
!= kDiscardProtocolPort
) )
5450 for( connPtr
= &context
->connectionList
; ( conn
= *connPtr
) != NULL
; connPtr
= &conn
->next
)
5452 if( ( conn
->port
== instance
->port
) &&
5453 ( SockAddrCompareAddr( &conn
->sip
, &ipaddr
->sip
) == 0 ) ) break;
5457 if( conn
->status
== kInProgressErr
) conn
->status
= kTimeoutErr
;
5458 *connPtr
= conn
->next
;
5459 conn
->context
= NULL
;
5460 AsyncConnection_Forget( &conn
->asyncCnx
);
5466 if( conn
->status
== kNoErr
)
5468 FPrintF( stdout
, " (%sconnected%s in %.3f ms)\n",
5469 useColor
? kANSIGreen
: "", useColor
? kANSINormal
: "", conn
->connectTimeSecs
* 1000 );
5473 FPrintF( stdout
, " (%scould not connect%s: %m)\n",
5474 useColor
? kANSIRed
: "", useColor
? kANSINormal
: "", conn
->status
);
5476 _BrowseAllConnectionRelease( conn
);
5480 FPrintF( stdout
, " (no connection attempted)\n" );
5484 FPrintF( stdout
, "\n" );
5485 if( instance
->txtLen
== 0 ) continue;
5487 FPrintF( stdout
, "%*s" "TXT record (%zu byte%?c):\n",
5488 Indent( 3 ), instance
->txtLen
, instance
->txtLen
!= 1, 's' );
5489 if( instance
->txtLen
> 1 )
5491 FPrintF( stdout
, "%3{txt}", instance
->txtPtr
, instance
->txtLen
);
5495 FPrintF( stdout
, "%*s" "%#H\n", Indent( 3 ), instance
->txtPtr
, (int) instance
->txtLen
, INT_MAX
);
5497 FPrintF( stdout
, "\n" );
5499 FPrintF( stdout
, "\n" );
5503 _BrowseAllContextFree( context
);
5507 //===========================================================================================================================
5508 // _IsServiceTypeTCP
5509 //===========================================================================================================================
5511 static Boolean
_IsServiceTypeTCP( const char *inServiceType
)
5514 const uint8_t * secondLabel
;
5515 uint8_t name
[ kDomainNameLengthMax
];
5517 err
= DomainNameFromString( name
, inServiceType
, NULL
);
5520 secondLabel
= DomainNameGetNextLabel( name
);
5521 if( secondLabel
&& DomainNameEqual( secondLabel
, (const uint8_t *) "\x04" "_tcp" ) ) return( true );
5526 //===========================================================================================================================
5528 //===========================================================================================================================
5530 const FlagStringPair kGetNameInfoFlagStringPairs
[] =
5532 CaseFlagStringify( NI_NUMERICSCOPE
),
5533 CaseFlagStringify( NI_DGRAM
),
5534 CaseFlagStringify( NI_NUMERICSERV
),
5535 CaseFlagStringify( NI_NAMEREQD
),
5536 CaseFlagStringify( NI_NUMERICHOST
),
5537 CaseFlagStringify( NI_NOFQDN
),
5541 static void GetNameInfoCmd( void )
5547 const FlagStringPair
* pair
;
5549 char host
[ NI_MAXHOST
];
5550 char serv
[ NI_MAXSERV
];
5552 err
= StringToSockAddr( gGetNameInfo_IPAddress
, &sip
, sizeof( sip
), &sockAddrLen
);
5556 FPrintF( stderr
, "Failed to convert \"%s\" to a sockaddr.\n", gGetNameInfo_IPAddress
);
5561 if( gGetNameInfoFlag_DGram
) flags
|= NI_DGRAM
;
5562 if( gGetNameInfoFlag_NameReqd
) flags
|= NI_NAMEREQD
;
5563 if( gGetNameInfoFlag_NoFQDN
) flags
|= NI_NOFQDN
;
5564 if( gGetNameInfoFlag_NumericHost
) flags
|= NI_NUMERICHOST
;
5565 if( gGetNameInfoFlag_NumericScope
) flags
|= NI_NUMERICSCOPE
;
5566 if( gGetNameInfoFlag_NumericServ
) flags
|= NI_NUMERICSERV
;
5570 FPrintF( stdout
, "SockAddr: %##a\n", &sip
.sa
);
5571 FPrintF( stdout
, "Flags: 0x%X < ", flags
);
5572 for( pair
= kGetNameInfoFlagStringPairs
; pair
->str
!= NULL
; ++pair
)
5574 if( flags
& pair
->flag
) FPrintF( stdout
, "%s ", pair
->str
);
5576 FPrintF( stdout
, ">\n" );
5577 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
5578 FPrintF( stdout
, "---\n" );
5580 // Call getnameinfo().
5582 err
= getnameinfo( &sip
.sa
, (socklen_t
) sockAddrLen
, host
, (socklen_t
) sizeof( host
), serv
, (socklen_t
) sizeof( serv
),
5584 gettimeofday( &now
, NULL
);
5587 FPrintF( stderr
, "Error %d: %s.\n", err
, gai_strerror( err
) );
5591 FPrintF( stdout
, "host: %s\n", host
);
5592 FPrintF( stdout
, "serv: %s\n", serv
);
5594 FPrintF( stdout
, "---\n" );
5595 FPrintF( stdout
, "End time: %{du:time}\n", &now
);
5598 gExitCode
= err
? 1 : 0;
5601 //===========================================================================================================================
5602 // GetAddrInfoStressCmd
5603 //===========================================================================================================================
5607 DNSServiceRef mainRef
;
5608 DNSServiceRef sdRef
;
5609 DNSServiceFlags flags
;
5610 unsigned int interfaceIndex
;
5611 unsigned int connectionNumber
;
5612 unsigned int requestCount
;
5613 unsigned int requestCountMax
;
5614 unsigned int requestCountLimit
;
5615 unsigned int durationMinMs
;
5616 unsigned int durationMaxMs
;
5620 static void GetAddrInfoStressEvent( void *inContext
);
5621 static void DNSSD_API
5622 GetAddrInfoStressCallback(
5623 DNSServiceRef inSDRef
,
5624 DNSServiceFlags inFlags
,
5625 uint32_t inInterfaceIndex
,
5626 DNSServiceErrorType inError
,
5627 const char * inHostname
,
5628 const struct sockaddr
* inSockAddr
,
5632 static void GetAddrInfoStressCmd( void )
5635 GAIStressContext
* context
= NULL
;
5637 DNSServiceFlags flags
;
5639 char ifName
[ kInterfaceNameBufLen
];
5641 if( gGAIStress_TestDurationSecs
< 0 )
5643 FPrintF( stdout
, "Invalid test duration: %d s.\n", gGAIStress_TestDurationSecs
);
5647 if( gGAIStress_ConnectionCount
<= 0 )
5649 FPrintF( stdout
, "Invalid simultaneous connection count: %d.\n", gGAIStress_ConnectionCount
);
5653 if( gGAIStress_DurationMinMs
<= 0 )
5655 FPrintF( stdout
, "Invalid minimum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMinMs
);
5659 if( gGAIStress_DurationMaxMs
<= 0 )
5661 FPrintF( stdout
, "Invalid maximum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMaxMs
);
5665 if( gGAIStress_DurationMinMs
> gGAIStress_DurationMaxMs
)
5667 FPrintF( stdout
, "Invalid minimum and maximum DNSServiceGetAddrInfo() durations: %d ms and %d ms.\n",
5668 gGAIStress_DurationMinMs
, gGAIStress_DurationMaxMs
);
5672 if( gGAIStress_RequestCountMax
<= 0 )
5674 FPrintF( stdout
, "Invalid maximum request count: %d.\n", gGAIStress_RequestCountMax
);
5681 flags
= GetDNSSDFlagsFromOpts();
5683 // Set interface index.
5685 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
5686 require_noerr_quiet( err
, exit
);
5688 for( i
= 0; i
< gGAIStress_ConnectionCount
; ++i
)
5690 context
= (GAIStressContext
*) calloc( 1, sizeof( *context
) );
5691 require_action( context
, exit
, err
= kNoMemoryErr
);
5693 context
->flags
= flags
;
5694 context
->interfaceIndex
= ifIndex
;
5695 context
->connectionNumber
= (unsigned int)( i
+ 1 );
5696 context
->requestCountMax
= (unsigned int) gGAIStress_RequestCountMax
;
5697 context
->durationMinMs
= (unsigned int) gGAIStress_DurationMinMs
;
5698 context
->durationMaxMs
= (unsigned int) gGAIStress_DurationMaxMs
;
5700 dispatch_async_f( dispatch_get_main_queue(), context
, GetAddrInfoStressEvent
);
5704 if( gGAIStress_TestDurationSecs
> 0 )
5706 dispatch_after_f( dispatch_time_seconds( gGAIStress_TestDurationSecs
), dispatch_get_main_queue(), NULL
, Exit
);
5709 FPrintF( stdout
, "Flags: %#{flags}\n", flags
, kDNSServiceFlagsDescriptors
);
5710 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) ifIndex
, InterfaceIndexToName( ifIndex
, ifName
) );
5711 FPrintF( stdout
, "Test duration: " );
5712 if( gGAIStress_TestDurationSecs
== 0 )
5714 FPrintF( stdout
, "∞\n" );
5718 FPrintF( stdout
, "%d s\n", gGAIStress_TestDurationSecs
);
5720 FPrintF( stdout
, "Connection count: %d\n", gGAIStress_ConnectionCount
);
5721 FPrintF( stdout
, "Request duration min: %d ms\n", gGAIStress_DurationMinMs
);
5722 FPrintF( stdout
, "Request duration max: %d ms\n", gGAIStress_DurationMaxMs
);
5723 FPrintF( stdout
, "Request count max: %d\n", gGAIStress_RequestCountMax
);
5724 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
5725 FPrintF( stdout
, "---\n" );
5730 FreeNullSafe( context
);
5731 if( err
) exit( 1 );
5734 //===========================================================================================================================
5735 // GetAddrInfoStressEvent
5736 //===========================================================================================================================
5738 #define kStressRandStrLen 5
5740 #define kLowercaseAlphaCharSet "abcdefghijklmnopqrstuvwxyz"
5742 static void GetAddrInfoStressEvent( void *inContext
)
5744 GAIStressContext
* const context
= (GAIStressContext
*) inContext
;
5746 DNSServiceRef sdRef
;
5747 unsigned int nextMs
;
5748 char randomStr
[ kStressRandStrLen
+ 1 ];
5749 char hostname
[ kStressRandStrLen
+ 4 + 1 ];
5750 Boolean isConnectionNew
= false;
5751 static Boolean printedHeader
= false;
5753 if( !context
->mainRef
|| ( context
->requestCount
>= context
->requestCountLimit
) )
5755 DNSServiceForget( &context
->mainRef
);
5756 context
->sdRef
= NULL
;
5757 context
->requestCount
= 0;
5758 context
->requestCountLimit
= RandomRange( 1, context
->requestCountMax
);
5760 err
= DNSServiceCreateConnection( &context
->mainRef
);
5761 require_noerr( err
, exit
);
5763 err
= DNSServiceSetDispatchQueue( context
->mainRef
, dispatch_get_main_queue() );
5764 require_noerr( err
, exit
);
5766 isConnectionNew
= true;
5769 RandomString( kLowercaseAlphaCharSet
, sizeof_string( kLowercaseAlphaCharSet
), 2, kStressRandStrLen
, randomStr
);
5770 SNPrintF( hostname
, sizeof( hostname
), "%s.com", randomStr
);
5772 nextMs
= RandomRange( context
->durationMinMs
, context
->durationMaxMs
);
5774 if( !printedHeader
)
5776 FPrintF( stdout
, "%-26s Conn Hostname Dur (ms)\n", "Timestamp" );
5777 printedHeader
= true;
5779 FPrintF( stdout
, "%{du:time} %3u%c %9s %8u\n",
5780 NULL
, context
->connectionNumber
, isConnectionNew
? '*': ' ', hostname
, nextMs
);
5782 DNSServiceForget( &context
->sdRef
);
5783 sdRef
= context
->mainRef
;
5784 err
= DNSServiceGetAddrInfo( &sdRef
, context
->flags
| kDNSServiceFlagsShareConnection
, context
->interfaceIndex
,
5785 kDNSServiceProtocol_IPv4
| kDNSServiceProtocol_IPv6
, hostname
, GetAddrInfoStressCallback
, NULL
);
5786 require_noerr( err
, exit
);
5787 context
->sdRef
= sdRef
;
5789 context
->requestCount
++;
5791 dispatch_after_f( dispatch_time_milliseconds( nextMs
), dispatch_get_main_queue(), context
, GetAddrInfoStressEvent
);
5794 if( err
) exit( 1 );
5797 //===========================================================================================================================
5798 // GetAddrInfoStressCallback
5799 //===========================================================================================================================
5801 static void DNSSD_API
5802 GetAddrInfoStressCallback(
5803 DNSServiceRef inSDRef
,
5804 DNSServiceFlags inFlags
,
5805 uint32_t inInterfaceIndex
,
5806 DNSServiceErrorType inError
,
5807 const char * inHostname
,
5808 const struct sockaddr
* inSockAddr
,
5814 Unused( inInterfaceIndex
);
5816 Unused( inHostname
);
5817 Unused( inSockAddr
);
5819 Unused( inContext
);
5822 //===========================================================================================================================
5824 //===========================================================================================================================
5828 sockaddr_ip serverAddr
;
5834 dispatch_source_t readSource
;
5841 Boolean printRawRData
; // True if RDATA results are not to be formatted.
5842 uint8_t msgBuf
[ 512 ];
5846 static void DNSQueryPrintPrologue( const DNSQueryContext
*inContext
);
5847 static void DNSQueryReadHandler( void *inContext
);
5848 static void DNSQueryCancelHandler( void *inContext
);
5850 static void DNSQueryCmd( void )
5853 DNSQueryContext
* context
= NULL
;
5855 size_t msgLen
, sendLen
;
5857 // Check command parameters.
5859 if( gDNSQuery_TimeLimitSecs
< -1 )
5861 FPrintF( stdout
, "Invalid time limit: %d seconds.\n", gDNSQuery_TimeLimitSecs
);
5865 if( ( gDNSQuery_Flags
< INT16_MIN
) || ( gDNSQuery_Flags
> UINT16_MAX
) )
5867 FPrintF( stdout
, "DNS flags-and-codes value is out of the unsigned 16-bit range: 0x%08X.\n", gDNSQuery_Flags
);
5874 context
= (DNSQueryContext
*) calloc( 1, sizeof( *context
) );
5875 require_action( context
, exit
, err
= kNoMemoryErr
);
5877 context
->name
= gDNSQuery_Name
;
5878 context
->sock
= kInvalidSocketRef
;
5879 context
->timeLimitSecs
= gDNSQuery_TimeLimitSecs
;
5880 context
->queryID
= (uint16_t) Random32();
5881 context
->useTCP
= gDNSQuery_UseTCP
? true : false;
5882 context
->printRawRData
= gDNSQuery_RawRData
? true : false;
5884 #if( TARGET_OS_DARWIN )
5885 if( gDNSQuery_Server
)
5888 err
= StringToSockAddr( gDNSQuery_Server
, &context
->serverAddr
, sizeof( context
->serverAddr
), NULL
);
5889 require_noerr( err
, exit
);
5891 #if( TARGET_OS_DARWIN )
5894 err
= GetDefaultDNSServer( &context
->serverAddr
);
5895 require_noerr( err
, exit
);
5898 if( SockAddrGetPort( &context
->serverAddr
) == 0 ) SockAddrSetPort( &context
->serverAddr
, kDNSPort
);
5900 err
= RecordTypeFromArgString( gDNSQuery_Type
, &context
->type
);
5901 require_noerr( err
, exit
);
5903 // Write query message.
5905 check_compile_time_code( sizeof( context
->msgBuf
) >= ( kDNSQueryMessageMaxLen
+ 2 ) );
5907 msgPtr
= context
->useTCP
? &context
->msgBuf
[ 2 ] : context
->msgBuf
;
5908 err
= WriteDNSQueryMessage( msgPtr
, context
->queryID
, (uint16_t) gDNSQuery_Flags
, context
->name
, context
->type
,
5909 kDNSServiceClass_IN
, &msgLen
);
5910 require_noerr( err
, exit
);
5911 check( msgLen
<= UINT16_MAX
);
5913 if( context
->useTCP
)
5915 WriteBig16( context
->msgBuf
, msgLen
);
5916 sendLen
= 2 + msgLen
;
5923 DNSQueryPrintPrologue( context
);
5925 if( gDNSQuery_Verbose
)
5927 FPrintF( stdout
, "DNS message to send:\n\n%{du:dnsmsg}", msgPtr
, msgLen
);
5928 FPrintF( stdout
, "---\n" );
5931 if( context
->useTCP
)
5933 // Create TCP socket.
5935 context
->sock
= socket( context
->serverAddr
.sa
.sa_family
, SOCK_STREAM
, IPPROTO_TCP
);
5936 err
= map_socket_creation_errno( context
->sock
);
5937 require_noerr( err
, exit
);
5939 err
= SocketConnect( context
->sock
, &context
->serverAddr
, 5 );
5940 require_noerr( err
, exit
);
5944 // Create UDP socket.
5946 err
= UDPClientSocketOpen( AF_UNSPEC
, &context
->serverAddr
, 0, -1, NULL
, &context
->sock
);
5947 require_noerr( err
, exit
);
5950 context
->sendTicks
= UpTicks();
5951 err
= _SocketWriteAll( context
->sock
, context
->msgBuf
, sendLen
, 5 );
5952 require_noerr( err
, exit
);
5954 if( context
->timeLimitSecs
== 0 ) goto exit
;
5956 err
= DispatchReadSourceCreate( context
->sock
, NULL
, DNSQueryReadHandler
, DNSQueryCancelHandler
, context
,
5957 &context
->readSource
);
5958 require_noerr( err
, exit
);
5959 dispatch_resume( context
->readSource
);
5961 if( context
->timeLimitSecs
> 0 )
5963 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
5971 dispatch_source_forget( &context
->readSource
);
5972 ForgetSocket( &context
->sock
);
5975 if( err
) exit( 1 );
5978 //===========================================================================================================================
5979 // DNSQueryPrintPrologue
5980 //===========================================================================================================================
5982 static void DNSQueryPrintPrologue( const DNSQueryContext
*inContext
)
5984 const int timeLimitSecs
= inContext
->timeLimitSecs
;
5986 FPrintF( stdout
, "Name: %s\n", inContext
->name
);
5987 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->type
), inContext
->type
);
5988 FPrintF( stdout
, "Server: %##a\n", &inContext
->serverAddr
);
5989 FPrintF( stdout
, "Transport: %s\n", inContext
->useTCP
? "TCP" : "UDP" );
5990 FPrintF( stdout
, "Time limit: " );
5991 if( timeLimitSecs
>= 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
5992 else FPrintF( stdout
, "∞\n" );
5993 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
5994 FPrintF( stdout
, "---\n" );
5997 //===========================================================================================================================
5998 // DNSQueryReadHandler
5999 //===========================================================================================================================
6001 static void DNSQueryReadHandler( void *inContext
)
6005 const uint64_t nowTicks
= UpTicks();
6006 DNSQueryContext
* const context
= (DNSQueryContext
*) inContext
;
6008 gettimeofday( &now
, NULL
);
6010 if( context
->useTCP
)
6012 if( !context
->haveTCPLen
)
6014 err
= SocketReadData( context
->sock
, &context
->msgBuf
, 2, &context
->msgOffset
);
6015 if( err
== EWOULDBLOCK
) { err
= kNoErr
; goto exit
; }
6016 require_noerr( err
, exit
);
6018 context
->msgOffset
= 0;
6019 context
->msgLen
= ReadBig16( context
->msgBuf
);
6020 context
->haveTCPLen
= true;
6021 if( context
->msgLen
<= sizeof( context
->msgBuf
) )
6023 context
->msgPtr
= context
->msgBuf
;
6027 context
->msgPtr
= (uint8_t *) malloc( context
->msgLen
);
6028 require_action( context
->msgPtr
, exit
, err
= kNoMemoryErr
);
6032 err
= SocketReadData( context
->sock
, context
->msgPtr
, context
->msgLen
, &context
->msgOffset
);
6033 if( err
== EWOULDBLOCK
) { err
= kNoErr
; goto exit
; }
6034 require_noerr( err
, exit
);
6035 context
->msgOffset
= 0;
6036 context
->haveTCPLen
= false;
6040 sockaddr_ip fromAddr
;
6042 context
->msgPtr
= context
->msgBuf
;
6043 err
= SocketRecvFrom( context
->sock
, context
->msgPtr
, sizeof( context
->msgBuf
), &context
->msgLen
, &fromAddr
,
6044 sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
6045 require_noerr( err
, exit
);
6047 check( SockAddrCompareAddr( &fromAddr
, &context
->serverAddr
) == 0 );
6050 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
6051 FPrintF( stdout
, "Source: %##a\n", &context
->serverAddr
);
6052 FPrintF( stdout
, "Message size: %zu\n", context
->msgLen
);
6053 FPrintF( stdout
, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks
- context
->sendTicks
) );
6054 FPrintF( stdout
, "%.*{du:dnsmsg}", context
->printRawRData
? 1 : 0, context
->msgPtr
, context
->msgLen
);
6056 if( ( context
->msgLen
>= kDNSHeaderLength
) && ( DNSHeaderGetID( (DNSHeader
*) context
->msgPtr
) == context
->queryID
) )
6058 Exit( kExitReason_ReceivedResponse
);
6062 if( err
) dispatch_source_forget( &context
->readSource
);
6065 //===========================================================================================================================
6066 // DNSQueryCancelHandler
6067 //===========================================================================================================================
6069 static void DNSQueryCancelHandler( void *inContext
)
6071 DNSQueryContext
* const context
= (DNSQueryContext
*) inContext
;
6073 check( !context
->readSource
);
6074 ForgetSocket( &context
->sock
);
6075 if( context
->msgPtr
!= context
->msgBuf
) ForgetMem( &context
->msgPtr
);
6077 dispatch_async_f( dispatch_get_main_queue(), NULL
, Exit
);
6080 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
6081 //===========================================================================================================================
6083 //===========================================================================================================================
6085 #define kDNSCryptPort 443
6087 #define kDNSCryptMinPadLength 8
6088 #define kDNSCryptMaxPadLength 256
6089 #define kDNSCryptBlockSize 64
6090 #define kDNSCryptCertMinimumLength 124
6091 #define kDNSCryptClientMagicLength 8
6092 #define kDNSCryptResolverMagicLength 8
6093 #define kDNSCryptHalfNonceLength 12
6094 #define kDNSCryptCertMagicLength 4
6096 check_compile_time( ( kDNSCryptHalfNonceLength
* 2 ) == crypto_box_NONCEBYTES
);
6098 static const uint8_t kDNSCryptCertMagic
[ kDNSCryptCertMagicLength
] = { 'D', 'N', 'S', 'C' };
6099 static const uint8_t kDNSCryptResolverMagic
[ kDNSCryptResolverMagicLength
] =
6101 0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38
6106 uint8_t certMagic
[ kDNSCryptCertMagicLength
];
6107 uint8_t esVersion
[ 2 ];
6108 uint8_t minorVersion
[ 2 ];
6109 uint8_t signature
[ crypto_sign_BYTES
];
6110 uint8_t publicKey
[ crypto_box_PUBLICKEYBYTES
];
6111 uint8_t clientMagic
[ kDNSCryptClientMagicLength
];
6112 uint8_t serial
[ 4 ];
6113 uint8_t startTime
[ 4 ];
6114 uint8_t endTime
[ 4 ];
6115 uint8_t extensions
[ 1 ]; // Variably-sized extension data.
6119 check_compile_time( offsetof( DNSCryptCert
, extensions
) == kDNSCryptCertMinimumLength
);
6123 uint8_t clientMagic
[ kDNSCryptClientMagicLength
];
6124 uint8_t clientPublicKey
[ crypto_box_PUBLICKEYBYTES
];
6125 uint8_t clientNonce
[ kDNSCryptHalfNonceLength
];
6126 uint8_t poly1305MAC
[ 16 ];
6128 } DNSCryptQueryHeader
;
6130 check_compile_time( sizeof( DNSCryptQueryHeader
) == 68 );
6131 check_compile_time( sizeof( DNSCryptQueryHeader
) >= crypto_box_ZEROBYTES
);
6132 check_compile_time( ( sizeof( DNSCryptQueryHeader
) - crypto_box_ZEROBYTES
+ crypto_box_BOXZEROBYTES
) ==
6133 offsetof( DNSCryptQueryHeader
, poly1305MAC
) );
6137 uint8_t resolverMagic
[ kDNSCryptResolverMagicLength
];
6138 uint8_t clientNonce
[ kDNSCryptHalfNonceLength
];
6139 uint8_t resolverNonce
[ kDNSCryptHalfNonceLength
];
6140 uint8_t poly1305MAC
[ 16 ];
6142 } DNSCryptResponseHeader
;
6144 check_compile_time( sizeof( DNSCryptResponseHeader
) == 48 );
6145 check_compile_time( offsetof( DNSCryptResponseHeader
, poly1305MAC
) >= crypto_box_BOXZEROBYTES
);
6146 check_compile_time( ( offsetof( DNSCryptResponseHeader
, poly1305MAC
) - crypto_box_BOXZEROBYTES
+ crypto_box_ZEROBYTES
) ==
6147 sizeof( DNSCryptResponseHeader
) );
6151 sockaddr_ip serverAddr
;
6153 const char * providerName
;
6155 const uint8_t * certPtr
;
6157 dispatch_source_t readSource
;
6162 Boolean printRawRData
;
6163 uint8_t serverPublicSignKey
[ crypto_sign_PUBLICKEYBYTES
];
6164 uint8_t serverPublicKey
[ crypto_box_PUBLICKEYBYTES
];
6165 uint8_t clientPublicKey
[ crypto_box_PUBLICKEYBYTES
];
6166 uint8_t clientSecretKey
[ crypto_box_SECRETKEYBYTES
];
6167 uint8_t clientMagic
[ kDNSCryptClientMagicLength
];
6168 uint8_t clientNonce
[ kDNSCryptHalfNonceLength
];
6169 uint8_t nmKey
[ crypto_box_BEFORENMBYTES
];
6170 uint8_t msgBuf
[ 512 ];
6174 static void DNSCryptReceiveCertHandler( void *inContext
);
6175 static void DNSCryptReceiveResponseHandler( void *inContext
);
6176 static void DNSCryptProceed( void *inContext
);
6177 static OSStatus
DNSCryptProcessCert( DNSCryptContext
*inContext
);
6178 static OSStatus
DNSCryptBuildQuery( DNSCryptContext
*inContext
);
6179 static OSStatus
DNSCryptSendQuery( DNSCryptContext
*inContext
);
6180 static void DNSCryptPrintCertificate( const DNSCryptCert
*inCert
, size_t inLen
);
6182 static void DNSCryptCmd( void )
6185 DNSCryptContext
* context
= NULL
;
6186 size_t writtenBytes
;
6188 SocketContext
* sockCtx
;
6189 SocketRef sock
= kInvalidSocketRef
;
6192 // Check command parameters.
6194 if( gDNSCrypt_TimeLimitSecs
< -1 )
6196 FPrintF( stdout
, "Invalid time limit: %d seconds.\n", gDNSCrypt_TimeLimitSecs
);
6203 context
= (DNSCryptContext
*) calloc( 1, sizeof( *context
) );
6204 require_action( context
, exit
, err
= kNoMemoryErr
);
6206 context
->providerName
= gDNSCrypt_ProviderName
;
6207 context
->qname
= gDNSCrypt_Name
;
6208 context
->timeLimitSecs
= gDNSCrypt_TimeLimitSecs
;
6209 context
->printRawRData
= gDNSCrypt_RawRData
? true : false;
6211 err
= crypto_box_keypair( context
->clientPublicKey
, context
->clientSecretKey
);
6212 require_noerr( err
, exit
);
6214 err
= HexToData( gDNSCrypt_ProviderKey
, kSizeCString
, kHexToData_DefaultFlags
,
6215 context
->serverPublicSignKey
, sizeof( context
->serverPublicSignKey
), &writtenBytes
, &totalBytes
, &ptr
);
6216 if( err
|| ( *ptr
!= '\0' ) )
6218 FPrintF( stderr
, "Failed to parse public signing key hex string (%s).\n", gDNSCrypt_ProviderKey
);
6221 else if( totalBytes
!= sizeof( context
->serverPublicSignKey
) )
6223 FPrintF( stderr
, "Public signing key contains incorrect number of hex bytes (%zu != %zu)\n",
6224 totalBytes
, sizeof( context
->serverPublicSignKey
) );
6228 check( writtenBytes
== totalBytes
);
6230 err
= StringToSockAddr( gDNSCrypt_Server
, &context
->serverAddr
, sizeof( context
->serverAddr
), NULL
);
6231 require_noerr( err
, exit
);
6232 if( SockAddrGetPort( &context
->serverAddr
) == 0 ) SockAddrSetPort( &context
->serverAddr
, kDNSCryptPort
);
6234 err
= RecordTypeFromArgString( gDNSCrypt_Type
, &context
->qtype
);
6235 require_noerr( err
, exit
);
6237 // Write query message.
6239 context
->queryID
= (uint16_t) Random32();
6240 err
= WriteDNSQueryMessage( context
->msgBuf
, context
->queryID
, kDNSHeaderFlag_RecursionDesired
, context
->providerName
,
6241 kDNSServiceType_TXT
, kDNSServiceClass_IN
, &context
->msgLen
);
6242 require_noerr( err
, exit
);
6244 // Create UDP socket.
6246 err
= UDPClientSocketOpen( AF_UNSPEC
, &context
->serverAddr
, 0, -1, NULL
, &sock
);
6247 require_noerr( err
, exit
);
6251 context
->sendTicks
= UpTicks();
6252 err
= _SocketWriteAll( sock
, context
->msgBuf
, context
->msgLen
, 5 );
6253 require_noerr( err
, exit
);
6255 err
= SocketContextCreate( sock
, context
, &sockCtx
);
6256 require_noerr( err
, exit
);
6257 sock
= kInvalidSocketRef
;
6259 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, DNSCryptReceiveCertHandler
, SocketContextCancelHandler
, sockCtx
,
6260 &context
->readSource
);
6261 if( err
) ForgetSocketContext( &sockCtx
);
6262 require_noerr( err
, exit
);
6264 dispatch_resume( context
->readSource
);
6266 if( context
->timeLimitSecs
> 0 )
6268 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
6274 if( context
) free( context
);
6275 ForgetSocket( &sock
);
6276 if( err
) exit( 1 );
6279 //===========================================================================================================================
6280 // DNSCryptReceiveCertHandler
6281 //===========================================================================================================================
6283 static void DNSCryptReceiveCertHandler( void *inContext
)
6287 const uint64_t nowTicks
= UpTicks();
6288 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
6289 DNSCryptContext
* const context
= (DNSCryptContext
*) sockCtx
->userContext
;
6290 const DNSHeader
* hdr
;
6291 sockaddr_ip fromAddr
;
6292 const uint8_t * ptr
;
6293 const uint8_t * txtPtr
;
6295 unsigned int answerCount
, i
;
6296 uint8_t targetName
[ kDomainNameLengthMax
];
6298 gettimeofday( &now
, NULL
);
6300 dispatch_source_forget( &context
->readSource
);
6302 err
= SocketRecvFrom( sockCtx
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &context
->msgLen
,
6303 &fromAddr
, sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
6304 require_noerr( err
, exit
);
6305 check( SockAddrCompareAddr( &fromAddr
, &context
->serverAddr
) == 0 );
6307 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
6308 FPrintF( stdout
, "Source: %##a\n", &context
->serverAddr
);
6309 FPrintF( stdout
, "Message size: %zu\n", context
->msgLen
);
6310 FPrintF( stdout
, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks
- context
->sendTicks
) );
6311 FPrintF( stdout
, "%.*{du:dnsmsg}", context
->printRawRData
? 1 : 0, context
->msgBuf
, context
->msgLen
);
6313 require_action_quiet( context
->msgLen
>= kDNSHeaderLength
, exit
, err
= kSizeErr
);
6315 hdr
= (DNSHeader
*) context
->msgBuf
;
6316 require_action_quiet( DNSHeaderGetID( hdr
) == context
->queryID
, exit
, err
= kMismatchErr
);
6318 err
= DNSMessageGetAnswerSection( context
->msgBuf
, context
->msgLen
, &ptr
);
6319 require_noerr( err
, exit
);
6321 err
= DomainNameFromString( targetName
, context
->providerName
, NULL
);
6322 require_noerr( err
, exit
);
6324 answerCount
= DNSHeaderGetAnswerCount( hdr
);
6325 for( i
= 0; i
< answerCount
; ++i
)
6329 uint8_t name
[ kDomainNameLengthMax
];
6331 err
= DNSMessageExtractRecord( context
->msgBuf
, context
->msgLen
, ptr
, name
, &type
, &class, NULL
, &txtPtr
, &txtLen
,
6333 require_noerr( err
, exit
);
6335 if( ( type
== kDNSServiceType_TXT
) && ( class == kDNSServiceClass_IN
) && DomainNameEqual( name
, targetName
) )
6341 if( txtLen
< ( 1 + kDNSCryptCertMinimumLength
) )
6343 FPrintF( stderr
, "TXT record length is too short (%u < %u)\n", txtLen
, kDNSCryptCertMinimumLength
+ 1 );
6347 if( txtPtr
[ 0 ] < kDNSCryptCertMinimumLength
)
6349 FPrintF( stderr
, "TXT record value length is too short (%u < %u)\n", txtPtr
[ 0 ], kDNSCryptCertMinimumLength
);
6354 context
->certLen
= txtPtr
[ 0 ];
6355 context
->certPtr
= &txtPtr
[ 1 ];
6357 dispatch_async_f( dispatch_get_main_queue(), context
, DNSCryptProceed
);
6360 if( err
) Exit( NULL
);
6363 //===========================================================================================================================
6364 // DNSCryptReceiveResponseHandler
6365 //===========================================================================================================================
6367 static void DNSCryptReceiveResponseHandler( void *inContext
)
6371 const uint64_t nowTicks
= UpTicks();
6372 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
6373 DNSCryptContext
* const context
= (DNSCryptContext
*) sockCtx
->userContext
;
6374 sockaddr_ip fromAddr
;
6375 DNSCryptResponseHeader
* hdr
;
6376 const uint8_t * end
;
6377 uint8_t * ciphertext
;
6378 uint8_t * plaintext
;
6379 const uint8_t * response
;
6380 uint8_t nonce
[ crypto_box_NONCEBYTES
];
6382 gettimeofday( &now
, NULL
);
6384 dispatch_source_forget( &context
->readSource
);
6386 err
= SocketRecvFrom( sockCtx
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &context
->msgLen
,
6387 &fromAddr
, sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
6388 require_noerr( err
, exit
);
6389 check( SockAddrCompareAddr( &fromAddr
, &context
->serverAddr
) == 0 );
6391 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
6392 FPrintF( stdout
, "Source: %##a\n", &context
->serverAddr
);
6393 FPrintF( stdout
, "Message size: %zu\n", context
->msgLen
);
6394 FPrintF( stdout
, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks
- context
->sendTicks
) );
6396 if( context
->msgLen
< sizeof( DNSCryptResponseHeader
) )
6398 FPrintF( stderr
, "DNSCrypt response is too short.\n" );
6403 hdr
= (DNSCryptResponseHeader
*) context
->msgBuf
;
6405 if( memcmp( hdr
->resolverMagic
, kDNSCryptResolverMagic
, kDNSCryptResolverMagicLength
) != 0 )
6407 FPrintF( stderr
, "DNSCrypt response resolver magic %#H != %#H\n",
6408 hdr
->resolverMagic
, kDNSCryptResolverMagicLength
, INT_MAX
,
6409 kDNSCryptResolverMagic
, kDNSCryptResolverMagicLength
, INT_MAX
);
6414 if( memcmp( hdr
->clientNonce
, context
->clientNonce
, kDNSCryptHalfNonceLength
) != 0 )
6416 FPrintF( stderr
, "DNSCrypt response client nonce mismatch.\n" );
6421 memcpy( nonce
, hdr
->clientNonce
, crypto_box_NONCEBYTES
);
6423 ciphertext
= hdr
->poly1305MAC
- crypto_box_BOXZEROBYTES
;
6424 memset( ciphertext
, 0, crypto_box_BOXZEROBYTES
);
6426 plaintext
= (uint8_t *)( hdr
+ 1 ) - crypto_box_ZEROBYTES
;
6427 check( plaintext
== ciphertext
);
6429 end
= context
->msgBuf
+ context
->msgLen
;
6431 err
= crypto_box_open_afternm( plaintext
, ciphertext
, (size_t)( end
- ciphertext
), nonce
, context
->nmKey
);
6432 require_noerr( err
, exit
);
6434 response
= plaintext
+ crypto_box_ZEROBYTES
;
6435 FPrintF( stdout
, "%.*{du:dnsmsg}", context
->printRawRData
? 1 : 0, response
, (size_t)( end
- response
) );
6436 Exit( kExitReason_ReceivedResponse
);
6439 if( err
) Exit( NULL
);
6442 //===========================================================================================================================
6444 //===========================================================================================================================
6446 static void DNSCryptProceed( void *inContext
)
6449 DNSCryptContext
* const context
= (DNSCryptContext
*) inContext
;
6451 err
= DNSCryptProcessCert( context
);
6452 require_noerr_quiet( err
, exit
);
6454 err
= DNSCryptBuildQuery( context
);
6455 require_noerr_quiet( err
, exit
);
6457 err
= DNSCryptSendQuery( context
);
6458 require_noerr_quiet( err
, exit
);
6461 if( err
) Exit( NULL
);
6464 //===========================================================================================================================
6465 // DNSCryptProcessCert
6466 //===========================================================================================================================
6468 static OSStatus
DNSCryptProcessCert( DNSCryptContext
*inContext
)
6471 const DNSCryptCert
* const cert
= (DNSCryptCert
*) inContext
->certPtr
;
6472 const uint8_t * const certEnd
= inContext
->certPtr
+ inContext
->certLen
;
6474 time_t startTimeSecs
, endTimeSecs
;
6477 unsigned long long tempLen
;
6479 DNSCryptPrintCertificate( cert
, inContext
->certLen
);
6481 if( memcmp( cert
->certMagic
, kDNSCryptCertMagic
, kDNSCryptCertMagicLength
) != 0 )
6483 FPrintF( stderr
, "DNSCrypt certificate magic %#H != %#H\n",
6484 cert
->certMagic
, kDNSCryptCertMagicLength
, INT_MAX
,
6485 kDNSCryptCertMagic
, kDNSCryptCertMagicLength
, INT_MAX
);
6490 startTimeSecs
= (time_t) ReadBig32( cert
->startTime
);
6491 endTimeSecs
= (time_t) ReadBig32( cert
->endTime
);
6493 gettimeofday( &now
, NULL
);
6494 if( now
.tv_sec
< startTimeSecs
)
6496 FPrintF( stderr
, "DNSCrypt certificate start time is in the future.\n" );
6500 if( now
.tv_sec
>= endTimeSecs
)
6502 FPrintF( stderr
, "DNSCrypt certificate has expired.\n" );
6507 signedLen
= (size_t)( certEnd
- cert
->signature
);
6508 tempBuf
= (uint8_t *) malloc( signedLen
);
6509 require_action( tempBuf
, exit
, err
= kNoMemoryErr
);
6510 err
= crypto_sign_open( tempBuf
, &tempLen
, cert
->signature
, signedLen
, inContext
->serverPublicSignKey
);
6514 FPrintF( stderr
, "DNSCrypt certificate failed verification.\n" );
6515 err
= kAuthenticationErr
;
6519 memcpy( inContext
->serverPublicKey
, cert
->publicKey
, crypto_box_PUBLICKEYBYTES
);
6520 memcpy( inContext
->clientMagic
, cert
->clientMagic
, kDNSCryptClientMagicLength
);
6522 err
= crypto_box_beforenm( inContext
->nmKey
, inContext
->serverPublicKey
, inContext
->clientSecretKey
);
6523 require_noerr( err
, exit
);
6525 inContext
->certPtr
= NULL
;
6526 inContext
->certLen
= 0;
6527 inContext
->msgLen
= 0;
6533 //===========================================================================================================================
6534 // DNSCryptBuildQuery
6535 //===========================================================================================================================
6537 static OSStatus
DNSCryptPadQuery( uint8_t *inMsgPtr
, size_t inMsgLen
, size_t inMaxLen
, size_t *outPaddedLen
);
6539 static OSStatus
DNSCryptBuildQuery( DNSCryptContext
*inContext
)
6542 DNSCryptQueryHeader
* const hdr
= (DNSCryptQueryHeader
*) inContext
->msgBuf
;
6543 uint8_t * const queryPtr
= (uint8_t *)( hdr
+ 1 );
6545 size_t paddedQueryLen
;
6546 const uint8_t * const msgLimit
= inContext
->msgBuf
+ sizeof( inContext
->msgBuf
);
6547 const uint8_t * padLimit
;
6548 uint8_t nonce
[ crypto_box_NONCEBYTES
];
6550 check_compile_time_code( sizeof( inContext
->msgBuf
) >= ( sizeof( DNSCryptQueryHeader
) + kDNSQueryMessageMaxLen
) );
6552 inContext
->queryID
= (uint16_t) Random32();
6553 err
= WriteDNSQueryMessage( queryPtr
, inContext
->queryID
, kDNSHeaderFlag_RecursionDesired
, inContext
->qname
,
6554 inContext
->qtype
, kDNSServiceClass_IN
, &queryLen
);
6555 require_noerr( err
, exit
);
6557 padLimit
= &queryPtr
[ queryLen
+ kDNSCryptMaxPadLength
];
6558 if( padLimit
> msgLimit
) padLimit
= msgLimit
;
6560 err
= DNSCryptPadQuery( queryPtr
, queryLen
, (size_t)( padLimit
- queryPtr
), &paddedQueryLen
);
6561 require_noerr( err
, exit
);
6563 memset( queryPtr
- crypto_box_ZEROBYTES
, 0, crypto_box_ZEROBYTES
);
6564 RandomBytes( inContext
->clientNonce
, kDNSCryptHalfNonceLength
);
6565 memcpy( nonce
, inContext
->clientNonce
, kDNSCryptHalfNonceLength
);
6566 memset( &nonce
[ kDNSCryptHalfNonceLength
], 0, kDNSCryptHalfNonceLength
);
6568 err
= crypto_box_afternm( queryPtr
- crypto_box_ZEROBYTES
, queryPtr
- crypto_box_ZEROBYTES
,
6569 paddedQueryLen
+ crypto_box_ZEROBYTES
, nonce
, inContext
->nmKey
);
6570 require_noerr( err
, exit
);
6572 memcpy( hdr
->clientMagic
, inContext
->clientMagic
, kDNSCryptClientMagicLength
);
6573 memcpy( hdr
->clientPublicKey
, inContext
->clientPublicKey
, crypto_box_PUBLICKEYBYTES
);
6574 memcpy( hdr
->clientNonce
, nonce
, kDNSCryptHalfNonceLength
);
6576 inContext
->msgLen
= (size_t)( &queryPtr
[ paddedQueryLen
] - inContext
->msgBuf
);
6582 static OSStatus
DNSCryptPadQuery( uint8_t *inMsgPtr
, size_t inMsgLen
, size_t inMaxLen
, size_t *outPaddedLen
)
6587 require_action_quiet( ( inMsgLen
+ kDNSCryptMinPadLength
) <= inMaxLen
, exit
, err
= kSizeErr
);
6589 paddedLen
= inMsgLen
+ kDNSCryptMinPadLength
+
6590 arc4random_uniform( (uint32_t)( inMaxLen
- ( inMsgLen
+ kDNSCryptMinPadLength
) + 1 ) );
6591 paddedLen
+= ( kDNSCryptBlockSize
- ( paddedLen
% kDNSCryptBlockSize
) );
6592 if( paddedLen
> inMaxLen
) paddedLen
= inMaxLen
;
6594 inMsgPtr
[ inMsgLen
] = 0x80;
6595 memset( &inMsgPtr
[ inMsgLen
+ 1 ], 0, paddedLen
- ( inMsgLen
+ 1 ) );
6597 if( outPaddedLen
) *outPaddedLen
= paddedLen
;
6604 //===========================================================================================================================
6605 // DNSCryptSendQuery
6606 //===========================================================================================================================
6608 static OSStatus
DNSCryptSendQuery( DNSCryptContext
*inContext
)
6611 SocketContext
* sockCtx
;
6612 SocketRef sock
= kInvalidSocketRef
;
6614 check( inContext
->msgLen
> 0 );
6615 check( !inContext
->readSource
);
6617 err
= UDPClientSocketOpen( AF_UNSPEC
, &inContext
->serverAddr
, 0, -1, NULL
, &sock
);
6618 require_noerr( err
, exit
);
6620 inContext
->sendTicks
= UpTicks();
6621 err
= _SocketWriteAll( sock
, inContext
->msgBuf
, inContext
->msgLen
, 5 );
6622 require_noerr( err
, exit
);
6624 err
= SocketContextCreate( sock
, inContext
, &sockCtx
);
6625 require_noerr( err
, exit
);
6626 sock
= kInvalidSocketRef
;
6628 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, DNSCryptReceiveResponseHandler
, SocketContextCancelHandler
, sockCtx
,
6629 &inContext
->readSource
);
6630 if( err
) ForgetSocketContext( &sockCtx
);
6631 require_noerr( err
, exit
);
6633 dispatch_resume( inContext
->readSource
);
6636 ForgetSocket( &sock
);
6640 //===========================================================================================================================
6641 // DNSCryptPrintCertificate
6642 //===========================================================================================================================
6644 #define kCertTimeStrBufLen 32
6646 static char * CertTimeStr( time_t inTime
, char inBuffer
[ kCertTimeStrBufLen
] );
6648 static void DNSCryptPrintCertificate( const DNSCryptCert
*inCert
, size_t inLen
)
6650 time_t startTime
, endTime
;
6652 char timeBuf
[ kCertTimeStrBufLen
];
6654 check( inLen
>= kDNSCryptCertMinimumLength
);
6656 startTime
= (time_t) ReadBig32( inCert
->startTime
);
6657 endTime
= (time_t) ReadBig32( inCert
->endTime
);
6659 FPrintF( stdout
, "DNSCrypt certificate (%zu bytes):\n", inLen
);
6660 FPrintF( stdout
, "Cert Magic: %#H\n", inCert
->certMagic
, kDNSCryptCertMagicLength
, INT_MAX
);
6661 FPrintF( stdout
, "ES Version: %u\n", ReadBig16( inCert
->esVersion
) );
6662 FPrintF( stdout
, "Minor Version: %u\n", ReadBig16( inCert
->minorVersion
) );
6663 FPrintF( stdout
, "Signature: %H\n", inCert
->signature
, crypto_sign_BYTES
/ 2, INT_MAX
);
6664 FPrintF( stdout
, " %H\n", &inCert
->signature
[ crypto_sign_BYTES
/ 2 ], crypto_sign_BYTES
/ 2, INT_MAX
);
6665 FPrintF( stdout
, "Public Key: %H\n", inCert
->publicKey
, sizeof( inCert
->publicKey
), INT_MAX
);
6666 FPrintF( stdout
, "Client Magic: %H\n", inCert
->clientMagic
, kDNSCryptClientMagicLength
, INT_MAX
);
6667 FPrintF( stdout
, "Serial: %u\n", ReadBig32( inCert
->serial
) );
6668 FPrintF( stdout
, "Start Time: %u (%s)\n", (uint32_t) startTime
, CertTimeStr( startTime
, timeBuf
) );
6669 FPrintF( stdout
, "End Time: %u (%s)\n", (uint32_t) endTime
, CertTimeStr( endTime
, timeBuf
) );
6671 if( inLen
> kDNSCryptCertMinimumLength
)
6673 extLen
= (int)( inLen
- kDNSCryptCertMinimumLength
);
6674 FPrintF( stdout
, "Extensions: %.1H\n", inCert
->extensions
, extLen
, extLen
);
6676 FPrintF( stdout
, "\n" );
6679 static char * CertTimeStr( time_t inTime
, char inBuffer
[ kCertTimeStrBufLen
] )
6683 tm
= localtime( &inTime
);
6686 dlogassert( "localtime() returned a NULL pointer.\n" );
6691 strftime( inBuffer
, kCertTimeStrBufLen
, "%a %b %d %H:%M:%S %Z %Y", tm
);
6697 #endif // DNSSDUTIL_INCLUDE_DNSCRYPT
6699 //===========================================================================================================================
6701 //===========================================================================================================================
6705 const char * qnameStr
; // Name (QNAME) of the record being queried as a C string.
6706 dispatch_source_t readSourceV4
; // Read dispatch source for IPv4 socket.
6707 dispatch_source_t readSourceV6
; // Read dispatch source for IPv6 socket.
6708 int localPort
; // The port number to which the sockets are bound.
6709 int receiveSecs
; // After send, the amount of time to spend receiving.
6710 uint32_t ifIndex
; // Index of the interface over which to send the query.
6711 uint16_t qtype
; // The type (QTYPE) of the record being queried.
6712 Boolean isQU
; // True if the query is QU, i.e., requests unicast responses.
6713 Boolean allResponses
; // True if all mDNS messages received should be printed.
6714 Boolean printRawRData
; // True if RDATA should be printed as hexdumps.
6715 Boolean useIPv4
; // True if the query should be sent via IPv4 multicast.
6716 Boolean useIPv6
; // True if the query should be sent via IPv6 multicast.
6717 char ifName
[ IF_NAMESIZE
+ 1 ]; // Name of the interface over which to send the query.
6718 uint8_t qname
[ kDomainNameLengthMax
]; // Buffer to hold the QNAME in DNS label format.
6719 uint8_t msgBuf
[ kMDNSMessageSizeMax
]; // mDNS message buffer.
6723 static void MDNSQueryPrintPrologue( const MDNSQueryContext
*inContext
);
6724 static void MDNSQueryReadHandler( void *inContext
);
6726 static void MDNSQueryCmd( void )
6729 MDNSQueryContext
* context
;
6730 SocketRef sockV4
= kInvalidSocketRef
;
6731 SocketRef sockV6
= kInvalidSocketRef
;
6733 const char * ifname
;
6735 unsigned int sendCount
;
6737 // Check command parameters.
6739 if( gMDNSQuery_ReceiveSecs
< -1 )
6741 FPrintF( stdout
, "Invalid receive time value: %d seconds.\n", gMDNSQuery_ReceiveSecs
);
6746 context
= (MDNSQueryContext
*) calloc( 1, sizeof( *context
) );
6747 require_action( context
, exit
, err
= kNoMemoryErr
);
6749 context
->qnameStr
= gMDNSQuery_Name
;
6750 context
->receiveSecs
= gMDNSQuery_ReceiveSecs
;
6751 context
->isQU
= gMDNSQuery_IsQU
? true : false;
6752 context
->allResponses
= gMDNSQuery_AllResponses
? true : false;
6753 context
->printRawRData
= gMDNSQuery_RawRData
? true : false;
6754 context
->useIPv4
= ( gMDNSQuery_UseIPv4
|| !gMDNSQuery_UseIPv6
) ? true : false;
6755 context
->useIPv6
= ( gMDNSQuery_UseIPv6
|| !gMDNSQuery_UseIPv4
) ? true : false;
6757 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
6758 require_noerr_quiet( err
, exit
);
6760 ifname
= if_indextoname( context
->ifIndex
, context
->ifName
);
6761 require_action( ifname
, exit
, err
= kNameErr
);
6763 err
= RecordTypeFromArgString( gMDNSQuery_Type
, &context
->qtype
);
6764 require_noerr( err
, exit
);
6766 // Set up IPv4 socket.
6768 if( context
->useIPv4
)
6770 err
= CreateMulticastSocket( GetMDNSMulticastAddrV4(),
6771 gMDNSQuery_SourcePort
? gMDNSQuery_SourcePort
: ( context
->isQU
? context
->localPort
: kMDNSPort
),
6772 ifname
, context
->ifIndex
, !context
->isQU
, &context
->localPort
, &sockV4
);
6773 require_noerr( err
, exit
);
6776 // Set up IPv6 socket.
6778 if( context
->useIPv6
)
6780 err
= CreateMulticastSocket( GetMDNSMulticastAddrV6(),
6781 gMDNSQuery_SourcePort
? gMDNSQuery_SourcePort
: ( context
->isQU
? context
->localPort
: kMDNSPort
),
6782 ifname
, context
->ifIndex
, !context
->isQU
, &context
->localPort
, &sockV6
);
6783 require_noerr( err
, exit
);
6786 // Craft mDNS query message.
6788 check_compile_time_code( sizeof( context
->msgBuf
) >= kDNSQueryMessageMaxLen
);
6789 err
= WriteDNSQueryMessage( context
->msgBuf
, kDefaultMDNSMessageID
, kDefaultMDNSQueryFlags
, context
->qnameStr
,
6790 context
->qtype
, context
->isQU
? ( kDNSServiceClass_IN
| kQClassUnicastResponseBit
) : kDNSServiceClass_IN
, &msgLen
);
6791 require_noerr( err
, exit
);
6795 MDNSQueryPrintPrologue( context
);
6797 // Send mDNS query message.
6800 if( IsValidSocket( sockV4
) )
6802 const struct sockaddr
* const mcastAddr4
= GetMDNSMulticastAddrV4();
6804 n
= sendto( sockV4
, context
->msgBuf
, msgLen
, 0, mcastAddr4
, SockAddrGetSize( mcastAddr4
) );
6805 err
= map_socket_value_errno( sockV4
, n
== (ssize_t
) msgLen
, n
);
6808 FPrintF( stderr
, "*** Failed to send query on IPv4 socket with error %#m\n", err
);
6809 ForgetSocket( &sockV4
);
6816 if( IsValidSocket( sockV6
) )
6818 const struct sockaddr
* const mcastAddr6
= GetMDNSMulticastAddrV6();
6820 n
= sendto( sockV6
, context
->msgBuf
, msgLen
, 0, mcastAddr6
, SockAddrGetSize( mcastAddr6
) );
6821 err
= map_socket_value_errno( sockV6
, n
== (ssize_t
) msgLen
, n
);
6824 FPrintF( stderr
, "*** Failed to send query on IPv6 socket with error %#m\n", err
);
6825 ForgetSocket( &sockV6
);
6832 require_action_quiet( sendCount
> 0, exit
, err
= kUnexpectedErr
);
6834 // If there's no wait period after the send, then exit.
6836 if( context
->receiveSecs
== 0 ) goto exit
;
6838 // Create dispatch read sources for socket(s).
6840 if( IsValidSocket( sockV4
) )
6842 SocketContext
* sockCtx
;
6844 err
= SocketContextCreate( sockV4
, context
, &sockCtx
);
6845 require_noerr( err
, exit
);
6846 sockV4
= kInvalidSocketRef
;
6848 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, MDNSQueryReadHandler
, SocketContextCancelHandler
, sockCtx
,
6849 &context
->readSourceV4
);
6850 if( err
) ForgetSocketContext( &sockCtx
);
6851 require_noerr( err
, exit
);
6853 dispatch_resume( context
->readSourceV4
);
6856 if( IsValidSocket( sockV6
) )
6858 SocketContext
* sockCtx
;
6860 err
= SocketContextCreate( sockV6
, context
, &sockCtx
);
6861 require_noerr( err
, exit
);
6862 sockV6
= kInvalidSocketRef
;
6864 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, MDNSQueryReadHandler
, SocketContextCancelHandler
, sockCtx
,
6865 &context
->readSourceV6
);
6866 if( err
) ForgetSocketContext( &sockCtx
);
6867 require_noerr( err
, exit
);
6869 dispatch_resume( context
->readSourceV6
);
6872 if( context
->receiveSecs
> 0 )
6874 dispatch_after_f( dispatch_time_seconds( context
->receiveSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
6880 ForgetSocket( &sockV4
);
6881 ForgetSocket( &sockV6
);
6882 if( err
) exit( 1 );
6885 //===========================================================================================================================
6887 //===========================================================================================================================
6889 static void _MDNSColliderCmdStopHandler( void *inContext
, OSStatus inError
);
6891 static void MDNSColliderCmd( void )
6894 MDNSColliderRef collider
= NULL
;
6895 uint8_t * rdataPtr
= NULL
;
6896 size_t rdataLen
= 0;
6897 const char * ifname
;
6899 MDNSColliderProtocols protocols
;
6901 char ifName
[ IF_NAMESIZE
+ 1 ];
6902 uint8_t name
[ kDomainNameLengthMax
];
6904 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
6905 require_noerr_quiet( err
, exit
);
6907 ifname
= if_indextoname( ifIndex
, ifName
);
6910 FPrintF( stderr
, "error: Invalid interface name or index: %s\n", gInterface
);
6915 err
= DomainNameFromString( name
, gMDNSCollider_Name
, NULL
);
6918 FPrintF( stderr
, "error: Invalid record name: %s\n", gMDNSCollider_Name
);
6922 err
= RecordTypeFromArgString( gMDNSCollider_Type
, &type
);
6923 require_noerr_quiet( err
, exit
);
6925 if( gMDNSCollider_RecordData
)
6927 err
= RecordDataFromArgString( gMDNSCollider_RecordData
, &rdataPtr
, &rdataLen
);
6928 require_noerr_quiet( err
, exit
);
6931 err
= MDNSColliderCreate( dispatch_get_main_queue(), &collider
);
6932 require_noerr( err
, exit
);
6934 err
= MDNSColliderSetProgram( collider
, gMDNSCollider_Program
);
6937 FPrintF( stderr
, "error: Failed to set program string: '%s'\n", gMDNSCollider_Program
);
6941 err
= MDNSColliderSetRecord( collider
, name
, type
, rdataPtr
, rdataLen
);
6942 require_noerr( err
, exit
);
6943 ForgetMem( &rdataPtr
);
6945 protocols
= kMDNSColliderProtocol_None
;
6946 if( gMDNSCollider_UseIPv4
|| !gMDNSCollider_UseIPv6
) protocols
|= kMDNSColliderProtocol_IPv4
;
6947 if( gMDNSCollider_UseIPv6
|| !gMDNSCollider_UseIPv4
) protocols
|= kMDNSColliderProtocol_IPv6
;
6948 MDNSColliderSetProtocols( collider
, protocols
);
6949 MDNSColliderSetInterfaceIndex( collider
, ifIndex
);
6950 MDNSColliderSetStopHandler( collider
, _MDNSColliderCmdStopHandler
, collider
);
6952 err
= MDNSColliderStart( collider
);
6953 require_noerr( err
, exit
);
6958 FreeNullSafe( rdataPtr
);
6959 CFReleaseNullSafe( collider
);
6960 if( err
) exit( 1 );
6963 static void _MDNSColliderCmdStopHandler( void *inContext
, OSStatus inError
)
6965 MDNSColliderRef
const collider
= (MDNSColliderRef
) inContext
;
6967 CFRelease( collider
);
6968 exit( inError
? 1 : 0 );
6971 //===========================================================================================================================
6972 // MDNSQueryPrintPrologue
6973 //===========================================================================================================================
6975 static void MDNSQueryPrintPrologue( const MDNSQueryContext
*inContext
)
6977 const int receiveSecs
= inContext
->receiveSecs
;
6979 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, inContext
->ifName
);
6980 FPrintF( stdout
, "Name: %s\n", inContext
->qnameStr
);
6981 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->qtype
), inContext
->qtype
);
6982 FPrintF( stdout
, "Class: IN (%s)\n", inContext
->isQU
? "QU" : "QM" );
6983 FPrintF( stdout
, "Local port: %d\n", inContext
->localPort
);
6984 FPrintF( stdout
, "IP protocols: %?s%?s%?s\n",
6985 inContext
->useIPv4
, "IPv4", ( inContext
->useIPv4
&& inContext
->useIPv6
), ", ", inContext
->useIPv6
, "IPv6" );
6986 FPrintF( stdout
, "Receive duration: " );
6987 if( receiveSecs
>= 0 ) FPrintF( stdout
, "%d second%?c\n", receiveSecs
, receiveSecs
!= 1, 's' );
6988 else FPrintF( stdout
, "∞\n" );
6989 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
6992 //===========================================================================================================================
6993 // MDNSQueryReadHandler
6994 //===========================================================================================================================
6996 static void MDNSQueryReadHandler( void *inContext
)
7000 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
7001 MDNSQueryContext
* const context
= (MDNSQueryContext
*) sockCtx
->userContext
;
7003 sockaddr_ip fromAddr
;
7004 Boolean foundAnswer
= false;
7006 gettimeofday( &now
, NULL
);
7008 err
= SocketRecvFrom( sockCtx
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &msgLen
, &fromAddr
,
7009 sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
7010 require_noerr( err
, exit
);
7012 if( !context
->allResponses
&& ( msgLen
>= kDNSHeaderLength
) )
7014 const uint8_t * ptr
;
7015 const DNSHeader
* const hdr
= (DNSHeader
*) context
->msgBuf
;
7016 unsigned int rrCount
, i
;
7017 uint16_t type
, class;
7018 uint8_t name
[ kDomainNameLengthMax
];
7020 err
= DNSMessageGetAnswerSection( context
->msgBuf
, msgLen
, &ptr
);
7021 require_noerr( err
, exit
);
7023 if( context
->qname
[ 0 ] == 0 )
7025 err
= DomainNameAppendString( context
->qname
, context
->qnameStr
, NULL
);
7026 require_noerr( err
, exit
);
7029 rrCount
= DNSHeaderGetAnswerCount( hdr
) + DNSHeaderGetAuthorityCount( hdr
) + DNSHeaderGetAdditionalCount( hdr
);
7030 for( i
= 0; i
< rrCount
; ++i
)
7032 err
= DNSMessageExtractRecord( context
->msgBuf
, msgLen
, ptr
, name
, &type
, &class, NULL
, NULL
, NULL
, &ptr
);
7033 require_noerr( err
, exit
);
7035 if( ( ( context
->qtype
== kDNSServiceType_ANY
) || ( type
== context
->qtype
) ) &&
7036 DomainNameEqual( name
, context
->qname
) )
7043 if( context
->allResponses
|| foundAnswer
)
7045 FPrintF( stdout
, "---\n" );
7046 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
7047 FPrintF( stdout
, "Source: %##a\n", &fromAddr
);
7048 FPrintF( stdout
, "Message size: %zu\n\n%#.*{du:dnsmsg}",
7049 msgLen
, context
->printRawRData
? 1 : 0, context
->msgBuf
, msgLen
);
7053 if( err
) exit( 1 );
7056 //===========================================================================================================================
7058 //===========================================================================================================================
7060 static void PIDToUUIDCmd( void )
7064 struct proc_uniqidentifierinfo info
;
7066 n
= proc_pidinfo( gPIDToUUID_PID
, PROC_PIDUNIQIDENTIFIERINFO
, 1, &info
, sizeof( info
) );
7067 require_action_quiet( n
== (int) sizeof( info
), exit
, err
= kUnknownErr
);
7069 FPrintF( stdout
, "%#U\n", info
.p_uuid
);
7073 if( err
) exit( 1 );
7076 //===========================================================================================================================
7078 //===========================================================================================================================
7080 typedef struct DNSServerPrivate
* DNSServerRef
;
7084 DNSServerRef server
; // Reference to the DNS server.
7085 dispatch_source_t sigIntSource
; // Dispatch SIGINT source.
7086 dispatch_source_t sigTermSource
; // Dispatch SIGTERM source.
7087 const char * domainOverride
; // If non-NULL, the server is to use this domain instead of "d.test.".
7088 #if( TARGET_OS_DARWIN )
7089 dispatch_source_t processMonitor
; // Process monitor source for process being followed, if any.
7090 pid_t followPID
; // PID of process being followed, if any. (If it exits, we exit).
7091 Boolean addedResolver
; // True if system DNS settings contains a resolver entry for server.
7093 Boolean loopbackOnly
; // True if the server should be bound to the loopback interface.
7095 } DNSServerCmdContext
;
7099 kDNSServerEvent_Started
= 1,
7100 kDNSServerEvent_Stopped
= 2
7102 } DNSServerEventType
;
7104 typedef void ( *DNSServerEventHandler_f
)( DNSServerEventType inType
, uintptr_t inEventData
, void *inContext
);
7106 CFTypeID
DNSServerGetTypeID( void );
7109 dispatch_queue_t inQueue
,
7110 DNSServerEventHandler_f inEventHandler
,
7111 void * inEventContext
,
7112 unsigned int inResponseDelayMs
,
7113 uint32_t inDefaultTTL
,
7115 Boolean inLoopbackOnly
,
7116 const char * inDomain
,
7117 Boolean inBadUDPMode
,
7118 DNSServerRef
* outServer
);
7119 static void DNSServerStart( DNSServerRef inServer
);
7120 static void DNSServerStop( DNSServerRef inServer
);
7122 #define ForgetDNSServer( X ) ForgetCustomEx( X, DNSServerStop, CFRelease )
7124 static void DNSServerCmdContextFree( DNSServerCmdContext
*inContext
);
7125 static void DNSServerCmdEventHandler( DNSServerEventType inType
, uintptr_t inEventData
, void *inContext
);
7126 static void DNSServerCmdSigIntHandler( void *inContext
);
7127 static void DNSServerCmdSigTermHandler( void *inContext
);
7128 #if( TARGET_OS_DARWIN )
7129 static void DNSServerCmdFollowedProcessHandler( void *inContext
);
7132 ulog_define_ex( kDNSSDUtilIdentifier
, DNSServer
, kLogLevelInfo
, kLogFlags_None
, "DNSServer", NULL
);
7133 #define ds_ulog( LEVEL, ... ) ulog( &log_category_from_name( DNSServer ), (LEVEL), __VA_ARGS__ )
7135 static void DNSServerCmd( void )
7138 DNSServerCmdContext
* context
= NULL
;
7140 if( gDNSServer_Foreground
)
7142 LogControl( "DNSServer:output=file;stdout,DNSServer:flags=time;prefix" );
7145 err
= CheckIntegerArgument( gDNSServer_ResponseDelayMs
, "response delay (ms)", 0, INT_MAX
);
7146 require_noerr_quiet( err
, exit
);
7148 err
= CheckIntegerArgument( gDNSServer_DefaultTTL
, "default TTL", 0, INT32_MAX
);
7149 require_noerr_quiet( err
, exit
);
7151 err
= CheckIntegerArgument( gDNSServer_Port
, "port number", -UINT16_MAX
, UINT16_MAX
);
7152 require_noerr_quiet( err
, exit
);
7154 context
= (DNSServerCmdContext
*) calloc( 1, sizeof( *context
) );
7155 require_action( context
, exit
, err
= kNoMemoryErr
);
7157 context
->domainOverride
= gDNSServer_DomainOverride
;
7158 context
->loopbackOnly
= gDNSServer_LoopbackOnly
? true : false;
7160 #if( TARGET_OS_DARWIN )
7161 if( gDNSServer_FollowPID
)
7163 context
->followPID
= _StringToPID( gDNSServer_FollowPID
, &err
);
7164 if( err
|| ( context
->followPID
< 0 ) )
7166 FPrintF( stderr
, "error: Invalid follow PID: %s\n", gDNSServer_FollowPID
);
7171 err
= DispatchProcessMonitorCreate( context
->followPID
, DISPATCH_PROC_EXIT
, dispatch_get_main_queue(),
7172 DNSServerCmdFollowedProcessHandler
, NULL
, context
, &context
->processMonitor
);
7173 require_noerr( err
, exit
);
7174 dispatch_resume( context
->processMonitor
);
7178 context
->followPID
= -1;
7182 signal( SIGINT
, SIG_IGN
);
7183 err
= DispatchSignalSourceCreate( SIGINT
, dispatch_get_main_queue(), DNSServerCmdSigIntHandler
, context
,
7184 &context
->sigIntSource
);
7185 require_noerr( err
, exit
);
7186 dispatch_resume( context
->sigIntSource
);
7188 signal( SIGTERM
, SIG_IGN
);
7189 err
= DispatchSignalSourceCreate( SIGTERM
, dispatch_get_main_queue(), DNSServerCmdSigTermHandler
, context
,
7190 &context
->sigTermSource
);
7191 require_noerr( err
, exit
);
7192 dispatch_resume( context
->sigTermSource
);
7194 err
= DNSServerCreate( dispatch_get_main_queue(), DNSServerCmdEventHandler
, context
,
7195 (unsigned int) gDNSServer_ResponseDelayMs
, (uint32_t) gDNSServer_DefaultTTL
, gDNSServer_Port
, context
->loopbackOnly
,
7196 context
->domainOverride
, gDNSServer_BadUDPMode
? true : false, &context
->server
);
7197 require_noerr( err
, exit
);
7199 DNSServerStart( context
->server
);
7203 FPrintF( stderr
, "Failed to start DNS server: %#m\n", err
);
7204 if( context
) DNSServerCmdContextFree( context
);
7205 if( err
) exit( 1 );
7208 //===========================================================================================================================
7209 // DNSServerCmdContextFree
7210 //===========================================================================================================================
7212 static void DNSServerCmdContextFree( DNSServerCmdContext
*inContext
)
7214 ForgetCF( &inContext
->server
);
7215 dispatch_source_forget( &inContext
->sigIntSource
);
7216 dispatch_source_forget( &inContext
->sigTermSource
);
7217 #if( TARGET_OS_DARWIN )
7218 dispatch_source_forget( &inContext
->processMonitor
);
7223 //===========================================================================================================================
7224 // DNSServerCmdEventHandler
7225 //===========================================================================================================================
7227 #if( TARGET_OS_DARWIN )
7228 static OSStatus
_DNSServerCmdLoopbackResolverAdd( const char *inDomain
, int inPort
);
7229 static OSStatus
_DNSServerCmdLoopbackResolverRemove( void );
7232 static void DNSServerCmdEventHandler( DNSServerEventType inType
, uintptr_t inEventData
, void *inContext
)
7235 DNSServerCmdContext
* const context
= (DNSServerCmdContext
*) inContext
;
7237 if( inType
== kDNSServerEvent_Started
)
7239 #if( TARGET_OS_DARWIN )
7240 const int port
= (int) inEventData
;
7242 err
= _DNSServerCmdLoopbackResolverAdd( context
->domainOverride
? context
->domainOverride
: "d.test.", port
);
7245 ds_ulog( kLogLevelError
, "Failed to add loopback resolver to DNS configuration for \"d.test.\" domain: %#m\n",
7247 if( context
->loopbackOnly
) ForgetDNSServer( &context
->server
);
7251 context
->addedResolver
= true;
7255 else if( inType
== kDNSServerEvent_Stopped
)
7257 const OSStatus stopError
= (OSStatus
) inEventData
;
7259 if( stopError
) ds_ulog( kLogLevelError
, "The server stopped unexpectedly with error: %#m.\n", stopError
);
7262 #if( TARGET_OS_DARWIN )
7263 if( context
->addedResolver
)
7265 err
= _DNSServerCmdLoopbackResolverRemove();
7268 ds_ulog( kLogLevelError
, "Failed to remove loopback resolver from DNS configuration: %#m\n", err
);
7272 context
->addedResolver
= false;
7275 else if( context
->loopbackOnly
)
7280 DNSServerCmdContextFree( context
);
7281 exit( ( stopError
|| err
) ? 1 : 0 );
7285 #if( TARGET_OS_DARWIN )
7286 //===========================================================================================================================
7287 // _DNSServerCmdLoopbackResolverAdd
7288 //===========================================================================================================================
7290 static OSStatus
_DNSServerCmdLoopbackResolverAdd( const char *inDomain
, int inPort
)
7293 SCDynamicStoreRef store
;
7294 CFPropertyListRef plist
= NULL
;
7295 CFStringRef key
= NULL
;
7296 const uint32_t loopbackV4
= htonl( INADDR_LOOPBACK
);
7299 store
= SCDynamicStoreCreate( NULL
, CFSTR( kDNSSDUtilIdentifier
), NULL
, NULL
);
7300 err
= map_scerror( store
);
7301 require_noerr( err
, exit
);
7303 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
7318 kSCPropNetDNSSupplementalMatchDomains
, inDomain
,
7319 kSCPropNetDNSServerAddresses
, &loopbackV4
, in6addr_loopback
.s6_addr
,
7320 kSCPropNetDNSServerPort
, inPort
,
7321 kSCPropInterfaceName
, CFSTR( "lo0" ),
7322 kSCPropNetDNSConfirmedServiceID
, CFSTR( "com.apple.dnssdutil.server" ) );
7323 require_noerr( err
, exit
);
7325 key
= SCDynamicStoreKeyCreateNetworkServiceEntity( NULL
, kSCDynamicStoreDomainState
,
7326 CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS
);
7327 require_action( key
, exit
, err
= kUnknownErr
);
7329 success
= SCDynamicStoreSetValue( store
, key
, plist
);
7330 require_action( success
, exit
, err
= kUnknownErr
);
7333 CFReleaseNullSafe( store
);
7334 CFReleaseNullSafe( plist
);
7335 CFReleaseNullSafe( key
);
7339 //===========================================================================================================================
7340 // _DNSServerCmdLoopbackResolverRemove
7341 //===========================================================================================================================
7343 static OSStatus
_DNSServerCmdLoopbackResolverRemove( void )
7346 SCDynamicStoreRef store
;
7347 CFStringRef key
= NULL
;
7350 store
= SCDynamicStoreCreate( NULL
, CFSTR( kDNSSDUtilIdentifier
), NULL
, NULL
);
7351 err
= map_scerror( store
);
7352 require_noerr( err
, exit
);
7354 key
= SCDynamicStoreKeyCreateNetworkServiceEntity( NULL
, kSCDynamicStoreDomainState
,
7355 CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS
);
7356 require_action( key
, exit
, err
= kUnknownErr
);
7358 success
= SCDynamicStoreRemoveValue( store
, key
);
7359 require_action( success
, exit
, err
= kUnknownErr
);
7362 CFReleaseNullSafe( store
);
7363 CFReleaseNullSafe( key
);
7368 //===========================================================================================================================
7369 // DNSServerCmdSigIntHandler
7370 //===========================================================================================================================
7372 static void _DNSServerCmdShutdown( DNSServerCmdContext
*inContext
, int inSignal
);
7374 static void DNSServerCmdSigIntHandler( void *inContext
)
7376 _DNSServerCmdShutdown( (DNSServerCmdContext
*) inContext
, SIGINT
);
7379 //===========================================================================================================================
7380 // DNSServerCmdSigTermHandler
7381 //===========================================================================================================================
7383 static void DNSServerCmdSigTermHandler( void *inContext
)
7385 _DNSServerCmdShutdown( (DNSServerCmdContext
*) inContext
, SIGTERM
);
7388 #if( TARGET_OS_DARWIN )
7389 //===========================================================================================================================
7390 // DNSServerCmdFollowedProcessHandler
7391 //===========================================================================================================================
7393 static void DNSServerCmdFollowedProcessHandler( void *inContext
)
7395 DNSServerCmdContext
* const context
= (DNSServerCmdContext
*) inContext
;
7397 if( dispatch_source_get_data( context
->processMonitor
) & DISPATCH_PROC_EXIT
) _DNSServerCmdShutdown( context
, 0 );
7401 //===========================================================================================================================
7402 // _DNSServerCmdExternalExit
7403 //===========================================================================================================================
7405 #define SignalNumberToString( X ) ( \
7406 ( (X) == SIGINT ) ? "SIGINT" : \
7407 ( (X) == SIGTERM ) ? "SIGTERM" : \
7410 static void _DNSServerCmdShutdown( DNSServerCmdContext
*inContext
, int inSignal
)
7412 dispatch_source_forget( &inContext
->sigIntSource
);
7413 dispatch_source_forget( &inContext
->sigTermSource
);
7414 #if( TARGET_OS_DARWIN )
7415 dispatch_source_forget( &inContext
->processMonitor
);
7419 ds_ulog( kLogLevelNotice
, "Exiting: followed process (%lld) exited\n", (int64_t) inContext
->followPID
);
7424 ds_ulog( kLogLevelNotice
, "Exiting: received signal %d (%s)\n", inSignal
, SignalNumberToString( inSignal
) );
7427 ForgetDNSServer( &inContext
->server
);
7430 //===========================================================================================================================
7432 //===========================================================================================================================
7434 #define kDDotTestDomainName (const uint8_t *) "\x01" "d" "\x04" "test"
7436 typedef struct DNSDelayedResponse DNSDelayedResponse
;
7437 struct DNSDelayedResponse
7439 DNSDelayedResponse
* next
;
7440 sockaddr_ip destAddr
;
7441 uint64_t targetTicks
;
7446 struct DNSServerPrivate
7448 CFRuntimeBase base
; // CF object base.
7449 uint8_t * domain
; // Parent domain of server's resource records.
7450 dispatch_queue_t queue
; // Queue for DNS server's events.
7451 dispatch_source_t readSourceUDPv4
; // Read source for IPv4 UDP socket.
7452 dispatch_source_t readSourceUDPv6
; // Read source for IPv6 UDP socket.
7453 dispatch_source_t readSourceTCPv4
; // Read source for IPv4 TCP socket.
7454 dispatch_source_t readSourceTCPv6
; // Read source for IPv6 TCP socket.
7455 SocketRef sockUDPv4
;
7456 SocketRef sockUDPv6
;
7457 DNSServerEventHandler_f eventHandler
;
7458 void * eventContext
;
7459 DNSDelayedResponse
* responseList
;
7460 dispatch_source_t responseTimer
;
7461 unsigned int responseDelayMs
;
7462 uint32_t defaultTTL
;
7463 uint32_t serial
; // Serial number for SOA record.
7464 int port
; // Port to use for receiving and sending DNS messages.
7467 Boolean loopbackOnly
;
7468 Boolean badUDPMode
; // True if the server runs in Bad UDP mode.
7471 static void _DNSServerUDPReadHandler( void *inContext
);
7472 static void _DNSServerTCPReadHandler( void *inContext
);
7473 static void _DNSDelayedResponseFree( DNSDelayedResponse
*inResponse
);
7474 static void _DNSDelayedResponseFreeList( DNSDelayedResponse
*inList
);
7476 CF_CLASS_DEFINE( DNSServer
);
7480 dispatch_queue_t inQueue
,
7481 DNSServerEventHandler_f inEventHandler
,
7482 void * inEventContext
,
7483 unsigned int inResponseDelayMs
,
7484 uint32_t inDefaultTTL
,
7486 Boolean inLoopbackOnly
,
7487 const char * inDomain
,
7488 Boolean inBadUDPMode
,
7489 DNSServerRef
* outServer
)
7492 DNSServerRef obj
= NULL
;
7494 require_action_quiet( inDefaultTTL
<= INT32_MAX
, exit
, err
= kRangeErr
);
7496 CF_OBJECT_CREATE( DNSServer
, obj
, err
, exit
);
7498 ReplaceDispatchQueue( &obj
->queue
, inQueue
);
7499 obj
->eventHandler
= inEventHandler
;
7500 obj
->eventContext
= inEventContext
;
7501 obj
->responseDelayMs
= inResponseDelayMs
;
7502 obj
->defaultTTL
= inDefaultTTL
;
7504 obj
->loopbackOnly
= inLoopbackOnly
;
7505 obj
->badUDPMode
= inBadUDPMode
;
7509 err
= StringToDomainName( inDomain
, &obj
->domain
, NULL
);
7510 require_noerr_quiet( err
, exit
);
7514 err
= DomainNameDup( kDDotTestDomainName
, &obj
->domain
, NULL
);
7515 require_noerr_quiet( err
, exit
);
7523 CFReleaseNullSafe( obj
);
7527 //===========================================================================================================================
7528 // _DNSServerFinalize
7529 //===========================================================================================================================
7531 static void _DNSServerFinalize( CFTypeRef inObj
)
7533 DNSServerRef
const me
= (DNSServerRef
) inObj
;
7535 check( !me
->readSourceUDPv4
);
7536 check( !me
->readSourceUDPv6
);
7537 check( !me
->readSourceTCPv4
);
7538 check( !me
->readSourceTCPv6
);
7539 check( !me
->responseTimer
);
7540 ForgetMem( &me
->domain
);
7541 dispatch_forget( &me
->queue
);
7544 //===========================================================================================================================
7546 //===========================================================================================================================
7548 static void _DNSServerStart( void *inContext
);
7549 static void _DNSServerStop( void *inContext
, OSStatus inError
);
7551 static void DNSServerStart( DNSServerRef me
)
7554 dispatch_async_f( me
->queue
, me
, _DNSServerStart
);
7557 static void _DNSServerStart( void *inContext
)
7561 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7562 SocketRef sock
= kInvalidSocketRef
;
7563 SocketContext
* sockCtx
= NULL
;
7564 const uint32_t loopbackV4
= htonl( INADDR_LOOPBACK
);
7565 int year
, month
, day
;
7567 // Create IPv4 UDP socket.
7568 // Initially, me->port is the port requested by the user. If it's 0, then the user wants any available ephemeral port.
7569 // If it's negative, then the user would like a port number equal to its absolute value, but will settle for any
7570 // available ephemeral port, if it's not available. The actual port number that was used will be stored in me->port and
7571 // used for the remaining sockets.
7573 err
= _ServerSocketOpenEx2( AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
, me
->loopbackOnly
? &loopbackV4
: NULL
,
7574 me
->port
, &me
->port
, kSocketBufferSize_DontSet
, me
->loopbackOnly
? true : false, &sock
);
7575 require_noerr( err
, exit
);
7576 check( me
->port
> 0 );
7578 // Create read source for IPv4 UDP socket.
7580 err
= SocketContextCreate( sock
, me
, &sockCtx
);
7581 require_noerr( err
, exit
);
7582 sock
= kInvalidSocketRef
;
7584 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _DNSServerUDPReadHandler
, SocketContextCancelHandler
, sockCtx
,
7585 &me
->readSourceUDPv4
);
7586 require_noerr( err
, exit
);
7587 dispatch_resume( me
->readSourceUDPv4
);
7588 me
->sockUDPv4
= sockCtx
->sock
;
7591 // Create IPv6 UDP socket.
7593 err
= _ServerSocketOpenEx2( AF_INET6
, SOCK_DGRAM
, IPPROTO_UDP
, me
->loopbackOnly
? &in6addr_loopback
: NULL
,
7594 me
->port
, NULL
, kSocketBufferSize_DontSet
, me
->loopbackOnly
? true : false, &sock
);
7595 require_noerr( err
, exit
);
7597 // Create read source for IPv6 UDP socket.
7599 err
= SocketContextCreate( sock
, me
, &sockCtx
);
7600 require_noerr( err
, exit
);
7601 sock
= kInvalidSocketRef
;
7603 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _DNSServerUDPReadHandler
, SocketContextCancelHandler
, sockCtx
,
7604 &me
->readSourceUDPv6
);
7605 require_noerr( err
, exit
);
7606 dispatch_resume( me
->readSourceUDPv6
);
7607 me
->sockUDPv6
= sockCtx
->sock
;
7610 // Create IPv4 TCP socket.
7612 err
= _ServerSocketOpenEx2( AF_INET
, SOCK_STREAM
, IPPROTO_TCP
, me
->loopbackOnly
? &loopbackV4
: NULL
,
7613 me
->port
, NULL
, kSocketBufferSize_DontSet
, false, &sock
);
7614 require_noerr( err
, exit
);
7616 // Create read source for IPv4 TCP socket.
7618 err
= SocketContextCreate( sock
, me
, &sockCtx
);
7619 require_noerr( err
, exit
);
7620 sock
= kInvalidSocketRef
;
7622 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _DNSServerTCPReadHandler
, SocketContextCancelHandler
, sockCtx
,
7623 &me
->readSourceTCPv4
);
7624 require_noerr( err
, exit
);
7625 dispatch_resume( me
->readSourceTCPv4
);
7628 // Create IPv6 TCP socket.
7630 err
= _ServerSocketOpenEx2( AF_INET6
, SOCK_STREAM
, IPPROTO_TCP
, me
->loopbackOnly
? &in6addr_loopback
: NULL
,
7631 me
->port
, NULL
, kSocketBufferSize_DontSet
, false, &sock
);
7632 require_noerr( err
, exit
);
7634 // Create read source for IPv6 TCP socket.
7636 err
= SocketContextCreate( sock
, me
, &sockCtx
);
7637 require_noerr( err
, exit
);
7638 sock
= kInvalidSocketRef
;
7640 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _DNSServerTCPReadHandler
, SocketContextCancelHandler
, sockCtx
,
7641 &me
->readSourceTCPv6
);
7642 require_noerr( err
, exit
);
7643 dispatch_resume( me
->readSourceTCPv6
);
7646 ds_ulog( kLogLevelInfo
, "Server is using port %d.\n", me
->port
);
7647 if( me
->eventHandler
) me
->eventHandler( kDNSServerEvent_Started
, (uintptr_t) me
->port
, me
->eventContext
);
7649 // Create the serial number for the server's SOA record in the YYYMMDDnn convention recommended by
7650 // <https://tools.ietf.org/html/rfc1912#section-2.2> using the current time.
7652 gettimeofday( &now
, NULL
);
7653 SecondsToYMD_HMS( ( INT64_C_safe( kDaysToUnixEpoch
) * kSecondsPerDay
) + now
.tv_sec
, &year
, &month
, &day
,
7655 me
->serial
= (uint32_t)( ( year
* 1000000 ) + ( month
* 10000 ) + ( day
* 100 ) + 1 );
7658 ForgetSocket( &sock
);
7659 if( sockCtx
) SocketContextRelease( sockCtx
);
7660 if( err
) _DNSServerStop( me
, err
);
7663 //===========================================================================================================================
7665 //===========================================================================================================================
7667 static void _DNSServerUserStop( void *inContext
);
7668 static void _DNSServerStop2( void *inContext
);
7670 static void DNSServerStop( DNSServerRef me
)
7673 dispatch_async_f( me
->queue
, me
, _DNSServerUserStop
);
7676 static void _DNSServerUserStop( void *inContext
)
7678 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7680 _DNSServerStop( me
, kNoErr
);
7684 static void _DNSServerStop( void *inContext
, OSStatus inError
)
7686 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7688 me
->stopError
= inError
;
7689 dispatch_source_forget( &me
->readSourceUDPv4
);
7690 dispatch_source_forget( &me
->readSourceUDPv6
);
7691 dispatch_source_forget( &me
->readSourceTCPv4
);
7692 dispatch_source_forget( &me
->readSourceTCPv6
);
7693 dispatch_source_forget( &me
->responseTimer
);
7694 me
->sockUDPv4
= kInvalidSocketRef
;
7695 me
->sockUDPv6
= kInvalidSocketRef
;
7697 if( me
->responseList
)
7699 _DNSDelayedResponseFreeList( me
->responseList
);
7700 me
->responseList
= NULL
;
7702 dispatch_async_f( me
->queue
, me
, _DNSServerStop2
);
7705 static void _DNSServerStop2( void *inContext
)
7707 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7712 if( me
->eventHandler
) me
->eventHandler( kDNSServerEvent_Stopped
, (uintptr_t) me
->stopError
, me
->eventContext
);
7718 //===========================================================================================================================
7719 // _DNSDelayedResponseFree
7720 //===========================================================================================================================
7722 static void _DNSDelayedResponseFree( DNSDelayedResponse
*inResponse
)
7724 ForgetMem( &inResponse
->msgPtr
);
7728 //===========================================================================================================================
7729 // _DNSDelayedResponseFreeList
7730 //===========================================================================================================================
7732 static void _DNSDelayedResponseFreeList( DNSDelayedResponse
*inList
)
7734 DNSDelayedResponse
* response
;
7736 while( ( response
= inList
) != NULL
)
7738 inList
= response
->next
;
7739 _DNSDelayedResponseFree( response
);
7743 //===========================================================================================================================
7744 // _DNSServerUDPReadHandler
7745 //===========================================================================================================================
7748 _DNSServerAnswerQuery(
7749 DNSServerRef inServer
,
7750 const uint8_t * inQueryPtr
,
7753 uint8_t ** outResponsePtr
,
7754 size_t * outResponseLen
);
7756 #define _DNSServerAnswerQueryForUDP( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
7757 _DNSServerAnswerQuery( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, false, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
7759 #define _DNSServerAnswerQueryForTCP( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
7760 _DNSServerAnswerQuery( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, true, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
7763 _DNSServerScheduleDelayedResponse(
7764 DNSServerRef inServer
,
7765 const struct sockaddr
* inDestAddr
,
7768 static void _DNSServerUDPDelayedSend( void *inContext
);
7770 static void _DNSServerUDPReadHandler( void *inContext
)
7773 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
7774 DNSServerRef
const me
= (DNSServerRef
) sockCtx
->userContext
;
7777 sockaddr_ip clientAddr
;
7778 socklen_t clientAddrLen
;
7779 uint8_t * responsePtr
= NULL
; // malloc'd
7783 gettimeofday( &now
, NULL
);
7787 clientAddrLen
= (socklen_t
) sizeof( clientAddr
);
7788 n
= recvfrom( sockCtx
->sock
, (char *) msg
, sizeof( msg
), 0, &clientAddr
.sa
, &clientAddrLen
);
7789 err
= map_socket_value_errno( sockCtx
->sock
, n
>= 0, n
);
7790 require_noerr( err
, exit
);
7792 ds_ulog( kLogLevelInfo
, "UDP server received %zd bytes from %##a at %{du:time}.\n", n
, &clientAddr
, &now
);
7794 if( n
< kDNSHeaderLength
)
7796 ds_ulog( kLogLevelInfo
, "UDP DNS message is too small (%zd < %d).\n", n
, kDNSHeaderLength
);
7800 ds_ulog( kLogLevelInfo
, "UDP received message:\n\n%1{du:dnsmsg}", msg
, (size_t) n
);
7804 err
= _DNSServerAnswerQueryForUDP( me
, msg
, (size_t) n
, &responsePtr
, &responseLen
);
7805 require_noerr_quiet( err
, exit
);
7807 // Schedule response.
7809 if( me
->responseDelayMs
> 0 )
7811 err
= _DNSServerScheduleDelayedResponse( me
, &clientAddr
.sa
, responsePtr
, responseLen
);
7812 require_noerr( err
, exit
);
7817 ds_ulog( kLogLevelInfo
, "UDP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen
, responsePtr
, responseLen
);
7819 n
= sendto( sockCtx
->sock
, (char *) responsePtr
, responseLen
, 0, &clientAddr
.sa
, clientAddrLen
);
7820 err
= map_socket_value_errno( sockCtx
->sock
, n
== (ssize_t
) responseLen
, n
);
7821 require_noerr( err
, exit
);
7825 FreeNullSafe( responsePtr
);
7830 _DNSServerScheduleDelayedResponse(
7832 const struct sockaddr
* inDestAddr
,
7837 DNSDelayedResponse
* response
;
7838 DNSDelayedResponse
** responsePtr
;
7839 DNSDelayedResponse
* newResponse
;
7840 uint64_t targetTicks
;
7842 targetTicks
= UpTicks() + MillisecondsToUpTicks( me
->responseDelayMs
);
7844 newResponse
= (DNSDelayedResponse
*) calloc( 1, sizeof( *newResponse
) );
7845 require_action( newResponse
, exit
, err
= kNoMemoryErr
);
7847 if( !me
->responseList
|| ( targetTicks
< me
->responseList
->targetTicks
) )
7849 dispatch_source_forget( &me
->responseTimer
);
7851 err
= DispatchTimerCreate( dispatch_time_milliseconds( me
->responseDelayMs
), DISPATCH_TIME_FOREVER
,
7852 ( (uint64_t) me
->responseDelayMs
) * kNanosecondsPerMillisecond
/ 10, me
->queue
, _DNSServerUDPDelayedSend
,
7853 NULL
, me
, &me
->responseTimer
);
7854 require_noerr( err
, exit
);
7855 dispatch_resume( me
->responseTimer
);
7858 SockAddrCopy( inDestAddr
, &newResponse
->destAddr
);
7859 newResponse
->targetTicks
= targetTicks
;
7860 newResponse
->msgPtr
= inMsgPtr
;
7861 newResponse
->msgLen
= inMsgLen
;
7863 for( responsePtr
= &me
->responseList
; ( response
= *responsePtr
) != NULL
; responsePtr
= &response
->next
)
7865 if( newResponse
->targetTicks
< response
->targetTicks
) break;
7867 newResponse
->next
= response
;
7868 *responsePtr
= newResponse
;
7873 if( newResponse
) _DNSDelayedResponseFree( newResponse
);
7877 static void _DNSServerUDPDelayedSend( void *inContext
)
7880 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7881 DNSDelayedResponse
* response
;
7885 uint64_t remainingNs
;
7886 DNSDelayedResponse
* freeList
= NULL
;
7888 dispatch_source_forget( &me
->responseTimer
);
7890 nowTicks
= UpTicks();
7891 while( ( ( response
= me
->responseList
) != NULL
) && ( response
->targetTicks
<= nowTicks
) )
7893 me
->responseList
= response
->next
;
7895 ds_ulog( kLogLevelInfo
, "UDP sending %zu byte response (delayed):\n\n%1{du:dnsmsg}",
7896 response
->msgLen
, response
->msgPtr
, response
->msgLen
);
7898 sock
= ( response
->destAddr
.sa
.sa_family
== AF_INET
) ? me
->sockUDPv4
: me
->sockUDPv6
;
7899 n
= sendto( sock
, (char *) response
->msgPtr
, response
->msgLen
, 0, &response
->destAddr
.sa
,
7900 SockAddrGetSize( &response
->destAddr
) );
7901 err
= map_socket_value_errno( sock
, n
== (ssize_t
) response
->msgLen
, n
);
7904 response
->next
= freeList
;
7905 freeList
= response
;
7906 nowTicks
= UpTicks();
7911 check( response
->targetTicks
> nowTicks
);
7912 remainingNs
= UpTicksToNanoseconds( response
->targetTicks
- nowTicks
);
7913 if( remainingNs
> INT64_MAX
) remainingNs
= INT64_MAX
;
7915 err
= DispatchTimerCreate( dispatch_time( DISPATCH_TIME_NOW
, (int64_t) remainingNs
), DISPATCH_TIME_FOREVER
, 0,
7916 me
->queue
, _DNSServerUDPDelayedSend
, NULL
, me
, &me
->responseTimer
);
7917 require_noerr( err
, exit
);
7918 dispatch_resume( me
->responseTimer
);
7922 if( freeList
) _DNSDelayedResponseFreeList( freeList
);
7925 //===========================================================================================================================
7926 // _DNSServerAnswerQuery
7927 //===========================================================================================================================
7929 #define kLabelPrefix_Alias "alias"
7930 #define kLabelPrefix_AliasTTL "alias-ttl"
7931 #define kLabelPrefix_Count "count-"
7932 #define kLabelPrefix_Tag "tag-"
7933 #define kLabelPrefix_TTL "ttl-"
7934 #define kLabel_IPv4 "ipv4"
7935 #define kLabel_IPv6 "ipv6"
7936 #define kLabelPrefix_SRV "srv-"
7938 #define kMaxAliasTTLCount ( ( kDomainLabelLengthMax - sizeof_string( kLabelPrefix_AliasTTL ) ) / 2 )
7939 #define kMaxParsedSRVCount ( kDomainNameLengthMax / ( 1 + sizeof_string( kLabelPrefix_SRV ) + 5 ) )
7943 uint16_t priority
; // Priority from SRV label.
7944 uint16_t weight
; // Weight from SRV label.
7945 uint16_t port
; // Port number from SRV label.
7946 uint16_t targetLen
; // Total length of the target hostname labels that follow an SRV label.
7947 const uint8_t * targetPtr
; // Pointer to the target hostname embedded in a domain name.
7952 _DNSServerInitializeResponseMessage(
7955 unsigned int inFlags
,
7956 const uint8_t * inQName
,
7957 unsigned int inQType
,
7958 unsigned int inQClass
);
7960 _DNSServerAnswerQueryDynamically(
7961 DNSServerRef inServer
,
7962 const uint8_t * inQName
,
7963 unsigned int inQType
,
7964 unsigned int inQClass
,
7966 DataBuffer
* inDB
);
7968 _DNSServerNameIsSRVName(
7969 DNSServerRef inServer
,
7970 const uint8_t * inName
,
7971 const uint8_t ** outDomainPtr
,
7972 size_t * outDomainLen
,
7973 ParsedSRV inSRVArray
[ kMaxParsedSRVCount
],
7974 size_t * outSRVCount
);
7976 _DNSServerNameIsHostname(
7977 DNSServerRef inServer
,
7978 const uint8_t * inName
,
7979 uint32_t * outAliasCount
,
7980 uint32_t inAliasTTLs
[ kMaxAliasTTLCount
],
7981 size_t * outAliasTTLCount
,
7982 unsigned int * outCount
,
7983 unsigned int * outRandCount
,
7986 Boolean
* outHasAAAA
,
7987 Boolean
* outHasSOA
);
7990 _DNSServerAnswerQuery(
7992 const uint8_t * const inQueryPtr
,
7993 const size_t inQueryLen
,
7995 uint8_t ** outResponsePtr
,
7996 size_t * outResponseLen
)
8000 const uint8_t * ptr
;
8001 const uint8_t * const queryEnd
= &inQueryPtr
[ inQueryLen
];
8002 const DNSHeader
* qhdr
;
8003 const dns_fixed_fields_question
* fields
;
8004 unsigned int msgID
, qflags
, qtype
, qclass
, rflags
;
8005 uint8_t qname
[ kDomainNameLengthMax
];
8007 DataBuffer_Init( &dataBuf
, NULL
, 0, kDNSMaxTCPMessageSize
);
8009 require_action_quiet( inQueryLen
>= kDNSHeaderLength
, exit
, err
= kUnderrunErr
);
8011 qhdr
= (const DNSHeader
*) inQueryPtr
;
8012 msgID
= DNSHeaderGetID( qhdr
);
8013 qflags
= DNSHeaderGetFlags( qhdr
);
8015 // Minimal checking of the query message's header.
8017 if( ( qflags
& kDNSHeaderFlag_Response
) || // The message must be a query, not a response.
8018 ( DNSFlagsGetOpCode( qflags
) != kDNSOpCode_Query
) || // OPCODE must be QUERY (standard query).
8019 ( DNSHeaderGetQuestionCount( qhdr
) != 1 ) ) // There should be a single question.
8027 ptr
= (const uint8_t *) &qhdr
[ 1 ];
8028 err
= DNSMessageExtractDomainName( inQueryPtr
, inQueryLen
, ptr
, qname
, &ptr
);
8029 require_noerr( err
, exit
);
8031 // Get QTYPE and QCLASS.
8033 require_action_quiet( ( (size_t)( queryEnd
- ptr
) ) >= sizeof( *fields
), exit
, err
= kUnderrunErr
);
8034 fields
= (const dns_fixed_fields_question
*) ptr
;
8035 qtype
= dns_fixed_fields_question_get_type( fields
);
8036 qclass
= dns_fixed_fields_question_get_class( fields
);
8038 // Create a tentative response message.
8040 rflags
= kDNSHeaderFlag_Response
;
8041 if( qflags
& kDNSHeaderFlag_RecursionDesired
) rflags
|= kDNSHeaderFlag_RecursionDesired
;
8042 DNSFlagsSetOpCode( rflags
, kDNSOpCode_Query
);
8044 if( me
->badUDPMode
&& !inForTCP
) msgID
= (uint16_t)( msgID
+ 1 );
8045 err
= _DNSServerInitializeResponseMessage( &dataBuf
, msgID
, rflags
, qname
, qtype
, qclass
);
8046 require_noerr( err
, exit
);
8048 err
= _DNSServerAnswerQueryDynamically( me
, qname
, qtype
, qclass
, inForTCP
, &dataBuf
);
8051 DNSFlagsSetRCode( rflags
, kDNSRCode_ServerFailure
);
8052 err
= _DNSServerInitializeResponseMessage( &dataBuf
, msgID
, rflags
, qname
, qtype
, qclass
);
8053 require_noerr( err
, exit
);
8056 err
= DataBuffer_Detach( &dataBuf
, outResponsePtr
, outResponseLen
);
8057 require_noerr( err
, exit
);
8060 DataBuffer_Free( &dataBuf
);
8065 _DNSServerInitializeResponseMessage(
8068 unsigned int inFlags
,
8069 const uint8_t * inQName
,
8070 unsigned int inQType
,
8071 unsigned int inQClass
)
8076 DataBuffer_Reset( inDB
);
8078 memset( &header
, 0, sizeof( header
) );
8079 DNSHeaderSetID( &header
, inID
);
8080 DNSHeaderSetFlags( &header
, inFlags
);
8081 DNSHeaderSetQuestionCount( &header
, 1 );
8083 err
= DataBuffer_Append( inDB
, &header
, sizeof( header
) );
8084 require_noerr( err
, exit
);
8086 err
= _DataBuffer_AppendDNSQuestion( inDB
, inQName
, DomainNameLength( inQName
), (uint16_t) inQType
,
8087 (uint16_t) inQClass
);
8088 require_noerr( err
, exit
);
8095 _DNSServerAnswerQueryDynamically(
8097 const uint8_t * const inQName
,
8098 const unsigned int inQType
,
8099 const unsigned int inQClass
,
8100 const Boolean inForTCP
,
8101 DataBuffer
* const inDB
)
8105 unsigned int flags
, rcode
;
8106 uint32_t aliasCount
, i
;
8107 uint32_t aliasTTLs
[ kMaxAliasTTLCount
];
8108 size_t aliasTTLCount
;
8109 unsigned int addrCount
, randCount
;
8111 ParsedSRV srvArray
[ kMaxParsedSRVCount
];
8113 const uint8_t * srvDomainPtr
;
8114 size_t srvDomainLen
;
8115 unsigned int answerCount
;
8116 Boolean notImplemented
, truncated
;
8117 Boolean useAliasTTLs
, nameExists
, nameHasA
, nameHasAAAA
, nameHasSRV
, nameHasSOA
;
8118 uint8_t namePtr
[ 2 ];
8119 dns_fixed_fields_record fields
;
8124 require_action_quiet( inQClass
== kDNSServiceClass_IN
, done
, notImplemented
= true );
8126 notImplemented
= false;
8129 nameHasAAAA
= false;
8131 useAliasTTLs
= false;
8136 if( _DNSServerNameIsHostname( me
, inQName
, &aliasCount
, aliasTTLs
, &aliasTTLCount
, &addrCount
, &randCount
, &ttl
,
8137 &nameHasA
, &nameHasAAAA
, &nameHasSOA
) )
8139 check( !( ( aliasCount
> 0 ) && ( aliasTTLCount
> 0 ) ) );
8140 check( ( addrCount
>= 1 ) && ( addrCount
<= 255 ) );
8141 check( ( randCount
== 0 ) || ( ( randCount
>= addrCount
) && ( randCount
<= 255 ) ) );
8142 check( nameHasA
|| nameHasAAAA
);
8144 if( aliasTTLCount
> 0 )
8146 aliasCount
= (uint32_t) aliasTTLCount
;
8147 useAliasTTLs
= true;
8151 else if( _DNSServerNameIsSRVName( me
, inQName
, &srvDomainPtr
, &srvDomainLen
, srvArray
, &srvCount
) )
8156 require_quiet( nameExists
, done
);
8158 if( aliasCount
> 0 )
8161 uint8_t rdataLabel
[ 1 + kDomainLabelLengthMax
+ 1 ];
8163 // If aliasCount is non-zero, then the first label of QNAME is either "alias" or "alias-<N>". superPtr is a name
8164 // compression pointer to the second label of QNAME, i.e., the immediate superdomain name of QNAME. It's used for
8165 // the RDATA of CNAME records whose canonical name ends with the superdomain name. It may also be used to construct
8166 // CNAME record names, when the offset to the previous CNAME's RDATA doesn't fit in a compression pointer.
8168 const uint8_t superPtr
[ 2 ] = { 0xC0, (uint8_t)( kDNSHeaderLength
+ 1 + inQName
[ 0 ] ) };
8170 // The name of the first CNAME record is equal to QNAME, so nameOffset is set to offset of QNAME.
8172 nameOffset
= kDNSHeaderLength
;
8174 for( i
= aliasCount
; i
>= 1; --i
)
8180 uint8_t nameLabel
[ 1 + kDomainLabelLengthMax
];
8182 if( nameOffset
<= kDNSCompressionOffsetMax
)
8184 DNSMessageWriteLabelPointer( namePtr
, nameOffset
);
8185 nameLen
= sizeof( namePtr
);
8189 memcpy( nameLabel
, rdataLabel
, 1 + rdataLabel
[ 0 ] );
8190 nameLen
= 1 + nameLabel
[ 0 ] + sizeof( superPtr
);
8195 char * dst
= (char *) &rdataLabel
[ 1 ];
8196 char * const lim
= (char *) &rdataLabel
[ countof( rdataLabel
) ];
8200 err
= SNPrintF_Add( &dst
, lim
, kLabelPrefix_AliasTTL
);
8201 require_noerr( err
, exit
);
8203 for( j
= aliasCount
- ( i
- 1 ); j
< aliasCount
; ++j
)
8205 err
= SNPrintF_Add( &dst
, lim
, "-%u", aliasTTLs
[ j
] );
8206 require_noerr( err
, exit
);
8211 err
= SNPrintF_Add( &dst
, lim
, kLabelPrefix_Alias
"%?{end}-%u", i
== 2, i
- 1 );
8212 require_noerr( err
, exit
);
8214 rdataLabel
[ 0 ] = (uint8_t)( dst
- (char *) &rdataLabel
[ 1 ] );
8215 rdataLen
= 1 + rdataLabel
[ 0 ] + sizeof( superPtr
);
8219 rdataLen
= sizeof( superPtr
);
8224 size_t recordLen
= nameLen
+ sizeof( fields
) + rdataLen
;
8226 if( ( DataBuffer_GetLen( inDB
) + recordLen
) > kDNSMaxUDPMessageSize
)
8234 // Set CNAME record's NAME.
8236 if( nameOffset
<= kDNSCompressionOffsetMax
)
8238 err
= DataBuffer_Append( inDB
, namePtr
, sizeof( namePtr
) );
8239 require_noerr( err
, exit
);
8243 err
= DataBuffer_Append( inDB
, nameLabel
, 1 + nameLabel
[ 0 ] );
8244 require_noerr( err
, exit
);
8246 err
= DataBuffer_Append( inDB
, superPtr
, sizeof( superPtr
) );
8247 require_noerr( err
, exit
);
8250 // Set CNAME record's TYPE, CLASS, TTL, and RDLENGTH.
8252 aliasTTL
= useAliasTTLs
? aliasTTLs
[ aliasCount
- i
] : me
->defaultTTL
;
8253 dns_fixed_fields_record_init( &fields
, kDNSServiceType_CNAME
, kDNSServiceClass_IN
, aliasTTL
,
8254 (uint16_t) rdataLen
);
8255 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
8256 require_noerr( err
, exit
);
8258 // Save offset of CNAME record's RDATA, which may be used for the name of the next CNAME record.
8260 nameOffset
= DataBuffer_GetLen( inDB
);
8262 // Set CNAME record's RDATA.
8266 err
= DataBuffer_Append( inDB
, rdataLabel
, 1 + rdataLabel
[ 0 ] );
8267 require_noerr( err
, exit
);
8269 err
= DataBuffer_Append( inDB
, superPtr
, sizeof( superPtr
) );
8270 require_noerr( err
, exit
);
8273 namePtr
[ 0 ] = superPtr
[ 0 ];
8274 namePtr
[ 1 ] = superPtr
[ 1 ];
8278 // There are no aliases, so initialize the name compression pointer to point to QNAME.
8280 DNSMessageWriteLabelPointer( namePtr
, kDNSHeaderLength
);
8283 if( ( inQType
== kDNSServiceType_A
) || ( inQType
== kDNSServiceType_AAAA
) )
8285 uint8_t * lsb
; // Pointer to the least significant byte of record data.
8286 size_t recordLen
; // Length of the entire record.
8287 size_t rdataLen
; // Length of record's RDATA.
8288 uint8_t rdata
[ 16 ]; // A buffer that's big enough for either A or AAAA RDATA.
8289 uint8_t randIntegers
[ 255 ]; // Array for random integers in [1, 255].
8290 const int useBadAddrs
= ( me
->badUDPMode
&& !inForTCP
) ? true : false;
8292 if( inQType
== kDNSServiceType_A
)
8294 const uint32_t baseAddrV4
= useBadAddrs
? kDNSServerBadBaseAddrV4
: kDNSServerBaseAddrV4
;
8296 require_quiet( nameHasA
, done
);
8299 WriteBig32( rdata
, baseAddrV4
);
8304 const uint8_t * const baseAddrV6
= useBadAddrs
? kDNSServerBadBaseAddrV6
: kDNSServerBaseAddrV6
;
8306 require_quiet( nameHasAAAA
, done
);
8309 memcpy( rdata
, baseAddrV6
, 16 );
8315 // Populate the array with all integers between 1 and <randCount>, inclusive.
8317 for( i
= 0; i
< randCount
; ++i
) randIntegers
[ i
] = (uint8_t)( i
+ 1 );
8319 // Prevent dubious static analyzer warning.
8320 // Note: _DNSServerNameIsHostname() already enforces randCount >= addrCount. Also, this require_fatal() check
8321 // needs to be placed right before the next for-loop. Any earlier, and the static analyzer warning will persist
8324 require_fatal( addrCount
<= randCount
, "Invalid Count label values: addrCount %u > randCount %u",
8325 addrCount
, randCount
);
8327 // Create a contiguous subarray starting at index 0 that contains <addrCount> randomly chosen integers between
8328 // 1 and <randCount>, inclusive.
8329 // Loop invariant 1: Array elements with indexes in [0, i - 1] have been randomly chosen.
8330 // Loop invariant 2: Array elements with indexes in [i, randCount - 1] are candidates for being chosen.
8332 for( i
= 0; i
< addrCount
; ++i
)
8337 j
= RandomRange( i
, randCount
- 1 );
8340 tmp
= randIntegers
[ i
];
8341 randIntegers
[ i
] = randIntegers
[ j
];
8342 randIntegers
[ j
] = tmp
;
8347 recordLen
= sizeof( namePtr
) + sizeof( fields
) + rdataLen
;
8348 for( i
= 0; i
< addrCount
; ++i
)
8350 if( !inForTCP
&& ( ( DataBuffer_GetLen( inDB
) + recordLen
) > kDNSMaxUDPMessageSize
) )
8358 err
= DataBuffer_Append( inDB
, namePtr
, sizeof( namePtr
) );
8359 require_noerr( err
, exit
);
8361 // Set record TYPE, CLASS, TTL, and RDLENGTH.
8363 dns_fixed_fields_record_init( &fields
, (uint16_t) inQType
, kDNSServiceClass_IN
, ttl
, (uint16_t) rdataLen
);
8364 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
8365 require_noerr( err
, exit
);
8367 // Set record RDATA.
8369 *lsb
= ( randCount
> 0 ) ? randIntegers
[ i
] : ( *lsb
+ 1 );
8371 err
= DataBuffer_Append( inDB
, rdata
, rdataLen
);
8372 require_noerr( err
, exit
);
8377 else if( inQType
== kDNSServiceType_SRV
)
8379 require_quiet( nameHasSRV
, done
);
8381 dns_fixed_fields_record_init( &fields
, kDNSServiceType_SRV
, kDNSServiceClass_IN
, me
->defaultTTL
, 0 );
8383 for( i
= 0; i
< srvCount
; ++i
)
8385 dns_fixed_fields_srv fieldsSRV
;
8388 const ParsedSRV
* const srv
= &srvArray
[ i
];
8390 rdataLen
= sizeof( fieldsSRV
) + srvDomainLen
+ srv
->targetLen
+ 1;
8391 recordLen
= sizeof( namePtr
) + sizeof( fields
) + rdataLen
;
8393 if( !inForTCP
&& ( ( DataBuffer_GetLen( inDB
) + recordLen
) > kDNSMaxUDPMessageSize
) )
8399 // Append record NAME.
8401 err
= DataBuffer_Append( inDB
, namePtr
, sizeof( namePtr
) );
8402 require_noerr( err
, exit
);
8404 // Append record TYPE, CLASS, TTL, and RDLENGTH.
8406 WriteBig16( fields
.rdlength
, rdataLen
);
8407 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
8408 require_noerr( err
, exit
);
8410 // Append SRV RDATA.
8412 dns_fixed_fields_srv_init( &fieldsSRV
, srv
->priority
, srv
->weight
, srv
->port
);
8414 err
= DataBuffer_Append( inDB
, &fieldsSRV
, sizeof( fieldsSRV
) );
8415 require_noerr( err
, exit
);
8417 if( srv
->targetLen
> 0 )
8419 err
= DataBuffer_Append( inDB
, srv
->targetPtr
, srv
->targetLen
);
8420 require_noerr( err
, exit
);
8423 if( srvDomainLen
> 0 )
8425 err
= DataBuffer_Append( inDB
, srvDomainPtr
, srvDomainLen
);
8426 require_noerr( err
, exit
);
8429 err
= DataBuffer_Append( inDB
, "", 1 ); // Append root label.
8430 require_noerr( err
, exit
);
8435 else if( inQType
== kDNSServiceType_SOA
)
8437 size_t nameLen
, recordLen
;
8439 require_quiet( nameHasSOA
, done
);
8441 nameLen
= DomainNameLength( me
->domain
);
8444 err
= AppendSOARecord( NULL
, me
->domain
, nameLen
, 0, 0, 0, kRootLabel
, kRootLabel
, 0, 0, 0, 0, 0, &recordLen
);
8445 require_noerr( err
, exit
);
8447 if( ( DataBuffer_GetLen( inDB
) + recordLen
) > kDNSMaxUDPMessageSize
)
8454 err
= AppendSOARecord( inDB
, me
->domain
, nameLen
, kDNSServiceType_SOA
, kDNSServiceClass_IN
, me
->defaultTTL
,
8455 kRootLabel
, kRootLabel
, me
->serial
, 1 * kSecondsPerDay
, 2 * kSecondsPerHour
, 1000 * kSecondsPerHour
,
8456 me
->defaultTTL
, NULL
);
8457 require_noerr( err
, exit
);
8463 hdr
= (DNSHeader
*) DataBuffer_GetPtr( inDB
);
8464 flags
= DNSHeaderGetFlags( hdr
);
8465 if( notImplemented
)
8467 rcode
= kDNSRCode_NotImplemented
;
8471 flags
|= kDNSHeaderFlag_AuthAnswer
;
8472 if( truncated
) flags
|= kDNSHeaderFlag_Truncation
;
8473 rcode
= nameExists
? kDNSRCode_NoError
: kDNSRCode_NXDomain
;
8475 DNSFlagsSetRCode( flags
, rcode
);
8476 DNSHeaderSetFlags( hdr
, flags
);
8477 DNSHeaderSetAnswerCount( hdr
, answerCount
);
8485 _DNSServerNameIsHostname(
8487 const uint8_t * inName
,
8488 uint32_t * outAliasCount
,
8489 uint32_t inAliasTTLs
[ kMaxAliasTTLCount
],
8490 size_t * outAliasTTLCount
,
8491 unsigned int * outCount
,
8492 unsigned int * outRandCount
,
8495 Boolean
* outHasAAAA
,
8496 Boolean
* outHasSOA
)
8499 const uint8_t * label
;
8500 const uint8_t * nextLabel
;
8501 uint32_t aliasCount
= 0; // Arg from Alias label. Valid values are in [2, 2^31 - 1].
8502 unsigned int count
= 0; // First arg from Count label. Valid values are in [1, 255].
8503 unsigned int randCount
= 0; // Second arg from Count label. Valid values are in [count, 255].
8504 int32_t ttl
= -1; // Arg from TTL label. Valid values are in [0, 2^31 - 1].
8505 size_t aliasTTLCount
= 0; // Count of TTL args from Alias-TTL label.
8506 int hasTagLabel
= false;
8507 int hasIPv4Label
= false;
8508 int hasIPv6Label
= false;
8509 int isNameValid
= false;
8511 for( label
= inName
; label
[ 0 ]; label
= nextLabel
)
8515 nextLabel
= &label
[ 1 + label
[ 0 ] ];
8517 // Check if the first label is a valid alias TTL sequence label.
8519 if( ( label
== inName
) && ( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_AliasTTL
) == 0 ) )
8521 const char * ptr
= (const char *) &label
[ 1 + sizeof_string( kLabelPrefix_AliasTTL
) ];
8522 const char * const end
= (const char *) nextLabel
;
8525 check( label
[ 0 ] <= kDomainLabelLengthMax
);
8529 if( *ptr
!= '-' ) break;
8531 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &next
);
8532 if( err
|| ( arg
> INT32_MAX
) ) break; // TTL must be in [0, 2^31 - 1].
8533 inAliasTTLs
[ aliasTTLCount
++ ] = arg
;
8536 if( ( aliasTTLCount
== 0 ) || ( ptr
!= end
) ) break;
8539 // Check if the first label is a valid alias label.
8541 else if( ( label
== inName
) && ( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_Alias
) == 0 ) )
8543 const char * ptr
= (const char *) &label
[ 1 + sizeof_string( kLabelPrefix_Alias
) ];
8544 const char * const end
= (const char *) nextLabel
;
8548 if( *ptr
++ != '-' ) break;
8549 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8550 if( err
|| ( arg
< 2 ) || ( arg
> INT32_MAX
) ) break; // Alias count must be in [2, 2^31 - 1].
8552 if( ptr
!= end
) break;
8560 // Check if this label is a valid count label.
8562 else if( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_Count
) == 0 )
8564 const char * ptr
= (const char *) &label
[ 1 + sizeof_string( kLabelPrefix_Count
) ];
8565 const char * const end
= (const char *) nextLabel
;
8567 if( count
> 0 ) break; // Count cannot be specified more than once.
8569 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8570 if( err
|| ( arg
< 1 ) || ( arg
> 255 ) ) break; // Count must be in [1, 255].
8571 count
= (unsigned int) arg
;
8575 if( *ptr
++ != '-' ) break;
8576 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8577 if( err
|| ( arg
< (uint32_t) count
) || ( arg
> 255 ) ) break; // Rand count must be in [count, 255].
8578 randCount
= (unsigned int) arg
;
8579 if( ptr
!= end
) break;
8583 // Check if this label is a valid TTL label.
8585 else if( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_TTL
) == 0 )
8587 const char * ptr
= (const char *) &label
[ 1 + sizeof_string( kLabelPrefix_TTL
) ];
8588 const char * const end
= (const char *) nextLabel
;
8590 if( ttl
>= 0 ) break; // TTL cannot be specified more than once.
8592 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8593 if( err
|| ( arg
> INT32_MAX
) ) break; // TTL must be in [0, 2^31 - 1].
8594 ttl
= (int32_t) arg
;
8595 if( ptr
!= end
) break;
8598 // Check if this label is a valid IPv4 label.
8600 else if( strnicmpx( &label
[ 1 ], label
[ 0 ], kLabel_IPv4
) == 0 )
8602 if( hasIPv4Label
|| hasIPv6Label
) break; // Valid names have at most one IPv4 or IPv6 label.
8603 hasIPv4Label
= true;
8606 // Check if this label is a valid IPv6 label.
8608 else if( strnicmpx( &label
[ 1 ], label
[ 0 ], kLabel_IPv6
) == 0 )
8610 if( hasIPv4Label
|| hasIPv6Label
) break; // Valid names have at most one IPv4 or IPv6 label.
8611 hasIPv6Label
= true;
8614 // Check if this label is a valid tag label.
8616 else if( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_Tag
) == 0 )
8621 // If this and the remaining labels are equal to "d.test.", then the name exists. Otherwise, this label is invalid.
8622 // In both cases, there are no more labels to check.
8626 if( DomainNameEqual( label
, me
->domain
) ) isNameValid
= true;
8630 require_quiet( isNameValid
, exit
);
8632 if( outAliasCount
) *outAliasCount
= aliasCount
;
8633 if( outAliasTTLCount
) *outAliasTTLCount
= aliasTTLCount
;
8634 if( outCount
) *outCount
= ( count
> 0 ) ? count
: 1;
8635 if( outRandCount
) *outRandCount
= randCount
;
8636 if( outTTL
) *outTTL
= ( ttl
>= 0 ) ? ( (uint32_t) ttl
) : me
->defaultTTL
;
8637 if( outHasA
) *outHasA
= ( hasIPv4Label
|| !hasIPv6Label
) ? true : false;
8638 if( outHasAAAA
) *outHasAAAA
= ( hasIPv6Label
|| !hasIPv4Label
) ? true : false;
8641 *outHasSOA
= ( !count
&& ( ttl
< 0 ) && !hasIPv4Label
&& !hasIPv6Label
&& !hasTagLabel
) ? true : false;
8645 return( isNameValid
? true : false );
8649 _DNSServerNameIsSRVName(
8651 const uint8_t * inName
,
8652 const uint8_t ** outDomainPtr
,
8653 size_t * outDomainLen
,
8654 ParsedSRV inSRVArray
[ kMaxParsedSRVCount
],
8655 size_t * outSRVCount
)
8658 const uint8_t * label
;
8659 const uint8_t * domainPtr
;
8663 int isNameValid
= false;
8667 // Ensure that first label, i.e, the service label, begins with a '_' character.
8669 require_quiet( ( label
[ 0 ] > 0 ) && ( label
[ 1 ] == '_' ), exit
);
8670 label
= DomainNameGetNextLabel( label
);
8672 // Ensure that the second label, i.e., the proto label, begins with a '_' character (usually _tcp or _udp).
8674 require_quiet( ( label
[ 0 ] > 0 ) && ( label
[ 1 ] == '_' ), exit
);
8675 label
= DomainNameGetNextLabel( label
);
8677 // Parse the domain name, if any.
8682 if( DomainNameEqual( label
, me
->domain
) ||
8683 ( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_SRV
) == 0 ) ) break;
8684 label
= DomainNameGetNextLabel( label
);
8686 require_quiet( *label
, exit
);
8688 domainLen
= (size_t)( label
- domainPtr
);
8690 // Parse SRV labels, if any.
8693 while( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_SRV
) == 0 )
8695 const uint8_t * const nextLabel
= DomainNameGetNextLabel( label
);
8696 const char * ptr
= (const char *) &label
[ 1 + sizeof_string( kLabelPrefix_SRV
) ];
8697 const char * const end
= (const char *) nextLabel
;
8698 const uint8_t * target
;
8699 unsigned int priority
, weight
, port
;
8701 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8702 require_quiet( !err
&& ( arg
<= UINT16_MAX
), exit
);
8703 priority
= (unsigned int) arg
;
8705 require_quiet( ( ptr
< end
) && ( *ptr
== '-' ), exit
);
8708 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8709 require_quiet( !err
&& ( arg
<= UINT16_MAX
), exit
);
8710 weight
= (unsigned int) arg
;
8712 require_quiet( ( ptr
< end
) && ( *ptr
== '-' ), exit
);
8715 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8716 require_quiet( !err
&& ( arg
<= UINT16_MAX
), exit
);
8717 port
= (unsigned int) arg
;
8719 require_quiet( ptr
== end
, exit
);
8722 for( label
= nextLabel
; *label
; label
= DomainNameGetNextLabel( label
) )
8724 if( DomainNameEqual( label
, me
->domain
) ||
8725 ( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_SRV
) == 0 ) ) break;
8727 require_quiet( *label
, exit
);
8731 inSRVArray
[ srvCount
].priority
= (uint16_t) priority
;
8732 inSRVArray
[ srvCount
].weight
= (uint16_t) weight
;
8733 inSRVArray
[ srvCount
].port
= (uint16_t) port
;
8734 inSRVArray
[ srvCount
].targetPtr
= target
;
8735 inSRVArray
[ srvCount
].targetLen
= (uint16_t)( label
- target
);
8739 require_quiet( DomainNameEqual( label
, me
->domain
), exit
);
8742 if( outDomainPtr
) *outDomainPtr
= domainPtr
;
8743 if( outDomainLen
) *outDomainLen
= domainLen
;
8744 if( outSRVCount
) *outSRVCount
= srvCount
;
8747 return( isNameValid
? true : false );
8750 //===========================================================================================================================
8751 // _DNSServerTCPReadHandler
8752 //===========================================================================================================================
8756 DNSServerRef server
; // Reference to DNS server object.
8757 sockaddr_ip clientAddr
; // Client's address.
8758 dispatch_source_t readSource
; // Dispatch read source for client socket.
8759 dispatch_source_t writeSource
; // Dispatch write source for client socket.
8760 size_t offset
; // Offset into receive buffer.
8761 void * msgPtr
; // Pointer to dynamically allocated message buffer.
8762 size_t msgLen
; // Length of message buffer.
8763 Boolean readSuspended
; // True if the read source is currently suspended.
8764 Boolean writeSuspended
; // True if the write source is currently suspended.
8765 Boolean receivedLength
; // True if receiving DNS message as opposed to the message length.
8766 uint8_t lenBuf
[ 2 ]; // Buffer for two-octet message length field.
8767 iovec_t iov
[ 2 ]; // IO vector for writing response message.
8768 iovec_t
* iovPtr
; // Vector pointer for SocketWriteData().
8769 int iovCount
; // Vector count for SocketWriteData().
8771 } TCPConnectionContext
;
8773 static void TCPConnectionStop( TCPConnectionContext
*inContext
);
8774 static void TCPConnectionContextFree( TCPConnectionContext
*inContext
);
8775 static void TCPConnectionReadHandler( void *inContext
);
8776 static void TCPConnectionWriteHandler( void *inContext
);
8778 #define TCPConnectionForget( X ) ForgetCustomEx( X, TCPConnectionStop, TCPConnectionContextFree )
8780 static void _DNSServerTCPReadHandler( void *inContext
)
8783 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
8784 DNSServerRef
const me
= (DNSServerRef
) sockCtx
->userContext
;
8785 TCPConnectionContext
* connection
;
8786 socklen_t clientAddrLen
;
8787 SocketRef newSock
= kInvalidSocketRef
;
8788 SocketContext
* newSockCtx
= NULL
;
8790 connection
= (TCPConnectionContext
*) calloc( 1, sizeof( *connection
) );
8791 require_action( connection
, exit
, err
= kNoMemoryErr
);
8794 connection
->server
= me
;
8796 clientAddrLen
= (socklen_t
) sizeof( connection
->clientAddr
);
8797 newSock
= accept( sockCtx
->sock
, &connection
->clientAddr
.sa
, &clientAddrLen
);
8798 err
= map_socket_creation_errno( newSock
);
8799 require_noerr( err
, exit
);
8801 err
= SocketContextCreate( newSock
, connection
, &newSockCtx
);
8802 require_noerr( err
, exit
);
8803 newSock
= kInvalidSocketRef
;
8805 err
= DispatchReadSourceCreate( newSockCtx
->sock
, me
->queue
, TCPConnectionReadHandler
, SocketContextCancelHandler
,
8806 newSockCtx
, &connection
->readSource
);
8807 require_noerr( err
, exit
);
8808 SocketContextRetain( newSockCtx
);
8809 dispatch_resume( connection
->readSource
);
8811 err
= DispatchWriteSourceCreate( newSockCtx
->sock
, me
->queue
, TCPConnectionWriteHandler
, SocketContextCancelHandler
,
8812 newSockCtx
, &connection
->writeSource
);
8813 require_noerr( err
, exit
);
8814 SocketContextRetain( newSockCtx
);
8815 connection
->writeSuspended
= true;
8819 ForgetSocket( &newSock
);
8820 SocketContextRelease( newSockCtx
);
8821 TCPConnectionForget( &connection
);
8824 //===========================================================================================================================
8825 // TCPConnectionStop
8826 //===========================================================================================================================
8828 static void TCPConnectionStop( TCPConnectionContext
*inContext
)
8830 dispatch_source_forget_ex( &inContext
->readSource
, &inContext
->readSuspended
);
8831 dispatch_source_forget_ex( &inContext
->writeSource
, &inContext
->writeSuspended
);
8834 //===========================================================================================================================
8835 // TCPConnectionContextFree
8836 //===========================================================================================================================
8838 static void TCPConnectionContextFree( TCPConnectionContext
*inContext
)
8840 check( !inContext
->readSource
);
8841 check( !inContext
->writeSource
);
8842 ForgetCF( &inContext
->server
);
8843 ForgetMem( &inContext
->msgPtr
);
8847 //===========================================================================================================================
8848 // TCPConnectionReadHandler
8849 //===========================================================================================================================
8851 static void TCPConnectionReadHandler( void *inContext
)
8854 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
8855 TCPConnectionContext
* connection
= (TCPConnectionContext
*) sockCtx
->userContext
;
8857 uint8_t * responsePtr
= NULL
; // malloc'd
8860 // Receive message length.
8862 if( !connection
->receivedLength
)
8864 err
= SocketReadData( sockCtx
->sock
, connection
->lenBuf
, sizeof( connection
->lenBuf
), &connection
->offset
);
8865 if( err
== EWOULDBLOCK
) goto exit
;
8866 require_noerr( err
, exit
);
8868 connection
->offset
= 0;
8869 connection
->msgLen
= ReadBig16( connection
->lenBuf
);
8870 connection
->msgPtr
= malloc( connection
->msgLen
);
8871 require_action( connection
->msgPtr
, exit
, err
= kNoMemoryErr
);
8872 connection
->receivedLength
= true;
8877 err
= SocketReadData( sockCtx
->sock
, connection
->msgPtr
, connection
->msgLen
, &connection
->offset
);
8878 if( err
== EWOULDBLOCK
) goto exit
;
8879 require_noerr( err
, exit
);
8881 gettimeofday( &now
, NULL
);
8882 dispatch_suspend( connection
->readSource
);
8883 connection
->readSuspended
= true;
8885 ds_ulog( kLogLevelInfo
, "TCP server received %zu bytes from %##a at %{du:time}.\n",
8886 connection
->msgLen
, &connection
->clientAddr
, &now
);
8888 if( connection
->msgLen
< kDNSHeaderLength
)
8890 ds_ulog( kLogLevelInfo
, "TCP DNS message is too small (%zu < %d).\n", connection
->msgLen
, kDNSHeaderLength
);
8894 ds_ulog( kLogLevelInfo
, "TCP received message:\n\n%1{du:dnsmsg}", connection
->msgPtr
, connection
->msgLen
);
8898 err
= _DNSServerAnswerQueryForTCP( connection
->server
, connection
->msgPtr
, connection
->msgLen
, &responsePtr
,
8900 require_noerr_quiet( err
, exit
);
8904 ds_ulog( kLogLevelInfo
, "TCP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen
, responsePtr
, responseLen
);
8906 free( connection
->msgPtr
);
8907 connection
->msgPtr
= responsePtr
;
8908 connection
->msgLen
= responseLen
;
8911 check( connection
->msgLen
<= UINT16_MAX
);
8912 WriteBig16( connection
->lenBuf
, connection
->msgLen
);
8913 connection
->iov
[ 0 ].iov_base
= connection
->lenBuf
;
8914 connection
->iov
[ 0 ].iov_len
= sizeof( connection
->lenBuf
);
8915 connection
->iov
[ 1 ].iov_base
= connection
->msgPtr
;
8916 connection
->iov
[ 1 ].iov_len
= connection
->msgLen
;
8918 connection
->iovPtr
= connection
->iov
;
8919 connection
->iovCount
= 2;
8921 check( connection
->writeSuspended
);
8922 dispatch_resume( connection
->writeSource
);
8923 connection
->writeSuspended
= false;
8926 FreeNullSafe( responsePtr
);
8927 if( err
&& ( err
!= EWOULDBLOCK
) ) TCPConnectionForget( &connection
);
8930 //===========================================================================================================================
8931 // TCPConnectionWriteHandler
8932 //===========================================================================================================================
8934 static void TCPConnectionWriteHandler( void *inContext
)
8937 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
8938 TCPConnectionContext
* connection
= (TCPConnectionContext
*) sockCtx
->userContext
;
8940 err
= SocketWriteData( sockCtx
->sock
, &connection
->iovPtr
, &connection
->iovCount
);
8941 if( err
== EWOULDBLOCK
) goto exit
;
8944 TCPConnectionForget( &connection
);
8950 //===========================================================================================================================
8952 //===========================================================================================================================
8956 uint8_t * hostname
; // Used as the base name for hostnames and service names.
8957 uint8_t * serviceLabel
; // Label containing the base service name.
8958 unsigned int maxInstanceCount
; // Maximum number of service instances and hostnames.
8959 uint64_t * bitmaps
; // Array of 64-bit bitmaps for keeping track of needed responses.
8960 size_t bitmapCount
; // Number of 64-bit bitmaps.
8961 dispatch_source_t readSourceV4
; // Read dispatch source for IPv4 socket.
8962 dispatch_source_t readSourceV6
; // Read dispatch source for IPv6 socket.
8963 uint32_t ifIndex
; // Index of the interface to run on.
8964 unsigned int recordCountA
; // Number of A records per hostname.
8965 unsigned int recordCountAAAA
; // Number of AAAA records per hostname.
8966 unsigned int maxDropCount
; // If > 0, the drop rates apply to only the first <maxDropCount> responses.
8967 double ucastDropRate
; // Probability of dropping a unicast response.
8968 double mcastDropRate
; // Probability of dropping a multicast query or response.
8969 uint8_t * dropCounters
; // If maxDropCount > 0, array of <maxInstanceCount> response drop counters.
8970 Boolean noAdditionals
; // True if responses are to not include additional records.
8971 Boolean useIPv4
; // True if the replier is to use IPv4.
8972 Boolean useIPv6
; // True if the replier is to use IPv6.
8973 uint8_t msgBuf
[ kMDNSMessageSizeMax
]; // Buffer for received mDNS message.
8974 #if( TARGET_OS_DARWIN )
8975 dispatch_source_t processMonitor
; // Process monitor source for process being followed, if any.
8976 pid_t followPID
; // PID of process being followed, if any. (If it exits, we exit).
8979 } MDNSReplierContext
;
8981 typedef struct MRResourceRecord MRResourceRecord
;
8982 struct MRResourceRecord
8984 MRResourceRecord
* next
; // Next item in list.
8985 uint8_t * name
; // Resource record name.
8986 uint16_t type
; // Resource record type.
8987 uint16_t class; // Resource record class.
8988 uint32_t ttl
; // Resource record TTL.
8989 uint16_t rdlength
; // Resource record data length.
8990 uint8_t * rdata
; // Resource record data.
8991 const uint8_t * target
; // For SRV records, pointer to target in RDATA.
8994 typedef struct MRNameOffsetItem MRNameOffsetItem
;
8995 struct MRNameOffsetItem
8997 MRNameOffsetItem
* next
; // Next item in list.
8998 uint16_t offset
; // Offset of domain name in response message.
8999 uint8_t name
[ 1 ]; // Variable-length array for domain name.
9002 #if( TARGET_OS_DARWIN )
9003 static void _MDNSReplierFollowedProcessHandler( void *inContext
);
9005 static void _MDNSReplierReadHandler( void *inContext
);
9007 _MDNSReplierAnswerQuery(
9008 MDNSReplierContext
* inContext
,
9009 const uint8_t * inQueryPtr
,
9011 sockaddr_ip
* inSender
,
9013 unsigned int inIndex
);
9015 _MDNSReplierAnswerListAdd(
9016 MDNSReplierContext
* inContext
,
9017 MRResourceRecord
** inAnswerList
,
9018 unsigned int inIndex
,
9019 const uint8_t * inName
,
9020 unsigned int inType
,
9021 unsigned int inClass
);
9023 _MDNSReplierAnswerListRemovePTR(
9024 MRResourceRecord
** inAnswerListPtr
,
9025 const uint8_t * inName
,
9026 const uint8_t * inRData
);
9028 _MDNSReplierSendOrDropResponse(
9029 MDNSReplierContext
* inContext
,
9030 MRResourceRecord
* inAnswerList
,
9031 sockaddr_ip
* inQuerier
,
9033 unsigned int inIndex
,
9034 Boolean inUnicast
);
9036 _MDNSReplierCreateResponse(
9037 MDNSReplierContext
* inContext
,
9038 MRResourceRecord
* inAnswerList
,
9039 unsigned int inIndex
,
9040 uint8_t ** outResponsePtr
,
9041 size_t * outResponseLen
);
9043 _MDNSReplierAppendNameToResponse(
9044 DataBuffer
* inResponse
,
9045 const uint8_t * inName
,
9046 MRNameOffsetItem
** inNameOffsetListPtr
);
9048 _MDNSReplierServiceTypeMatch(
9049 const MDNSReplierContext
* inContext
,
9050 const uint8_t * inName
,
9051 unsigned int * outTXTSize
,
9052 unsigned int * outCount
);
9054 _MDNSReplierServiceInstanceNameMatch(
9055 const MDNSReplierContext
* inContext
,
9056 const uint8_t * inName
,
9057 unsigned int * outIndex
,
9058 unsigned int * outTXTSize
,
9059 unsigned int * outCount
);
9060 static Boolean
_MDNSReplierAboutRecordNameMatch( const MDNSReplierContext
*inContext
, const uint8_t *inName
);
9062 _MDNSReplierHostnameMatch(
9063 const MDNSReplierContext
* inContext
,
9064 const uint8_t * inName
,
9065 unsigned int * outIndex
);
9066 static OSStatus
_MDNSReplierCreateTXTRecord( const uint8_t *inRecordName
, size_t inSize
, uint8_t **outTXT
);
9068 _MRResourceRecordCreate(
9073 uint16_t inRDLength
,
9075 MRResourceRecord
** outRecord
);
9076 static void _MRResourceRecordFree( MRResourceRecord
*inRecord
);
9077 static void _MRResourceRecordFreeList( MRResourceRecord
*inList
);
9078 static OSStatus
_MRNameOffsetItemCreate( const uint8_t *inName
, uint16_t inOffset
, MRNameOffsetItem
**outItem
);
9079 static void _MRNameOffsetItemFree( MRNameOffsetItem
*inItem
);
9080 static void _MRNameOffsetItemFreeList( MRNameOffsetItem
*inList
);
9082 ulog_define_ex( kDNSSDUtilIdentifier
, MDNSReplier
, kLogLevelInfo
, kLogFlags_None
, "MDNSReplier", NULL
);
9083 #define mr_ulog( LEVEL, ... ) ulog( &log_category_from_name( MDNSReplier ), (LEVEL), __VA_ARGS__ )
9085 static void MDNSReplierCmd( void )
9088 MDNSReplierContext
* context
;
9089 SocketRef sockV4
= kInvalidSocketRef
;
9090 SocketRef sockV6
= kInvalidSocketRef
;
9091 const char * ifname
;
9093 uint8_t name
[ 1 + kDomainLabelLengthMax
+ 1 ];
9094 char ifnameBuf
[ IF_NAMESIZE
+ 1 ];
9096 err
= CheckIntegerArgument( gMDNSReplier_MaxInstanceCount
, "max instance count", 1, UINT16_MAX
);
9097 require_noerr_quiet( err
, exit
);
9099 err
= CheckIntegerArgument( gMDNSReplier_RecordCountA
, "A record count", 0, 255 );
9100 require_noerr_quiet( err
, exit
);
9102 err
= CheckIntegerArgument( gMDNSReplier_RecordCountAAAA
, "AAAA record count", 0, 255 );
9103 require_noerr_quiet( err
, exit
);
9105 err
= CheckDoubleArgument( gMDNSReplier_UnicastDropRate
, "unicast drop rate", 0.0, 1.0 );
9106 require_noerr_quiet( err
, exit
);
9108 err
= CheckDoubleArgument( gMDNSReplier_MulticastDropRate
, "multicast drop rate", 0.0, 1.0 );
9109 require_noerr_quiet( err
, exit
);
9111 err
= CheckIntegerArgument( gMDNSReplier_MaxDropCount
, "drop count", 0, 255 );
9112 require_noerr_quiet( err
, exit
);
9114 if( gMDNSReplier_Foreground
)
9116 LogControl( "MDNSReplier:output=file;stdout,MDNSReplier:flags=time;prefix" );
9119 context
= (MDNSReplierContext
*) calloc( 1, sizeof( *context
) );
9120 require_action( context
, exit
, err
= kNoMemoryErr
);
9122 context
->maxInstanceCount
= (unsigned int) gMDNSReplier_MaxInstanceCount
;
9123 context
->recordCountA
= (unsigned int) gMDNSReplier_RecordCountA
;
9124 context
->recordCountAAAA
= (unsigned int) gMDNSReplier_RecordCountAAAA
;
9125 context
->maxDropCount
= (unsigned int) gMDNSReplier_MaxDropCount
;
9126 context
->ucastDropRate
= gMDNSReplier_UnicastDropRate
;
9127 context
->mcastDropRate
= gMDNSReplier_MulticastDropRate
;
9128 context
->noAdditionals
= gMDNSReplier_NoAdditionals
? true : false;
9129 context
->useIPv4
= ( gMDNSReplier_UseIPv4
|| !gMDNSReplier_UseIPv6
) ? true : false;
9130 context
->useIPv6
= ( gMDNSReplier_UseIPv6
|| !gMDNSReplier_UseIPv4
) ? true : false;
9131 context
->bitmapCount
= ( context
->maxInstanceCount
+ 63 ) / 64;
9133 #if( TARGET_OS_DARWIN )
9134 if( gMDNSReplier_FollowPID
)
9136 context
->followPID
= _StringToPID( gMDNSReplier_FollowPID
, &err
);
9137 if( err
|| ( context
->followPID
< 0 ) )
9139 FPrintF( stderr
, "error: Invalid follow PID: %s\n", gMDNSReplier_FollowPID
);
9143 err
= DispatchProcessMonitorCreate( context
->followPID
, DISPATCH_PROC_EXIT
, dispatch_get_main_queue(),
9144 _MDNSReplierFollowedProcessHandler
, NULL
, context
, &context
->processMonitor
);
9145 require_noerr( err
, exit
);
9146 dispatch_resume( context
->processMonitor
);
9150 context
->followPID
= -1;
9154 if( context
->maxDropCount
> 0 )
9156 context
->dropCounters
= (uint8_t *) calloc( context
->maxInstanceCount
, sizeof( *context
->dropCounters
) );
9157 require_action( context
->dropCounters
, exit
, err
= kNoMemoryErr
);
9160 context
->bitmaps
= (uint64_t *) calloc( context
->bitmapCount
, sizeof( *context
->bitmaps
) );
9161 require_action( context
->bitmaps
, exit
, err
= kNoMemoryErr
);
9163 // Create the base hostname label.
9165 len
= strlen( gMDNSReplier_Hostname
);
9166 if( context
->maxInstanceCount
> 1 )
9168 unsigned int maxInstanceCount
, digitCount
;
9170 // When there's more than one instance, extra bytes are needed to append " (<instance index>)" or
9171 // "-<instance index>" to the base hostname.
9173 maxInstanceCount
= context
->maxInstanceCount
;
9174 for( digitCount
= 0; maxInstanceCount
> 0; ++digitCount
) maxInstanceCount
/= 10;
9175 len
+= ( 3 + digitCount
);
9178 if( len
<= kDomainLabelLengthMax
)
9180 uint8_t * dst
= &name
[ 1 ];
9181 uint8_t * lim
= &name
[ countof( name
) ];
9183 SNPrintF_Add( (char **) &dst
, (char *) lim
, "%s", gMDNSReplier_Hostname
);
9184 name
[ 0 ] = (uint8_t)( dst
- &name
[ 1 ] );
9186 err
= DomainNameDupLower( name
, &context
->hostname
, NULL
);
9187 require_noerr( err
, exit
);
9191 FPrintF( stderr
, "error: Base name \"%s\" is too long for max instance count of %u.\n",
9192 gMDNSReplier_Hostname
, context
->maxInstanceCount
);
9196 // Create the service label.
9198 len
= strlen( gMDNSReplier_ServiceTypeTag
) + 3; // We need three extra bytes for the service type prefix "_t-".
9199 if( len
<= kDomainLabelLengthMax
)
9201 uint8_t * dst
= &name
[ 1 ];
9202 uint8_t * lim
= &name
[ countof( name
) ];
9204 SNPrintF_Add( (char **) &dst
, (char *) lim
, "_t-%s", gMDNSReplier_ServiceTypeTag
);
9205 name
[ 0 ] = (uint8_t)( dst
- &name
[ 1 ] );
9207 err
= DomainNameDupLower( name
, &context
->serviceLabel
, NULL
);
9208 require_noerr( err
, exit
);
9212 FPrintF( stderr
, "error: Service type tag is too long.\n" );
9216 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
9217 require_noerr_quiet( err
, exit
);
9219 ifname
= if_indextoname( context
->ifIndex
, ifnameBuf
);
9220 require_action( ifname
, exit
, err
= kNameErr
);
9222 // Set up IPv4 socket.
9224 if( context
->useIPv4
)
9226 err
= CreateMulticastSocket( GetMDNSMulticastAddrV4(), kMDNSPort
, ifname
, context
->ifIndex
, true, NULL
, &sockV4
);
9227 require_noerr( err
, exit
);
9230 // Set up IPv6 socket.
9232 if( context
->useIPv6
)
9234 err
= CreateMulticastSocket( GetMDNSMulticastAddrV6(), kMDNSPort
, ifname
, context
->ifIndex
, true, NULL
, &sockV6
);
9235 require_noerr( err
, exit
);
9238 // Create dispatch read sources for socket(s).
9240 if( IsValidSocket( sockV4
) )
9242 SocketContext
* sockCtx
;
9244 err
= SocketContextCreate( sockV4
, context
, &sockCtx
);
9245 require_noerr( err
, exit
);
9246 sockV4
= kInvalidSocketRef
;
9248 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, _MDNSReplierReadHandler
, SocketContextCancelHandler
, sockCtx
,
9249 &context
->readSourceV4
);
9250 if( err
) ForgetSocketContext( &sockCtx
);
9251 require_noerr( err
, exit
);
9253 dispatch_resume( context
->readSourceV4
);
9256 if( IsValidSocket( sockV6
) )
9258 SocketContext
* sockCtx
;
9260 err
= SocketContextCreate( sockV6
, context
, &sockCtx
);
9261 require_noerr( err
, exit
);
9262 sockV6
= kInvalidSocketRef
;
9264 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, _MDNSReplierReadHandler
, SocketContextCancelHandler
, sockCtx
,
9265 &context
->readSourceV6
);
9266 if( err
) ForgetSocketContext( &sockCtx
);
9267 require_noerr( err
, exit
);
9269 dispatch_resume( context
->readSourceV6
);
9275 ForgetSocket( &sockV4
);
9276 ForgetSocket( &sockV6
);
9280 #if( TARGET_OS_DARWIN )
9281 //===========================================================================================================================
9282 // _MDNSReplierFollowedProcessHandler
9283 //===========================================================================================================================
9285 static void _MDNSReplierFollowedProcessHandler( void *inContext
)
9287 MDNSReplierContext
* const context
= (MDNSReplierContext
*) inContext
;
9289 if( dispatch_source_get_data( context
->processMonitor
) & DISPATCH_PROC_EXIT
)
9291 mr_ulog( kLogLevelNotice
, "Exiting: followed process (%lld) exited.\n", (int64_t) context
->followPID
);
9297 //===========================================================================================================================
9298 // _MDNSReplierReadHandler
9299 //===========================================================================================================================
9301 #define ShouldDrop( P ) ( ( (P) > 0.0 ) && ( ( (P) >= 1.0 ) || RandomlyTrue( P ) ) )
9303 static void _MDNSReplierReadHandler( void *inContext
)
9306 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
9307 MDNSReplierContext
* const context
= (MDNSReplierContext
*) sockCtx
->userContext
;
9310 const DNSHeader
* hdr
;
9311 unsigned int flags
, questionCount
, i
, j
;
9312 const uint8_t * ptr
;
9313 int drop
, isMetaQuery
;
9315 err
= SocketRecvFrom( sockCtx
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &msgLen
, &sender
, sizeof( sender
),
9316 NULL
, NULL
, NULL
, NULL
);
9317 require_noerr( err
, exit
);
9319 if( msgLen
< kDNSHeaderLength
)
9321 mr_ulog( kLogLevelInfo
, "Message is too small (%zu < %d).\n", msgLen
, kDNSHeaderLength
);
9325 // Perform header field checks.
9326 // The message ID and most flag bits are silently ignored (see <https://tools.ietf.org/html/rfc6762#section-18>).
9328 hdr
= (DNSHeader
*) context
->msgBuf
;
9329 flags
= DNSHeaderGetFlags( hdr
);
9330 require_quiet( ( flags
& kDNSHeaderFlag_Response
) == 0, exit
); // Reject responses.
9331 require_quiet( DNSFlagsGetOpCode( flags
) == kDNSOpCode_Query
, exit
); // Reject opcodes other than standard query.
9332 require_quiet( DNSFlagsGetRCode( flags
) == kDNSRCode_NoError
, exit
); // Reject non-zero rcodes.
9334 drop
= ( !context
->maxDropCount
&& ShouldDrop( context
->mcastDropRate
) ) ? true : false;
9336 mr_ulog( kLogLevelInfo
, "Received %zu byte message from %##a%?s:\n\n%#1{du:dnsmsg}",
9337 msgLen
, &sender
, drop
, " (dropping)", context
->msgBuf
, msgLen
);
9339 // Based on the QNAMEs in the query message, determine from which sets of records we may possibly need answers.
9341 questionCount
= DNSHeaderGetQuestionCount( hdr
);
9342 require_quiet( questionCount
> 0, exit
);
9344 memset( context
->bitmaps
, 0, context
->bitmapCount
* sizeof_element( context
->bitmaps
) );
9346 isMetaQuery
= false;
9347 ptr
= (const uint8_t *) &hdr
[ 1 ];
9348 for( i
= 0; i
< questionCount
; ++i
)
9350 unsigned int count
, index
;
9351 uint16_t qtype
, qclass
;
9352 uint8_t qname
[ kDomainNameLengthMax
];
9354 err
= DNSMessageExtractQuestion( context
->msgBuf
, msgLen
, ptr
, qname
, &qtype
, &qclass
, &ptr
);
9355 require_noerr_quiet( err
, exit
);
9357 if( ( qclass
& ~kQClassUnicastResponseBit
) != kDNSServiceClass_IN
) continue;
9359 if( _MDNSReplierHostnameMatch( context
, qname
, &index
) ||
9360 _MDNSReplierServiceInstanceNameMatch( context
, qname
, &index
, NULL
, NULL
) )
9362 if( ( index
>= 1 ) && ( index
<= context
->maxInstanceCount
) )
9364 context
->bitmaps
[ ( index
- 1 ) / 64 ] |= ( UINT64_C( 1 ) << ( ( index
- 1 ) % 64 ) );
9367 else if( _MDNSReplierServiceTypeMatch( context
, qname
, NULL
, &count
) )
9369 if( ( count
>= 1 ) && ( count
<= context
->maxInstanceCount
) )
9371 for( j
= 0; j
< (unsigned int) context
->bitmapCount
; ++j
)
9375 context
->bitmaps
[ j
] |= ( ( UINT64_C( 1 ) << count
) - 1 );
9380 context
->bitmaps
[ j
] = ~UINT64_C( 0 );
9386 else if( _MDNSReplierAboutRecordNameMatch( context
, qname
) )
9392 // Attempt to answer the query message using selected record sets.
9396 err
= _MDNSReplierAnswerQuery( context
, context
->msgBuf
, msgLen
, &sender
, sockCtx
->sock
, 0 );
9399 if( drop
) goto exit
;
9401 for( i
= 0; i
< context
->bitmapCount
; ++i
)
9403 for( j
= 0; ( context
->bitmaps
[ i
] != 0 ) && ( j
< 64 ); ++j
)
9405 const uint64_t bitmask
= UINT64_C( 1 ) << j
;
9407 if( context
->bitmaps
[ i
] & bitmask
)
9409 context
->bitmaps
[ i
] &= ~bitmask
;
9411 err
= _MDNSReplierAnswerQuery( context
, context
->msgBuf
, msgLen
, &sender
, sockCtx
->sock
,
9412 ( i
* 64 ) + j
+ 1 );
9422 //===========================================================================================================================
9423 // _MDNSReplierAnswerQuery
9424 //===========================================================================================================================
9427 _MDNSReplierAnswerQuery(
9428 MDNSReplierContext
* inContext
,
9429 const uint8_t * inQueryPtr
,
9431 sockaddr_ip
* inSender
,
9433 unsigned int inIndex
)
9436 const DNSHeader
* hdr
;
9437 const uint8_t * ptr
;
9438 unsigned int questionCount
, answerCount
, i
;
9439 MRResourceRecord
* ucastAnswerList
= NULL
;
9440 MRResourceRecord
* mcastAnswerList
= NULL
;
9442 require_action( inIndex
<= inContext
->maxInstanceCount
, exit
, err
= kRangeErr
);
9444 // Get answers for questions.
9446 check( inQueryLen
>= kDNSHeaderLength
);
9447 hdr
= (const DNSHeader
*) inQueryPtr
;
9448 questionCount
= DNSHeaderGetQuestionCount( hdr
);
9450 ptr
= (const uint8_t *) &hdr
[ 1 ];
9451 for( i
= 0; i
< questionCount
; ++i
)
9453 MRResourceRecord
** answerListPtr
;
9454 uint16_t qtype
, qclass
;
9455 uint8_t qname
[ kDomainNameLengthMax
];
9457 err
= DNSMessageExtractQuestion( inQueryPtr
, inQueryLen
, ptr
, qname
, &qtype
, &qclass
, &ptr
);
9458 require_noerr_quiet( err
, exit
);
9460 if( qclass
& kQClassUnicastResponseBit
)
9462 qclass
&= ~kQClassUnicastResponseBit
;
9463 answerListPtr
= &ucastAnswerList
;
9467 answerListPtr
= &mcastAnswerList
;
9470 err
= _MDNSReplierAnswerListAdd( inContext
, answerListPtr
, inIndex
, qname
, qtype
, qclass
);
9471 require_noerr( err
, exit
);
9473 require_action_quiet( mcastAnswerList
|| ucastAnswerList
, exit
, err
= kNoErr
);
9475 // Suppress known answers.
9476 // Records in the Answer section of the query message are known answers, so remove them from the answer lists.
9477 // See <https://tools.ietf.org/html/rfc6762#section-7.1>.
9479 answerCount
= DNSHeaderGetAnswerCount( hdr
);
9480 for( i
= 0; i
< answerCount
; ++i
)
9482 const uint8_t * rdataPtr
;
9483 const uint8_t * recordPtr
;
9484 uint16_t type
, class;
9485 uint8_t name
[ kDomainNameLengthMax
];
9486 uint8_t instance
[ kDomainNameLengthMax
];
9489 err
= DNSMessageExtractRecord( inQueryPtr
, inQueryLen
, ptr
, NULL
, &type
, &class, NULL
, NULL
, NULL
, &ptr
);
9490 require_noerr_quiet( err
, exit
);
9492 if( ( type
!= kDNSServiceType_PTR
) || ( class != kDNSServiceClass_IN
) ) continue;
9494 err
= DNSMessageExtractRecord( inQueryPtr
, inQueryLen
, recordPtr
, name
, NULL
, NULL
, NULL
, &rdataPtr
, NULL
, NULL
);
9495 require_noerr( err
, exit
);
9497 err
= DNSMessageExtractDomainName( inQueryPtr
, inQueryLen
, rdataPtr
, instance
, NULL
);
9498 require_noerr_quiet( err
, exit
);
9500 if( ucastAnswerList
) _MDNSReplierAnswerListRemovePTR( &ucastAnswerList
, name
, instance
);
9501 if( mcastAnswerList
) _MDNSReplierAnswerListRemovePTR( &mcastAnswerList
, name
, instance
);
9503 require_action_quiet( mcastAnswerList
|| ucastAnswerList
, exit
, err
= kNoErr
);
9505 // Send or drop responses.
9507 if( ucastAnswerList
)
9509 err
= _MDNSReplierSendOrDropResponse( inContext
, ucastAnswerList
, inSender
, inSock
, inIndex
, true );
9510 require_noerr( err
, exit
);
9513 if( mcastAnswerList
)
9515 err
= _MDNSReplierSendOrDropResponse( inContext
, mcastAnswerList
, inSender
, inSock
, inIndex
, false );
9516 require_noerr( err
, exit
);
9521 _MRResourceRecordFreeList( ucastAnswerList
);
9522 _MRResourceRecordFreeList( mcastAnswerList
);
9526 //===========================================================================================================================
9527 // _MDNSReplierAnswerListAdd
9528 //===========================================================================================================================
9531 _MDNSReplierAnswerListAdd(
9532 MDNSReplierContext
* inContext
,
9533 MRResourceRecord
** inAnswerList
,
9534 unsigned int inIndex
,
9535 const uint8_t * inName
,
9536 unsigned int inType
,
9537 unsigned int inClass
)
9540 uint8_t * recordName
= NULL
;
9541 uint8_t * rdataPtr
= NULL
;
9543 MRResourceRecord
* answer
;
9544 MRResourceRecord
** answerPtr
;
9545 const uint8_t * const hostname
= inContext
->hostname
;
9548 unsigned int count
, txtSize
;
9550 require_action( inIndex
<= inContext
->maxInstanceCount
, exit
, err
= kRangeErr
);
9551 require_action_quiet( inClass
== kDNSServiceClass_IN
, exit
, err
= kNoErr
);
9553 for( answerPtr
= inAnswerList
; ( answer
= *answerPtr
) != NULL
; answerPtr
= &answer
->next
)
9555 if( ( answer
->type
== inType
) && DomainNameEqual( answer
->name
, inName
) )
9562 // Index 0 is reserved for answering queries about the mdnsreplier, while all other index values up to the maximum
9563 // instance count are for answering queries about service instances.
9567 if( _MDNSReplierAboutRecordNameMatch( inContext
, inName
) )
9569 int listHasTXT
= false;
9571 if( inType
== kDNSServiceType_ANY
)
9573 for( answer
= *inAnswerList
; answer
; answer
= answer
->next
)
9575 if( ( answer
->type
== kDNSServiceType_TXT
) && DomainNameEqual( answer
->name
, inName
) )
9583 if( ( inType
== kDNSServiceType_TXT
) || ( ( inType
== kDNSServiceType_ANY
) && !listHasTXT
) )
9585 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9586 require_noerr( err
, exit
);
9588 err
= CreateTXTRecordDataFromString( "ready=yes", ',', &rdataPtr
, &rdataLen
);
9589 require_noerr( err
, exit
);
9591 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_TXT
, kDNSServiceClass_IN
, kMDNSRecordTTL_Other
,
9592 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9593 require_noerr( err
, exit
);
9597 *answerPtr
= answer
;
9599 else if( inType
== kDNSServiceType_NSEC
)
9601 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9602 require_noerr( err
, exit
);
9604 err
= CreateNSECRecordData( recordName
, &rdataPtr
, &rdataLen
, 1, kDNSServiceType_TXT
);
9605 require_noerr( err
, exit
);
9607 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_NSEC
, kDNSServiceClass_IN
, kMDNSRecordTTL_Host
,
9608 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9609 require_noerr( err
, exit
);
9613 *answerPtr
= answer
;
9617 else if( _MDNSReplierHostnameMatch( inContext
, inName
, &index
) && ( index
== inIndex
) )
9619 int listHasA
= false;
9620 int listHasAAAA
= false;
9622 if( inType
== kDNSServiceType_ANY
)
9624 for( answer
= *inAnswerList
; answer
; answer
= answer
->next
)
9626 if( answer
->type
== kDNSServiceType_A
)
9628 if( !listHasA
&& DomainNameEqual( answer
->name
, inName
) ) listHasA
= true;
9630 else if( answer
->type
== kDNSServiceType_AAAA
)
9632 if( !listHasAAAA
&& DomainNameEqual( answer
->name
, inName
) ) listHasAAAA
= true;
9634 if( listHasA
&& listHasAAAA
) break;
9638 if( ( inType
== kDNSServiceType_A
) || ( ( inType
== kDNSServiceType_ANY
) && !listHasA
) )
9640 for( i
= 1; i
<= inContext
->recordCountA
; ++i
)
9642 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9643 require_noerr( err
, exit
);
9646 rdataPtr
= (uint8_t *) malloc( rdataLen
);
9647 require_action( rdataPtr
, exit
, err
= kNoMemoryErr
);
9650 WriteBig16( &rdataPtr
[ 1 ], inIndex
);
9651 rdataPtr
[ 3 ] = (uint8_t) i
;
9653 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_A
, kDNSServiceClass_IN
, kMDNSRecordTTL_Host
,
9654 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9655 require_noerr( err
, exit
);
9659 *answerPtr
= answer
;
9660 answerPtr
= &answer
->next
;
9664 if( ( inType
== kDNSServiceType_AAAA
) || ( ( inType
== kDNSServiceType_ANY
) && !listHasAAAA
) )
9666 for( i
= 1; i
<= inContext
->recordCountAAAA
; ++i
)
9668 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9669 require_noerr( err
, exit
);
9672 rdataPtr
= (uint8_t *) _memdup( kMDNSReplierBaseAddrV6
, rdataLen
);
9673 require_action( rdataPtr
, exit
, err
= kNoMemoryErr
);
9675 WriteBig16( &rdataPtr
[ 12 ], inIndex
);
9676 rdataPtr
[ 15 ] = (uint8_t) i
;
9678 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_AAAA
, kDNSServiceClass_IN
, kMDNSRecordTTL_Host
,
9679 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9680 require_noerr( err
, exit
);
9684 *answerPtr
= answer
;
9685 answerPtr
= &answer
->next
;
9688 else if( inType
== kDNSServiceType_NSEC
)
9690 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9691 require_noerr( err
, exit
);
9693 if( ( inContext
->recordCountA
> 0 ) && ( inContext
->recordCountAAAA
> 0 ) )
9695 err
= CreateNSECRecordData( recordName
, &rdataPtr
, &rdataLen
, 2, kDNSServiceType_A
, kDNSServiceType_AAAA
);
9696 require_noerr( err
, exit
);
9698 else if( inContext
->recordCountA
> 0 )
9700 err
= CreateNSECRecordData( recordName
, &rdataPtr
, &rdataLen
, 1, kDNSServiceType_A
);
9701 require_noerr( err
, exit
);
9703 else if( inContext
->recordCountAAAA
> 0 )
9705 err
= CreateNSECRecordData( recordName
, &rdataPtr
, &rdataLen
, 1, kDNSServiceType_AAAA
);
9706 require_noerr( err
, exit
);
9710 err
= CreateNSECRecordData( recordName
, &rdataPtr
, &rdataLen
, 0 );
9711 require_noerr( err
, exit
);
9714 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_NSEC
, kDNSServiceClass_IN
, kMDNSRecordTTL_Host
,
9715 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9716 require_noerr( err
, exit
);
9720 *answerPtr
= answer
;
9723 else if( _MDNSReplierServiceTypeMatch( inContext
, inName
, NULL
, &count
) && ( count
>= inIndex
) )
9725 int listHasPTR
= false;
9727 if( inType
== kDNSServiceType_ANY
)
9729 for( answer
= *inAnswerList
; answer
; answer
= answer
->next
)
9731 if( ( answer
->type
== kDNSServiceType_PTR
) && DomainNameEqual( answer
->name
, inName
) )
9739 if( ( inType
== kDNSServiceType_PTR
) || ( ( inType
== kDNSServiceType_ANY
) && !listHasPTR
) )
9741 size_t recordNameLen
;
9745 err
= DomainNameDupLower( inName
, &recordName
, &recordNameLen
);
9746 require_noerr( err
, exit
);
9748 rdataLen
= 1 + hostname
[ 0 ] + 10 + recordNameLen
;
9749 rdataPtr
= (uint8_t *) malloc( rdataLen
);
9750 require_action( rdataPtr
, exit
, err
= kNoMemoryErr
);
9752 lim
= &rdataPtr
[ rdataLen
];
9754 ptr
= &rdataPtr
[ 1 ];
9755 memcpy( ptr
, &hostname
[ 1 ], hostname
[ 0 ] );
9756 ptr
+= hostname
[ 0 ];
9757 if( inIndex
!= 1 ) SNPrintF_Add( (char **) &ptr
, (char *) lim
, " (%u)", inIndex
);
9758 rdataPtr
[ 0 ] = (uint8_t)( ptr
- &rdataPtr
[ 1 ] );
9760 check( (size_t)( lim
- ptr
) >= recordNameLen
);
9761 memcpy( ptr
, recordName
, recordNameLen
);
9762 ptr
+= recordNameLen
;
9764 rdataLen
= (size_t)( ptr
- rdataPtr
);
9766 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_PTR
, kDNSServiceClass_IN
, kMDNSRecordTTL_Other
,
9767 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9768 require_noerr( err
, exit
);
9772 *answerPtr
= answer
;
9775 else if( _MDNSReplierServiceInstanceNameMatch( inContext
, inName
, &index
, &txtSize
, &count
) &&
9776 ( index
== inIndex
) && ( count
>= inIndex
) )
9778 int listHasSRV
= false;
9779 int listHasTXT
= false;
9781 if( inType
== kDNSServiceType_ANY
)
9783 for( answer
= *inAnswerList
; answer
; answer
= answer
->next
)
9785 if( answer
->type
== kDNSServiceType_SRV
)
9787 if( !listHasSRV
&& DomainNameEqual( answer
->name
, inName
) ) listHasSRV
= true;
9789 else if( answer
->type
== kDNSServiceType_TXT
)
9791 if( !listHasTXT
&& DomainNameEqual( answer
->name
, inName
) ) listHasTXT
= true;
9793 if( listHasSRV
&& listHasTXT
) break;
9797 if( ( inType
== kDNSServiceType_SRV
) || ( ( inType
== kDNSServiceType_ANY
) && !listHasSRV
) )
9799 dns_fixed_fields_srv
* fields
;
9802 uint8_t * targetPtr
;
9804 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9805 require_noerr( err
, exit
);
9807 rdataLen
= sizeof( dns_fixed_fields_srv
) + 1 + hostname
[ 0 ] + 10 + kLocalNameLen
;
9808 rdataPtr
= (uint8_t *) malloc( rdataLen
);
9809 require_action( rdataPtr
, exit
, err
= kNoMemoryErr
);
9811 lim
= &rdataPtr
[ rdataLen
];
9813 fields
= (dns_fixed_fields_srv
*) rdataPtr
;
9814 dns_fixed_fields_srv_init( fields
, 0, 0, (uint16_t)( kMDNSReplierPortBase
+ txtSize
) );
9816 targetPtr
= (uint8_t *) &fields
[ 1 ];
9818 ptr
= &targetPtr
[ 1 ];
9819 memcpy( ptr
, &hostname
[ 1 ], hostname
[ 0 ] );
9820 ptr
+= hostname
[ 0 ];
9821 if( inIndex
!= 1 ) SNPrintF_Add( (char **) &ptr
, (char *) lim
, "-%u", inIndex
);
9822 targetPtr
[ 0 ] = (uint8_t)( ptr
- &targetPtr
[ 1 ] );
9824 check( (size_t)( lim
- ptr
) >= kLocalNameLen
);
9825 memcpy( ptr
, kLocalName
, kLocalNameLen
);
9826 ptr
+= kLocalNameLen
;
9828 rdataLen
= (size_t)( ptr
- rdataPtr
);
9830 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_SRV
, kDNSServiceClass_IN
, kMDNSRecordTTL_Host
,
9831 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9832 require_noerr( err
, exit
);
9836 *answerPtr
= answer
;
9837 answerPtr
= &answer
->next
;
9840 if( ( inType
== kDNSServiceType_TXT
) || ( ( inType
== kDNSServiceType_ANY
) && !listHasTXT
) )
9842 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9843 require_noerr( err
, exit
);
9846 err
= _MDNSReplierCreateTXTRecord( inName
, rdataLen
, &rdataPtr
);
9847 require_noerr( err
, exit
);
9849 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_TXT
, kDNSServiceClass_IN
, kMDNSRecordTTL_Other
,
9850 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9851 require_noerr( err
, exit
);
9855 *answerPtr
= answer
;
9857 else if( inType
== kDNSServiceType_NSEC
)
9859 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9860 require_noerr( err
, exit
);
9862 err
= CreateNSECRecordData( recordName
, &rdataPtr
, &rdataLen
, 2, kDNSServiceType_TXT
, kDNSServiceType_SRV
);
9863 require_noerr( err
, exit
);
9865 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_NSEC
, kDNSServiceClass_IN
, kMDNSRecordTTL_Host
,
9866 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9867 require_noerr( err
, exit
);
9871 *answerPtr
= answer
;
9877 FreeNullSafe( recordName
);
9878 FreeNullSafe( rdataPtr
);
9882 //===========================================================================================================================
9883 // _MDNSReplierAnswerListRemovePTR
9884 //===========================================================================================================================
9887 _MDNSReplierAnswerListRemovePTR(
9888 MRResourceRecord
** inAnswerListPtr
,
9889 const uint8_t * inName
,
9890 const uint8_t * inRData
)
9892 MRResourceRecord
* answer
;
9893 MRResourceRecord
** answerPtr
;
9895 for( answerPtr
= inAnswerListPtr
; ( answer
= *answerPtr
) != NULL
; answerPtr
= &answer
->next
)
9897 if( ( answer
->type
== kDNSServiceType_PTR
) && ( answer
->class == kDNSServiceClass_IN
) &&
9898 DomainNameEqual( answer
->name
, inName
) && DomainNameEqual( answer
->rdata
, inRData
) ) break;
9902 *answerPtr
= answer
->next
;
9903 _MRResourceRecordFree( answer
);
9907 //===========================================================================================================================
9908 // _MDNSReplierSendOrDropResponse
9909 //===========================================================================================================================
9912 _MDNSReplierSendOrDropResponse(
9913 MDNSReplierContext
* inContext
,
9914 MRResourceRecord
* inAnswerList
,
9915 sockaddr_ip
* inQuerier
,
9917 unsigned int inIndex
,
9921 uint8_t * responsePtr
= NULL
;
9923 const struct sockaddr
* destAddr
;
9925 const double dropRate
= inUnicast
? inContext
->ucastDropRate
: inContext
->mcastDropRate
;
9928 check( inIndex
<= inContext
->maxInstanceCount
);
9930 // If maxDropCount > 0, then the drop rates apply only to the first maxDropCount responses. Otherwise, all messages are
9931 // subject to their respective drop rate. Also, responses to queries about mDNS replier itself (indicated by index 0),
9932 // as opposed to those for service instance records, are never dropped.
9937 if( inContext
->maxDropCount
> 0 )
9939 uint8_t * const dropCount
= &inContext
->dropCounters
[ inIndex
- 1 ];
9941 if( *dropCount
< inContext
->maxDropCount
)
9943 if( ShouldDrop( dropRate
) ) drop
= true;
9947 else if( ShouldDrop( dropRate
) )
9953 err
= _MDNSReplierCreateResponse( inContext
, inAnswerList
, inIndex
, &responsePtr
, &responseLen
);
9954 require_noerr( err
, exit
);
9958 destAddr
= &inQuerier
->sa
;
9962 destAddr
= ( inQuerier
->sa
.sa_family
== AF_INET
) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
9965 mr_ulog( kLogLevelInfo
, "%s %zu byte response to %##a:\n\n%#1{du:dnsmsg}",
9966 drop
? "Dropping" : "Sending", responseLen
, destAddr
, responsePtr
, responseLen
);
9970 n
= sendto( inSock
, (char *) responsePtr
, responseLen
, 0, destAddr
, SockAddrGetSize( destAddr
) );
9971 err
= map_socket_value_errno( inSock
, n
== (ssize_t
) responseLen
, n
);
9972 require_noerr( err
, exit
);
9976 FreeNullSafe( responsePtr
);
9980 //===========================================================================================================================
9981 // _MDNSReplierCreateResponse
9982 //===========================================================================================================================
9985 _MDNSReplierCreateResponse(
9986 MDNSReplierContext
* inContext
,
9987 MRResourceRecord
* inAnswerList
,
9988 unsigned int inIndex
,
9989 uint8_t ** outResponsePtr
,
9990 size_t * outResponseLen
)
9993 DataBuffer responseDB
;
9995 MRResourceRecord
* answer
;
9996 uint8_t * responsePtr
;
9997 size_t responseLen
, len
;
9998 unsigned int answerCount
, recordCount
;
9999 MRNameOffsetItem
* nameOffsetList
= NULL
;
10001 DataBuffer_Init( &responseDB
, NULL
, 0, SIZE_MAX
);
10003 // The current answers in the answer list will make up the response's Answer Record Section.
10006 for( answer
= inAnswerList
; answer
; answer
= answer
->next
) { ++answerCount
; }
10008 // Unless configured not to, add any additional answers to the answer list for the Additional Record Section.
10010 if( !inContext
->noAdditionals
)
10012 for( answer
= inAnswerList
; answer
; answer
= answer
->next
)
10014 switch( answer
->type
)
10016 case kDNSServiceType_PTR
:
10017 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->rdata
, kDNSServiceType_SRV
,
10019 require_noerr( err
, exit
);
10021 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->rdata
, kDNSServiceType_TXT
,
10023 require_noerr( err
, exit
);
10026 case kDNSServiceType_SRV
:
10027 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->target
, kDNSServiceType_A
,
10029 require_noerr( err
, exit
);
10031 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->target
, kDNSServiceType_AAAA
,
10033 require_noerr( err
, exit
);
10035 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->name
, kDNSServiceType_NSEC
,
10037 require_noerr( err
, exit
);
10040 case kDNSServiceType_TXT
:
10041 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->name
, kDNSServiceType_NSEC
,
10043 require_noerr( err
, exit
);
10046 case kDNSServiceType_A
:
10047 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->name
, kDNSServiceType_AAAA
,
10049 require_noerr( err
, exit
);
10051 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->name
, kDNSServiceType_NSEC
,
10053 require_noerr( err
, exit
);
10056 case kDNSServiceType_AAAA
:
10057 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->name
, kDNSServiceType_A
,
10059 require_noerr( err
, exit
);
10061 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->name
, kDNSServiceType_NSEC
,
10063 require_noerr( err
, exit
);
10072 // Append a provisional header to the response message.
10074 memset( &hdr
, 0, sizeof( hdr
) );
10075 DNSHeaderSetFlags( &hdr
, kDNSHeaderFlag_Response
| kDNSHeaderFlag_AuthAnswer
);
10077 err
= DataBuffer_Append( &responseDB
, &hdr
, sizeof( hdr
) );
10078 require_noerr( err
, exit
);
10080 // Append answers to response message.
10082 responseLen
= DataBuffer_GetLen( &responseDB
);
10084 for( answer
= inAnswerList
; answer
; answer
= answer
->next
)
10086 dns_fixed_fields_record fields
;
10087 unsigned int class;
10089 // Append record NAME.
10091 err
= _MDNSReplierAppendNameToResponse( &responseDB
, answer
->name
, &nameOffsetList
);
10092 require_noerr( err
, exit
);
10094 // Append record TYPE, CLASS, TTL, and provisional RDLENGTH.
10096 class = answer
->class;
10097 if( ( answer
->type
== kDNSServiceType_SRV
) || ( answer
->type
== kDNSServiceType_TXT
) ||
10098 ( answer
->type
== kDNSServiceType_A
) || ( answer
->type
== kDNSServiceType_AAAA
) ||
10099 ( answer
->type
== kDNSServiceType_NSEC
) )
10101 class |= kRRClassCacheFlushBit
;
10104 dns_fixed_fields_record_init( &fields
, answer
->type
, (uint16_t) class, answer
->ttl
, (uint16_t) answer
->rdlength
);
10105 err
= DataBuffer_Append( &responseDB
, &fields
, sizeof( fields
) );
10106 require_noerr( err
, exit
);
10108 // Append record RDATA.
10109 // The RDATA of PTR, SRV, and NSEC records contain domain names, which are subject to name compression.
10111 if( ( answer
->type
== kDNSServiceType_PTR
) || ( answer
->type
== kDNSServiceType_SRV
) ||
10112 ( answer
->type
== kDNSServiceType_NSEC
) )
10115 uint8_t * rdLengthPtr
;
10116 const size_t rdLengthOffset
= DataBuffer_GetLen( &responseDB
) - 2;
10117 const size_t rdataOffset
= DataBuffer_GetLen( &responseDB
);
10119 if( answer
->type
== kDNSServiceType_PTR
)
10121 err
= _MDNSReplierAppendNameToResponse( &responseDB
, answer
->rdata
, &nameOffsetList
);
10122 require_noerr( err
, exit
);
10124 else if( answer
->type
== kDNSServiceType_SRV
)
10126 require_fatal( answer
->target
== &answer
->rdata
[ 6 ], "Bad SRV record target pointer." );
10128 err
= DataBuffer_Append( &responseDB
, answer
->rdata
, (size_t)( answer
->target
- answer
->rdata
) );
10129 require_noerr( err
, exit
);
10131 err
= _MDNSReplierAppendNameToResponse( &responseDB
, answer
->target
, &nameOffsetList
);
10132 require_noerr( err
, exit
);
10136 const size_t nameLen
= DomainNameLength( answer
->rdata
);
10138 err
= _MDNSReplierAppendNameToResponse( &responseDB
, answer
->rdata
, &nameOffsetList
);
10139 require_noerr( err
, exit
);
10141 require_fatal( answer
->rdlength
> nameLen
, "Bad NSEC record data length." );
10143 err
= DataBuffer_Append( &responseDB
, &answer
->rdata
[ nameLen
], answer
->rdlength
- nameLen
);
10144 require_noerr( err
, exit
);
10147 // Set the actual RDLENGTH, which may be less than the original due to name compression.
10149 rdlength
= DataBuffer_GetLen( &responseDB
) - rdataOffset
;
10150 check( rdlength
<= UINT16_MAX
);
10152 rdLengthPtr
= DataBuffer_GetPtr( &responseDB
) + rdLengthOffset
;
10153 WriteBig16( rdLengthPtr
, rdlength
);
10157 err
= DataBuffer_Append( &responseDB
, answer
->rdata
, answer
->rdlength
);
10158 require_noerr( err
, exit
);
10161 if( DataBuffer_GetLen( &responseDB
) > kMDNSMessageSizeMax
) break;
10162 responseLen
= DataBuffer_GetLen( &responseDB
);
10166 // Set the response header's Answer and Additional record counts.
10167 // Note: recordCount may be less than answerCount if including all answerCount records would cause the size of the
10168 // response message to exceed the maximum mDNS message size.
10170 if( recordCount
<= answerCount
)
10172 DNSHeaderSetAnswerCount( (DNSHeader
*) DataBuffer_GetPtr( &responseDB
), recordCount
);
10176 DNSHeaderSetAnswerCount( (DNSHeader
*) DataBuffer_GetPtr( &responseDB
), answerCount
);
10177 DNSHeaderSetAdditionalCount( (DNSHeader
*) DataBuffer_GetPtr( &responseDB
), recordCount
- answerCount
);
10180 err
= DataBuffer_Detach( &responseDB
, &responsePtr
, &len
);
10181 require_noerr( err
, exit
);
10183 if( outResponsePtr
) *outResponsePtr
= responsePtr
;
10184 if( outResponseLen
) *outResponseLen
= responseLen
;
10187 _MRNameOffsetItemFreeList( nameOffsetList
);
10188 DataBuffer_Free( &responseDB
);
10192 //===========================================================================================================================
10193 // _MDNSReplierAppendNameToResponse
10194 //===========================================================================================================================
10197 _MDNSReplierAppendNameToResponse(
10198 DataBuffer
* inResponse
,
10199 const uint8_t * inName
,
10200 MRNameOffsetItem
** inNameOffsetListPtr
)
10203 const uint8_t * subname
;
10204 const uint8_t * limit
;
10206 MRNameOffsetItem
* item
;
10207 uint8_t compressionPtr
[ 2 ];
10209 nameOffset
= DataBuffer_GetLen( inResponse
);
10211 // Find the name's longest subname (more accurately, its longest sub-FQDN) in the name compression list.
10213 for( subname
= inName
; subname
[ 0 ] != 0; subname
+= ( 1 + subname
[ 0 ] ) )
10215 for( item
= *inNameOffsetListPtr
; item
; item
= item
->next
)
10217 if( DomainNameEqual( item
->name
, subname
) ) break;
10220 // If an item was found for this subname, then append a name compression pointer and we're done. Otherwise, append
10221 // the subname's first label.
10225 DNSMessageWriteLabelPointer( compressionPtr
, item
->offset
);
10227 err
= DataBuffer_Append( inResponse
, compressionPtr
, sizeof( compressionPtr
) );
10228 require_noerr( err
, exit
);
10233 err
= DataBuffer_Append( inResponse
, subname
, 1 + subname
[ 0 ] );
10234 require_noerr( err
, exit
);
10238 // If we made it to the root label, then no subname was able to be compressed. All of the name's labels up to the root
10239 // label were appended to the response message, so a root label is needed to terminate the complete name.
10241 if( subname
[ 0 ] == 0 )
10243 err
= DataBuffer_Append( inResponse
, "", 1 );
10244 require_noerr( err
, exit
);
10247 // Add subnames that weren't able to be compressed and their offsets to the name compression list.
10250 for( subname
= inName
; subname
< limit
; subname
+= ( 1 + subname
[ 0 ] ) )
10252 const size_t subnameOffset
= nameOffset
+ (size_t)( subname
- inName
);
10254 if( subnameOffset
> kDNSCompressionOffsetMax
) break;
10256 err
= _MRNameOffsetItemCreate( subname
, (uint16_t) subnameOffset
, &item
);
10257 require_noerr( err
, exit
);
10259 item
->next
= *inNameOffsetListPtr
;
10260 *inNameOffsetListPtr
= item
;
10268 //===========================================================================================================================
10269 // _MDNSReplierServiceTypeMatch
10270 //===========================================================================================================================
10273 _MDNSReplierServiceTypeMatch(
10274 const MDNSReplierContext
* inContext
,
10275 const uint8_t * inName
,
10276 unsigned int * outTXTSize
,
10277 unsigned int * outCount
)
10282 uint32_t txtSize
, count
;
10283 const uint8_t * const serviceLabel
= inContext
->serviceLabel
;
10284 int nameMatches
= false;
10286 require_quiet( inName
[ 0 ] >= serviceLabel
[ 0 ], exit
);
10287 if( _memicmp( &inName
[ 1 ], &serviceLabel
[ 1 ], serviceLabel
[ 0 ] ) != 0 ) goto exit
;
10289 ptr
= (const char *) &inName
[ 1 + serviceLabel
[ 0 ] ];
10290 end
= (const char *) &inName
[ 1 + inName
[ 0 ] ];
10292 require_quiet( ( ptr
< end
) && ( *ptr
== '-' ), exit
);
10295 err
= DecimalTextToUInt32( ptr
, end
, &txtSize
, &ptr
);
10296 require_noerr_quiet( err
, exit
);
10297 require_quiet( txtSize
<= UINT16_MAX
, exit
);
10299 require_quiet( ( ptr
< end
) && ( *ptr
== '-' ), exit
);
10302 err
= DecimalTextToUInt32( ptr
, end
, &count
, &ptr
);
10303 require_noerr_quiet( err
, exit
);
10304 require_quiet( count
<= UINT16_MAX
, exit
);
10305 require_quiet( ptr
== end
, exit
);
10307 if( !DomainNameEqual( (const uint8_t *) ptr
, (const uint8_t *) "\x04" "_tcp" "\x05" "local" ) ) goto exit
;
10308 nameMatches
= true;
10310 if( outTXTSize
) *outTXTSize
= txtSize
;
10311 if( outCount
) *outCount
= count
;
10314 return( nameMatches
? true : false );
10317 //===========================================================================================================================
10318 // _MDNSReplierServiceInstanceNameMatch
10319 //===========================================================================================================================
10322 _MDNSReplierServiceInstanceNameMatch(
10323 const MDNSReplierContext
* inContext
,
10324 const uint8_t * inName
,
10325 unsigned int * outIndex
,
10326 unsigned int * outTXTSize
,
10327 unsigned int * outCount
)
10330 const uint8_t * ptr
;
10331 const uint8_t * end
;
10333 unsigned int txtSize
, count
;
10334 const uint8_t * const hostname
= inContext
->hostname
;
10335 int nameMatches
= false;
10337 require_quiet( inName
[ 0 ] >= hostname
[ 0 ], exit
);
10338 if( _memicmp( &inName
[ 1 ], &hostname
[ 1 ], hostname
[ 0 ] ) != 0 ) goto exit
;
10340 ptr
= &inName
[ 1 + hostname
[ 0 ] ];
10341 end
= &inName
[ 1 + inName
[ 0 ] ];
10344 require_quiet( ( end
- ptr
) >= 2, exit
);
10345 require_quiet( ( ptr
[ 0 ] == ' ' ) && ( ptr
[ 1 ] == '(' ), exit
);
10348 err
= DecimalTextToUInt32( (const char *) ptr
, (const char *) end
, &index
, (const char **) &ptr
);
10349 require_noerr_quiet( err
, exit
);
10350 require_quiet( ( index
>= 2 ) && ( index
<= UINT16_MAX
), exit
);
10352 require_quiet( ( ( end
- ptr
) == 1 ) && ( *ptr
== ')' ), exit
);
10360 if( !_MDNSReplierServiceTypeMatch( inContext
, ptr
, &txtSize
, &count
) ) goto exit
;
10361 nameMatches
= true;
10363 if( outIndex
) *outIndex
= index
;
10364 if( outTXTSize
) *outTXTSize
= txtSize
;
10365 if( outCount
) *outCount
= count
;
10368 return( nameMatches
? true : false );
10371 //===========================================================================================================================
10372 // _MDNSReplierAboutRecordNameMatch
10373 //===========================================================================================================================
10375 #define _MemIEqual( PTR1, LEN1, PTR2, LEN2 ) \
10376 ( ( ( LEN1 ) == ( LEN2 ) ) && ( _memicmp( ( PTR1 ), ( PTR2 ), ( LEN1 ) ) == 0 ) )
10378 static Boolean
_MDNSReplierAboutRecordNameMatch( const MDNSReplierContext
*inContext
, const uint8_t *inName
)
10380 const uint8_t * subname
;
10381 const uint8_t * const hostname
= inContext
->hostname
;
10382 int nameMatches
= false;
10384 if( strnicmpx( &inName
[ 1 ], inName
[ 0 ], "about" ) != 0 ) goto exit
;
10385 subname
= DomainNameGetNextLabel( inName
);
10387 if( !_MemIEqual( &subname
[ 1 ], subname
[ 0 ], &hostname
[ 1 ], hostname
[ 0 ] ) ) goto exit
;
10388 subname
= DomainNameGetNextLabel( subname
);
10390 if( !DomainNameEqual( subname
, kLocalName
) ) goto exit
;
10391 nameMatches
= true;
10394 return( nameMatches
? true : false );
10397 //===========================================================================================================================
10398 // _MDNSReplierHostnameMatch
10399 //===========================================================================================================================
10402 _MDNSReplierHostnameMatch(
10403 const MDNSReplierContext
* inContext
,
10404 const uint8_t * inName
,
10405 unsigned int * outIndex
)
10408 const uint8_t * ptr
;
10409 const uint8_t * end
;
10411 const uint8_t * const hostname
= inContext
->hostname
;
10412 int nameMatches
= false;
10414 require_quiet( inName
[ 0 ] >= hostname
[ 0 ], exit
);
10415 if( _memicmp( &inName
[ 1 ], &hostname
[ 1 ], hostname
[ 0 ] ) != 0 ) goto exit
;
10417 ptr
= &inName
[ 1 + hostname
[ 0 ] ];
10418 end
= &inName
[ 1 + inName
[ 0 ] ];
10421 require_quiet( *ptr
== '-', exit
);
10424 err
= DecimalTextToUInt32( (const char *) ptr
, (const char *) end
, &index
, (const char **) &ptr
);
10425 require_noerr_quiet( err
, exit
);
10426 require_quiet( ( index
>= 2 ) && ( index
<= UINT16_MAX
), exit
);
10427 require_quiet( ptr
== end
, exit
);
10434 if( !DomainNameEqual( ptr
, kLocalName
) ) goto exit
;
10435 nameMatches
= true;
10437 if( outIndex
) *outIndex
= index
;
10440 return( nameMatches
? true : false );
10443 //===========================================================================================================================
10444 // _MDNSReplierCreateTXTRecord
10445 //===========================================================================================================================
10447 static OSStatus
_MDNSReplierCreateTXTRecord( const uint8_t *inRecordName
, size_t inSize
, uint8_t **outTXT
)
10452 size_t i
, wholeCount
, remCount
;
10455 uint8_t txtStr
[ 16 ];
10457 require_action_quiet( inSize
> 0, exit
, err
= kSizeErr
);
10459 txt
= (uint8_t *) malloc( inSize
);
10460 require_action( txt
, exit
, err
= kNoMemoryErr
);
10462 hash
= _FNV1( inRecordName
, DomainNameLength( inRecordName
) );
10465 n
= MemPrintF( &txtStr
[ 1 ], 15, "hash=0x%08X", hash
);
10469 wholeCount
= inSize
/ 16;
10470 for( i
= 0; i
< wholeCount
; ++i
)
10472 memcpy( ptr
, txtStr
, 16 );
10476 remCount
= inSize
% 16;
10479 txtStr
[ 0 ] = (uint8_t)( remCount
- 1 );
10480 memcpy( ptr
, txtStr
, remCount
);
10483 check( ptr
== &txt
[ inSize
] );
10492 //===========================================================================================================================
10493 // _MRResourceRecordCreate
10494 //===========================================================================================================================
10497 _MRResourceRecordCreate(
10502 uint16_t inRDLength
,
10504 MRResourceRecord
** outRecord
)
10507 MRResourceRecord
* obj
;
10509 obj
= (MRResourceRecord
*) calloc( 1, sizeof( *obj
) );
10510 require_action( obj
, exit
, err
= kNoMemoryErr
);
10512 obj
->name
= inName
;
10513 obj
->type
= inType
;
10514 obj
->class = inClass
;
10516 obj
->rdlength
= inRDLength
;
10517 obj
->rdata
= inRData
;
10519 if( inType
== kDNSServiceType_SRV
)
10521 require_action_quiet( obj
->rdlength
> sizeof( dns_fixed_fields_srv
), exit
, err
= kMalformedErr
);
10522 obj
->target
= obj
->rdata
+ sizeof( dns_fixed_fields_srv
);
10530 FreeNullSafe( obj
);
10534 //===========================================================================================================================
10535 // _MRResourceRecordFree
10536 //===========================================================================================================================
10538 static void _MRResourceRecordFree( MRResourceRecord
*inRecord
)
10540 ForgetMem( &inRecord
->name
);
10541 ForgetMem( &inRecord
->rdata
);
10545 //===========================================================================================================================
10546 // _MRResourceRecordFreeList
10547 //===========================================================================================================================
10549 static void _MRResourceRecordFreeList( MRResourceRecord
*inList
)
10551 MRResourceRecord
* record
;
10553 while( ( record
= inList
) != NULL
)
10555 inList
= record
->next
;
10556 _MRResourceRecordFree( record
);
10560 //===========================================================================================================================
10561 // _MRNameOffsetItemCreate
10562 //===========================================================================================================================
10564 static OSStatus
_MRNameOffsetItemCreate( const uint8_t *inName
, uint16_t inOffset
, MRNameOffsetItem
**outItem
)
10567 MRNameOffsetItem
* obj
;
10570 require_action_quiet( inOffset
<= kDNSCompressionOffsetMax
, exit
, err
= kSizeErr
);
10572 nameLen
= DomainNameLength( inName
);
10573 obj
= (MRNameOffsetItem
*) calloc( 1, offsetof( MRNameOffsetItem
, name
) + nameLen
);
10574 require_action( obj
, exit
, err
= kNoMemoryErr
);
10576 obj
->offset
= inOffset
;
10577 memcpy( obj
->name
, inName
, nameLen
);
10586 //===========================================================================================================================
10587 // _MRNameOffsetItemFree
10588 //===========================================================================================================================
10590 static void _MRNameOffsetItemFree( MRNameOffsetItem
*inItem
)
10595 //===========================================================================================================================
10596 // _MRNameOffsetItemFreeList
10597 //===========================================================================================================================
10599 static void _MRNameOffsetItemFreeList( MRNameOffsetItem
*inList
)
10601 MRNameOffsetItem
* item
;
10603 while( ( item
= inList
) != NULL
)
10605 inList
= item
->next
;
10606 _MRNameOffsetItemFree( item
);
10610 //===========================================================================================================================
10612 //===========================================================================================================================
10614 #define kGAIPerfStandardTTL ( 1 * kSecondsPerHour )
10616 typedef struct GAITesterPrivate
* GAITesterRef
;
10617 typedef struct GAITestCase GAITestCase
;
10621 const char * name
; // Domain name that was resolved.
10622 uint64_t connectionTimeUs
; // Time in microseconds that it took to create a DNS-SD connection.
10623 uint64_t firstTimeUs
; // Time in microseconds that it took to get the first address result.
10624 uint64_t timeUs
; // Time in microseconds that it took to get all expected address results.
10627 } GAITestItemResult
;
10629 typedef void ( *GAITesterStopHandler_f
)( void *inContext
, OSStatus inError
);
10631 ( *GAITesterResultsHandler_f
)(
10632 const char * inCaseTitle
,
10633 NanoTime64 inCaseStartTime
,
10634 NanoTime64 inCaseEndTime
,
10635 const GAITestItemResult
* inResultArray
,
10636 size_t inResultCount
,
10637 void * inContext
);
10639 typedef unsigned int GAITestAddrType
;
10640 #define kGAITestAddrType_None 0
10641 #define kGAITestAddrType_IPv4 ( 1U << 0 )
10642 #define kGAITestAddrType_IPv6 ( 1U << 1 )
10643 #define kGAITestAddrType_Both ( kGAITestAddrType_IPv4 | kGAITestAddrType_IPv6 )
10645 #define GAITestAddrTypeIsValid( X ) \
10646 ( ( (X) & kGAITestAddrType_Both ) && ( ( (X) & ~kGAITestAddrType_Both ) == 0 ) )
10650 GAITesterRef tester
; // GAI tester object.
10651 CFMutableArrayRef testCaseResults
; // Array of test case results.
10652 unsigned int iterTimeLimitMs
; // Amount of time to allow each iteration to complete.
10653 unsigned int callDelayMs
; // Amount of time to wait before calling DNSServiceGetAddrInfo().
10654 unsigned int serverDelayMs
; // Amount of additional time to have server delay its responses.
10655 unsigned int defaultIterCount
; // Default test case iteration count.
10656 dispatch_source_t sigIntSource
; // Dispatch source for SIGINT.
10657 dispatch_source_t sigTermSource
; // Dispatch source for SIGTERM.
10658 char * outputFilePath
; // File to write test results to. If NULL, then write to stdout.
10659 OutputFormatType outputFormat
; // Format of test results output.
10660 Boolean skipPathEval
; // True if DNSServiceGetAddrInfo() path evaluation is to be skipped.
10661 Boolean badUDPMode
; // True if the test DNS server is to run in Bad UDP mode.
10662 Boolean testFailed
; // True if at least one test case iteration failed.
10666 static void GAIPerfContextFree( GAIPerfContext
*inContext
);
10667 static OSStatus
GAIPerfAddAdvancedTestCases( GAIPerfContext
*inContext
);
10668 static OSStatus
GAIPerfAddBasicTestCases( GAIPerfContext
*inContext
);
10669 static void GAIPerfTesterStopHandler( void *inContext
, OSStatus inError
);
10671 GAIPerfResultsHandler(
10672 const char * inCaseTitle
,
10673 NanoTime64 inCaseStartTime
,
10674 NanoTime64 inCaseEndTime
,
10675 const GAITestItemResult
* inResultArray
,
10676 size_t inResultCount
,
10677 void * inContext
);
10678 static void GAIPerfSignalHandler( void *inContext
);
10680 CFTypeID
GAITesterGetTypeID( void );
10683 dispatch_queue_t inQueue
,
10684 unsigned int inCallDelayMs
,
10685 int inServerDelayMs
,
10686 int inServerDefaultTTL
,
10687 Boolean inSkipPathEvaluation
,
10688 Boolean inBadUDPMode
,
10689 GAITesterRef
* outTester
);
10690 static void GAITesterStart( GAITesterRef inTester
);
10691 static void GAITesterStop( GAITesterRef inTester
);
10692 static OSStatus
GAITesterAddTestCase( GAITesterRef inTester
, GAITestCase
*inCase
);
10694 GAITesterSetStopHandler(
10695 GAITesterRef inTester
,
10696 GAITesterStopHandler_f inEventHandler
,
10697 void * inEventContext
);
10699 GAITesterSetResultsHandler(
10700 GAITesterRef inTester
,
10701 GAITesterResultsHandler_f inResultsHandler
,
10702 void * inResultsContext
);
10704 static OSStatus
GAITestCaseCreate( const char *inTitle
, GAITestCase
**outCase
);
10705 static void GAITestCaseFree( GAITestCase
*inCase
);
10707 GAITestCaseAddItem(
10708 GAITestCase
* inCase
,
10709 unsigned int inAliasCount
,
10710 unsigned int inAddressCount
,
10712 GAITestAddrType inHasAddrs
,
10713 GAITestAddrType inWantAddrs
,
10714 unsigned int inTimeLimitMs
,
10715 unsigned int inItemCount
);
10717 GAITestCaseAddLocalHostItem(
10718 GAITestCase
* inCase
,
10719 GAITestAddrType inWantAddrs
,
10720 unsigned int inTimeLimitMs
,
10721 unsigned int inItemCount
);
10723 static void GAIPerfCmd( void )
10726 GAIPerfContext
* context
= NULL
;
10728 err
= CheckRootUser();
10729 require_noerr_quiet( err
, exit
);
10731 err
= CheckIntegerArgument( gGAIPerf_CallDelayMs
, "call delay (ms)", 0, INT_MAX
);
10732 require_noerr_quiet( err
, exit
);
10734 err
= CheckIntegerArgument( gGAIPerf_ServerDelayMs
, "server delay (ms)", 0, INT_MAX
);
10735 require_noerr_quiet( err
, exit
);
10737 err
= CheckIntegerArgument( gGAIPerf_IterationCount
, "iteration count", 1, INT_MAX
);
10738 require_noerr_quiet( err
, exit
);
10740 err
= CheckIntegerArgument( gGAIPerf_IterationTimeLimitMs
, "iteration time limit (ms)", 0, INT_MAX
);
10741 require_noerr_quiet( err
, exit
);
10743 context
= (GAIPerfContext
*) calloc( 1, sizeof( *context
) );
10744 require_action( context
, exit
, err
= kNoMemoryErr
);
10746 context
->testCaseResults
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
10747 require_action( context
->testCaseResults
, exit
, err
= kNoMemoryErr
);
10749 context
->iterTimeLimitMs
= (unsigned int) gGAIPerf_IterationTimeLimitMs
;
10750 context
->callDelayMs
= (unsigned int) gGAIPerf_CallDelayMs
;
10751 context
->serverDelayMs
= (unsigned int) gGAIPerf_ServerDelayMs
;
10752 context
->defaultIterCount
= (unsigned int) gGAIPerf_IterationCount
;
10753 context
->skipPathEval
= gGAIPerf_SkipPathEvalulation
? true : false;
10754 context
->badUDPMode
= gGAIPerf_BadUDPMode
? true : false;
10756 if( gGAIPerf_OutputFilePath
)
10758 context
->outputFilePath
= strdup( gGAIPerf_OutputFilePath
);
10759 require_action( context
->outputFilePath
, exit
, err
= kNoMemoryErr
);
10762 err
= OutputFormatFromArgString( gGAIPerf_OutputFormat
, &context
->outputFormat
);
10763 require_noerr_quiet( err
, exit
);
10765 err
= GAITesterCreate( dispatch_get_main_queue(), context
->callDelayMs
, (int) context
->serverDelayMs
,
10766 kGAIPerfStandardTTL
, context
->skipPathEval
, context
->badUDPMode
, &context
->tester
);
10767 require_noerr( err
, exit
);
10769 check( gGAIPerf_TestSuite
);
10770 if( strcasecmp( gGAIPerf_TestSuite
, kGAIPerfTestSuiteName_Basic
) == 0 )
10772 err
= GAIPerfAddBasicTestCases( context
);
10773 require_noerr( err
, exit
);
10775 else if( strcasecmp( gGAIPerf_TestSuite
, kGAIPerfTestSuiteName_Advanced
) == 0 )
10777 err
= GAIPerfAddAdvancedTestCases( context
);
10778 require_noerr( err
, exit
);
10782 FPrintF( stderr
, "error: Invalid test suite name: %s.\n", gGAIPerf_TestSuite
);
10786 GAITesterSetStopHandler( context
->tester
, GAIPerfTesterStopHandler
, context
);
10787 GAITesterSetResultsHandler( context
->tester
, GAIPerfResultsHandler
, context
);
10789 signal( SIGINT
, SIG_IGN
);
10790 err
= DispatchSignalSourceCreate( SIGINT
, dispatch_get_main_queue(), GAIPerfSignalHandler
, context
,
10791 &context
->sigIntSource
);
10792 require_noerr( err
, exit
);
10793 dispatch_resume( context
->sigIntSource
);
10795 signal( SIGTERM
, SIG_IGN
);
10796 err
= DispatchSignalSourceCreate( SIGTERM
, dispatch_get_main_queue(), GAIPerfSignalHandler
, context
,
10797 &context
->sigTermSource
);
10798 require_noerr( err
, exit
);
10799 dispatch_resume( context
->sigTermSource
);
10801 GAITesterStart( context
->tester
);
10805 if( context
) GAIPerfContextFree( context
);
10809 //===========================================================================================================================
10810 // GAIPerfContextFree
10811 //===========================================================================================================================
10813 static void GAIPerfContextFree( GAIPerfContext
*inContext
)
10815 ForgetCF( &inContext
->tester
);
10816 ForgetCF( &inContext
->testCaseResults
);
10817 ForgetMem( &inContext
->outputFilePath
);
10818 dispatch_source_forget( &inContext
->sigIntSource
);
10819 dispatch_source_forget( &inContext
->sigTermSource
);
10823 //===========================================================================================================================
10824 // GAIPerfAddAdvancedTestCases
10825 //===========================================================================================================================
10827 #define kTestCaseTitleBufferSize 128
10830 _GAIPerfWriteTestCaseTitle(
10831 char inBuffer
[ kTestCaseTitleBufferSize
],
10832 unsigned int inCNAMERecordCount
,
10833 unsigned int inARecordCount
,
10834 unsigned int inAAAARecordCount
,
10835 GAITestAddrType inRequested
,
10836 unsigned int inIterationCount
,
10837 Boolean inIterationsAreUnique
);
10839 _GAIPerfWriteLocalHostTestCaseTitle(
10840 char inBuffer
[ kTestCaseTitleBufferSize
],
10841 GAITestAddrType inRequested
,
10842 unsigned int inIterationCount
);
10844 #define kGAIPerfAdvancedTestSuite_MaxAliasCount 4
10845 #define kGAIPerfAdvancedTestSuite_MaxAddrCount 8
10847 static OSStatus
GAIPerfAddAdvancedTestCases( GAIPerfContext
*inContext
)
10850 unsigned int aliasCount
, addressCount
, i
;
10851 GAITestCase
* testCase
= NULL
;
10852 char title
[ kTestCaseTitleBufferSize
];
10855 while( aliasCount
<= kGAIPerfAdvancedTestSuite_MaxAliasCount
)
10857 for( addressCount
= 1; addressCount
<= kGAIPerfAdvancedTestSuite_MaxAddrCount
; addressCount
*= 2 )
10859 // Add a test case to resolve a domain name with
10861 // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
10863 // to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which
10864 // requires server queries.
10866 _GAIPerfWriteTestCaseTitle( title
, aliasCount
, addressCount
, addressCount
, kGAITestAddrType_Both
,
10867 inContext
->defaultIterCount
, true );
10869 err
= GAITestCaseCreate( title
, &testCase
);
10870 require_noerr( err
, exit
);
10872 for( i
= 0; i
< inContext
->defaultIterCount
; ++i
)
10874 err
= GAITestCaseAddItem( testCase
, aliasCount
, addressCount
, kGAIPerfStandardTTL
,
10875 kGAITestAddrType_Both
, kGAITestAddrType_Both
, inContext
->iterTimeLimitMs
, 1 );
10876 require_noerr( err
, exit
);
10879 err
= GAITesterAddTestCase( inContext
->tester
, testCase
);
10880 require_noerr( err
, exit
);
10883 // Add a test case to resolve a domain name with
10885 // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
10887 // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique domain name, which requires a server
10888 // query. The subsequent iterations resolve the same domain name as the preliminary iteration, which should
10889 // ideally require no server queries, i.e., the results should come from the cache.
10891 _GAIPerfWriteTestCaseTitle( title
, aliasCount
, addressCount
, addressCount
, kGAITestAddrType_Both
,
10892 inContext
->defaultIterCount
, false );
10894 err
= GAITestCaseCreate( title
, &testCase
);
10895 require_noerr( err
, exit
);
10897 err
= GAITestCaseAddItem( testCase
, aliasCount
, addressCount
, kGAIPerfStandardTTL
,
10898 kGAITestAddrType_Both
, kGAITestAddrType_Both
, inContext
->iterTimeLimitMs
, inContext
->defaultIterCount
+ 1 );
10899 require_noerr( err
, exit
);
10901 err
= GAITesterAddTestCase( inContext
->tester
, testCase
);
10902 require_noerr( err
, exit
);
10906 aliasCount
= ( aliasCount
== 0 ) ? 1 : ( 2 * aliasCount
);
10909 // Finally, add a test case to resolve localhost to its IPv4 and IPv6 addresses.
10911 _GAIPerfWriteLocalHostTestCaseTitle( title
, kGAITestAddrType_Both
, inContext
->defaultIterCount
);
10913 err
= GAITestCaseCreate( title
, &testCase
);
10914 require_noerr( err
, exit
);
10916 err
= GAITestCaseAddLocalHostItem( testCase
, kGAITestAddrType_Both
, inContext
->iterTimeLimitMs
,
10917 inContext
->defaultIterCount
);
10918 require_noerr( err
, exit
);
10920 err
= GAITesterAddTestCase( inContext
->tester
, testCase
);
10921 require_noerr( err
, exit
);
10925 if( testCase
) GAITestCaseFree( testCase
);
10929 //===========================================================================================================================
10930 // _GAIPerfWriteTestCaseTitle
10931 //===========================================================================================================================
10933 #define GAITestAddrTypeToRequestKeyValue( X ) ( \
10934 ( (X) == kGAITestAddrType_Both ) ? "ipv4\\,ipv6" : \
10935 ( (X) == kGAITestAddrType_IPv4 ) ? "ipv4" : \
10936 ( (X) == kGAITestAddrType_IPv6 ) ? "ipv6" : \
10940 _GAIPerfWriteTestCaseTitle(
10941 char inBuffer
[ kTestCaseTitleBufferSize
],
10942 unsigned int inCNAMERecordCount
,
10943 unsigned int inARecordCount
,
10944 unsigned int inAAAARecordCount
,
10945 GAITestAddrType inRequested
,
10946 unsigned int inIterationCount
,
10947 Boolean inIterationsAreUnique
)
10949 SNPrintF( inBuffer
, kTestCaseTitleBufferSize
, "name=dynamic,cname=%u,a=%u,aaaa=%u,req=%s,iterations=%u%?s",
10950 inCNAMERecordCount
, inARecordCount
, inAAAARecordCount
, GAITestAddrTypeToRequestKeyValue( inRequested
),
10951 inIterationCount
, inIterationsAreUnique
, ",unique" );
10954 //===========================================================================================================================
10955 // _GAIPerfWriteLocalHostTestCaseTitle
10956 //===========================================================================================================================
10959 _GAIPerfWriteLocalHostTestCaseTitle(
10960 char inBuffer
[ kTestCaseTitleBufferSize
],
10961 GAITestAddrType inRequested
,
10962 unsigned int inIterationCount
)
10964 SNPrintF( inBuffer
, kTestCaseTitleBufferSize
, "name=localhost,req=%s,iterations=%u",
10965 GAITestAddrTypeToRequestKeyValue( inRequested
), inIterationCount
);
10968 //===========================================================================================================================
10969 // GAIPerfAddBasicTestCases
10970 //===========================================================================================================================
10972 #define kGAIPerfBasicTestSuite_AliasCount 2
10973 #define kGAIPerfBasicTestSuite_AddrCount 4
10975 static OSStatus
GAIPerfAddBasicTestCases( GAIPerfContext
*inContext
)
10978 GAITestCase
* testCase
= NULL
;
10979 char title
[ kTestCaseTitleBufferSize
];
10983 // Resolve a domain name with
10985 // 2 CNAME records, 4 A records, and 4 AAAA records
10987 // to its IPv4 and IPv6 addresses. Each of the iterations resolves a unique domain name, which requires server
10990 _GAIPerfWriteTestCaseTitle( title
, kGAIPerfBasicTestSuite_AliasCount
,
10991 kGAIPerfBasicTestSuite_AddrCount
, kGAIPerfBasicTestSuite_AddrCount
, kGAITestAddrType_Both
,
10992 inContext
->defaultIterCount
, true );
10994 err
= GAITestCaseCreate( title
, &testCase
);
10995 require_noerr( err
, exit
);
10997 for( i
= 0; i
< inContext
->defaultIterCount
; ++i
)
10999 err
= GAITestCaseAddItem( testCase
, kGAIPerfBasicTestSuite_AliasCount
, kGAIPerfBasicTestSuite_AddrCount
,
11000 kGAIPerfStandardTTL
, kGAITestAddrType_Both
, kGAITestAddrType_Both
, inContext
->iterTimeLimitMs
, 1 );
11001 require_noerr( err
, exit
);
11004 err
= GAITesterAddTestCase( inContext
->tester
, testCase
);
11005 require_noerr( err
, exit
);
11009 // Resolve a domain name with
11011 // 2 CNAME records, 4 A records, and 4 AAAA records
11013 // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which
11014 // requires server queries. Each of the subsequent iterations resolves the same domain name as the preliminary
11015 // iteration, which should ideally require no additional server queries, i.e., the results should come from the cache.
11017 _GAIPerfWriteTestCaseTitle( title
, kGAIPerfBasicTestSuite_AliasCount
,
11018 kGAIPerfBasicTestSuite_AddrCount
, kGAIPerfBasicTestSuite_AddrCount
, kGAITestAddrType_Both
,
11019 inContext
->defaultIterCount
, false );
11021 err
= GAITestCaseCreate( title
, &testCase
);
11022 require_noerr( err
, exit
);
11024 err
= GAITestCaseAddItem( testCase
, kGAIPerfBasicTestSuite_AliasCount
, kGAIPerfBasicTestSuite_AddrCount
,
11025 kGAIPerfStandardTTL
, kGAITestAddrType_Both
, kGAITestAddrType_Both
, inContext
->iterTimeLimitMs
,
11026 inContext
->defaultIterCount
+ 1 );
11027 require_noerr( err
, exit
);
11029 err
= GAITesterAddTestCase( inContext
->tester
, testCase
);
11030 require_noerr( err
, exit
);
11034 // Each iteration resolves localhost to its IPv4 and IPv6 addresses.
11036 _GAIPerfWriteLocalHostTestCaseTitle( title
, kGAITestAddrType_Both
, inContext
->defaultIterCount
);
11038 err
= GAITestCaseCreate( title
, &testCase
);
11039 require_noerr( err
, exit
);
11041 err
= GAITestCaseAddLocalHostItem( testCase
, kGAITestAddrType_Both
, inContext
->iterTimeLimitMs
,
11042 inContext
->defaultIterCount
);
11043 require_noerr( err
, exit
);
11045 err
= GAITesterAddTestCase( inContext
->tester
, testCase
);
11046 require_noerr( err
, exit
);
11050 if( testCase
) GAITestCaseFree( testCase
);
11054 //===========================================================================================================================
11055 // GAIPerfTesterStopHandler
11056 //===========================================================================================================================
11058 #define kGAIPerfResultsKey_Info CFSTR( "info" )
11059 #define kGAIPerfResultsKey_TestCases CFSTR( "testCases" )
11060 #define kGAIPerfResultsKey_Success CFSTR( "success" )
11062 #define kGAIPerfInfoKey_CallDelay CFSTR( "callDelayMs" )
11063 #define kGAIPerfInfoKey_ServerDelay CFSTR( "serverDelayMs" )
11064 #define kGAIPerfInfoKey_SkippedPathEval CFSTR( "skippedPathEval" )
11065 #define kGAIPerfInfoKey_UsedBadUDPMode CFSTR( "usedBadUPDMode" )
11067 static void GAIPerfTesterStopHandler( void *inContext
, OSStatus inError
)
11070 GAIPerfContext
* const context
= (GAIPerfContext
*) inContext
;
11071 CFPropertyListRef plist
;
11075 require_noerr_quiet( err
, exit
);
11077 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
11081 "%kO=%lli" // callDelayMs
11082 "%kO=%lli" // serverDelayMs
11083 "%kO=%b" // skippedPathEval
11084 "%kO=%b" // usedBadUPDMode
11086 "%kO=%O" // testCases
11087 "%kO=%b" // success
11089 kGAIPerfResultsKey_Info
,
11090 kGAIPerfInfoKey_CallDelay
, (int64_t) context
->callDelayMs
,
11091 kGAIPerfInfoKey_ServerDelay
, (int64_t) context
->serverDelayMs
,
11092 kGAIPerfInfoKey_SkippedPathEval
, context
->skipPathEval
,
11093 kGAIPerfInfoKey_UsedBadUDPMode
, context
->badUDPMode
,
11094 kGAIPerfResultsKey_TestCases
, context
->testCaseResults
,
11095 kGAIPerfResultsKey_Success
, !context
->testFailed
);
11096 require_noerr( err
, exit
);
11098 err
= OutputPropertyList( plist
, context
->outputFormat
, context
->outputFilePath
);
11099 CFRelease( plist
);
11100 require_noerr( err
, exit
);
11103 exitCode
= err
? 1 : ( context
->testFailed
? 2 : 0 );
11104 GAIPerfContextFree( context
);
11108 //===========================================================================================================================
11109 // GAIPerfResultsHandler
11110 //===========================================================================================================================
11112 // Keys for test case dictionary
11114 #define kGAIPerfTestCaseKey_Title CFSTR( "title" )
11115 #define kGAIPerfTestCaseKey_StartTime CFSTR( "startTime" )
11116 #define kGAIPerfTestCaseKey_EndTime CFSTR( "endTime" )
11117 #define kGAIPerfTestCaseKey_Results CFSTR( "results" )
11118 #define kGAIPerfTestCaseKey_FirstStats CFSTR( "firstStats" )
11119 #define kGAIPerfTestCaseKey_ConnectionStats CFSTR( "connectionStats" )
11120 #define kGAIPerfTestCaseKey_Stats CFSTR( "stats" )
11122 // Keys for test case results array entry dictionaries
11124 #define kGAIPerfTestCaseResultKey_Name CFSTR( "name" )
11125 #define kGAIPerfTestCaseResultKey_ConnectionTime CFSTR( "connectionTimeUs" )
11126 #define kGAIPerfTestCaseResultKey_FirstTime CFSTR( "firstTimeUs" )
11127 #define kGAIPerfTestCaseResultKey_Time CFSTR( "timeUs" )
11129 // Keys for test case stats dictionaries
11131 #define kGAIPerfTestCaseStatsKey_Count CFSTR( "count" )
11132 #define kGAIPerfTestCaseStatsKey_Min CFSTR( "min" )
11133 #define kGAIPerfTestCaseStatsKey_Max CFSTR( "max" )
11134 #define kGAIPerfTestCaseStatsKey_Mean CFSTR( "mean" )
11135 #define kGAIPerfTestCaseStatsKey_StdDev CFSTR( "sd" )
11146 #define GAIPerfStatsInit( X ) \
11147 do { (X)->min = DBL_MAX; (X)->max = DBL_MIN; (X)->mean = 0.0; (X)->stdDev = 0.0; } while( 0 )
11150 GAIPerfResultsHandler(
11151 const char * inCaseTitle
,
11152 NanoTime64 inCaseStartTime
,
11153 NanoTime64 inCaseEndTime
,
11154 const GAITestItemResult
* inResultArray
,
11155 size_t inResultCount
,
11159 GAIPerfContext
* const context
= (GAIPerfContext
*) inContext
;
11160 int namesAreDynamic
, namesAreUnique
;
11162 size_t count
, startIndex
;
11163 CFMutableArrayRef results
= NULL
;
11164 GAIPerfStats stats
, firstStats
, connStats
;
11165 double sum
, firstSum
, connSum
;
11166 size_t keyValueLen
, i
;
11167 char keyValue
[ 16 ]; // Size must be at least strlen( "name=dynamic" ) + 1 bytes.
11168 char startTime
[ 32 ];
11169 char endTime
[ 32 ];
11170 const GAITestItemResult
* result
;
11172 // If this test case resolves the same "d.test." name in each iteration (title contains the "name=dynamic" key-value
11173 // pair, but not the "unique" key), then don't count the first iteration, whose purpose is to populate the cache with
11174 // the domain name's CNAME, A, and AAAA records.
11176 namesAreDynamic
= false;
11177 namesAreUnique
= false;
11179 while( _ParseQuotedEscapedString( ptr
, NULL
, ",", keyValue
, sizeof( keyValue
), &keyValueLen
, NULL
, &ptr
) )
11181 if( strnicmpx( keyValue
, keyValueLen
, "name=dynamic" ) == 0 )
11183 namesAreDynamic
= true;
11185 else if( strnicmpx( keyValue
, keyValueLen
, "unique" ) == 0 )
11187 namesAreUnique
= true;
11189 if( namesAreDynamic
&& namesAreUnique
) break;
11192 startIndex
= ( ( inResultCount
> 0 ) && namesAreDynamic
&& !namesAreUnique
) ? 1 : 0;
11193 results
= CFArrayCreateMutable( NULL
, (CFIndex
)( inResultCount
- startIndex
), &kCFTypeArrayCallBacks
);
11194 require_action( results
, exit
, err
= kNoMemoryErr
);
11196 GAIPerfStatsInit( &stats
);
11197 GAIPerfStatsInit( &firstStats
);
11198 GAIPerfStatsInit( &connStats
);
11204 for( i
= startIndex
; i
< inResultCount
; ++i
)
11208 result
= &inResultArray
[ i
];
11210 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, results
,
11213 "%kO=%lli" // connectionTimeUs
11214 "%kO=%lli" // firstTimeUs
11215 "%kO=%lli" // timeUs
11216 "%kO=%lli" // error
11218 kGAIPerfTestCaseResultKey_Name
, result
->name
,
11219 kGAIPerfTestCaseResultKey_ConnectionTime
, (int64_t) result
->connectionTimeUs
,
11220 kGAIPerfTestCaseResultKey_FirstTime
, (int64_t) result
->firstTimeUs
,
11221 kGAIPerfTestCaseResultKey_Time
, (int64_t) result
->timeUs
,
11222 CFSTR( "error" ), (int64_t) result
->error
);
11223 require_noerr( err
, exit
);
11225 if( !result
->error
)
11227 value
= (double) result
->timeUs
;
11228 if( value
< stats
.min
) stats
.min
= value
;
11229 if( value
> stats
.max
) stats
.max
= value
;
11232 value
= (double) result
->firstTimeUs
;
11233 if( value
< firstStats
.min
) firstStats
.min
= value
;
11234 if( value
> firstStats
.max
) firstStats
.max
= value
;
11237 value
= (double) result
->connectionTimeUs
;
11238 if( value
< connStats
.min
) connStats
.min
= value
;
11239 if( value
> connStats
.max
) connStats
.max
= value
;
11246 context
->testFailed
= true;
11252 stats
.mean
= sum
/ count
;
11253 firstStats
.mean
= firstSum
/ count
;
11254 connStats
.mean
= connSum
/ count
;
11259 for( i
= startIndex
; i
< inResultCount
; ++i
)
11263 result
= &inResultArray
[ i
];
11264 if( result
->error
) continue;
11266 diff
= stats
.mean
- (double) result
->timeUs
;
11267 sum
+= ( diff
* diff
);
11269 diff
= firstStats
.mean
- (double) result
->firstTimeUs
;
11270 firstSum
+= ( diff
* diff
);
11272 diff
= connStats
.mean
- (double) result
->connectionTimeUs
;
11273 connSum
+= ( diff
* diff
);
11275 stats
.stdDev
= sqrt( sum
/ count
);
11276 firstStats
.stdDev
= sqrt( firstSum
/ count
);
11277 connStats
.stdDev
= sqrt( connSum
/ count
);
11280 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, context
->testCaseResults
,
11311 kGAIPerfTestCaseKey_Title
, inCaseTitle
,
11312 kGAIPerfTestCaseKey_StartTime
, _NanoTime64ToTimestamp( inCaseStartTime
, startTime
, sizeof( startTime
) ),
11313 kGAIPerfTestCaseKey_EndTime
, _NanoTime64ToTimestamp( inCaseEndTime
, endTime
, sizeof( endTime
) ),
11314 kGAIPerfTestCaseKey_Results
, results
,
11315 kGAIPerfTestCaseKey_Stats
,
11316 kGAIPerfTestCaseStatsKey_Count
, (int64_t) count
,
11317 kGAIPerfTestCaseStatsKey_Min
, stats
.min
,
11318 kGAIPerfTestCaseStatsKey_Max
, stats
.max
,
11319 kGAIPerfTestCaseStatsKey_Mean
, stats
.mean
,
11320 kGAIPerfTestCaseStatsKey_StdDev
, stats
.stdDev
,
11321 kGAIPerfTestCaseKey_FirstStats
,
11322 kGAIPerfTestCaseStatsKey_Count
, (int64_t) count
,
11323 kGAIPerfTestCaseStatsKey_Min
, firstStats
.min
,
11324 kGAIPerfTestCaseStatsKey_Max
, firstStats
.max
,
11325 kGAIPerfTestCaseStatsKey_Mean
, firstStats
.mean
,
11326 kGAIPerfTestCaseStatsKey_StdDev
, firstStats
.stdDev
,
11327 kGAIPerfTestCaseKey_ConnectionStats
,
11328 kGAIPerfTestCaseStatsKey_Count
, (int64_t) count
,
11329 kGAIPerfTestCaseStatsKey_Min
, connStats
.min
,
11330 kGAIPerfTestCaseStatsKey_Max
, connStats
.max
,
11331 kGAIPerfTestCaseStatsKey_Mean
, connStats
.mean
,
11332 kGAIPerfTestCaseStatsKey_StdDev
, connStats
.stdDev
);
11333 require_noerr( err
, exit
);
11336 CFReleaseNullSafe( results
);
11337 if( err
) exit( 1 );
11340 //===========================================================================================================================
11341 // GAIPerfSignalHandler
11342 //===========================================================================================================================
11344 static void GAIPerfSignalHandler( void *inContext
)
11346 GAIPerfContext
* const context
= (GAIPerfContext
*) inContext
;
11348 if( !context
->tester
) exit( 1 );
11349 GAITesterStop( context
->tester
);
11350 context
->tester
= NULL
;
11353 //===========================================================================================================================
11355 //===========================================================================================================================
11357 // A character set of lower-case alphabet characters and digits and a string length of six allows for 36^6 = 2,176,782,336
11358 // possible strings to use in the Tag label.
11360 #define kGAITesterTagStringLen 6
11362 typedef struct GAITestItem GAITestItem
;
11365 GAITestItem
* next
; // Next test item in list.
11366 char * name
; // Domain name to resolve.
11367 uint64_t connectionTimeUs
; // Time in microseconds that it took to create a DNS-SD connection.
11368 uint64_t firstTimeUs
; // Time in microseconds that it took to get the first address result.
11369 uint64_t timeUs
; // Time in microseconds that it took to get all expected address results.
11370 unsigned int addressCount
; // Address count of the domain name, i.e., the Count label argument.
11371 OSStatus error
; // Current status/error.
11372 unsigned int timeLimitMs
; // Time limit in milliseconds for the test item's completion.
11373 Boolean hasV4
; // True if the domain name has one or more IPv4 addresses.
11374 Boolean hasV6
; // True if the domain name has one or more IPv6 addresses.
11375 Boolean wantV4
; // True if DNSServiceGetAddrInfo() should be called to get IPv4 addresses.
11376 Boolean wantV6
; // True if DNSServiceGetAddrInfo() should be called to get IPv6 addresses.
11381 GAITestCase
* next
; // Next test case in list.
11382 GAITestItem
* itemList
; // List of test items.
11383 char * title
; // Title of the test case.
11386 struct GAITesterPrivate
11388 CFRuntimeBase base
; // CF object base.
11389 dispatch_queue_t queue
; // Serial work queue.
11390 DNSServiceRef connection
; // Reference to the shared DNS-SD connection.
11391 DNSServiceRef getAddrInfo
; // Reference to the current DNSServiceGetAddrInfo operation.
11392 GAITestCase
* caseList
; // List of test cases.
11393 GAITestCase
* currentCase
; // Pointer to the current test case.
11394 GAITestItem
* currentItem
; // Pointer to the current test item.
11395 NanoTime64 caseStartTime
; // Start time of current test case in Unix time as nanoseconds.
11396 NanoTime64 caseEndTime
; // End time of current test case in Unix time as nanoseconds.
11397 unsigned int callDelayMs
; // Amount of time to wait before calling DNSServiceGetAddrInfo().
11398 Boolean skipPathEval
; // True if DNSServiceGetAddrInfo() path evaluation is to be skipped.
11399 Boolean stopped
; // True if the tester has been stopped.
11400 Boolean badUDPMode
; // True if the test DNS server is to run in Bad UDP mode.
11401 dispatch_source_t timer
; // Timer for enforcing a test item's time limit.
11402 pcap_t
* pcap
; // Captures traffic between mDNSResponder and test DNS server.
11403 pid_t serverPID
; // PID of the test DNS server.
11404 int serverDelayMs
; // Additional time to have the server delay its responses by.
11405 int serverDefaultTTL
; // Default TTL for the server's records.
11406 GAITesterStopHandler_f stopHandler
; // User's stop handler.
11407 void * stopContext
; // User's event handler context.
11408 GAITesterResultsHandler_f resultsHandler
; // User's results handler.
11409 void * resultsContext
; // User's results handler context.
11411 // Variables for current test item.
11413 uint64_t bitmapV4
; // Bitmap of IPv4 results that have yet to be received.
11414 uint64_t bitmapV6
; // Bitmap of IPv6 results that have yet to be received.
11415 uint64_t startTicks
; // Start ticks of DNSServiceGetAddrInfo().
11416 uint64_t connTicks
; // Ticks when the connection was created.
11417 uint64_t firstTicks
; // Ticks when the first DNSServiceGetAddrInfo result was received.
11418 uint64_t endTicks
; // Ticks when the last DNSServiceGetAddrInfo result was received.
11419 Boolean gotFirstResult
; // True if the first result has been received.
11422 CF_CLASS_DEFINE( GAITester
);
11424 static void _GAITesterStartNextTest( GAITesterRef inTester
);
11425 static OSStatus
_GAITesterCreatePacketCapture( pcap_t
**outPCap
);
11426 static void _GAITesterFirstGAITimeout( void *inContext
);
11427 static void _GAITesterTimeout( void *inContext
);
11428 static void DNSSD_API
11429 _GAITesterFirstGAICallback(
11430 DNSServiceRef inSDRef
,
11431 DNSServiceFlags inFlags
,
11432 uint32_t inInterfaceIndex
,
11433 DNSServiceErrorType inError
,
11434 const char * inHostname
,
11435 const struct sockaddr
* inSockAddr
,
11437 void * inContext
);
11438 static void DNSSD_API
11439 _GAITesterGetAddrInfoCallback(
11440 DNSServiceRef inSDRef
,
11441 DNSServiceFlags inFlags
,
11442 uint32_t inInterfaceIndex
,
11443 DNSServiceErrorType inError
,
11444 const char * inHostname
,
11445 const struct sockaddr
* inSockAddr
,
11447 void * inContext
);
11448 static void _GAITesterCompleteCurrentTest( GAITesterRef inTester
, OSStatus inError
);
11450 #define ForgetPacketCapture( X ) ForgetCustom( X, pcap_close )
11454 const char * inName
,
11455 unsigned int inAddressCount
,
11456 GAITestAddrType inHasAddrs
,
11457 GAITestAddrType inWantAddrs
,
11458 unsigned int inTimeLimitMs
,
11459 GAITestItem
** outItem
);
11460 static OSStatus
GAITestItemDup( const GAITestItem
*inItem
, GAITestItem
**outItem
);
11461 static void GAITestItemFree( GAITestItem
*inItem
);
11465 dispatch_queue_t inQueue
,
11466 unsigned int inCallDelayMs
,
11467 int inServerDelayMs
,
11468 int inServerDefaultTTL
,
11469 Boolean inSkipPathEvaluation
,
11470 Boolean inBadUDPMode
,
11471 GAITesterRef
* outTester
)
11474 GAITesterRef obj
= NULL
;
11476 CF_OBJECT_CREATE( GAITester
, obj
, err
, exit
);
11478 ReplaceDispatchQueue( &obj
->queue
, inQueue
);
11479 obj
->callDelayMs
= inCallDelayMs
;
11480 obj
->serverPID
= -1;
11481 obj
->serverDelayMs
= inServerDelayMs
;
11482 obj
->serverDefaultTTL
= inServerDefaultTTL
;
11483 obj
->skipPathEval
= inSkipPathEvaluation
;
11484 obj
->badUDPMode
= inBadUDPMode
;
11491 CFReleaseNullSafe( obj
);
11495 //===========================================================================================================================
11496 // _GAITesterFinalize
11497 //===========================================================================================================================
11499 static void _GAITesterFinalize( CFTypeRef inObj
)
11501 GAITesterRef
const me
= (GAITesterRef
) inObj
;
11502 GAITestCase
* testCase
;
11504 check( !me
->getAddrInfo
);
11505 check( !me
->connection
);
11506 check( !me
->timer
);
11507 dispatch_forget( &me
->queue
);
11508 while( ( testCase
= me
->caseList
) != NULL
)
11510 me
->caseList
= testCase
->next
;
11511 GAITestCaseFree( testCase
);
11515 //===========================================================================================================================
11517 //===========================================================================================================================
11519 static void _GAITesterStart( void *inContext
);
11520 static void _GAITesterStop( GAITesterRef me
, OSStatus inError
);
11522 static void GAITesterStart( GAITesterRef me
)
11525 dispatch_async_f( me
->queue
, me
, _GAITesterStart
);
11528 #define kGAITesterFirstGAITimeoutSecs 4
11530 static void _GAITesterStart( void *inContext
)
11533 GAITesterRef
const me
= (GAITesterRef
) inContext
;
11534 DNSServiceFlags flags
;
11536 char tag
[ kGAITesterTagStringLen
+ 1 ];
11538 err
= SpawnCommand( &me
->serverPID
, "dnssdutil server --loopback --follow %lld%?s%?d%?s%?d%?s",
11539 (int64_t) getpid(),
11540 me
->serverDefaultTTL
>= 0, " --defaultTTL ",
11541 me
->serverDefaultTTL
>= 0, me
->serverDefaultTTL
,
11542 me
->serverDelayMs
>= 0, " --responseDelay ",
11543 me
->serverDelayMs
>= 0, me
->serverDelayMs
,
11544 me
->badUDPMode
, " --badUDPMode" );
11545 require_noerr_quiet( err
, exit
);
11547 SNPrintF( name
, sizeof( name
), "tag-gaitester-probe-%s.ipv4.d.test",
11548 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
11551 if( me
->skipPathEval
) flags
|= kDNSServiceFlagsPathEvaluationDone
;
11553 err
= DNSServiceGetAddrInfo( &me
->getAddrInfo
, flags
, kDNSServiceInterfaceIndexAny
, kDNSServiceProtocol_IPv4
, name
,
11554 _GAITesterFirstGAICallback
, me
);
11555 require_noerr( err
, exit
);
11557 err
= DNSServiceSetDispatchQueue( me
->getAddrInfo
, me
->queue
);
11558 require_noerr( err
, exit
);
11560 err
= DispatchTimerOneShotCreate( dispatch_time_seconds( kGAITesterFirstGAITimeoutSecs
),
11561 UINT64_C_safe( kGAITesterFirstGAITimeoutSecs
) * kNanosecondsPerSecond
/ 10, me
->queue
,
11562 _GAITesterFirstGAITimeout
, me
, &me
->timer
);
11563 require_noerr( err
, exit
);
11564 dispatch_resume( me
->timer
);
11567 if( err
) _GAITesterStop( me
, err
);
11570 //===========================================================================================================================
11572 //===========================================================================================================================
11574 static void _GAITesterUserStop( void *inContext
);
11576 static void GAITesterStop( GAITesterRef me
)
11579 dispatch_async_f( me
->queue
, me
, _GAITesterUserStop
);
11582 static void _GAITesterUserStop( void *inContext
)
11584 GAITesterRef
const me
= (GAITesterRef
) inContext
;
11586 _GAITesterStop( me
, kCanceledErr
);
11590 static void _GAITesterStop( GAITesterRef me
, OSStatus inError
)
11594 ForgetPacketCapture( &me
->pcap
);
11595 dispatch_source_forget( &me
->timer
);
11596 DNSServiceForget( &me
->getAddrInfo
);
11597 DNSServiceForget( &me
->connection
);
11598 if( me
->serverPID
!= -1 )
11600 err
= kill( me
->serverPID
, SIGTERM
);
11601 err
= map_global_noerr_errno( err
);
11602 check_noerr( err
);
11603 me
->serverPID
= -1;
11608 me
->stopped
= true;
11609 if( me
->stopHandler
) me
->stopHandler( me
->stopContext
, inError
);
11614 //===========================================================================================================================
11615 // GAITesterAddTestCase
11616 //===========================================================================================================================
11618 static OSStatus
GAITesterAddTestCase( GAITesterRef me
, GAITestCase
*inCase
)
11621 GAITestCase
** ptr
;
11623 require_action_quiet( inCase
->itemList
, exit
, err
= kCountErr
);
11625 for( ptr
= &me
->caseList
; *ptr
; ptr
= &( *ptr
)->next
) {}
11633 //===========================================================================================================================
11634 // GAITesterSetStopHandler
11635 //===========================================================================================================================
11637 static void GAITesterSetStopHandler( GAITesterRef me
, GAITesterStopHandler_f inStopHandler
, void *inStopContext
)
11639 me
->stopHandler
= inStopHandler
;
11640 me
->stopContext
= inStopContext
;
11643 //===========================================================================================================================
11644 // GAITesterSetResultsHandler
11645 //===========================================================================================================================
11647 static void GAITesterSetResultsHandler( GAITesterRef me
, GAITesterResultsHandler_f inResultsHandler
, void *inResultsContext
)
11649 me
->resultsHandler
= inResultsHandler
;
11650 me
->resultsContext
= inResultsContext
;
11653 //===========================================================================================================================
11654 // _GAITesterStartNextTest
11655 //===========================================================================================================================
11657 static void _GAITesterStartNextTest( GAITesterRef me
)
11660 GAITestItem
* item
;
11661 DNSServiceFlags flags
;
11662 DNSServiceProtocol protocols
;
11665 if( me
->currentItem
) me
->currentItem
= me
->currentItem
->next
;
11667 if( !me
->currentItem
)
11669 if( me
->currentCase
)
11671 // No more test items means that the current test case has completed.
11673 me
->caseEndTime
= NanoTimeGetCurrent();
11675 if( me
->resultsHandler
)
11677 size_t resultCount
, i
;
11678 GAITestItemResult
* resultArray
;
11681 for( item
= me
->currentCase
->itemList
; item
; item
= item
->next
) ++resultCount
;
11682 check( resultCount
> 0 );
11684 resultArray
= (GAITestItemResult
*) calloc( resultCount
, sizeof( *resultArray
) );
11685 require_action( resultArray
, exit
, err
= kNoMemoryErr
);
11687 item
= me
->currentCase
->itemList
;
11688 for( i
= 0; i
< resultCount
; ++i
)
11690 resultArray
[ i
].name
= item
->name
;
11691 resultArray
[ i
].connectionTimeUs
= item
->connectionTimeUs
;
11692 resultArray
[ i
].firstTimeUs
= item
->firstTimeUs
;
11693 resultArray
[ i
].timeUs
= item
->timeUs
;
11694 resultArray
[ i
].error
= item
->error
;
11697 me
->resultsHandler( me
->currentCase
->title
, me
->caseStartTime
, me
->caseEndTime
, resultArray
, resultCount
,
11698 me
->resultsContext
);
11699 ForgetMem( &resultArray
);
11702 me
->currentCase
= me
->currentCase
->next
;
11703 if( !me
->currentCase
)
11712 me
->currentCase
= me
->caseList
;
11714 require_action_quiet( me
->currentCase
->itemList
, exit
, err
= kInternalErr
);
11715 me
->currentItem
= me
->currentCase
->itemList
;
11718 item
= me
->currentItem
;
11719 check( ( item
->addressCount
>= 1 ) && ( item
->addressCount
<= 64 ) );
11721 if( !item
->wantV4
) me
->bitmapV4
= 0;
11722 else if( !item
->hasV4
) me
->bitmapV4
= 1;
11723 else if( item
->addressCount
< 64 ) me
->bitmapV4
= ( UINT64_C( 1 ) << item
->addressCount
) - 1;
11724 else me
->bitmapV4
= ~UINT64_C( 0 );
11726 if( !item
->wantV6
) me
->bitmapV6
= 0;
11727 else if( !item
->hasV6
) me
->bitmapV6
= 1;
11728 else if( item
->addressCount
< 64 ) me
->bitmapV6
= ( UINT64_C( 1 ) << item
->addressCount
) - 1;
11729 else me
->bitmapV6
= ~UINT64_C( 0 );
11730 check( ( me
->bitmapV4
!= 0 ) || ( me
->bitmapV6
!= 0 ) );
11731 me
->gotFirstResult
= false;
11733 // Perform preliminary tasks if this is the start of a new test case.
11735 if( item
== me
->currentCase
->itemList
)
11737 // Flush mDNSResponder's cache.
11739 err
= systemf( NULL
, "killall -HUP mDNSResponder" );
11740 require_noerr( err
, exit
);
11743 me
->caseStartTime
= NanoTimeGetCurrent();
11744 me
->caseEndTime
= kNanoTime_Invalid
;
11747 // Start a packet capture.
11749 check( !me
->pcap
);
11750 err
= _GAITesterCreatePacketCapture( &me
->pcap
);
11751 require_noerr( err
, exit
);
11753 // Start timer for test item's time limit.
11755 check( !me
->timer
);
11756 if( item
->timeLimitMs
> 0 )
11758 unsigned int timeLimitMs
;
11760 timeLimitMs
= item
->timeLimitMs
;
11761 if( me
->callDelayMs
> 0 ) timeLimitMs
+= (unsigned int) me
->callDelayMs
;
11762 if( me
->serverDelayMs
> 0 ) timeLimitMs
+= (unsigned int) me
->serverDelayMs
;
11764 err
= DispatchTimerCreate( dispatch_time_milliseconds( timeLimitMs
), DISPATCH_TIME_FOREVER
,
11765 ( (uint64_t) timeLimitMs
) * kNanosecondsPerMillisecond
/ 10,
11766 me
->queue
, _GAITesterTimeout
, NULL
, me
, &me
->timer
);
11767 require_noerr( err
, exit
);
11768 dispatch_resume( me
->timer
);
11771 // Call DNSServiceGetAddrInfo().
11773 if( me
->callDelayMs
> 0 ) usleep( ( (useconds_t
) me
->callDelayMs
) * kMicrosecondsPerMillisecond
);
11775 flags
= kDNSServiceFlagsShareConnection
| kDNSServiceFlagsReturnIntermediates
;
11776 if( me
->skipPathEval
) flags
|= kDNSServiceFlagsPathEvaluationDone
;
11779 if( item
->wantV4
) protocols
|= kDNSServiceProtocol_IPv4
;
11780 if( item
->wantV6
) protocols
|= kDNSServiceProtocol_IPv6
;
11782 me
->startTicks
= UpTicks();
11784 check( !me
->connection
);
11785 err
= DNSServiceCreateConnection( &me
->connection
);
11786 require_noerr( err
, exit
);
11788 err
= DNSServiceSetDispatchQueue( me
->connection
, me
->queue
);
11789 require_noerr( err
, exit
);
11791 me
->connTicks
= UpTicks();
11793 check( !me
->getAddrInfo
);
11794 me
->getAddrInfo
= me
->connection
;
11795 err
= DNSServiceGetAddrInfo( &me
->getAddrInfo
, flags
, kDNSServiceInterfaceIndexAny
, protocols
, item
->name
,
11796 _GAITesterGetAddrInfoCallback
, me
);
11797 require_noerr( err
, exit
);
11800 if( err
|| done
) _GAITesterStop( me
, err
);
11803 //===========================================================================================================================
11804 // _GAITesterCreatePacketCapture
11805 //===========================================================================================================================
11807 static OSStatus
_GAITesterCreatePacketCapture( pcap_t
**outPCap
)
11811 struct bpf_program program
;
11812 char errBuf
[ PCAP_ERRBUF_SIZE
];
11814 pcap
= pcap_create( "lo0", errBuf
);
11815 require_action_string( pcap
, exit
, err
= kUnknownErr
, errBuf
);
11817 err
= pcap_set_buffer_size( pcap
, 512 * kBytesPerKiloByte
);
11818 require_noerr_action( err
, exit
, err
= kUnknownErr
);
11820 err
= pcap_set_snaplen( pcap
, 512 );
11821 require_noerr_action( err
, exit
, err
= kUnknownErr
);
11823 err
= pcap_set_immediate_mode( pcap
, 0 );
11824 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
11826 err
= pcap_activate( pcap
);
11827 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
11829 err
= pcap_setdirection( pcap
, PCAP_D_INOUT
);
11830 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
11832 err
= pcap_setnonblock( pcap
, 1, errBuf
);
11833 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, errBuf
);
11835 err
= pcap_compile( pcap
, &program
, "udp port 53", 1, PCAP_NETMASK_UNKNOWN
);
11836 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
11838 err
= pcap_setfilter( pcap
, &program
);
11839 pcap_freecode( &program
);
11840 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
11846 if( pcap
) pcap_close( pcap
);
11850 //===========================================================================================================================
11851 // _GAITesterFirstGAITimeout
11852 //===========================================================================================================================
11854 static void _GAITesterFirstGAITimeout( void *inContext
)
11856 GAITesterRef
const me
= (GAITesterRef
) inContext
;
11858 _GAITesterStop( me
, kNoResourcesErr
);
11861 //===========================================================================================================================
11862 // _GAITesterTimeout
11863 //===========================================================================================================================
11865 static void _GAITesterTimeout( void *inContext
)
11867 GAITesterRef
const me
= (GAITesterRef
) inContext
;
11869 _GAITesterCompleteCurrentTest( me
, kTimeoutErr
);
11872 //===========================================================================================================================
11873 // _GAITesterFirstGAICallback
11874 //===========================================================================================================================
11876 static void DNSSD_API
11877 _GAITesterFirstGAICallback(
11878 DNSServiceRef inSDRef
,
11879 DNSServiceFlags inFlags
,
11880 uint32_t inInterfaceIndex
,
11881 DNSServiceErrorType inError
,
11882 const char * inHostname
,
11883 const struct sockaddr
* inSockAddr
,
11887 GAITesterRef
const me
= (GAITesterRef
) inContext
;
11890 Unused( inInterfaceIndex
);
11891 Unused( inHostname
);
11892 Unused( inSockAddr
);
11895 if( ( inFlags
& kDNSServiceFlagsAdd
) && !inError
)
11897 dispatch_source_forget( &me
->timer
);
11898 DNSServiceForget( &me
->getAddrInfo
);
11900 _GAITesterStartNextTest( me
);
11904 //===========================================================================================================================
11905 // _GAITesterGetAddrInfoCallback
11906 //===========================================================================================================================
11908 static void DNSSD_API
11909 _GAITesterGetAddrInfoCallback(
11910 DNSServiceRef inSDRef
,
11911 DNSServiceFlags inFlags
,
11912 uint32_t inInterfaceIndex
,
11913 DNSServiceErrorType inError
,
11914 const char * inHostname
,
11915 const struct sockaddr
* inSockAddr
,
11920 GAITesterRef
const me
= (GAITesterRef
) inContext
;
11921 GAITestItem
* const item
= me
->currentItem
;
11922 const sockaddr_ip
* const sip
= (const sockaddr_ip
*) inSockAddr
;
11924 uint64_t * bitmapPtr
;
11929 Unused( inInterfaceIndex
);
11930 Unused( inHostname
);
11933 nowTicks
= UpTicks();
11935 require_action_quiet( inFlags
& kDNSServiceFlagsAdd
, exit
, err
= kFlagErr
);
11937 // Check if we were expecting an IP address result of this type.
11939 if( sip
->sa
.sa_family
== AF_INET
)
11941 bitmapPtr
= &me
->bitmapV4
;
11942 hasAddr
= item
->hasV4
;
11944 else if( sip
->sa
.sa_family
== AF_INET6
)
11946 bitmapPtr
= &me
->bitmapV6
;
11947 hasAddr
= item
->hasV6
;
11958 uint32_t addrOffset
;
11960 require_noerr_action_quiet( inError
, exit
, err
= inError
);
11962 if( sip
->sa
.sa_family
== AF_INET
)
11964 const uint32_t addrV4
= ntohl( sip
->v4
.sin_addr
.s_addr
);
11966 if( strcasecmp( item
->name
, "localhost." ) == 0 )
11968 if( addrV4
== INADDR_LOOPBACK
) bitmask
= 1;
11972 addrOffset
= addrV4
- kDNSServerBaseAddrV4
;
11973 if( ( addrOffset
>= 1 ) && ( addrOffset
<= item
->addressCount
) )
11975 bitmask
= UINT64_C( 1 ) << ( addrOffset
- 1 );
11981 const uint8_t * const addrV6
= sip
->v6
.sin6_addr
.s6_addr
;
11983 if( strcasecmp( item
->name
, "localhost." ) == 0 )
11985 if( memcmp( addrV6
, in6addr_loopback
.s6_addr
, 16 ) == 0 ) bitmask
= 1;
11987 else if( memcmp( addrV6
, kDNSServerBaseAddrV6
, 15 ) == 0 )
11989 addrOffset
= addrV6
[ 15 ];
11990 if( ( addrOffset
>= 1 ) && ( addrOffset
<= item
->addressCount
) )
11992 bitmask
= UINT64_C( 1 ) << ( addrOffset
- 1 );
11999 require_action_quiet( inError
== kDNSServiceErr_NoSuchRecord
, exit
, err
= inError
? inError
: kUnexpectedErr
);
12002 require_action_quiet( bitmask
!= 0, exit
, err
= kValueErr
);
12003 require_action_quiet( *bitmapPtr
& bitmask
, exit
, err
= kDuplicateErr
);
12005 *bitmapPtr
&= ~bitmask
;
12006 if( !me
->gotFirstResult
)
12008 me
->firstTicks
= nowTicks
;
12009 me
->gotFirstResult
= true;
12014 if( err
|| ( ( me
->bitmapV4
== 0 ) && ( me
->bitmapV6
== 0 ) ) )
12016 me
->endTicks
= nowTicks
;
12017 _GAITesterCompleteCurrentTest( me
, err
);
12021 //===========================================================================================================================
12022 // _GAITesterCompleteCurrentTest
12023 //===========================================================================================================================
12026 _GAITesterGetDNSMessageFromPacket(
12027 const uint8_t * inPacketPtr
,
12028 size_t inPacketLen
,
12029 const uint8_t ** outMsgPtr
,
12030 size_t * outMsgLen
);
12032 static void _GAITesterCompleteCurrentTest( GAITesterRef me
, OSStatus inError
)
12035 GAITestItem
* const item
= me
->currentItem
;
12036 struct timeval timeStamps
[ 4 ];
12037 struct timeval
* tsPtr
;
12038 struct timeval
* tsQA
= NULL
;
12039 struct timeval
* tsQAAAA
= NULL
;
12040 struct timeval
* tsRA
= NULL
;
12041 struct timeval
* tsRAAAA
= NULL
;
12042 struct timeval
* t1
;
12043 struct timeval
* t2
;
12044 int64_t idleTimeUs
;
12045 uint8_t name
[ kDomainNameLengthMax
];
12047 dispatch_source_forget( &me
->timer
);
12048 DNSServiceForget( &me
->getAddrInfo
);
12049 DNSServiceForget( &me
->connection
);
12051 item
->error
= inError
;
12058 err
= DomainNameFromString( name
, item
->name
, NULL
);
12059 require_noerr( err
, exit
);
12061 tsPtr
= &timeStamps
[ 0 ];
12065 struct pcap_pkthdr
* pktHdr
;
12066 const uint8_t * packet
;
12067 const uint8_t * msgPtr
;
12069 const DNSHeader
* hdr
;
12070 unsigned int flags
;
12071 const uint8_t * ptr
;
12072 uint16_t qtype
, qclass
;
12073 uint8_t qname
[ kDomainNameLengthMax
];
12075 status
= pcap_next_ex( me
->pcap
, &pktHdr
, &packet
);
12076 if( status
!= 1 ) break;
12077 if( _GAITesterGetDNSMessageFromPacket( packet
, pktHdr
->caplen
, &msgPtr
, &msgLen
) != kNoErr
) continue;
12078 if( msgLen
< kDNSHeaderLength
) continue;
12080 hdr
= (const DNSHeader
*) msgPtr
;
12081 flags
= DNSHeaderGetFlags( hdr
);
12082 if( DNSFlagsGetOpCode( flags
) != kDNSOpCode_Query
) continue;
12083 if( DNSHeaderGetQuestionCount( hdr
) < 1 ) continue;
12085 ptr
= (const uint8_t *) &hdr
[ 1 ];
12086 if( DNSMessageExtractQuestion( msgPtr
, msgLen
, ptr
, qname
, &qtype
, &qclass
, NULL
) != kNoErr
) continue;
12087 if( qclass
!= kDNSServiceClass_IN
) continue;
12088 if( !DomainNameEqual( qname
, name
) ) continue;
12090 if( item
->wantV4
&& ( qtype
== kDNSServiceType_A
) )
12092 if( flags
& kDNSHeaderFlag_Response
)
12094 if( tsQA
&& !tsRA
)
12097 *tsRA
= pktHdr
->ts
;
12103 *tsQA
= pktHdr
->ts
;
12106 else if( item
->wantV6
&& ( qtype
== kDNSServiceType_AAAA
) )
12108 if( flags
& kDNSHeaderFlag_Response
)
12110 if( tsQAAAA
&& !tsRAAAA
)
12113 *tsRAAAA
= pktHdr
->ts
;
12116 else if( !tsQAAAA
)
12119 *tsQAAAA
= pktHdr
->ts
;
12124 // t1 is the time when the last query was sent.
12126 if( tsQA
&& tsQAAAA
) t1
= TIMEVAL_GT( *tsQA
, *tsQAAAA
) ? tsQA
: tsQAAAA
;
12127 else t1
= tsQA
? tsQA
: tsQAAAA
;
12129 // t2 is when the first response was received.
12131 if( tsRA
&& tsRAAAA
) t2
= TIMEVAL_LT( *tsRA
, *tsRAAAA
) ? tsRA
: tsRAAAA
;
12132 else t2
= tsRA
? tsRA
: tsRAAAA
;
12136 idleTimeUs
= TIMEVAL_USEC64_DIFF( *t2
, *t1
);
12137 if( idleTimeUs
< 0 ) idleTimeUs
= 0;
12144 item
->connectionTimeUs
= UpTicksToMicroseconds( me
->connTicks
- me
->startTicks
);
12145 item
->firstTimeUs
= UpTicksToMicroseconds( me
->firstTicks
- me
->connTicks
) - (uint64_t) idleTimeUs
;
12146 item
->timeUs
= UpTicksToMicroseconds( me
->endTicks
- me
->connTicks
) - (uint64_t) idleTimeUs
;
12149 ForgetPacketCapture( &me
->pcap
);
12150 if( err
) _GAITesterStop( me
, err
);
12151 else _GAITesterStartNextTest( me
);
12154 //===========================================================================================================================
12155 // _GAITesterGetDNSMessageFromPacket
12156 //===========================================================================================================================
12158 #define kHeaderSizeNullLink 4
12159 #define kHeaderSizeIPv4Min 20
12160 #define kHeaderSizeIPv6 40
12161 #define kHeaderSizeUDP 8
12163 #define kIPProtocolUDP 0x11
12166 _GAITesterGetDNSMessageFromPacket(
12167 const uint8_t * inPacketPtr
,
12168 size_t inPacketLen
,
12169 const uint8_t ** outMsgPtr
,
12170 size_t * outMsgLen
)
12173 const uint8_t * nullLink
;
12174 uint32_t addressFamily
;
12175 const uint8_t * ip
;
12178 const uint8_t * msg
;
12179 const uint8_t * const end
= &inPacketPtr
[ inPacketLen
];
12181 nullLink
= &inPacketPtr
[ 0 ];
12182 require_action_quiet( ( end
- nullLink
) >= kHeaderSizeNullLink
, exit
, err
= kUnderrunErr
);
12183 addressFamily
= ReadHost32( &nullLink
[ 0 ] );
12185 ip
= &nullLink
[ kHeaderSizeNullLink
];
12186 if( addressFamily
== AF_INET
)
12188 require_action_quiet( ( end
- ip
) >= kHeaderSizeIPv4Min
, exit
, err
= kUnderrunErr
);
12189 ipHeaderLen
= ( ip
[ 0 ] & 0x0F ) * 4;
12190 protocol
= ip
[ 9 ];
12192 else if( addressFamily
== AF_INET6
)
12194 require_action_quiet( ( end
- ip
) >= kHeaderSizeIPv6
, exit
, err
= kUnderrunErr
);
12195 ipHeaderLen
= kHeaderSizeIPv6
;
12196 protocol
= ip
[ 6 ];
12203 require_action_quiet( protocol
== kIPProtocolUDP
, exit
, err
= kTypeErr
);
12204 require_action_quiet( ( end
- ip
) >= ( ipHeaderLen
+ kHeaderSizeUDP
), exit
, err
= kUnderrunErr
);
12206 msg
= &ip
[ ipHeaderLen
+ kHeaderSizeUDP
];
12209 *outMsgLen
= (size_t)( end
- msg
);
12216 //===========================================================================================================================
12217 // GAITestCaseCreate
12218 //===========================================================================================================================
12220 static OSStatus
GAITestCaseCreate( const char *inTitle
, GAITestCase
**outCase
)
12225 obj
= (GAITestCase
*) calloc( 1, sizeof( *obj
) );
12226 require_action( obj
, exit
, err
= kNoMemoryErr
);
12228 obj
->title
= strdup( inTitle
);
12229 require_action( obj
->title
, exit
, err
= kNoMemoryErr
);
12236 if( obj
) GAITestCaseFree( obj
);
12240 //===========================================================================================================================
12242 //===========================================================================================================================
12244 static void GAITestCaseFree( GAITestCase
*inCase
)
12246 GAITestItem
* item
;
12248 while( ( item
= inCase
->itemList
) != NULL
)
12250 inCase
->itemList
= item
->next
;
12251 GAITestItemFree( item
);
12253 ForgetMem( &inCase
->title
);
12257 //===========================================================================================================================
12258 // GAITestCaseAddItem
12259 //===========================================================================================================================
12262 GAITestCaseAddItem(
12263 GAITestCase
* inCase
,
12264 unsigned int inAliasCount
,
12265 unsigned int inAddressCount
,
12267 GAITestAddrType inHasAddrs
,
12268 GAITestAddrType inWantAddrs
,
12269 unsigned int inTimeLimitMs
,
12270 unsigned int inItemCount
)
12273 GAITestItem
* item
;
12274 GAITestItem
* item2
;
12275 GAITestItem
* newItemList
= NULL
;
12276 GAITestItem
** itemPtr
;
12281 char tag
[ kGAITesterTagStringLen
+ 1 ];
12283 require_action_quiet( inItemCount
> 0, exit
, err
= kNoErr
);
12285 // Limit address count to 64 because we use 64-bit bitmaps for keeping track of addresses.
12287 require_action_quiet( ( inAddressCount
>= 1 ) && ( inAddressCount
<= 64 ), exit
, err
= kCountErr
);
12288 require_action_quiet( ( inAliasCount
>= 0 ) && ( inAliasCount
<= INT32_MAX
), exit
, err
= kCountErr
);
12289 require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs
), exit
, err
= kValueErr
);
12292 end
= &name
[ countof( name
) ];
12294 // Add Alias label.
12296 if( inAliasCount
== 1 ) SNPrintF_Add( &ptr
, end
, "alias." );
12297 else if( inAliasCount
>= 2 ) SNPrintF_Add( &ptr
, end
, "alias-%u.", inAliasCount
);
12299 // Add Count label.
12301 SNPrintF_Add( &ptr
, end
, "count-%u.", inAddressCount
);
12305 if( inTTL
>= 0 ) SNPrintF_Add( &ptr
, end
, "ttl-%d.", inTTL
);
12309 SNPrintF_Add( &ptr
, end
, "tag-%s.",
12310 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
12312 // Add IPv4 or IPv6 label if necessary.
12314 if( inHasAddrs
== kGAITestAddrType_IPv4
) SNPrintF_Add( &ptr
, end
, "ipv4." );
12315 else if( inHasAddrs
== kGAITestAddrType_IPv6
) SNPrintF_Add( &ptr
, end
, "ipv6." );
12317 // Finally, add the d.test. labels.
12319 SNPrintF_Add( &ptr
, end
, "d.test." );
12323 err
= GAITestItemCreate( name
, inAddressCount
, inHasAddrs
, inWantAddrs
, inTimeLimitMs
, &item
);
12324 require_noerr( err
, exit
);
12326 newItemList
= item
;
12327 itemPtr
= &item
->next
;
12329 // Create repeat items.
12331 for( i
= 1; i
< inItemCount
; ++i
)
12333 err
= GAITestItemDup( item
, &item2
);
12334 require_noerr( err
, exit
);
12337 itemPtr
= &item2
->next
;
12340 // Append to test case's item list.
12342 for( itemPtr
= &inCase
->itemList
; *itemPtr
; itemPtr
= &( *itemPtr
)->next
) {}
12343 *itemPtr
= newItemList
;
12344 newItemList
= NULL
;
12347 while( ( item
= newItemList
) != NULL
)
12349 newItemList
= item
->next
;
12350 GAITestItemFree( item
);
12355 //===========================================================================================================================
12356 // GAITestCaseAddLocalHostItem
12357 //===========================================================================================================================
12360 GAITestCaseAddLocalHostItem(
12361 GAITestCase
* inCase
,
12362 GAITestAddrType inWantAddrs
,
12363 unsigned int inTimeLimitMs
,
12364 unsigned int inItemCount
)
12367 GAITestItem
* item
;
12368 GAITestItem
* item2
;
12369 GAITestItem
* newItemList
= NULL
;
12370 GAITestItem
** itemPtr
;
12373 require_action_quiet( inItemCount
> 1, exit
, err
= kNoErr
);
12375 err
= GAITestItemCreate( "localhost.", 1, kGAITestAddrType_Both
, inWantAddrs
, inTimeLimitMs
, &item
);
12376 require_noerr( err
, exit
);
12378 newItemList
= item
;
12379 itemPtr
= &item
->next
;
12381 // Create repeat items.
12383 for( i
= 1; i
< inItemCount
; ++i
)
12385 err
= GAITestItemDup( item
, &item2
);
12386 require_noerr( err
, exit
);
12389 itemPtr
= &item2
->next
;
12392 for( itemPtr
= &inCase
->itemList
; *itemPtr
; itemPtr
= &( *itemPtr
)->next
) {}
12393 *itemPtr
= newItemList
;
12394 newItemList
= NULL
;
12397 while( ( item
= newItemList
) != NULL
)
12399 newItemList
= item
->next
;
12400 GAITestItemFree( item
);
12405 //===========================================================================================================================
12406 // GAITestItemCreate
12407 //===========================================================================================================================
12411 const char * inName
,
12412 unsigned int inAddressCount
,
12413 GAITestAddrType inHasAddrs
,
12414 GAITestAddrType inWantAddrs
,
12415 unsigned int inTimeLimitMs
,
12416 GAITestItem
** outItem
)
12419 GAITestItem
* obj
= NULL
;
12421 require_action_quiet( inAddressCount
>= 1, exit
, err
= kCountErr
);
12422 require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs
), exit
, err
= kValueErr
);
12423 require_action_quiet( GAITestAddrTypeIsValid( inWantAddrs
), exit
, err
= kValueErr
);
12425 obj
= (GAITestItem
*) calloc( 1, sizeof( *obj
) );
12426 require_action( obj
, exit
, err
= kNoMemoryErr
);
12428 obj
->name
= strdup( inName
);
12429 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
12431 obj
->addressCount
= inAddressCount
;
12432 obj
->hasV4
= ( inHasAddrs
& kGAITestAddrType_IPv4
) ? true : false;
12433 obj
->hasV6
= ( inHasAddrs
& kGAITestAddrType_IPv6
) ? true : false;
12434 obj
->wantV4
= ( inWantAddrs
& kGAITestAddrType_IPv4
) ? true : false;
12435 obj
->wantV6
= ( inWantAddrs
& kGAITestAddrType_IPv6
) ? true : false;
12436 obj
->error
= kInProgressErr
;
12437 obj
->timeLimitMs
= inTimeLimitMs
;
12444 if( obj
) GAITestItemFree( obj
);
12448 //===========================================================================================================================
12450 //===========================================================================================================================
12452 static OSStatus
GAITestItemDup( const GAITestItem
*inItem
, GAITestItem
**outItem
)
12457 obj
= (GAITestItem
*) calloc( 1, sizeof( *obj
) );
12458 require_action( obj
, exit
, err
= kNoMemoryErr
);
12464 obj
->name
= strdup( inItem
->name
);
12465 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
12473 if( obj
) GAITestItemFree( obj
);
12477 //===========================================================================================================================
12479 //===========================================================================================================================
12481 static void GAITestItemFree( GAITestItem
*inItem
)
12483 ForgetMem( &inItem
->name
);
12487 //===========================================================================================================================
12488 // MDNSDiscoveryTestCmd
12489 //===========================================================================================================================
12491 #define kMDNSDiscoveryTestFirstQueryTimeoutSecs 4
12495 DNSServiceRef query
; // Reference to DNSServiceQueryRecord for replier's "about" TXT record.
12496 dispatch_source_t queryTimer
; // Used to time out the "about" TXT record query.
12497 NanoTime64 startTime
; // When the test started.
12498 NanoTime64 endTime
; // When the test ended.
12499 pid_t replierPID
; // PID of mDNS replier.
12500 uint32_t ifIndex
; // Index of interface to run the replier on.
12501 unsigned int instanceCount
; // Desired number of service instances.
12502 unsigned int txtSize
; // Desired size of each service instance's TXT record data.
12503 unsigned int recordCountA
; // Desired number of A records per replier hostname.
12504 unsigned int recordCountAAAA
; // Desired number of AAAA records per replier hostname.
12505 unsigned int maxDropCount
; // Replier's --maxDropCount option argument.
12506 double ucastDropRate
; // Replier's probability of dropping a unicast response.
12507 double mcastDropRate
; // Replier's probability of dropping a multicast query or response.
12508 Boolean noAdditionals
; // True if the replier is to not include additional records in responses.
12509 Boolean useIPv4
; // True if the replier is to use IPv4.
12510 Boolean useIPv6
; // True if the replier is to use IPv6.
12511 Boolean flushedCache
; // True if mDNSResponder's record cache was flushed before testing.
12512 char * replierCommand
; // Command used to run the replier.
12513 char * serviceType
; // Type of services to browse for.
12514 ServiceBrowserRef browser
; // Service browser.
12515 unsigned int browseTimeSecs
; // Amount of time to spend browsing in seconds.
12516 const char * outputFilePath
; // File to write test results to. If NULL, then write to stdout.
12517 OutputFormatType outputFormat
; // Format of test results output.
12518 Boolean outputAppendNewline
; // True if a newline character should be appended to JSON output.
12519 char hostname
[ 32 + 1 ]; // Base hostname that the replier is to use for instance and host names.
12520 char tag
[ 4 + 1 ]; // Tag that the replier is to use in its service types.
12522 } MDNSDiscoveryTestContext
;
12524 static void _MDNSDiscoveryTestFirstQueryTimeout( void *inContext
);
12525 static void DNSSD_API
12526 _MDNSDiscoveryTestAboutQueryCallback(
12527 DNSServiceRef inSDRef
,
12528 DNSServiceFlags inFlags
,
12529 uint32_t inInterfaceIndex
,
12530 DNSServiceErrorType inError
,
12531 const char * inFullName
,
12534 uint16_t inRDataLen
,
12535 const void * inRDataPtr
,
12537 void * inContext
);
12539 _MDNSDiscoveryTestServiceBrowserCallback(
12540 ServiceBrowserResults
* inResults
,
12542 void * inContext
);
12543 static Boolean
_MDNSDiscoveryTestTXTRecordIsValid( const uint8_t *inRecordName
, const uint8_t *inTXTPtr
, size_t inTXTLen
);
12545 static void MDNSDiscoveryTestCmd( void )
12548 MDNSDiscoveryTestContext
* context
;
12549 char queryName
[ sizeof_field( MDNSDiscoveryTestContext
, hostname
) + 15 ];
12551 context
= (MDNSDiscoveryTestContext
*) calloc( 1, sizeof( *context
) );
12552 require_action( context
, exit
, err
= kNoMemoryErr
);
12554 err
= CheckIntegerArgument( gMDNSDiscoveryTest_InstanceCount
, "instance count", 1, UINT16_MAX
);
12555 require_noerr_quiet( err
, exit
);
12557 err
= CheckIntegerArgument( gMDNSDiscoveryTest_TXTSize
, "TXT size", 1, UINT16_MAX
);
12558 require_noerr_quiet( err
, exit
);
12560 err
= CheckIntegerArgument( gMDNSDiscoveryTest_BrowseTimeSecs
, "browse time (seconds)", 1, INT_MAX
);
12561 require_noerr_quiet( err
, exit
);
12563 err
= CheckIntegerArgument( gMDNSDiscoveryTest_RecordCountA
, "A record count", 0, 64 );
12564 require_noerr_quiet( err
, exit
);
12566 err
= CheckIntegerArgument( gMDNSDiscoveryTest_RecordCountAAAA
, "AAAA record count", 0, 64 );
12567 require_noerr_quiet( err
, exit
);
12569 err
= CheckDoubleArgument( gMDNSDiscoveryTest_UnicastDropRate
, "unicast drop rate", 0.0, 1.0 );
12570 require_noerr_quiet( err
, exit
);
12572 err
= CheckDoubleArgument( gMDNSDiscoveryTest_MulticastDropRate
, "multicast drop rate", 0.0, 1.0 );
12573 require_noerr_quiet( err
, exit
);
12575 err
= CheckIntegerArgument( gMDNSDiscoveryTest_MaxDropCount
, "drop count", 0, 255 );
12576 require_noerr_quiet( err
, exit
);
12578 context
->replierPID
= -1;
12579 context
->instanceCount
= (unsigned int) gMDNSDiscoveryTest_InstanceCount
;
12580 context
->txtSize
= (unsigned int) gMDNSDiscoveryTest_TXTSize
;
12581 context
->browseTimeSecs
= (unsigned int) gMDNSDiscoveryTest_BrowseTimeSecs
;
12582 context
->recordCountA
= (unsigned int) gMDNSDiscoveryTest_RecordCountA
;
12583 context
->recordCountAAAA
= (unsigned int) gMDNSDiscoveryTest_RecordCountAAAA
;
12584 context
->ucastDropRate
= gMDNSDiscoveryTest_UnicastDropRate
;
12585 context
->mcastDropRate
= gMDNSDiscoveryTest_MulticastDropRate
;
12586 context
->maxDropCount
= (unsigned int) gMDNSDiscoveryTest_MaxDropCount
;
12587 context
->outputFilePath
= gMDNSDiscoveryTest_OutputFilePath
;
12588 context
->outputAppendNewline
= gMDNSDiscoveryTest_OutputAppendNewline
? true : false;
12589 context
->noAdditionals
= gMDNSDiscoveryTest_NoAdditionals
? true : false;
12590 context
->useIPv4
= ( gMDNSDiscoveryTest_UseIPv4
|| !gMDNSDiscoveryTest_UseIPv6
) ? true : false;
12591 context
->useIPv6
= ( gMDNSDiscoveryTest_UseIPv6
|| !gMDNSDiscoveryTest_UseIPv4
) ? true : false;
12593 if( gMDNSDiscoveryTest_Interface
)
12595 err
= InterfaceIndexFromArgString( gMDNSDiscoveryTest_Interface
, &context
->ifIndex
);
12596 require_noerr_quiet( err
, exit
);
12600 err
= _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All
, NULL
, &context
->ifIndex
);
12601 require_noerr_quiet( err
, exit
);
12604 err
= OutputFormatFromArgString( gMDNSDiscoveryTest_OutputFormat
, &context
->outputFormat
);
12605 require_noerr_quiet( err
, exit
);
12607 if( gMDNSDiscoveryTest_FlushCache
)
12609 err
= CheckRootUser();
12610 require_noerr_quiet( err
, exit
);
12612 err
= systemf( NULL
, "killall -HUP mDNSResponder" );
12613 require_noerr( err
, exit
);
12615 context
->flushedCache
= true;
12618 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( context
->hostname
) - 1,
12619 context
->hostname
);
12620 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( context
->tag
) - 1, context
->tag
);
12622 ASPrintF( &context
->serviceType
, "_t-%s-%u-%u._tcp", context
->tag
, context
->txtSize
, context
->instanceCount
);
12623 require_action( context
->serviceType
, exit
, err
= kUnknownErr
);
12625 ASPrintF( &context
->replierCommand
,
12626 "dnssdutil mdnsreplier --follow %lld --interface %u --hostname %s --tag %s --maxInstanceCount %u "
12627 "--countA %u --countAAAA %u --udrop %.1f --mdrop %.1f --maxDropCount %u %?s%?s%?s",
12628 (int64_t) getpid(),
12632 context
->instanceCount
,
12633 context
->recordCountA
,
12634 context
->recordCountAAAA
,
12635 context
->ucastDropRate
,
12636 context
->mcastDropRate
,
12637 context
->maxDropCount
,
12638 context
->noAdditionals
, " --noAdditionals",
12639 context
->useIPv4
, " --ipv4",
12640 context
->useIPv6
, " --ipv6" );
12641 require_action_quiet( context
->replierCommand
, exit
, err
= kUnknownErr
);
12643 err
= SpawnCommand( &context
->replierPID
, "%s", context
->replierCommand
);
12644 require_noerr_quiet( err
, exit
);
12646 // Query for the replier's about TXT record. A response means it's fully up and running.
12648 SNPrintF( queryName
, sizeof( queryName
), "about.%s.local.", context
->hostname
);
12649 err
= DNSServiceQueryRecord( &context
->query
, kDNSServiceFlagsForceMulticast
, context
->ifIndex
, queryName
,
12650 kDNSServiceType_TXT
, kDNSServiceClass_IN
, _MDNSDiscoveryTestAboutQueryCallback
, context
);
12651 require_noerr( err
, exit
);
12653 err
= DNSServiceSetDispatchQueue( context
->query
, dispatch_get_main_queue() );
12654 require_noerr( err
, exit
);
12656 err
= DispatchTimerCreate( dispatch_time_seconds( kMDNSDiscoveryTestFirstQueryTimeoutSecs
),
12657 DISPATCH_TIME_FOREVER
, UINT64_C_safe( kMDNSDiscoveryTestFirstQueryTimeoutSecs
) * kNanosecondsPerSecond
/ 10, NULL
,
12658 _MDNSDiscoveryTestFirstQueryTimeout
, NULL
, context
, &context
->queryTimer
);
12659 require_noerr( err
, exit
);
12660 dispatch_resume( context
->queryTimer
);
12662 context
->startTime
= NanoTimeGetCurrent();
12669 //===========================================================================================================================
12670 // _MDNSDiscoveryTestFirstQueryTimeout
12671 //===========================================================================================================================
12673 static void _MDNSDiscoveryTestFirstQueryTimeout( void *inContext
)
12675 MDNSDiscoveryTestContext
* const context
= (MDNSDiscoveryTestContext
*) inContext
;
12677 dispatch_source_forget( &context
->queryTimer
);
12679 FPrintF( stderr
, "error: Query for mdnsreplier's \"about\" TXT record timed out.\n" );
12683 //===========================================================================================================================
12684 // _MDNSDiscoveryTestAboutQueryCallback
12685 //===========================================================================================================================
12687 static void DNSSD_API
12688 _MDNSDiscoveryTestAboutQueryCallback(
12689 DNSServiceRef inSDRef
,
12690 DNSServiceFlags inFlags
,
12691 uint32_t inInterfaceIndex
,
12692 DNSServiceErrorType inError
,
12693 const char * inFullName
,
12696 uint16_t inRDataLen
,
12697 const void * inRDataPtr
,
12702 MDNSDiscoveryTestContext
* const context
= (MDNSDiscoveryTestContext
*) inContext
;
12705 Unused( inInterfaceIndex
);
12706 Unused( inFullName
);
12709 Unused( inRDataLen
);
12710 Unused( inRDataPtr
);
12714 require_noerr( err
, exit
);
12715 require_quiet( inFlags
& kDNSServiceFlagsAdd
, exit
);
12717 DNSServiceForget( &context
->query
);
12718 dispatch_source_forget( &context
->queryTimer
);
12720 err
= ServiceBrowserCreate( dispatch_get_main_queue(), 0, "local.", context
->browseTimeSecs
, false, &context
->browser
);
12721 require_noerr( err
, exit
);
12723 err
= ServiceBrowserAddServiceType( context
->browser
, context
->serviceType
);
12724 require_noerr( err
, exit
);
12726 ServiceBrowserSetCallback( context
->browser
, _MDNSDiscoveryTestServiceBrowserCallback
, context
);
12727 ServiceBrowserStart( context
->browser
);
12730 if( err
) exit( 1 );
12733 //===========================================================================================================================
12734 // _MDNSDiscoveryTestServiceBrowserCallback
12735 //===========================================================================================================================
12737 #define kMDNSDiscoveryTestResultsKey_ReplierInfo CFSTR( "replierInfo" )
12738 #define kMDNSDiscoveryTestResultsKey_StartTime CFSTR( "startTime" )
12739 #define kMDNSDiscoveryTestResultsKey_EndTime CFSTR( "endTime" )
12740 #define kMDNSDiscoveryTestResultsKey_BrowseTimeSecs CFSTR( "browseTimeSecs" )
12741 #define kMDNSDiscoveryTestResultsKey_ServiceType CFSTR( "serviceType" )
12742 #define kMDNSDiscoveryTestResultsKey_FlushedCache CFSTR( "flushedCache" )
12743 #define kMDNSDiscoveryTestResultsKey_UnexpectedInstances CFSTR( "unexpectedInstances" )
12744 #define kMDNSDiscoveryTestResultsKey_MissingInstances CFSTR( "missingInstances" )
12745 #define kMDNSDiscoveryTestResultsKey_IncorrectInstances CFSTR( "incorrectInstances" )
12746 #define kMDNSDiscoveryTestResultsKey_Success CFSTR( "success" )
12747 #define kMDNSDiscoveryTestResultsKey_TotalResolveTime CFSTR( "totalResolveTimeUs" )
12749 #define kMDNSDiscoveryTestReplierInfoKey_Command CFSTR( "command" )
12750 #define kMDNSDiscoveryTestReplierInfoKey_InstanceCount CFSTR( "instanceCount" )
12751 #define kMDNSDiscoveryTestReplierInfoKey_TXTSize CFSTR( "txtSize" )
12752 #define kMDNSDiscoveryTestReplierInfoKey_RecordCountA CFSTR( "recordCountA" )
12753 #define kMDNSDiscoveryTestReplierInfoKey_RecordCountAAAA CFSTR( "recordCountAAAA" )
12754 #define kMDNSDiscoveryTestReplierInfoKey_Hostname CFSTR( "hostname" )
12755 #define kMDNSDiscoveryTestReplierInfoKey_NoAdditionals CFSTR( "noAdditionals" )
12756 #define kMDNSDiscoveryTestReplierInfoKey_UnicastDropRate CFSTR( "ucastDropRate" )
12757 #define kMDNSDiscoveryTestReplierInfoKey_MulticastDropRate CFSTR( "mcastDropRate" )
12758 #define kMDNSDiscoveryTestReplierInfoKey_MaxDropCount CFSTR( "maxDropCount" )
12760 #define kMDNSDiscoveryTestUnexpectedInstanceKey_Name CFSTR( "name" )
12761 #define kMDNSDiscoveryTestUnexpectedInstanceKey_InterfaceIndex CFSTR( "interfaceIndex" )
12763 #define kMDNSDiscoveryTestIncorrectInstanceKey_Name CFSTR( "name" )
12764 #define kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve CFSTR( "didResolve" )
12765 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadHostname CFSTR( "badHostname" )
12766 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadPort CFSTR( "badPort" )
12767 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadTXT CFSTR( "badTXT" )
12768 #define kMDNSDiscoveryTestIncorrectInstanceKey_UnexpectedAddrs CFSTR( "unexpectedAddrs" )
12769 #define kMDNSDiscoveryTestIncorrectInstanceKey_MissingAddrs CFSTR( "missingAddrs" )
12771 static void _MDNSDiscoveryTestServiceBrowserCallback( ServiceBrowserResults
*inResults
, OSStatus inError
, void *inContext
)
12774 MDNSDiscoveryTestContext
* const context
= (MDNSDiscoveryTestContext
*) inContext
;
12775 const SBRDomain
* domain
;
12776 const SBRServiceType
* type
;
12777 const SBRServiceInstance
* instance
;
12778 const SBRServiceInstance
** instanceArray
= NULL
;
12779 const SBRIPAddress
* ipaddr
;
12780 size_t hostnameLen
;
12785 CFMutableArrayRef unexpectedInstances
;
12786 CFMutableArrayRef missingInstances
;
12787 CFMutableArrayRef incorrectInstances
;
12788 CFMutableDictionaryRef plist
= NULL
;
12789 CFMutableDictionaryRef badDict
= NULL
;
12790 CFMutableArrayRef unexpectedAddrs
= NULL
;
12791 CFMutableArrayRef missingAddrs
= NULL
;
12792 uint64_t maxResolveTimeUs
;
12793 int success
= false;
12794 char startTime
[ 32 ];
12795 char endTime
[ 32 ];
12797 context
->endTime
= NanoTimeGetCurrent();
12800 require_noerr( err
, exit
);
12802 _NanoTime64ToTimestamp( context
->startTime
, startTime
, sizeof( startTime
) );
12803 _NanoTime64ToTimestamp( context
->endTime
, endTime
, sizeof( endTime
) );
12804 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
12808 "%kO=%s" // replierCommand
12809 "%kO=%lli" // txtSize
12810 "%kO=%lli" // instanceCount
12811 "%kO=%lli" // recordCountA
12812 "%kO=%lli" // recordCountAAAA
12813 "%kO=%s" // hostname
12814 "%kO=%b" // noAdditionals
12815 "%kO=%f" // ucastDropRate
12816 "%kO=%f" // mcastDropRate
12817 "%kO=%i" // maxDropCount
12819 "%kO=%s" // startTime
12820 "%kO=%s" // endTime
12821 "%kO=%lli" // browseTimeSecs
12822 "%kO=%s" // serviceType
12823 "%kO=%b" // flushedCache
12824 "%kO=[%@]" // unexpectedInstances
12825 "%kO=[%@]" // missingInstances
12826 "%kO=[%@]" // incorrectInstances
12828 kMDNSDiscoveryTestResultsKey_ReplierInfo
,
12829 kMDNSDiscoveryTestReplierInfoKey_Command
, context
->replierCommand
,
12830 kMDNSDiscoveryTestReplierInfoKey_InstanceCount
, (int64_t) context
->instanceCount
,
12831 kMDNSDiscoveryTestReplierInfoKey_TXTSize
, (int64_t) context
->txtSize
,
12832 kMDNSDiscoveryTestReplierInfoKey_RecordCountA
, (int64_t) context
->recordCountA
,
12833 kMDNSDiscoveryTestReplierInfoKey_RecordCountAAAA
, (int64_t) context
->recordCountAAAA
,
12834 kMDNSDiscoveryTestReplierInfoKey_Hostname
, context
->hostname
,
12835 kMDNSDiscoveryTestReplierInfoKey_NoAdditionals
, context
->noAdditionals
,
12836 kMDNSDiscoveryTestReplierInfoKey_UnicastDropRate
, context
->ucastDropRate
,
12837 kMDNSDiscoveryTestReplierInfoKey_MulticastDropRate
, context
->mcastDropRate
,
12838 kMDNSDiscoveryTestReplierInfoKey_MaxDropCount
, context
->maxDropCount
,
12839 kMDNSDiscoveryTestResultsKey_StartTime
, startTime
,
12840 kMDNSDiscoveryTestResultsKey_EndTime
, endTime
,
12841 kMDNSDiscoveryTestResultsKey_BrowseTimeSecs
, (int64_t) context
->browseTimeSecs
,
12842 kMDNSDiscoveryTestResultsKey_ServiceType
, context
->serviceType
,
12843 kMDNSDiscoveryTestResultsKey_FlushedCache
, context
->flushedCache
,
12844 kMDNSDiscoveryTestResultsKey_UnexpectedInstances
, &unexpectedInstances
,
12845 kMDNSDiscoveryTestResultsKey_MissingInstances
, &missingInstances
,
12846 kMDNSDiscoveryTestResultsKey_IncorrectInstances
, &incorrectInstances
);
12847 require_noerr( err
, exit
);
12849 for( domain
= inResults
->domainList
; domain
&& ( strcasecmp( domain
->name
, "local." ) != 0 ); domain
= domain
->next
) {}
12850 require_action( domain
, exit
, err
= kInternalErr
);
12852 for( type
= domain
->typeList
; type
&& ( strcasecmp( type
->name
, context
->serviceType
) != 0 ); type
= type
->next
) {}
12853 require_action( type
, exit
, err
= kInternalErr
);
12855 instanceArray
= (const SBRServiceInstance
**) calloc( context
->instanceCount
, sizeof( *instanceArray
) );
12856 require_action( instanceArray
, exit
, err
= kNoMemoryErr
);
12858 hostnameLen
= strlen( context
->hostname
);
12859 for( instance
= type
->instanceList
; instance
; instance
= instance
->next
)
12861 unsigned int instanceNumber
= 0;
12863 if( strcmp_prefix( instance
->name
, context
->hostname
) == 0 )
12865 ptr
= &instance
->name
[ hostnameLen
];
12866 if( ( ptr
[ 0 ] == ' ' ) && ( ptr
[ 1 ] == '(' ) )
12869 for( end
= ptr
; isdigit_safe( *end
); ++end
) {}
12870 if( DecimalTextToUInt32( ptr
, end
, &u32
, &ptr
) == kNoErr
)
12872 if( ( u32
>= 2 ) && ( u32
<= context
->instanceCount
) && ( ptr
[ 0 ] == ')' ) && ( ptr
[ 1 ] == '\0' ) )
12874 instanceNumber
= u32
;
12878 else if( *ptr
== '\0' )
12880 instanceNumber
= 1;
12883 if( ( instanceNumber
!= 0 ) && ( instance
->ifIndex
== context
->ifIndex
) )
12885 check( !instanceArray
[ instanceNumber
- 1 ] );
12886 instanceArray
[ instanceNumber
- 1 ] = instance
;
12890 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, unexpectedInstances
,
12895 kMDNSDiscoveryTestUnexpectedInstanceKey_Name
, instance
->name
,
12896 kMDNSDiscoveryTestUnexpectedInstanceKey_InterfaceIndex
, (int64_t) instance
->ifIndex
);
12897 require_noerr( err
, exit
);
12901 maxResolveTimeUs
= 0;
12902 for( i
= 1; i
<= context
->instanceCount
; ++i
)
12904 int isHostnameValid
;
12907 instance
= instanceArray
[ i
- 1 ];
12912 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, missingInstances
, "%s", context
->hostname
);
12913 require_noerr( err
, exit
);
12917 char * instanceName
= NULL
;
12919 ASPrintF( &instanceName
, "%s (%u)", context
->hostname
, i
);
12920 require_action( instanceName
, exit
, err
= kUnknownErr
);
12922 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, missingInstances
, "%s", instanceName
);
12923 free( instanceName
);
12924 require_noerr( err
, exit
);
12929 if( !instance
->hostname
)
12931 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, incorrectInstances
,
12936 kMDNSDiscoveryTestIncorrectInstanceKey_Name
, instance
->name
,
12937 kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve
, false );
12938 require_noerr( err
, exit
);
12942 badDict
= CFDictionaryCreateMutable( NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
12943 require_action( badDict
, exit
, err
= kNoMemoryErr
);
12945 isHostnameValid
= false;
12946 if( strcmp_prefix( instance
->hostname
, context
->hostname
) == 0 )
12948 ptr
= &instance
->hostname
[ hostnameLen
];
12951 if( strcmp( ptr
, ".local." ) == 0 ) isHostnameValid
= true;
12953 else if( *ptr
== '-' )
12956 for( end
= ptr
; isdigit_safe( *end
); ++end
) {}
12957 if( DecimalTextToUInt32( ptr
, end
, &u32
, &ptr
) == kNoErr
)
12959 if( ( u32
== i
) && ( strcmp( ptr
, ".local." ) == 0 ) ) isHostnameValid
= true;
12963 if( !isHostnameValid
)
12965 err
= CFDictionarySetCString( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_BadHostname
, instance
->hostname
,
12967 require_noerr( err
, exit
);
12970 if( instance
->port
!= (uint16_t)( kMDNSReplierPortBase
+ context
->txtSize
) )
12972 err
= CFDictionarySetInt64( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_BadPort
, instance
->port
);
12973 require_noerr( err
, exit
);
12976 isTXTValid
= false;
12977 if( instance
->txtLen
== context
->txtSize
)
12979 uint8_t name
[ kDomainNameLengthMax
];
12981 err
= DomainNameFromString( name
, instance
->name
, NULL
);
12982 require_noerr( err
, exit
);
12984 err
= DomainNameAppendString( name
, type
->name
, NULL
);
12985 require_noerr( err
, exit
);
12987 err
= DomainNameAppendString( name
, "local", NULL
);
12988 require_noerr( err
, exit
);
12990 if( _MDNSDiscoveryTestTXTRecordIsValid( name
, instance
->txtPtr
, instance
->txtLen
) ) isTXTValid
= true;
12994 char * hexStr
= NULL
;
12996 ASPrintF( &hexStr
, "%.4H", instance
->txtPtr
, (int) instance
->txtLen
, (int) instance
->txtLen
);
12997 require_action( hexStr
, exit
, err
= kUnknownErr
);
12999 err
= CFDictionarySetCString( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_BadTXT
, hexStr
, kSizeCString
);
13001 require_noerr( err
, exit
);
13004 if( isHostnameValid
)
13006 uint64_t addrV4Bitmap
, addrV6Bitmap
, bitmask
, resolveTimeUs
;
13008 uint8_t addrV4
[ 4 ];
13009 uint8_t addrV6
[ 16 ];
13011 if( context
->recordCountA
< 64 ) addrV4Bitmap
= ( UINT64_C( 1 ) << context
->recordCountA
) - 1;
13012 else addrV4Bitmap
= ~UINT64_C( 0 );
13014 if( context
->recordCountAAAA
< 64 ) addrV6Bitmap
= ( UINT64_C( 1 ) << context
->recordCountAAAA
) - 1;
13015 else addrV6Bitmap
= ~UINT64_C( 0 );
13018 WriteBig16( &addrV4
[ 1 ], i
);
13021 memcpy( addrV6
, kMDNSReplierBaseAddrV6
, 16 );
13022 WriteBig16( &addrV6
[ 12 ], i
);
13024 unexpectedAddrs
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13025 require_action( unexpectedAddrs
, exit
, err
= kNoMemoryErr
);
13028 for( ipaddr
= instance
->ipaddrList
; ipaddr
; ipaddr
= ipaddr
->next
)
13030 const uint8_t * addrPtr
;
13032 int isAddrValid
= false;
13034 if( ipaddr
->sip
.sa
.sa_family
== AF_INET
)
13036 addrPtr
= (const uint8_t *) &ipaddr
->sip
.v4
.sin_addr
.s_addr
;
13037 lsb
= addrPtr
[ 3 ];
13038 if( ( memcmp( addrPtr
, addrV4
, 3 ) == 0 ) && ( lsb
>= 1 ) && ( lsb
<= context
->recordCountA
) )
13040 bitmask
= UINT64_C( 1 ) << ( lsb
- 1 );
13041 addrV4Bitmap
&= ~bitmask
;
13042 isAddrValid
= true;
13045 else if( ipaddr
->sip
.sa
.sa_family
== AF_INET6
)
13047 addrPtr
= ipaddr
->sip
.v6
.sin6_addr
.s6_addr
;
13048 lsb
= addrPtr
[ 15 ];
13049 if( ( memcmp( addrPtr
, addrV6
, 15 ) == 0 ) && ( lsb
>= 1 ) && ( lsb
<= context
->recordCountAAAA
) )
13051 bitmask
= UINT64_C( 1 ) << ( lsb
- 1 );
13052 addrV6Bitmap
&= ~bitmask
;
13053 isAddrValid
= true;
13058 if( ipaddr
->resolveTimeUs
> resolveTimeUs
) resolveTimeUs
= ipaddr
->resolveTimeUs
;
13062 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, unexpectedAddrs
, "%##a", &ipaddr
->sip
);
13063 require_noerr( err
, exit
);
13067 resolveTimeUs
+= ( instance
->discoverTimeUs
+ instance
->resolveTimeUs
);
13068 if( resolveTimeUs
> maxResolveTimeUs
) maxResolveTimeUs
= resolveTimeUs
;
13070 if( CFArrayGetCount( unexpectedAddrs
) > 0 )
13072 CFDictionarySetValue( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_UnexpectedAddrs
, unexpectedAddrs
);
13074 ForgetCF( &unexpectedAddrs
);
13076 missingAddrs
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13077 require_action( missingAddrs
, exit
, err
= kNoMemoryErr
);
13079 for( j
= 1; addrV4Bitmap
!= 0; ++j
)
13081 bitmask
= UINT64_C( 1 ) << ( j
- 1 );
13082 if( addrV4Bitmap
& bitmask
)
13084 addrV4Bitmap
&= ~bitmask
;
13085 addrV4
[ 3 ] = (uint8_t) j
;
13086 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, missingAddrs
, "%.4a", addrV4
);
13087 require_noerr( err
, exit
);
13090 for( j
= 1; addrV6Bitmap
!= 0; ++j
)
13092 bitmask
= UINT64_C( 1 ) << ( j
- 1 );
13093 if( addrV6Bitmap
& bitmask
)
13095 addrV6Bitmap
&= ~bitmask
;
13096 addrV6
[ 15 ] = (uint8_t) j
;
13097 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, missingAddrs
, "%.16a", addrV6
);
13098 require_noerr( err
, exit
);
13102 if( CFArrayGetCount( missingAddrs
) > 0 )
13104 CFDictionarySetValue( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_MissingAddrs
, missingAddrs
);
13106 ForgetCF( &missingAddrs
);
13109 if( CFDictionaryGetCount( badDict
) > 0 )
13111 err
= CFDictionarySetCString( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_Name
, instance
->name
,
13113 require_noerr( err
, exit
);
13115 CFDictionarySetBoolean( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve
, true );
13116 CFArrayAppendValue( incorrectInstances
, badDict
);
13118 ForgetCF( &badDict
);
13121 if( ( CFArrayGetCount( unexpectedInstances
) == 0 ) &&
13122 ( CFArrayGetCount( missingInstances
) == 0 ) &&
13123 ( CFArrayGetCount( incorrectInstances
) == 0 ) )
13125 err
= CFDictionarySetInt64( plist
, kMDNSDiscoveryTestResultsKey_TotalResolveTime
, (int64_t) maxResolveTimeUs
);
13126 require_noerr( err
, exit
);
13133 CFDictionarySetBoolean( plist
, kMDNSDiscoveryTestResultsKey_Success
, success
);
13135 err
= OutputPropertyList( plist
, context
->outputFormat
, context
->outputFilePath
);
13136 require_noerr_quiet( err
, exit
);
13139 ForgetCF( &context
->browser
);
13140 if( context
->replierPID
!= -1 )
13142 kill( context
->replierPID
, SIGTERM
);
13143 context
->replierPID
= -1;
13145 FreeNullSafe( instanceArray
);
13146 CFReleaseNullSafe( plist
);
13147 CFReleaseNullSafe( badDict
);
13148 CFReleaseNullSafe( unexpectedAddrs
);
13149 CFReleaseNullSafe( missingAddrs
);
13150 exit( err
? 1 : ( success
? 0 : 2 ) );
13153 //===========================================================================================================================
13154 // _MDNSDiscoveryTestTXTRecordIsValid
13155 //===========================================================================================================================
13157 static Boolean
_MDNSDiscoveryTestTXTRecordIsValid( const uint8_t *inRecordName
, const uint8_t *inTXTPtr
, size_t inTXTLen
)
13161 const uint8_t * ptr
;
13162 size_t i
, wholeCount
, remCount
;
13163 uint8_t txtStr
[ 16 ];
13165 if( inTXTLen
== 0 ) return( false );
13167 hash
= _FNV1( inRecordName
, DomainNameLength( inRecordName
) );
13170 n
= MemPrintF( &txtStr
[ 1 ], 15, "hash=0x%08X", hash
);
13174 wholeCount
= inTXTLen
/ 16;
13175 for( i
= 0; i
< wholeCount
; ++i
)
13177 if( memcmp( ptr
, txtStr
, 16 ) != 0 ) return( false );
13181 remCount
= inTXTLen
% 16;
13184 txtStr
[ 0 ] = (uint8_t)( remCount
- 1 );
13185 if( memcmp( ptr
, txtStr
, remCount
) != 0 ) return( false );
13188 check( ptr
== &inTXTPtr
[ inTXTLen
] );
13192 //===========================================================================================================================
13194 //===========================================================================================================================
13196 #define kDotLocalTestPreparationTimeLimitSecs 5
13197 #define kDotLocalTestSubtestDurationSecs 5
13199 // Constants for SRV record query subtest.
13201 #define kDotLocalTestSRV_Priority 1
13202 #define kDotLocalTestSRV_Weight 0
13203 #define kDotLocalTestSRV_Port 80
13204 #define kDotLocalTestSRV_TargetName ( (const uint8_t *) "\x03" "www" "\x07" "example" "\x03" "com" )
13205 #define kDotLocalTestSRV_TargetStr "www.example.com."
13206 #define kDotLocalTestSRV_ResultStr "1 0 80 " kDotLocalTestSRV_TargetStr
13210 kDotLocalTestState_Unset
= 0,
13211 kDotLocalTestState_Preparing
= 1,
13212 kDotLocalTestState_GAIMDNSOnly
= 2,
13213 kDotLocalTestState_GAIDNSOnly
= 3,
13214 kDotLocalTestState_GAIBoth
= 4,
13215 kDotLocalTestState_GAINeither
= 5,
13216 kDotLocalTestState_GAINoSuchRecord
= 6,
13217 kDotLocalTestState_QuerySRV
= 7,
13218 kDotLocalTestState_Done
= 8
13220 } DotLocalTestState
;
13224 const char * testDesc
; // Description of the current subtest.
13225 char * queryName
; // Query name for GetAddrInfo or QueryRecord operation.
13226 dispatch_source_t timer
; // Timer used for limiting the time for each subtest.
13227 NanoTime64 startTime
; // Timestamp of when the subtest started.
13228 NanoTime64 endTime
; // Timestamp of when the subtest ended.
13229 CFMutableArrayRef correctResults
; // Operation results that were expected.
13230 CFMutableArrayRef duplicateResults
; // Operation results that were expected, but were already received.
13231 CFMutableArrayRef unexpectedResults
; // Operation results that were unexpected.
13232 OSStatus error
; // Subtest's error code.
13233 uint32_t addrDNSv4
; // If hasDNSv4 is true, the expected DNS IPv4 address for queryName.
13234 uint32_t addrMDNSv4
; // If hasMDNSv4 is true, the expected MDNS IPv4 address for queryName.
13235 uint8_t addrDNSv6
[ 16 ]; // If hasDNSv6 is true, the expected DNS IPv6 address for queryName.
13236 uint8_t addrMDNSv6
[ 16 ]; // If hasMDNSv6 is true, the expected MDNS IPv6 address for queryName.
13237 Boolean hasDNSv4
; // True if queryName has a DNS IPv4 address.
13238 Boolean hasDNSv6
; // True if queryName has a DNS IPv6 address.
13239 Boolean hasMDNSv4
; // True if queryName has an MDNS IPv4 address.
13240 Boolean hasMDNSv6
; // True if queryName has an MDNS IPv6 address.
13241 Boolean needDNSv4
; // True if operation is expecting, but hasn't received a DNS IPv4 result.
13242 Boolean needDNSv6
; // True if operation is expecting, but hasn't received a DNS IPv6 result.
13243 Boolean needMDNSv4
; // True if operation is expecting, but hasn't received an MDNS IPv4 result.
13244 Boolean needMDNSv6
; // True if operation is expecting, but hasn't received an MDNS IPv6 result.
13245 Boolean needSRV
; // True if operation is expecting, but hasn't received an SRV result.
13251 dispatch_source_t timer
; // Timer used for limiting the time for each state/subtest.
13252 DotLocalSubtest
* subtest
; // Current subtest's state.
13253 DNSServiceRef connection
; // Shared connection for DNS-SD operations.
13254 DNSServiceRef op
; // Reference for the current DNS-SD operation.
13255 DNSServiceRef op2
; // Reference for mdnsreplier probe query used during preparing state.
13256 DNSRecordRef localSOARef
; // Reference returned by DNSServiceRegisterRecord() for local. SOA record.
13257 char * replierCmd
; // Command used to invoke the mdnsreplier.
13258 char * serverCmd
; // Command used to invoke the test DNS server.
13259 CFMutableArrayRef reportsGAI
; // Reports for subtests that use DNSServiceGetAddrInfo.
13260 CFMutableArrayRef reportsQuerySRV
; // Reports for subtests that use DNSServiceQueryRecord for SRV records.
13261 NanoTime64 startTime
; // Timestamp for when the test started.
13262 NanoTime64 endTime
; // Timestamp for when the test ended.
13263 DotLocalTestState state
; // The test's current state.
13264 pid_t replierPID
; // PID of spawned mdnsreplier.
13265 pid_t serverPID
; // PID of spawned test DNS server.
13266 uint32_t ifIndex
; // Interface index used for mdnsreplier.
13267 char * outputFilePath
; // File to write test results to. If NULL, then write to stdout.
13268 OutputFormatType outputFormat
; // Format of test results output.
13269 Boolean registeredSOA
; // True if the dummy local. SOA record was successfully registered.
13270 Boolean serverIsReady
; // True if response was received for test DNS server probe query.
13271 Boolean replierIsReady
; // True if response was received for mdnsreplier probe query.
13272 Boolean testFailed
; // True if at least one subtest failed.
13273 char labelStr
[ 20 + 1 ]; // Unique label string used for for making the query names used by subtests.
13274 // The format of this string is "dotlocal-test-<six random chars>".
13275 } DotLocalTestContext
;
13277 static void _DotLocalTestStateMachine( DotLocalTestContext
*inContext
);
13278 static void DNSSD_API
13279 _DotLocalTestProbeQueryRecordCallback(
13280 DNSServiceRef inSDRef
,
13281 DNSServiceFlags inFlags
,
13282 uint32_t inInterfaceIndex
,
13283 DNSServiceErrorType inError
,
13284 const char * inFullName
,
13287 uint16_t inRDataLen
,
13288 const void * inRDataPtr
,
13290 void * inContext
);
13291 static void DNSSD_API
13292 _DotLocalTestRegisterRecordCallback(
13293 DNSServiceRef inSDRef
,
13294 DNSRecordRef inRecordRef
,
13295 DNSServiceFlags inFlags
,
13296 DNSServiceErrorType inError
,
13297 void * inContext
);
13298 static void _DotLocalTestTimerHandler( void *inContext
);
13299 static void DNSSD_API
13300 _DotLocalTestGAICallback(
13301 DNSServiceRef inSDRef
,
13302 DNSServiceFlags inFlags
,
13303 uint32_t inInterfaceIndex
,
13304 DNSServiceErrorType inError
,
13305 const char * inHostname
,
13306 const struct sockaddr
* inSockAddr
,
13308 void * inContext
);
13309 static void DNSSD_API
13310 _DotLocalTestQueryRecordCallback(
13311 DNSServiceRef inSDRef
,
13312 DNSServiceFlags inFlags
,
13313 uint32_t inInterfaceIndex
,
13314 DNSServiceErrorType inError
,
13315 const char * inFullName
,
13318 uint16_t inRDataLen
,
13319 const void * inRDataPtr
,
13321 void * inContext
);
13323 static void DotLocalTestCmd( void )
13326 DotLocalTestContext
* context
;
13327 uint8_t * rdataPtr
;
13329 DNSServiceFlags flags
;
13330 char queryName
[ 64 ];
13331 char randBuf
[ 6 + 1 ]; // Large enough for four and six character random strings below.
13333 context
= (DotLocalTestContext
*) calloc( 1, sizeof( *context
) );
13334 require_action( context
, exit
, err
= kNoMemoryErr
);
13336 context
->startTime
= NanoTimeGetCurrent();
13337 context
->endTime
= kNanoTime_Invalid
;
13339 context
->state
= kDotLocalTestState_Preparing
;
13341 if( gDotLocalTest_Interface
)
13343 err
= InterfaceIndexFromArgString( gDotLocalTest_Interface
, &context
->ifIndex
);
13344 require_noerr_quiet( err
, exit
);
13348 err
= _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All
, NULL
, &context
->ifIndex
);
13349 require_noerr_quiet( err
, exit
);
13352 if( gDotLocalTest_OutputFilePath
)
13354 context
->outputFilePath
= strdup( gDotLocalTest_OutputFilePath
);
13355 require_action( context
->outputFilePath
, exit
, err
= kNoMemoryErr
);
13358 err
= OutputFormatFromArgString( gDotLocalTest_OutputFormat
, &context
->outputFormat
);
13359 require_noerr_quiet( err
, exit
);
13361 context
->reportsGAI
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13362 require_action( context
->reportsGAI
, exit
, err
= kNoMemoryErr
);
13364 context
->reportsQuerySRV
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13365 require_action( context
->reportsQuerySRV
, exit
, err
= kNoMemoryErr
);
13367 SNPrintF( context
->labelStr
, sizeof( context
->labelStr
), "dotlocal-test-%s",
13368 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, 6, randBuf
) );
13370 // Spawn an mdnsreplier.
13372 ASPrintF( &context
->replierCmd
,
13373 "dnssdutil mdnsreplier --follow %lld --interface %u --hostname %s --tag %s --maxInstanceCount 2 --countA 1"
13375 (int64_t) getpid(), context
->ifIndex
, context
->labelStr
,
13376 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, 4, randBuf
) );
13377 require_action_quiet( context
->replierCmd
, exit
, err
= kUnknownErr
);
13379 err
= SpawnCommand( &context
->replierPID
, "%s", context
->replierCmd
);
13380 require_noerr( err
, exit
);
13382 // Spawn a test DNS server
13384 ASPrintF( &context
->serverCmd
,
13385 "dnssdutil server --loopback --follow %lld --port 0 --defaultTTL 300 --domain %s.local.",
13386 (int64_t) getpid(), context
->labelStr
);
13387 require_action_quiet( context
->serverCmd
, exit
, err
= kUnknownErr
);
13389 err
= SpawnCommand( &context
->serverPID
, "%s", context
->serverCmd
);
13390 require_noerr( err
, exit
);
13392 // Create a shared DNS-SD connection.
13394 err
= DNSServiceCreateConnection( &context
->connection
);
13395 require_noerr( err
, exit
);
13397 err
= DNSServiceSetDispatchQueue( context
->connection
, dispatch_get_main_queue() );
13398 require_noerr( err
, exit
);
13400 // Create probe query for DNS server, i.e., query for any name that has an A record.
13402 SNPrintF( queryName
, sizeof( queryName
), "tag-dotlocal-test-probe.ipv4.%s.local.", context
->labelStr
);
13404 flags
= kDNSServiceFlagsShareConnection
;
13405 #if( TARGET_OS_WATCH )
13406 flags
|= kDNSServiceFlagsPathEvaluationDone
;
13409 context
->op
= context
->connection
;
13410 err
= DNSServiceQueryRecord( &context
->op
, flags
, kDNSServiceInterfaceIndexAny
, queryName
, kDNSServiceType_A
,
13411 kDNSServiceClass_IN
, _DotLocalTestProbeQueryRecordCallback
, context
);
13412 require_noerr( err
, exit
);
13414 // Create probe query for mdnsreplier's "about" TXT record.
13416 SNPrintF( queryName
, sizeof( queryName
), "about.%s.local.", context
->labelStr
);
13418 flags
= kDNSServiceFlagsShareConnection
| kDNSServiceFlagsForceMulticast
;
13419 #if( TARGET_OS_WATCH )
13420 flags
|= kDNSServiceFlagsPathEvaluationDone
;
13423 context
->op2
= context
->connection
;
13424 err
= DNSServiceQueryRecord( &context
->op2
, flags
, context
->ifIndex
, queryName
, kDNSServiceType_TXT
, kDNSServiceClass_IN
,
13425 _DotLocalTestProbeQueryRecordCallback
, context
);
13426 require_noerr( err
, exit
);
13428 // Register a dummy local. SOA record.
13430 err
= CreateSOARecordData( kRootLabel
, kRootLabel
, 1976040101, 1 * kSecondsPerDay
, 2 * kSecondsPerHour
,
13431 1000 * kSecondsPerHour
, 2 * kSecondsPerDay
, &rdataPtr
, &rdataLen
);
13432 require_noerr( err
, exit
);
13434 err
= DNSServiceRegisterRecord( context
->connection
, &context
->localSOARef
, kDNSServiceFlagsUnique
,
13435 kDNSServiceInterfaceIndexLocalOnly
, "local.", kDNSServiceType_SOA
, kDNSServiceClass_IN
, 1,
13436 rdataPtr
, 1 * kSecondsPerHour
, _DotLocalTestRegisterRecordCallback
, context
);
13437 require_noerr( err
, exit
);
13439 // Start timer for probe responses and SOA record registration.
13441 err
= DispatchTimerOneShotCreate( dispatch_time_seconds( kDotLocalTestPreparationTimeLimitSecs
),
13442 INT64_C_safe( kDotLocalTestPreparationTimeLimitSecs
) * kNanosecondsPerSecond
/ 10, dispatch_get_main_queue(),
13443 _DotLocalTestTimerHandler
, context
, &context
->timer
);
13444 require_noerr( err
, exit
);
13445 dispatch_resume( context
->timer
);
13450 if( err
) ErrQuit( 1, "error: %#m\n", err
);
13453 //===========================================================================================================================
13454 // _DotLocalTestStateMachine
13455 //===========================================================================================================================
13457 static OSStatus
_DotLocalSubtestCreate( DotLocalSubtest
**outSubtest
);
13458 static void _DotLocalSubtestFree( DotLocalSubtest
*inSubtest
);
13459 static OSStatus
_DotLocalTestStartSubtest( DotLocalTestContext
*inContext
);
13460 static OSStatus
_DotLocalTestFinalizeSubtest( DotLocalTestContext
*inContext
);
13461 static void _DotLocalTestFinalizeAndExit( DotLocalTestContext
*inContext
) ATTRIBUTE_NORETURN
;
13463 static void _DotLocalTestStateMachine( DotLocalTestContext
*inContext
)
13466 DotLocalTestState nextState
;
13468 DNSServiceForget( &inContext
->op
);
13469 DNSServiceForget( &inContext
->op2
);
13470 dispatch_source_forget( &inContext
->timer
);
13472 switch( inContext
->state
)
13474 case kDotLocalTestState_Preparing
: nextState
= kDotLocalTestState_GAIMDNSOnly
; break;
13475 case kDotLocalTestState_GAIMDNSOnly
: nextState
= kDotLocalTestState_GAIDNSOnly
; break;
13476 case kDotLocalTestState_GAIDNSOnly
: nextState
= kDotLocalTestState_GAIBoth
; break;
13477 case kDotLocalTestState_GAIBoth
: nextState
= kDotLocalTestState_GAINeither
; break;
13478 case kDotLocalTestState_GAINeither
: nextState
= kDotLocalTestState_GAINoSuchRecord
; break;
13479 case kDotLocalTestState_GAINoSuchRecord
: nextState
= kDotLocalTestState_QuerySRV
; break;
13480 case kDotLocalTestState_QuerySRV
: nextState
= kDotLocalTestState_Done
; break;
13481 default: err
= kStateErr
; goto exit
;
13484 if( inContext
->state
== kDotLocalTestState_Preparing
)
13486 if( !inContext
->registeredSOA
|| !inContext
->serverIsReady
|| !inContext
->replierIsReady
)
13488 FPrintF( stderr
, "Preparation timed out: Registered SOA? %s. Server ready? %s. mdnsreplier ready? %s.\n",
13489 YesNoStr( inContext
->registeredSOA
),
13490 YesNoStr( inContext
->serverIsReady
),
13491 YesNoStr( inContext
->replierIsReady
) );
13492 err
= kNotPreparedErr
;
13498 err
= _DotLocalTestFinalizeSubtest( inContext
);
13499 require_noerr( err
, exit
);
13502 inContext
->state
= nextState
;
13503 if( inContext
->state
== kDotLocalTestState_Done
) _DotLocalTestFinalizeAndExit( inContext
);
13504 err
= _DotLocalTestStartSubtest( inContext
);
13507 if( err
) ErrQuit( 1, "error: %#m\n", err
);
13510 //===========================================================================================================================
13511 // _DotLocalSubtestCreate
13512 //===========================================================================================================================
13514 static OSStatus
_DotLocalSubtestCreate( DotLocalSubtest
**outSubtest
)
13517 DotLocalSubtest
* obj
;
13519 obj
= (DotLocalSubtest
*) calloc( 1, sizeof( *obj
) );
13520 require_action( obj
, exit
, err
= kNoMemoryErr
);
13522 obj
->correctResults
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13523 require_action( obj
->correctResults
, exit
, err
= kNoMemoryErr
);
13525 obj
->duplicateResults
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13526 require_action( obj
->duplicateResults
, exit
, err
= kNoMemoryErr
);
13528 obj
->unexpectedResults
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13529 require_action( obj
->unexpectedResults
, exit
, err
= kNoMemoryErr
);
13536 if( obj
) _DotLocalSubtestFree( obj
);
13540 //===========================================================================================================================
13541 // _DotLocalSubtestFree
13542 //===========================================================================================================================
13544 static void _DotLocalSubtestFree( DotLocalSubtest
*inSubtest
)
13546 ForgetMem( &inSubtest
->queryName
);
13547 ForgetCF( &inSubtest
->correctResults
);
13548 ForgetCF( &inSubtest
->duplicateResults
);
13549 ForgetCF( &inSubtest
->unexpectedResults
);
13553 //===========================================================================================================================
13554 // _DotLocalTestStartSubtest
13555 //===========================================================================================================================
13557 static OSStatus
_DotLocalTestStartSubtest( DotLocalTestContext
*inContext
)
13560 DotLocalSubtest
* subtest
= NULL
;
13561 DNSServiceRef op
= NULL
;
13562 DNSServiceFlags flags
;
13564 err
= _DotLocalSubtestCreate( &subtest
);
13565 require_noerr( err
, exit
);
13567 if( inContext
->state
== kDotLocalTestState_GAIMDNSOnly
)
13569 ASPrintF( &subtest
->queryName
, "%s-2.local.", inContext
->labelStr
);
13570 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13572 subtest
->hasMDNSv4
= subtest
->needMDNSv4
= true;
13573 subtest
->hasMDNSv6
= subtest
->needMDNSv6
= true;
13575 subtest
->addrMDNSv4
= htonl( 0x00000201 ); // 0.0.2.1
13576 memcpy( subtest
->addrMDNSv6
, kMDNSReplierBaseAddrV6
, 16 ); // 2001:db8:2::2:1
13577 subtest
->addrMDNSv6
[ 13 ] = 2;
13578 subtest
->addrMDNSv6
[ 15 ] = 1;
13580 subtest
->testDesc
= kDotLocalTestSubtestDesc_GAIMDNSOnly
;
13583 else if( inContext
->state
== kDotLocalTestState_GAIDNSOnly
)
13585 ASPrintF( &subtest
->queryName
, "tag-dns-only.%s.local.", inContext
->labelStr
);
13586 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13588 subtest
->hasDNSv4
= subtest
->needDNSv4
= true;
13589 subtest
->hasDNSv6
= subtest
->needDNSv6
= true;
13591 subtest
->addrDNSv4
= htonl( kDNSServerBaseAddrV4
+ 1 ); // 203.0.113.1
13592 memcpy( subtest
->addrDNSv6
, kDNSServerBaseAddrV6
, 16 ); // 2001:db8:1::1
13593 subtest
->addrDNSv6
[ 15 ] = 1;
13595 subtest
->testDesc
= kDotLocalTestSubtestDesc_GAIDNSOnly
;
13598 else if( inContext
->state
== kDotLocalTestState_GAIBoth
)
13600 ASPrintF( &subtest
->queryName
, "%s.local.", inContext
->labelStr
);
13601 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13603 subtest
->hasDNSv4
= subtest
->needDNSv4
= true;
13604 subtest
->hasDNSv6
= subtest
->needDNSv6
= true;
13605 subtest
->hasMDNSv4
= subtest
->needMDNSv4
= true;
13606 subtest
->hasMDNSv6
= subtest
->needMDNSv6
= true;
13608 subtest
->addrDNSv4
= htonl( kDNSServerBaseAddrV4
+ 1 ); // 203.0.113.1
13609 memcpy( subtest
->addrDNSv6
, kDNSServerBaseAddrV6
, 16 ); // 2001:db8:1::1
13610 subtest
->addrDNSv6
[ 15 ] = 1;
13612 subtest
->addrMDNSv4
= htonl( 0x00000101 ); // 0.0.1.1
13613 memcpy( subtest
->addrMDNSv6
, kMDNSReplierBaseAddrV6
, 16 ); // 2001:db8:2::1:1
13614 subtest
->addrMDNSv6
[ 13 ] = 1;
13615 subtest
->addrMDNSv6
[ 15 ] = 1;
13617 subtest
->testDesc
= kDotLocalTestSubtestDesc_GAIBoth
;
13620 else if( inContext
->state
== kDotLocalTestState_GAINeither
)
13622 ASPrintF( &subtest
->queryName
, "doesnotexit-%s.local.", inContext
->labelStr
);
13623 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13625 subtest
->testDesc
= kDotLocalTestSubtestDesc_GAINeither
;
13628 else if( inContext
->state
== kDotLocalTestState_GAINoSuchRecord
)
13630 ASPrintF( &subtest
->queryName
, "doesnotexit-dns.%s.local.", inContext
->labelStr
);
13631 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13633 subtest
->hasDNSv4
= subtest
->needDNSv4
= true;
13634 subtest
->hasDNSv6
= subtest
->needDNSv6
= true;
13635 subtest
->testDesc
= kDotLocalTestSubtestDesc_GAINoSuchRecord
;
13638 else if( inContext
->state
== kDotLocalTestState_QuerySRV
)
13640 ASPrintF( &subtest
->queryName
, "_http._tcp.srv-%u-%u-%u.%s%s.local.",
13641 kDotLocalTestSRV_Priority
, kDotLocalTestSRV_Weight
, kDotLocalTestSRV_Port
, kDotLocalTestSRV_TargetStr
,
13642 inContext
->labelStr
);
13643 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13645 subtest
->needSRV
= true;
13646 subtest
->testDesc
= kDotLocalTestSubtestDesc_QuerySRV
;
13655 // Start new operation.
13657 flags
= kDNSServiceFlagsShareConnection
| kDNSServiceFlagsReturnIntermediates
;
13658 #if( TARGET_OS_WATCH )
13659 flags
|= kDNSServiceFlagsPathEvaluationDone
;
13662 subtest
->startTime
= NanoTimeGetCurrent();
13663 subtest
->endTime
= kNanoTime_Invalid
;
13665 if( inContext
->state
== kDotLocalTestState_QuerySRV
)
13667 op
= inContext
->connection
;
13668 err
= DNSServiceQueryRecord( &op
, flags
, kDNSServiceInterfaceIndexAny
, subtest
->queryName
,
13669 kDNSServiceType_SRV
, kDNSServiceClass_IN
, _DotLocalTestQueryRecordCallback
, inContext
);
13670 require_noerr( err
, exit
);
13674 op
= inContext
->connection
;
13675 err
= DNSServiceGetAddrInfo( &op
, flags
, kDNSServiceInterfaceIndexAny
,
13676 kDNSServiceProtocol_IPv4
| kDNSServiceProtocol_IPv6
, subtest
->queryName
, _DotLocalTestGAICallback
, inContext
);
13677 require_noerr( err
, exit
);
13682 check( !inContext
->timer
);
13683 err
= DispatchTimerOneShotCreate( dispatch_time_seconds( kDotLocalTestSubtestDurationSecs
),
13684 INT64_C_safe( kDotLocalTestSubtestDurationSecs
) * kNanosecondsPerSecond
/ 10, dispatch_get_main_queue(),
13685 _DotLocalTestTimerHandler
, inContext
, &inContext
->timer
);
13686 require_noerr( err
, exit
);
13687 dispatch_resume( inContext
->timer
);
13689 check( !inContext
->op
);
13690 inContext
->op
= op
;
13693 check( !inContext
->subtest
);
13694 inContext
->subtest
= subtest
;
13698 if( subtest
) _DotLocalSubtestFree( subtest
);
13699 if( op
) DNSServiceRefDeallocate( op
);
13703 //===========================================================================================================================
13704 // _DotLocalTestFinalizeSubtest
13705 //===========================================================================================================================
13707 #define kDotLocalTestReportKey_StartTime CFSTR( "startTime" ) // String.
13708 #define kDotLocalTestReportKey_EndTime CFSTR( "endTime" ) // String.
13709 #define kDotLocalTestReportKey_Success CFSTR( "success" ) // Boolean.
13710 #define kDotLocalTestReportKey_MDNSReplierCmd CFSTR( "replierCmd" ) // String.
13711 #define kDotLocalTestReportKey_DNSServerCmd CFSTR( "serverCmd" ) // String.
13712 #define kDotLocalTestReportKey_GetAddrInfoTests CFSTR( "testsGAI" ) // Array of Dictionaries.
13713 #define kDotLocalTestReportKey_QuerySRVTests CFSTR( "testsQuerySRV" ) // Array of Dictionaries.
13714 #define kDotLocalTestReportKey_Description CFSTR( "description" ) // String.
13715 #define kDotLocalTestReportKey_QueryName CFSTR( "queryName" ) // String.
13716 #define kDotLocalTestReportKey_Error CFSTR( "error" ) // Integer.
13717 #define kDotLocalTestReportKey_Results CFSTR( "results" ) // Dictionary of Arrays.
13718 #define kDotLocalTestReportKey_CorrectResults CFSTR( "correct" ) // Array of Strings
13719 #define kDotLocalTestReportKey_DuplicateResults CFSTR( "duplicates" ) // Array of Strings.
13720 #define kDotLocalTestReportKey_UnexpectedResults CFSTR( "unexpected" ) // Array of Strings.
13721 #define kDotLocalTestReportKey_MissingResults CFSTR( "missing" ) // Array of Strings.
13723 static OSStatus
_DotLocalTestFinalizeSubtest( DotLocalTestContext
*inContext
)
13726 DotLocalSubtest
* subtest
;
13727 CFMutableDictionaryRef reportDict
;
13728 CFMutableDictionaryRef resultsDict
;
13729 CFMutableArrayRef missingResults
, reportArray
;
13730 char startTime
[ 32 ];
13731 char endTime
[ 32 ];
13733 subtest
= inContext
->subtest
;
13734 inContext
->subtest
= NULL
;
13736 subtest
->endTime
= NanoTimeGetCurrent();
13737 _NanoTime64ToTimestamp( subtest
->startTime
, startTime
, sizeof( startTime
) );
13738 _NanoTime64ToTimestamp( subtest
->endTime
, endTime
, sizeof( endTime
) );
13741 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &reportDict
,
13743 "%kO=%s" // startTime
13744 "%kO=%s" // endTime
13745 "%kO=%s" // queryName
13746 "%kO=%s" // description
13747 "%kO={%@}" // results
13749 kDotLocalTestReportKey_StartTime
, startTime
,
13750 kDotLocalTestReportKey_EndTime
, endTime
,
13751 kDotLocalTestReportKey_QueryName
, subtest
->queryName
,
13752 kDotLocalTestReportKey_Description
, subtest
->testDesc
,
13753 kDotLocalTestReportKey_Results
, &resultsDict
);
13754 require_noerr( err
, exit
);
13756 missingResults
= NULL
;
13757 switch( inContext
->state
)
13759 case kDotLocalTestState_GAIMDNSOnly
:
13760 case kDotLocalTestState_GAIDNSOnly
:
13761 case kDotLocalTestState_GAIBoth
:
13762 case kDotLocalTestState_GAINeither
:
13763 if( subtest
->needDNSv4
|| subtest
->needDNSv6
|| subtest
->needMDNSv4
|| subtest
->needMDNSv6
)
13765 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &missingResults
,
13767 "%.4a" // Expected DNS IPv4 address
13768 "%.16a" // Expected DNS IPv6 address
13769 "%.4a" // Expected MDNS IPv4 address
13770 "%.16a" // Expected MDNS IPv6 address
13772 subtest
->needDNSv4
? &subtest
->addrDNSv4
: NULL
,
13773 subtest
->needDNSv6
? subtest
->addrDNSv6
: NULL
,
13774 subtest
->needMDNSv4
? &subtest
->addrMDNSv4
: NULL
,
13775 subtest
->needMDNSv6
? subtest
->addrMDNSv6
: NULL
);
13776 require_noerr( err
, exit
);
13780 case kDotLocalTestState_QuerySRV
:
13781 if( subtest
->needSRV
)
13783 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &missingResults
,
13785 "%s" // Expected SRV record data as a string.
13787 kDotLocalTestSRV_ResultStr
);
13788 require_noerr( err
, exit
);
13792 case kDotLocalTestState_GAINoSuchRecord
:
13793 if( subtest
->needDNSv4
|| subtest
->needDNSv6
)
13795 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &missingResults
,
13797 "%s" // No Such Record (A)
13798 "%s" // No Such Record (AAAA)
13800 subtest
->needDNSv4
? kNoSuchRecordAStr
: NULL
,
13801 subtest
->needDNSv6
? kNoSuchRecordAAAAStr
: NULL
);
13802 require_noerr( err
, exit
);
13811 CFDictionarySetValue( resultsDict
, kDotLocalTestReportKey_CorrectResults
, subtest
->correctResults
);
13813 if( missingResults
)
13815 CFDictionarySetValue( resultsDict
, kDotLocalTestReportKey_MissingResults
, missingResults
);
13816 ForgetCF( &missingResults
);
13817 if( !subtest
->error
) subtest
->error
= kNotFoundErr
;
13820 if( CFArrayGetCount( subtest
->unexpectedResults
) > 0 )
13822 CFDictionarySetValue( resultsDict
, kDotLocalTestReportKey_UnexpectedResults
, subtest
->unexpectedResults
);
13823 if( !subtest
->error
) subtest
->error
= kUnexpectedErr
;
13826 if( CFArrayGetCount( subtest
->duplicateResults
) > 0 )
13828 CFDictionarySetValue( resultsDict
, kDotLocalTestReportKey_DuplicateResults
, subtest
->duplicateResults
);
13829 if( !subtest
->error
) subtest
->error
= kDuplicateErr
;
13832 if( subtest
->error
) inContext
->testFailed
= true;
13833 err
= CFDictionarySetInt64( reportDict
, kDotLocalTestReportKey_Error
, subtest
->error
);
13834 require_noerr( err
, exit
);
13836 reportArray
= ( inContext
->state
== kDotLocalTestState_QuerySRV
) ? inContext
->reportsQuerySRV
: inContext
->reportsGAI
;
13837 CFArrayAppendValue( reportArray
, reportDict
);
13840 _DotLocalSubtestFree( subtest
);
13841 CFReleaseNullSafe( reportDict
);
13845 //===========================================================================================================================
13846 // _DotLocalTestFinalizeAndExit
13847 //===========================================================================================================================
13849 static void _DotLocalTestFinalizeAndExit( DotLocalTestContext
*inContext
)
13852 CFPropertyListRef plist
;
13853 char timestampStart
[ 32 ];
13854 char timestampEnd
[ 32 ];
13856 check( !inContext
->subtest
);
13857 inContext
->endTime
= NanoTimeGetCurrent();
13859 if( inContext
->replierPID
!= -1 )
13861 kill( inContext
->replierPID
, SIGTERM
);
13862 inContext
->replierPID
= -1;
13864 if( inContext
->serverPID
!= -1 )
13866 kill( inContext
->serverPID
, SIGTERM
);
13867 inContext
->serverPID
= -1;
13869 err
= DNSServiceRemoveRecord( inContext
->connection
, inContext
->localSOARef
, 0 );
13870 require_noerr( err
, exit
);
13872 _NanoTime64ToTimestamp( inContext
->startTime
, timestampStart
, sizeof( timestampStart
) );
13873 _NanoTime64ToTimestamp( inContext
->endTime
, timestampEnd
, sizeof( timestampEnd
) );
13875 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
13877 "%kO=%s" // startTime
13878 "%kO=%s" // endTime
13879 "%kO=%O" // testsGAI
13880 "%kO=%O" // testsQuerySRV
13881 "%kO=%b" // success
13882 "%kO=%s" // replierCmd
13883 "%kO=%s" // serverCmd
13885 kDotLocalTestReportKey_StartTime
, timestampStart
,
13886 kDotLocalTestReportKey_EndTime
, timestampEnd
,
13887 kDotLocalTestReportKey_GetAddrInfoTests
, inContext
->reportsGAI
,
13888 kDotLocalTestReportKey_QuerySRVTests
, inContext
->reportsQuerySRV
,
13889 kDotLocalTestReportKey_Success
, inContext
->testFailed
? false : true,
13890 kDotLocalTestReportKey_MDNSReplierCmd
, inContext
->replierCmd
,
13891 kDotLocalTestReportKey_DNSServerCmd
, inContext
->serverCmd
);
13892 require_noerr( err
, exit
);
13894 ForgetCF( &inContext
->reportsGAI
);
13895 ForgetCF( &inContext
->reportsQuerySRV
);
13897 err
= OutputPropertyList( plist
, inContext
->outputFormat
, inContext
->outputFilePath
);
13898 CFRelease( plist
);
13899 require_noerr( err
, exit
);
13901 exit( inContext
->testFailed
? 2 : 0 );
13904 ErrQuit( 1, "error: %#m\n", err
);
13907 //===========================================================================================================================
13908 // _DotLocalTestProbeQueryRecordCallback
13909 //===========================================================================================================================
13911 static void DNSSD_API
13912 _DotLocalTestProbeQueryRecordCallback(
13913 DNSServiceRef inSDRef
,
13914 DNSServiceFlags inFlags
,
13915 uint32_t inInterfaceIndex
,
13916 DNSServiceErrorType inError
,
13917 const char * inFullName
,
13920 uint16_t inRDataLen
,
13921 const void * inRDataPtr
,
13925 DotLocalTestContext
* const context
= (DotLocalTestContext
*) inContext
;
13927 Unused( inInterfaceIndex
);
13928 Unused( inFullName
);
13931 Unused( inRDataLen
);
13932 Unused( inRDataPtr
);
13935 check( context
->state
== kDotLocalTestState_Preparing
);
13937 require_quiet( ( inFlags
& kDNSServiceFlagsAdd
) && !inError
, exit
);
13939 if( inSDRef
== context
->op
)
13941 DNSServiceForget( &context
->op
);
13942 context
->serverIsReady
= true;
13944 else if( inSDRef
== context
->op2
)
13946 DNSServiceForget( &context
->op2
);
13947 context
->replierIsReady
= true;
13950 if( context
->registeredSOA
&& context
->serverIsReady
&& context
->replierIsReady
)
13952 _DotLocalTestStateMachine( context
);
13959 //===========================================================================================================================
13960 // _DotLocalTestRegisterRecordCallback
13961 //===========================================================================================================================
13963 static void DNSSD_API
13964 _DotLocalTestRegisterRecordCallback(
13965 DNSServiceRef inSDRef
,
13966 DNSRecordRef inRecordRef
,
13967 DNSServiceFlags inFlags
,
13968 DNSServiceErrorType inError
,
13971 DotLocalTestContext
* const context
= (DotLocalTestContext
*) inContext
;
13974 Unused( inRecordRef
);
13977 if( inError
) ErrQuit( 1, "error: local. SOA record registration failed: %#m\n", inError
);
13979 if( !context
->registeredSOA
)
13981 context
->registeredSOA
= true;
13982 if( context
->serverIsReady
&& context
->replierIsReady
) _DotLocalTestStateMachine( context
);
13986 //===========================================================================================================================
13987 // _DotLocalTestTimerHandler
13988 //===========================================================================================================================
13990 static void _DotLocalTestTimerHandler( void *inContext
)
13992 _DotLocalTestStateMachine( (DotLocalTestContext
*) inContext
);
13995 //===========================================================================================================================
13996 // _DotLocalTestGAICallback
13997 //===========================================================================================================================
13999 static void DNSSD_API
14000 _DotLocalTestGAICallback(
14001 DNSServiceRef inSDRef
,
14002 DNSServiceFlags inFlags
,
14003 uint32_t inInterfaceIndex
,
14004 DNSServiceErrorType inError
,
14005 const char * inHostname
,
14006 const struct sockaddr
* inSockAddr
,
14011 DotLocalTestContext
* const context
= (DotLocalTestContext
*) inContext
;
14012 DotLocalSubtest
* const subtest
= context
->subtest
;
14013 const sockaddr_ip
* const sip
= (const sockaddr_ip
*) inSockAddr
;
14016 Unused( inInterfaceIndex
);
14017 Unused( inHostname
);
14020 require_action_quiet( inFlags
& kDNSServiceFlagsAdd
, exit
, err
= kFlagErr
);
14021 require_action_quiet( ( sip
->sa
.sa_family
== AF_INET
) || ( sip
->sa
.sa_family
== AF_INET6
), exit
, err
= kTypeErr
);
14023 if( context
->state
== kDotLocalTestState_GAINoSuchRecord
)
14025 if( inError
== kDNSServiceErr_NoSuchRecord
)
14027 CFMutableArrayRef array
= NULL
;
14028 const char * noSuchRecordStr
;
14030 if( sip
->sa
.sa_family
== AF_INET
)
14032 array
= subtest
->needDNSv4
? subtest
->correctResults
: subtest
->duplicateResults
;
14033 subtest
->needDNSv4
= false;
14035 noSuchRecordStr
= kNoSuchRecordAStr
;
14039 array
= subtest
->needDNSv6
? subtest
->correctResults
: subtest
->duplicateResults
;
14040 subtest
->needDNSv6
= false;
14042 noSuchRecordStr
= kNoSuchRecordAAAAStr
;
14044 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, array
, "%s", noSuchRecordStr
);
14045 require_noerr( err
, fatal
);
14047 else if( !inError
)
14049 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, subtest
->unexpectedResults
, "%##a", sip
);
14050 require_noerr( err
, fatal
);
14062 CFMutableArrayRef array
= NULL
;
14064 if( sip
->sa
.sa_family
== AF_INET
)
14066 const uint32_t addrV4
= sip
->v4
.sin_addr
.s_addr
;
14068 if( subtest
->hasDNSv4
&& ( addrV4
== subtest
->addrDNSv4
) )
14070 array
= subtest
->needDNSv4
? subtest
->correctResults
: subtest
->duplicateResults
;
14071 subtest
->needDNSv4
= false;
14073 else if( subtest
->hasMDNSv4
&& ( addrV4
== subtest
->addrMDNSv4
) )
14075 array
= subtest
->needMDNSv4
? subtest
->correctResults
: subtest
->duplicateResults
;
14076 subtest
->needMDNSv4
= false;
14081 const uint8_t * const addrV6
= sip
->v6
.sin6_addr
.s6_addr
;
14083 if( subtest
->hasDNSv6
&& ( memcmp( addrV6
, subtest
->addrDNSv6
, 16 ) == 0 ) )
14085 array
= subtest
->needDNSv6
? subtest
->correctResults
: subtest
->duplicateResults
;
14086 subtest
->needDNSv6
= false;
14088 else if( subtest
->hasMDNSv6
&& ( memcmp( addrV6
, subtest
->addrMDNSv6
, 16 ) == 0 ) )
14090 array
= subtest
->needMDNSv6
? subtest
->correctResults
: subtest
->duplicateResults
;
14091 subtest
->needMDNSv6
= false;
14094 if( !array
) array
= subtest
->unexpectedResults
;
14095 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, array
, "%##a", sip
);
14096 require_noerr( err
, fatal
);
14098 else if( inError
== kDNSServiceErr_NoSuchRecord
)
14100 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, subtest
->unexpectedResults
, "%s",
14101 ( sip
->sa
.sa_family
== AF_INET
) ? kNoSuchRecordAStr
: kNoSuchRecordAAAAStr
);
14102 require_noerr( err
, fatal
);
14114 subtest
->error
= err
;
14115 _DotLocalTestStateMachine( context
);
14120 ErrQuit( 1, "error: %#m\n", err
);
14123 //===========================================================================================================================
14124 // _DotLocalTestQueryRecordCallback
14125 //===========================================================================================================================
14127 static void DNSSD_API
14128 _DotLocalTestQueryRecordCallback(
14129 DNSServiceRef inSDRef
,
14130 DNSServiceFlags inFlags
,
14131 uint32_t inInterfaceIndex
,
14132 DNSServiceErrorType inError
,
14133 const char * inFullName
,
14136 uint16_t inRDataLen
,
14137 const void * inRDataPtr
,
14142 DotLocalTestContext
* const context
= (DotLocalTestContext
*) inContext
;
14143 DotLocalSubtest
* const subtest
= context
->subtest
;
14144 const dns_fixed_fields_srv
* fields
;
14145 const uint8_t * target
;
14146 const uint8_t * ptr
;
14147 const uint8_t * end
;
14149 unsigned int priority
, weight
, port
;
14150 CFMutableArrayRef array
;
14153 Unused( inInterfaceIndex
);
14154 Unused( inFullName
);
14157 check( context
->state
== kDotLocalTestState_QuerySRV
);
14160 require_noerr_quiet( err
, exit
);
14161 require_action_quiet( inFlags
& kDNSServiceFlagsAdd
, exit
, err
= kFlagErr
);
14162 require_action_quiet( ( inType
== kDNSServiceType_SRV
) && ( inClass
== kDNSServiceClass_IN
), exit
, err
= kTypeErr
);
14163 require_action_quiet( inRDataLen
> sizeof( dns_fixed_fields_srv
), exit
, err
= kSizeErr
);
14165 fields
= (const dns_fixed_fields_srv
*) inRDataPtr
;
14166 priority
= dns_fixed_fields_srv_get_priority( fields
);
14167 weight
= dns_fixed_fields_srv_get_weight( fields
);
14168 port
= dns_fixed_fields_srv_get_port( fields
);
14169 target
= (const uint8_t *) &fields
[ 1 ];
14170 end
= ( (const uint8_t *) inRDataPtr
) + inRDataLen
;
14171 for( ptr
= target
; ( ptr
< end
) && ( *ptr
!= 0 ); ptr
+= ( 1 + *ptr
) ) {}
14173 if( ( priority
== kDotLocalTestSRV_Priority
) &&
14174 ( weight
== kDotLocalTestSRV_Weight
) &&
14175 ( port
== kDotLocalTestSRV_Port
) &&
14176 ( ptr
< end
) && DomainNameEqual( target
, kDotLocalTestSRV_TargetName
) )
14178 array
= subtest
->needSRV
? subtest
->correctResults
: subtest
->duplicateResults
;
14179 subtest
->needSRV
= false;
14183 array
= subtest
->unexpectedResults
;
14187 DNSRecordDataToString( inRDataPtr
, inRDataLen
, kDNSServiceType_SRV
, NULL
, 0, &rdataStr
);
14190 ASPrintF( &rdataStr
, "%#H", inRDataPtr
, inRDataLen
, inRDataLen
);
14191 require_action( rdataStr
, fatal
, err
= kNoMemoryErr
);
14194 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, array
, "%s", rdataStr
);
14196 require_noerr( err
, fatal
);
14201 subtest
->error
= err
;
14202 _DotLocalTestStateMachine( context
);
14207 ErrQuit( 1, "error: %#m\n", err
);
14210 //===========================================================================================================================
14211 // ProbeConflictTestCmd
14212 //===========================================================================================================================
14214 #define kProbeConflictTestService_DefaultName "pctest-name"
14215 #define kProbeConflictTestService_Port 60000
14217 #define kProbeConflictTestTXTPtr "\x13" "PROBE-CONFLICT-TEST"
14218 #define kProbeConflictTestTXTLen sizeof_string( kProbeConflictTestTXTPtr )
14222 const char * description
;
14223 const char * program
;
14224 Boolean expectsRename
;
14226 } ProbeConflictTestCase
;
14228 #define kPCTProgPreWait "wait 1000;" // Wait 1 second before sending gratuitous response.
14229 #define kPCTProgPostWait "wait 8000;" // Wait 8 seconds after sending gratuitous response.
14230 // This allows ~2.75 seconds for probing and ~5 seconds for a rename.
14232 static const ProbeConflictTestCase kProbeConflictTestCases
[] =
14236 { "No probe conflicts.", kPCTProgPreWait
"probes n-n-n;" "send;" kPCTProgPostWait
, false },
14238 // One multicast probe conflict
14240 { "One multicast probe conflict (1).", kPCTProgPreWait
"probes m;" "send;" kPCTProgPostWait
, false },
14241 { "One multicast probe conflict (2).", kPCTProgPreWait
"probes n-m;" "send;" kPCTProgPostWait
, false },
14242 { "One multicast probe conflict (3).", kPCTProgPreWait
"probes n-n-m;" "send;" kPCTProgPostWait
, false },
14244 // One unicast probe conflict
14246 { "One unicast probe conflict (1).", kPCTProgPreWait
"probes u;" "send;" kPCTProgPostWait
, true },
14247 { "One unicast probe conflict (2).", kPCTProgPreWait
"probes n-u;" "send;" kPCTProgPostWait
, true },
14248 { "One unicast probe conflict (3).", kPCTProgPreWait
"probes n-n-u;" "send;" kPCTProgPostWait
, true },
14250 // One multicast and one unicast probe conflict
14252 { "Multicast and unicast probe conflict (1).", kPCTProgPreWait
"probes m-u;" "send;" kPCTProgPostWait
, true },
14253 { "Multicast and unicast probe conflict (2).", kPCTProgPreWait
"probes m-n-u;" "send;" kPCTProgPostWait
, true },
14254 { "Multicast and unicast probe conflict (3).", kPCTProgPreWait
"probes m-n-n-u;" "send;" kPCTProgPostWait
, true },
14255 { "Multicast and unicast probe conflict (4).", kPCTProgPreWait
"probes n-m-u;" "send;" kPCTProgPostWait
, true },
14256 { "Multicast and unicast probe conflict (5).", kPCTProgPreWait
"probes n-m-n-u;" "send;" kPCTProgPostWait
, true },
14257 { "Multicast and unicast probe conflict (6).", kPCTProgPreWait
"probes n-m-n-n-u;" "send;" kPCTProgPostWait
, true },
14258 { "Multicast and unicast probe conflict (7).", kPCTProgPreWait
"probes n-n-m-u;" "send;" kPCTProgPostWait
, true },
14259 { "Multicast and unicast probe conflict (8).", kPCTProgPreWait
"probes n-n-m-n-u;" "send;" kPCTProgPostWait
, true },
14260 { "Multicast and unicast probe conflict (9).", kPCTProgPreWait
"probes n-n-m-n-n-u;" "send;" kPCTProgPostWait
, true },
14262 // Two multicast probe conflicts
14264 { "Two multicast probe conflicts (1).", kPCTProgPreWait
"probes m-m;" "send;" kPCTProgPostWait
, true },
14265 { "Two multicast probe conflicts (2).", kPCTProgPreWait
"probes m-n-m;" "send;" kPCTProgPostWait
, true },
14266 { "Two multicast probe conflicts (3).", kPCTProgPreWait
"probes m-n-n-m;" "send;" kPCTProgPostWait
, true },
14267 { "Two multicast probe conflicts (4).", kPCTProgPreWait
"probes n-m-m;" "send;" kPCTProgPostWait
, true },
14268 { "Two multicast probe conflicts (5).", kPCTProgPreWait
"probes n-m-n-m-n;" "send;" kPCTProgPostWait
, true },
14269 { "Two multicast probe conflicts (6).", kPCTProgPreWait
"probes n-m-n-n-m;" "send;" kPCTProgPostWait
, true },
14270 { "Two multicast probe conflicts (7).", kPCTProgPreWait
"probes n-n-m-m;" "send;" kPCTProgPostWait
, true },
14271 { "Two multicast probe conflicts (8).", kPCTProgPreWait
"probes n-n-m-n-m;" "send;" kPCTProgPostWait
, true },
14272 { "Two multicast probe conflicts (9).", kPCTProgPreWait
"probes n-n-m-n-n-m;" "send;" kPCTProgPostWait
, true },
14275 #define kProbeConflictTestCaseCount countof( kProbeConflictTestCases )
14279 DNSServiceRef registration
; // Test service registration.
14280 NanoTime64 testStartTime
; // Test's start time.
14281 NanoTime64 startTime
; // Current test case's start time.
14282 MDNSColliderRef collider
; // mDNS collider object.
14283 CFMutableArrayRef results
; // Array of test case results.
14284 char * serviceName
; // Test service's instance name as a string. (malloced)
14285 char * serviceType
; // Test service's service type as a string. (malloced)
14286 uint8_t * recordName
; // FQDN of collider's record (same as test service's SRV+TXT records). (malloced)
14287 unsigned int testCaseIndex
; // Index of the current test case.
14288 uint32_t ifIndex
; // Index of the interface that the collider is to operate on.
14289 char * outputFilePath
; // File to write test results to. If NULL, then write to stdout. (malloced)
14290 OutputFormatType outputFormat
; // Format of test report output.
14291 Boolean registered
; // True if the test service instance is currently registered.
14292 Boolean testFailed
; // True if at least one test case failed.
14294 } ProbeConflictTestContext
;
14296 static void DNSSD_API
14297 _ProbeConflictTestRegisterCallback(
14298 DNSServiceRef inSDRef
,
14299 DNSServiceFlags inFlags
,
14300 DNSServiceErrorType inError
,
14301 const char * inName
,
14302 const char * inType
,
14303 const char * inDomain
,
14304 void * inContext
);
14305 static void _ProbeConflictTestColliderStopHandler( void *inContext
, OSStatus inError
);
14306 static OSStatus
_ProbeConflictTestStartNextTest( ProbeConflictTestContext
*inContext
);
14307 static OSStatus
_ProbeConflictTestStopCurrentTest( ProbeConflictTestContext
*inContext
, Boolean inRenamed
);
14308 static void _ProbeConflictTestFinalizeAndExit( ProbeConflictTestContext
*inContext
) ATTRIBUTE_NORETURN
;
14310 static void ProbeConflictTestCmd( void )
14313 ProbeConflictTestContext
* context
;
14314 const char * serviceName
;
14317 context
= (ProbeConflictTestContext
*) calloc( 1, sizeof( *context
) );
14318 require_action( context
, exit
, err
= kNoMemoryErr
);
14320 if( gProbeConflictTest_Interface
)
14322 err
= InterfaceIndexFromArgString( gProbeConflictTest_Interface
, &context
->ifIndex
);
14323 require_noerr_quiet( err
, exit
);
14327 err
= _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All
, NULL
, &context
->ifIndex
);
14328 require_noerr_quiet( err
, exit
);
14331 if( gProbeConflictTest_OutputFilePath
)
14333 context
->outputFilePath
= strdup( gProbeConflictTest_OutputFilePath
);
14334 require_action( context
->outputFilePath
, exit
, err
= kNoMemoryErr
);
14337 err
= OutputFormatFromArgString( gProbeConflictTest_OutputFormat
, &context
->outputFormat
);
14338 require_noerr_quiet( err
, exit
);
14340 context
->results
= CFArrayCreateMutable( NULL
, kProbeConflictTestCaseCount
, &kCFTypeArrayCallBacks
);
14341 require_action( context
->results
, exit
, err
= kNoMemoryErr
);
14343 serviceName
= gProbeConflictTest_UseComputerName
? NULL
: kProbeConflictTestService_DefaultName
;
14345 ASPrintF( &context
->serviceType
, "_pctest-%s._udp",
14346 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
14347 require_action( context
->serviceType
, exit
, err
= kNoMemoryErr
);
14349 context
->testStartTime
= NanoTimeGetCurrent();
14350 err
= DNSServiceRegister( &context
->registration
, 0, context
->ifIndex
, serviceName
, context
->serviceType
, "local.",
14351 NULL
, htons( kProbeConflictTestService_Port
), 0, NULL
, _ProbeConflictTestRegisterCallback
, context
);
14352 require_noerr( err
, exit
);
14354 err
= DNSServiceSetDispatchQueue( context
->registration
, dispatch_get_main_queue() );
14355 require_noerr( err
, exit
);
14363 //===========================================================================================================================
14364 // _ProbeConflictTestRegisterCallback
14365 //===========================================================================================================================
14367 static void DNSSD_API
14368 _ProbeConflictTestRegisterCallback(
14369 DNSServiceRef inSDRef
,
14370 DNSServiceFlags inFlags
,
14371 DNSServiceErrorType inError
,
14372 const char * inName
,
14373 const char * inType
,
14374 const char * inDomain
,
14378 ProbeConflictTestContext
* const context
= (ProbeConflictTestContext
*) inContext
;
14382 Unused( inDomain
);
14385 require_noerr( err
, exit
);
14387 if( !context
->registered
)
14389 if( inFlags
& kDNSServiceFlagsAdd
)
14392 size_t recordNameLen
;
14394 uint8_t name
[ kDomainNameLengthMax
];
14396 context
->registered
= true;
14398 FreeNullSafe( context
->serviceName
);
14399 context
->serviceName
= strdup( inName
);
14400 require_action( context
->serviceName
, exit
, err
= kNoMemoryErr
);
14402 err
= DomainNameFromString( name
, context
->serviceName
, NULL
);
14403 require_noerr( err
, exit
);
14405 err
= DomainNameAppendString( name
, context
->serviceType
, NULL
);
14406 require_noerr( err
, exit
);
14408 err
= DomainNameAppendString( name
, "local", NULL
);
14409 require_noerr( err
, exit
);
14411 ForgetMem( &context
->recordName
);
14412 err
= DomainNameDup( name
, &context
->recordName
, &recordNameLen
);
14413 require_noerr( err
, exit
);
14414 require_fatal( recordNameLen
> 0, "Record name length is zero." ); // Prevents dubious static analyzer warning.
14416 // Make the first label all caps so that it's easier to spot in system logs.
14418 ptr
= context
->recordName
;
14419 for( len
= *ptr
++; len
> 0; --len
, ++ptr
) *ptr
= (uint8_t) toupper_safe( *ptr
);
14421 err
= _ProbeConflictTestStartNextTest( context
);
14422 require_noerr( err
, exit
);
14427 if( !( inFlags
& kDNSServiceFlagsAdd
) )
14429 context
->registered
= false;
14430 err
= _ProbeConflictTestStopCurrentTest( context
, true );
14431 require_noerr( err
, exit
);
14437 if( err
) exit( 1 );
14440 //===========================================================================================================================
14441 // _ProbeConflictTestColliderStopHandler
14442 //===========================================================================================================================
14444 static void _ProbeConflictTestColliderStopHandler( void *inContext
, OSStatus inError
)
14447 ProbeConflictTestContext
* const context
= (ProbeConflictTestContext
*) inContext
;
14450 require_noerr_quiet( err
, exit
);
14452 ForgetCF( &context
->collider
);
14454 err
= _ProbeConflictTestStopCurrentTest( context
, false );
14455 require_noerr( err
, exit
);
14457 err
= _ProbeConflictTestStartNextTest( context
);
14458 require_noerr( err
, exit
);
14461 if( err
) exit( 1 );
14464 //===========================================================================================================================
14465 // _ProbeConflictTestStartNextTest
14466 //===========================================================================================================================
14468 static OSStatus
_ProbeConflictTestStartNextTest( ProbeConflictTestContext
*inContext
)
14471 const ProbeConflictTestCase
* testCase
;
14473 check( !inContext
->collider
);
14475 if( inContext
->testCaseIndex
< kProbeConflictTestCaseCount
)
14477 testCase
= &kProbeConflictTestCases
[ inContext
->testCaseIndex
];
14481 _ProbeConflictTestFinalizeAndExit( inContext
);
14484 err
= MDNSColliderCreate( dispatch_get_main_queue(), &inContext
->collider
);
14485 require_noerr( err
, exit
);
14487 err
= MDNSColliderSetProgram( inContext
->collider
, testCase
->program
);
14488 require_noerr( err
, exit
);
14490 err
= MDNSColliderSetRecord( inContext
->collider
, inContext
->recordName
, kDNSServiceType_TXT
,
14491 kProbeConflictTestTXTPtr
, kProbeConflictTestTXTLen
);
14492 require_noerr( err
, exit
);
14494 MDNSColliderSetProtocols( inContext
->collider
, kMDNSColliderProtocol_IPv4
);
14495 MDNSColliderSetInterfaceIndex( inContext
->collider
, inContext
->ifIndex
);
14496 MDNSColliderSetStopHandler( inContext
->collider
, _ProbeConflictTestColliderStopHandler
, inContext
);
14498 inContext
->startTime
= NanoTimeGetCurrent();
14499 err
= MDNSColliderStart( inContext
->collider
);
14500 require_noerr( err
, exit
);
14506 //===========================================================================================================================
14507 // _ProbeConflictTestStopCurrentTest
14508 //===========================================================================================================================
14510 #define kProbeConflictTestCaseResultKey_Description CFSTR( "description" )
14511 #define kProbeConflictTestCaseResultKey_StartTime CFSTR( "startTime" )
14512 #define kProbeConflictTestCaseResultKey_EndTime CFSTR( "endTime" )
14513 #define kProbeConflictTestCaseResultKey_ExpectedRename CFSTR( "expectedRename" )
14514 #define kProbeConflictTestCaseResultKey_ServiceName CFSTR( "serviceName" )
14515 #define kProbeConflictTestCaseResultKey_Passed CFSTR( "passed" )
14517 static OSStatus
_ProbeConflictTestStopCurrentTest( ProbeConflictTestContext
*inContext
, Boolean inRenamed
)
14520 const ProbeConflictTestCase
* testCase
;
14523 char startTime
[ 32 ];
14524 char endTime
[ 32 ];
14526 now
= NanoTimeGetCurrent();
14528 if( inContext
->collider
)
14530 MDNSColliderSetStopHandler( inContext
->collider
, NULL
, NULL
);
14531 MDNSColliderStop( inContext
->collider
);
14532 CFRelease( inContext
->collider
);
14533 inContext
->collider
= NULL
;
14536 testCase
= &kProbeConflictTestCases
[ inContext
->testCaseIndex
];
14537 passed
= ( ( testCase
->expectsRename
&& inRenamed
) || ( !testCase
->expectsRename
&& !inRenamed
) ) ? true : false;
14538 if( !passed
) inContext
->testFailed
= true;
14540 _NanoTime64ToTimestamp( inContext
->startTime
, startTime
, sizeof( startTime
) );
14541 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
14543 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, inContext
->results
,
14545 "%kO=%s" // description
14546 "%kO=%b" // expectedRename
14547 "%kO=%s" // startTime
14548 "%kO=%s" // endTime
14549 "%kO=%s" // serviceName
14552 kProbeConflictTestCaseResultKey_Description
, testCase
->description
,
14553 kProbeConflictTestCaseResultKey_ExpectedRename
, testCase
->expectsRename
,
14554 kProbeConflictTestCaseResultKey_StartTime
, startTime
,
14555 kProbeConflictTestCaseResultKey_EndTime
, endTime
,
14556 kProbeConflictTestCaseResultKey_ServiceName
, inContext
->serviceName
,
14557 kProbeConflictTestCaseResultKey_Passed
, passed
);
14558 require_noerr( err
, exit
);
14560 ++inContext
->testCaseIndex
;
14566 //===========================================================================================================================
14567 // _ProbeConflictTestFinalizeAndExit
14568 //===========================================================================================================================
14570 #define kProbeConflictTestReportKey_StartTime CFSTR( "startTime" )
14571 #define kProbeConflictTestReportKey_EndTime CFSTR( "endTime" )
14572 #define kProbeConflictTestReportKey_ServiceType CFSTR( "serviceType" )
14573 #define kProbeConflictTestReportKey_Results CFSTR( "results" )
14574 #define kProbeConflictTestReportKey_Passed CFSTR( "passed" )
14576 static void _ProbeConflictTestFinalizeAndExit( ProbeConflictTestContext
*inContext
)
14579 CFPropertyListRef plist
;
14581 char startTime
[ 32 ];
14582 char endTime
[ 32 ];
14584 now
= NanoTimeGetCurrent();
14586 check( !inContext
->collider
);
14588 _NanoTime64ToTimestamp( inContext
->testStartTime
, startTime
, sizeof( startTime
) );
14589 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
14591 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
14593 "%kO=%s" // startTime
14594 "%kO=%s" // endTime
14595 "%kO=%s" // serviceType
14596 "%kO=%O" // results
14599 kProbeConflictTestReportKey_StartTime
, startTime
,
14600 kProbeConflictTestReportKey_EndTime
, endTime
,
14601 kProbeConflictTestReportKey_ServiceType
, inContext
->serviceType
,
14602 kProbeConflictTestReportKey_Results
, inContext
->results
,
14603 kProbeConflictTestReportKey_Passed
, inContext
->testFailed
? false : true );
14604 require_noerr( err
, exit
);
14605 ForgetCF( &inContext
->results
);
14607 err
= OutputPropertyList( plist
, inContext
->outputFormat
, inContext
->outputFilePath
);
14608 CFRelease( plist
);
14609 require_noerr( err
, exit
);
14611 exit( inContext
->testFailed
? 2 : 0 );
14614 ErrQuit( 1, "error: %#m\n", err
);
14617 //===========================================================================================================================
14618 // ExpensiveConstrainedsTestCmd
14619 //===========================================================================================================================
14621 #define NOTIFICATION_TIME_THRESHOLD 1500 // The maximum wating time allowed before notification happens
14622 #define TEST_REPETITION 2 // the number of repetition that one test has to passed
14623 #define LOOPBACK_INTERFACE_NAME "lo0"
14624 #define WIFI_TEST_QUESTION_NAME "www.example.com"
14625 #define EXPENSIVE_CONSTRAINED_MAX_RETRIES 1
14626 #define EXPENSIVE_CONSTRAINED_TEST_INTERVAL 5
14627 // Use "-n tag-expensive-test.ttl-86400.d.test." to run the test locally
14628 // #define LOOPBACK_TEST_QUESTION_NAME "tag-expensive-test.ttl-86400.d.test."
14630 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_START_TIME CFSTR( "Start Time" )
14631 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_END_TIME CFSTR( "End Time" )
14632 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_ALL_PASSED CFSTR( "All Tests Passed" )
14633 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_SUBTEST_RESULT CFSTR( "Subtest Results" )
14635 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME CFSTR( "Start Time" )
14636 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME CFSTR( "End Time" )
14637 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME CFSTR( "Question Name" )
14638 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS CFSTR( "DNS Service Flags" )
14639 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS CFSTR( "Protocols" )
14640 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX CFSTR( "Interface Index" )
14641 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME CFSTR( "Interface Name" )
14642 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT CFSTR( "Result" )
14643 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_ERROR CFSTR( "Error Description" )
14644 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS CFSTR( "Test Progress" )
14646 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_START_TIME CFSTR( "Start Time" )
14647 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_END_TIME CFSTR( "End Time" )
14648 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_STATE CFSTR( "State" )
14649 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPECT_RESULT CFSTR( "Expected Result" )
14650 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_ACTUAL_RESULT CFSTR( "Actual Result" )
14651 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPENSIVE_PREV_NOW CFSTR( "Expensive Prev->Now" )
14652 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CONSTRAINED_PREV_NOW CFSTR( "Constrained Prev->Now" )
14653 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CALL_BACK CFSTR( "Call Back" )
14655 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_TIMESTAMP CFSTR( "Timestamp" )
14656 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_NAME CFSTR( "Answer Name" )
14657 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_FLAGS CFSTR( "Add or Remove" )
14658 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_INTERFACE CFSTR( "Interface Index" )
14659 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_ADDRESS CFSTR( "Address" )
14661 // All the states that ends with _PREPARE represents the state where the test state is reset and initialized.
14662 enum ExpensiveConstrainedTestState
14665 TEST_EXPENSIVE_PREPARE
,
14666 TEST_EXPENSIVE
, // Test if mDNSResponder can handle "expensive" status change of the corresponding interface
14667 TEST_CONSTRAINED_PREPARE
,
14668 TEST_CONSTRAINED
, // Test if mDNSResponder can handle "constrained" status change of the corresponding interface
14669 TEST_EXPENSIVE_CONSTRAINED_PREPARE
,
14670 TEST_EXPENSIVE_CONSTRAINED
, // Test if mDNSResponder can handle "expensive" and "constrained" status change of the corresponding interface at the same time
14674 enum ExpensiveConstrainedTestOperation
14676 RESULT_ADD
, // received response for the given query, which means mDNSResponder is able to send out the query over the interface, because the interface status is changed.
14677 RESULT_RMV
, // received negative response for the given query, which means mDNSResponder is not able to send out the query over the interface, because the interface status is changed.
14678 NO_UPDATE
// no status update notification
14683 uint32_t subtestIndex
; // The index of parameter for the subtest
14684 DNSServiceRef opRef
; // sdRef for the DNSServiceGetAddrInfo operation.
14685 const char * name
; // Hostname to resolve.
14686 DNSServiceFlags flags
; // Flags argument for DNSServiceGetAddrInfo().
14687 DNSServiceProtocol protocols
; // Protocols argument for DNSServiceGetAddrInfo().
14688 uint32_t ifIndex
; // Interface index argument for DNSServiceGetAddrInfo().
14689 char ifName
[IFNAMSIZ
]; // Interface name for the given interface index.
14690 dispatch_source_t timer
; // The test will check if the current behavior is valid, which is called by
14691 // the timer per 2s.
14693 Boolean isExpensivePrev
; // If the interface is expensive in the previous test step.
14694 Boolean isExpensiveNow
; // If the interface is expensive now.
14695 Boolean isConstrainedPrev
; // If the interface is constrained in the previous test step.
14696 Boolean isConstrainedNow
; // If the interface is constrained now.
14697 Boolean startFromExpensive
; // All the test will start from expensive/constrained interface, so there won's be an answer until the interface is changed.
14698 uint8_t numOfRetries
; // the number of retries we can have if the test fail
14699 struct timeval updateTime
; // The time when interface status(expensive or constrained) is changed.
14700 struct timeval notificationTime
; // The time when callback function, which is passed to DNSServiceGetAddrInfo, gets called.
14701 uint32_t counter
; // To record how many times the test has repeated.
14702 enum ExpensiveConstrainedTestState state
; // The current test state.
14703 enum ExpensiveConstrainedTestOperation expectedOperation
; // the test expects this kind of notification
14704 enum ExpensiveConstrainedTestOperation operation
; // represents what notification the callback function gets.
14706 NanoTime64 testReport_startTime
; // when the entire test starts
14707 CFMutableArrayRef subtestReport
; // stores the log message for every subtest
14708 NanoTime64 subtestReport_startTime
; // when the subtest starts
14709 CFMutableArrayRef subtestProgress
; // one test iteration
14710 NanoTime64 subtestProgress_startTime
; // when the test iteration starts
14711 CFMutableArrayRef subtestProgress_callBack
; // array of ADD/REMOVE events
14712 char * outputFilePath
; // File to write test results to. If NULL, then write to stdout. (malloced)
14713 OutputFormatType outputFormat
; // Format of test report output.
14714 } ExpensiveConstrainedContext
;
14716 // structure that controls how the subtest is run
14719 const char *qname
; // the name of the query, when the ends with ".d.test.", test will send query to local DNS server
14720 Boolean deny_expensive
; // if the query should avoid using expensive interface
14721 Boolean deny_constrained
; // if the query should avoid using constrained interface
14722 Boolean start_from_expensive
; // if the query should starts from using an expensive interface
14723 Boolean ipv4_query
; // only allow IPv4 query
14724 Boolean ipv6_query
; // only allow IPv6 query
14725 int8_t test_passed
; // if the subtest passes
14726 } ExpensiveConstrainedTestParams
;
14728 static ExpensiveConstrainedTestParams ExpensiveConstrainedSubtestParams
[] =
14730 // qname deny_expensive deny_constrained start_from_expensive ipv4_query ipv6_query
14731 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, true, true, -1},
14732 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, true, true, -1},
14733 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, true, true, -1},
14734 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, true, true, -1},
14735 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, true, true, -1},
14736 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, true, true, -1},
14738 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, true, false, -1},
14739 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, true, false, -1},
14740 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, true, false, -1},
14741 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, true, false, -1},
14742 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, true, false, -1},
14743 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, true, false, -1},
14745 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, false, true, -1},
14746 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, false, true, -1},
14747 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, false, true, -1},
14748 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, false, true, -1},
14749 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, false, true, -1},
14750 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, false, true, -1}
14753 static void ExpensiveConstrainedSetupLocalDNSServer( ExpensiveConstrainedContext
*context
);
14754 static void ExpensiveConstrainedStartTestHandler( ExpensiveConstrainedContext
*context
);
14755 static void ExpensiveConstrainedStopTestHandler( ExpensiveConstrainedContext
*context
);
14756 static void ExpensiveConstrainedSetupTimer( ExpensiveConstrainedContext
*context
, uint32_t second
);
14757 static void ExpensiveConstrainedTestTimerEventHandler( ExpensiveConstrainedContext
*context
);
14758 static void DNSSD_API
14759 ExpensiveConstrainedCallback(
14760 DNSServiceRef inSDRef
,
14761 DNSServiceFlags inFlags
,
14762 uint32_t inInterfaceIndex
,
14763 DNSServiceErrorType inError
,
14764 const char * inHostname
,
14765 const struct sockaddr
* inSockAddr
,
14767 void * inContext
);
14768 static void ExpensiveConstrainedInitializeContext( ExpensiveConstrainedContext
*context
);
14769 static void ExpensiveConstrainedStopAndCleanTheTest( ExpensiveConstrainedContext
*context
);
14770 static void ExpensiveConstrainedSubtestProgressReport( ExpensiveConstrainedContext
*context
);
14771 static void ExpensiveConstrainedSubtestReport( ExpensiveConstrainedContext
*context
, const char *error_description
);
14772 static void ExpensiveConstrainedFinalResultReport( ExpensiveConstrainedContext
*context
, Boolean allPassed
);
14773 static const char *ExpensiveConstrainedProtocolString(DNSServiceProtocol protocol
);
14774 static const char *ExpensiveConstrainedStateString(enum ExpensiveConstrainedTestState state
);
14775 static const char *ExpensiveConstrainedOperationString(enum ExpensiveConstrainedTestOperation operation
);
14776 static Boolean
expensiveConstrainedEndsWith( const char *str
, const char *suffix
);
14778 //===========================================================================================================================
14779 // ExpensiveConstrainedTestCmd
14780 //===========================================================================================================================
14782 static void ExpensiveConstrainedTestCmd( void )
14785 dispatch_source_t signalSource
= NULL
;
14786 ExpensiveConstrainedContext
* context
= NULL
;
14788 // Set up SIGINT handler.
14789 signal( SIGINT
, SIG_IGN
);
14790 err
= DispatchSignalSourceCreate( SIGINT
, dispatch_get_main_queue(), Exit
, kExitReason_SIGINT
, &signalSource
);
14791 require_noerr( err
, exit
);
14792 dispatch_resume( signalSource
);
14794 // create the test context
14795 context
= (ExpensiveConstrainedContext
*) calloc( 1, sizeof(*context
) );
14796 require_action( context
, exit
, err
= kNoMemoryErr
);
14798 // get the command line option
14799 err
= OutputFormatFromArgString( gExpensiveConstrainedTest_OutputFormat
, &context
->outputFormat
);
14800 require_noerr_quiet( err
, exit
);
14801 if ( gExpensiveConstrainedTest_OutputFilePath
)
14803 context
->outputFilePath
= strdup( gExpensiveConstrainedTest_OutputFilePath
);
14804 require_noerr_quiet( context
->outputFilePath
, exit
);
14807 // initialize context
14808 context
->subtestIndex
= 0;
14809 context
->numOfRetries
= EXPENSIVE_CONSTRAINED_MAX_RETRIES
;
14811 // initialize the CFArray used to store the log
14812 context
->subtestReport
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14813 context
->testReport_startTime
= NanoTimeGetCurrent();
14815 // setup local DNS server
14816 ExpensiveConstrainedSetupLocalDNSServer( context
);
14818 ExpensiveConstrainedStartTestHandler( context
);
14826 //===========================================================================================================================
14827 // ExpensiveConstrainedSetupLocalDNSServer
14828 //===========================================================================================================================
14830 static void ExpensiveConstrainedSetupLocalDNSServer( ExpensiveConstrainedContext
*context
)
14832 pid_t current_pid
= getpid();
14833 OSStatus err
= SpawnCommand( &context
->serverPID
, "dnssdutil server -l --follow %d", current_pid
);
14836 FPrintF( stdout
, "dnssdutil server -l --follow <PID> failed, error: %d\n", err
);
14842 //===========================================================================================================================
14843 // ExpensiveConstrainedStartTestHandler
14844 //===========================================================================================================================
14846 static void ExpensiveConstrainedStartTestHandler( ExpensiveConstrainedContext
*context
)
14849 ExpensiveConstrainedSetupTimer( context
, EXPENSIVE_CONSTRAINED_TEST_INTERVAL
);
14851 // set the event handler for the 3s timer
14852 dispatch_source_set_event_handler( context
->timer
, ^{
14853 ExpensiveConstrainedTestTimerEventHandler( context
);
14856 dispatch_resume( context
->timer
);
14859 //===========================================================================================================================
14860 // ExpensiveConstrainedStartTestHandler
14861 //===========================================================================================================================
14863 static void ExpensiveConstrainedStopTestHandler( ExpensiveConstrainedContext
*context
)
14865 dispatch_cancel( context
->timer
);
14866 dispatch_release( context
->timer
);
14867 context
->timer
= NULL
;
14870 //===========================================================================================================================
14871 // ExpensiveConstrainedSetupTimer
14872 //===========================================================================================================================
14874 static void ExpensiveConstrainedSetupTimer( ExpensiveConstrainedContext
*context
, uint32_t second
)
14876 // set the timer source, the event handler will be called for every "second" seconds
14877 context
->timer
= dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, dispatch_get_main_queue() );
14878 if ( context
->timer
== NULL
)
14880 FPrintF( stdout
, "dispatch_source_create:DISPATCH_SOURCE_TYPE_TIMER failed\n" );
14883 // the first block will be put into the queue "second"s after calling dispatch_resume
14884 dispatch_source_set_timer( context
->timer
, dispatch_time( DISPATCH_TIME_NOW
, second
* NSEC_PER_SEC
),
14885 (unsigned long long)(second
) * NSEC_PER_SEC
, 100ull * NSEC_PER_MSEC
);
14888 //===========================================================================================================================
14889 // ExpensiveConstrainedTestTimerEventHandler
14890 //===========================================================================================================================
14892 static void ExpensiveConstrainedTestTimerEventHandler( ExpensiveConstrainedContext
*context
)
14895 char buffer
[ 1024 ];
14896 const char *errorDescription
= NULL
;
14898 // do not log the state if we are in transition state
14899 if (context
->state
!= TEST_BEGIN
14900 && context
->state
!= TEST_SUCCEEDED
14901 && context
->state
!= TEST_CONSTRAINED_PREPARE
14902 && context
->state
!= TEST_EXPENSIVE_CONSTRAINED_PREPARE
)
14903 ExpensiveConstrainedSubtestProgressReport( context
);
14905 switch ( context
->state
) {
14908 ExpensiveConstrainedStopTestHandler( context
);
14910 // clear mDNSResponder cache
14911 err
= systemf( NULL
, "killall -HUP mDNSResponder" );
14912 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed");
14914 // initialize the global parameters
14915 ExpensiveConstrainedInitializeContext( context
);
14917 // The local DNS server is set up on the local only interface.
14918 gExpensiveConstrainedTest_Interface
= LOOPBACK_INTERFACE_NAME
;
14919 strncpy( context
->ifName
, gExpensiveConstrainedTest_Interface
, sizeof( context
->ifName
) );
14921 // The local DNS server is unscoped, so we must set our question to unscoped.
14922 context
->ifIndex
= kDNSServiceInterfaceIndexAny
;
14924 // The question name must end with "d.test.", "tag-expensive-test.ttl-86400.d.test." for example, then the test will
14925 // use the local dns server set up previously to run the test locally.
14926 require_action( gExpensiveConstrainedTest_Name
!= NULL
&& expensiveConstrainedEndsWith( gExpensiveConstrainedTest_Name
, "d.test." ), test_failed
,
14927 SNPrintF( buffer
, sizeof( buffer
), "The question name (%s) must end with \"d.test.\".\n", gExpensiveConstrainedTest_Name
);
14928 errorDescription
= buffer
);
14930 // get the quesion name
14931 context
->name
= gExpensiveConstrainedTest_Name
;
14933 // set the initial state for the interface
14934 context
->startFromExpensive
= gExpensiveConstrainedTest_StartFromExpensive
;
14935 err
= systemf( NULL
, "ifconfig %s %sexpensive && ifconfig %s -constrained", context
->ifName
, context
->startFromExpensive
? "" : "-", context
->ifName
);
14936 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed");
14937 sleep( 5 ); // wait for 5s to allow the interface change event de delivered to others
14939 // get question flag
14940 if ( gExpensiveConstrainedTest_DenyExpensive
) context
->flags
|= kDNSServiceFlagsDenyExpensive
;
14941 if ( gExpensiveConstrainedTest_DenyConstrained
) context
->flags
|= kDNSServiceFlagsDenyConstrained
;
14942 if ( gExpensiveConstrainedTest_ProtocolIPv4
) context
->protocols
|= kDNSServiceProtocol_IPv4
;
14943 if ( gExpensiveConstrainedTest_ProtocolIPv6
) context
->protocols
|= kDNSServiceProtocol_IPv6
;
14945 // prevent mDNSResponder from doing extra path evaluation and changing the interface to others(such as Bluetooth)
14946 #if( TARGET_OS_WATCH )
14947 context
->flags
|= kDNSServiceFlagsPathEvaluationDone
;
14951 DNSServiceGetAddrInfo( &context
->opRef
, context
->flags
, context
->ifIndex
, context
->protocols
, context
->name
, ExpensiveConstrainedCallback
, context
);
14953 // set the initial test status
14954 context
->subtestReport_startTime
= NanoTimeGetCurrent();
14955 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
14956 context
->state
= TEST_EXPENSIVE_PREPARE
; // start from expensive test
14957 context
->isExpensiveNow
= context
->startFromExpensive
? true : false;
14958 context
->isConstrainedNow
= false;
14959 context
->expectedOperation
= context
->isExpensiveNow
&& ( context
->flags
& kDNSServiceFlagsDenyExpensive
) ? NO_UPDATE
: RESULT_ADD
;
14960 context
->operation
= NO_UPDATE
;
14961 context
->subtestProgress
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14962 require_action( context
->subtestProgress
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14963 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14964 require_action( context
->subtestProgress
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14966 // set the queue where the callback will be called when there is an answer for the query
14967 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
14968 require_noerr( err
, test_failed
);
14970 ExpensiveConstrainedStartTestHandler( context
);
14973 case TEST_EXPENSIVE_PREPARE
:
14974 require_action( context
->isConstrainedNow
== false, test_failed
,
14975 SNPrintF( buffer
, sizeof( buffer
), "Interface %s should be unconstrained.\n", context
->ifName
);
14976 errorDescription
= buffer
);
14977 require_action( context
->expectedOperation
== context
->operation
, test_failed
,
14978 errorDescription
= "Operation is not expected" );
14980 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
14981 context
->state
= TEST_EXPENSIVE
; // begin to test expensive flag
14982 context
->counter
= 0; // the number of test repetition that has passed
14983 context
->isExpensivePrev
= context
->isExpensiveNow
;
14984 context
->isExpensiveNow
= !context
->isExpensiveNow
; // flip the expensive status
14985 context
->isConstrainedPrev
= false; // the interface is currently unconstrained
14986 context
->isConstrainedNow
= false; // the interface will be unconstrained in the current test
14987 if ( gExpensiveConstrainedTest_DenyExpensive
)
14988 context
->expectedOperation
= context
->isExpensiveNow
? RESULT_RMV
: RESULT_ADD
;
14990 context
->expectedOperation
= NO_UPDATE
;
14991 context
->operation
= NO_UPDATE
; // NO_UPDATE means the call back function has not been called
14992 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14993 require_action( context
->subtestProgress_callBack
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14995 err
= systemf( NULL
, "ifconfig %s %sexpensive", context
->ifName
, context
->isExpensiveNow
? "" : "-" );
14996 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
14998 // record the starting timestamp
14999 gettimeofday( &context
->updateTime
, NULL
);
15002 case TEST_EXPENSIVE
:
15003 // Since we are testing expensive flag, we should always turn the expensive flag on and off.
15004 require_action( context
->isExpensivePrev
^ context
->isExpensiveNow
, test_failed
,
15005 SNPrintF( buffer
, sizeof( buffer
), "The current expensive status should be different with the previous one: %d -> %d\n", context
->isExpensivePrev
, context
->isExpensiveNow
);
15006 errorDescription
= buffer
);
15007 // constrained flag is always turned off when testing expensive
15008 require_action( context
->isConstrainedNow
== false, test_failed
,
15009 SNPrintF( buffer
, sizeof( buffer
), "The interface %s should be unconstrained when testing \"expensive\"\n", context
->ifName
);
15010 errorDescription
= buffer
);
15011 require_action( context
->expectedOperation
== context
->operation
, test_failed
, errorDescription
= "Operation is not expected" );
15013 context
->counter
++; // one test repetition has passed
15014 if ( context
->counter
== TEST_REPETITION
) // expensive test finished
15016 // prepare to test constrained flag
15017 context
->state
= TEST_CONSTRAINED_PREPARE
;
15019 // reset the interface
15020 err
= systemf( NULL
, "ifconfig %s -expensive && ifconfig %s -constrained", context
->ifName
, context
->ifName
);
15021 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
15023 context
->isExpensiveNow
= false;
15024 context
->isConstrainedNow
= false;
15025 gettimeofday( &context
->updateTime
, NULL
);
15029 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
15030 context
->isExpensivePrev
= context
->isExpensiveNow
;
15031 context
->isExpensiveNow
= !context
->isExpensiveNow
; // flip the expensive status
15032 if ( gExpensiveConstrainedTest_DenyExpensive
)
15033 context
->expectedOperation
= context
->isExpensiveNow
? RESULT_RMV
: RESULT_ADD
;
15035 context
->expectedOperation
= NO_UPDATE
;
15036 context
->operation
= NO_UPDATE
;
15037 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
15038 require_action( context
->subtestProgress_callBack
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
15040 err
= systemf( NULL
, "ifconfig %s %sexpensive", context
->ifName
, context
->isExpensiveNow
? "" : "-" );
15041 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
15043 gettimeofday( &context
->updateTime
, NULL
);
15046 case TEST_CONSTRAINED_PREPARE
:
15047 // The interface should be inexpensive and unconstrained when the constrained test starts
15048 require_action( context
->isExpensiveNow
== false, test_failed
, SNPrintF( buffer
, sizeof( buffer
), "Interface %s should be inexpensive.", context
->ifName
);
15049 errorDescription
= buffer
);
15050 require_action( context
->isConstrainedNow
== false, test_failed
, SNPrintF( buffer
, sizeof( buffer
), "Interface %s should be unconstrained.\n", context
->ifName
);
15051 errorDescription
= buffer
);
15053 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
15054 context
->state
= TEST_CONSTRAINED
; // constrained interface is now under testing
15055 context
->counter
= 0;
15056 context
->isExpensivePrev
= false;
15057 context
->isExpensiveNow
= false;
15058 context
->isConstrainedPrev
= false;
15059 context
->isConstrainedNow
= true; // will set constrained flag on the interface
15060 if ( gExpensiveConstrainedTest_DenyConstrained
)
15061 context
->expectedOperation
= RESULT_RMV
;
15063 context
->expectedOperation
= NO_UPDATE
;
15064 context
->operation
= NO_UPDATE
;
15065 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
15066 require_action( context
->subtestProgress_callBack
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
15068 // change interface to the constrained one
15069 err
= systemf( NULL
, "ifconfig %s -expensive && ifconfig %s constrained", context
->ifName
, context
->ifName
);
15070 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
15072 gettimeofday( &context
->updateTime
, NULL
);
15074 case TEST_CONSTRAINED
:
15075 // Since we are testing constrained flag, we should always turn the constrained flag on and off.
15076 require_action( context
->isConstrainedPrev
^ context
->isConstrainedNow
, test_failed
,
15077 SNPrintF( buffer
, sizeof( buffer
), "The current constrained status should be different with the previous one: %d -> %d\n", context
->isConstrainedPrev
, context
->isConstrainedNow
);
15078 errorDescription
= buffer
);
15079 require_action( context
->isExpensiveNow
== false, test_failed
,
15080 SNPrintF( buffer
, sizeof( buffer
), "The interface %s should be inexpensive when testing \"constrained\"\n", context
->ifName
);
15081 errorDescription
= buffer
);
15082 require_action( context
->expectedOperation
== context
->operation
, test_failed
, errorDescription
= "Operation is not expected");
15084 context
->counter
++;
15085 if (context
->counter
== TEST_REPETITION
)
15087 // test changing expensive and constrained flags at the same time
15088 context
->state
= TEST_EXPENSIVE_CONSTRAINED_PREPARE
;
15091 err
= systemf( NULL
, "ifconfig %s -expensive && ifconfig %s -constrained", context
->ifName
, context
->ifName
);
15092 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
15094 context
->isExpensiveNow
= false;
15095 context
->isConstrainedNow
= false;
15096 gettimeofday( &context
->updateTime
, NULL
);
15100 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
15101 context
->isConstrainedPrev
= context
->isConstrainedNow
;
15102 context
->isConstrainedNow
= !context
->isConstrainedNow
; // flip constrained flag
15103 if ( gExpensiveConstrainedTest_DenyConstrained
)
15104 context
->expectedOperation
= context
->isConstrainedNow
? RESULT_RMV
: RESULT_ADD
;
15106 context
->expectedOperation
= NO_UPDATE
;
15107 context
->operation
= NO_UPDATE
;
15108 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
15109 require_action( context
->subtestProgress_callBack
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
15111 err
= systemf( NULL
, "ifconfig %s %sconstrained", context
->ifName
, context
->isConstrainedNow
? "" : "-" );
15112 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
15114 gettimeofday(&context
->updateTime
, NULL
);
15117 case TEST_EXPENSIVE_CONSTRAINED_PREPARE
:
15118 // The interface should be inexpensive and unconstrained when the constrained test starts
15119 require_action( context
->isExpensiveNow
== false, test_failed
,
15120 SNPrintF( buffer
, sizeof( buffer
), "Interface %s should be inexpensive.\n", context
->ifName
);
15121 errorDescription
= buffer
);
15122 require_action( context
->isConstrainedNow
== false, test_failed
,
15123 SNPrintF(buffer
, sizeof( buffer
), "Interface %s should be unconstrained.\n", context
->ifName
);
15124 errorDescription
= buffer
);
15126 // now flip expensive and constrained at the same time
15127 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
15128 context
->state
= TEST_EXPENSIVE_CONSTRAINED
;
15129 context
->counter
= 0;
15130 context
->isExpensivePrev
= false;
15131 context
->isExpensiveNow
= true;
15132 context
->isConstrainedPrev
= false;
15133 context
->isConstrainedNow
= true;
15134 if (gExpensiveConstrainedTest_DenyConstrained
|| gExpensiveConstrainedTest_DenyExpensive
)
15135 context
->expectedOperation
= RESULT_RMV
;
15137 context
->expectedOperation
= NO_UPDATE
;
15138 context
->operation
= NO_UPDATE
;
15139 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
15140 require_action( context
->subtestProgress_callBack
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
15142 err
= systemf(NULL
, "ifconfig %s expensive && ifconfig %s constrained", context
->ifName
, context
->ifName
);
15143 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
15145 gettimeofday( &context
->updateTime
, NULL
);
15147 case TEST_EXPENSIVE_CONSTRAINED
:
15148 // expensive and constrained flag should always be changed
15149 require_action( ( context
->isExpensivePrev
^ context
->isExpensiveNow
) && ( context
->isConstrainedPrev
^ context
->isConstrainedNow
), test_failed
,
15150 SNPrintF( buffer
, sizeof( buffer
), "Both expensive and constrained status need to be changed" );
15151 errorDescription
= buffer
);
15152 require_action( context
->isExpensiveNow
== context
->isConstrainedNow
, test_failed
, errorDescription
= "context->isExpensiveNow != context->isConstrainedNow" );
15153 require_action( context
->expectedOperation
== context
->operation
, test_failed
, errorDescription
= "Operation is not expected" );
15155 context
->counter
++;
15156 if ( context
->counter
== TEST_REPETITION
)
15158 context
->state
= TEST_SUCCEEDED
;
15162 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
15163 context
->isExpensivePrev
= context
->isExpensiveNow
;
15164 context
->isExpensiveNow
= !context
->isExpensiveNow
;
15165 context
->isConstrainedPrev
= context
->isConstrainedNow
;
15166 context
->isConstrainedNow
= !context
->isConstrainedNow
;
15167 if (gExpensiveConstrainedTest_DenyConstrained
|| gExpensiveConstrainedTest_DenyExpensive
)
15168 context
->expectedOperation
= context
->isExpensiveNow
? RESULT_RMV
: RESULT_ADD
;
15170 context
->expectedOperation
= NO_UPDATE
;
15171 context
->operation
= NO_UPDATE
;
15172 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
15173 require_action( context
->subtestProgress_callBack
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
15175 err
= systemf( NULL
, "ifconfig %s %sexpensive && ifconfig %s %sconstrained", context
->ifName
, context
->isExpensiveNow
? "" : "-", context
->ifName
, context
->isConstrainedNow
? "" : "-" );
15176 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
15178 gettimeofday( &context
->updateTime
, NULL
);
15183 ExpensiveConstrainedSubtestReport( context
, errorDescription
);
15184 ExpensiveConstrainedStopAndCleanTheTest( context
);
15185 if ( context
->numOfRetries
> 0 )
15187 context
->state
= TEST_BEGIN
;
15188 context
->numOfRetries
--;
15191 ExpensiveConstrainedSubtestParams
[context
->subtestIndex
++].test_passed
= 0;
15192 if (context
->subtestIndex
== (int) countof( ExpensiveConstrainedSubtestParams
))
15194 ExpensiveConstrainedFinalResultReport( context
, false );
15197 if (context
->timer
== NULL
)
15199 // If timer is NULL, it means that we encounter error before we set up the test handler, which is unrecoverable.
15200 ExpensiveConstrainedFinalResultReport( context
, false );
15203 context
->state
= TEST_BEGIN
;
15205 case TEST_SUCCEEDED
:
15206 ExpensiveConstrainedSubtestReport( context
, NULL
);
15207 ExpensiveConstrainedStopAndCleanTheTest( context
);
15208 ExpensiveConstrainedSubtestParams
[context
->subtestIndex
++].test_passed
= 1;
15209 if (context
->subtestIndex
== (int) countof( ExpensiveConstrainedSubtestParams
))
15211 // all the subtests have been run
15212 Boolean hasFailed
= false;
15213 for ( int i
= 0; i
< (int) countof( ExpensiveConstrainedSubtestParams
) && !hasFailed
; i
++ )
15214 hasFailed
= ( ExpensiveConstrainedSubtestParams
[i
].test_passed
!= 1 );
15216 ExpensiveConstrainedFinalResultReport( context
, !hasFailed
);
15217 exit( hasFailed
? 2 : 0 );
15219 context
->state
= TEST_BEGIN
;
15222 FPrintF( stdout
, "unknown error\n" );
15227 //===========================================================================================================================
15228 // ExpensiveConstrainedCallback
15229 //===========================================================================================================================
15231 static void DNSSD_API
15232 ExpensiveConstrainedCallback(
15233 __unused DNSServiceRef inSDRef
,
15234 DNSServiceFlags inFlags
,
15235 uint32_t inInterfaceIndex
,
15236 DNSServiceErrorType inError
,
15237 const char * inHostname
,
15238 const struct sockaddr
* inSockAddr
,
15239 __unused
uint32_t inTTL
,
15242 ExpensiveConstrainedContext
* const context
= (ExpensiveConstrainedContext
*)inContext
;
15244 const char * addrStr
;
15245 char addrStrBuf
[ kSockAddrStringMaxSize
];
15246 char inFlagsDescription
[ 128 ];
15248 char nowTimestamp
[ 32 ];
15250 switch ( inError
) {
15251 case kDNSServiceErr_NoError
:
15252 case kDNSServiceErr_NoSuchRecord
:
15255 case kDNSServiceErr_Timeout
:
15256 Exit( kExitReason_Timeout
);
15263 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
15265 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
15272 err
= SockAddrToString( inSockAddr
, kSockAddrStringFlagsNone
, addrStrBuf
);
15273 require_noerr( err
, exit
);
15274 addrStr
= addrStrBuf
;
15278 addrStr
= ( inSockAddr
->sa_family
== AF_INET
) ? kNoSuchRecordAStr
: kNoSuchRecordAAAAStr
;
15281 now
= NanoTimeGetCurrent();
15282 _NanoTime64ToTimestamp( now
, nowTimestamp
, sizeof( nowTimestamp
) );
15283 SNPrintF( inFlagsDescription
, sizeof( inFlagsDescription
), "%{du:cbflags}", inFlags
);
15284 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, context
->subtestProgress_callBack
,
15292 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_TIMESTAMP
, nowTimestamp
,
15293 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_NAME
, inHostname
,
15294 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_FLAGS
, inFlagsDescription
,
15295 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_INTERFACE
, (int64_t) inInterfaceIndex
,
15296 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_ADDRESS
, addrStr
15298 require_noerr_quiet( err
, exit
);
15300 if ( inFlags
& kDNSServiceFlagsMoreComing
)
15303 if ( inFlags
& kDNSServiceFlagsAdd
)
15304 context
->operation
= RESULT_ADD
;
15306 context
->operation
= RESULT_RMV
;
15308 gettimeofday(&context
->notificationTime
, NULL
);
15310 if( err
) exit( 1 );
15313 //===========================================================================================================================
15314 // ExpensiveConstrainedInitializeContext
15315 //===========================================================================================================================
15317 static void ExpensiveConstrainedInitializeContext( ExpensiveConstrainedContext
*context
)
15319 // clear the flags of the previous subtest
15320 context
->flags
= 0;
15321 context
->protocols
= 0;
15323 // get the parameter for the current subtest
15324 const ExpensiveConstrainedTestParams
*param
= &ExpensiveConstrainedSubtestParams
[context
->subtestIndex
];
15325 gExpensiveConstrainedTest_Name
= param
->qname
;
15326 gExpensiveConstrainedTest_DenyExpensive
= param
->deny_expensive
;
15327 gExpensiveConstrainedTest_DenyConstrained
= param
->deny_constrained
;
15328 gExpensiveConstrainedTest_StartFromExpensive
= param
->start_from_expensive
;
15329 gExpensiveConstrainedTest_ProtocolIPv4
= param
->ipv4_query
;
15330 gExpensiveConstrainedTest_ProtocolIPv6
= param
->ipv6_query
;
15333 //===========================================================================================================================
15334 // ExpensiveConstrainedStopAndCleanTheTest
15335 //===========================================================================================================================
15337 static void ExpensiveConstrainedStopAndCleanTheTest( ExpensiveConstrainedContext
*context
)
15339 // Stop the ongoing query
15340 if ( context
->opRef
!= NULL
)
15341 DNSServiceRefDeallocate( context
->opRef
);
15343 context
->opRef
= NULL
;
15344 context
->flags
= 0;
15345 context
->protocols
= 0;
15348 //===========================================================================================================================
15349 // ExpensiveConstrainedSubtestProgressReport
15350 //===========================================================================================================================
15352 static void ExpensiveConstrainedSubtestProgressReport( ExpensiveConstrainedContext
*context
)
15356 char startTime
[ 32 ];
15357 char endTime
[ 32 ];
15358 char expensive
[ 32 ];
15359 char constrained
[ 32 ];
15361 now
= NanoTimeGetCurrent();
15362 _NanoTime64ToTimestamp( context
->subtestProgress_startTime
, startTime
, sizeof( startTime
) );
15363 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
15365 snprintf( expensive
, sizeof( expensive
), "%s -> %s", context
->isExpensivePrev
? "True" : "False", context
->isExpensiveNow
? "True" : "False" );
15366 snprintf( constrained
, sizeof( constrained
), "%s -> %s", context
->isConstrainedPrev
? "True" : "False", context
->isConstrainedNow
? "True" : "False" );
15368 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, context
->subtestProgress
,
15379 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_START_TIME
, startTime
,
15380 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_END_TIME
, endTime
,
15381 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_STATE
, ExpensiveConstrainedStateString(context
->state
),
15382 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPECT_RESULT
, ExpensiveConstrainedOperationString(context
->expectedOperation
),
15383 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_ACTUAL_RESULT
, ExpensiveConstrainedOperationString(context
->operation
),
15384 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPENSIVE_PREV_NOW
, expensive
,
15385 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CONSTRAINED_PREV_NOW
, constrained
,
15386 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CALL_BACK
, context
->subtestProgress_callBack
15388 require_noerr( err
, exit
);
15389 ForgetCF( &context
->subtestProgress_callBack
);
15393 ErrQuit( 1, "error: %#m\n", err
);
15396 //===========================================================================================================================
15397 // ExpensiveConstrainedFinalSubtestReport
15398 //===========================================================================================================================
15400 static void ExpensiveConstrainedSubtestReport( ExpensiveConstrainedContext
*context
, const char *error_description
)
15404 char startTime
[ 32 ];
15405 char endTime
[ 32 ];
15406 char flagDescription
[ 1024 ];
15408 now
= NanoTimeGetCurrent();
15409 _NanoTime64ToTimestamp( context
->subtestReport_startTime
, startTime
, sizeof( startTime
) );
15410 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
15411 SNPrintF( flagDescription
, sizeof( flagDescription
), "%#{flags}", context
->flags
, kDNSServiceFlagsDescriptors
);
15413 if (error_description
!= NULL
)
15415 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, context
->subtestReport
,
15428 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME
, startTime
,
15429 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME
, endTime
,
15430 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME
, context
->name
,
15431 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS
, flagDescription
,
15432 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS
, ExpensiveConstrainedProtocolString( context
->protocols
),
15433 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX
, (int64_t) context
->ifIndex
,
15434 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME
, context
->ifName
,
15435 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT
, CFSTR( "Fail" ),
15436 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_ERROR
, error_description
,
15437 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS
, context
->subtestProgress
15442 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, context
->subtestReport
,
15454 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME
, startTime
,
15455 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME
, endTime
,
15456 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME
, context
->name
,
15457 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS
, flagDescription
,
15458 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS
, ExpensiveConstrainedProtocolString( context
->protocols
),
15459 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX
, (int64_t) context
->ifIndex
,
15460 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME
, context
->ifName
,
15461 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT
, CFSTR( "Pass" ),
15462 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS
, context
->subtestProgress
15466 require_noerr( err
, exit
);
15467 ForgetCF( &context
->subtestProgress
);
15470 ErrQuit( 1, "error: %#m\n", err
);
15473 //===========================================================================================================================
15474 // ExpensiveConstrainedFinalResultReport
15475 //===========================================================================================================================
15477 static void ExpensiveConstrainedFinalResultReport( ExpensiveConstrainedContext
*context
, Boolean allPassed
)
15480 CFPropertyListRef plist
;
15482 char startTime
[ 32 ];
15483 char endTime
[ 32 ];
15485 now
= NanoTimeGetCurrent();
15486 _NanoTime64ToTimestamp( context
->testReport_startTime
, startTime
, sizeof( startTime
) );
15487 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
15489 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
15496 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_START_TIME
, startTime
,
15497 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_END_TIME
, endTime
,
15498 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_ALL_PASSED
, allPassed
,
15499 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_SUBTEST_RESULT
, context
->subtestReport
15501 require_noerr( err
, exit
);
15502 ForgetCF( &context
->subtestReport
);
15504 err
= OutputPropertyList( plist
, context
->outputFormat
, context
->outputFilePath
);
15505 CFRelease( plist
);
15506 require_noerr( err
, exit
);
15510 ErrQuit( 1, "error: %#m\n", err
);
15513 //===========================================================================================================================
15514 // ExpensiveConstrainedProtocolString
15515 //===========================================================================================================================
15517 static const char *ExpensiveConstrainedProtocolString( DNSServiceProtocol protocol
)
15519 const char *str
= NULL
;
15520 switch ( protocol
) {
15521 case kDNSServiceProtocol_IPv4
:
15524 case kDNSServiceProtocol_IPv6
:
15527 case kDNSServiceProtocol_IPv4
| kDNSServiceProtocol_IPv6
:
15528 str
= "IPv4 & IPv6";
15536 //===========================================================================================================================
15537 // ExpensiveConstrainedStateString
15538 //===========================================================================================================================
15540 static const char *ExpensiveConstrainedStateString( enum ExpensiveConstrainedTestState state
)
15542 const char *str
= NULL
;
15545 str
= "TEST_BEGIN";
15547 case TEST_EXPENSIVE_PREPARE
:
15548 str
= "TEST_EXPENSIVE_PREPARE";
15550 case TEST_EXPENSIVE
:
15551 str
= "TEST_EXPENSIVE";
15553 case TEST_CONSTRAINED_PREPARE
:
15554 str
= "TEST_CONSTRAINED_PREPARE";
15556 case TEST_CONSTRAINED
:
15557 str
= "TEST_CONSTRAINED";
15559 case TEST_EXPENSIVE_CONSTRAINED_PREPARE
:
15560 str
= "TEST_EXPENSIVE_CONSTRAINED_PREPARE";
15562 case TEST_EXPENSIVE_CONSTRAINED
:
15563 str
= "TEST_EXPENSIVE_CONSTRAINED";
15566 str
= "TEST_FAILED";
15568 case TEST_SUCCEEDED
:
15569 str
= "TEST_SUCCEEDED";
15579 //===========================================================================================================================
15580 // ExpensiveConstrainedOperationString
15581 //===========================================================================================================================
15583 static const char *ExpensiveConstrainedOperationString( enum ExpensiveConstrainedTestOperation operation
)
15585 const char *str
= NULL
;
15586 switch ( operation
) {
15588 str
= "RESULT_ADD";
15591 str
= "RESULT_RMV";
15603 //===========================================================================================================================
15604 // expensiveConstrainedEndsWith
15605 //===========================================================================================================================
15606 static Boolean
expensiveConstrainedEndsWith( const char *str
, const char *suffix
)
15608 if ( !str
|| !suffix
)
15610 size_t lenstr
= strlen( str
);
15611 size_t lensuffix
= strlen( suffix
);
15612 if ( lensuffix
> lenstr
)
15614 return strncmp( str
+ lenstr
- lensuffix
, suffix
, lensuffix
) == 0;
15617 //===========================================================================================================================
15618 // RegistrationTestCmd
15619 //===========================================================================================================================
15621 typedef struct RegistrationSubtest RegistrationSubtest
;
15625 CFMutableArrayRef subtestReports
; // Array of subtest reports.
15626 dispatch_source_t timer
; // Timer to enforce subtest durations.
15627 dispatch_source_t sigSourceINT
; // SIGINT signal handler for a clean test exit.
15628 dispatch_source_t sigSourceTERM
; // SIGTERM signal handler for a clean test exit.
15629 RegistrationSubtest
* subtest
; // Current subtest.
15630 char * outputFilePath
; // Path of test result output file. If NULL, stdout will be used.
15631 OutputFormatType outputFormat
; // Format of test results output.
15632 CFStringRef computerNamePrev
; // Previous ComputerName.
15633 CFStringRef localHostNamePrev
; // Previous LocalHostName.
15634 NanoTime64 startTime
; // Test's start time.
15635 char * computerName
; // Temporary ComputerName to set during testing. (malloc'd)
15636 char * localHostName
; // Temporary LocalHostName to set during testing. (malloc'd)
15637 CFStringEncoding computerNamePrevEncoding
; // Previous ComputerName's encoding.
15638 int subtestIndex
; // Index of current subtest.
15639 Boolean computerNameSet
; // True if a temporary ComputerName was set.
15640 Boolean localHostNameSet
; // True if a temporary LocalHostName was set.
15641 Boolean failed
; // True if at least one non-skipped subtest failed.
15642 Boolean forBATS
; // True if the test is running in a BATS environment.
15644 } RegistrationTest
;
15648 kRegistrationInterfaceSet_Null
= 0,
15649 kRegistrationInterfaceSet_All
= 1,
15650 kRegistrationInterfaceSet_AllPlusAWDL
= 2,
15651 kRegistrationInterfaceSet_LoopbackOnly
= 3,
15652 kRegistrationInterfaceSet_AWDLOnly
= 4
15654 } RegistrationInterfaceSet
;
15658 RegistrationInterfaceSet interfaceSet
; // Interfaces to register the service over.
15659 Boolean useDefaultName
; // True if registration is to use the default service name.
15660 Boolean useLODiscovery
; // True if discovery is to use kDNSServiceInterfaceIndexLocalOnly.
15662 } RegistrationSubtestParams
;
15664 static const RegistrationSubtestParams kRegistrationSubtestParams
[] =
15666 { kRegistrationInterfaceSet_All
, true, false },
15667 { kRegistrationInterfaceSet_All
, false, false },
15668 { kRegistrationInterfaceSet_AllPlusAWDL
, true, false },
15669 { kRegistrationInterfaceSet_AllPlusAWDL
, false, false },
15670 { kRegistrationInterfaceSet_LoopbackOnly
, true, false },
15671 { kRegistrationInterfaceSet_LoopbackOnly
, false, false },
15672 { kRegistrationInterfaceSet_AWDLOnly
, true, false },
15673 { kRegistrationInterfaceSet_AWDLOnly
, false, false },
15674 { kRegistrationInterfaceSet_All
, true, true },
15675 { kRegistrationInterfaceSet_All
, false, true },
15676 { kRegistrationInterfaceSet_AllPlusAWDL
, true, true },
15677 { kRegistrationInterfaceSet_AllPlusAWDL
, false, true },
15678 { kRegistrationInterfaceSet_LoopbackOnly
, true, true },
15679 { kRegistrationInterfaceSet_LoopbackOnly
, false, true },
15680 { kRegistrationInterfaceSet_AWDLOnly
, true, true },
15681 { kRegistrationInterfaceSet_AWDLOnly
, false, true }
15686 NanoTime64 browseResultTime
; // Per-interface browse result time.
15687 NanoTime64 querySRVResultTime
; // Per-interface SRV record query result time.
15688 NanoTime64 queryTXTResultTime
; // Per-interface TXT record query result time.
15690 } RegistrationResultTimes
;
15694 MDNSInterfaceItem base
; // Underlying MDNSInterface linked-list item.
15695 RegistrationResultTimes times
; // Per-interface result times.
15697 } RegistrationInterfaceItem
;
15699 struct RegistrationSubtest
15701 DNSServiceRef registration
; // DNS-SD service registration.
15702 DNSServiceRef connection
; // Shared DNS-SD connection.
15703 DNSServiceRef browse
; // DNS-SD browse for service's type.
15704 DNSServiceRef querySRV
; // DNS-SD query request for service's SRV record.
15705 DNSServiceRef queryTXT
; // DNS-SD query request for service's TXT record.
15706 CFMutableArrayRef unexpected
; // Array of unexpected registration, browse, and query results.
15707 #if( TARGET_OS_WATCH )
15708 CFMutableArrayRef ignored
; // Array of unexpected, but ignored, browse and query results.
15710 const char * serviceName
; // Service's name.
15711 char * serviceNameCustom
; // Service's name if using a custom name. (malloc'd)
15712 char * serviceType
; // Service's service type. (malloc'd)
15713 size_t serviceTypeLen
; // C string length of service's service type.
15714 char * serviceFQDN
; // Service's FQDN, i.e., name of its SRV and TXT records.
15715 uint8_t * txtPtr
; // Pointer to service's TXT record data. (malloc'd)
15716 size_t txtLen
; // Length of service's TXT record data.
15717 RegistrationInterfaceItem
* ifList
; // If ifIndex == 0, interfaces that service should register over.
15718 RegistrationResultTimes ifTimes
; // If ifIndex != 0, result times for interface with that index.
15719 RegistrationTest
* test
; // Pointer to parent test.
15720 NanoTime64 startTime
; // Subtest's start time.
15721 char * description
; // Subtest's description. (malloc'd)
15722 uint32_t ifIndex
; // Interface index used for service registration.
15723 uint16_t port
; // Service's port number.
15724 Boolean useLODiscovery
; // True if discovery is to use kDNSServiceInterfaceIndexLocalOnly.
15725 Boolean includeAWDL
; // True if the IncludeAWDL flag was used during registration.
15726 Boolean ifIsAWDL
; // True if ifIndex is the index of an AWDL interface.
15727 Boolean skipped
; // True if this subtest is to be skipped.
15728 Boolean registered
; // True if the test service was successfully registered.
15729 Boolean useDefaultName
; // True if the service is to use the default service name.
15732 static OSStatus
_RegistrationTestCreate( RegistrationTest
**outTest
);
15733 static void _RegistrationTestFree( RegistrationTest
*inTest
);
15734 static void _RegistrationTestBegin( void *inContext
);
15735 static void _RegistrationTestProceed( RegistrationTest
*inTest
);
15736 static OSStatus
_RegistrationTestStart( RegistrationTest
*inTest
);
15737 static void _RegistrationTestStop( RegistrationTest
*inTest
);
15738 #define _RegistrationTestForget( X ) ForgetCustomEx( X, _RegistrationTestStop, _RegistrationTestFree )
15740 _RegistrationTestStartSubtest(
15741 RegistrationTest
* inTest
,
15742 const RegistrationSubtestParams
* inParams
,
15743 Boolean
* outSkipped
);
15744 static OSStatus
_RegistrationTestEndSubtest( RegistrationTest
*inTest
);
15745 static void _RegistrationTestEnd( RegistrationTest
*inTest
) ATTRIBUTE_NORETURN
;
15746 static void _RegistrationTestExit( RegistrationTest
*inTest
, OSStatus inError
) ATTRIBUTE_NORETURN
;
15747 static OSStatus
_RegistrationSubtestCreate( RegistrationSubtest
**outSubtest
);
15748 static void _RegistrationSubtestStop( RegistrationSubtest
*inSubtest
);
15749 static void _RegistrationSubtestFree( RegistrationSubtest
*inSubtest
);
15750 #define _RegistrationSubtestForget( X ) ForgetCustomEx( X, _RegistrationSubtestStop, _RegistrationSubtestFree )
15751 static OSStatus
_RegistrationTestInterfaceListCreate( Boolean inIncludeAWDL
, RegistrationInterfaceItem
**outList
);
15753 _RegistrationTestCreateRandomTXTRecord(
15756 uint8_t ** outTXTPtr
,
15757 size_t * outTXTLen
);
15758 static void DNSSD_API
15759 _RegistrationSubtestRegisterCallback(
15760 DNSServiceRef inSDRef
,
15761 DNSServiceFlags inFlags
,
15762 DNSServiceErrorType inError
,
15763 const char * inName
,
15764 const char * inType
,
15765 const char * inDomain
,
15766 void * inContext
);
15767 static void DNSSD_API
15768 _RegistrationSubtestBrowseCallback(
15769 DNSServiceRef inSDRef
,
15770 DNSServiceFlags inFlags
,
15771 uint32_t inIfIndex
,
15772 DNSServiceErrorType inError
,
15773 const char * inServiceName
,
15774 const char * inServiceType
,
15775 const char * inDomain
,
15776 void * inContext
);
15777 static void DNSSD_API
15778 _RegistrationSubtestQueryCallback(
15779 DNSServiceRef inSDRef
,
15780 DNSServiceFlags inFlags
,
15781 uint32_t inIfIndex
,
15782 DNSServiceErrorType inError
,
15783 const char * inName
,
15786 uint16_t inRDataLen
,
15787 const void * inRDataPtr
,
15789 void * inContext
);
15790 static Boolean
_RegistrationSubtestValidServiceType( const RegistrationSubtest
*inSubtest
, const char *inServiceType
);
15791 static RegistrationResultTimes
*
15792 _RegistrationSubtestGetInterfaceResultTimes(
15793 RegistrationSubtest
* inSubtest
,
15794 uint32_t inIfIndex
,
15795 Boolean
* outIsAWDL
);
15796 static void _RegistrationTestTimerHandler( void *inContext
);
15797 #if( TARGET_OS_WATCH )
15798 static Boolean
_RegistrationTestInterfaceIsWiFi( const char *inIfName
);
15801 static void RegistrationTestCmd( void )
15804 RegistrationTest
* test
;
15806 err
= _RegistrationTestCreate( &test
);
15807 require_noerr( err
, exit
);
15809 if( gRegistrationTest_BATSEnvironment
) test
->forBATS
= true;
15810 if( gRegistrationTest_OutputFilePath
)
15812 test
->outputFilePath
= strdup( gRegistrationTest_OutputFilePath
);
15813 require_action( test
->outputFilePath
, exit
, err
= kNoMemoryErr
);
15816 err
= OutputFormatFromArgString( gRegistrationTest_OutputFormat
, &test
->outputFormat
);
15817 require_noerr_quiet( err
, exit
);
15819 dispatch_async_f( dispatch_get_main_queue(), test
, _RegistrationTestBegin
);
15823 if( test
) _RegistrationTestFree( test
);
15824 ErrQuit( 1, "error: %#m\n", err
);
15827 //===========================================================================================================================
15829 static OSStatus
_RegistrationTestCreate( RegistrationTest
**outTest
)
15832 RegistrationTest
* obj
;
15834 obj
= (RegistrationTest
*) calloc( 1, sizeof( *obj
) );
15835 require_action( obj
, exit
, err
= kNoMemoryErr
);
15837 obj
->subtestReports
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
15838 require_action( obj
->subtestReports
, exit
, err
= kNoMemoryErr
);
15845 if( obj
) _RegistrationTestFree( obj
);
15849 //===========================================================================================================================
15851 static void _RegistrationTestFree( RegistrationTest
*inTest
)
15853 check( !inTest
->timer
);
15854 check( !inTest
->sigSourceINT
);
15855 check( !inTest
->sigSourceTERM
);
15856 check( !inTest
->computerNameSet
);
15857 check( !inTest
->localHostNameSet
);
15858 check( !inTest
->subtest
);
15859 ForgetCF( &inTest
->subtestReports
);
15860 ForgetMem( &inTest
->outputFilePath
);
15861 ForgetCF( &inTest
->computerNamePrev
);
15862 ForgetCF( &inTest
->localHostNamePrev
);
15863 ForgetMem( &inTest
->computerName
);
15864 ForgetMem( &inTest
->localHostName
);
15867 //===========================================================================================================================
15869 static void _RegistrationTestBegin( void *inContext
)
15871 _RegistrationTestProceed( (RegistrationTest
*) inContext
);
15874 //===========================================================================================================================
15876 static void _RegistrationTestProceed( RegistrationTest
*inTest
)
15879 Boolean skippedSubtest
;
15885 if( !inTest
->startTime
)
15887 err
= _RegistrationTestStart( inTest
);
15888 require_noerr_quiet( err
, exit
);
15890 inTest
->startTime
= NanoTimeGetCurrent();
15894 err
= _RegistrationTestEndSubtest( inTest
);
15895 require_noerr( err
, exit
);
15897 ++inTest
->subtestIndex
;
15900 subtestIndex
= inTest
->subtestIndex
;
15901 if( subtestIndex
< (int) countof( kRegistrationSubtestParams
) )
15903 err
= _RegistrationTestStartSubtest( inTest
, &kRegistrationSubtestParams
[ subtestIndex
], &skippedSubtest
);
15904 require_noerr_quiet( err
, exit
);
15908 _RegistrationTestEnd( inTest
);
15911 } while( skippedSubtest
);
15914 if( err
) _RegistrationTestExit( inTest
, err
);
15917 //===========================================================================================================================
15919 static void _RegistrationTestSignalHandler( void *inContext
);
15921 static OSStatus
_RegistrationTestStart( RegistrationTest
*inTest
)
15926 // Save original ComputerName and LocalHostName.
15928 check( !inTest
->computerNamePrev
);
15929 inTest
->computerNamePrev
= SCDynamicStoreCopyComputerName( NULL
, &inTest
->computerNamePrevEncoding
);
15930 err
= map_scerror( inTest
->computerNamePrev
);
15931 require_noerr( err
, exit
);
15933 check( !inTest
->localHostNamePrev
);
15934 inTest
->localHostNamePrev
= SCDynamicStoreCopyLocalHostName( NULL
);
15935 err
= map_scerror( inTest
->localHostNamePrev
);
15936 require_noerr( err
, exit
);
15938 // Generate a unique test ComputerName.
15940 check( !inTest
->computerName
);
15941 ASPrintF( &inTest
->computerName
, "dnssdutil-regtest-computer-name-%s",
15942 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
15943 require_action( inTest
->computerName
, exit
, err
= kNoMemoryErr
);
15945 // Generate a unique test LocalHostName.
15947 check( !inTest
->localHostName
);
15948 ASPrintF( &inTest
->localHostName
, "dnssdutil-regtest-local-hostname-%s",
15949 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
15950 require_action( inTest
->localHostName
, exit
, err
= kNoMemoryErr
);
15952 // Set up SIGINT signal handler.
15954 signal( SIGINT
, SIG_IGN
);
15955 check( !inTest
->sigSourceINT
);
15956 err
= DispatchSignalSourceCreate( SIGINT
, dispatch_get_main_queue(), _RegistrationTestSignalHandler
, inTest
,
15957 &inTest
->sigSourceINT
);
15958 require_noerr( err
, exit
);
15959 dispatch_resume( inTest
->sigSourceINT
);
15961 // Set up SIGTERM signal handler.
15963 signal( SIGTERM
, SIG_IGN
);
15964 check( !inTest
->sigSourceTERM
);
15965 err
= DispatchSignalSourceCreate( SIGTERM
, dispatch_get_main_queue(), _RegistrationTestSignalHandler
, inTest
,
15966 &inTest
->sigSourceTERM
);
15967 require_noerr( err
, exit
);
15968 dispatch_resume( inTest
->sigSourceTERM
);
15970 // Set test ComputerName.
15972 check( !inTest
->computerNameSet
);
15973 err
= _SetComputerNameWithUTF8CString( inTest
->computerName
);
15974 require_noerr( err
, exit
);
15975 inTest
->computerNameSet
= true;
15977 // Set test LocalHostName.
15979 check( !inTest
->localHostNameSet
);
15980 err
= _SetLocalHostNameWithUTF8CString( inTest
->localHostName
);
15981 require_noerr( err
, exit
);
15982 inTest
->localHostNameSet
= true;
15985 if( err
) _RegistrationTestStop( inTest
);
15989 static void _RegistrationTestSignalHandler( void *inContext
)
15991 RegistrationTest
* const test
= (RegistrationTest
*) inContext
;
15993 FPrintF( stderr
, "Registration test got a SIGINT or SIGTERM signal, exiting..." );
15995 _RegistrationTestExit( test
, kCanceledErr
);
15998 //===========================================================================================================================
16000 static void _RegistrationTestStop( RegistrationTest
*inTest
)
16004 dispatch_source_forget( &inTest
->timer
);
16005 dispatch_source_forget( &inTest
->sigSourceINT
);
16006 dispatch_source_forget( &inTest
->sigSourceTERM
);
16007 _RegistrationSubtestForget( &inTest
->subtest
);
16008 if( inTest
->computerNameSet
)
16010 err
= _SetComputerName( inTest
->computerNamePrev
, inTest
->computerNamePrevEncoding
);
16011 check_noerr( err
);
16012 if( !err
) inTest
->computerNameSet
= false;
16014 if( inTest
->localHostNameSet
)
16016 err
= _SetLocalHostName( inTest
->localHostNamePrev
);
16017 check_noerr( err
);
16018 if( !err
) inTest
->localHostNameSet
= false;
16022 //===========================================================================================================================
16024 #define kRegistrationTestSubtestDurationSecs 5
16027 _RegistrationTestStartSubtest(
16028 RegistrationTest
* inTest
,
16029 const RegistrationSubtestParams
* inParams
,
16030 Boolean
* outSkipped
)
16033 RegistrationSubtest
* subtest
;
16034 const char * interfaceDesc
;
16035 DNSServiceFlags flags
;
16039 err
= _RegistrationSubtestCreate( &subtest
);
16040 require_noerr( err
, exit
);
16042 subtest
->test
= inTest
;
16043 subtest
->useDefaultName
= inParams
->useDefaultName
;
16044 subtest
->useLODiscovery
= inParams
->useLODiscovery
;
16046 // Determine registration interfaces.
16048 switch( inParams
->interfaceSet
)
16050 case kRegistrationInterfaceSet_All
:
16051 subtest
->ifIndex
= kDNSServiceInterfaceIndexAny
;
16053 if( !subtest
->useLODiscovery
)
16055 err
= _RegistrationTestInterfaceListCreate( false, &subtest
->ifList
);
16056 require_noerr( err
, exit
);
16058 interfaceDesc
= "all interfaces (excluding AWDL)";
16061 case kRegistrationInterfaceSet_AllPlusAWDL
:
16062 subtest
->ifIndex
= kDNSServiceInterfaceIndexAny
;
16063 subtest
->includeAWDL
= true;
16065 if( !subtest
->useLODiscovery
)
16067 err
= _RegistrationTestInterfaceListCreate( true, &subtest
->ifList
);
16068 require_noerr( err
, exit
);
16070 interfaceDesc
= "all interfaces (including AWDL)";
16073 case kRegistrationInterfaceSet_LoopbackOnly
:
16074 subtest
->ifIndex
= if_nametoindex( "lo0" );
16075 if( subtest
->ifIndex
== 0 )
16077 FPrintF( stderr
, "Failed to get index for loopback interface lo0.\n" );
16078 err
= kNoResourcesErr
;
16081 interfaceDesc
= "loopback interface";
16084 case kRegistrationInterfaceSet_AWDLOnly
:
16085 err
= _MDNSInterfaceGetAny( kMDNSInterfaceSubset_AWDL
, NULL
, &subtest
->ifIndex
);
16086 if( err
== kNotFoundErr
)
16088 FPrintF( stderr
, "Warning: No mDNS-capable AWDL interface is available.\n" );
16089 subtest
->skipped
= true;
16092 require_noerr( err
, exit
);
16094 subtest
->ifIsAWDL
= true;
16095 interfaceDesc
= "AWDL interface";
16103 // Create description.
16105 ASPrintF( &subtest
->description
, "Service registration over %s using %s service name.%s",
16106 interfaceDesc
, subtest
->useDefaultName
? "default" : "custom",
16107 subtest
->useLODiscovery
? " (LocalOnly discovery)" : "" );
16108 require_action( subtest
->description
, exit
, err
= kNoMemoryErr
);
16110 if( subtest
->skipped
)
16112 subtest
->startTime
= NanoTimeGetCurrent();
16116 // Generate a service name.
16118 if( subtest
->useDefaultName
)
16120 subtest
->serviceName
= inTest
->computerName
;
16124 ASPrintF( &subtest
->serviceNameCustom
, "dnssdutil-regtest-service-name-%s",
16125 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
16126 require_action( subtest
->serviceNameCustom
, exit
, err
= kNoMemoryErr
);
16128 subtest
->serviceName
= subtest
->serviceNameCustom
;
16131 // Generate a service type.
16133 ASPrintF( &subtest
->serviceType
, "_regtest-%s._udp",
16134 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
16135 require_action( subtest
->serviceType
, exit
, err
= kNoMemoryErr
);
16137 subtest
->serviceTypeLen
= strlen( subtest
->serviceType
);
16139 // Create SRV and TXT record name FQDN.
16141 ASPrintF( &subtest
->serviceFQDN
, "%s.%s.local.", subtest
->serviceName
, subtest
->serviceType
);
16142 require_action( subtest
->serviceFQDN
, exit
, err
= kNoMemoryErr
);
16144 // Generate a port number.
16146 subtest
->port
= (uint16_t) RandomRange( 60000, 65535 );
16148 // Generate TXT record data.
16150 err
= _RegistrationTestCreateRandomTXTRecord( 100, 1000, &subtest
->txtPtr
, &subtest
->txtLen
);
16151 require_noerr( err
, exit
);
16153 // Register service.
16155 subtest
->startTime
= NanoTimeGetCurrent();
16157 flags
= kDNSServiceFlagsNoAutoRename
;
16158 if( subtest
->includeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
16159 err
= DNSServiceRegister( &subtest
->registration
, flags
, subtest
->ifIndex
,
16160 subtest
->useDefaultName
? NULL
: subtest
->serviceNameCustom
, subtest
->serviceType
, "local.",
16161 NULL
, htons( subtest
->port
), (uint16_t) subtest
->txtLen
, subtest
->txtPtr
,
16162 _RegistrationSubtestRegisterCallback
, subtest
);
16163 require_noerr( err
, exit
);
16165 err
= DNSServiceSetDispatchQueue( subtest
->registration
, dispatch_get_main_queue() );
16166 require_noerr( err
, exit
);
16170 check( !inTest
->timer
);
16171 err
= DispatchTimerOneShotCreate( dispatch_time_seconds( kRegistrationTestSubtestDurationSecs
),
16172 INT64_C_safe( kRegistrationTestSubtestDurationSecs
) * kNanosecondsPerSecond
/ 10, dispatch_get_main_queue(),
16173 _RegistrationTestTimerHandler
, inTest
, &inTest
->timer
);
16174 require_noerr( err
, exit
);
16175 dispatch_resume( inTest
->timer
);
16178 *outSkipped
= subtest
->skipped
;
16180 check( !inTest
->subtest
);
16181 inTest
->subtest
= subtest
;
16185 _RegistrationSubtestForget( &subtest
);
16189 //===========================================================================================================================
16191 #define kRegistrationTestReportKey_ComputerName CFSTR( "computerName" ) // String
16192 #define kRegistrationTestReportKey_Description CFSTR( "description" ) // String
16193 #define kRegistrationTestReportKey_Domain CFSTR( "domain" ) // String
16194 #define kRegistrationTestReportKey_EndTime CFSTR( "endTime" ) // String
16195 #define kRegistrationTestReportKey_Error CFSTR( "error" ) // Integer
16196 #define kRegistrationTestReportKey_Flags CFSTR( "flags" ) // Integer
16197 #define kRegistrationTestReportKey_IgnoredResults CFSTR( "ignoredResults" ) // Array of dictionaries
16198 #define kRegistrationTestReportKey_InterfaceIndex CFSTR( "ifIndex" ) // Integer
16199 #define kRegistrationTestReportKey_InterfaceName CFSTR( "ifName" ) // String
16200 #define kRegistrationTestReportKey_LocalHostName CFSTR( "localHostName" ) // String
16201 #define kRegistrationTestReportKey_MissingResults CFSTR( "missingResults" ) // Array of dictionaries
16202 #define kRegistrationTestReportKey_Pass CFSTR( "pass" ) // Boolean
16203 #define kRegistrationTestReportKey_Port CFSTR( "port" ) // Integer
16204 #define kRegistrationTestReportKey_RDataFormatted CFSTR( "rdataFormatted" ) // String
16205 #define kRegistrationTestReportKey_RDataHexString CFSTR( "rdataHexString" ) // String
16206 #define kRegistrationTestReportKey_RecordClass CFSTR( "recordClass" ) // Integer
16207 #define kRegistrationTestReportKey_RecordType CFSTR( "recordType" ) // Integer
16208 #define kRegistrationTestReportKey_Registered CFSTR( "registered" ) // Boolean
16209 #define kRegistrationTestReportKey_ResultType CFSTR( "resultType" ) // String
16210 #define kRegistrationTestReportKey_ServiceFQDN CFSTR( "serviceFQDN" ) // String
16211 #define kRegistrationTestReportKey_ServiceName CFSTR( "serviceName" ) // String
16212 #define kRegistrationTestReportKey_ServiceType CFSTR( "serviceType" ) // String
16213 #define kRegistrationTestReportKey_Skipped CFSTR( "skipped" ) // Boolean
16214 #define kRegistrationTestReportKey_StartTime CFSTR( "startTime" ) // String
16215 #define kRegistrationTestReportKey_Subtests CFSTR( "subtests" ) // Array of dictionaries
16216 #define kRegistrationTestReportKey_Timestamp CFSTR( "timestamp" ) // String
16217 #define kRegistrationTestReportKey_TXT CFSTR( "txt" ) // String
16218 #define kRegistrationTestReportKey_UnexpectedResults CFSTR( "unexpectedResults" ) // Array of dictionaries
16219 #define kRegistrationTestReportKey_UsedDefaultName CFSTR( "usedDefaultName" ) // Boolean
16220 #define kRegistrationTestReportKey_UsedLODiscovery CFSTR( "usedLODiscovery" ) // Boolean
16222 #define kRegistrationTestResultType_Browse CFSTR( "browse" )
16223 #define kRegistrationTestResultType_Query CFSTR( "query" )
16224 #define kRegistrationTestResultType_QuerySRV CFSTR( "querySRV" )
16225 #define kRegistrationTestResultType_QueryTXT CFSTR( "queryTXT" )
16226 #define kRegistrationTestResultType_Registration CFSTR( "registration" )
16229 _RegistrationTestAppendMissingResults(
16230 CFMutableArrayRef inMissingResults
,
16231 const RegistrationResultTimes
* inTimes
,
16232 uint32_t inIfIndex
,
16233 const char * inIfName
);
16235 static OSStatus
_RegistrationTestEndSubtest( RegistrationTest
*inTest
)
16238 RegistrationSubtest
* subtest
;
16239 CFMutableDictionaryRef subtestReport
;
16240 CFMutableArrayRef missing
;
16243 Boolean subtestFailed
;
16244 char startTime
[ 32 ];
16245 char endTime
[ 32 ];
16246 char ifNameBuf
[ IF_NAMESIZE
+ 1 ];
16248 now
= NanoTimeGetCurrent();
16250 subtest
= inTest
->subtest
;
16251 inTest
->subtest
= NULL
;
16252 _RegistrationSubtestStop( subtest
);
16255 subtestReport
= NULL
;
16257 if( subtest
->txtPtr
)
16259 err
= DNSRecordDataToString( subtest
->txtPtr
, subtest
->txtLen
, kDNSServiceType_TXT
, NULL
, 0, &txtStr
);
16260 require_noerr( err
, exit
);
16262 _NanoTime64ToTimestamp( subtest
->startTime
, startTime
, sizeof( startTime
) );
16263 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
16264 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &subtestReport
,
16266 "%kO=%s" // description
16267 "%kO=%s" // startTime
16268 "%kO=%s" // endTime
16269 "%kO=%s" // serviceFQDN
16270 "%kO=%lli" // ifIndex
16274 "%kO=%b" // registered
16275 "%kO=%b" // usedDefaultName
16276 "%kO=%b" // usedLODiscovery
16278 kRegistrationTestReportKey_Description
, subtest
->description
,
16279 kRegistrationTestReportKey_StartTime
, startTime
,
16280 kRegistrationTestReportKey_EndTime
, endTime
,
16281 kRegistrationTestReportKey_ServiceFQDN
, subtest
->serviceFQDN
,
16282 kRegistrationTestReportKey_InterfaceIndex
, (int64_t) subtest
->ifIndex
,
16283 kRegistrationTestReportKey_InterfaceName
, if_indextoname( subtest
->ifIndex
, ifNameBuf
),
16284 kRegistrationTestReportKey_Port
, (int64_t) subtest
->port
,
16285 kRegistrationTestReportKey_TXT
, txtStr
,
16286 kRegistrationTestReportKey_Registered
, (int) subtest
->registered
,
16287 kRegistrationTestReportKey_UsedDefaultName
, (int) subtest
->useDefaultName
,
16288 kRegistrationTestReportKey_UsedLODiscovery
, (int) subtest
->useLODiscovery
);
16289 ForgetMem( &txtStr
);
16290 require_noerr( err
, exit
);
16292 if( !subtest
->skipped
&& subtest
->registered
)
16294 missing
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
16295 require_action( missing
, exit
, err
= kNoMemoryErr
);
16297 if( subtest
->ifList
)
16299 RegistrationInterfaceItem
* item
;
16301 for( item
= subtest
->ifList
; item
; item
= (RegistrationInterfaceItem
*) item
->base
.next
)
16303 #if( TARGET_OS_WATCH )
16304 if( inTest
->forBATS
&& item
->base
.isWiFi
) continue;
16306 err
= _RegistrationTestAppendMissingResults( missing
, &item
->times
, item
->base
.ifIndex
, item
->base
.ifName
);
16307 require_noerr( err
, exit
);
16312 err
= _RegistrationTestAppendMissingResults( missing
, &subtest
->ifTimes
, subtest
->ifIndex
, NULL
);
16313 require_noerr( err
, exit
);
16316 subtestFailed
= false;
16317 if( CFArrayGetCount( missing
) > 0 )
16319 subtestFailed
= true;
16320 CFDictionarySetValue( subtestReport
, kRegistrationTestReportKey_MissingResults
, missing
);
16322 if( CFArrayGetCount( subtest
->unexpected
) > 0 )
16324 subtestFailed
= true;
16325 CFDictionarySetValue( subtestReport
, kRegistrationTestReportKey_UnexpectedResults
, subtest
->unexpected
);
16327 #if( TARGET_OS_WATCH )
16328 if( CFArrayGetCount( subtest
->ignored
) > 0 )
16330 CFDictionarySetValue( subtestReport
, kRegistrationTestReportKey_IgnoredResults
, subtest
->ignored
);
16336 subtestFailed
= true;
16339 CFDictionarySetBoolean( subtestReport
, kRegistrationTestReportKey_Pass
, subtestFailed
? false : true );
16340 if( subtestFailed
)
16342 CFDictionarySetBoolean( subtestReport
, kRegistrationTestReportKey_Skipped
, subtest
->skipped
);
16343 if( !subtest
->skipped
) inTest
->failed
= true;
16345 CFArrayAppendValue( inTest
->subtestReports
, subtestReport
);
16348 CFReleaseNullSafe( missing
);
16349 CFReleaseNullSafe( subtestReport
);
16350 _RegistrationSubtestFree( subtest
);
16355 _RegistrationTestAppendMissingResult(
16356 CFMutableArrayRef inMissingResults
,
16357 CFStringRef inType
,
16358 uint32_t inIfIndex
,
16359 const char * inIfName
);
16362 _RegistrationTestAppendMissingResults(
16363 CFMutableArrayRef inMissingResults
,
16364 const RegistrationResultTimes
* inTimes
,
16365 uint32_t inIfIndex
,
16366 const char * inIfName
)
16370 if( !inTimes
->browseResultTime
)
16372 err
= _RegistrationTestAppendMissingResult( inMissingResults
, kRegistrationTestResultType_Browse
,
16373 inIfIndex
, inIfName
);
16374 require_noerr( err
, exit
);
16376 if( !inTimes
->querySRVResultTime
)
16378 err
= _RegistrationTestAppendMissingResult( inMissingResults
, kRegistrationTestResultType_QuerySRV
,
16379 inIfIndex
, inIfName
);
16380 require_noerr( err
, exit
);
16382 if( !inTimes
->queryTXTResultTime
)
16384 err
= _RegistrationTestAppendMissingResult( inMissingResults
, kRegistrationTestResultType_QueryTXT
,
16385 inIfIndex
, inIfName
);
16386 require_noerr( err
, exit
);
16395 _RegistrationTestAppendMissingResult(
16396 CFMutableArrayRef inMissingResults
,
16397 CFStringRef inType
,
16398 uint32_t inIfIndex
,
16399 const char * inIfName
)
16402 char ifName
[ IF_NAMESIZE
+ 1 ];
16404 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, inMissingResults
,
16406 "%kO=%O" // resultType
16407 "%kO=%lli" // ifIndex
16410 kRegistrationTestReportKey_ResultType
, inType
,
16411 kRegistrationTestReportKey_InterfaceIndex
, (int64_t) inIfIndex
,
16412 kRegistrationTestReportKey_InterfaceName
, inIfName
? inIfName
: if_indextoname( inIfIndex
, ifName
) );
16416 //===========================================================================================================================
16418 static void _RegistrationTestEnd( RegistrationTest
*inTest
)
16422 CFPropertyListRef plist
;
16423 char startTime
[ 32 ];
16424 char endTime
[ 32 ];
16426 now
= NanoTimeGetCurrent();
16427 _NanoTime64ToTimestamp( inTest
->startTime
, startTime
, sizeof( startTime
) );
16428 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
16430 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
16432 "%kO=%s" // startTime
16433 "%kO=%s" // endTime
16434 "%kO=%s" // computerName
16435 "%kO=%s" // localHostName
16436 "%kO=%O" // subtests
16439 kRegistrationTestReportKey_StartTime
, startTime
,
16440 kRegistrationTestReportKey_EndTime
, endTime
,
16441 kRegistrationTestReportKey_ComputerName
, inTest
->computerName
,
16442 kRegistrationTestReportKey_LocalHostName
, inTest
->localHostName
,
16443 kRegistrationTestReportKey_Subtests
, inTest
->subtestReports
,
16444 kRegistrationTestReportKey_Pass
, inTest
->failed
? false : true );
16445 require_noerr( err
, exit
);
16447 err
= OutputPropertyList( plist
, inTest
->outputFormat
, inTest
->outputFilePath
);
16448 CFRelease( plist
);
16449 require_noerr( err
, exit
);
16452 _RegistrationTestExit( inTest
, err
);
16455 //===========================================================================================================================
16457 static void _RegistrationTestExit( RegistrationTest
*inTest
, OSStatus inError
)
16463 FPrintF( stderr
, "error: %#m\n", inError
);
16468 exitCode
= inTest
->failed
? 2 : 0;
16470 _RegistrationTestForget( &inTest
);
16474 //===========================================================================================================================
16476 static OSStatus
_RegistrationSubtestCreate( RegistrationSubtest
**outSubtest
)
16479 RegistrationSubtest
* obj
;
16481 obj
= (RegistrationSubtest
*) calloc( 1, sizeof( *obj
) );
16482 require_action( obj
, exit
, err
= kNoMemoryErr
);
16484 obj
->unexpected
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
16485 require_action( obj
->unexpected
, exit
, err
= kNoMemoryErr
);
16487 #if( TARGET_OS_WATCH )
16488 obj
->ignored
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
16489 require_action( obj
->ignored
, exit
, err
= kNoMemoryErr
);
16497 if( obj
) _RegistrationSubtestFree( obj
);
16501 //===========================================================================================================================
16503 static void _RegistrationSubtestFree( RegistrationSubtest
*inSubtest
)
16505 check( !inSubtest
->registration
);
16506 check( !inSubtest
->browse
);
16507 check( !inSubtest
->querySRV
);
16508 check( !inSubtest
->queryTXT
);
16509 check( !inSubtest
->connection
);
16510 ForgetMem( &inSubtest
->serviceNameCustom
);
16511 ForgetMem( &inSubtest
->serviceType
);
16512 ForgetMem( &inSubtest
->serviceFQDN
);
16513 ForgetMem( &inSubtest
->txtPtr
);
16514 ForgetCF( &inSubtest
->unexpected
);
16515 #if( TARGET_OS_WATCH )
16516 ForgetCF( &inSubtest
->ignored
);
16518 _MDNSInterfaceListForget( (MDNSInterfaceItem
**) &inSubtest
->ifList
);
16519 ForgetMem( &inSubtest
->description
);
16523 //===========================================================================================================================
16525 static void _RegistrationSubtestStop( RegistrationSubtest
*inSubtest
)
16527 DNSServiceForget( &inSubtest
->registration
);
16528 DNSServiceForget( &inSubtest
->browse
);
16529 DNSServiceForget( &inSubtest
->querySRV
);
16530 DNSServiceForget( &inSubtest
->queryTXT
);
16531 DNSServiceForget( &inSubtest
->connection
);
16534 //===========================================================================================================================
16536 static OSStatus
_RegistrationTestInterfaceListCreate( Boolean inIncludeAWDL
, RegistrationInterfaceItem
**outList
)
16539 RegistrationInterfaceItem
* list
;
16540 const MDNSInterfaceSubset subset
= inIncludeAWDL
? kMDNSInterfaceSubset_All
: kMDNSInterfaceSubset_NonAWDL
;
16542 err
= _MDNSInterfaceListCreate( subset
, sizeof( *list
), (MDNSInterfaceItem
**) &list
);
16543 require_noerr( err
, exit
);
16551 //===========================================================================================================================
16554 _RegistrationTestCreateRandomTXTRecord(
16557 uint8_t ** outTXTPtr
,
16558 size_t * outTXTLen
)
16562 const uint8_t * txtEnd
;
16563 uint8_t * txtPtr
= NULL
;
16566 require_action_quiet( inMinLen
<= inMaxLen
, exit
, err
= kSizeErr
);
16568 txtLen
= RandomRange( inMinLen
, inMaxLen
);
16569 txtPtr
= (uint8_t *) malloc( txtLen
+ 1 );
16570 require_action( txtPtr
, exit
, err
= kNoMemoryErr
);
16572 _RandomStringExact( kAlphaNumericCharSet
, sizeof_string( kAlphaNumericCharSet
), txtLen
, (char *)txtPtr
);
16575 txtEnd
= &txtPtr
[ txtLen
];
16576 while( ptr
< txtEnd
)
16578 size_t maxLen
, len
;
16580 maxLen
= ( (size_t)( txtEnd
- ptr
) ) - 1;
16581 len
= RandomRange( 1, 255 );
16582 if( len
> maxLen
) len
= maxLen
;
16584 *ptr
= (uint8_t) len
;
16585 ptr
+= ( 1 + len
);
16587 check( ptr
== txtEnd
);
16591 *outTXTPtr
= txtPtr
;
16594 if( outTXTLen
) *outTXTLen
= txtLen
;
16598 FreeNullSafe( txtPtr
);
16602 //===========================================================================================================================
16604 static void DNSSD_API
16605 _RegistrationSubtestRegisterCallback(
16606 DNSServiceRef inSDRef
,
16607 DNSServiceFlags inFlags
,
16608 DNSServiceErrorType inError
,
16609 const char * inServiceName
,
16610 const char * inServiceType
,
16611 const char * inDomain
,
16615 const NanoTime64 now
= NanoTimeGetCurrent();
16616 RegistrationSubtest
* const subtest
= (RegistrationSubtest
*) inContext
;
16620 if( ( inFlags
& kDNSServiceFlagsAdd
) && !inError
&&
16621 ( strcasecmp( inServiceName
, subtest
->serviceName
) == 0 ) &&
16622 _RegistrationSubtestValidServiceType( subtest
, inServiceType
) &&
16623 ( strcasecmp( inDomain
, "local." ) == 0 ) )
16625 if( !subtest
->registered
)
16627 DNSServiceRef sdRef
;
16628 const DNSServiceFlags flags
= kDNSServiceFlagsShareConnection
| kDNSServiceFlagsIncludeAWDL
;
16630 subtest
->registered
= true;
16632 // Create shared connection.
16634 check( !subtest
->connection
);
16635 err
= DNSServiceCreateConnection( &subtest
->connection
);
16636 require_noerr( err
, exit
);
16638 err
= DNSServiceSetDispatchQueue( subtest
->connection
, dispatch_get_main_queue() );
16639 require_noerr( err
, exit
);
16643 check( !subtest
->browse
);
16644 sdRef
= subtest
->connection
;
16645 err
= DNSServiceBrowse( &sdRef
, flags
,
16646 subtest
->useLODiscovery
? kDNSServiceInterfaceIndexLocalOnly
: kDNSServiceInterfaceIndexAny
,
16647 subtest
->serviceType
, "local.", _RegistrationSubtestBrowseCallback
, subtest
);
16648 require_noerr( err
, exit
);
16650 subtest
->browse
= sdRef
;
16655 char timestamp
[ 32 ];
16657 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, subtest
->unexpected
,
16659 "%kO=%O" // resultType
16660 "%kO=%s" // timestamp
16661 "%kO=%lli" // flags
16662 "%kO=%lli" // error
16663 "%kO=%s" // serviceName
16664 "%kO=%s" // serviceType
16667 kRegistrationTestReportKey_ResultType
, kRegistrationTestResultType_Registration
,
16668 kRegistrationTestReportKey_Timestamp
, _NanoTime64ToTimestamp( now
, timestamp
, sizeof( timestamp
) ),
16669 kRegistrationTestReportKey_Flags
, (int64_t) inFlags
,
16670 kRegistrationTestReportKey_Error
, (int64_t) inError
,
16671 kRegistrationTestReportKey_ServiceName
, inServiceName
,
16672 kRegistrationTestReportKey_ServiceType
, inServiceType
,
16673 kRegistrationTestReportKey_Domain
, inDomain
);
16674 require_noerr( err
, exit
);
16679 if( err
) _RegistrationTestExit( subtest
->test
, err
);
16682 //===========================================================================================================================
16684 static void DNSSD_API
16685 _RegistrationSubtestBrowseCallback(
16686 DNSServiceRef inSDRef
,
16687 DNSServiceFlags inFlags
,
16688 uint32_t inIfIndex
,
16689 DNSServiceErrorType inError
,
16690 const char * inServiceName
,
16691 const char * inServiceType
,
16692 const char * inDomain
,
16697 RegistrationSubtest
* const subtest
= (RegistrationSubtest
*) inContext
;
16698 Boolean serviceIsCorrect
, resultIsExpected
;
16702 now
= NanoTimeGetCurrent();
16703 if( !inError
&& ( strcasecmp( inServiceName
, subtest
->serviceName
) == 0 ) &&
16704 _RegistrationSubtestValidServiceType( subtest
, inServiceType
) && ( strcasecmp( inDomain
, "local." ) == 0 ) )
16706 serviceIsCorrect
= true;
16710 serviceIsCorrect
= false;
16713 resultIsExpected
= false;
16714 if( serviceIsCorrect
&& ( inFlags
& kDNSServiceFlagsAdd
) )
16716 RegistrationResultTimes
* times
;
16718 times
= _RegistrationSubtestGetInterfaceResultTimes( subtest
, inIfIndex
, NULL
);
16721 DNSServiceRef sdRef
;
16722 const DNSServiceFlags flags
= kDNSServiceFlagsShareConnection
| kDNSServiceFlagsIncludeAWDL
;
16725 resultIsExpected
= true;
16726 if( !times
->browseResultTime
) times
->browseResultTime
= now
;
16728 ifIndex
= subtest
->useLODiscovery
? kDNSServiceInterfaceIndexLocalOnly
: kDNSServiceInterfaceIndexAny
;
16729 if( !subtest
->querySRV
)
16731 // Start SRV record query.
16733 sdRef
= subtest
->connection
;
16734 err
= DNSServiceQueryRecord( &sdRef
, flags
, ifIndex
, subtest
->serviceFQDN
, kDNSServiceType_SRV
,
16735 kDNSServiceClass_IN
, _RegistrationSubtestQueryCallback
, subtest
);
16736 require_noerr( err
, exit
);
16738 subtest
->querySRV
= sdRef
;
16740 if( !subtest
->queryTXT
)
16742 // Start TXT record query.
16744 sdRef
= subtest
->connection
;
16745 err
= DNSServiceQueryRecord( &sdRef
, flags
, ifIndex
, subtest
->serviceFQDN
, kDNSServiceType_TXT
,
16746 kDNSServiceClass_IN
, _RegistrationSubtestQueryCallback
, subtest
);
16747 require_noerr( err
, exit
);
16749 subtest
->queryTXT
= sdRef
;
16754 if( !resultIsExpected
)
16756 CFMutableArrayRef resultArray
;
16757 char timestamp
[ 32 ];
16758 const char * ifNamePtr
;
16759 char ifNameBuf
[ IF_NAMESIZE
+ 1 ];
16761 ifNamePtr
= if_indextoname( inIfIndex
, ifNameBuf
);
16762 resultArray
= subtest
->unexpected
;
16763 #if( TARGET_OS_WATCH )
16764 if( subtest
->test
->forBATS
&& ( subtest
->ifIndex
== kDNSServiceInterfaceIndexAny
) &&
16765 ifNamePtr
&& _RegistrationTestInterfaceIsWiFi( ifNamePtr
) && serviceIsCorrect
)
16767 resultArray
= subtest
->ignored
;
16770 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, resultArray
,
16772 "%kO=%O" // resultType
16773 "%kO=%s" // timestamp
16774 "%kO=%lli" // flags
16775 "%kO=%lli" // ifIndex
16777 "%kO=%lli" // error
16778 "%kO=%s" // serviceName
16779 "%kO=%s" // serviceType
16782 kRegistrationTestReportKey_ResultType
, kRegistrationTestResultType_Browse
,
16783 kRegistrationTestReportKey_Timestamp
, _NanoTime64ToTimestamp( now
, timestamp
, sizeof( timestamp
) ),
16784 kRegistrationTestReportKey_Flags
, (int64_t) inFlags
,
16785 kRegistrationTestReportKey_InterfaceIndex
, (int64_t) inIfIndex
,
16786 kRegistrationTestReportKey_InterfaceName
, ifNamePtr
,
16787 kRegistrationTestReportKey_Error
, (int64_t) inError
,
16788 kRegistrationTestReportKey_ServiceName
, inServiceName
,
16789 kRegistrationTestReportKey_ServiceType
, inServiceType
,
16790 kRegistrationTestReportKey_Domain
, inDomain
);
16791 require_noerr( err
, exit
);
16796 if( err
) _RegistrationTestExit( subtest
->test
, err
);
16799 //===========================================================================================================================
16802 _RegistrationSubtestIsSRVRecordDataValid(
16803 RegistrationSubtest
* inSubtest
,
16804 const uint8_t * inRDataPtr
,
16806 Boolean inExpectRandHostname
);
16808 static void DNSSD_API
16809 _RegistrationSubtestQueryCallback(
16810 DNSServiceRef inSDRef
,
16811 DNSServiceFlags inFlags
,
16812 uint32_t inIfIndex
,
16813 DNSServiceErrorType inError
,
16814 const char * inName
,
16817 uint16_t inRDataLen
,
16818 const void * inRDataPtr
,
16824 RegistrationSubtest
* const subtest
= (RegistrationSubtest
*) inContext
;
16825 Boolean resultIsExpected
;
16830 now
= NanoTimeGetCurrent();
16831 resultIsExpected
= false;
16832 if( ( inFlags
& kDNSServiceFlagsAdd
) && !inError
&& ( strcasecmp( inName
, subtest
->serviceFQDN
) == 0 ) &&
16833 ( inClass
== kDNSServiceClass_IN
) )
16835 RegistrationResultTimes
* times
;
16838 times
= _RegistrationSubtestGetInterfaceResultTimes( subtest
, inIfIndex
, &isAWDL
);
16841 if( inType
== kDNSServiceType_SRV
)
16843 Boolean expectRandHostname
;
16845 if( isAWDL
|| ( ( subtest
->ifIndex
== kDNSServiceInterfaceIndexAny
) && subtest
->includeAWDL
) )
16847 expectRandHostname
= true;
16851 expectRandHostname
= false;
16853 if( _RegistrationSubtestIsSRVRecordDataValid( subtest
, inRDataPtr
, inRDataLen
, expectRandHostname
) )
16855 resultIsExpected
= true;
16856 if( !times
->querySRVResultTime
) times
->querySRVResultTime
= now
;
16859 else if( inType
== kDNSServiceType_TXT
)
16861 if( MemEqual( inRDataPtr
, inRDataLen
, subtest
->txtPtr
, subtest
->txtLen
) )
16863 resultIsExpected
= true;
16864 if( !times
->queryTXTResultTime
) times
->queryTXTResultTime
= now
;
16870 if( !resultIsExpected
)
16872 CFMutableArrayRef resultArray
;
16873 CFMutableDictionaryRef resultDict
;
16874 CFStringRef rdataKey
;
16876 const char * ifNamePtr
;
16877 char timestamp
[ 32 ];
16878 char ifNameBuf
[ IF_NAMESIZE
+ 1 ];
16880 ifNamePtr
= if_indextoname( inIfIndex
, ifNameBuf
);
16881 resultArray
= subtest
->unexpected
;
16882 #if( TARGET_OS_WATCH )
16883 if( subtest
->test
->forBATS
&& ( subtest
->ifIndex
== kDNSServiceInterfaceIndexAny
) &&
16884 ifNamePtr
&& _RegistrationTestInterfaceIsWiFi( ifNamePtr
) && !inError
&&
16885 ( strcasecmp( inName
, subtest
->serviceFQDN
) == 0 ) )
16887 if( inType
== kDNSServiceType_SRV
)
16889 const Boolean expectRandHostname
= subtest
->includeAWDL
? true : false;
16891 if( _RegistrationSubtestIsSRVRecordDataValid( subtest
, inRDataPtr
, inRDataLen
, expectRandHostname
) )
16893 resultArray
= subtest
->ignored
;
16896 else if( inType
== kDNSServiceType_TXT
)
16898 if( MemEqual( inRDataPtr
, inRDataLen
, subtest
->txtPtr
, subtest
->txtLen
) )
16900 resultArray
= subtest
->ignored
;
16905 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &resultDict
,
16907 "%kO=%O" // resultType
16908 "%kO=%s" // timestamp
16909 "%kO=%lli" // flags
16910 "%kO=%lli" // ifIndex
16912 "%kO=%lli" // error
16913 "%kO=%s" // serviceFQDN
16914 "%kO=%lli" // recordType
16915 "%kO=%lli" // class
16917 kRegistrationTestReportKey_ResultType
, kRegistrationTestResultType_Query
,
16918 kRegistrationTestReportKey_Timestamp
, _NanoTime64ToTimestamp( now
, timestamp
, sizeof( timestamp
) ),
16919 kRegistrationTestReportKey_Flags
, (int64_t) inFlags
,
16920 kRegistrationTestReportKey_InterfaceIndex
, (int64_t) inIfIndex
,
16921 kRegistrationTestReportKey_InterfaceName
, ifNamePtr
,
16922 kRegistrationTestReportKey_Error
, (int64_t) inError
,
16923 kRegistrationTestReportKey_ServiceFQDN
, inName
,
16924 kRegistrationTestReportKey_RecordType
, (int64_t) inType
,
16925 kRegistrationTestReportKey_RecordClass
, (int64_t) inClass
);
16926 require_noerr( err
, exit
);
16929 DNSRecordDataToString( inRDataPtr
, inRDataLen
, inType
, NULL
, 0, &rdataStr
);
16932 rdataKey
= kRegistrationTestReportKey_RDataFormatted
;
16936 ASPrintF( &rdataStr
, "%#H", inRDataPtr
, inRDataLen
, inRDataLen
);
16937 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
16939 rdataKey
= kRegistrationTestReportKey_RDataHexString
;
16941 err
= CFDictionarySetCString( resultDict
, rdataKey
, rdataStr
, kSizeCString
);
16942 ForgetMem( &rdataStr
);
16943 if( err
) CFRelease( resultDict
);
16944 require_noerr( err
, exit
);
16946 CFArrayAppendValue( resultArray
, resultDict
);
16947 CFRelease( resultDict
);
16952 if( err
) _RegistrationTestExit( subtest
->test
, err
);
16956 _RegistrationSubtestIsSRVRecordDataValid(
16957 RegistrationSubtest
* inSubtest
,
16958 const uint8_t * inRDataPtr
,
16960 Boolean inExpectRandHostname
)
16962 const dns_fixed_fields_srv
* fields
;
16963 const uint8_t * const end
= &inRDataPtr
[ inRDataLen
];
16964 const uint8_t * label
;
16970 require_quiet( inRDataLen
>= sizeof( dns_fixed_fields_srv
), exit
);
16972 fields
= (const dns_fixed_fields_srv
*) inRDataPtr
;
16973 port
= dns_fixed_fields_srv_get_port( fields
);
16974 require_quiet( port
== inSubtest
->port
, exit
);
16976 // First target label should be a UUID string for the AWDL interface.
16978 label
= (const uint8_t *) &fields
[ 1 ];
16979 require_quiet( ( end
- label
) >= 1, exit
);
16982 require_quiet( ( (size_t)( end
- label
) ) >= ( 1 + len
), exit
);
16984 if( inExpectRandHostname
)
16986 if( StringToUUID( (const char *) &label
[ 1 ], len
, false, NULL
) != kNoErr
) goto exit
;
16990 if( strnicmpx( &label
[ 1 ], len
, inSubtest
->test
->localHostName
) != 0 ) goto exit
;
16993 // Second target label should be "local".
16995 label
= &label
[ 1 + len
];
16996 require_quiet( ( end
- label
) >= 1, exit
);
16999 require_quiet( ( (size_t)( end
- label
) ) >= ( 1 + len
), exit
);
17001 if( ( len
!= kLocalLabel
[ 0 ] ) || ( _memicmp( &label
[ 1 ], &kLocalLabel
[ 1 ], kLocalLabel
[ 0 ] ) != 0 ) ) goto exit
;
17003 // Third target label should be the root label.
17005 label
= &label
[ 1 + len
];
17006 require_quiet( ( end
- label
) >= 1, exit
);
17009 if( len
!= 0 ) goto exit
;
17017 //===========================================================================================================================
17019 static Boolean
_RegistrationSubtestValidServiceType( const RegistrationSubtest
*inSubtest
, const char *inServiceType
)
17021 if( stricmp_prefix( inServiceType
, inSubtest
->serviceType
) == 0 )
17023 const char * const ptr
= &inServiceType
[ inSubtest
->serviceTypeLen
];
17025 if( ( ptr
[ 0 ] == '\0' ) || ( ( ptr
[ 0 ] == '.' ) && ( ptr
[ 1 ] == '\0' ) ) ) return( true );
17030 //===========================================================================================================================
17032 static RegistrationResultTimes
*
17033 _RegistrationSubtestGetInterfaceResultTimes(
17034 RegistrationSubtest
* inSubtest
,
17035 uint32_t inIfIndex
,
17036 Boolean
* outIsAWDL
)
17038 if( inSubtest
->ifList
)
17040 RegistrationInterfaceItem
* item
;
17042 for( item
= inSubtest
->ifList
; item
; item
= (RegistrationInterfaceItem
*) item
->base
.next
)
17044 if( inIfIndex
== item
->base
.ifIndex
)
17046 if( outIsAWDL
) *outIsAWDL
= item
->base
.isAWDL
? true : false;
17047 return( &item
->times
);
17053 if( inIfIndex
== inSubtest
->ifIndex
)
17055 if( outIsAWDL
) *outIsAWDL
= inSubtest
->ifIsAWDL
? true : false;
17056 return( &inSubtest
->ifTimes
);
17062 //===========================================================================================================================
17064 static void _RegistrationTestTimerHandler( void *inContext
)
17066 RegistrationTest
* const test
= (RegistrationTest
*) inContext
;
17068 dispatch_source_forget( &test
->timer
);
17069 _RegistrationTestProceed( test
);
17072 //===========================================================================================================================
17074 #if( TARGET_OS_WATCH )
17075 static Boolean
_RegistrationTestInterfaceIsWiFi( const char *inIfName
)
17077 NetTransportType type
= kNetTransportType_Undefined
;
17079 SocketGetInterfaceInfo( kInvalidSocketRef
, inIfName
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &type
);
17080 return( ( type
== kNetTransportType_WiFi
) ? true : false );
17084 #if( TARGET_OS_DARWIN )
17085 //===========================================================================================================================
17086 // KeepAliveTestCmd
17087 //===========================================================================================================================
17091 kKeepAliveCallVariant_Null
= 0,
17092 kKeepAliveCallVariant_TakesSocket
= 1, // DNSServiceSleepKeepalive(), which takes a connected socket.
17093 kKeepAliveCallVariant_TakesSockAddrs
= 2, // DNSServiceSleepKeepalive_sockaddr(), which takes connection's sockaddrs.
17095 } KeepAliveCallVariant
;
17099 int family
; // TCP connection's address family.
17100 KeepAliveCallVariant callVariant
; // Describes which DNSServiceSleepKeepalive* call to use.
17101 const char * description
;
17103 } KeepAliveSubtestParams
;
17105 const KeepAliveSubtestParams kKeepAliveSubtestParams
[] =
17107 { AF_INET
, kKeepAliveCallVariant_TakesSocket
, "Calls DNSServiceSleepKeepalive() for IPv4 TCP connection." },
17108 { AF_INET
, kKeepAliveCallVariant_TakesSockAddrs
, "Calls DNSServiceSleepKeepalive_sockaddr() for IPv4 TCP connection." },
17109 { AF_INET6
, kKeepAliveCallVariant_TakesSocket
, "Calls DNSServiceSleepKeepalive() for IPv6 TCP connection." },
17110 { AF_INET6
, kKeepAliveCallVariant_TakesSockAddrs
, "Calls DNSServiceSleepKeepalive_sockaddr() for IPv6 TCP connection." }
17115 sockaddr_ip local
; // TCP connection's local address and port.
17116 sockaddr_ip remote
; // TCP connection's remote address and port.
17117 NanoTime64 startTime
; // Subtest's start time.
17118 NanoTime64 endTime
; // Subtest's end time.
17119 SocketRef clientSock
; // Socket for client-side of TCP connection.
17120 SocketRef serverSock
; // Socket for server-side of TCP connection.
17121 char * recordName
; // Keepalive record's name.
17122 char * dataStr
; // Data expected to be contained in keepalive record's data.
17123 const char * description
; // Subtests's description.
17124 unsigned int timeoutKA
; // Randomly-generated timeout value that gets put in keepalive record's rdata.
17125 OSStatus error
; // Subtest's error.
17127 } KeepAliveSubtest
;
17129 typedef struct KeepAliveTest
* KeepAliveTestRef
;
17133 KeepAliveTestRef test
; // Weak back pointer to test.
17135 } KeepAliveTestConnectionContext
;
17137 struct KeepAliveTest
17139 dispatch_queue_t queue
; // Serial queue for test events.
17140 dispatch_semaphore_t doneSem
; // Semaphore to signal when the test is done.
17141 dispatch_source_t readSource
; // Read source for TCP listener socket.
17142 DNSServiceRef keepalive
; // DNSServiceSleepKeepalive{,2} operation.
17143 DNSServiceRef query
; // Query to verify registered keepalive record.
17144 dispatch_source_t timer
; // Timer to put time limit on query.
17145 AsyncConnectionRef connection
; // Establishes current subtest's TCP connection.
17146 KeepAliveTestConnectionContext
* connectionCtx
; // Weak pointer to connection's context.
17147 NanoTime64 startTime
; // Test's start time.
17148 NanoTime64 endTime
; // Test's end time.
17149 OSStatus error
; // Test's error.
17150 size_t subtestIdx
; // Index of current subtest.
17151 KeepAliveSubtest subtests
[ 4 ]; // Subtest array.
17153 check_compile_time( countof_field( struct KeepAliveTest
, subtests
) == countof( kKeepAliveSubtestParams
) );
17155 ulog_define_ex( kDNSSDUtilIdentifier
, KeepAliveTest
, kLogLevelInfo
, kLogFlags_None
, "KeepAliveTest", NULL
);
17156 #define kat_ulog( LEVEL, ... ) ulog( &log_category_from_name( KeepAliveTest ), (LEVEL), __VA_ARGS__ )
17158 static OSStatus
_KeepAliveTestCreate( KeepAliveTestRef
*outTest
);
17159 static OSStatus
_KeepAliveTestRun( KeepAliveTestRef inTest
);
17160 static void _KeepAliveTestFree( KeepAliveTestRef inTest
);
17162 static void KeepAliveTestCmd( void )
17165 OutputFormatType outputFormat
;
17166 KeepAliveTestRef test
= NULL
;
17167 CFPropertyListRef plist
= NULL
;
17168 CFMutableArrayRef subtests
;
17170 size_t subtestFailCount
;
17171 Boolean testPassed
= false;
17172 char startTime
[ 32 ];
17173 char endTime
[ 32 ];
17175 err
= OutputFormatFromArgString( gKeepAliveTest_OutputFormat
, &outputFormat
);
17176 require_noerr_quiet( err
, exit
);
17178 err
= _KeepAliveTestCreate( &test
);
17179 require_noerr( err
, exit
);
17181 err
= _KeepAliveTestRun( test
);
17182 require_noerr( err
, exit
);
17184 _NanoTime64ToTimestamp( test
->startTime
, startTime
, sizeof( startTime
) );
17185 _NanoTime64ToTimestamp( test
->endTime
, endTime
, sizeof( endTime
) );
17186 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
17188 "%kO=%s" // startTime
17189 "%kO=%s" // endTime
17190 "%kO=[%@]" // subtests
17192 CFSTR( "startTime" ), startTime
,
17193 CFSTR( "endTime" ), endTime
,
17194 CFSTR( "subtests" ), &subtests
);
17195 require_noerr( err
, exit
);
17197 subtestFailCount
= 0;
17198 check( test
->subtestIdx
== countof( test
->subtests
) );
17199 for( i
= 0; i
< countof( test
->subtests
); ++i
)
17201 KeepAliveSubtest
* const subtest
= &test
->subtests
[ i
];
17202 char errorDesc
[ 128 ];
17204 _NanoTime64ToTimestamp( subtest
->startTime
, startTime
, sizeof( startTime
) );
17205 _NanoTime64ToTimestamp( subtest
->endTime
, endTime
, sizeof( endTime
) );
17206 SNPrintF( errorDesc
, sizeof( errorDesc
), "%m", subtest
->error
);
17207 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, subtests
,
17209 "%kO=%s" // startTime
17210 "%kO=%s" // endTime
17211 "%kO=%s" // description
17212 "%kO=%##a" // localAddr
17213 "%kO=%##a" // remoteAddr
17214 "%kO=%s" // recordName
17215 "%kO=%s" // expectedRData
17219 "%kO=%s" // description
17222 CFSTR( "startTime" ), startTime
,
17223 CFSTR( "endTime" ), endTime
,
17224 CFSTR( "description" ), subtest
->description
,
17225 CFSTR( "localAddr" ), &subtest
->local
.sa
,
17226 CFSTR( "remoteAddr" ), &subtest
->remote
.sa
,
17227 CFSTR( "recordName" ), subtest
->recordName
,
17228 CFSTR( "expectedRData" ), subtest
->dataStr
,
17230 CFSTR( "code" ), (int64_t) subtest
->error
,
17231 CFSTR( "description" ), errorDesc
17233 require_noerr( err
, exit
);
17234 if( subtest
->error
) ++subtestFailCount
;
17236 if( subtestFailCount
== 0 ) testPassed
= true;
17237 CFPropertyListAppendFormatted( kCFAllocatorDefault
, plist
, "%kO=%b", CFSTR( "pass" ), testPassed
);
17239 err
= OutputPropertyList( plist
, outputFormat
, gKeepAliveTest_OutputFilePath
);
17240 require_noerr( err
, exit
);
17243 if( test
) _KeepAliveTestFree( test
);
17244 CFReleaseNullSafe( plist
);
17245 gExitCode
= err
? 1 : ( testPassed
? 0 : 2 );
17248 //===========================================================================================================================
17250 static void _KeepAliveTestStart( void *inCtx
);
17251 static void _KeepAliveTestStop( KeepAliveTestRef inTest
, OSStatus inError
);
17252 static OSStatus
_KeepAliveTestStartSubtest( KeepAliveTestRef inTest
);
17253 static void _KeepAliveTestStopSubtest( KeepAliveTestRef inTest
);
17254 static KeepAliveSubtest
* _KeepAliveTestGetSubtest( KeepAliveTestRef inTest
);
17255 static const char * _KeepAliveTestGetSubtestLogPrefix( KeepAliveTestRef inTest
, char *inBufPtr
, size_t inBufLen
);
17256 static OSStatus
_KeepAliveTestContinue( KeepAliveTestRef inTest
, OSStatus inSubtestError
, Boolean
*outDone
);
17257 static void _KeepAliveTestTCPAcceptHandler( void *inCtx
);
17258 static void _KeepAliveTestConnectionHandler( SocketRef inSock
, OSStatus inError
, void *inArg
);
17259 static void _KeepAliveTestHandleConnection( KeepAliveTestRef inTest
, SocketRef inSock
, OSStatus inError
);
17260 static void _KeepAliveTestForgetConnection( KeepAliveTestRef inTest
);
17261 static void DNSSD_API
_KeepAliveTestKeepaliveCallback( DNSServiceRef inSDRef
, DNSServiceErrorType inErr
, void *inCtx
);
17262 static void _KeepAliveTestQueryTimerHandler( void *inCtx
);
17263 static void DNSSD_API
17264 _KeepAliveTestQueryRecordCallback(
17265 DNSServiceRef inSDRef
,
17266 DNSServiceFlags inFlags
,
17267 uint32_t inInterfaceIndex
,
17268 DNSServiceErrorType inError
,
17269 const char * inFullName
,
17272 uint16_t inRDataLen
,
17273 const void * inRDataPtr
,
17277 static OSStatus
_KeepAliveTestCreate( KeepAliveTestRef
*outTest
)
17280 KeepAliveTestRef test
;
17283 test
= (KeepAliveTestRef
) calloc( 1, sizeof( *test
) );
17284 require_action( test
, exit
, err
= kNoMemoryErr
);
17286 test
->error
= kInProgressErr
;
17287 for( i
= 0; i
< countof( test
->subtests
); ++i
)
17289 KeepAliveSubtest
* const subtest
= &test
->subtests
[ i
];
17291 subtest
->local
.sa
.sa_family
= AF_UNSPEC
;
17292 subtest
->remote
.sa
.sa_family
= AF_UNSPEC
;
17293 subtest
->clientSock
= kInvalidSocketRef
;
17294 subtest
->serverSock
= kInvalidSocketRef
;
17296 test
->queue
= dispatch_queue_create( "com.apple.dnssdutil.keepalive-test", DISPATCH_QUEUE_SERIAL
);
17297 require_action( test
->queue
, exit
, err
= kNoResourcesErr
);
17299 test
->doneSem
= dispatch_semaphore_create( 0 );
17300 require_action( test
->doneSem
, exit
, err
= kNoResourcesErr
);
17307 if( test
) _KeepAliveTestFree( test
);
17311 //===========================================================================================================================
17313 static OSStatus
_KeepAliveTestRun( KeepAliveTestRef inTest
)
17315 dispatch_async_f( inTest
->queue
, inTest
, _KeepAliveTestStart
);
17316 dispatch_semaphore_wait( inTest
->doneSem
, DISPATCH_TIME_FOREVER
);
17317 return( inTest
->error
);
17320 //===========================================================================================================================
17322 static void _KeepAliveTestFree( KeepAliveTestRef inTest
)
17326 check( !inTest
->readSource
);
17327 check( !inTest
->query
);
17328 check( !inTest
->timer
);
17329 check( !inTest
->keepalive
);
17330 check( !inTest
->connection
);
17331 check( !inTest
->connectionCtx
);
17332 dispatch_forget( &inTest
->queue
);
17333 dispatch_forget( &inTest
->doneSem
);
17334 for( i
= 0; i
< countof( inTest
->subtests
); ++i
)
17336 KeepAliveSubtest
* const subtest
= &inTest
->subtests
[ i
];
17338 check( !IsValidSocket( subtest
->clientSock
) );
17339 check( !IsValidSocket( subtest
->serverSock
) );
17340 ForgetMem( &subtest
->recordName
);
17341 ForgetMem( &subtest
->dataStr
);
17346 //===========================================================================================================================
17348 static void _KeepAliveTestStart( void *inCtx
)
17351 const KeepAliveTestRef test
= (KeepAliveTestRef
) inCtx
;
17353 test
->error
= kInProgressErr
;
17354 test
->startTime
= NanoTimeGetCurrent();
17355 err
= _KeepAliveTestStartSubtest( test
);
17356 require_noerr( err
, exit
);
17359 if( err
) _KeepAliveTestStop( test
, err
);
17362 //===========================================================================================================================
17364 static void _KeepAliveTestStop( KeepAliveTestRef inTest
, OSStatus inError
)
17368 inTest
->error
= inError
;
17369 inTest
->endTime
= NanoTimeGetCurrent();
17370 _KeepAliveTestStopSubtest( inTest
);
17371 for( i
= 0; i
< countof( inTest
->subtests
); ++i
)
17373 KeepAliveSubtest
* const subtest
= &inTest
->subtests
[ i
];
17375 ForgetSocket( &subtest
->clientSock
);
17376 ForgetSocket( &subtest
->serverSock
);
17378 dispatch_semaphore_signal( inTest
->doneSem
);
17381 //===========================================================================================================================
17383 static OSStatus
_KeepAliveTestStartSubtest( KeepAliveTestRef inTest
)
17386 KeepAliveSubtest
* const subtest
= _KeepAliveTestGetSubtest( inTest
);
17387 const KeepAliveSubtestParams
* const params
= &kKeepAliveSubtestParams
[ inTest
->subtestIdx
];
17389 SocketRef sock
= kInvalidSocketRef
;
17390 const uint32_t loopbackV4
= htonl( INADDR_LOOPBACK
);
17391 SocketContext
* sockCtx
= NULL
;
17392 KeepAliveTestConnectionContext
* cnxCtx
= NULL
;
17394 char serverAddrStr
[ 64 ];
17397 subtest
->error
= kInProgressErr
;
17398 subtest
->startTime
= NanoTimeGetCurrent();
17399 subtest
->description
= params
->description
;
17401 require_action( ( params
->family
== AF_INET
) || ( params
->family
== AF_INET6
), exit
, err
= kInternalErr
);
17403 // Create TCP listener socket.
17405 useIPv4
= ( params
->family
== AF_INET
) ? true : false;
17406 err
= ServerSocketOpenEx( params
->family
, SOCK_STREAM
, IPPROTO_TCP
,
17407 useIPv4
? ( (const void *) &loopbackV4
) : ( (const void *) &in6addr_loopback
), kSocketPort_Auto
, &port
,
17408 kSocketBufferSize_DontSet
, &sock
);
17409 require_noerr( err
, exit
);
17411 if( useIPv4
) SNPrintF( serverAddrStr
, sizeof( serverAddrStr
), "%.4a:%d", &loopbackV4
, port
);
17412 else SNPrintF( serverAddrStr
, sizeof( serverAddrStr
), "[%.16a]:%d", in6addr_loopback
.s6_addr
, port
);
17413 _KeepAliveTestGetSubtestLogPrefix( inTest
, prefix
, sizeof( prefix
) );
17414 kat_ulog( kLogLevelInfo
, "%s: Will listen for connections on %s\n", prefix
, serverAddrStr
);
17416 err
= SocketContextCreate( sock
, inTest
, &sockCtx
);
17417 require_noerr( err
, exit
);
17418 sock
= kInvalidSocketRef
;
17420 // Create read source for TCP listener socket.
17422 check( !inTest
->readSource
);
17423 err
= DispatchReadSourceCreate( sockCtx
->sock
, inTest
->queue
, _KeepAliveTestTCPAcceptHandler
,
17424 SocketContextCancelHandler
, sockCtx
, &inTest
->readSource
);
17425 require_noerr( err
, exit
);
17427 dispatch_resume( inTest
->readSource
);
17429 cnxCtx
= (KeepAliveTestConnectionContext
*) calloc( 1, sizeof( *cnxCtx
) );
17430 require_action( cnxCtx
, exit
, err
= kNoMemoryErr
);
17432 // Start asynchronous connection to listener socket.
17434 kat_ulog( kLogLevelInfo
, "%s: Will connect to %s\n", prefix
, serverAddrStr
);
17436 check( !inTest
->connection
);
17437 err
= AsyncConnection_Connect( &inTest
->connection
, serverAddrStr
, 0, kAsyncConnectionFlags_None
,
17438 5 * UINT64_C_safe( kNanosecondsPerSecond
), kSocketBufferSize_DontSet
, kSocketBufferSize_DontSet
,
17439 NULL
, NULL
, _KeepAliveTestConnectionHandler
, cnxCtx
, inTest
->queue
);
17440 require_noerr( err
, exit
);
17442 cnxCtx
->test
= inTest
;
17443 check( !inTest
->connectionCtx
);
17444 inTest
->connectionCtx
= cnxCtx
;
17448 ForgetSocket( &sock
);
17449 if( sockCtx
) SocketContextRelease( sockCtx
);
17450 FreeNullSafe( cnxCtx
);
17454 //===========================================================================================================================
17456 static void _KeepAliveTestStopSubtest( KeepAliveTestRef inTest
)
17458 dispatch_source_forget( &inTest
->readSource
);
17459 DNSServiceForget( &inTest
->keepalive
);
17460 DNSServiceForget( &inTest
->query
);
17461 dispatch_source_forget( &inTest
->timer
);
17462 _KeepAliveTestForgetConnection( inTest
);
17465 //===========================================================================================================================
17467 static KeepAliveSubtest
* _KeepAliveTestGetSubtest( KeepAliveTestRef inTest
)
17469 return( ( inTest
->subtestIdx
< countof( inTest
->subtests
) ) ? &inTest
->subtests
[ inTest
->subtestIdx
] : NULL
);
17472 //===========================================================================================================================
17474 static const char * _KeepAliveTestGetSubtestLogPrefix( KeepAliveTestRef inTest
, char *inBufPtr
, size_t inBufLen
)
17476 SNPrintF( inBufPtr
, inBufLen
, "Subtest %zu/%zu", inTest
->subtestIdx
+ 1, countof( inTest
->subtests
) );
17477 return( inBufPtr
);
17480 //===========================================================================================================================
17482 static OSStatus
_KeepAliveTestContinue( KeepAliveTestRef inTest
, OSStatus inSubtestError
, Boolean
*outDone
)
17485 KeepAliveSubtest
* subtest
;
17487 require_action( inTest
->subtestIdx
<= countof( inTest
->subtests
), exit
, err
= kInternalErr
);
17489 if( inTest
->subtestIdx
< countof( inTest
->subtests
) )
17491 subtest
= _KeepAliveTestGetSubtest( inTest
);
17492 _KeepAliveTestStopSubtest( inTest
);
17493 subtest
->endTime
= NanoTimeGetCurrent();
17494 subtest
->error
= inSubtestError
;
17495 if( ++inTest
->subtestIdx
< countof( inTest
->subtests
) )
17497 err
= _KeepAliveTestStartSubtest( inTest
);
17498 require_noerr_quiet( err
, exit
);
17504 if( outDone
) *outDone
= ( !err
&& ( inTest
->subtestIdx
== countof( inTest
->subtests
) ) ) ? true : false;
17508 //===========================================================================================================================
17510 static void _KeepAliveTestTCPAcceptHandler( void *inCtx
)
17513 const SocketContext
* const sockCtx
= (SocketContext
*) inCtx
;
17514 const KeepAliveTestRef test
= (KeepAliveTestRef
) sockCtx
->userContext
;
17515 KeepAliveSubtest
* const subtest
= _KeepAliveTestGetSubtest( test
);
17520 check( !IsValidSocket( subtest
->serverSock
) );
17521 len
= (socklen_t
) sizeof( peer
);
17522 subtest
->serverSock
= accept( sockCtx
->sock
, &peer
.sa
, &len
);
17523 err
= map_socket_creation_errno( subtest
->serverSock
);
17524 require_noerr( err
, exit
);
17526 _KeepAliveTestGetSubtestLogPrefix( test
, prefix
, sizeof( prefix
) );
17527 kat_ulog( kLogLevelInfo
, "%s: Accepted connection from %##a\n", prefix
, &peer
.sa
);
17529 dispatch_source_forget( &test
->readSource
);
17532 if( err
) _KeepAliveTestStop( test
, err
);
17535 //===========================================================================================================================
17537 static void _KeepAliveTestConnectionHandler( SocketRef inSock
, OSStatus inError
, void *inArg
)
17539 KeepAliveTestConnectionContext
* ctx
= (KeepAliveTestConnectionContext
*) inArg
;
17540 const KeepAliveTestRef test
= ctx
->test
;
17544 _KeepAliveTestForgetConnection( test
);
17545 _KeepAliveTestHandleConnection( test
, inSock
, inError
);
17546 inSock
= kInvalidSocketRef
;
17548 ForgetSocket( &inSock
);
17552 //===========================================================================================================================
17554 #define kKeepAliveTestQueryTimeoutSecs 5
17556 static void _KeepAliveTestHandleConnection( KeepAliveTestRef inTest
, SocketRef inSock
, OSStatus inError
)
17559 KeepAliveSubtest
* const subtest
= _KeepAliveTestGetSubtest( inTest
);
17560 const KeepAliveSubtestParams
* const params
= &kKeepAliveSubtestParams
[ inTest
->subtestIdx
];
17564 Boolean subtestFailed
= false;
17568 require_noerr_action( inError
, exit
, err
= inError
);
17570 check( !IsValidSocket( subtest
->clientSock
) );
17571 subtest
->clientSock
= inSock
;
17572 inSock
= kInvalidSocketRef
;
17574 // Get local and remote IP addresses.
17576 len
= (socklen_t
) sizeof( subtest
->local
);
17577 err
= getsockname( subtest
->clientSock
, &subtest
->local
.sa
, &len
);
17578 err
= map_global_noerr_errno( err
);
17579 require_noerr( err
, exit
);
17581 len
= (socklen_t
) sizeof( subtest
->remote
);
17582 err
= getpeername( subtest
->clientSock
, &subtest
->remote
.sa
, &len
);
17583 err
= map_global_noerr_errno( err
);
17584 require_noerr( err
, exit
);
17586 _KeepAliveTestGetSubtestLogPrefix( inTest
, prefix
, sizeof( prefix
) );
17587 kat_ulog( kLogLevelInfo
, "%s: Connection established: %##a <-> %##a\n",
17588 prefix
, &subtest
->local
.sa
, &subtest
->remote
.sa
);
17590 // Call either DNSServiceSleepKeepalive() or DNSServiceSleepKeepalive_sockaddr().
17592 check( subtest
->timeoutKA
== 0 );
17593 subtest
->timeoutKA
= (unsigned int) RandomRange( 1, UINT_MAX
);
17595 switch( params
->callVariant
)
17597 case kKeepAliveCallVariant_TakesSocket
:
17598 kat_ulog( kLogLevelInfo
, "%s: Will call DNSServiceSleepKeepalive() for client-side socket\n", prefix
);
17599 check( !inTest
->keepalive
);
17600 err
= DNSServiceSleepKeepalive( &inTest
->keepalive
, 0, subtest
->clientSock
,
17601 subtest
->timeoutKA
, _KeepAliveTestKeepaliveCallback
, inTest
);
17602 require_noerr( err
, exit
);
17604 err
= DNSServiceSetDispatchQueue( inTest
->keepalive
, inTest
->queue
);
17605 require_noerr( err
, exit
);
17608 case kKeepAliveCallVariant_TakesSockAddrs
:
17609 kat_ulog( kLogLevelInfo
,
17610 "%s: Will call DNSServiceSleepKeepalive_sockaddr() for local and remote sockaddrs\n", prefix
);
17611 if( !SOFT_LINK_HAS_FUNCTION( system_dnssd
, DNSServiceSleepKeepalive_sockaddr
) )
17613 kat_ulog( kLogLevelError
,
17614 "%s: Failed to soft link DNSServiceSleepKeepalive_sockaddr from libsystem_dnssd.\n", prefix
);
17615 subtestFailed
= true;
17616 err
= kUnsupportedErr
;
17619 check( !inTest
->keepalive
);
17620 err
= soft_DNSServiceSleepKeepalive_sockaddr( &inTest
->keepalive
, 0, &subtest
->local
.sa
, &subtest
->remote
.sa
,
17621 subtest
->timeoutKA
, _KeepAliveTestKeepaliveCallback
, inTest
);
17622 require_noerr( err
, exit
);
17624 err
= DNSServiceSetDispatchQueue( inTest
->keepalive
, inTest
->queue
);
17625 require_noerr( err
, exit
);
17629 kat_ulog( kLogLevelError
, "%s: Invalid KeepAliveCallVariant value %d\n", prefix
, (int) params
->callVariant
);
17630 err
= kInternalErr
;
17633 // Use the same logic that the DNSServiceSleepKeepalive functions use to derive a record name and rdata.
17636 family
= subtest
->local
.sa
.sa_family
;
17637 if( family
== AF_INET
)
17639 const struct sockaddr_in
* const sin
= &subtest
->local
.v4
;
17640 const uint8_t * ptr
;
17642 check_compile_time_code( sizeof( sin
->sin_addr
.s_addr
) == 4 );
17643 ptr
= (const uint8_t *) &sin
->sin_addr
.s_addr
;
17644 for( i
= 0; i
< 4; ++i
) value
+= ptr
[ i
];
17645 value
+= sin
->sin_port
; // Note: No ntohl(). This is what DNSServiceSleepKeepalive does.
17647 check( subtest
->remote
.sa
.sa_family
== AF_INET
);
17648 ASPrintF( &subtest
->dataStr
, "t=%u h=%.4a d=%.4a l=%u r=%u",
17649 subtest
->timeoutKA
, &subtest
->local
.v4
.sin_addr
.s_addr
, &subtest
->remote
.v4
.sin_addr
.s_addr
,
17650 ntohs( subtest
->local
.v4
.sin_port
), ntohs( subtest
->remote
.v4
.sin_port
) );
17651 require_action( subtest
->dataStr
, exit
, err
= kNoMemoryErr
);
17653 else if( family
== AF_INET6
)
17655 const struct sockaddr_in6
* const sin6
= &subtest
->local
.v6
;
17657 check_compile_time_code( countof( sin6
->sin6_addr
.s6_addr
) == 16 );
17658 for( i
= 0; i
< 16; ++i
) value
+= sin6
->sin6_addr
.s6_addr
[ i
];
17659 value
+= sin6
->sin6_port
; // Note: No ntohl(). This is what DNSServiceSleepKeepalive does.
17661 check( subtest
->remote
.sa
.sa_family
== AF_INET6
);
17662 ASPrintF( &subtest
->dataStr
, "t=%u H=%.16a D=%.16a l=%u r=%u",
17663 subtest
->timeoutKA
, subtest
->local
.v6
.sin6_addr
.s6_addr
, subtest
->remote
.v6
.sin6_addr
.s6_addr
,
17664 ntohs( subtest
->local
.v6
.sin6_port
), ntohs( subtest
->remote
.v6
.sin6_port
) );
17665 require_action( subtest
->dataStr
, exit
, err
= kNoMemoryErr
);
17669 kat_ulog( kLogLevelError
, "%s: Unexpected local address family %d\n", prefix
, family
);
17670 err
= kInternalErr
;
17674 // Start query for the new keepalive record.
17676 check( !subtest
->recordName
);
17677 ASPrintF( &subtest
->recordName
, "%u._keepalive._dns-sd._udp.local.", value
);
17678 require_action( subtest
->recordName
, exit
, err
= kNoMemoryErr
);
17680 kat_ulog( kLogLevelInfo
, "%s: Will query for %s NULL record\n", prefix
, subtest
->recordName
);
17681 check( !inTest
->query
);
17682 err
= DNSServiceQueryRecord( &inTest
->query
, 0, kDNSServiceInterfaceIndexLocalOnly
, subtest
->recordName
,
17683 kDNSServiceType_NULL
, kDNSServiceClass_IN
, _KeepAliveTestQueryRecordCallback
, inTest
);
17684 require_noerr( err
, exit
);
17686 err
= DNSServiceSetDispatchQueue( inTest
->query
, inTest
->queue
);
17687 require_noerr( err
, exit
);
17689 // Start timer to enforce a time limit on the query.
17691 check( !inTest
->timer
);
17692 err
= DispatchTimerOneShotCreate( dispatch_time_seconds( kKeepAliveTestQueryTimeoutSecs
),
17693 kKeepAliveTestQueryTimeoutSecs
* ( INT64_C_safe( kNanosecondsPerSecond
) / 20 ), inTest
->queue
,
17694 _KeepAliveTestQueryTimerHandler
, inTest
, &inTest
->timer
);
17695 require_noerr( err
, exit
);
17696 dispatch_resume( inTest
->timer
);
17699 ForgetSocket( &inSock
);
17700 if( subtestFailed
)
17702 err
= _KeepAliveTestContinue( inTest
, err
, &done
);
17703 check_noerr( err
);
17709 if( err
|| done
) _KeepAliveTestStop( inTest
, err
);
17712 //===========================================================================================================================
17714 static void _KeepAliveTestForgetConnection( KeepAliveTestRef inTest
)
17716 if( inTest
->connection
)
17718 check( inTest
->connectionCtx
);
17719 inTest
->connectionCtx
->test
= NULL
; // Unset the connection's back pointer to test.
17720 inTest
->connectionCtx
= NULL
; // Context will be freed by the connection's handler.
17721 AsyncConnection_Forget( &inTest
->connection
);
17725 //===========================================================================================================================
17727 static void DNSSD_API
_KeepAliveTestKeepaliveCallback( DNSServiceRef inSDRef
, DNSServiceErrorType inError
, void *inCtx
)
17730 const KeepAliveTestRef test
= (KeepAliveTestRef
) inCtx
;
17735 _KeepAliveTestGetSubtestLogPrefix( test
, prefix
, sizeof( prefix
) );
17736 kat_ulog( kLogLevelInfo
, "%s: Keepalive callback error: %#m\n", prefix
, inError
);
17742 err
= _KeepAliveTestContinue( test
, inError
, &done
);
17743 check_noerr( err
);
17744 if( err
|| done
) _KeepAliveTestStop( test
, err
);
17748 //===========================================================================================================================
17750 static void _KeepAliveTestQueryTimerHandler( void *inCtx
)
17753 const KeepAliveTestRef test
= (KeepAliveTestRef
) inCtx
;
17754 KeepAliveSubtest
* const subtest
= _KeepAliveTestGetSubtest( test
);
17758 _KeepAliveTestGetSubtestLogPrefix( test
, prefix
, sizeof( prefix
) );
17759 kat_ulog( kLogLevelInfo
, "%s: Query for \"%s\" timed out.\n", prefix
, subtest
->recordName
);
17761 err
= _KeepAliveTestContinue( test
, kTimeoutErr
, &done
);
17762 check_noerr( err
);
17763 if( err
|| done
) _KeepAliveTestStop( test
, err
);
17766 //===========================================================================================================================
17768 static void DNSSD_API
17769 _KeepAliveTestQueryRecordCallback(
17770 DNSServiceRef inSDRef
,
17771 DNSServiceFlags inFlags
,
17772 uint32_t inInterfaceIndex
,
17773 DNSServiceErrorType inError
,
17774 const char * inFullName
,
17777 uint16_t inRDataLen
,
17778 const void * inRDataPtr
,
17783 const KeepAliveTestRef test
= (KeepAliveTestRef
) inCtx
;
17784 KeepAliveSubtest
* const subtest
= _KeepAliveTestGetSubtest( test
);
17785 const uint8_t * ptr
;
17786 size_t dataStrLen
, minLen
;
17791 Unused( inInterfaceIndex
);
17794 _KeepAliveTestGetSubtestLogPrefix( test
, prefix
, sizeof( prefix
) );
17795 if( strcasecmp( inFullName
, subtest
->recordName
) != 0 )
17797 kat_ulog( kLogLevelError
, "%s: QueryRecord(%s) result: Got unexpected record name \"%s\".\n",
17798 prefix
, subtest
->recordName
, inFullName
);
17799 err
= kUnexpectedErr
;
17802 if( inType
!= kDNSServiceType_NULL
)
17804 kat_ulog( kLogLevelError
, "%s: QueryRecord(%s) result: Got unexpected record type %d (%s) != %d (NULL).\n",
17805 prefix
, subtest
->recordName
, inType
, RecordTypeToString( inType
), kDNSServiceType_NULL
);
17806 err
= kUnexpectedErr
;
17809 if( inClass
!= kDNSServiceClass_IN
)
17811 kat_ulog( kLogLevelError
, "%s: QueryRecord(%s) result: Got unexpected record class %d != %d (IN).\n",
17812 prefix
, subtest
->recordName
, inClass
, kDNSServiceClass_IN
);
17813 err
= kUnexpectedErr
;
17818 kat_ulog( kLogLevelError
, "%s: QueryRecord(%s) result: Got unexpected error %#m.\n",
17819 prefix
, subtest
->recordName
, inError
);
17823 if( ( inFlags
& kDNSServiceFlagsAdd
) == 0 )
17825 kat_ulog( kLogLevelError
, "%s: QueryRecord(%s) result: Missing Add flag.\n", prefix
, subtest
->recordName
);
17826 err
= kUnexpectedErr
;
17829 kat_ulog( kLogLevelInfo
, "%s: QueryRecord(%s) result rdata: %#H\n",
17830 prefix
, subtest
->recordName
, inRDataPtr
, inRDataLen
, inRDataLen
);
17832 dataStrLen
= strlen( subtest
->dataStr
) + 1; // There's a NUL terminator at the end of the rdata.
17833 minLen
= 1 + dataStrLen
; // The first byte of the rdata is a length byte.
17834 if( inRDataLen
< minLen
)
17836 kat_ulog( kLogLevelError
, "%s: QueryRecord(%s) result: rdata length (%d) is less than expected minimum (%zu).\n",
17837 prefix
, subtest
->recordName
, inRDataLen
, minLen
);
17838 err
= kUnexpectedErr
;
17841 ptr
= (const uint8_t *) inRDataPtr
;
17842 if( ptr
[ 0 ] < dataStrLen
)
17844 kat_ulog( kLogLevelError
,
17845 "%s: QueryRecord(%s) result: rdata length byte value (%d) is less than expected minimum (%zu).\n",
17846 prefix
, subtest
->recordName
, ptr
[ 0 ], dataStrLen
);
17847 err
= kUnexpectedErr
;
17850 if( memcmp( &ptr
[ 1 ], subtest
->dataStr
, dataStrLen
- 1 ) != 0 )
17852 kat_ulog( kLogLevelError
, "%s: QueryRecord(%s) result: rdata body doesn't contain '%s'.\n",
17853 prefix
, subtest
->recordName
, subtest
->dataStr
);
17858 err
= _KeepAliveTestContinue( test
, err
, &done
);
17859 check_noerr( err
);
17860 if( err
|| done
) _KeepAliveTestStop( test
, kNoErr
);
17862 #endif // TARGET_OS_DARWIN
17864 //===========================================================================================================================
17866 //===========================================================================================================================
17868 #define kSSDPPort 1900
17872 HTTPHeader header
; // HTTP header object for sending and receiving.
17873 dispatch_source_t readSourceV4
; // Read dispatch source for IPv4 socket.
17874 dispatch_source_t readSourceV6
; // Read dispatch source for IPv6 socket.
17875 int receiveSecs
; // After send, the amount of time to spend receiving.
17876 uint32_t ifindex
; // Index of the interface over which to send the query.
17877 Boolean useIPv4
; // True if the query should be sent via IPv4 multicast.
17878 Boolean useIPv6
; // True if the query should be sent via IPv6 multicast.
17880 } SSDPDiscoverContext
;
17882 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext
*inContext
);
17883 static void SSDPDiscoverReadHandler( void *inContext
);
17884 static int SocketToPortNumber( SocketRef inSock
);
17885 static OSStatus
WriteSSDPSearchRequest( HTTPHeader
*inHeader
, const void *inHostSA
, int inMX
, const char *inST
);
17887 static void SSDPDiscoverCmd( void )
17890 struct timeval now
;
17891 SSDPDiscoverContext
* context
;
17892 dispatch_source_t signalSource
= NULL
;
17893 SocketRef sockV4
= kInvalidSocketRef
;
17894 SocketRef sockV6
= kInvalidSocketRef
;
17898 // Set up SIGINT handler.
17900 signal( SIGINT
, SIG_IGN
);
17901 err
= DispatchSignalSourceCreate( SIGINT
, dispatch_get_main_queue(), Exit
, kExitReason_SIGINT
, &signalSource
);
17902 require_noerr( err
, exit
);
17903 dispatch_resume( signalSource
);
17905 // Check command parameters.
17907 if( gSSDPDiscover_ReceiveSecs
< -1 )
17909 FPrintF( stdout
, "Invalid receive time: %d seconds.\n", gSSDPDiscover_ReceiveSecs
);
17916 context
= (SSDPDiscoverContext
*) calloc( 1, sizeof( *context
) );
17917 require_action( context
, exit
, err
= kNoMemoryErr
);
17919 context
->receiveSecs
= gSSDPDiscover_ReceiveSecs
;
17920 context
->useIPv4
= ( gSSDPDiscover_UseIPv4
|| !gSSDPDiscover_UseIPv6
) ? true : false;
17921 context
->useIPv6
= ( gSSDPDiscover_UseIPv6
|| !gSSDPDiscover_UseIPv4
) ? true : false;
17923 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifindex
);
17924 require_noerr_quiet( err
, exit
);
17926 // Set up IPv4 socket.
17928 if( context
->useIPv4
)
17931 err
= UDPClientSocketOpen( AF_INET
, NULL
, 0, -1, &port
, &sockV4
);
17932 require_noerr( err
, exit
);
17934 err
= SocketSetMulticastInterface( sockV4
, NULL
, context
->ifindex
);
17935 require_noerr( err
, exit
);
17937 err
= setsockopt( sockV4
, IPPROTO_IP
, IP_MULTICAST_LOOP
, (char *) &(uint8_t){ 1 }, (socklen_t
) sizeof( uint8_t ) );
17938 err
= map_socket_noerr_errno( sockV4
, err
);
17939 require_noerr( err
, exit
);
17942 // Set up IPv6 socket.
17944 if( context
->useIPv6
)
17946 err
= UDPClientSocketOpen( AF_INET6
, NULL
, 0, -1, NULL
, &sockV6
);
17947 require_noerr( err
, exit
);
17949 err
= SocketSetMulticastInterface( sockV6
, NULL
, context
->ifindex
);
17950 require_noerr( err
, exit
);
17952 err
= setsockopt( sockV6
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, (char *) &(int){ 1 }, (socklen_t
) sizeof( int ) );
17953 err
= map_socket_noerr_errno( sockV6
, err
);
17954 require_noerr( err
, exit
);
17959 SSDPDiscoverPrintPrologue( context
);
17961 // Send mDNS query message.
17964 if( IsValidSocket( sockV4
) )
17966 struct sockaddr_in mcastAddr4
;
17968 memset( &mcastAddr4
, 0, sizeof( mcastAddr4
) );
17969 SIN_LEN_SET( &mcastAddr4
);
17970 mcastAddr4
.sin_family
= AF_INET
;
17971 mcastAddr4
.sin_port
= htons( kSSDPPort
);
17972 mcastAddr4
.sin_addr
.s_addr
= htonl( 0xEFFFFFFA ); // 239.255.255.250
17974 err
= WriteSSDPSearchRequest( &context
->header
, &mcastAddr4
, gSSDPDiscover_MX
, gSSDPDiscover_ST
);
17975 require_noerr( err
, exit
);
17977 n
= sendto( sockV4
, context
->header
.buf
, context
->header
.len
, 0, (const struct sockaddr
*) &mcastAddr4
,
17978 (socklen_t
) sizeof( mcastAddr4
) );
17979 err
= map_socket_value_errno( sockV4
, n
== (ssize_t
) context
->header
.len
, n
);
17982 FPrintF( stderr
, "*** Failed to send query on IPv4 socket with error %#m\n", err
);
17983 ForgetSocket( &sockV4
);
17987 if( gSSDPDiscover_Verbose
)
17989 gettimeofday( &now
, NULL
);
17990 FPrintF( stdout
, "---\n" );
17991 FPrintF( stdout
, "Send time: %{du:time}\n", &now
);
17992 FPrintF( stdout
, "Source Port: %d\n", SocketToPortNumber( sockV4
) );
17993 FPrintF( stdout
, "Destination: %##a\n", &mcastAddr4
);
17994 FPrintF( stdout
, "Message size: %zu\n", context
->header
.len
);
17995 FPrintF( stdout
, "HTTP header:\n%1{text}", context
->header
.buf
, context
->header
.len
);
18001 if( IsValidSocket( sockV6
) )
18003 struct sockaddr_in6 mcastAddr6
;
18005 memset( &mcastAddr6
, 0, sizeof( mcastAddr6
) );
18006 SIN6_LEN_SET( &mcastAddr6
);
18007 mcastAddr6
.sin6_family
= AF_INET6
;
18008 mcastAddr6
.sin6_port
= htons( kSSDPPort
);
18009 mcastAddr6
.sin6_addr
.s6_addr
[ 0 ] = 0xFF; // SSDP IPv6 link-local multicast address FF02::C
18010 mcastAddr6
.sin6_addr
.s6_addr
[ 1 ] = 0x02;
18011 mcastAddr6
.sin6_addr
.s6_addr
[ 15 ] = 0x0C;
18013 err
= WriteSSDPSearchRequest( &context
->header
, &mcastAddr6
, gSSDPDiscover_MX
, gSSDPDiscover_ST
);
18014 require_noerr( err
, exit
);
18016 n
= sendto( sockV6
, context
->header
.buf
, context
->header
.len
, 0, (const struct sockaddr
*) &mcastAddr6
,
18017 (socklen_t
) sizeof( mcastAddr6
) );
18018 err
= map_socket_value_errno( sockV6
, n
== (ssize_t
) context
->header
.len
, n
);
18021 FPrintF( stderr
, "*** Failed to send query on IPv6 socket with error %#m\n", err
);
18022 ForgetSocket( &sockV6
);
18026 if( gSSDPDiscover_Verbose
)
18028 gettimeofday( &now
, NULL
);
18029 FPrintF( stdout
, "---\n" );
18030 FPrintF( stdout
, "Send time: %{du:time}\n", &now
);
18031 FPrintF( stdout
, "Source Port: %d\n", SocketToPortNumber( sockV6
) );
18032 FPrintF( stdout
, "Destination: %##a\n", &mcastAddr6
);
18033 FPrintF( stdout
, "Message size: %zu\n", context
->header
.len
);
18034 FPrintF( stdout
, "HTTP header:\n%1{text}", context
->header
.buf
, context
->header
.len
);
18039 require_action_quiet( sendCount
> 0, exit
, err
= kUnexpectedErr
);
18041 // If there's no wait period after the send, then exit.
18043 if( context
->receiveSecs
== 0 ) goto exit
;
18045 // Create dispatch read sources for socket(s).
18047 if( IsValidSocket( sockV4
) )
18049 SocketContext
* sockCtx
;
18051 err
= SocketContextCreate( sockV4
, context
, &sockCtx
);
18052 require_noerr( err
, exit
);
18053 sockV4
= kInvalidSocketRef
;
18055 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, SSDPDiscoverReadHandler
, SocketContextCancelHandler
, sockCtx
,
18056 &context
->readSourceV4
);
18057 if( err
) ForgetSocketContext( &sockCtx
);
18058 require_noerr( err
, exit
);
18060 dispatch_resume( context
->readSourceV4
);
18063 if( IsValidSocket( sockV6
) )
18065 SocketContext
* sockCtx
;
18067 err
= SocketContextCreate( sockV6
, context
, &sockCtx
);
18068 require_noerr( err
, exit
);
18069 sockV6
= kInvalidSocketRef
;
18071 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, SSDPDiscoverReadHandler
, SocketContextCancelHandler
, sockCtx
,
18072 &context
->readSourceV6
);
18073 if( err
) ForgetSocketContext( &sockCtx
);
18074 require_noerr( err
, exit
);
18076 dispatch_resume( context
->readSourceV6
);
18079 if( context
->receiveSecs
> 0 )
18081 dispatch_after_f( dispatch_time_seconds( context
->receiveSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
18087 ForgetSocket( &sockV4
);
18088 ForgetSocket( &sockV6
);
18089 dispatch_source_forget( &signalSource
);
18090 exit( err
? 1 : 0 );
18093 static int SocketToPortNumber( SocketRef inSock
)
18099 len
= (socklen_t
) sizeof( sip
);
18100 err
= getsockname( inSock
, &sip
.sa
, &len
);
18101 err
= map_socket_noerr_errno( inSock
, err
);
18102 check_noerr( err
);
18103 return( err
? -1 : SockAddrGetPort( &sip
) );
18106 static OSStatus
WriteSSDPSearchRequest( HTTPHeader
*inHeader
, const void *inHostSA
, int inMX
, const char *inST
)
18110 err
= HTTPHeader_InitRequest( inHeader
, "M-SEARCH", "*", "HTTP/1.1" );
18111 require_noerr( err
, exit
);
18113 err
= HTTPHeader_SetField( inHeader
, "Host", "%##a", inHostSA
);
18114 require_noerr( err
, exit
);
18116 err
= HTTPHeader_SetField( inHeader
, "ST", "%s", inST
? inST
: "ssdp:all" );
18117 require_noerr( err
, exit
);
18119 err
= HTTPHeader_SetField( inHeader
, "Man", "\"ssdp:discover\"" );
18120 require_noerr( err
, exit
);
18122 err
= HTTPHeader_SetField( inHeader
, "MX", "%d", inMX
);
18123 require_noerr( err
, exit
);
18125 err
= HTTPHeader_Commit( inHeader
);
18126 require_noerr( err
, exit
);
18132 //===========================================================================================================================
18133 // SSDPDiscoverPrintPrologue
18134 //===========================================================================================================================
18136 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext
*inContext
)
18138 const int receiveSecs
= inContext
->receiveSecs
;
18139 const char * ifName
;
18140 char ifNameBuf
[ IF_NAMESIZE
+ 1 ];
18141 NetTransportType ifType
;
18143 ifName
= if_indextoname( inContext
->ifindex
, ifNameBuf
);
18145 ifType
= kNetTransportType_Undefined
;
18146 if( ifName
) SocketGetInterfaceInfo( kInvalidSocketRef
, ifName
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &ifType
);
18148 FPrintF( stdout
, "Interface: %s/%d/%s\n",
18149 ifName
? ifName
: "?", inContext
->ifindex
, NetTransportTypeToString( ifType
) );
18150 FPrintF( stdout
, "IP protocols: %?s%?s%?s\n",
18151 inContext
->useIPv4
, "IPv4", ( inContext
->useIPv4
&& inContext
->useIPv6
), ", ", inContext
->useIPv6
, "IPv6" );
18152 FPrintF( stdout
, "Receive duration: " );
18153 if( receiveSecs
>= 0 ) FPrintF( stdout
, "%d second%?c\n", receiveSecs
, receiveSecs
!= 1, 's' );
18154 else FPrintF( stdout
, "∞\n" );
18155 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
18158 //===========================================================================================================================
18159 // SSDPDiscoverReadHandler
18160 //===========================================================================================================================
18162 static Boolean
_HTTPHeader_Validate( HTTPHeader
*inHeader
);
18164 static void SSDPDiscoverReadHandler( void *inContext
)
18167 struct timeval now
;
18168 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
18169 SSDPDiscoverContext
* const context
= (SSDPDiscoverContext
*) sockCtx
->userContext
;
18170 HTTPHeader
* const header
= &context
->header
;
18171 sockaddr_ip fromAddr
;
18174 gettimeofday( &now
, NULL
);
18176 err
= SocketRecvFrom( sockCtx
->sock
, header
->buf
, sizeof( header
->buf
), &msgLen
, &fromAddr
, sizeof( fromAddr
),
18177 NULL
, NULL
, NULL
, NULL
);
18178 require_noerr( err
, exit
);
18180 FPrintF( stdout
, "---\n" );
18181 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
18182 FPrintF( stdout
, "Source: %##a\n", &fromAddr
);
18183 FPrintF( stdout
, "Message size: %zu\n", msgLen
);
18184 header
->len
= msgLen
;
18185 if( _HTTPHeader_Validate( header
) )
18187 FPrintF( stdout
, "HTTP header:\n%1{text}", header
->buf
, header
->len
);
18188 if( header
->extraDataLen
> 0 )
18190 FPrintF( stdout
, "HTTP body: %1.1H", header
->extraDataPtr
, (int) header
->extraDataLen
, INT_MAX
);
18195 FPrintF( stdout
, "Invalid HTTP message:\n%1.1H", header
->buf
, (int) msgLen
, INT_MAX
);
18200 if( err
) exit( 1 );
18203 //===========================================================================================================================
18204 // _HTTPHeader_Validate
18206 // Parses for the end of an HTTP header and updates the HTTPHeader structure so it's ready to parse. Returns true if valid.
18207 // This assumes the "buf" and "len" fields are set. The other fields are set by this function.
18209 // Note: This was copied from CoreUtils because the HTTPHeader_Validate function is currently not exported in the framework.
18210 //===========================================================================================================================
18212 static Boolean
_HTTPHeader_Validate( HTTPHeader
*inHeader
)
18217 // Check for interleaved binary data (4 byte header that begins with $). See RFC 2326 section 10.12.
18219 require( inHeader
->len
< sizeof( inHeader
->buf
), exit
);
18220 src
= inHeader
->buf
;
18221 end
= src
+ inHeader
->len
;
18222 if( ( ( end
- src
) >= 4 ) && ( src
[ 0 ] == '$' ) )
18228 // Search for an empty line (HTTP-style header/body separator). CRLFCRLF, LFCRLF, or LFLF accepted.
18229 // $$$ TO DO: Start from the last search location to avoid re-searching the same data over and over.
18233 while( ( src
< end
) && ( src
[ 0 ] != '\n' ) ) ++src
;
18234 if( src
>= end
) goto exit
;
18236 if( ( ( end
- src
) >= 2 ) && ( src
[ 0 ] == '\r' ) && ( src
[ 1 ] == '\n' ) ) // CFLFCRLF or LFCRLF
18241 else if( ( ( end
- src
) >= 1 ) && ( src
[ 0 ] == '\n' ) ) // LFLF
18248 inHeader
->extraDataPtr
= src
;
18249 inHeader
->extraDataLen
= (size_t)( end
- src
);
18250 inHeader
->len
= (size_t)( src
- inHeader
->buf
);
18257 #if( TARGET_OS_DARWIN )
18258 //===========================================================================================================================
18260 //===========================================================================================================================
18262 // res_query() from libresolv is actually called res_9_query (see /usr/include/resolv.h).
18264 SOFT_LINK_LIBRARY_EX( "/usr/lib", resolv
);
18265 SOFT_LINK_FUNCTION_EX( resolv
, res_9_query
,
18267 ( const char *dname
, int class, int type
, u_char
*answer
, int anslen
),
18268 ( dname
, class, type
, answer
, anslen
) );
18270 // res_query() from libinfo
18272 SOFT_LINK_LIBRARY_EX( "/usr/lib", info
);
18273 SOFT_LINK_FUNCTION_EX( info
, res_query
,
18275 ( const char *dname
, int class, int type
, u_char
*answer
, int anslen
),
18276 ( dname
, class, type
, answer
, anslen
) );
18278 typedef int ( *res_query_f
)( const char *dname
, int class, int type
, u_char
*answer
, int anslen
);
18280 static void ResQueryCmd( void )
18283 res_query_f res_query_ptr
;
18285 uint16_t type
, class;
18286 uint8_t answer
[ 1024 ];
18288 // Get pointer to one of the res_query() functions.
18290 if( gResQuery_UseLibInfo
)
18292 if( !SOFT_LINK_HAS_FUNCTION( info
, res_query
) )
18294 FPrintF( stderr
, "Failed to soft link res_query from libinfo.\n" );
18295 err
= kNotFoundErr
;
18298 res_query_ptr
= soft_res_query
;
18302 if( !SOFT_LINK_HAS_FUNCTION( resolv
, res_9_query
) )
18304 FPrintF( stderr
, "Failed to soft link res_query from libresolv.\n" );
18305 err
= kNotFoundErr
;
18308 res_query_ptr
= soft_res_9_query
;
18311 // Get record type.
18313 err
= RecordTypeFromArgString( gResQuery_Type
, &type
);
18314 require_noerr( err
, exit
);
18316 // Get record class.
18318 if( gResQuery_Class
)
18320 err
= RecordClassFromArgString( gResQuery_Class
, &class );
18321 require_noerr( err
, exit
);
18325 class = kDNSServiceClass_IN
;
18330 FPrintF( stdout
, "Name: %s\n", gResQuery_Name
);
18331 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( type
), type
);
18332 FPrintF( stdout
, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN
) ? "IN" : "???", class );
18333 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
18334 FPrintF( stdout
, "---\n" );
18336 // Call res_query().
18338 n
= res_query_ptr( gResQuery_Name
, class, type
, (u_char
*) answer
, (int) sizeof( answer
) );
18341 FPrintF( stderr
, "res_query() failed with error: %d (%s).\n", h_errno
, hstrerror( h_errno
) );
18348 FPrintF( stdout
, "Message size: %d\n\n%{du:dnsmsg}", n
, answer
, (size_t) n
);
18351 if( err
) exit( 1 );
18354 //===========================================================================================================================
18355 // ResolvDNSQueryCmd
18356 //===========================================================================================================================
18358 // 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
18359 // avoid including the header file.
18361 typedef void * dns_handle_t
;
18363 SOFT_LINK_FUNCTION_EX( resolv
, dns_open
, dns_handle_t
, ( const char *path
), ( path
) );
18364 SOFT_LINK_FUNCTION_VOID_RETURN_EX( resolv
, dns_free
, ( dns_handle_t
*dns
), ( dns
) );
18365 SOFT_LINK_FUNCTION_EX( resolv
, dns_query
,
18373 struct sockaddr
* from
,
18374 uint32_t * fromlen
),
18375 ( dns
, name
, dnsclass
, dnstype
, buf
, len
, from
, fromlen
) );
18377 static void ResolvDNSQueryCmd( void )
18381 dns_handle_t dns
= NULL
;
18382 uint16_t type
, class;
18385 uint8_t answer
[ 1024 ];
18387 // Make sure that the required symbols are available.
18389 if( !SOFT_LINK_HAS_FUNCTION( resolv
, dns_open
) )
18391 FPrintF( stderr
, "Failed to soft link dns_open from libresolv.\n" );
18392 err
= kNotFoundErr
;
18396 if( !SOFT_LINK_HAS_FUNCTION( resolv
, dns_free
) )
18398 FPrintF( stderr
, "Failed to soft link dns_free from libresolv.\n" );
18399 err
= kNotFoundErr
;
18403 if( !SOFT_LINK_HAS_FUNCTION( resolv
, dns_query
) )
18405 FPrintF( stderr
, "Failed to soft link dns_query from libresolv.\n" );
18406 err
= kNotFoundErr
;
18410 // Get record type.
18412 err
= RecordTypeFromArgString( gResolvDNSQuery_Type
, &type
);
18413 require_noerr( err
, exit
);
18415 // Get record class.
18417 if( gResolvDNSQuery_Class
)
18419 err
= RecordClassFromArgString( gResolvDNSQuery_Class
, &class );
18420 require_noerr( err
, exit
);
18424 class = kDNSServiceClass_IN
;
18429 dns
= soft_dns_open( gResolvDNSQuery_Path
);
18432 FPrintF( stderr
, "dns_open( %s ) failed.\n", gResolvDNSQuery_Path
);
18439 FPrintF( stdout
, "Name: %s\n", gResolvDNSQuery_Name
);
18440 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( type
), type
);
18441 FPrintF( stdout
, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN
) ? "IN" : "???", class );
18442 FPrintF( stdout
, "Path: %s\n", gResolvDNSQuery_Path
? gResolvDNSQuery_Name
: "<NULL>" );
18443 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
18444 FPrintF( stdout
, "---\n" );
18446 // Call dns_query().
18448 memset( &from
, 0, sizeof( from
) );
18449 fromLen
= (uint32_t) sizeof( from
);
18450 n
= soft_dns_query( dns
, gResolvDNSQuery_Name
, class, type
, (char *) answer
, (uint32_t) sizeof( answer
), &from
.sa
,
18454 FPrintF( stderr
, "dns_query() failed with error: %d (%s).\n", h_errno
, hstrerror( h_errno
) );
18461 FPrintF( stdout
, "From: %##a\n", &from
);
18462 FPrintF( stdout
, "Message size: %d\n\n%{du:dnsmsg}", n
, answer
, (size_t) n
);
18465 if( dns
) soft_dns_free( dns
);
18466 if( err
) exit( 1 );
18469 //===========================================================================================================================
18471 //===========================================================================================================================
18474 _CFHostResolveCallback(
18476 CFHostInfoType inInfoType
,
18477 const CFStreamError
* inError
,
18480 static void CFHostCmd( void )
18485 CFHostRef host
= NULL
;
18486 CFHostClientContext context
;
18487 CFStreamError streamErr
;
18489 name
= CFStringCreateWithCString( kCFAllocatorDefault
, gCFHost_Name
, kCFStringEncodingUTF8
);
18490 require_action( name
, exit
, err
= kUnknownErr
);
18492 host
= CFHostCreateWithName( kCFAllocatorDefault
, name
);
18494 require_action( host
, exit
, err
= kUnknownErr
);
18496 memset( &context
, 0, sizeof( context
) );
18497 success
= CFHostSetClient( host
, _CFHostResolveCallback
, &context
);
18498 require_action( success
, exit
, err
= kUnknownErr
);
18500 CFHostScheduleWithRunLoop( host
, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode
);
18504 FPrintF( stdout
, "Hostname: %s\n", gCFHost_Name
);
18505 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
18506 FPrintF( stdout
, "---\n" );
18508 success
= CFHostStartInfoResolution( host
, kCFHostAddresses
, &streamErr
);
18509 require_action( success
, exit
, err
= kUnknownErr
);
18515 CFReleaseNullSafe( host
);
18516 if( err
) exit( 1 );
18519 static void _CFHostResolveCallback( CFHostRef inHost
, CFHostInfoType inInfoType
, const CFStreamError
*inError
, void *inInfo
)
18522 struct timeval now
;
18524 gettimeofday( &now
, NULL
);
18526 Unused( inInfoType
);
18529 if( inError
&& ( inError
->domain
!= 0 ) && ( inError
->error
) )
18531 err
= inError
->error
;
18532 if( inError
->domain
== kCFStreamErrorDomainNetDB
)
18534 FPrintF( stderr
, "Error %d: %s.\n", err
, gai_strerror( err
) );
18538 FPrintF( stderr
, "Error %#m\n", err
);
18543 CFArrayRef addresses
;
18545 CFDataRef addrData
;
18546 const struct sockaddr
* sockAddr
;
18547 Boolean wasResolved
= false;
18549 addresses
= CFHostGetAddressing( inHost
, &wasResolved
);
18550 check( wasResolved
);
18554 count
= CFArrayGetCount( addresses
);
18555 for( i
= 0; i
< count
; ++i
)
18557 addrData
= CFArrayGetCFDataAtIndex( addresses
, i
, &err
);
18558 require_noerr( err
, exit
);
18560 sockAddr
= (const struct sockaddr
*) CFDataGetBytePtr( addrData
);
18561 FPrintF( stdout
, "%##a\n", sockAddr
);
18567 FPrintF( stdout
, "---\n" );
18568 FPrintF( stdout
, "End time: %{du:time}\n", &now
);
18570 if( gCFHost_WaitSecs
> 0 ) sleep( (unsigned int) gCFHost_WaitSecs
);
18573 exit( err
? 1 : 0 );
18576 //===========================================================================================================================
18579 // Note: Based on ajn's supplemental test tool.
18580 //===========================================================================================================================
18582 static void DNSConfigAddCmd( void )
18585 CFMutableDictionaryRef dict
= NULL
;
18586 CFMutableArrayRef array
= NULL
;
18588 SCDynamicStoreRef store
= NULL
;
18589 CFStringRef key
= NULL
;
18592 // Create dictionary.
18594 dict
= CFDictionaryCreateMutable( NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
18595 require_action( dict
, exit
, err
= kNoMemoryErr
);
18597 // Add DNS server IP addresses.
18599 array
= CFArrayCreateMutable( NULL
, (CFIndex
) gDNSConfigAdd_IPAddrCount
, &kCFTypeArrayCallBacks
);
18600 require_action( array
, exit
, err
= kNoMemoryErr
);
18602 for( i
= 0; i
< gDNSConfigAdd_IPAddrCount
; ++i
)
18604 CFStringRef addrStr
;
18606 addrStr
= CFStringCreateWithCString( NULL
, gDNSConfigAdd_IPAddrArray
[ i
], kCFStringEncodingUTF8
);
18607 require_action( addrStr
, exit
, err
= kUnknownErr
);
18609 CFArrayAppendValue( array
, addrStr
);
18610 CFRelease( addrStr
);
18613 CFDictionarySetValue( dict
, kSCPropNetDNSServerAddresses
, array
);
18614 ForgetCF( &array
);
18616 // Add domains, if any.
18618 array
= CFArrayCreateMutable( NULL
, (CFIndex
) Min( gDNSConfigAdd_DomainCount
, 1 ), &kCFTypeArrayCallBacks
);
18619 require_action( array
, exit
, err
= kNoMemoryErr
);
18621 if( gDNSConfigAdd_DomainCount
> 0 )
18623 for( i
= 0; i
< gDNSConfigAdd_DomainCount
; ++i
)
18625 CFStringRef domainStr
;
18627 domainStr
= CFStringCreateWithCString( NULL
, gDNSConfigAdd_DomainArray
[ i
], kCFStringEncodingUTF8
);
18628 require_action( domainStr
, exit
, err
= kUnknownErr
);
18630 CFArrayAppendValue( array
, domainStr
);
18631 CFRelease( domainStr
);
18636 // There are no domains, but the domain array needs to be non-empty, so add a zero-length string to the array.
18638 CFArrayAppendValue( array
, CFSTR( "" ) );
18641 CFDictionarySetValue( dict
, kSCPropNetDNSSupplementalMatchDomains
, array
);
18642 ForgetCF( &array
);
18644 // Add interface, if any.
18646 if( gDNSConfigAdd_Interface
)
18648 err
= CFDictionarySetCString( dict
, kSCPropInterfaceName
, gDNSConfigAdd_Interface
, kSizeCString
);
18649 require_noerr( err
, exit
);
18651 CFDictionarySetValue( dict
, kSCPropNetDNSConfirmedServiceID
, gDNSConfigAdd_ID
);
18654 // Set dictionary in dynamic store.
18656 store
= SCDynamicStoreCreate( NULL
, CFSTR( kDNSSDUtilIdentifier
), NULL
, NULL
);
18657 err
= map_scerror( store
);
18658 require_noerr( err
, exit
);
18660 key
= SCDynamicStoreKeyCreateNetworkServiceEntity( NULL
, kSCDynamicStoreDomainState
, gDNSConfigAdd_ID
, kSCEntNetDNS
);
18661 require_action( key
, exit
, err
= kUnknownErr
);
18663 success
= SCDynamicStoreSetValue( store
, key
, dict
);
18664 require_action( success
, exit
, err
= kUnknownErr
);
18667 CFReleaseNullSafe( dict
);
18668 CFReleaseNullSafe( array
);
18669 CFReleaseNullSafe( store
);
18670 CFReleaseNullSafe( key
);
18671 gExitCode
= err
? 1 : 0;
18674 //===========================================================================================================================
18675 // DNSConfigRemoveCmd
18676 //===========================================================================================================================
18678 static void DNSConfigRemoveCmd( void )
18681 SCDynamicStoreRef store
= NULL
;
18682 CFStringRef key
= NULL
;
18685 store
= SCDynamicStoreCreate( NULL
, CFSTR( kDNSSDUtilIdentifier
), NULL
, NULL
);
18686 err
= map_scerror( store
);
18687 require_noerr( err
, exit
);
18689 key
= SCDynamicStoreKeyCreateNetworkServiceEntity( NULL
, kSCDynamicStoreDomainState
, gDNSConfigRemove_ID
, kSCEntNetDNS
);
18690 require_action( key
, exit
, err
= kUnknownErr
);
18692 success
= SCDynamicStoreRemoveValue( store
, key
);
18693 require_action( success
, exit
, err
= kUnknownErr
);
18696 CFReleaseNullSafe( store
);
18697 CFReleaseNullSafe( key
);
18698 gExitCode
= err
? 1 : 0;
18701 //===========================================================================================================================
18703 //===========================================================================================================================
18705 static OSStatus
_XPCDictionaryCreateFromString( const char *inString
, xpc_object_t
*outDict
);
18707 static void XPCSendCmd( void )
18710 xpc_object_t msg
, reply
;
18712 err
= _XPCDictionaryCreateFromString( gXPCSend_MessageStr
, &msg
);
18713 require_noerr_quiet( err
, exit
);
18715 FPrintF( stdout
, "Service: %s\n", gXPCSend_ServiceName
);
18716 FPrintF( stdout
, "Message: %s\n", gXPCSend_MessageStr
);
18717 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
18718 FPrintF( stdout
, "---\n" );
18719 FPrintF( stdout
, "XPC Message:\n%{xpc}\n", msg
);
18721 err
= xpc_send_message_sync( gXPCSend_ServiceName
, 0, 0, msg
, &reply
);
18722 xpc_forget( &msg
);
18723 require_noerr_quiet( err
, exit
);
18725 FPrintF( stdout
, "XPC Reply:\n%{xpc}\n", reply
);
18726 FPrintF( stdout
, "---\n" );
18727 FPrintF( stdout
, "End time: %{du:time}\n", NULL
);
18728 xpc_forget( &reply
);
18731 if( err
) ErrQuit( 1, "error: %#m\n", err
);
18734 //===========================================================================================================================
18735 // _XPCDictionaryCreateFromString
18736 //===========================================================================================================================
18738 #define kXPCObjectPrefix_Bool "bool:"
18739 #define kXPCObjectPrefix_Data "data:"
18740 #define kXPCObjectPrefix_Int64 "int:"
18741 #define kXPCObjectPrefix_String "string:"
18742 #define kXPCObjectPrefix_UInt64 "uint:"
18743 #define kXPCObjectPrefix_UUID "uuid:"
18745 typedef struct XPCListItem XPCListItem
;
18748 XPCListItem
* next
;
18753 static OSStatus
_XPCListItemCreate( xpc_object_t inObject
, const char *inKey
, XPCListItem
**outItem
);
18754 static void _XPCListItemFree( XPCListItem
*inItem
);
18755 static void _XPCListFree( XPCListItem
*inList
);
18757 static OSStatus
_XPCObjectFromString( const char *inString
, xpc_object_t
*outObject
);
18759 static OSStatus
_XPCDictionaryCreateFromString( const char *inString
, xpc_object_t
*outDict
)
18762 xpc_object_t container
;
18763 const char * ptr
= inString
;
18764 const char * const end
= inString
+ strlen( inString
);
18765 XPCListItem
* list
= NULL
;
18767 container
= xpc_dictionary_create( NULL
, NULL
, 0 );
18768 require_action( container
, exit
, err
= kNoMemoryErr
);
18772 xpc_type_t containerType
;
18773 xpc_object_t value
;
18775 char keyStr
[ 256 ];
18776 char valStr
[ 256 ];
18778 // At this point, zero or more of the current container's elements have been parsed.
18779 // Skip the white space leading up to the container's next element, if any, or the container's end.
18781 while( isspace_safe( *ptr
) ) ++ptr
;
18783 // Check if we're done with the current container.
18786 if( c
== '\0' ) break;
18788 containerType
= xpc_get_type( container
);
18789 if( ( ( containerType
== XPC_TYPE_DICTIONARY
) && ( c
== '}' ) ) ||
18790 ( ( containerType
== XPC_TYPE_ARRAY
) && ( c
== ']' ) ) )
18792 XPCListItem
* item
;
18795 require_action_quiet( item
, exit
, err
= kMalformedErr
);
18799 // Add the current container to its parent container.
18803 xpc_dictionary_set_value( item
->obj
, item
->key
, container
);
18807 xpc_array_append_value( item
->obj
, container
);
18810 // Continue with the parent container.
18812 xpc_release( container
);
18813 container
= xpc_retain( item
->obj
);
18815 _XPCListItemFree( item
);
18819 // If the current container is a dictionary, parse the key string.
18821 if( containerType
== XPC_TYPE_DICTIONARY
)
18823 err
= _ParseEscapedString( ptr
, end
, "={}[]" kWhiteSpaceCharSet
, keyStr
, sizeof( keyStr
), NULL
, NULL
, &ptr
);
18824 require_noerr_quiet( err
, exit
);
18827 require_action_quiet( c
== '=', exit
, err
= kMalformedErr
);
18831 // Check if the value is a dictionary ({...}) or an array ([...]).
18834 if( ( c
== '{' ) || ( c
== '[' ) )
18836 XPCListItem
* item
;
18840 // Save the current container.
18842 err
= _XPCListItemCreate( container
, ( containerType
== XPC_TYPE_DICTIONARY
) ? keyStr
: NULL
, &item
);
18843 require_noerr( err
, exit
);
18849 // Create and continue with the child container.
18851 xpc_release( container
);
18854 container
= xpc_dictionary_create( NULL
, NULL
, 0 );
18855 require_action( container
, exit
, err
= kNoMemoryErr
);
18859 container
= xpc_array_create( NULL
, 0 );
18860 require_action( container
, exit
, err
= kNoMemoryErr
);
18865 // Parse the value string.
18867 err
= _ParseEscapedString( ptr
, end
, "{}[]" kWhiteSpaceCharSet
, valStr
, sizeof( valStr
), NULL
, NULL
, &ptr
);
18868 require_noerr_quiet( err
, exit
);
18870 err
= _XPCObjectFromString( valStr
, &value
);
18871 require_noerr_quiet( err
, exit
);
18873 if( containerType
== XPC_TYPE_DICTIONARY
)
18875 xpc_dictionary_set_value( container
, keyStr
, value
);
18879 xpc_array_append_value( container
, value
);
18881 xpc_forget( &value
);
18883 require_action_quiet( !list
, exit
, err
= kMalformedErr
);
18885 check( container
);
18886 check( xpc_get_type( container
) == XPC_TYPE_DICTIONARY
);
18888 *outDict
= container
;
18893 xpc_release_null_safe( container
);
18894 if( list
) _XPCListFree( list
);
18898 static OSStatus
_XPCObjectFromString( const char *inString
, xpc_object_t
*outObject
)
18901 xpc_object_t object
;
18907 else if( stricmp_prefix( inString
, kXPCObjectPrefix_Bool
) == 0 )
18909 const char * const str
= inString
+ sizeof_string( kXPCObjectPrefix_Bool
);
18912 if( IsTrueString( str
, kSizeCString
) )
18916 else if( IsFalseString( str
, kSizeCString
) )
18926 object
= xpc_bool_create( value
);
18927 require_action( object
, exit
, err
= kNoMemoryErr
);
18932 else if( stricmp_prefix( inString
, kXPCObjectPrefix_Data
) == 0 )
18934 const char * const str
= inString
+ sizeof_string( kXPCObjectPrefix_Data
);
18938 err
= HexToDataCopy( str
, kSizeCString
, kHexToData_DefaultFlags
, &dataPtr
, &dataLen
, NULL
);
18939 require_noerr( err
, exit
);
18941 object
= xpc_data_create( dataPtr
, dataLen
);
18943 require_action( object
, exit
, err
= kNoMemoryErr
);
18948 else if( stricmp_prefix( inString
, kXPCObjectPrefix_Int64
) == 0 )
18950 const char * const str
= inString
+ sizeof_string( kXPCObjectPrefix_Int64
);
18953 i64
= _StringToInt64( str
, &err
);
18954 require_noerr_quiet( err
, exit
);
18956 object
= xpc_int64_create( i64
);
18957 require_action( object
, exit
, err
= kNoMemoryErr
);
18962 else if( stricmp_prefix( inString
, kXPCObjectPrefix_String
) == 0 )
18964 const char * const str
= inString
+ sizeof_string( kXPCObjectPrefix_String
);
18966 object
= xpc_string_create( str
);
18967 require_action( object
, exit
, err
= kNoMemoryErr
);
18972 else if( stricmp_prefix( inString
, kXPCObjectPrefix_UInt64
) == 0 )
18974 const char * const str
= inString
+ sizeof_string( kXPCObjectPrefix_UInt64
);
18977 u64
= _StringToUInt64( str
, &err
);
18978 require_noerr_quiet( err
, exit
);
18980 object
= xpc_uint64_create( u64
);
18981 require_action( object
, exit
, err
= kNoMemoryErr
);
18986 else if( stricmp_prefix( inString
, kXPCObjectPrefix_UUID
) == 0 )
18988 const char * const str
= inString
+ sizeof_string( kXPCObjectPrefix_UUID
);
18991 err
= uuid_parse( str
, uuid
);
18992 require_noerr_action_quiet( err
, exit
, err
= kValueErr
);
18994 object
= xpc_uuid_create( uuid
);
18995 require_action( object
, exit
, err
= kNoMemoryErr
);
18998 // Unsupported prefix
19006 *outObject
= object
;
19013 static OSStatus
_XPCListItemCreate( xpc_object_t inObject
, const char *inKey
, XPCListItem
**outItem
)
19016 XPCListItem
* item
;
19018 item
= (XPCListItem
*) calloc( 1, sizeof( *item
) );
19019 require_action( item
, exit
, err
= kNoMemoryErr
);
19021 item
->obj
= xpc_retain( inObject
);
19022 if( ( xpc_get_type( item
->obj
) == XPC_TYPE_DICTIONARY
) && inKey
)
19024 item
->key
= strdup( inKey
);
19025 require_action( item
->key
, exit
, err
= kNoMemoryErr
);
19033 if( item
) _XPCListItemFree( item
);
19037 static void _XPCListItemFree( XPCListItem
*inItem
)
19039 xpc_forget( &inItem
->obj
);
19040 ForgetMem( &inItem
->key
);
19044 static void _XPCListFree( XPCListItem
*inList
)
19046 XPCListItem
* item
;
19048 while( ( item
= inList
) != NULL
)
19050 inList
= item
->next
;
19051 _XPCListItemFree( item
);
19054 #endif // TARGET_OS_DARWIN
19056 #if( MDNSRESPONDER_PROJECT )
19057 //===========================================================================================================================
19058 // InterfaceMonitorCmd
19059 //===========================================================================================================================
19061 static void _InterfaceMonitorPrint( mdns_interface_monitor_t inMonitor
);
19062 static void _InterfaceMonitorSignalHandler( void *inContext
);
19064 static void InterfaceMonitorCmd( void )
19067 mdns_interface_monitor_t monitor
;
19068 dispatch_source_t signalSource
= NULL
;
19070 __block
int exitCode
;
19072 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
19073 require_noerr_quiet( err
, exit
);
19075 monitor
= mdns_interface_monitor_create( ifIndex
);
19076 require_action( monitor
, exit
, err
= kNoResourcesErr
);
19079 mdns_interface_monitor_set_queue( monitor
, dispatch_get_main_queue() );
19080 mdns_interface_monitor_set_event_handler( monitor
,
19081 ^( mdns_event_t inEvent
, OSStatus inError
)
19085 case mdns_event_error
:
19086 FPrintF( stderr
, "error: Interface monitor failed: %#m\n", inError
);
19087 mdns_interface_monitor_invalidate( monitor
);
19091 case mdns_event_invalidated
:
19092 FPrintF( stdout
, "Interface monitor invalidated.\n" );
19093 mdns_release( monitor
);
19097 FPrintF( stdout
, "Unhandled event '%s' (%ld)\n", mdns_event_to_string( inEvent
), (long) inEvent
);
19101 mdns_interface_monitor_set_update_handler( monitor
,
19102 ^( __unused mdns_interface_flags_t inChangeFlags
)
19104 _InterfaceMonitorPrint( monitor
);
19107 _InterfaceMonitorPrint( monitor
);
19108 mdns_interface_monitor_activate( monitor
);
19110 signal( SIGINT
, SIG_IGN
);
19111 err
= DispatchSignalSourceCreate( SIGINT
, dispatch_get_main_queue(), _InterfaceMonitorSignalHandler
, monitor
,
19113 require_noerr( err
, exit
);
19114 dispatch_resume( signalSource
);
19119 if( err
) ErrQuit( 1, "error: %#m\n", err
);
19122 static void _InterfaceMonitorPrint( mdns_interface_monitor_t inMonitor
)
19124 FPrintF( stdout
, "%{du:time} %@\n", NULL
, inMonitor
);
19127 static void _InterfaceMonitorSignalHandler( void *inContext
)
19129 mdns_interface_monitor_invalidate( (mdns_interface_monitor_t
) inContext
);
19132 //===========================================================================================================================
19134 //===========================================================================================================================
19136 static void _DNSProxyCallback( DNSXConnRef inConnection
, DNSXErrorType inError
);
19137 static void _DNSProxyCmdSignalHandler( void *inContext
);
19139 static void DNSProxyCmd( void )
19143 DNSXConnRef connection
;
19144 IfIndex inputIfIndexes
[ MaxInputIf
];
19145 dispatch_source_t sigIntSource
= NULL
;
19146 dispatch_source_t sigTermSource
= NULL
;
19147 uint32_t outputIfIndex
;
19148 char ifName
[ kInterfaceNameBufLen
];
19150 if( gDNSProxy_InputInterfaceCount
> MaxInputIf
)
19152 FPrintF( stderr
, "error: Invalid input interface count: %zu > %d (max).\n",
19153 gDNSProxy_InputInterfaceCount
, MaxInputIf
);
19158 for( i
= 0; i
< gDNSProxy_InputInterfaceCount
; ++i
)
19162 err
= InterfaceIndexFromArgString( gDNSProxy_InputInterfaces
[ i
], &ifIndex
);
19163 require_noerr_quiet( err
, exit
);
19165 inputIfIndexes
[ i
] = ifIndex
;
19167 while( i
< MaxInputIf
) inputIfIndexes
[ i
++ ] = 0; // Remaining interface indexes are required to be 0.
19169 if( gDNSProxy_OutputInterface
)
19171 err
= InterfaceIndexFromArgString( gDNSProxy_OutputInterface
, &outputIfIndex
);
19172 require_noerr_quiet( err
, exit
);
19176 outputIfIndex
= kDNSIfindexAny
;
19179 FPrintF( stdout
, "Input Interfaces:" );
19180 for( i
= 0; i
< gDNSProxy_InputInterfaceCount
; ++i
)
19182 const uint32_t ifIndex
= (uint32_t) inputIfIndexes
[ i
];
19184 FPrintF( stdout
, "%s %u (%s)", ( i
== 0 ) ? "" : ",", ifIndex
, InterfaceIndexToName( ifIndex
, ifName
) );
19186 FPrintF( stdout
, "\n" );
19187 FPrintF( stdout
, "Output Interface: %u (%s)\n", outputIfIndex
, InterfaceIndexToName( outputIfIndex
, ifName
) );
19188 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
19189 FPrintF( stdout
, "---\n" );
19192 err
= DNSXEnableProxy( &connection
, kDNSProxyEnable
, inputIfIndexes
, outputIfIndex
, dispatch_get_main_queue(),
19193 _DNSProxyCallback
);
19194 require_noerr_quiet( err
, exit
);
19196 signal( SIGINT
, SIG_IGN
);
19197 err
= DispatchSignalSourceCreate( SIGINT
, dispatch_get_main_queue(), _DNSProxyCmdSignalHandler
, connection
,
19199 require_noerr( err
, exit
);
19200 dispatch_activate( sigIntSource
);
19202 signal( SIGTERM
, SIG_IGN
);
19203 err
= DispatchSignalSourceCreate( SIGTERM
, dispatch_get_main_queue(), _DNSProxyCmdSignalHandler
, connection
,
19205 require_noerr( err
, exit
);
19206 dispatch_activate( sigTermSource
);
19211 if( err
) ErrQuit( 1, "error: %#m\n", err
);
19214 static void _DNSProxyCallback( DNSXConnRef inConnection
, DNSXErrorType inError
)
19216 Unused( inConnection
);
19218 if( inError
) ErrQuit( 1, "error: DNS proxy failed: %#m\n", inError
);
19221 static void _DNSProxyCmdSignalHandler( void *inContext
)
19223 DNSXConnRef
const connection
= (DNSXConnRef
) inContext
;
19224 struct timeval now
;
19226 gettimeofday( &now
, NULL
);
19228 DNSXRefDeAlloc( connection
);
19230 FPrintF( stdout
, "---\n" );
19231 FPrintF( stdout
, "End time: %{du:time}\n", &now
);
19235 //===========================================================================================================================
19237 //===========================================================================================================================
19239 static void XCTestCmd( void )
19242 setenv(DNSSDUTIL_XCTEST
, DNSSDUTIL_XCTEST
, 0);
19243 if(!TestUtilsRunXCTestNamed(gXCTest_Classname
)) {
19246 unsetenv(DNSSDUTIL_XCTEST
);
19250 #endif // MDNSRESPONDER_PROJECT
19252 //===========================================================================================================================
19253 // DaemonVersionCmd
19254 //===========================================================================================================================
19256 static void DaemonVersionCmd( void )
19259 uint32_t size
, version
;
19262 size
= (uint32_t) sizeof( version
);
19263 err
= DNSServiceGetProperty( kDNSServiceProperty_DaemonVersion
, &version
, &size
);
19264 require_noerr( err
, exit
);
19266 FPrintF( stdout
, "Daemon version: %s\n", SourceVersionToCString( version
, strBuf
) );
19269 if( err
) exit( 1 );
19272 //===========================================================================================================================
19274 //===========================================================================================================================
19276 static void Exit( void *inContext
)
19278 const char * const reason
= (const char *) inContext
;
19280 FPrintF( stdout
, "---\n" );
19281 FPrintF( stdout
, "End time: %{du:time}\n", NULL
);
19282 if( reason
) FPrintF( stdout
, "End reason: %s\n", reason
);
19286 //===========================================================================================================================
19287 // _PrintFExtensionTimestampHandler
19288 //===========================================================================================================================
19291 _PrintFExtensionTimestampHandler(
19292 PrintFContext
* inContext
,
19293 PrintFFormat
* inFormat
,
19294 PrintFVAList
* inArgs
,
19295 void * inUserContext
)
19297 struct timeval now
;
19298 const struct timeval
* tv
;
19299 struct tm
* localTime
;
19302 char dateTimeStr
[ 32 ];
19304 Unused( inUserContext
);
19306 tv
= va_arg( inArgs
->args
, const struct timeval
* );
19307 require_action_quiet( !inFormat
->suppress
, exit
, n
= 0 );
19311 gettimeofday( &now
, NULL
);
19314 localTime
= localtime( &tv
->tv_sec
);
19315 len
= strftime( dateTimeStr
, sizeof( dateTimeStr
), "%Y-%m-%d %H:%M:%S", localTime
);
19316 if( len
== 0 ) dateTimeStr
[ 0 ] = '\0';
19318 n
= PrintFCore( inContext
, "%s.%06u", dateTimeStr
, (unsigned int) tv
->tv_usec
);
19324 //===========================================================================================================================
19325 // _PrintFExtensionDNSMessageHandler
19326 //===========================================================================================================================
19329 _PrintFExtensionDNSMessageHandler(
19330 PrintFContext
* inContext
,
19331 PrintFFormat
* inFormat
,
19332 PrintFVAList
* inArgs
,
19333 void * inUserContext
)
19336 const void * msgPtr
;
19341 Boolean printRawRData
;
19343 Unused( inUserContext
);
19345 msgPtr
= va_arg( inArgs
->args
, const void * );
19346 msgLen
= va_arg( inArgs
->args
, size_t );
19347 require_action_quiet( !inFormat
->suppress
, exit
, n
= 0 );
19349 isMDNS
= ( inFormat
->altForm
> 0 ) ? true : false;
19350 printRawRData
= ( inFormat
->precision
> 0 ) ? true : false;
19351 err
= DNSMessageToText( msgPtr
, msgLen
, isMDNS
, printRawRData
, &text
);
19354 n
= PrintFCore( inContext
, "%*{text}", inFormat
->fieldWidth
, text
, kSizeCString
);
19359 n
= PrintFCore( inContext
, "%*.1H", inFormat
->fieldWidth
, msgPtr
, (int) msgLen
, (int) msgLen
);
19366 //===========================================================================================================================
19367 // _PrintFExtensionCallbackFlagsHandler
19368 //===========================================================================================================================
19371 _PrintFExtensionCallbackFlagsHandler(
19372 PrintFContext
* inContext
,
19373 PrintFFormat
* inFormat
,
19374 PrintFVAList
* inArgs
,
19375 void * inUserContext
)
19377 DNSServiceFlags flags
;
19380 Unused( inUserContext
);
19382 flags
= va_arg( inArgs
->args
, DNSServiceFlags
);
19383 require_action_quiet( !inFormat
->suppress
, exit
, n
= 0 );
19385 n
= PrintFCore( inContext
, "%08X %s%c %c%c",
19386 flags
, DNSServiceFlagsToAddRmvStr( flags
),
19387 ( flags
& kDNSServiceFlagsMoreComing
) ? '+' : ' ',
19388 ( flags
& kDNSServiceFlagAnsweredFromCache
) ? 'C' : ' ',
19389 ( flags
& kDNSServiceFlagsExpiredAnswer
) ? '*' : ' ' );
19395 //===========================================================================================================================
19396 // _PrintFExtensionDNSRecordDataHandler
19397 //===========================================================================================================================
19400 _PrintFExtensionDNSRecordDataHandler(
19401 PrintFContext
* inContext
,
19402 PrintFFormat
* inFormat
,
19403 PrintFVAList
* inArgs
,
19404 void * inUserContext
)
19406 const void * rdataPtr
;
19407 unsigned int rdataLen
, rdataType
;
19410 Unused( inUserContext
);
19412 rdataType
= va_arg( inArgs
->args
, unsigned int );
19413 rdataPtr
= va_arg( inArgs
->args
, const void * );
19414 rdataLen
= va_arg( inArgs
->args
, unsigned int );
19415 require_action_quiet( !inFormat
->suppress
, exit
, n
= 0 );
19417 check( inFormat
->fieldWidth
< INT_MAX
);
19418 fieldWidth
= inFormat
->leftJustify
? -( (int) inFormat
->fieldWidth
) : ( (int) inFormat
->fieldWidth
);
19422 char * rdataStr
= NULL
;
19424 DNSRecordDataToString( rdataPtr
, rdataLen
, rdataType
, NULL
, 0, &rdataStr
);
19427 n
= PrintFCore( inContext
, "%*s", fieldWidth
, rdataStr
);
19432 n
= PrintFCore( inContext
, "%*H", fieldWidth
, rdataPtr
, rdataLen
, rdataLen
);
19437 n
= PrintFCore( inContext
, "%*s", fieldWidth
, "<< ZERO-LENGTH RDATA >>" );
19444 //===========================================================================================================================
19445 // GetDNSSDFlagsFromOpts
19446 //===========================================================================================================================
19448 static DNSServiceFlags
GetDNSSDFlagsFromOpts( void )
19450 DNSServiceFlags flags
;
19452 flags
= (DNSServiceFlags
) gDNSSDFlags
;
19453 if( flags
& kDNSServiceFlagsShareConnection
)
19455 FPrintF( stderr
, "*** Warning: kDNSServiceFlagsShareConnection (0x%X) is explicitly set in flag parameters.\n",
19456 kDNSServiceFlagsShareConnection
);
19459 if( gDNSSDFlag_AllowExpiredAnswers
) flags
|= kDNSServiceFlagsAllowExpiredAnswers
;
19460 if( gDNSSDFlag_BrowseDomains
) flags
|= kDNSServiceFlagsBrowseDomains
;
19461 if( gDNSSDFlag_DenyCellular
) flags
|= kDNSServiceFlagsDenyCellular
;
19462 if( gDNSSDFlag_DenyConstrained
) flags
|= kDNSServiceFlagsDenyConstrained
;
19463 if( gDNSSDFlag_DenyExpensive
) flags
|= kDNSServiceFlagsDenyExpensive
;
19464 if( gDNSSDFlag_ForceMulticast
) flags
|= kDNSServiceFlagsForceMulticast
;
19465 if( gDNSSDFlag_IncludeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
19466 if( gDNSSDFlag_KnownUnique
) flags
|= kDNSServiceFlagsKnownUnique
;
19467 if( gDNSSDFlag_NoAutoRename
) flags
|= kDNSServiceFlagsNoAutoRename
;
19468 if( gDNSSDFlag_PathEvaluationDone
) flags
|= kDNSServiceFlagsPathEvaluationDone
;
19469 if( gDNSSDFlag_RegistrationDomains
) flags
|= kDNSServiceFlagsRegistrationDomains
;
19470 if( gDNSSDFlag_ReturnIntermediates
) flags
|= kDNSServiceFlagsReturnIntermediates
;
19471 if( gDNSSDFlag_Shared
) flags
|= kDNSServiceFlagsShared
;
19472 if( gDNSSDFlag_SuppressUnusable
) flags
|= kDNSServiceFlagsSuppressUnusable
;
19473 if( gDNSSDFlag_Timeout
) flags
|= kDNSServiceFlagsTimeout
;
19474 if( gDNSSDFlag_UnicastResponse
) flags
|= kDNSServiceFlagsUnicastResponse
;
19475 if( gDNSSDFlag_Unique
) flags
|= kDNSServiceFlagsUnique
;
19476 if( gDNSSDFlag_WakeOnResolve
) flags
|= kDNSServiceFlagsWakeOnResolve
;
19481 //===========================================================================================================================
19482 // CreateConnectionFromArgString
19483 //===========================================================================================================================
19486 CreateConnectionFromArgString(
19487 const char * inString
,
19488 dispatch_queue_t inQueue
,
19489 DNSServiceRef
* outSDRef
,
19490 ConnectionDesc
* outDesc
)
19493 DNSServiceRef sdRef
= NULL
;
19494 ConnectionType type
;
19495 int32_t pid
= -1; // Initializing because the analyzer claims pid may be used uninitialized.
19496 uint8_t uuid
[ 16 ];
19498 if( strcasecmp( inString
, kConnectionArg_Normal
) == 0 )
19500 err
= DNSServiceCreateConnection( &sdRef
);
19501 require_noerr( err
, exit
);
19502 type
= kConnectionType_Normal
;
19504 else if( stricmp_prefix( inString
, kConnectionArgPrefix_PID
) == 0 )
19506 const char * const pidStr
= inString
+ sizeof_string( kConnectionArgPrefix_PID
);
19508 err
= StringToInt32( pidStr
, &pid
);
19511 FPrintF( stderr
, "Invalid delegate connection PID value: %s\n", pidStr
);
19516 memset( uuid
, 0, sizeof( uuid
) );
19517 err
= DNSServiceCreateDelegateConnection( &sdRef
, pid
, uuid
);
19520 FPrintF( stderr
, "DNSServiceCreateDelegateConnection() returned %#m for PID %d\n", err
, pid
);
19523 type
= kConnectionType_DelegatePID
;
19525 else if( stricmp_prefix( inString
, kConnectionArgPrefix_UUID
) == 0 )
19527 const char * const uuidStr
= inString
+ sizeof_string( kConnectionArgPrefix_UUID
);
19529 check_compile_time_code( sizeof( uuid
) == sizeof( uuid_t
) );
19531 err
= StringToUUID( uuidStr
, kSizeCString
, false, uuid
);
19534 FPrintF( stderr
, "Invalid delegate connection UUID value: %s\n", uuidStr
);
19539 err
= DNSServiceCreateDelegateConnection( &sdRef
, 0, uuid
);
19542 FPrintF( stderr
, "DNSServiceCreateDelegateConnection() returned %#m for UUID %#U\n", err
, uuid
);
19545 type
= kConnectionType_DelegateUUID
;
19549 FPrintF( stderr
, "Unrecognized connection string \"%s\".\n", inString
);
19554 err
= DNSServiceSetDispatchQueue( sdRef
, inQueue
);
19555 require_noerr( err
, exit
);
19560 outDesc
->type
= type
;
19561 if( type
== kConnectionType_DelegatePID
) outDesc
->delegate
.pid
= pid
;
19562 else if( type
== kConnectionType_DelegateUUID
) memcpy( outDesc
->delegate
.uuid
, uuid
, 16 );
19567 if( sdRef
) DNSServiceRefDeallocate( sdRef
);
19571 //===========================================================================================================================
19572 // InterfaceIndexFromArgString
19573 //===========================================================================================================================
19575 static OSStatus
InterfaceIndexFromArgString( const char *inString
, uint32_t *outIndex
)
19582 ifIndex
= if_nametoindex( inString
);
19585 err
= StringToUInt32( inString
, &ifIndex
);
19588 FPrintF( stderr
, "error: Invalid interface value: %s\n", inString
);
19599 *outIndex
= ifIndex
;
19606 //===========================================================================================================================
19607 // RecordDataFromArgString
19608 //===========================================================================================================================
19610 static OSStatus
RecordDataFromArgString( const char *inString
, uint8_t **outDataPtr
, size_t *outDataLen
)
19613 uint8_t * dataPtr
= NULL
;
19620 else if( stricmp_prefix( inString
, kRDataArgPrefix_Domain
) == 0 )
19622 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_Domain
);
19624 err
= StringToDomainName( str
, &dataPtr
, &dataLen
);
19625 require_noerr_quiet( err
, exit
);
19630 else if( stricmp_prefix( inString
, kRDataArgPrefix_File
) == 0 )
19632 const char * const path
= inString
+ sizeof_string( kRDataArgPrefix_File
);
19634 err
= CopyFileDataByPath( path
, (char **) &dataPtr
, &dataLen
);
19635 require_noerr( err
, exit
);
19636 require_action( dataLen
<= kDNSRecordDataLengthMax
, exit
, err
= kSizeErr
);
19639 // Hexadecimal string
19641 else if( stricmp_prefix( inString
, kRDataArgPrefix_HexString
) == 0 )
19643 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_HexString
);
19645 err
= HexToDataCopy( str
, kSizeCString
, kHexToData_DefaultFlags
, &dataPtr
, &dataLen
, NULL
);
19646 require_noerr( err
, exit
);
19647 require_action( dataLen
<= kDNSRecordDataLengthMax
, exit
, err
= kSizeErr
);
19650 // IPv4 address string
19652 else if( stricmp_prefix( inString
, kRDataArgPrefix_IPv4
) == 0 )
19654 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_IPv4
);
19656 err
= StringToARecordData( str
, &dataPtr
, &dataLen
);
19657 require_noerr_quiet( err
, exit
);
19660 // IPv6 address string
19662 else if( stricmp_prefix( inString
, kRDataArgPrefix_IPv6
) == 0 )
19664 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_IPv6
);
19666 err
= StringToAAAARecordData( str
, &dataPtr
, &dataLen
);
19667 require_noerr_quiet( err
, exit
);
19672 else if( stricmp_prefix( inString
, kRDataArgPrefix_SRV
) == 0 )
19674 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_SRV
);
19676 err
= CreateSRVRecordDataFromString( str
, &dataPtr
, &dataLen
);
19677 require_noerr( err
, exit
);
19680 // String with escaped hex and octal bytes
19682 else if( stricmp_prefix( inString
, kRDataArgPrefix_String
) == 0 )
19684 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_String
);
19685 const char * const end
= str
+ strlen( str
);
19692 success
= _ParseQuotedEscapedString( str
, end
, "", NULL
, 0, NULL
, &totalLen
, NULL
);
19693 require_action( success
, exit
, err
= kParamErr
);
19694 require_action( totalLen
<= kDNSRecordDataLengthMax
, exit
, err
= kSizeErr
);
19696 dataLen
= totalLen
;
19697 dataPtr
= (uint8_t *) malloc( dataLen
);
19698 require_action( dataPtr
, exit
, err
= kNoMemoryErr
);
19700 success
= _ParseQuotedEscapedString( str
, end
, "", (char *) dataPtr
, dataLen
, &copiedLen
, NULL
, NULL
);
19701 require_action( success
, exit
, err
= kParamErr
);
19702 check( copiedLen
== dataLen
);
19713 else if( stricmp_prefix( inString
, kRDataArgPrefix_TXT
) == 0 )
19715 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_TXT
);
19717 err
= CreateTXTRecordDataFromString( str
, ',', &dataPtr
, &dataLen
);
19718 require_noerr( err
, exit
);
19721 // Unrecognized format
19725 FPrintF( stderr
, "Unrecognized record data string \"%s\".\n", inString
);
19731 *outDataLen
= dataLen
;
19732 *outDataPtr
= dataPtr
;
19736 FreeNullSafe( dataPtr
);
19740 //===========================================================================================================================
19741 // RecordTypeFromArgString
19742 //===========================================================================================================================
19746 uint16_t value
; // Record type's numeric value.
19747 const char * name
; // Record type's name as a string (e.g., "A", "PTR", "SRV").
19751 static const RecordType kRecordTypes
[] =
19755 { kDNSServiceType_A
, "A" },
19756 { kDNSServiceType_AAAA
, "AAAA" },
19757 { kDNSServiceType_PTR
, "PTR" },
19758 { kDNSServiceType_SRV
, "SRV" },
19759 { kDNSServiceType_TXT
, "TXT" },
19760 { kDNSServiceType_CNAME
, "CNAME" },
19761 { kDNSServiceType_SOA
, "SOA" },
19762 { kDNSServiceType_NSEC
, "NSEC" },
19763 { kDNSServiceType_NS
, "NS" },
19764 { kDNSServiceType_MX
, "MX" },
19765 { kDNSServiceType_ANY
, "ANY" },
19766 { kDNSServiceType_OPT
, "OPT" },
19768 // Less common types.
19770 { kDNSServiceType_MD
, "MD" },
19771 { kDNSServiceType_NS
, "NS" },
19772 { kDNSServiceType_MD
, "MD" },
19773 { kDNSServiceType_MF
, "MF" },
19774 { kDNSServiceType_MB
, "MB" },
19775 { kDNSServiceType_MG
, "MG" },
19776 { kDNSServiceType_MR
, "MR" },
19777 { kDNSServiceType_NULL
, "NULL" },
19778 { kDNSServiceType_WKS
, "WKS" },
19779 { kDNSServiceType_HINFO
, "HINFO" },
19780 { kDNSServiceType_MINFO
, "MINFO" },
19781 { kDNSServiceType_RP
, "RP" },
19782 { kDNSServiceType_AFSDB
, "AFSDB" },
19783 { kDNSServiceType_X25
, "X25" },
19784 { kDNSServiceType_ISDN
, "ISDN" },
19785 { kDNSServiceType_RT
, "RT" },
19786 { kDNSServiceType_NSAP
, "NSAP" },
19787 { kDNSServiceType_NSAP_PTR
, "NSAP_PTR" },
19788 { kDNSServiceType_SIG
, "SIG" },
19789 { kDNSServiceType_KEY
, "KEY" },
19790 { kDNSServiceType_PX
, "PX" },
19791 { kDNSServiceType_GPOS
, "GPOS" },
19792 { kDNSServiceType_LOC
, "LOC" },
19793 { kDNSServiceType_NXT
, "NXT" },
19794 { kDNSServiceType_EID
, "EID" },
19795 { kDNSServiceType_NIMLOC
, "NIMLOC" },
19796 { kDNSServiceType_ATMA
, "ATMA" },
19797 { kDNSServiceType_NAPTR
, "NAPTR" },
19798 { kDNSServiceType_KX
, "KX" },
19799 { kDNSServiceType_CERT
, "CERT" },
19800 { kDNSServiceType_A6
, "A6" },
19801 { kDNSServiceType_DNAME
, "DNAME" },
19802 { kDNSServiceType_SINK
, "SINK" },
19803 { kDNSServiceType_APL
, "APL" },
19804 { kDNSServiceType_DS
, "DS" },
19805 { kDNSServiceType_SSHFP
, "SSHFP" },
19806 { kDNSServiceType_IPSECKEY
, "IPSECKEY" },
19807 { kDNSServiceType_RRSIG
, "RRSIG" },
19808 { kDNSServiceType_DNSKEY
, "DNSKEY" },
19809 { kDNSServiceType_DHCID
, "DHCID" },
19810 { kDNSServiceType_NSEC3
, "NSEC3" },
19811 { kDNSServiceType_NSEC3PARAM
, "NSEC3PARAM" },
19812 { kDNSServiceType_HIP
, "HIP" },
19813 { kDNSServiceType_SPF
, "SPF" },
19814 { kDNSServiceType_UINFO
, "UINFO" },
19815 { kDNSServiceType_UID
, "UID" },
19816 { kDNSServiceType_GID
, "GID" },
19817 { kDNSServiceType_UNSPEC
, "UNSPEC" },
19818 { kDNSServiceType_TKEY
, "TKEY" },
19819 { kDNSServiceType_TSIG
, "TSIG" },
19820 { kDNSServiceType_IXFR
, "IXFR" },
19821 { kDNSServiceType_AXFR
, "AXFR" },
19822 { kDNSServiceType_MAILB
, "MAILB" },
19823 { kDNSServiceType_MAILA
, "MAILA" }
19826 static OSStatus
RecordTypeFromArgString( const char *inString
, uint16_t *outValue
)
19830 const RecordType
* type
;
19831 const RecordType
* const end
= kRecordTypes
+ countof( kRecordTypes
);
19833 for( type
= kRecordTypes
; type
< end
; ++type
)
19835 if( strcasecmp( type
->name
, inString
) == 0 )
19837 *outValue
= type
->value
;
19842 err
= StringToInt32( inString
, &i32
);
19843 require_noerr_quiet( err
, exit
);
19844 require_action_quiet( ( i32
>= 0 ) && ( i32
<= UINT16_MAX
), exit
, err
= kParamErr
);
19846 *outValue
= (uint16_t) i32
;
19852 //===========================================================================================================================
19853 // RecordClassFromArgString
19854 //===========================================================================================================================
19856 static OSStatus
RecordClassFromArgString( const char *inString
, uint16_t *outValue
)
19861 if( strcasecmp( inString
, "IN" ) == 0 )
19863 *outValue
= kDNSServiceClass_IN
;
19868 err
= StringToInt32( inString
, &i32
);
19869 require_noerr_quiet( err
, exit
);
19870 require_action_quiet( ( i32
>= 0 ) && ( i32
<= UINT16_MAX
), exit
, err
= kParamErr
);
19872 *outValue
= (uint16_t) i32
;
19878 //===========================================================================================================================
19879 // InterfaceIndexToName
19880 //===========================================================================================================================
19882 static char * InterfaceIndexToName( uint32_t inIfIndex
, char inNameBuf
[ kInterfaceNameBufLen
] )
19884 switch( inIfIndex
)
19886 case kDNSServiceInterfaceIndexAny
:
19887 strlcpy( inNameBuf
, "Any", kInterfaceNameBufLen
);
19890 case kDNSServiceInterfaceIndexLocalOnly
:
19891 strlcpy( inNameBuf
, "LocalOnly", kInterfaceNameBufLen
);
19894 case kDNSServiceInterfaceIndexUnicast
:
19895 strlcpy( inNameBuf
, "Unicast", kInterfaceNameBufLen
);
19898 case kDNSServiceInterfaceIndexP2P
:
19899 strlcpy( inNameBuf
, "P2P", kInterfaceNameBufLen
);
19902 #if( defined( kDNSServiceInterfaceIndexBLE ) )
19903 case kDNSServiceInterfaceIndexBLE
:
19904 strlcpy( inNameBuf
, "BLE", kInterfaceNameBufLen
);
19912 name
= if_indextoname( inIfIndex
, inNameBuf
);
19913 if( !name
) strlcpy( inNameBuf
, "NO NAME", kInterfaceNameBufLen
);
19918 return( inNameBuf
);
19921 //===========================================================================================================================
19922 // RecordTypeToString
19923 //===========================================================================================================================
19925 static const char * RecordTypeToString( unsigned int inValue
)
19927 const RecordType
* type
;
19928 const RecordType
* const end
= kRecordTypes
+ countof( kRecordTypes
);
19930 for( type
= kRecordTypes
; type
< end
; ++type
)
19932 if( type
->value
== inValue
) return( type
->name
);
19937 //===========================================================================================================================
19938 // DNSRecordDataToString
19939 //===========================================================================================================================
19942 DNSRecordDataToString(
19943 const void * inRDataPtr
,
19945 unsigned int inRDataType
,
19946 const void * inMsgPtr
,
19948 char ** outString
)
19951 const uint8_t * const rdataPtr
= (uint8_t *) inRDataPtr
;
19952 const uint8_t * const rdataEnd
= rdataPtr
+ inRDataLen
;
19954 const uint8_t * ptr
;
19956 char domainNameStr
[ kDNSServiceMaxDomainName
];
19962 if( inRDataType
== kDNSServiceType_A
)
19964 require_action_quiet( inRDataLen
== 4, exit
, err
= kMalformedErr
);
19966 ASPrintF( &rdataStr
, "%.4a", rdataPtr
);
19967 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
19972 else if( inRDataType
== kDNSServiceType_AAAA
)
19974 require_action_quiet( inRDataLen
== 16, exit
, err
= kMalformedErr
);
19976 ASPrintF( &rdataStr
, "%.16a", rdataPtr
);
19977 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
19980 // PTR, CNAME, or NS Record
19982 else if( ( inRDataType
== kDNSServiceType_PTR
) ||
19983 ( inRDataType
== kDNSServiceType_CNAME
) ||
19984 ( inRDataType
== kDNSServiceType_NS
) )
19988 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, NULL
);
19989 require_noerr( err
, exit
);
19993 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, NULL
);
19994 require_noerr( err
, exit
);
19997 rdataStr
= strdup( domainNameStr
);
19998 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
20003 else if( inRDataType
== kDNSServiceType_SRV
)
20005 const dns_fixed_fields_srv
* fields
;
20006 const uint8_t * target
;
20007 unsigned int priority
, weight
, port
;
20009 require_action_quiet( inRDataLen
> sizeof( dns_fixed_fields_srv
), exit
, err
= kMalformedErr
);
20011 fields
= (const dns_fixed_fields_srv
*) rdataPtr
;
20012 priority
= dns_fixed_fields_srv_get_priority( fields
);
20013 weight
= dns_fixed_fields_srv_get_weight( fields
);
20014 port
= dns_fixed_fields_srv_get_port( fields
);
20015 target
= (const uint8_t *) &fields
[ 1 ];
20019 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, target
, domainNameStr
, NULL
);
20020 require_noerr( err
, exit
);
20024 err
= DomainNameToString( target
, rdataEnd
, domainNameStr
, NULL
);
20025 require_noerr( err
, exit
);
20028 ASPrintF( &rdataStr
, "%u %u %u %s", priority
, weight
, port
, domainNameStr
);
20029 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
20034 else if( inRDataType
== kDNSServiceType_TXT
)
20036 require_action_quiet( inRDataLen
> 0, exit
, err
= kMalformedErr
);
20038 if( inRDataLen
== 1 )
20040 ASPrintF( &rdataStr
, "%#H", rdataPtr
, (int) inRDataLen
, INT_MAX
);
20041 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
20045 ASPrintF( &rdataStr
, "%#{txt}", rdataPtr
, inRDataLen
);
20046 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
20052 else if( inRDataType
== kDNSServiceType_SOA
)
20054 const dns_fixed_fields_soa
* fields
;
20055 uint32_t serial
, refresh
, retry
, expire
, minimum
;
20059 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, &ptr
);
20060 require_noerr( err
, exit
);
20062 require_action_quiet( ptr
< rdataEnd
, exit
, err
= kMalformedErr
);
20064 rdataStr
= strdup( domainNameStr
);
20065 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
20067 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, ptr
, domainNameStr
, &ptr
);
20068 require_noerr( err
, exit
);
20072 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, &ptr
);
20073 require_noerr( err
, exit
);
20075 rdataStr
= strdup( domainNameStr
);
20076 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
20078 err
= DomainNameToString( ptr
, rdataEnd
, domainNameStr
, &ptr
);
20079 require_noerr( err
, exit
);
20082 require_action_quiet( ( rdataEnd
- ptr
) == sizeof( dns_fixed_fields_soa
), exit
, err
= kMalformedErr
);
20084 fields
= (const dns_fixed_fields_soa
*) ptr
;
20085 serial
= dns_fixed_fields_soa_get_serial( fields
);
20086 refresh
= dns_fixed_fields_soa_get_refresh( fields
);
20087 retry
= dns_fixed_fields_soa_get_retry( fields
);
20088 expire
= dns_fixed_fields_soa_get_expire( fields
);
20089 minimum
= dns_fixed_fields_soa_get_minimum( fields
);
20091 n
= AppendPrintF( &rdataStr
, " %s %u %u %u %u %u\n", domainNameStr
, serial
, refresh
, retry
, expire
, minimum
);
20092 require_action( n
> 0, exit
, err
= kUnknownErr
);
20097 else if( inRDataType
== kDNSServiceType_NSEC
)
20099 unsigned int windowBlock
, bitmapLen
, i
, recordType
;
20100 const uint8_t * bitmapPtr
;
20104 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, &ptr
);
20105 require_noerr( err
, exit
);
20109 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, &ptr
);
20110 require_noerr( err
, exit
);
20113 require_action_quiet( ptr
< rdataEnd
, exit
, err
= kMalformedErr
);
20115 rdataStr
= strdup( domainNameStr
);
20116 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
20118 for( ; ptr
< rdataEnd
; ptr
+= ( 2 + bitmapLen
) )
20120 require_action_quiet( ( ptr
+ 2 ) < rdataEnd
, exit
, err
= kMalformedErr
);
20122 windowBlock
= ptr
[ 0 ];
20123 bitmapLen
= ptr
[ 1 ];
20124 bitmapPtr
= &ptr
[ 2 ];
20126 require_action_quiet( ( bitmapLen
>= 1 ) && ( bitmapLen
<= 32 ) , exit
, err
= kMalformedErr
);
20127 require_action_quiet( ( bitmapPtr
+ bitmapLen
) <= rdataEnd
, exit
, err
= kMalformedErr
);
20129 for( i
= 0; i
< BitArray_MaxBits( bitmapLen
); ++i
)
20131 if( BitArray_GetBit( bitmapPtr
, bitmapLen
, i
) )
20133 recordType
= ( windowBlock
* 256 ) + i
;
20134 n
= AppendPrintF( &rdataStr
, " %s", RecordTypeToString( recordType
) );
20135 require_action( n
> 0, exit
, err
= kUnknownErr
);
20143 else if( inRDataType
== kDNSServiceType_MX
)
20145 uint16_t preference
;
20146 const uint8_t * exchange
;
20148 require_action_quiet( ( rdataPtr
+ 2 ) < rdataEnd
, exit
, err
= kMalformedErr
);
20150 preference
= ReadBig16( rdataPtr
);
20151 exchange
= &rdataPtr
[ 2 ];
20155 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, exchange
, domainNameStr
, NULL
);
20156 require_noerr( err
, exit
);
20160 err
= DomainNameToString( exchange
, rdataEnd
, domainNameStr
, NULL
);
20161 require_noerr( err
, exit
);
20164 n
= ASPrintF( &rdataStr
, "%u %s", preference
, domainNameStr
);
20165 require_action( n
> 0, exit
, err
= kUnknownErr
);
20168 // Unhandled record type
20172 err
= kNotHandledErr
;
20177 *outString
= rdataStr
;
20182 FreeNullSafe( rdataStr
);
20186 //===========================================================================================================================
20187 // DNSMessageToText
20188 //===========================================================================================================================
20190 #define DNSFlagsOpCodeToString( X ) ( \
20191 ( (X) == kDNSOpCode_Query ) ? "Query" : \
20192 ( (X) == kDNSOpCode_InverseQuery ) ? "IQuery" : \
20193 ( (X) == kDNSOpCode_Status ) ? "Status" : \
20194 ( (X) == kDNSOpCode_Notify ) ? "Notify" : \
20195 ( (X) == kDNSOpCode_Update ) ? "Update" : \
20198 #define DNSFlagsRCodeToString( X ) ( \
20199 ( (X) == kDNSRCode_NoError ) ? "NoError" : \
20200 ( (X) == kDNSRCode_FormatError ) ? "FormErr" : \
20201 ( (X) == kDNSRCode_ServerFailure ) ? "ServFail" : \
20202 ( (X) == kDNSRCode_NXDomain ) ? "NXDomain" : \
20203 ( (X) == kDNSRCode_NotImplemented ) ? "NotImp" : \
20204 ( (X) == kDNSRCode_Refused ) ? "Refused" : \
20209 const uint8_t * inMsgPtr
,
20211 const Boolean inMDNS
,
20212 const Boolean inPrintRaw
,
20216 DataBuffer dataBuf
;
20218 const DNSHeader
* hdr
;
20219 const uint8_t * ptr
;
20220 unsigned int id
, flags
, opcode
, rcode
;
20221 unsigned int questionCount
, answerCount
, authorityCount
, additionalCount
, i
, totalRRCount
;
20222 uint8_t name
[ kDomainNameLengthMax
];
20223 char nameStr
[ kDNSServiceMaxDomainName
];
20225 DataBuffer_Init( &dataBuf
, NULL
, 0, SIZE_MAX
);
20226 #define _Append( ... ) do { err = DataBuffer_AppendF( &dataBuf, __VA_ARGS__ ); require_noerr( err, exit ); } while( 0 )
20228 require_action_quiet( inMsgLen
>= kDNSHeaderLength
, exit
, err
= kSizeErr
);
20230 hdr
= (DNSHeader
*) inMsgPtr
;
20231 id
= DNSHeaderGetID( hdr
);
20232 flags
= DNSHeaderGetFlags( hdr
);
20233 questionCount
= DNSHeaderGetQuestionCount( hdr
);
20234 answerCount
= DNSHeaderGetAnswerCount( hdr
);
20235 authorityCount
= DNSHeaderGetAuthorityCount( hdr
);
20236 additionalCount
= DNSHeaderGetAdditionalCount( hdr
);
20237 opcode
= DNSFlagsGetOpCode( flags
);
20238 rcode
= DNSFlagsGetRCode( flags
);
20240 _Append( "ID: 0x%04X (%u)\n", id
, id
);
20241 _Append( "Flags: 0x%04X %c/%s %cAA%cTC%cRD%cRA%?s%?s %s\n",
20243 ( flags
& kDNSHeaderFlag_Response
) ? 'R' : 'Q', DNSFlagsOpCodeToString( opcode
),
20244 ( flags
& kDNSHeaderFlag_AuthAnswer
) ? ' ' : '!',
20245 ( flags
& kDNSHeaderFlag_Truncation
) ? ' ' : '!',
20246 ( flags
& kDNSHeaderFlag_RecursionDesired
) ? ' ' : '!',
20247 ( flags
& kDNSHeaderFlag_RecursionAvailable
) ? ' ' : '!',
20248 !inMDNS
, ( flags
& kDNSHeaderFlag_AuthenticData
) ? " AD" : "!AD",
20249 !inMDNS
, ( flags
& kDNSHeaderFlag_CheckingDisabled
) ? " CD" : "!CD",
20250 DNSFlagsRCodeToString( rcode
) );
20251 _Append( "Question count: %u\n", questionCount
);
20252 _Append( "Answer count: %u\n", answerCount
);
20253 _Append( "Authority count: %u\n", authorityCount
);
20254 _Append( "Additional count: %u\n", additionalCount
);
20256 ptr
= (const uint8_t *) &hdr
[ 1 ];
20257 for( i
= 0; i
< questionCount
; ++i
)
20259 uint16_t qtype
, qclass
;
20262 err
= DNSMessageExtractQuestion( inMsgPtr
, inMsgLen
, ptr
, name
, &qtype
, &qclass
, &ptr
);
20263 require_noerr( err
, exit
);
20265 err
= DomainNameToString( name
, NULL
, nameStr
, NULL
);
20266 require_noerr( err
, exit
);
20268 isQU
= ( inMDNS
&& ( qclass
& kQClassUnicastResponseBit
) ) ? true : false;
20269 if( inMDNS
) qclass
&= ~kQClassUnicastResponseBit
;
20271 if( i
== 0 ) _Append( "\nQUESTION SECTION\n" );
20273 _Append( "%-30s %2s %?2s%?2u %-5s\n",
20274 nameStr
, inMDNS
? ( isQU
? "QU" : "QM" ) : "",
20275 ( qclass
== kDNSServiceClass_IN
), "IN", ( qclass
!= kDNSServiceClass_IN
), qclass
, RecordTypeToString( qtype
) );
20278 totalRRCount
= answerCount
+ authorityCount
+ additionalCount
;
20279 for( i
= 0; i
< totalRRCount
; ++i
)
20284 const uint8_t * rdataPtr
;
20287 Boolean cacheFlush
;
20289 err
= DNSMessageExtractRecord( inMsgPtr
, inMsgLen
, ptr
, name
, &type
, &class, &ttl
, &rdataPtr
, &rdataLen
, &ptr
);
20290 require_noerr( err
, exit
);
20292 err
= DomainNameToString( name
, NULL
, nameStr
, NULL
);
20293 require_noerr( err
, exit
);
20295 cacheFlush
= ( inMDNS
&& ( class & kRRClassCacheFlushBit
) ) ? true : false;
20296 if( inMDNS
) class &= ~kRRClassCacheFlushBit
;
20299 if( !inPrintRaw
) DNSRecordDataToString( rdataPtr
, rdataLen
, type
, inMsgPtr
, inMsgLen
, &rdataStr
);
20302 ASPrintF( &rdataStr
, "%#H", rdataPtr
, (int) rdataLen
, (int) rdataLen
);
20303 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
20306 if( answerCount
&& ( i
== 0 ) ) _Append( "\nANSWER SECTION\n" );
20307 else if( authorityCount
&& ( i
== answerCount
) ) _Append( "\nAUTHORITY SECTION\n" );
20308 else if( additionalCount
&& ( i
== ( answerCount
+ authorityCount
) ) ) _Append( "\nADDITIONAL SECTION\n" );
20310 _Append( "%-42s %6u %2s %?2s%?2u %-5s %s\n",
20311 nameStr
, ttl
, cacheFlush
? "CF" : "",
20312 ( class == kDNSServiceClass_IN
), "IN", ( class != kDNSServiceClass_IN
), class,
20313 RecordTypeToString( type
), rdataStr
);
20318 err
= DataBuffer_Append( &dataBuf
, "", 1 );
20319 require_noerr( err
, exit
);
20321 err
= DataBuffer_Detach( &dataBuf
, (uint8_t **) outText
, &len
);
20322 require_noerr( err
, exit
);
20325 DataBuffer_Free( &dataBuf
);
20329 //===========================================================================================================================
20330 // WriteDNSQueryMessage
20331 //===========================================================================================================================
20334 WriteDNSQueryMessage(
20335 uint8_t inMsg
[ kDNSQueryMessageMaxLen
],
20338 const char * inQName
,
20341 size_t * outMsgLen
)
20344 uint8_t qname
[ kDomainNameLengthMax
];
20346 err
= DomainNameFromString( qname
, inQName
, NULL
);
20347 require_noerr_quiet( err
, exit
);
20349 err
= DNSMessageWriteQuery( inMsgID
, inFlags
, qname
, inQType
, inQClass
, inMsg
, outMsgLen
);
20350 require_noerr_quiet( err
, exit
);
20356 //===========================================================================================================================
20357 // DispatchSignalSourceCreate
20358 //===========================================================================================================================
20361 DispatchSignalSourceCreate(
20363 dispatch_queue_t inQueue
,
20364 DispatchHandler inEventHandler
,
20366 dispatch_source_t
* outSource
)
20369 dispatch_source_t source
;
20371 source
= dispatch_source_create( DISPATCH_SOURCE_TYPE_SIGNAL
, (uintptr_t) inSignal
, 0, inQueue
);
20372 require_action( source
, exit
, err
= kUnknownErr
);
20374 dispatch_set_context( source
, inContext
);
20375 dispatch_source_set_event_handler_f( source
, inEventHandler
);
20377 *outSource
= source
;
20384 //===========================================================================================================================
20385 // DispatchSocketSourceCreate
20386 //===========================================================================================================================
20389 DispatchSocketSourceCreate(
20391 dispatch_source_type_t inType
,
20392 dispatch_queue_t inQueue
,
20393 DispatchHandler inEventHandler
,
20394 DispatchHandler inCancelHandler
,
20396 dispatch_source_t
* outSource
)
20399 dispatch_source_t source
;
20401 source
= dispatch_source_create( inType
, (uintptr_t) inSock
, 0, inQueue
? inQueue
: dispatch_get_main_queue() );
20402 require_action( source
, exit
, err
= kUnknownErr
);
20404 dispatch_set_context( source
, inContext
);
20405 dispatch_source_set_event_handler_f( source
, inEventHandler
);
20406 dispatch_source_set_cancel_handler_f( source
, inCancelHandler
);
20408 *outSource
= source
;
20415 //===========================================================================================================================
20416 // DispatchTimerCreate
20417 //===========================================================================================================================
20420 DispatchTimerCreate(
20421 dispatch_time_t inStart
,
20422 uint64_t inIntervalNs
,
20423 uint64_t inLeewayNs
,
20424 dispatch_queue_t inQueue
,
20425 DispatchHandler inEventHandler
,
20426 DispatchHandler inCancelHandler
,
20428 dispatch_source_t
* outTimer
)
20431 dispatch_source_t timer
;
20433 timer
= dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, inQueue
? inQueue
: dispatch_get_main_queue() );
20434 require_action( timer
, exit
, err
= kUnknownErr
);
20436 dispatch_source_set_timer( timer
, inStart
, inIntervalNs
, inLeewayNs
);
20437 dispatch_set_context( timer
, inContext
);
20438 dispatch_source_set_event_handler_f( timer
, inEventHandler
);
20439 dispatch_source_set_cancel_handler_f( timer
, inCancelHandler
);
20448 //===========================================================================================================================
20449 // DispatchProcessMonitorCreate
20450 //===========================================================================================================================
20453 DispatchProcessMonitorCreate(
20455 unsigned long inFlags
,
20456 dispatch_queue_t inQueue
,
20457 DispatchHandler inEventHandler
,
20458 DispatchHandler inCancelHandler
,
20460 dispatch_source_t
* outMonitor
)
20463 dispatch_source_t monitor
;
20465 monitor
= dispatch_source_create( DISPATCH_SOURCE_TYPE_PROC
, (uintptr_t) inPID
, inFlags
,
20466 inQueue
? inQueue
: dispatch_get_main_queue() );
20467 require_action( monitor
, exit
, err
= kUnknownErr
);
20469 dispatch_set_context( monitor
, inContext
);
20470 dispatch_source_set_event_handler_f( monitor
, inEventHandler
);
20471 dispatch_source_set_cancel_handler_f( monitor
, inCancelHandler
);
20473 *outMonitor
= monitor
;
20480 //===========================================================================================================================
20481 // ServiceTypeDescription
20482 //===========================================================================================================================
20486 const char * name
; // Name of the service type in two-label "_service._proto" format.
20487 const char * description
; // Description of the service type.
20491 // A Non-comprehensive table of DNS-SD service types
20493 static const ServiceType kServiceTypes
[] =
20495 { "_acp-sync._tcp", "AirPort Base Station Sync" },
20496 { "_adisk._tcp", "Automatic Disk Discovery" },
20497 { "_afpovertcp._tcp", "Apple File Sharing" },
20498 { "_airdrop._tcp", "AirDrop" },
20499 { "_airplay._tcp", "AirPlay" },
20500 { "_airport._tcp", "AirPort Base Station" },
20501 { "_daap._tcp", "Digital Audio Access Protocol (iTunes)" },
20502 { "_eppc._tcp", "Remote AppleEvents" },
20503 { "_ftp._tcp", "File Transfer Protocol" },
20504 { "_home-sharing._tcp", "Home Sharing" },
20505 { "_homekit._tcp", "HomeKit" },
20506 { "_http._tcp", "World Wide Web HTML-over-HTTP" },
20507 { "_https._tcp", "HTTP over SSL/TLS" },
20508 { "_ipp._tcp", "Internet Printing Protocol" },
20509 { "_ldap._tcp", "Lightweight Directory Access Protocol" },
20510 { "_mediaremotetv._tcp", "Media Remote" },
20511 { "_net-assistant._tcp", "Apple Remote Desktop" },
20512 { "_od-master._tcp", "OpenDirectory Master" },
20513 { "_nfs._tcp", "Network File System" },
20514 { "_presence._tcp", "Peer-to-peer messaging / Link-Local Messaging" },
20515 { "_pdl-datastream._tcp", "Printer Page Description Language Data Stream" },
20516 { "_raop._tcp", "Remote Audio Output Protocol" },
20517 { "_rfb._tcp", "Remote Frame Buffer" },
20518 { "_scanner._tcp", "Bonjour Scanning" },
20519 { "_smb._tcp", "Server Message Block over TCP/IP" },
20520 { "_sftp-ssh._tcp", "Secure File Transfer Protocol over SSH" },
20521 { "_sleep-proxy._udp", "Sleep Proxy Server" },
20522 { "_ssh._tcp", "SSH Remote Login Protocol" },
20523 { "_teleport._tcp", "teleport" },
20524 { "_tftp._tcp", "Trivial File Transfer Protocol" },
20525 { "_workstation._tcp", "Workgroup Manager" },
20526 { "_webdav._tcp", "World Wide Web Distributed Authoring and Versioning (WebDAV)" },
20527 { "_webdavs._tcp", "WebDAV over SSL/TLS" }
20530 static const char * ServiceTypeDescription( const char *inName
)
20532 const ServiceType
* serviceType
;
20533 const ServiceType
* const end
= kServiceTypes
+ countof( kServiceTypes
);
20535 for( serviceType
= kServiceTypes
; serviceType
< end
; ++serviceType
)
20537 if( ( stricmp_prefix( inName
, serviceType
->name
) == 0 ) )
20539 const char * const ptr
= &inName
[ strlen( serviceType
->name
) ];
20541 if( ( ptr
[ 0 ] == '\0' ) || ( ( ptr
[ 0 ] == '.' ) && ( ptr
[ 1 ] == '\0' ) ) )
20543 return( serviceType
->description
);
20550 //===========================================================================================================================
20551 // SocketContextCreate
20552 //===========================================================================================================================
20554 static OSStatus
SocketContextCreate( SocketRef inSock
, void * inUserContext
, SocketContext
**outContext
)
20557 SocketContext
* context
;
20559 context
= (SocketContext
*) calloc( 1, sizeof( *context
) );
20560 require_action( context
, exit
, err
= kNoMemoryErr
);
20562 context
->refCount
= 1;
20563 context
->sock
= inSock
;
20564 context
->userContext
= inUserContext
;
20566 *outContext
= context
;
20573 //===========================================================================================================================
20574 // SocketContextRetain
20575 //===========================================================================================================================
20577 static SocketContext
* SocketContextRetain( SocketContext
*inContext
)
20579 ++inContext
->refCount
;
20580 return( inContext
);
20583 //===========================================================================================================================
20584 // SocketContextRelease
20585 //===========================================================================================================================
20587 static void SocketContextRelease( SocketContext
*inContext
)
20589 if( --inContext
->refCount
== 0 )
20591 ForgetSocket( &inContext
->sock
);
20596 //===========================================================================================================================
20597 // SocketContextCancelHandler
20598 //===========================================================================================================================
20600 static void SocketContextCancelHandler( void *inContext
)
20602 SocketContextRelease( (SocketContext
*) inContext
);
20605 //===========================================================================================================================
20607 //===========================================================================================================================
20609 static OSStatus
StringToInt32( const char *inString
, int32_t *outValue
)
20615 value
= strtol( inString
, &endPtr
, 0 );
20616 require_action_quiet( ( *endPtr
== '\0' ) && ( endPtr
!= inString
), exit
, err
= kParamErr
);
20617 require_action_quiet( ( value
>= INT32_MIN
) && ( value
<= INT32_MAX
), exit
, err
= kRangeErr
);
20619 *outValue
= (int32_t) value
;
20626 //===========================================================================================================================
20628 //===========================================================================================================================
20630 static OSStatus
StringToUInt32( const char *inString
, uint32_t *outValue
)
20636 value
= (uint32_t) strtol( inString
, &endPtr
, 0 );
20637 require_action_quiet( ( *endPtr
== '\0' ) && ( endPtr
!= inString
), exit
, err
= kParamErr
);
20646 #if( TARGET_OS_DARWIN )
20647 //===========================================================================================================================
20649 //===========================================================================================================================
20651 static int64_t _StringToInt64( const char *inString
, OSStatus
*outError
)
20658 set_errno_compat( 0 );
20659 val
= strtoll( inString
, &end
, 0 );
20660 errnoVal
= errno_compat();
20662 require_action_quiet( ( *end
== '\0' ) && ( end
!= inString
), exit
, err
= kMalformedErr
);
20663 require_action_quiet( ( ( val
!= LLONG_MIN
) && ( val
!= LLONG_MAX
) ) || ( errnoVal
!= ERANGE
), exit
, err
= kRangeErr
);
20664 require_action_quiet( ( val
>= INT64_MIN
) && ( val
<= INT64_MAX
), exit
, err
= kRangeErr
);
20668 if( outError
) *outError
= err
;
20669 return( (int64_t)val
);
20672 //===========================================================================================================================
20674 //===========================================================================================================================
20676 static uint64_t _StringToUInt64( const char *inString
, OSStatus
*outError
)
20679 unsigned long long val
;
20683 set_errno_compat( 0 );
20684 val
= strtoull( inString
, &end
, 0 );
20685 errnoVal
= errno_compat();
20687 require_action_quiet( ( *end
== '\0' ) && ( end
!= inString
), exit
, err
= kMalformedErr
);
20688 require_action_quiet( ( val
!= ULLONG_MAX
) || ( errnoVal
!= ERANGE
), exit
, err
= kRangeErr
);
20689 require_action_quiet( val
<= UINT64_MAX
, exit
, err
= kRangeErr
);
20693 if( outError
) *outError
= err
;
20694 return( (uint64_t)val
);
20697 //===========================================================================================================================
20699 //===========================================================================================================================
20701 static pid_t
_StringToPID( const char *inString
, OSStatus
*outError
)
20706 val
= _StringToInt64( inString
, &err
);
20707 require_noerr_quiet( err
, exit
);
20708 require_action_quiet( val
== (pid_t
) val
, exit
, err
= kRangeErr
);
20712 if( outError
) *outError
= err
;
20713 return( (pid_t
) val
);
20716 //===========================================================================================================================
20717 // _ParseEscapedString
20719 // Note: Similar to ParseEscapedString() from CoreUtils except that _ParseEscapedString() takes an optional C string
20720 // containing delimiter characters instead of being limited to one delimiter character. Also, when the function returns
20721 // due to a delimiter, the output pointer is set to the delimiter character instead of the character after the delimiter.
20722 //===========================================================================================================================
20725 _ParseEscapedString(
20726 const char * inSrc
,
20727 const char * inEnd
,
20728 const char * inDelimiters
,
20731 size_t * outCopiedLen
,
20732 size_t * outActualLen
,
20733 const char ** outPtr
)
20737 char * dst
= inBufPtr
;
20738 const char * const lim
= ( inBufLen
> 0 ) ? &inBufPtr
[ inBufLen
- 1 ] : inBufPtr
;
20743 if( !inDelimiters
) inDelimiters
= "";
20744 while( ptr
< inEnd
)
20750 for( del
= inDelimiters
; ( *del
!= '\0' ) && ( c
!= *del
); ++del
) {}
20751 if( *del
!= '\0' ) break;
20755 require_action_quiet( ptr
< inEnd
, exit
, err
= kUnderrunErr
);
20759 if( dst
< lim
) *dst
++ = (char) c
;
20761 if( inBufLen
> 0 ) *dst
= '\0';
20763 if( outCopiedLen
) *outCopiedLen
= (size_t)( dst
- inBufPtr
);
20764 if( outActualLen
) *outActualLen
= len
;
20765 if( outPtr
) *outPtr
= ptr
;
20773 //===========================================================================================================================
20774 // StringToARecordData
20775 //===========================================================================================================================
20777 static OSStatus
StringToARecordData( const char *inString
, uint8_t **outPtr
, size_t *outLen
)
20780 uint32_t * addrPtr
;
20781 const size_t addrLen
= sizeof( *addrPtr
);
20784 addrPtr
= (uint32_t *) malloc( addrLen
);
20785 require_action( addrPtr
, exit
, err
= kNoMemoryErr
);
20787 err
= _StringToIPv4Address( inString
, kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
, addrPtr
,
20788 NULL
, NULL
, NULL
, &end
);
20789 if( !err
&& ( *end
!= '\0' ) ) err
= kMalformedErr
;
20790 require_noerr_quiet( err
, exit
);
20792 *addrPtr
= HostToBig32( *addrPtr
);
20794 *outPtr
= (uint8_t *) addrPtr
;
20799 FreeNullSafe( addrPtr
);
20803 //===========================================================================================================================
20804 // StringToAAAARecordData
20805 //===========================================================================================================================
20807 static OSStatus
StringToAAAARecordData( const char *inString
, uint8_t **outPtr
, size_t *outLen
)
20811 const size_t addrLen
= 16;
20814 addrPtr
= (uint8_t *) malloc( addrLen
);
20815 require_action( addrPtr
, exit
, err
= kNoMemoryErr
);
20817 err
= _StringToIPv6Address( inString
,
20818 kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
| kStringToIPAddressFlagsNoScope
,
20819 addrPtr
, NULL
, NULL
, NULL
, &end
);
20820 if( !err
&& ( *end
!= '\0' ) ) err
= kMalformedErr
;
20821 require_noerr_quiet( err
, exit
);
20828 FreeNullSafe( addrPtr
);
20832 //===========================================================================================================================
20833 // StringToDomainName
20834 //===========================================================================================================================
20836 static OSStatus
StringToDomainName( const char *inString
, uint8_t **outPtr
, size_t *outLen
)
20842 uint8_t nameBuf
[ kDomainNameLengthMax
];
20844 err
= DomainNameFromString( nameBuf
, inString
, &end
);
20845 require_noerr_quiet( err
, exit
);
20847 nameLen
= (size_t)( end
- nameBuf
);
20848 namePtr
= _memdup( nameBuf
, nameLen
);
20849 require_action( namePtr
, exit
, err
= kNoMemoryErr
);
20853 if( outLen
) *outLen
= nameLen
;
20859 #if( TARGET_OS_DARWIN )
20860 //===========================================================================================================================
20861 // GetDefaultDNSServer
20862 //===========================================================================================================================
20864 static OSStatus
GetDefaultDNSServer( sockaddr_ip
*outAddr
)
20867 dns_config_t
* config
;
20868 struct sockaddr
* addr
;
20871 config
= dns_configuration_copy();
20872 require_action( config
, exit
, err
= kUnknownErr
);
20875 for( i
= 0; i
< config
->n_resolver
; ++i
)
20877 const dns_resolver_t
* const resolver
= config
->resolver
[ i
];
20879 if( !resolver
->domain
&& ( resolver
->n_nameserver
> 0 ) )
20881 addr
= resolver
->nameserver
[ 0 ];
20885 require_action_quiet( addr
, exit
, err
= kNotFoundErr
);
20887 SockAddrCopy( addr
, outAddr
);
20891 if( config
) dns_configuration_free( config
);
20896 //===========================================================================================================================
20897 // GetMDNSMulticastAddrV4
20898 //===========================================================================================================================
20900 static void _MDNSMulticastAddrV4Init( void *inContext
);
20902 static const struct sockaddr
* GetMDNSMulticastAddrV4( void )
20904 static struct sockaddr_in sMDNSMulticastAddrV4
;
20905 static dispatch_once_t sMDNSMulticastAddrV4InitOnce
= 0;
20907 dispatch_once_f( &sMDNSMulticastAddrV4InitOnce
, &sMDNSMulticastAddrV4
, _MDNSMulticastAddrV4Init
);
20908 return( (const struct sockaddr
*) &sMDNSMulticastAddrV4
);
20911 static void _MDNSMulticastAddrV4Init( void *inContext
)
20913 struct sockaddr_in
* const addr
= (struct sockaddr_in
*) inContext
;
20915 memset( addr
, 0, sizeof( *addr
) );
20916 SIN_LEN_SET( addr
);
20917 addr
->sin_family
= AF_INET
;
20918 addr
->sin_port
= htons( kMDNSPort
);
20919 addr
->sin_addr
.s_addr
= htonl( 0xE00000FB ); // The mDNS IPv4 multicast address is 224.0.0.251
20922 //===========================================================================================================================
20923 // GetMDNSMulticastAddrV6
20924 //===========================================================================================================================
20926 static void _MDNSMulticastAddrV6Init( void *inContext
);
20928 static const struct sockaddr
* GetMDNSMulticastAddrV6( void )
20930 static struct sockaddr_in6 sMDNSMulticastAddrV6
;
20931 static dispatch_once_t sMDNSMulticastAddrV6InitOnce
= 0;
20933 dispatch_once_f( &sMDNSMulticastAddrV6InitOnce
, &sMDNSMulticastAddrV6
, _MDNSMulticastAddrV6Init
);
20934 return( (const struct sockaddr
*) &sMDNSMulticastAddrV6
);
20937 static void _MDNSMulticastAddrV6Init( void *inContext
)
20939 struct sockaddr_in6
* const addr
= (struct sockaddr_in6
*) inContext
;
20941 memset( addr
, 0, sizeof( *addr
) );
20942 SIN6_LEN_SET( addr
);
20943 addr
->sin6_family
= AF_INET6
;
20944 addr
->sin6_port
= htons( kMDNSPort
);
20945 addr
->sin6_addr
.s6_addr
[ 0 ] = 0xFF; // The mDNS IPv6 multicast address is FF02::FB.
20946 addr
->sin6_addr
.s6_addr
[ 1 ] = 0x02;
20947 addr
->sin6_addr
.s6_addr
[ 15 ] = 0xFB;
20950 //===========================================================================================================================
20951 // CreateMulticastSocket
20952 //===========================================================================================================================
20955 CreateMulticastSocket(
20956 const struct sockaddr
* inAddr
,
20958 const char * inIfName
,
20959 uint32_t inIfIndex
,
20962 SocketRef
* outSock
)
20965 SocketRef sock
= kInvalidSocketRef
;
20966 const int family
= inAddr
->sa_family
;
20969 require_action_quiet( ( family
== AF_INET
) ||( family
== AF_INET6
), exit
, err
= kUnsupportedErr
);
20971 err
= ServerSocketOpen( family
, SOCK_DGRAM
, IPPROTO_UDP
, inPort
, &port
, kSocketBufferSize_DontSet
, &sock
);
20972 require_noerr_quiet( err
, exit
);
20974 err
= SocketSetMulticastInterface( sock
, inIfName
, inIfIndex
);
20975 require_noerr_quiet( err
, exit
);
20977 if( family
== AF_INET
)
20979 err
= setsockopt( sock
, IPPROTO_IP
, IP_MULTICAST_LOOP
, (char *) &(uint8_t){ 1 }, (socklen_t
) sizeof( uint8_t ) );
20980 err
= map_socket_noerr_errno( sock
, err
);
20981 require_noerr_quiet( err
, exit
);
20985 err
= setsockopt( sock
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, (char *) &(int){ 1 }, (socklen_t
) sizeof( int ) );
20986 err
= map_socket_noerr_errno( sock
, err
);
20987 require_noerr_quiet( err
, exit
);
20992 err
= SocketJoinMulticast( sock
, inAddr
, inIfName
, inIfIndex
);
20993 require_noerr_quiet( err
, exit
);
20996 if( outPort
) *outPort
= port
;
20998 sock
= kInvalidSocketRef
;
21001 ForgetSocket( &sock
);
21005 //===========================================================================================================================
21006 // DecimalTextToUInt32
21007 //===========================================================================================================================
21009 static OSStatus
DecimalTextToUInt32( const char *inSrc
, const char *inEnd
, uint32_t *outValue
, const char **outPtr
)
21013 const char * ptr
= inSrc
;
21015 require_action_quiet( ( ptr
< inEnd
) && isdigit_safe( *ptr
), exit
, err
= kMalformedErr
);
21017 value
= (uint64_t)( *ptr
++ - '0' );
21020 if( ( ptr
< inEnd
) && isdigit_safe( *ptr
) )
21022 err
= kMalformedErr
;
21028 while( ( ptr
< inEnd
) && isdigit_safe( *ptr
) )
21030 value
= ( value
* 10 ) + (uint64_t)( *ptr
++ - '0' );
21031 require_action_quiet( value
<= UINT32_MAX
, exit
, err
= kRangeErr
);
21035 *outValue
= (uint32_t) value
;
21036 if( outPtr
) *outPtr
= ptr
;
21043 //===========================================================================================================================
21044 // CheckIntegerArgument
21045 //===========================================================================================================================
21047 static OSStatus
CheckIntegerArgument( int inArgValue
, const char *inArgName
, int inMin
, int inMax
)
21049 if( ( inArgValue
>= inMin
) && ( inArgValue
<= inMax
) ) return( kNoErr
);
21051 FPrintF( stderr
, "error: Invalid %s: %d. Valid range is [%d, %d].\n", inArgName
, inArgValue
, inMin
, inMax
);
21052 return( kRangeErr
);
21055 //===========================================================================================================================
21056 // CheckDoubleArgument
21057 //===========================================================================================================================
21059 static OSStatus
CheckDoubleArgument( double inArgValue
, const char *inArgName
, double inMin
, double inMax
)
21061 if( ( inArgValue
>= inMin
) && ( inArgValue
<= inMax
) ) return( kNoErr
);
21063 FPrintF( stderr
, "error: Invalid %s: %.1f. Valid range is [%.1f, %.1f].\n", inArgName
, inArgValue
, inMin
, inMax
);
21064 return( kRangeErr
);
21067 //===========================================================================================================================
21069 //===========================================================================================================================
21071 static OSStatus
CheckRootUser( void )
21073 if( geteuid() == 0 ) return( kNoErr
);
21075 FPrintF( stderr
, "error: This command must to be run as root.\n" );
21076 return( kPermissionErr
);
21079 //===========================================================================================================================
21082 // Note: Based on systemf() from CoreUtils framework.
21083 //===========================================================================================================================
21085 extern char ** environ
;
21087 static OSStatus
SpawnCommand( pid_t
*outPID
, const char *inFormat
, ... )
21096 va_start( args
, inFormat
);
21097 VASPrintF( &command
, inFormat
, args
);
21099 require_action( command
, exit
, err
= kUnknownErr
);
21101 argv
[ 0 ] = "/bin/sh";
21103 argv
[ 2 ] = command
;
21105 err
= posix_spawn( &pid
, argv
[ 0 ], NULL
, NULL
, argv
, environ
);
21107 require_noerr_quiet( err
, exit
);
21109 if( outPID
) *outPID
= pid
;
21115 //===========================================================================================================================
21116 // OutputFormatFromArgString
21117 //===========================================================================================================================
21119 static OSStatus
OutputFormatFromArgString( const char *inArgString
, OutputFormatType
*outFormat
)
21122 OutputFormatType format
;
21124 format
= (OutputFormatType
) CLIArgToValue( "format", inArgString
, &err
,
21125 kOutputFormatStr_JSON
, kOutputFormatType_JSON
,
21126 kOutputFormatStr_XML
, kOutputFormatType_XML
,
21127 kOutputFormatStr_Binary
, kOutputFormatType_Binary
,
21129 if( outFormat
) *outFormat
= format
;
21133 //===========================================================================================================================
21134 // OutputPropertyList
21135 //===========================================================================================================================
21137 static OSStatus
OutputPropertyList( CFPropertyListRef inPList
, OutputFormatType inType
, const char *inOutputFilePath
)
21140 CFDataRef results
= NULL
;
21141 FILE * file
= NULL
;
21143 // Convert plist to a specific format.
21147 case kOutputFormatType_JSON
:
21148 results
= CFCreateJSONData( inPList
, kJSONFlags_None
, NULL
);
21149 require_action( results
, exit
, err
= kUnknownErr
);
21152 case kOutputFormatType_XML
:
21153 results
= CFPropertyListCreateData( NULL
, inPList
, kCFPropertyListXMLFormat_v1_0
, 0, NULL
);
21154 require_action( results
, exit
, err
= kUnknownErr
);
21157 case kOutputFormatType_Binary
:
21158 results
= CFPropertyListCreateData( NULL
, inPList
, kCFPropertyListBinaryFormat_v1_0
, 0, NULL
);
21159 require_action( results
, exit
, err
= kUnknownErr
);
21167 // Write formatted results to file or stdout.
21169 if( inOutputFilePath
)
21171 file
= fopen( inOutputFilePath
, "wb" );
21172 err
= map_global_value_errno( file
, file
);
21173 require_noerr( err
, exit
);
21180 err
= WriteANSIFile( file
, CFDataGetBytePtr( results
), (size_t) CFDataGetLength( results
) );
21181 require_noerr_quiet( err
, exit
);
21183 // Write a trailing newline for JSON-formatted results.
21185 if( inType
== kOutputFormatType_JSON
)
21187 err
= WriteANSIFile( file
, "\n", 1 );
21188 require_noerr_quiet( err
, exit
);
21192 if( file
&& ( file
!= stdout
) ) fclose( file
);
21193 CFReleaseNullSafe( results
);
21197 //===========================================================================================================================
21198 // CreateSRVRecordDataFromString
21199 //===========================================================================================================================
21201 static OSStatus
CreateSRVRecordDataFromString( const char *inString
, uint8_t **outPtr
, size_t *outLen
)
21204 DataBuffer dataBuf
;
21208 uint8_t target
[ kDomainNameLengthMax
];
21210 DataBuffer_Init( &dataBuf
, NULL
, 0, ( 3 * 2 ) + kDomainNameLengthMax
);
21212 // Parse and set the priority, weight, and port values (all three are unsigned 16-bit values).
21215 for( i
= 0; i
< 3; ++i
)
21221 value
= strtol( ptr
, &next
, 0 );
21222 require_action_quiet( ( next
!= ptr
) && ( *next
== ',' ), exit
, err
= kMalformedErr
);
21223 require_action_quiet( ( value
>= 0 ) && ( value
<= UINT16_MAX
), exit
, err
= kRangeErr
);
21226 WriteBig16( buf
, value
);
21228 err
= DataBuffer_Append( &dataBuf
, buf
, sizeof( buf
) );
21229 require_noerr( err
, exit
);
21232 // Set the target domain name.
21234 err
= DomainNameFromString( target
, ptr
, &end
);
21235 require_noerr_quiet( err
, exit
);
21237 err
= DataBuffer_Append( &dataBuf
, target
, (size_t)( end
- target
) );
21238 require_noerr( err
, exit
);
21240 err
= DataBuffer_Detach( &dataBuf
, outPtr
, outLen
);
21241 require_noerr( err
, exit
);
21244 DataBuffer_Free( &dataBuf
);
21248 //===========================================================================================================================
21249 // CreateTXTRecordDataFromString
21250 //===========================================================================================================================
21252 static OSStatus
CreateTXTRecordDataFromString(const char *inString
, int inDelimiter
, uint8_t **outPtr
, size_t *outLen
)
21255 DataBuffer dataBuf
;
21257 uint8_t txtStr
[ 256 ]; // Buffer for single TXT string: 1 length byte + up to 255 bytes of data.
21259 DataBuffer_Init( &dataBuf
, NULL
, 0, kDNSRecordDataLengthMax
);
21264 uint8_t * dst
= &txtStr
[ 1 ];
21265 const uint8_t * const lim
= &txtStr
[ 256 ];
21268 while( *src
&& ( *src
!= inDelimiter
) )
21270 if( ( c
= *src
++ ) == '\\' )
21272 require_action_quiet( *src
!= '\0', exit
, err
= kUnderrunErr
);
21275 require_action_quiet( dst
< lim
, exit
, err
= kOverrunErr
);
21276 *dst
++ = (uint8_t) c
;
21278 txtStr
[ 0 ] = (uint8_t)( dst
- &txtStr
[ 1 ] );
21279 err
= DataBuffer_Append( &dataBuf
, txtStr
, 1 + txtStr
[ 0 ] );
21280 require_noerr( err
, exit
);
21282 if( *src
== '\0' ) break;
21286 err
= DataBuffer_Detach( &dataBuf
, outPtr
, outLen
);
21287 require_noerr( err
, exit
);
21290 DataBuffer_Free( &dataBuf
);
21294 //===========================================================================================================================
21295 // CreateNSECRecordData
21296 //===========================================================================================================================
21298 DECLARE_QSORT_NUMERIC_COMPARATOR( _QSortCmpUnsigned
);
21299 DEFINE_QSORT_NUMERIC_COMPARATOR( unsigned int, _QSortCmpUnsigned
)
21301 #define kNSECBitmapMaxLength 32 // 32 bytes (256 bits). See <https://tools.ietf.org/html/rfc4034#section-4.1.2>.
21304 CreateNSECRecordData(
21305 const uint8_t * inNextDomainName
,
21308 unsigned int inTypeCount
,
21313 DataBuffer rdataDB
;
21314 unsigned int * array
= NULL
;
21315 unsigned int i
, type
, maxBit
, currBlock
, bitmapLen
;
21316 uint8_t fields
[ 2 + kNSECBitmapMaxLength
];
21317 uint8_t * const bitmap
= &fields
[ 2 ];
21319 va_start( args
, inTypeCount
);
21320 DataBuffer_Init( &rdataDB
, NULL
, 0, kDNSRecordDataLengthMax
);
21322 // Append Next Domain Name.
21324 err
= DataBuffer_Append( &rdataDB
, inNextDomainName
, DomainNameLength( inNextDomainName
) );
21325 require_noerr( err
, exit
);
21327 // Append Type Bit Maps.
21330 memset( bitmap
, 0, kNSECBitmapMaxLength
);
21331 if( inTypeCount
> 0 )
21333 array
= (unsigned int *) malloc( inTypeCount
* sizeof_element( array
) );
21334 require_action( array
, exit
, err
= kNoMemoryErr
);
21336 for( i
= 0; i
< inTypeCount
; ++i
)
21338 type
= va_arg( args
, unsigned int );
21339 require_action_quiet( type
<= UINT16_MAX
, exit
, err
= kRangeErr
);
21342 qsort( array
, inTypeCount
, sizeof_element( array
), _QSortCmpUnsigned
);
21344 currBlock
= array
[ 0 ] / 256;
21345 for( i
= 0; i
< inTypeCount
; ++i
)
21347 const unsigned int block
= array
[ i
] / 256;
21348 const unsigned int bit
= array
[ i
] % 256;
21350 if( block
!= currBlock
)
21352 bitmapLen
= BitArray_MaxBytes( maxBit
+ 1 );
21353 fields
[ 0 ] = (uint8_t) currBlock
;
21354 fields
[ 1 ] = (uint8_t) bitmapLen
;
21356 err
= DataBuffer_Append( &rdataDB
, fields
, 2 + bitmapLen
);
21357 require_noerr( err
, exit
);
21361 memset( bitmap
, 0, bitmapLen
);
21363 BitArray_SetBit( bitmap
, bit
);
21364 if( bit
> maxBit
) maxBit
= bit
;
21372 bitmapLen
= BitArray_MaxBytes( maxBit
+ 1 );
21373 fields
[ 0 ] = (uint8_t) currBlock
;
21374 fields
[ 1 ] = (uint8_t) bitmapLen
;
21376 err
= DataBuffer_Append( &rdataDB
, fields
, 2 + bitmapLen
);
21377 require_noerr( err
, exit
);
21379 err
= DataBuffer_Detach( &rdataDB
, outPtr
, outLen
);
21380 require_noerr( err
, exit
);
21384 DataBuffer_Free( &rdataDB
);
21385 FreeNullSafe( array
);
21389 //===========================================================================================================================
21391 //===========================================================================================================================
21394 _AppendSOARecordData(
21396 const uint8_t * inMName
,
21397 const uint8_t * inRName
,
21399 uint32_t inRefresh
,
21402 uint32_t inMinimumTTL
,
21408 const uint8_t * inNamePtr
,
21413 const uint8_t * inMName
,
21414 const uint8_t * inRName
,
21416 uint32_t inRefresh
,
21419 uint32_t inMinimumTTL
,
21424 size_t rdlengthOffset
= 0;
21425 uint8_t * rdlengthPtr
;
21429 err
= _DataBuffer_AppendDNSRecord( inDB
, inNamePtr
, inNameLen
, inType
, inClass
, inTTL
, NULL
, 0 );
21430 require_noerr( err
, exit
);
21432 rdlengthOffset
= DataBuffer_GetLen( inDB
) - 2;
21435 err
= _AppendSOARecordData( inDB
, inMName
, inRName
, inSerial
, inRefresh
, inRetry
, inExpire
, inMinimumTTL
, &rdataLen
);
21436 require_noerr( err
, exit
);
21440 rdlengthPtr
= DataBuffer_GetPtr( inDB
) + rdlengthOffset
;
21441 WriteBig16( rdlengthPtr
, rdataLen
);
21444 if( outLen
) *outLen
= inNameLen
+ sizeof( dns_fixed_fields_record
) + rdataLen
;
21452 _AppendSOARecordData(
21454 const uint8_t * inMName
,
21455 const uint8_t * inRName
,
21457 uint32_t inRefresh
,
21460 uint32_t inMinimumTTL
,
21464 dns_fixed_fields_soa fields
;
21465 const size_t mnameLen
= DomainNameLength( inMName
);
21466 const size_t rnameLen
= DomainNameLength( inRName
);
21470 err
= DataBuffer_Append( inDB
, inMName
, mnameLen
);
21471 require_noerr( err
, exit
);
21473 err
= DataBuffer_Append( inDB
, inRName
, rnameLen
);
21474 require_noerr( err
, exit
);
21476 dns_fixed_fields_soa_init( &fields
, inSerial
, inRefresh
, inRetry
, inExpire
, inMinimumTTL
);
21477 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
21478 require_noerr( err
, exit
);
21480 if( outLen
) *outLen
= mnameLen
+ rnameLen
+ sizeof( fields
);
21487 //===========================================================================================================================
21488 // CreateSOARecordData
21489 //===========================================================================================================================
21492 CreateSOARecordData(
21493 const uint8_t * inMName
,
21494 const uint8_t * inRName
,
21496 uint32_t inRefresh
,
21499 uint32_t inMinimumTTL
,
21504 DataBuffer rdataDB
;
21506 DataBuffer_Init( &rdataDB
, NULL
, 0, kDNSRecordDataLengthMax
);
21508 err
= _AppendSOARecordData( &rdataDB
, inMName
, inRName
, inSerial
, inRefresh
, inRetry
, inExpire
, inMinimumTTL
, NULL
);
21509 require_noerr( err
, exit
);
21511 err
= DataBuffer_Detach( &rdataDB
, outPtr
, outLen
);
21512 require_noerr( err
, exit
);
21515 DataBuffer_Free( &rdataDB
);
21519 //===========================================================================================================================
21520 // _DataBuffer_AppendDNSQuestion
21521 //===========================================================================================================================
21524 _DataBuffer_AppendDNSQuestion(
21526 const uint8_t * inNamePtr
,
21532 dns_fixed_fields_question fields
;
21534 err
= DataBuffer_Append( inDB
, inNamePtr
, inNameLen
);
21535 require_noerr( err
, exit
);
21537 dns_fixed_fields_question_init( &fields
, inType
, inClass
);
21538 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
21539 require_noerr( err
, exit
);
21545 //===========================================================================================================================
21546 // _DataBuffer_AppendDNSRecord
21547 //===========================================================================================================================
21550 _DataBuffer_AppendDNSRecord(
21552 const uint8_t * inNamePtr
,
21557 const uint8_t * inRDataPtr
,
21558 size_t inRDataLen
)
21561 dns_fixed_fields_record fields
;
21563 require_action_quiet( inRDataLen
< kDNSRecordDataLengthMax
, exit
, err
= kSizeErr
);
21565 err
= DataBuffer_Append( inDB
, inNamePtr
, inNameLen
);
21566 require_noerr( err
, exit
);
21568 dns_fixed_fields_record_init( &fields
, inType
, inClass
, inTTL
, (uint16_t) inRDataLen
);
21569 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
21570 require_noerr( err
, exit
);
21574 err
= DataBuffer_Append( inDB
, inRDataPtr
, inRDataLen
);
21575 require_noerr( err
, exit
);
21582 //===========================================================================================================================
21583 // _NanoTime64ToTimestamp
21584 //===========================================================================================================================
21586 static char * _NanoTime64ToTimestamp( NanoTime64 inTime
, char *inBuf
, size_t inMaxLen
)
21590 NanoTimeToTimeVal( inTime
, &tv
);
21591 return( MakeFractionalDateString( &tv
, inBuf
, inMaxLen
) );
21594 //===========================================================================================================================
21595 // _MDNSInterfaceListCreate
21596 //===========================================================================================================================
21598 static Boolean
_MDNSInterfaceIsBlacklisted( SocketRef inInfoSock
, const char *inIfName
);
21600 static OSStatus
_MDNSInterfaceListCreate( MDNSInterfaceSubset inSubset
, size_t inItemSize
, MDNSInterfaceItem
**outList
)
21603 struct ifaddrs
* ifaList
;
21604 const struct ifaddrs
* ifa
;
21605 MDNSInterfaceItem
* interfaceList
;
21606 MDNSInterfaceItem
** ptr
;
21607 SocketRef infoSock
;
21610 interfaceList
= NULL
;
21611 infoSock
= kInvalidSocketRef
;
21612 if( inItemSize
== 0 ) inItemSize
= sizeof( MDNSInterfaceItem
);
21613 require_action_quiet( inItemSize
>= sizeof( MDNSInterfaceItem
), exit
, err
= kSizeErr
);
21615 infoSock
= socket( AF_INET
, SOCK_DGRAM
, 0 );
21616 err
= map_socket_creation_errno( infoSock
);
21617 require_noerr( err
, exit
);
21619 err
= getifaddrs( &ifaList
);
21620 err
= map_global_noerr_errno( err
);
21621 require_noerr( err
, exit
);
21623 ptr
= &interfaceList
;
21624 for( ifa
= ifaList
; ifa
; ifa
= ifa
->ifa_next
)
21626 MDNSInterfaceItem
* item
;
21628 const unsigned int flagsMask
= IFF_UP
| IFF_MULTICAST
| IFF_POINTOPOINT
;
21629 const unsigned int flagsNeeded
= IFF_UP
| IFF_MULTICAST
;
21631 if( ( ifa
->ifa_flags
& flagsMask
) != flagsNeeded
) continue;
21632 if( !ifa
->ifa_addr
|| !ifa
->ifa_name
) continue;
21633 family
= ifa
->ifa_addr
->sa_family
;
21634 if( ( family
!= AF_INET
) && ( family
!= AF_INET6
) ) continue;
21636 for( item
= interfaceList
; item
&& ( strcmp( item
->ifName
, ifa
->ifa_name
) != 0 ); item
= item
->next
) {}
21639 NetTransportType type
;
21641 const char * const ifName
= ifa
->ifa_name
;
21643 if( _MDNSInterfaceIsBlacklisted( infoSock
, ifName
) ) continue;
21644 err
= SocketGetInterfaceInfo( infoSock
, ifName
, NULL
, &ifIndex
, NULL
, NULL
, NULL
, NULL
, NULL
, &type
);
21645 require_noerr( err
, exit
);
21647 if( ifIndex
== 0 ) continue;
21648 if( type
== kNetTransportType_AWDL
)
21650 if( inSubset
== kMDNSInterfaceSubset_NonAWDL
) continue;
21654 if( inSubset
== kMDNSInterfaceSubset_AWDL
) continue;
21656 item
= (MDNSInterfaceItem
*) calloc( 1, inItemSize
);
21657 require_action( item
, exit
, err
= kNoMemoryErr
);
21662 item
->ifName
= strdup( ifName
);
21663 require_action( item
->ifName
, exit
, err
= kNoMemoryErr
);
21665 item
->ifIndex
= ifIndex
;
21666 if( type
== kNetTransportType_AWDL
) item
->isAWDL
= true;
21667 else if( type
== kNetTransportType_WiFi
) item
->isWiFi
= true;
21669 if( family
== AF_INET
) item
->hasIPv4
= true;
21670 else item
->hasIPv6
= true;
21672 require_action_quiet( interfaceList
, exit
, err
= kNotFoundErr
);
21676 *outList
= interfaceList
;
21677 interfaceList
= NULL
;
21681 if( ifaList
) freeifaddrs( ifaList
);
21682 _MDNSInterfaceListFree( interfaceList
);
21683 ForgetSocket( &infoSock
);
21687 static Boolean
_MDNSInterfaceIsBlacklisted( SocketRef inInfoSock
, const char *inIfName
)
21691 static const char * const kMDNSInterfacePrefixBlacklist
[] = { "llw" };
21694 // Check if the interface name's prefix matches the prefix blacklist.
21696 for( i
= 0; i
< (int) countof( kMDNSInterfacePrefixBlacklist
); ++i
)
21698 const char * const prefix
= kMDNSInterfacePrefixBlacklist
[ i
];
21700 if( strcmp_prefix( inIfName
, prefix
) == 0 )
21702 const char * ptr
= &inIfName
[ strlen( prefix
) ];
21704 while( isdigit_safe( *ptr
) ) ++ptr
;
21705 if( *ptr
== '\0' ) return( true );
21709 // Check if the interface is used for inter-(co)processor networking.
21711 memset( &ifr
, 0, sizeof( ifr
) );
21712 strlcpy( ifr
.ifr_name
, inIfName
, sizeof( ifr
.ifr_name
) );
21713 err
= ioctl( inInfoSock
, SIOCGIFFUNCTIONALTYPE
, &ifr
);
21714 err
= map_global_value_errno( err
!= -1, err
);
21715 if( !err
&& ( ifr
.ifr_functional_type
== IFRTYPE_FUNCTIONAL_INTCOPROC
) ) return( true );
21720 //===========================================================================================================================
21721 // _MDNSInterfaceListFree
21722 //===========================================================================================================================
21724 static void _MDNSInterfaceListFree( MDNSInterfaceItem
*inList
)
21726 MDNSInterfaceItem
* item
;
21728 while( ( item
= inList
) != NULL
)
21730 inList
= item
->next
;
21731 FreeNullSafe( item
->ifName
);
21736 //===========================================================================================================================
21737 // _MDNSInterfaceGetAny
21738 //===========================================================================================================================
21740 static OSStatus
_MDNSInterfaceGetAny( MDNSInterfaceSubset inSubset
, char inNameBuf
[ IF_NAMESIZE
+ 1 ], uint32_t *outIndex
)
21743 MDNSInterfaceItem
* list
;
21744 const MDNSInterfaceItem
* item
;
21747 err
= _MDNSInterfaceListCreate( inSubset
, 0, &list
);
21748 require_noerr_quiet( err
, exit
);
21749 require_action_quiet( list
, exit
, err
= kNotFoundErr
);
21751 for( item
= list
; item
; item
= item
->next
)
21753 if( item
->hasIPv4
&& item
->hasIPv6
) break;
21755 if( !item
) item
= list
;
21756 if( inNameBuf
) strlcpy( inNameBuf
, item
->ifName
, IF_NAMESIZE
+ 1 );
21757 if( outIndex
) *outIndex
= item
->ifIndex
;
21760 _MDNSInterfaceListFree( list
);
21764 //===========================================================================================================================
21765 // _SetComputerName
21766 //===========================================================================================================================
21768 static OSStatus
_SetComputerName( CFStringRef inComputerName
, CFStringEncoding inEncoding
)
21771 SCPreferencesRef prefs
;
21774 prefs
= SCPreferencesCreateWithAuthorization( NULL
, CFSTR( kDNSSDUtilIdentifier
), NULL
, NULL
);
21775 err
= map_scerror( prefs
);
21776 require_noerr_quiet( err
, exit
);
21778 ok
= SCPreferencesSetComputerName( prefs
, inComputerName
, inEncoding
);
21779 err
= map_scerror( ok
);
21780 require_noerr_quiet( err
, exit
);
21782 ok
= SCPreferencesCommitChanges( prefs
);
21783 err
= map_scerror( ok
);
21784 require_noerr_quiet( err
, exit
);
21786 ok
= SCPreferencesApplyChanges( prefs
);
21787 err
= map_scerror( ok
);
21788 require_noerr_quiet( err
, exit
);
21791 CFReleaseNullSafe( prefs
);
21795 //===========================================================================================================================
21796 // _SetComputerNameWithUTF8CString
21797 //===========================================================================================================================
21799 static OSStatus
_SetComputerNameWithUTF8CString( const char *inComputerName
)
21802 CFStringRef computerName
;
21804 computerName
= CFStringCreateWithCString( NULL
, inComputerName
, kCFStringEncodingUTF8
);
21805 require_action( computerName
, exit
, err
= kNoMemoryErr
);
21807 err
= _SetComputerName( computerName
, kCFStringEncodingUTF8
);
21808 require_noerr_quiet( err
, exit
);
21811 CFReleaseNullSafe( computerName
);
21815 //===========================================================================================================================
21816 // _SetLocalHostName
21817 //===========================================================================================================================
21819 static OSStatus
_SetLocalHostName( CFStringRef inLocalHostName
)
21822 SCPreferencesRef prefs
;
21825 prefs
= SCPreferencesCreateWithAuthorization( NULL
, CFSTR( kDNSSDUtilIdentifier
), NULL
, NULL
);
21826 err
= map_scerror( prefs
);
21827 require_noerr_quiet( err
, exit
);
21829 ok
= SCPreferencesSetLocalHostName( prefs
, inLocalHostName
);
21830 err
= map_scerror( ok
);
21831 require_noerr_quiet( err
, exit
);
21833 ok
= SCPreferencesCommitChanges( prefs
);
21834 err
= map_scerror( ok
);
21835 require_noerr_quiet( err
, exit
);
21837 ok
= SCPreferencesApplyChanges( prefs
);
21838 err
= map_scerror( ok
);
21839 require_noerr_quiet( err
, exit
);
21842 CFReleaseNullSafe( prefs
);
21846 //===========================================================================================================================
21847 // _SetLocalHostNameWithUTF8CString
21848 //===========================================================================================================================
21850 static OSStatus
_SetLocalHostNameWithUTF8CString( const char *inLocalHostName
)
21853 CFStringRef localHostName
;
21855 localHostName
= CFStringCreateWithCString( NULL
, inLocalHostName
, kCFStringEncodingUTF8
);
21856 require_action( localHostName
, exit
, err
= kNoMemoryErr
);
21858 err
= _SetLocalHostName( localHostName
);
21859 require_noerr_quiet( err
, exit
);
21862 CFReleaseNullSafe( localHostName
);
21866 //===========================================================================================================================
21867 // MDNSColliderCreate
21868 //===========================================================================================================================
21872 kMDNSColliderOpCode_Invalid
= 0,
21873 kMDNSColliderOpCode_Send
= 1,
21874 kMDNSColliderOpCode_Wait
= 2,
21875 kMDNSColliderOpCode_SetProbeActions
= 3,
21876 kMDNSColliderOpCode_LoopPush
= 4,
21877 kMDNSColliderOpCode_LoopPop
= 5,
21878 kMDNSColliderOpCode_Exit
= 6
21880 } MDNSColliderOpCode
;
21884 MDNSColliderOpCode opcode
;
21887 } MDNSCInstruction
;
21889 #define kMaxLoopDepth 16
21891 struct MDNSColliderPrivate
21893 CFRuntimeBase base
; // CF object base.
21894 dispatch_queue_t queue
; // Queue for collider's events.
21895 dispatch_source_t readSourceV4
; // Read dispatch source for IPv4 socket.
21896 dispatch_source_t readSourceV6
; // Read dispatch source for IPv6 socket.
21897 SocketRef sockV4
; // IPv4 UDP socket for mDNS.
21898 SocketRef sockV6
; // IPv6 UDP socket for mDNS.
21899 uint8_t * target
; // Record name being targeted. (malloced)
21900 uint8_t * responsePtr
; // Response message pointer. (malloced)
21901 size_t responseLen
; // Response message length.
21902 uint8_t * probePtr
; // Probe query message pointer. (malloced)
21903 size_t probeLen
; // Probe query message length.
21904 unsigned int probeCount
; // Count of probe queries received for collider's record.
21905 uint32_t probeActionMap
; // Bitmap of actions to take for
21906 MDNSCInstruction
* program
; // Program to execute.
21907 uint32_t pc
; // Program's program counter.
21908 uint32_t loopCounts
[ kMaxLoopDepth
]; // Stack of loop counters.
21909 uint32_t loopDepth
; // Current loop depth.
21910 dispatch_source_t waitTimer
; // Timer for program's wait commands.
21911 uint32_t interfaceIndex
; // Interface over which to send and receive mDNS msgs.
21912 MDNSColliderStopHandler_f stopHandler
; // User's stop handler.
21913 void * stopContext
; // User's stop handler context.
21914 MDNSColliderProtocols protocols
; // Protocols to use, i.e., IPv4, IPv6.
21915 Boolean stopped
; // True if the collider has been stopped.
21916 uint8_t msgBuf
[ kMDNSMessageSizeMax
]; // mDNS message buffer.
21919 static void _MDNSColliderStop( MDNSColliderRef inCollider
, OSStatus inError
);
21920 static void _MDNSColliderReadHandler( void *inContext
);
21921 static void _MDNSColliderExecuteProgram( void *inContext
);
21922 static OSStatus
_MDNSColliderSendResponse( MDNSColliderRef inCollider
, SocketRef inSock
, const struct sockaddr
*inDest
);
21923 static OSStatus
_MDNSColliderSendProbe( MDNSColliderRef inCollider
, SocketRef inSock
, const struct sockaddr
*inDest
);
21925 CF_CLASS_DEFINE( MDNSCollider
);
21927 ulog_define_ex( kDNSSDUtilIdentifier
, MDNSCollider
, kLogLevelInfo
, kLogFlags_None
, "MDNSCollider", NULL
);
21928 #define mc_ulog( LEVEL, ... ) ulog( &log_category_from_name( MDNSCollider ), (LEVEL), __VA_ARGS__ )
21930 static OSStatus
MDNSColliderCreate( dispatch_queue_t inQueue
, MDNSColliderRef
*outCollider
)
21933 MDNSColliderRef obj
= NULL
;
21935 CF_OBJECT_CREATE( MDNSCollider
, obj
, err
, exit
);
21937 ReplaceDispatchQueue( &obj
->queue
, inQueue
);
21938 obj
->sockV4
= kInvalidSocketRef
;
21939 obj
->sockV6
= kInvalidSocketRef
;
21941 *outCollider
= obj
;
21948 //===========================================================================================================================
21949 // _MDNSColliderFinalize
21950 //===========================================================================================================================
21952 static void _MDNSColliderFinalize( CFTypeRef inObj
)
21954 MDNSColliderRef
const me
= (MDNSColliderRef
) inObj
;
21956 check( !me
->waitTimer
);
21957 check( !me
->readSourceV4
);
21958 check( !me
->readSourceV6
);
21959 check( !IsValidSocket( me
->sockV4
) );
21960 check( !IsValidSocket( me
->sockV6
) );
21961 ForgetMem( &me
->target
);
21962 ForgetMem( &me
->responsePtr
);
21963 ForgetMem( &me
->probePtr
);
21964 ForgetMem( &me
->program
);
21965 dispatch_forget( &me
->queue
);
21968 //===========================================================================================================================
21969 // MDNSColliderStart
21970 //===========================================================================================================================
21972 static void _MDNSColliderStart( void *inContext
);
21974 static OSStatus
MDNSColliderStart( MDNSColliderRef me
)
21978 require_action_quiet( me
->target
, exit
, err
= kNotPreparedErr
);
21979 require_action_quiet( me
->responsePtr
, exit
, err
= kNotPreparedErr
);
21980 require_action_quiet( me
->probePtr
, exit
, err
= kNotPreparedErr
);
21981 require_action_quiet( me
->program
, exit
, err
= kNotPreparedErr
);
21982 require_action_quiet( me
->interfaceIndex
, exit
, err
= kNotPreparedErr
);
21983 require_action_quiet( me
->protocols
, exit
, err
= kNotPreparedErr
);
21986 dispatch_async_f( me
->queue
, me
, _MDNSColliderStart
);
21993 static void _MDNSColliderStart( void *inContext
)
21996 MDNSColliderRef
const me
= (MDNSColliderRef
) inContext
;
21997 SocketRef sock
= kInvalidSocketRef
;
21998 SocketContext
* sockCtx
= NULL
;
22000 if( me
->protocols
& kMDNSColliderProtocol_IPv4
)
22002 err
= CreateMulticastSocket( GetMDNSMulticastAddrV4(), kMDNSPort
, NULL
, me
->interfaceIndex
, true, NULL
, &sock
);
22003 require_noerr( err
, exit
);
22005 err
= SocketContextCreate( sock
, me
, &sockCtx
);
22006 require_noerr( err
, exit
);
22007 sock
= kInvalidSocketRef
;
22009 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _MDNSColliderReadHandler
, SocketContextCancelHandler
,
22010 sockCtx
, &me
->readSourceV4
);
22011 require_noerr( err
, exit
);
22012 me
->sockV4
= sockCtx
->sock
;
22015 dispatch_resume( me
->readSourceV4
);
22018 if( me
->protocols
& kMDNSColliderProtocol_IPv6
)
22020 err
= CreateMulticastSocket( GetMDNSMulticastAddrV6(), kMDNSPort
, NULL
, me
->interfaceIndex
, true, NULL
, &sock
);
22021 require_noerr( err
, exit
);
22023 err
= SocketContextCreate( sock
, me
, &sockCtx
);
22024 require_noerr( err
, exit
);
22025 sock
= kInvalidSocketRef
;
22027 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _MDNSColliderReadHandler
, SocketContextCancelHandler
,
22028 sockCtx
, &me
->readSourceV6
);
22029 require_noerr( err
, exit
);
22030 me
->sockV6
= sockCtx
->sock
;
22033 dispatch_resume( me
->readSourceV6
);
22036 _MDNSColliderExecuteProgram( me
);
22040 ForgetSocket( &sock
);
22041 ForgetSocketContext( &sockCtx
);
22042 if( err
) _MDNSColliderStop( me
, err
);
22045 //===========================================================================================================================
22046 // MDNSColliderStop
22047 //===========================================================================================================================
22049 static void _MDNSColliderUserStop( void *inContext
);
22051 static void MDNSColliderStop( MDNSColliderRef me
)
22054 dispatch_async_f( me
->queue
, me
, _MDNSColliderUserStop
);
22057 static void _MDNSColliderUserStop( void *inContext
)
22059 MDNSColliderRef
const me
= (MDNSColliderRef
) inContext
;
22061 _MDNSColliderStop( me
, kCanceledErr
);
22065 //===========================================================================================================================
22066 // MDNSColliderSetProtocols
22067 //===========================================================================================================================
22069 static void MDNSColliderSetProtocols( MDNSColliderRef me
, MDNSColliderProtocols inProtocols
)
22071 me
->protocols
= inProtocols
;
22074 //===========================================================================================================================
22075 // MDNSColliderSetInterfaceIndex
22076 //===========================================================================================================================
22078 static void MDNSColliderSetInterfaceIndex( MDNSColliderRef me
, uint32_t inInterfaceIndex
)
22080 me
->interfaceIndex
= inInterfaceIndex
;
22083 //===========================================================================================================================
22084 // MDNSColliderSetProgram
22085 //===========================================================================================================================
22087 #define kMDNSColliderProgCmd_Done "done"
22088 #define kMDNSColliderProgCmd_Loop "loop"
22089 #define kMDNSColliderProgCmd_Send "send"
22090 #define kMDNSColliderProgCmd_Probes "probes"
22091 #define kMDNSColliderProgCmd_Wait "wait"
22093 typedef uint32_t MDNSColliderProbeAction
;
22095 #define kMDNSColliderProbeAction_None 0
22096 #define kMDNSColliderProbeAction_Respond 1
22097 #define kMDNSColliderProbeAction_RespondUnicast 2
22098 #define kMDNSColliderProbeAction_RespondMulticast 3
22099 #define kMDNSColliderProbeAction_Probe 4
22100 #define kMDNSColliderProbeAction_MaxValue kMDNSColliderProbeAction_Probe
22102 #define kMDNSColliderProbeActionBits_Count 3
22103 #define kMDNSColliderProbeActionBits_Mask ( ( 1U << kMDNSColliderProbeActionBits_Count ) - 1 )
22104 #define kMDNSColliderProbeActionMaxProbeCount ( 32 / kMDNSColliderProbeActionBits_Count )
22106 check_compile_time( kMDNSColliderProbeAction_MaxValue
<= kMDNSColliderProbeActionBits_Mask
);
22108 static OSStatus
_MDNSColliderParseProbeActionString( const char *inString
, size_t inLen
, uint32_t *outBitmap
);
22110 static OSStatus
MDNSColliderSetProgram( MDNSColliderRef me
, const char *inProgramStr
)
22114 unsigned int loopDepth
;
22118 MDNSCInstruction
* program
= NULL
;
22119 uint32_t loopStart
[ kMaxLoopDepth
];
22122 for( cmd
= inProgramStr
; *cmd
; cmd
= next
)
22124 for( end
= cmd
; *end
&& ( *end
!= ';' ); ++end
) {}
22125 require_action_quiet( end
!= cmd
, exit
, err
= kMalformedErr
);
22126 next
= ( *end
== ';' ) ? ( end
+ 1 ) : end
;
22130 program
= (MDNSCInstruction
*) calloc( insCount
+ 1, sizeof( *program
) );
22131 require_action( program
, exit
, err
= kNoMemoryErr
);
22135 for( cmd
= inProgramStr
; *cmd
; cmd
= next
)
22142 MDNSCInstruction
* const ins
= &program
[ insCount
];
22144 while( isspace_safe( *cmd
) ) ++cmd
;
22145 for( end
= cmd
; *end
&& ( *end
!= ';' ); ++end
) {}
22146 next
= ( *end
== ';' ) ? ( end
+ 1 ) : end
;
22148 for( ptr
= cmd
; ( ptr
< end
) && !isspace_safe( *ptr
); ++ptr
) {}
22149 cmdLen
= (size_t)( ptr
- cmd
);
22153 if( strnicmpx( cmd
, cmdLen
, kMDNSColliderProgCmd_Done
) == 0 )
22155 while( ( ptr
< end
) && isspace_safe( *ptr
) ) ++ptr
;
22156 require_action_quiet( ptr
== end
, exit
, err
= kMalformedErr
);
22158 require_action_quiet( loopDepth
> 0, exit
, err
= kMalformedErr
);
22160 ins
->opcode
= kMDNSColliderOpCode_LoopPop
;
22161 ins
->operand
= loopStart
[ --loopDepth
];
22166 else if( strnicmpx( cmd
, cmdLen
, kMDNSColliderProgCmd_Loop
) == 0 )
22168 for( arg
= ptr
; ( arg
< end
) && isspace_safe( *arg
); ++arg
) {}
22169 err
= DecimalTextToUInt32( arg
, end
, &value
, &ptr
);
22170 require_noerr_quiet( err
, exit
);
22171 require_action_quiet( value
> 0, exit
, err
= kValueErr
);
22173 while( ( ptr
< end
) && isspace_safe( *ptr
) ) ++ptr
;
22174 require_action_quiet( ptr
== end
, exit
, err
= kMalformedErr
);
22176 ins
->opcode
= kMDNSColliderOpCode_LoopPush
;
22177 ins
->operand
= value
;
22179 require_action_quiet( loopDepth
< kMaxLoopDepth
, exit
, err
= kNoSpaceErr
);
22180 loopStart
[ loopDepth
++ ] = insCount
+ 1;
22185 else if( strnicmpx( cmd
, cmdLen
, kMDNSColliderProgCmd_Probes
) == 0 )
22187 for( arg
= ptr
; ( arg
< end
) && isspace_safe( *arg
); ++arg
) {}
22188 for( ptr
= arg
; ( ptr
< end
) && !isspace_safe( *ptr
); ++ptr
) {}
22189 argLen
= (size_t)( ptr
- arg
);
22192 err
= _MDNSColliderParseProbeActionString( arg
, argLen
, &value
);
22193 require_noerr_quiet( err
, exit
);
22200 while( ( ptr
< end
) && isspace_safe( *ptr
) ) ++ptr
;
22201 require_action_quiet( ptr
== end
, exit
, err
= kMalformedErr
);
22203 ins
->opcode
= kMDNSColliderOpCode_SetProbeActions
;
22204 ins
->operand
= value
;
22209 else if( strnicmpx( cmd
, cmdLen
, kMDNSColliderProgCmd_Send
) == 0 )
22211 while( ( ptr
< end
) && isspace_safe( *ptr
) ) ++ptr
;
22212 require_action_quiet( ptr
== end
, exit
, err
= kMalformedErr
);
22214 ins
->opcode
= kMDNSColliderOpCode_Send
;
22219 else if( strnicmpx( cmd
, cmdLen
, kMDNSColliderProgCmd_Wait
) == 0 )
22221 for( arg
= ptr
; ( arg
< end
) && isspace_safe( *arg
); ++arg
) {}
22222 err
= DecimalTextToUInt32( arg
, end
, &value
, &ptr
);
22223 require_noerr_quiet( err
, exit
);
22225 while( ( ptr
< end
) && isspace_safe( *ptr
) ) ++ptr
;
22226 require_action_quiet( ptr
== end
, exit
, err
= kMalformedErr
);
22228 ins
->opcode
= kMDNSColliderOpCode_Wait
;
22229 ins
->operand
= value
;
22232 // Unrecognized command
22241 require_action_quiet( loopDepth
== 0, exit
, err
= kMalformedErr
);
22243 program
[ insCount
].opcode
= kMDNSColliderOpCode_Exit
;
22245 FreeNullSafe( me
->program
);
22246 me
->program
= program
;
22251 FreeNullSafe( program
);
22255 static OSStatus
_MDNSColliderParseProbeActionString( const char *inString
, size_t inLen
, uint32_t *outBitmap
)
22259 const char * const end
= &inString
[ inLen
];
22269 MDNSColliderProbeAction action
;
22272 if( isdigit_safe( c
) )
22277 count
= ( count
* 10 ) + ( c
- '0' );
22278 require_action_quiet( count
<= ( kMDNSColliderProbeActionMaxProbeCount
- index
), exit
, err
= kCountErr
);
22279 require_action_quiet( ptr
< end
, exit
, err
= kUnderrunErr
);
22282 } while( isdigit_safe( c
) );
22283 require_action_quiet( count
> 0, exit
, err
= kCountErr
);
22287 require_action_quiet( index
< kMDNSColliderProbeActionMaxProbeCount
, exit
, err
= kMalformedErr
);
22293 case 'n': action
= kMDNSColliderProbeAction_None
; break;
22294 case 'r': action
= kMDNSColliderProbeAction_Respond
; break;
22295 case 'u': action
= kMDNSColliderProbeAction_RespondUnicast
; break;
22296 case 'm': action
= kMDNSColliderProbeAction_RespondMulticast
; break;
22297 case 'p': action
= kMDNSColliderProbeAction_Probe
; break;
22298 default: err
= kMalformedErr
; goto exit
;
22303 require_action_quiet( ( c
== '-' ) && ( ptr
< end
), exit
, err
= kMalformedErr
);
22305 while( count
-- > 0 )
22307 bitmap
|= ( action
<< ( index
* kMDNSColliderProbeActionBits_Count
) );
22312 *outBitmap
= bitmap
;
22319 //===========================================================================================================================
22320 // MDNSColliderSetStopHandler
22321 //===========================================================================================================================
22323 static void MDNSColliderSetStopHandler( MDNSColliderRef me
, MDNSColliderStopHandler_f inStopHandler
, void *inStopContext
)
22325 me
->stopHandler
= inStopHandler
;
22326 me
->stopContext
= inStopContext
;
22329 //===========================================================================================================================
22330 // MDNSColliderSetRecord
22331 //===========================================================================================================================
22333 #define kMDNSColliderDummyStr "\x16" "mdnscollider-sent-this" "\x05" "local"
22334 #define kMDNSColliderDummyName ( (const uint8_t *) kMDNSColliderDummyStr )
22335 #define kMDNSColliderDummyNameLen sizeof( kMDNSColliderDummyStr )
22338 MDNSColliderSetRecord(
22339 MDNSColliderRef me
,
22340 const uint8_t * inName
,
22342 const void * inRDataPtr
,
22343 size_t inRDataLen
)
22348 uint8_t * targetPtr
= NULL
;
22350 uint8_t * responsePtr
= NULL
;
22351 size_t responseLen
;
22352 uint8_t * probePtr
= NULL
;
22355 DataBuffer_Init( &msgDB
, NULL
, 0, kMDNSMessageSizeMax
);
22357 err
= DomainNameDup( inName
, &targetPtr
, &targetLen
);
22358 require_noerr_quiet( err
, exit
);
22360 // Create response message.
22362 memset( &header
, 0, sizeof( header
) );
22363 DNSHeaderSetFlags( &header
, kDNSHeaderFlag_Response
| kDNSHeaderFlag_AuthAnswer
);
22364 DNSHeaderSetAnswerCount( &header
, 1 );
22366 err
= DataBuffer_Append( &msgDB
, &header
, sizeof( header
) );
22367 require_noerr( err
, exit
);
22369 err
= _DataBuffer_AppendDNSRecord( &msgDB
, targetPtr
, targetLen
, inType
, kDNSServiceClass_IN
| kRRClassCacheFlushBit
,
22370 1976, inRDataPtr
, inRDataLen
);
22371 require_noerr( err
, exit
);
22373 err
= DataBuffer_Detach( &msgDB
, &responsePtr
, &responseLen
);
22374 require_noerr( err
, exit
);
22376 // Create probe message.
22378 memset( &header
, 0, sizeof( header
) );
22379 DNSHeaderSetQuestionCount( &header
, 2 );
22380 DNSHeaderSetAuthorityCount( &header
, 1 );
22382 err
= DataBuffer_Append( &msgDB
, &header
, sizeof( header
) );
22383 require_noerr( err
, exit
);
22385 err
= _DataBuffer_AppendDNSQuestion( &msgDB
, targetPtr
, targetLen
, kDNSServiceType_ANY
, kDNSServiceClass_IN
);
22386 require_noerr( err
, exit
);
22388 err
= _DataBuffer_AppendDNSQuestion( &msgDB
, kMDNSColliderDummyName
, kMDNSColliderDummyNameLen
,
22389 kDNSServiceType_NULL
, kDNSServiceClass_IN
);
22390 require_noerr( err
, exit
);
22392 err
= _DataBuffer_AppendDNSRecord( &msgDB
, targetPtr
, targetLen
, inType
, kDNSServiceClass_IN
,
22393 1976, inRDataPtr
, inRDataLen
);
22394 require_noerr( err
, exit
);
22396 err
= DataBuffer_Detach( &msgDB
, &probePtr
, &probeLen
);
22397 require_noerr( err
, exit
);
22399 FreeNullSafe( me
->target
);
22400 me
->target
= targetPtr
;
22403 FreeNullSafe( me
->responsePtr
);
22404 me
->responsePtr
= responsePtr
;
22405 me
->responseLen
= responseLen
;
22406 responsePtr
= NULL
;
22408 FreeNullSafe( me
->probePtr
);
22409 me
->probePtr
= probePtr
;
22410 me
->probeLen
= probeLen
;
22414 DataBuffer_Free( &msgDB
);
22415 FreeNullSafe( targetPtr
);
22416 FreeNullSafe( responsePtr
);
22417 FreeNullSafe( probePtr
);
22421 //===========================================================================================================================
22422 // _MDNSColliderStop
22423 //===========================================================================================================================
22425 static void _MDNSColliderStop( MDNSColliderRef me
, OSStatus inError
)
22427 dispatch_source_forget( &me
->waitTimer
);
22428 dispatch_source_forget( &me
->readSourceV4
);
22429 dispatch_source_forget( &me
->readSourceV6
);
22430 me
->sockV4
= kInvalidSocketRef
;
22431 me
->sockV6
= kInvalidSocketRef
;
22435 me
->stopped
= true;
22436 if( me
->stopHandler
) me
->stopHandler( me
->stopContext
, inError
);
22441 //===========================================================================================================================
22442 // _MDNSColliderReadHandler
22443 //===========================================================================================================================
22445 static MDNSColliderProbeAction
_MDNSColliderGetProbeAction( uint32_t inBitmap
, unsigned int inProbeNumber
);
22446 static const char * _MDNSColliderProbeActionToString( MDNSColliderProbeAction inAction
);
22448 static void _MDNSColliderReadHandler( void *inContext
)
22451 struct timeval now
;
22452 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
22453 MDNSColliderRef
const me
= (MDNSColliderRef
) sockCtx
->userContext
;
22455 sockaddr_ip sender
;
22456 const DNSHeader
* hdr
;
22457 const uint8_t * ptr
;
22458 const struct sockaddr
* dest
;
22459 int probeFound
, probeIsQU
;
22460 unsigned int qCount
, i
;
22461 MDNSColliderProbeAction action
;
22463 gettimeofday( &now
, NULL
);
22465 err
= SocketRecvFrom( sockCtx
->sock
, me
->msgBuf
, sizeof( me
->msgBuf
), &msgLen
, &sender
, sizeof( sender
),
22466 NULL
, NULL
, NULL
, NULL
);
22467 require_noerr( err
, exit
);
22469 require_quiet( msgLen
>= kDNSHeaderLength
, exit
);
22470 hdr
= (const DNSHeader
*) me
->msgBuf
;
22472 probeFound
= false;
22474 qCount
= DNSHeaderGetQuestionCount( hdr
);
22475 ptr
= (const uint8_t *) &hdr
[ 1 ];
22476 for( i
= 0; i
< qCount
; ++i
)
22478 uint16_t qtype
, qclass
;
22479 uint8_t qname
[ kDomainNameLengthMax
];
22481 err
= DNSMessageExtractQuestion( me
->msgBuf
, msgLen
, ptr
, qname
, &qtype
, &qclass
, &ptr
);
22482 require_noerr_quiet( err
, exit
);
22484 if( ( qtype
== kDNSServiceType_NULL
) && ( qclass
== kDNSServiceClass_IN
) &&
22485 DomainNameEqual( qname
, kMDNSColliderDummyName
) )
22487 probeFound
= false;
22491 if( qtype
!= kDNSServiceType_ANY
) continue;
22492 if( ( qclass
& ~kQClassUnicastResponseBit
) != kDNSServiceClass_IN
) continue;
22493 if( !DomainNameEqual( qname
, me
->target
) ) continue;
22498 probeIsQU
= ( qclass
& kQClassUnicastResponseBit
) ? true : false;
22501 require_quiet( probeFound
, exit
);
22504 action
= _MDNSColliderGetProbeAction( me
->probeActionMap
, me
->probeCount
);
22506 mc_ulog( kLogLevelInfo
, "Received probe from %##a at %{du:time} (action: %s):\n\n%#1{du:dnsmsg}",
22507 &sender
, &now
, _MDNSColliderProbeActionToString( action
), me
->msgBuf
, msgLen
);
22509 if( ( action
== kMDNSColliderProbeAction_Respond
) ||
22510 ( action
== kMDNSColliderProbeAction_RespondUnicast
) ||
22511 ( action
== kMDNSColliderProbeAction_RespondMulticast
) )
22513 if( ( ( action
== kMDNSColliderProbeAction_Respond
) && probeIsQU
) ||
22514 ( action
== kMDNSColliderProbeAction_RespondUnicast
) )
22518 else if( ( ( action
== kMDNSColliderProbeAction_Respond
) && !probeIsQU
) ||
22519 ( action
== kMDNSColliderProbeAction_RespondMulticast
) )
22521 dest
= ( sender
.sa
.sa_family
== AF_INET
) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
22524 err
= _MDNSColliderSendResponse( me
, sockCtx
->sock
, dest
);
22525 require_noerr( err
, exit
);
22527 else if( action
== kMDNSColliderProbeAction_Probe
)
22529 dest
= ( sender
.sa
.sa_family
== AF_INET
) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
22531 err
= _MDNSColliderSendProbe( me
, sockCtx
->sock
, dest
);
22532 require_noerr( err
, exit
);
22539 static MDNSColliderProbeAction
_MDNSColliderGetProbeAction( uint32_t inBitmap
, unsigned int inProbeNumber
)
22541 MDNSColliderProbeAction action
;
22543 if( ( inProbeNumber
>= 1 ) && ( inProbeNumber
<= kMDNSColliderProbeActionMaxProbeCount
) )
22545 action
= ( inBitmap
>> ( ( inProbeNumber
- 1 ) * kMDNSColliderProbeActionBits_Count
) ) &
22546 kMDNSColliderProbeActionBits_Mask
;
22550 action
= kMDNSColliderProbeAction_None
;
22555 static const char * _MDNSColliderProbeActionToString( MDNSColliderProbeAction inAction
)
22559 case kMDNSColliderProbeAction_None
: return( "None" );
22560 case kMDNSColliderProbeAction_Respond
: return( "Respond" );
22561 case kMDNSColliderProbeAction_RespondUnicast
: return( "Respond (unicast)" );
22562 case kMDNSColliderProbeAction_RespondMulticast
: return( "Respond (multicast)" );
22563 case kMDNSColliderProbeAction_Probe
: return( "Probe" );
22564 default: return( "???" );
22568 //===========================================================================================================================
22569 // _MDNSColliderExecuteProgram
22570 //===========================================================================================================================
22572 static void _MDNSColliderExecuteProgram( void *inContext
)
22575 MDNSColliderRef
const me
= (MDNSColliderRef
) inContext
;
22578 dispatch_forget( &me
->waitTimer
);
22583 const MDNSCInstruction
* const ins
= &me
->program
[ me
->pc
++ ];
22586 switch( ins
->opcode
)
22588 case kMDNSColliderOpCode_Send
:
22589 if( IsValidSocket( me
->sockV4
) )
22591 err
= _MDNSColliderSendResponse( me
, me
->sockV4
, GetMDNSMulticastAddrV4() );
22592 require_noerr( err
, exit
);
22594 if( IsValidSocket( me
->sockV6
) )
22596 err
= _MDNSColliderSendResponse( me
, me
->sockV6
, GetMDNSMulticastAddrV6() );
22597 require_noerr( err
, exit
);
22601 case kMDNSColliderOpCode_Wait
:
22602 waitMs
= ins
->operand
;
22605 err
= DispatchTimerOneShotCreate( dispatch_time_milliseconds( waitMs
), 1, me
->queue
,
22606 _MDNSColliderExecuteProgram
, me
, &me
->waitTimer
);
22607 require_noerr( err
, exit
);
22608 dispatch_resume( me
->waitTimer
);
22613 case kMDNSColliderOpCode_SetProbeActions
:
22614 me
->probeCount
= 0;
22615 me
->probeActionMap
= ins
->operand
;
22618 case kMDNSColliderOpCode_LoopPush
:
22619 check( me
->loopDepth
< kMaxLoopDepth
);
22620 me
->loopCounts
[ me
->loopDepth
++ ] = ins
->operand
;
22623 case kMDNSColliderOpCode_LoopPop
:
22624 check( me
->loopDepth
> 0 );
22625 if( --me
->loopCounts
[ me
->loopDepth
- 1 ] > 0 )
22627 me
->pc
= ins
->operand
;
22635 case kMDNSColliderOpCode_Exit
:
22641 dlogassert( "Unhandled opcode %u\n", ins
->opcode
);
22648 if( err
|| stop
) _MDNSColliderStop( me
, err
);
22651 //===========================================================================================================================
22652 // _MDNSColliderSendResponse
22653 //===========================================================================================================================
22655 static OSStatus
_MDNSColliderSendResponse( MDNSColliderRef me
, SocketRef inSock
, const struct sockaddr
*inDest
)
22660 n
= sendto( inSock
, (char *) me
->responsePtr
, me
->responseLen
, 0, inDest
, SockAddrGetSize( inDest
) );
22661 err
= map_socket_value_errno( inSock
, n
== (ssize_t
) me
->responseLen
, n
);
22665 //===========================================================================================================================
22666 // _MDNSColliderSendProbe
22667 //===========================================================================================================================
22669 static OSStatus
_MDNSColliderSendProbe( MDNSColliderRef me
, SocketRef inSock
, const struct sockaddr
*inDest
)
22674 n
= sendto( inSock
, (char *) me
->probePtr
, me
->probeLen
, 0, inDest
, SockAddrGetSize( inDest
) );
22675 err
= map_socket_value_errno( inSock
, n
== (ssize_t
) me
->probeLen
, n
);
22679 //===========================================================================================================================
22680 // ServiceBrowserCreate
22681 //===========================================================================================================================
22683 typedef struct SBDomain SBDomain
;
22684 typedef struct SBServiceType SBServiceType
;
22685 typedef struct SBServiceBrowse SBServiceBrowse
;
22686 typedef struct SBServiceInstance SBServiceInstance
;
22687 typedef struct SBIPAddress SBIPAddress
;
22689 struct ServiceBrowserPrivate
22691 CFRuntimeBase base
; // CF object base.
22692 dispatch_queue_t queue
; // Queue for service browser's events.
22693 DNSServiceRef connection
; // Shared connection for DNS-SD ops.
22694 DNSServiceRef domainsQuery
; // Query for recommended browsing domains.
22695 char * domain
; // If non-null, then browsing is limited to this domain.
22696 StringListItem
* serviceTypeList
; // If non-null, then browsing is limited to these service types.
22697 ServiceBrowserCallback_f userCallback
; // User's callback. Called when browsing stops.
22698 void * userContext
; // User's callback context.
22699 SBDomain
* domainList
; // List of domains and their browse results.
22700 dispatch_source_t stopTimer
; // Timer to stop browsing after browseTimeSecs.
22701 uint32_t ifIndex
; // If non-zero, then browsing is limited to this interface.
22702 unsigned int browseTimeSecs
; // Amount of time to spend browsing in seconds.
22703 Boolean includeAWDL
; // True if the IncludeAWDL flag should be used for DNS-SD ops that
22704 // use the "any" interface.
22709 SBDomain
* next
; // Next domain object in list.
22710 ServiceBrowserRef browser
; // Pointer to parent service browser.
22711 char * name
; // Name of the domain.
22712 DNSServiceRef servicesQuery
; // Query for services (_services._dns-sd._udp.<domain> PTR record) in domain.
22713 SBServiceType
* typeList
; // List of service types to browse for in this domain.
22716 struct SBServiceType
22718 SBServiceType
* next
; // Next service type object in list.
22719 char * name
; // Name of the service type.
22720 SBServiceBrowse
* browseList
; // List of browses for this service type.
22723 struct SBServiceBrowse
22725 SBServiceBrowse
* next
; // Next browse object in list.
22726 ServiceBrowserRef browser
; // Pointer to parent service browser.
22727 DNSServiceRef browse
; // Reference to DNSServiceBrowse op.
22728 SBServiceInstance
* instanceList
; // List of service instances that were discovered by this browse.
22729 uint64_t startTicks
; // Value of UpTicks() when the browse op began.
22730 uint32_t ifIndex
; // If non-zero, then the browse is limited to this interface.
22733 struct SBServiceInstance
22735 SBServiceInstance
* next
; // Next service instance object in list.
22736 ServiceBrowserRef browser
; // Pointer to parent service browser.
22737 char * name
; // Name of the service instance.
22738 char * fqdn
; // Fully qualified domain name of service instance (for logging/debugging).
22739 uint32_t ifIndex
; // Index of interface over which this service instance was discovered.
22740 uint64_t discoverTimeUs
; // Time it took to discover this service instance in microseconds.
22741 DNSServiceRef resolve
; // Reference to DNSServiceResolve op for this service instance.
22742 uint64_t resolveStartTicks
; // Value of UpTicks() when the DNSServiceResolve op began.
22743 uint64_t resolveTimeUs
; // Time it took to resolve this service instance.
22744 char * hostname
; // Service instance's hostname. Result of DNSServiceResolve.
22745 uint16_t port
; // Service instance's port number. Result of DNSServiceResolve.
22746 uint8_t * txtPtr
; // Service instance's TXT record data. Result of DNSServiceResolve.
22747 size_t txtLen
; // Length of service instance's TXT record data.
22748 DNSServiceRef getAddrInfo
; // Reference to DNSServiceGetAddrInfo op for service instance's hostname.
22749 uint64_t gaiStartTicks
; // Value of UpTicks() when the DNSServiceGetAddrInfo op began.
22750 SBIPAddress
* ipaddrList
; // List of IP addresses that the hostname resolved to.
22755 SBIPAddress
* next
; // Next IP address object in list.
22756 sockaddr_ip sip
; // IPv4 or IPv6 address.
22757 uint64_t resolveTimeUs
; // Time it took to resolve this IP address in microseconds.
22762 SBRDomain
* domainList
; // List of domains in which services were found.
22763 int32_t refCount
; // This object's reference count.
22765 } ServiceBrowserResultsPrivate
;
22767 static void _ServiceBrowserStop( ServiceBrowserRef me
, OSStatus inError
);
22768 static OSStatus
_ServiceBrowserAddDomain( ServiceBrowserRef inBrowser
, const char *inDomain
);
22769 static OSStatus
_ServiceBrowserRemoveDomain( ServiceBrowserRef inBrowser
, const char *inName
);
22770 static void _ServiceBrowserTimerHandler( void *inContext
);
22771 static void DNSSD_API
22772 _ServiceBrowserDomainsQueryCallback(
22773 DNSServiceRef inSDRef
,
22774 DNSServiceFlags inFlags
,
22775 uint32_t inInterfaceIndex
,
22776 DNSServiceErrorType inError
,
22777 const char * inFullName
,
22780 uint16_t inRDataLen
,
22781 const void * inRDataPtr
,
22783 void * inContext
);
22784 static void DNSSD_API
22785 _ServiceBrowserServicesQueryCallback(
22786 DNSServiceRef inSDRef
,
22787 DNSServiceFlags inFlags
,
22788 uint32_t inInterfaceIndex
,
22789 DNSServiceErrorType inError
,
22790 const char * inFullName
,
22793 uint16_t inRDataLen
,
22794 const void * inRDataPtr
,
22796 void * inContext
);
22797 static void DNSSD_API
22798 _ServiceBrowserBrowseCallback(
22799 DNSServiceRef inSDRef
,
22800 DNSServiceFlags inFlags
,
22801 uint32_t inInterfaceIndex
,
22802 DNSServiceErrorType inError
,
22803 const char * inName
,
22804 const char * inRegType
,
22805 const char * inDomain
,
22806 void * inContext
);
22807 static void DNSSD_API
22808 _ServiceBrowserResolveCallback(
22809 DNSServiceRef inSDRef
,
22810 DNSServiceFlags inFlags
,
22811 uint32_t inInterfaceIndex
,
22812 DNSServiceErrorType inError
,
22813 const char * inFullName
,
22814 const char * inHostname
,
22817 const unsigned char * inTXTPtr
,
22818 void * inContext
);
22819 static void DNSSD_API
22820 _ServiceBrowserGAICallback(
22821 DNSServiceRef inSDRef
,
22822 DNSServiceFlags inFlags
,
22823 uint32_t inInterfaceIndex
,
22824 DNSServiceErrorType inError
,
22825 const char * inHostname
,
22826 const struct sockaddr
* inSockAddr
,
22828 void * inContext
);
22830 _ServiceBrowserAddServiceType(
22831 ServiceBrowserRef inBrowser
,
22832 SBDomain
* inDomain
,
22833 const char * inName
,
22834 uint32_t inIfIndex
);
22836 _ServiceBrowserRemoveServiceType(
22837 ServiceBrowserRef inBrowser
,
22838 SBDomain
* inDomain
,
22839 const char * inName
,
22840 uint32_t inIfIndex
);
22842 _ServiceBrowserAddServiceInstance(
22843 ServiceBrowserRef inBrowser
,
22844 SBServiceBrowse
* inBrowse
,
22845 uint32_t inIfIndex
,
22846 const char * inName
,
22847 const char * inRegType
,
22848 const char * inDomain
,
22849 uint64_t inDiscoverTimeUs
);
22851 _ServiceBrowserRemoveServiceInstance(
22852 ServiceBrowserRef inBrowser
,
22853 SBServiceBrowse
* inBrowse
,
22854 const char * inName
,
22855 uint32_t inIfIndex
);
22857 _ServiceBrowserAddIPAddress(
22858 ServiceBrowserRef inBrowser
,
22859 SBServiceInstance
* inInstance
,
22860 const struct sockaddr
* inSockAddr
,
22861 uint64_t inResolveTimeUs
);
22863 _ServiceBrowserRemoveIPAddress(
22864 ServiceBrowserRef inBrowser
,
22865 SBServiceInstance
* inInstance
,
22866 const struct sockaddr
* inSockAddr
);
22867 static OSStatus
_ServiceBrowserCreateResults( ServiceBrowserRef me
, ServiceBrowserResults
**outResults
);
22868 static OSStatus
_SBDomainCreate( const char *inName
, ServiceBrowserRef inBrowser
, SBDomain
**outDomain
);
22869 static void _SBDomainFree( SBDomain
*inDomain
);
22870 static OSStatus
_SBServiceTypeCreate( const char *inName
, SBServiceType
**outType
);
22871 static void _SBServiceTypeFree( SBServiceType
*inType
);
22872 static OSStatus
_SBServiceBrowseCreate( uint32_t inIfIndex
, ServiceBrowserRef inBrowser
, SBServiceBrowse
**outBrowse
);
22873 static void _SBServiceBrowseFree( SBServiceBrowse
*inBrowse
);
22875 _SBServiceInstanceCreate(
22876 const char * inName
,
22877 const char * inType
,
22878 const char * inDomain
,
22879 uint32_t inIfIndex
,
22880 uint64_t inDiscoverTimeUs
,
22881 ServiceBrowserRef inBrowser
,
22882 SBServiceInstance
** outInstance
);
22883 static void _SBServiceInstanceFree( SBServiceInstance
*inInstance
);
22885 _SBIPAddressCreate(
22886 const struct sockaddr
* inSockAddr
,
22887 uint64_t inResolveTimeUs
,
22888 SBIPAddress
** outIPAddress
);
22889 static void _SBIPAddressFree( SBIPAddress
*inIPAddress
);
22890 static void _SBIPAddressFreeList( SBIPAddress
*inList
);
22891 static OSStatus
_SBRDomainCreate( const char *inName
, SBRDomain
**outDomain
);
22892 static void _SBRDomainFree( SBRDomain
*inDomain
);
22893 static OSStatus
_SBRServiceTypeCreate( const char *inName
, SBRServiceType
**outType
);
22894 static void _SBRServiceTypeFree( SBRServiceType
*inType
);
22896 _SBRServiceInstanceCreate(
22897 const char * inName
,
22898 uint32_t inInterfaceIndex
,
22899 const char * inHostname
,
22901 const uint8_t * inTXTPtr
,
22903 uint64_t inDiscoverTimeUs
,
22904 uint64_t inResolveTimeUs
,
22905 SBRServiceInstance
** outInstance
);
22906 static void _SBRServiceInstanceFree( SBRServiceInstance
*inInstance
);
22908 _SBRIPAddressCreate(
22909 const struct sockaddr
* inSockAddr
,
22910 uint64_t inResolveTimeUs
,
22911 SBRIPAddress
** outIPAddress
);
22912 static void _SBRIPAddressFree( SBRIPAddress
*inIPAddress
);
22914 #define ForgetSBIPAddressList( X ) ForgetCustom( X, _SBIPAddressFreeList )
22916 CF_CLASS_DEFINE( ServiceBrowser
);
22918 ulog_define_ex( kDNSSDUtilIdentifier
, ServiceBrowser
, kLogLevelTrace
, kLogFlags_None
, "ServiceBrowser", NULL
);
22919 #define sb_ulog( LEVEL, ... ) ulog( &log_category_from_name( ServiceBrowser ), (LEVEL), __VA_ARGS__ )
22922 ServiceBrowserCreate(
22923 dispatch_queue_t inQueue
,
22924 uint32_t inInterfaceIndex
,
22925 const char * inDomain
,
22926 unsigned int inBrowseTimeSecs
,
22927 Boolean inIncludeAWDL
,
22928 ServiceBrowserRef
* outBrowser
)
22931 ServiceBrowserRef obj
;
22933 CF_OBJECT_CREATE( ServiceBrowser
, obj
, err
, exit
);
22935 ReplaceDispatchQueue( &obj
->queue
, inQueue
);
22936 obj
->ifIndex
= inInterfaceIndex
;
22939 obj
->domain
= strdup( inDomain
);
22940 require_action( obj
->domain
, exit
, err
= kNoMemoryErr
);
22942 obj
->browseTimeSecs
= inBrowseTimeSecs
;
22943 obj
->includeAWDL
= inIncludeAWDL
;
22950 CFReleaseNullSafe( obj
);
22954 //===========================================================================================================================
22955 // _ServiceBrowserFinalize
22956 //===========================================================================================================================
22958 static void _ServiceBrowserFinalize( CFTypeRef inObj
)
22960 ServiceBrowserRef
const me
= (ServiceBrowserRef
) inObj
;
22961 StringListItem
* serviceType
;
22963 dispatch_forget( &me
->queue
);
22964 check( !me
->connection
);
22965 check( !me
->domainsQuery
);
22966 ForgetMem( &me
->domain
);
22967 while( ( serviceType
= me
->serviceTypeList
) != NULL
)
22969 me
->serviceTypeList
= serviceType
->next
;
22970 ForgetMem( &serviceType
->str
);
22971 free( serviceType
);
22973 check( !me
->domainList
);
22974 check( !me
->stopTimer
);
22977 //===========================================================================================================================
22978 // ServiceBrowserStart
22979 //===========================================================================================================================
22981 static void _ServiceBrowserStart( void *inContext
);
22983 static void ServiceBrowserStart( ServiceBrowserRef me
)
22986 dispatch_async_f( me
->queue
, me
, _ServiceBrowserStart
);
22989 static void _ServiceBrowserStart( void *inContext
)
22992 ServiceBrowserRef
const me
= (ServiceBrowserRef
) inContext
;
22994 err
= DNSServiceCreateConnection( &me
->connection
);
22995 require_noerr( err
, exit
);
22997 err
= DNSServiceSetDispatchQueue( me
->connection
, me
->queue
);
22998 require_noerr( err
, exit
);
23002 err
= _ServiceBrowserAddDomain( me
, me
->domain
);
23003 require_noerr( err
, exit
);
23007 DNSServiceRef sdRef
;
23008 const char * const recordName
= "b._dns-sd._udp.local.";
23009 const uint32_t ifIndex
= kDNSServiceInterfaceIndexLocalOnly
;
23011 // Perform PTR meta-query for "b._dns-sd._udp.local." to enumerate recommended browsing domains.
23012 // See <https://tools.ietf.org/html/rfc6763#section-11>.
23014 sb_ulog( kLogLevelTrace
, "Starting PTR QueryRecord on interface %d for %s", (int32_t) ifIndex
, recordName
);
23016 sdRef
= me
->connection
;
23017 err
= DNSServiceQueryRecord( &sdRef
, kDNSServiceFlagsShareConnection
, ifIndex
, recordName
,
23018 kDNSServiceType_PTR
, kDNSServiceClass_IN
, _ServiceBrowserDomainsQueryCallback
, me
);
23019 require_noerr( err
, exit
);
23021 me
->domainsQuery
= sdRef
;
23024 err
= DispatchTimerCreate( dispatch_time_seconds( me
->browseTimeSecs
), DISPATCH_TIME_FOREVER
,
23025 100 * kNanosecondsPerMillisecond
, me
->queue
, _ServiceBrowserTimerHandler
, NULL
, me
, &me
->stopTimer
);
23026 require_noerr( err
, exit
);
23027 dispatch_resume( me
->stopTimer
);
23030 if( err
) _ServiceBrowserStop( me
, err
);
23033 //===========================================================================================================================
23034 // ServiceBrowserAddServiceType
23035 //===========================================================================================================================
23037 static OSStatus
ServiceBrowserAddServiceType( ServiceBrowserRef me
, const char *inServiceType
)
23040 StringListItem
* item
;
23041 StringListItem
** itemPtr
;
23042 StringListItem
* newItem
= NULL
;
23044 for( itemPtr
= &me
->serviceTypeList
; ( item
= *itemPtr
) != NULL
; itemPtr
= &item
->next
)
23046 if( strcmp( item
->str
, inServiceType
) == 0 ) break;
23050 newItem
= (StringListItem
*) calloc( 1, sizeof( *newItem
) );
23051 require_action( newItem
, exit
, err
= kNoMemoryErr
);
23053 newItem
->str
= strdup( inServiceType
);
23054 require_action( newItem
->str
, exit
, err
= kNoMemoryErr
);
23056 *itemPtr
= newItem
;
23062 FreeNullSafe( newItem
);
23066 //===========================================================================================================================
23067 // ServiceBrowserSetCallback
23068 //===========================================================================================================================
23070 static void ServiceBrowserSetCallback( ServiceBrowserRef me
, ServiceBrowserCallback_f inCallback
, void *inContext
)
23072 me
->userCallback
= inCallback
;
23073 me
->userContext
= inContext
;
23076 //===========================================================================================================================
23077 // ServiceBrowserResultsRetain
23078 //===========================================================================================================================
23080 static void ServiceBrowserResultsRetain( ServiceBrowserResults
*inResults
)
23082 ServiceBrowserResultsPrivate
* const results
= (ServiceBrowserResultsPrivate
*) inResults
;
23084 atomic_add_32( &results
->refCount
, 1 );
23087 //===========================================================================================================================
23088 // ServiceBrowserResultsRelease
23089 //===========================================================================================================================
23091 static void ServiceBrowserResultsRelease( ServiceBrowserResults
*inResults
)
23093 ServiceBrowserResultsPrivate
* const results
= (ServiceBrowserResultsPrivate
*) inResults
;
23094 SBRDomain
* domain
;
23096 if( atomic_add_and_fetch_32( &results
->refCount
, -1 ) == 0 )
23098 while( ( domain
= inResults
->domainList
) != NULL
)
23100 inResults
->domainList
= domain
->next
;
23101 _SBRDomainFree( domain
);
23107 //===========================================================================================================================
23108 // _ServiceBrowserStop
23109 //===========================================================================================================================
23111 static void _ServiceBrowserStop( ServiceBrowserRef me
, OSStatus inError
)
23116 SBServiceBrowse
* b
;
23117 SBServiceInstance
* i
;
23119 dispatch_source_forget( &me
->stopTimer
);
23120 DNSServiceForget( &me
->domainsQuery
);
23121 for( d
= me
->domainList
; d
; d
= d
->next
)
23123 DNSServiceForget( &d
->servicesQuery
);
23124 for( t
= d
->typeList
; t
; t
= t
->next
)
23126 for( b
= t
->browseList
; b
; b
= b
->next
)
23128 DNSServiceForget( &b
->browse
);
23129 for( i
= b
->instanceList
; i
; i
= i
->next
)
23131 DNSServiceForget( &i
->resolve
);
23132 DNSServiceForget( &i
->getAddrInfo
);
23137 DNSServiceForget( &me
->connection
);
23139 if( me
->userCallback
)
23141 ServiceBrowserResults
* results
= NULL
;
23143 err
= _ServiceBrowserCreateResults( me
, &results
);
23144 if( !err
) err
= inError
;
23146 me
->userCallback( results
, err
, me
->userContext
);
23147 me
->userCallback
= NULL
;
23148 me
->userContext
= NULL
;
23149 if( results
) ServiceBrowserResultsRelease( results
);
23152 while( ( d
= me
->domainList
) != NULL
)
23154 me
->domainList
= d
->next
;
23155 _SBDomainFree( d
);
23160 //===========================================================================================================================
23161 // _ServiceBrowserAddDomain
23162 //===========================================================================================================================
23164 static OSStatus
_ServiceBrowserAddDomain( ServiceBrowserRef me
, const char *inDomain
)
23168 SBDomain
** domainPtr
;
23169 SBDomain
* newDomain
= NULL
;
23171 for( domainPtr
= &me
->domainList
; ( domain
= *domainPtr
) != NULL
; domainPtr
= &domain
->next
)
23173 if( strcasecmp( domain
->name
, inDomain
) == 0 ) break;
23175 require_action_quiet( !domain
, exit
, err
= kDuplicateErr
);
23177 err
= _SBDomainCreate( inDomain
, me
, &newDomain
);
23178 require_noerr_quiet( err
, exit
);
23180 if( me
->serviceTypeList
)
23182 const StringListItem
* item
;
23184 for( item
= me
->serviceTypeList
; item
; item
= item
->next
)
23186 err
= _ServiceBrowserAddServiceType( me
, newDomain
, item
->str
, me
->ifIndex
);
23187 if( err
== kDuplicateErr
) err
= kNoErr
;
23188 require_noerr( err
, exit
);
23194 DNSServiceRef sdRef
;
23195 DNSServiceFlags flags
;
23197 // Perform PTR meta-query for _services._dns-sd._udp.<domain> to enumerate service types in domain.
23198 // See <https://tools.ietf.org/html/rfc6763#section-9>.
23200 ASPrintF( &recordName
, "_services._dns-sd._udp.%s", newDomain
->name
);
23201 require_action( recordName
, exit
, err
= kNoMemoryErr
);
23203 flags
= kDNSServiceFlagsShareConnection
;
23204 if( ( me
->ifIndex
== kDNSServiceInterfaceIndexAny
) && me
->includeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
23206 sb_ulog( kLogLevelTrace
, "Starting PTR QueryRecord on interface %d for %s", (int32_t) me
->ifIndex
, recordName
);
23208 sdRef
= newDomain
->browser
->connection
;
23209 err
= DNSServiceQueryRecord( &sdRef
, flags
, me
->ifIndex
, recordName
, kDNSServiceType_PTR
, kDNSServiceClass_IN
,
23210 _ServiceBrowserServicesQueryCallback
, newDomain
);
23211 free( recordName
);
23212 require_noerr( err
, exit
);
23214 newDomain
->servicesQuery
= sdRef
;
23217 *domainPtr
= newDomain
;
23222 if( newDomain
) _SBDomainFree( newDomain
);
23226 //===========================================================================================================================
23227 // _ServiceBrowserRemoveDomain
23228 //===========================================================================================================================
23230 static OSStatus
_ServiceBrowserRemoveDomain( ServiceBrowserRef me
, const char *inName
)
23234 SBDomain
** domainPtr
;
23236 for( domainPtr
= &me
->domainList
; ( domain
= *domainPtr
) != NULL
; domainPtr
= &domain
->next
)
23238 if( strcasecmp( domain
->name
, inName
) == 0 ) break;
23243 *domainPtr
= domain
->next
;
23244 _SBDomainFree( domain
);
23249 err
= kNotFoundErr
;
23255 //===========================================================================================================================
23256 // _ServiceBrowserTimerHandler
23257 //===========================================================================================================================
23259 static void _ServiceBrowserTimerHandler( void *inContext
)
23261 ServiceBrowserRef
const me
= (ServiceBrowserRef
) inContext
;
23263 _ServiceBrowserStop( me
, kNoErr
);
23266 //===========================================================================================================================
23267 // _ServiceBrowserDomainsQueryCallback
23268 //===========================================================================================================================
23270 static void DNSSD_API
23271 _ServiceBrowserDomainsQueryCallback(
23272 DNSServiceRef inSDRef
,
23273 DNSServiceFlags inFlags
,
23274 uint32_t inInterfaceIndex
,
23275 DNSServiceErrorType inError
,
23276 const char * inFullName
,
23279 uint16_t inRDataLen
,
23280 const void * inRDataPtr
,
23284 ServiceBrowserRef
const me
= (ServiceBrowserRef
) inContext
;
23286 char domainStr
[ kDNSServiceMaxDomainName
];
23292 sb_ulog( kLogLevelTrace
, "QueryRecord result: %s on interface %d for %s -> %{du:rdata}%?{end} (error: %#m)",
23293 DNSServiceFlagsToAddRmvStr( inFlags
), (int32_t) inInterfaceIndex
, inFullName
, inType
, inRDataPtr
, inRDataLen
,
23294 !inError
, inError
);
23296 require_noerr( inError
, exit
);
23298 err
= DomainNameToString( inRDataPtr
, ( (const uint8_t *) inRDataPtr
) + inRDataLen
, domainStr
, NULL
);
23299 require_noerr( err
, exit
);
23301 if( inFlags
& kDNSServiceFlagsAdd
)
23303 err
= _ServiceBrowserAddDomain( me
, domainStr
);
23304 if( err
== kDuplicateErr
) err
= kNoErr
;
23305 require_noerr( err
, exit
);
23309 err
= _ServiceBrowserRemoveDomain( me
, domainStr
);
23310 if( err
== kNotFoundErr
) err
= kNoErr
;
23311 require_noerr( err
, exit
);
23318 //===========================================================================================================================
23319 // _ServiceBrowserServicesQueryCallback
23320 //===========================================================================================================================
23322 static void DNSSD_API
23323 _ServiceBrowserServicesQueryCallback(
23324 DNSServiceRef inSDRef
,
23325 DNSServiceFlags inFlags
,
23326 uint32_t inInterfaceIndex
,
23327 DNSServiceErrorType inError
,
23328 const char * inFullName
,
23331 uint16_t inRDataLen
,
23332 const void * inRDataPtr
,
23337 SBDomain
* const domain
= (SBDomain
*) inContext
;
23338 ServiceBrowserRef
const me
= domain
->browser
;
23339 const uint8_t * src
;
23340 const uint8_t * end
;
23343 uint8_t serviceType
[ 2 * ( 1 + kDomainLabelLengthMax
) + 1 ];
23344 char serviceTypeStr
[ kDNSServiceMaxDomainName
];
23350 sb_ulog( kLogLevelTrace
, "QueryRecord result: %s on interface %d for %s -> %{du:rdata}%?{end} (error: %#m)",
23351 DNSServiceFlagsToAddRmvStr( inFlags
), (int32_t) inInterfaceIndex
, inFullName
, inType
, inRDataPtr
, inRDataLen
,
23352 !inError
, inError
);
23354 require_noerr( inError
, exit
);
23356 check( inType
== kDNSServiceType_PTR
);
23357 check( inClass
== kDNSServiceClass_IN
);
23359 // The first two labels of the domain name in the RDATA describe a service type.
23360 // See <https://tools.ietf.org/html/rfc6763#section-9>.
23362 src
= (const uint8_t *) inRDataPtr
;
23363 end
= src
+ inRDataLen
;
23365 for( i
= 0; i
< 2; ++i
)
23369 require_action_quiet( ( end
- src
) > 0, exit
, err
= kUnderrunErr
);
23372 require_action_quiet( ( labelLen
> 0 ) && ( labelLen
<= kDomainLabelLengthMax
), exit
, err
= kMalformedErr
);
23373 require_action_quiet( ( (size_t)( end
- src
) ) >= ( 1 + labelLen
), exit
, err
= kUnderrunErr
);
23375 memcpy( dst
, src
, 1 + labelLen
);
23376 src
+= 1 + labelLen
;
23377 dst
+= 1 + labelLen
;
23381 err
= DomainNameToString( serviceType
, NULL
, serviceTypeStr
, NULL
);
23382 require_noerr( err
, exit
);
23384 if( inFlags
& kDNSServiceFlagsAdd
)
23386 err
= _ServiceBrowserAddServiceType( me
, domain
, serviceTypeStr
, inInterfaceIndex
);
23387 if( err
== kDuplicateErr
) err
= kNoErr
;
23388 require_noerr( err
, exit
);
23392 err
= _ServiceBrowserRemoveServiceType( me
, domain
, serviceTypeStr
, inInterfaceIndex
);
23393 if( err
== kNotFoundErr
) err
= kNoErr
;
23394 require_noerr( err
, exit
);
23401 //===========================================================================================================================
23402 // _ServiceBrowserBrowseCallback
23403 //===========================================================================================================================
23405 static void DNSSD_API
23406 _ServiceBrowserBrowseCallback(
23407 DNSServiceRef inSDRef
,
23408 DNSServiceFlags inFlags
,
23409 uint32_t inInterfaceIndex
,
23410 DNSServiceErrorType inError
,
23411 const char * inName
,
23412 const char * inRegType
,
23413 const char * inDomain
,
23417 const uint64_t nowTicks
= UpTicks();
23418 SBServiceBrowse
* const browse
= (SBServiceBrowse
*) inContext
;
23419 ServiceBrowserRef
const me
= (ServiceBrowserRef
) browse
->browser
;
23423 sb_ulog( kLogLevelTrace
, "Browse result: %s on interface %d for %s.%s%s%?{end} (error: %#m)",
23424 DNSServiceFlagsToAddRmvStr( inFlags
), (int32_t) inInterfaceIndex
, inName
, inRegType
, inDomain
, !inError
, inError
);
23426 require_noerr( inError
, exit
);
23428 if( inFlags
& kDNSServiceFlagsAdd
)
23430 err
= _ServiceBrowserAddServiceInstance( me
, browse
, inInterfaceIndex
, inName
, inRegType
, inDomain
,
23431 UpTicksToMicroseconds( nowTicks
- browse
->startTicks
) );
23432 if( err
== kDuplicateErr
) err
= kNoErr
;
23433 require_noerr( err
, exit
);
23437 err
= _ServiceBrowserRemoveServiceInstance( me
, browse
, inName
, inInterfaceIndex
);
23438 if( err
== kNotFoundErr
) err
= kNoErr
;
23439 require_noerr( err
, exit
);
23446 //===========================================================================================================================
23447 // _ServiceBrowserResolveCallback
23448 //===========================================================================================================================
23450 static void DNSSD_API
23451 _ServiceBrowserResolveCallback(
23452 DNSServiceRef inSDRef
,
23453 DNSServiceFlags inFlags
,
23454 uint32_t inInterfaceIndex
,
23455 DNSServiceErrorType inError
,
23456 const char * inFullName
,
23457 const char * inHostname
,
23460 const unsigned char * inTXTPtr
,
23464 const uint64_t nowTicks
= UpTicks();
23465 SBServiceInstance
* const instance
= (SBServiceInstance
*) inContext
;
23466 ServiceBrowserRef
const me
= (ServiceBrowserRef
) instance
->browser
;
23471 sb_ulog( kLogLevelTrace
, "Resolve result on interface %d for %s -> %s:%u %#{txt}%?{end} (error %#m)",
23472 (int32_t) inInterfaceIndex
, inFullName
, inHostname
, inPort
, inTXTPtr
, (size_t) inTXTLen
, !inError
, inError
);
23474 require_noerr( inError
, exit
);
23476 if( !MemEqual( instance
->txtPtr
, instance
->txtLen
, inTXTPtr
, inTXTLen
) )
23478 FreeNullSafe( instance
->txtPtr
);
23479 instance
->txtPtr
= _memdup( inTXTPtr
, inTXTLen
);
23480 require_action( instance
->txtPtr
, exit
, err
= kNoMemoryErr
);
23482 instance
->txtLen
= inTXTLen
;
23485 instance
->port
= ntohs( inPort
);
23487 if( !instance
->hostname
|| ( strcasecmp( instance
->hostname
, inHostname
) != 0 ) )
23489 DNSServiceRef sdRef
;
23491 if( !instance
->hostname
) instance
->resolveTimeUs
= UpTicksToMicroseconds( nowTicks
- instance
->resolveStartTicks
);
23493 err
= ReplaceString( &instance
->hostname
, NULL
, inHostname
, kSizeCString
);
23494 require_noerr( err
, exit
);
23496 DNSServiceForget( &instance
->getAddrInfo
);
23497 ForgetSBIPAddressList( &instance
->ipaddrList
);
23499 sb_ulog( kLogLevelTrace
, "Starting GetAddrInfo on interface %d for %s",
23500 (int32_t) instance
->ifIndex
, instance
->hostname
);
23502 sdRef
= me
->connection
;
23503 instance
->gaiStartTicks
= UpTicks();
23504 err
= DNSServiceGetAddrInfo( &sdRef
, kDNSServiceFlagsShareConnection
, instance
->ifIndex
,
23505 kDNSServiceProtocol_IPv4
| kDNSServiceProtocol_IPv6
, instance
->hostname
, _ServiceBrowserGAICallback
, instance
);
23506 require_noerr( err
, exit
);
23508 instance
->getAddrInfo
= sdRef
;
23515 //===========================================================================================================================
23516 // _ServiceBrowserGAICallback
23517 //===========================================================================================================================
23519 static void DNSSD_API
23520 _ServiceBrowserGAICallback(
23521 DNSServiceRef inSDRef
,
23522 DNSServiceFlags inFlags
,
23523 uint32_t inInterfaceIndex
,
23524 DNSServiceErrorType inError
,
23525 const char * inHostname
,
23526 const struct sockaddr
* inSockAddr
,
23531 const uint64_t nowTicks
= UpTicks();
23532 SBServiceInstance
* const instance
= (SBServiceInstance
*) inContext
;
23533 ServiceBrowserRef
const me
= (ServiceBrowserRef
) instance
->browser
;
23538 sb_ulog( kLogLevelTrace
, "GetAddrInfo result: %s on interface %d for (%s ->) %s -> %##a%?{end} (error: %#m)",
23539 DNSServiceFlagsToAddRmvStr( inFlags
), (int32_t) inInterfaceIndex
, instance
->fqdn
, inHostname
, inSockAddr
,
23540 !inError
, inError
);
23542 require_noerr( inError
, exit
);
23544 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
23546 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
23550 if( inFlags
& kDNSServiceFlagsAdd
)
23552 err
= _ServiceBrowserAddIPAddress( me
, instance
, inSockAddr
,
23553 UpTicksToMicroseconds( nowTicks
- instance
->gaiStartTicks
) );
23554 if( err
== kDuplicateErr
) err
= kNoErr
;
23555 require_noerr( err
, exit
);
23559 err
= _ServiceBrowserRemoveIPAddress( me
, instance
, inSockAddr
);
23560 if( err
== kNotFoundErr
) err
= kNoErr
;
23561 require_noerr( err
, exit
);
23568 //===========================================================================================================================
23569 // _ServiceBrowserAddServiceType
23570 //===========================================================================================================================
23573 _ServiceBrowserAddServiceType(
23574 ServiceBrowserRef me
,
23575 SBDomain
* inDomain
,
23576 const char * inName
,
23577 uint32_t inIfIndex
)
23580 SBServiceType
* type
;
23581 SBServiceType
** typePtr
;
23582 SBServiceType
* newType
= NULL
;
23583 SBServiceBrowse
* browse
;
23584 SBServiceBrowse
** browsePtr
;
23585 SBServiceBrowse
* newBrowse
= NULL
;
23586 DNSServiceRef sdRef
;
23587 DNSServiceFlags flags
;
23589 for( typePtr
= &inDomain
->typeList
; ( type
= *typePtr
) != NULL
; typePtr
= &type
->next
)
23591 if( strcasecmp( type
->name
, inName
) == 0 ) break;
23595 err
= _SBServiceTypeCreate( inName
, &newType
);
23596 require_noerr_quiet( err
, exit
);
23601 for( browsePtr
= &type
->browseList
; ( browse
= *browsePtr
) != NULL
; browsePtr
= &browse
->next
)
23603 if( browse
->ifIndex
== inIfIndex
) break;
23605 require_action_quiet( !browse
, exit
, err
= kDuplicateErr
);
23607 err
= _SBServiceBrowseCreate( inIfIndex
, me
, &newBrowse
);
23608 require_noerr_quiet( err
, exit
);
23610 flags
= kDNSServiceFlagsShareConnection
;
23611 if( ( newBrowse
->ifIndex
== kDNSServiceInterfaceIndexAny
) && me
->includeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
23613 sb_ulog( kLogLevelTrace
, "Starting Browse on interface %d for %s%s",
23614 (int32_t) newBrowse
->ifIndex
, type
->name
, inDomain
->name
);
23616 sdRef
= me
->connection
;
23617 newBrowse
->startTicks
= UpTicks();
23618 err
= DNSServiceBrowse( &sdRef
, flags
, newBrowse
->ifIndex
, type
->name
, inDomain
->name
, _ServiceBrowserBrowseCallback
,
23620 require_noerr( err
, exit
);
23622 newBrowse
->browse
= sdRef
;
23623 *browsePtr
= newBrowse
;
23628 *typePtr
= newType
;
23633 if( newBrowse
) _SBServiceBrowseFree( newBrowse
);
23634 if( newType
) _SBServiceTypeFree( newType
);
23638 //===========================================================================================================================
23639 // _ServiceBrowserRemoveServiceType
23640 //===========================================================================================================================
23643 _ServiceBrowserRemoveServiceType(
23644 ServiceBrowserRef me
,
23645 SBDomain
* inDomain
,
23646 const char * inName
,
23647 uint32_t inIfIndex
)
23650 SBServiceType
* type
;
23651 SBServiceType
** typePtr
;
23652 SBServiceBrowse
* browse
;
23653 SBServiceBrowse
** browsePtr
;
23657 for( typePtr
= &inDomain
->typeList
; ( type
= *typePtr
) != NULL
; typePtr
= &type
->next
)
23659 if( strcasecmp( type
->name
, inName
) == 0 ) break;
23661 require_action_quiet( type
, exit
, err
= kNotFoundErr
);
23663 for( browsePtr
= &type
->browseList
; ( browse
= *browsePtr
) != NULL
; browsePtr
= &browse
->next
)
23665 if( browse
->ifIndex
== inIfIndex
) break;
23667 require_action_quiet( browse
, exit
, err
= kNotFoundErr
);
23669 *browsePtr
= browse
->next
;
23670 _SBServiceBrowseFree( browse
);
23671 if( !type
->browseList
)
23673 *typePtr
= type
->next
;
23674 _SBServiceTypeFree( type
);
23682 //===========================================================================================================================
23683 // _ServiceBrowserAddServiceInstance
23684 //===========================================================================================================================
23687 _ServiceBrowserAddServiceInstance(
23688 ServiceBrowserRef me
,
23689 SBServiceBrowse
* inBrowse
,
23690 uint32_t inIfIndex
,
23691 const char * inName
,
23692 const char * inRegType
,
23693 const char * inDomain
,
23694 uint64_t inDiscoverTimeUs
)
23697 DNSServiceRef sdRef
;
23698 SBServiceInstance
* instance
;
23699 SBServiceInstance
** instancePtr
;
23700 SBServiceInstance
* newInstance
= NULL
;
23702 for( instancePtr
= &inBrowse
->instanceList
; ( instance
= *instancePtr
) != NULL
; instancePtr
= &instance
->next
)
23704 if( ( instance
->ifIndex
== inIfIndex
) && ( strcasecmp( instance
->name
, inName
) == 0 ) ) break;
23706 require_action_quiet( !instance
, exit
, err
= kDuplicateErr
);
23708 err
= _SBServiceInstanceCreate( inName
, inRegType
, inDomain
, inIfIndex
, inDiscoverTimeUs
, me
, &newInstance
);
23709 require_noerr_quiet( err
, exit
);
23711 sb_ulog( kLogLevelTrace
, "Starting Resolve on interface %d for %s.%s%s",
23712 (int32_t) newInstance
->ifIndex
, inName
, inRegType
, inDomain
);
23714 sdRef
= me
->connection
;
23715 newInstance
->resolveStartTicks
= UpTicks();
23716 err
= DNSServiceResolve( &sdRef
, kDNSServiceFlagsShareConnection
, newInstance
->ifIndex
, inName
, inRegType
, inDomain
,
23717 _ServiceBrowserResolveCallback
, newInstance
);
23718 require_noerr( err
, exit
);
23720 newInstance
->resolve
= sdRef
;
23721 *instancePtr
= newInstance
;
23722 newInstance
= NULL
;
23725 if( newInstance
) _SBServiceInstanceFree( newInstance
);
23729 //===========================================================================================================================
23730 // _ServiceBrowserRemoveServiceInstance
23731 //===========================================================================================================================
23734 _ServiceBrowserRemoveServiceInstance(
23735 ServiceBrowserRef me
,
23736 SBServiceBrowse
* inBrowse
,
23737 const char * inName
,
23738 uint32_t inIfIndex
)
23741 SBServiceInstance
* instance
;
23742 SBServiceInstance
** ptr
;
23746 for( ptr
= &inBrowse
->instanceList
; ( instance
= *ptr
) != NULL
; ptr
= &instance
->next
)
23748 if( ( instance
->ifIndex
== inIfIndex
) && ( strcasecmp( instance
->name
, inName
) == 0 ) ) break;
23750 require_action_quiet( instance
, exit
, err
= kNotFoundErr
);
23752 *ptr
= instance
->next
;
23753 _SBServiceInstanceFree( instance
);
23760 //===========================================================================================================================
23761 // _ServiceBrowserAddIPAddress
23762 //===========================================================================================================================
23765 _ServiceBrowserAddIPAddress(
23766 ServiceBrowserRef me
,
23767 SBServiceInstance
* inInstance
,
23768 const struct sockaddr
* inSockAddr
,
23769 uint64_t inResolveTimeUs
)
23772 SBIPAddress
* ipaddr
;
23773 SBIPAddress
** ipaddrPtr
;
23774 SBIPAddress
* newIPAddr
= NULL
;
23778 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
23780 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
23785 for( ipaddrPtr
= &inInstance
->ipaddrList
; ( ipaddr
= *ipaddrPtr
) != NULL
; ipaddrPtr
= &ipaddr
->next
)
23787 if( SockAddrCompareAddr( &ipaddr
->sip
, inSockAddr
) == 0 ) break;
23789 require_action_quiet( !ipaddr
, exit
, err
= kDuplicateErr
);
23791 err
= _SBIPAddressCreate( inSockAddr
, inResolveTimeUs
, &newIPAddr
);
23792 require_noerr_quiet( err
, exit
);
23794 *ipaddrPtr
= newIPAddr
;
23799 if( newIPAddr
) _SBIPAddressFree( newIPAddr
);
23803 //===========================================================================================================================
23804 // _ServiceBrowserRemoveIPAddress
23805 //===========================================================================================================================
23808 _ServiceBrowserRemoveIPAddress(
23809 ServiceBrowserRef me
,
23810 SBServiceInstance
* inInstance
,
23811 const struct sockaddr
* inSockAddr
)
23814 SBIPAddress
* ipaddr
;
23815 SBIPAddress
** ipaddrPtr
;
23819 for( ipaddrPtr
= &inInstance
->ipaddrList
; ( ipaddr
= *ipaddrPtr
) != NULL
; ipaddrPtr
= &ipaddr
->next
)
23821 if( SockAddrCompareAddr( &ipaddr
->sip
.sa
, inSockAddr
) == 0 ) break;
23823 require_action_quiet( ipaddr
, exit
, err
= kNotFoundErr
);
23825 *ipaddrPtr
= ipaddr
->next
;
23826 _SBIPAddressFree( ipaddr
);
23833 //===========================================================================================================================
23834 // _ServiceBrowserCreateResults
23835 //===========================================================================================================================
23837 static OSStatus
_ServiceBrowserCreateResults( ServiceBrowserRef me
, ServiceBrowserResults
**outResults
)
23842 SBServiceBrowse
* b
;
23843 SBServiceInstance
* i
;
23845 ServiceBrowserResultsPrivate
* results
;
23846 SBRDomain
** domainPtr
;
23848 results
= (ServiceBrowserResultsPrivate
*) calloc( 1, sizeof( *results
) );
23849 require_action( results
, exit
, err
= kNoMemoryErr
);
23851 results
->refCount
= 1;
23853 domainPtr
= &results
->domainList
;
23854 for( d
= me
->domainList
; d
; d
= d
->next
)
23856 SBRDomain
* domain
;
23857 SBRServiceType
** typePtr
;
23859 err
= _SBRDomainCreate( d
->name
, &domain
);
23860 require_noerr_quiet( err
, exit
);
23861 *domainPtr
= domain
;
23862 domainPtr
= &domain
->next
;
23864 typePtr
= &domain
->typeList
;
23865 for( t
= d
->typeList
; t
; t
= t
->next
)
23867 SBRServiceType
* type
;
23868 SBRServiceInstance
** instancePtr
;
23870 err
= _SBRServiceTypeCreate( t
->name
, &type
);
23871 require_noerr_quiet( err
, exit
);
23873 typePtr
= &type
->next
;
23875 instancePtr
= &type
->instanceList
;
23876 for( b
= t
->browseList
; b
; b
= b
->next
)
23878 for( i
= b
->instanceList
; i
; i
= i
->next
)
23880 SBRServiceInstance
* instance
;
23881 SBRIPAddress
** ipaddrPtr
;
23883 err
= _SBRServiceInstanceCreate( i
->name
, i
->ifIndex
, i
->hostname
, i
->port
, i
->txtPtr
, i
->txtLen
,
23884 i
->discoverTimeUs
, i
->resolveTimeUs
, &instance
);
23885 require_noerr_quiet( err
, exit
);
23886 *instancePtr
= instance
;
23887 instancePtr
= &instance
->next
;
23889 ipaddrPtr
= &instance
->ipaddrList
;
23890 for( a
= i
->ipaddrList
; a
; a
= a
->next
)
23892 SBRIPAddress
* ipaddr
;
23894 err
= _SBRIPAddressCreate( &a
->sip
.sa
, a
->resolveTimeUs
, &ipaddr
);
23895 require_noerr_quiet( err
, exit
);
23897 *ipaddrPtr
= ipaddr
;
23898 ipaddrPtr
= &ipaddr
->next
;
23905 *outResults
= (ServiceBrowserResults
*) results
;
23910 if( results
) ServiceBrowserResultsRelease( (ServiceBrowserResults
*) results
);
23914 //===========================================================================================================================
23916 //===========================================================================================================================
23918 static OSStatus
_SBDomainCreate( const char *inName
, ServiceBrowserRef inBrowser
, SBDomain
**outDomain
)
23923 obj
= (SBDomain
*) calloc( 1, sizeof( *obj
) );
23924 require_action( obj
, exit
, err
= kNoMemoryErr
);
23926 obj
->name
= strdup( inName
);
23927 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
23929 obj
->browser
= inBrowser
;
23936 if( obj
) _SBDomainFree( obj
);
23940 //===========================================================================================================================
23942 //===========================================================================================================================
23944 static void _SBDomainFree( SBDomain
*inDomain
)
23946 SBServiceType
* type
;
23948 ForgetMem( &inDomain
->name
);
23949 DNSServiceForget( &inDomain
->servicesQuery
);
23950 while( ( type
= inDomain
->typeList
) != NULL
)
23952 inDomain
->typeList
= type
->next
;
23953 _SBServiceTypeFree( type
);
23958 //===========================================================================================================================
23959 // _SBServiceTypeCreate
23960 //===========================================================================================================================
23962 static OSStatus
_SBServiceTypeCreate( const char *inName
, SBServiceType
**outType
)
23965 SBServiceType
* obj
;
23967 obj
= (SBServiceType
*) calloc( 1, sizeof( *obj
) );
23968 require_action( obj
, exit
, err
= kNoMemoryErr
);
23970 obj
->name
= strdup( inName
);
23971 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
23978 if( obj
) _SBServiceTypeFree( obj
);
23982 //===========================================================================================================================
23983 // _SBServiceTypeFree
23984 //===========================================================================================================================
23986 static void _SBServiceTypeFree( SBServiceType
*inType
)
23988 SBServiceBrowse
* browse
;
23990 ForgetMem( &inType
->name
);
23991 while( ( browse
= inType
->browseList
) != NULL
)
23993 inType
->browseList
= browse
->next
;
23994 _SBServiceBrowseFree( browse
);
23999 //===========================================================================================================================
24000 // _SBServiceBrowseCreate
24001 //===========================================================================================================================
24003 static OSStatus
_SBServiceBrowseCreate( uint32_t inIfIndex
, ServiceBrowserRef inBrowser
, SBServiceBrowse
**outBrowse
)
24006 SBServiceBrowse
* obj
;
24008 obj
= (SBServiceBrowse
*) calloc( 1, sizeof( *obj
) );
24009 require_action( obj
, exit
, err
= kNoMemoryErr
);
24011 obj
->ifIndex
= inIfIndex
;
24012 obj
->browser
= inBrowser
;
24020 //===========================================================================================================================
24021 // _SBServiceBrowseFree
24022 //===========================================================================================================================
24024 static void _SBServiceBrowseFree( SBServiceBrowse
*inBrowse
)
24026 SBServiceInstance
* instance
;
24028 DNSServiceForget( &inBrowse
->browse
);
24029 while( ( instance
= inBrowse
->instanceList
) != NULL
)
24031 inBrowse
->instanceList
= instance
->next
;
24032 _SBServiceInstanceFree( instance
);
24037 //===========================================================================================================================
24038 // _SBServiceInstanceCreate
24039 //===========================================================================================================================
24042 _SBServiceInstanceCreate(
24043 const char * inName
,
24044 const char * inType
,
24045 const char * inDomain
,
24046 uint32_t inIfIndex
,
24047 uint64_t inDiscoverTimeUs
,
24048 ServiceBrowserRef inBrowser
,
24049 SBServiceInstance
** outInstance
)
24052 SBServiceInstance
* obj
;
24054 obj
= (SBServiceInstance
*) calloc( 1, sizeof( *obj
) );
24055 require_action( obj
, exit
, err
= kNoMemoryErr
);
24057 obj
->name
= strdup( inName
);
24058 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
24060 ASPrintF( &obj
->fqdn
, "%s.%s%s", obj
->name
, inType
, inDomain
);
24061 require_action( obj
->fqdn
, exit
, err
= kNoMemoryErr
);
24063 obj
->ifIndex
= inIfIndex
;
24064 obj
->discoverTimeUs
= inDiscoverTimeUs
;
24065 obj
->browser
= inBrowser
;
24067 *outInstance
= obj
;
24072 if( obj
) _SBServiceInstanceFree( obj
);
24076 //===========================================================================================================================
24077 // _SBServiceInstanceFree
24078 //===========================================================================================================================
24080 static void _SBServiceInstanceFree( SBServiceInstance
*inInstance
)
24082 ForgetMem( &inInstance
->name
);
24083 ForgetMem( &inInstance
->fqdn
);
24084 DNSServiceForget( &inInstance
->resolve
);
24085 ForgetMem( &inInstance
->hostname
);
24086 ForgetMem( &inInstance
->txtPtr
);
24087 DNSServiceForget( &inInstance
->getAddrInfo
);
24088 ForgetSBIPAddressList( &inInstance
->ipaddrList
);
24089 free( inInstance
);
24092 //===========================================================================================================================
24093 // _SBIPAddressCreate
24094 //===========================================================================================================================
24096 static OSStatus
_SBIPAddressCreate( const struct sockaddr
*inSockAddr
, uint64_t inResolveTimeUs
, SBIPAddress
**outIPAddress
)
24101 obj
= (SBIPAddress
*) calloc( 1, sizeof( *obj
) );
24102 require_action( obj
, exit
, err
= kNoMemoryErr
);
24104 SockAddrCopy( inSockAddr
, &obj
->sip
);
24105 obj
->resolveTimeUs
= inResolveTimeUs
;
24107 *outIPAddress
= obj
;
24114 //===========================================================================================================================
24115 // _SBIPAddressFree
24116 //===========================================================================================================================
24118 static void _SBIPAddressFree( SBIPAddress
*inIPAddress
)
24120 free( inIPAddress
);
24123 //===========================================================================================================================
24124 // _SBIPAddressFreeList
24125 //===========================================================================================================================
24127 static void _SBIPAddressFreeList( SBIPAddress
*inList
)
24129 SBIPAddress
* ipaddr
;
24131 while( ( ipaddr
= inList
) != NULL
)
24133 inList
= ipaddr
->next
;
24134 _SBIPAddressFree( ipaddr
);
24138 //===========================================================================================================================
24139 // _SBRDomainCreate
24140 //===========================================================================================================================
24142 static OSStatus
_SBRDomainCreate( const char *inName
, SBRDomain
**outDomain
)
24147 obj
= (SBRDomain
*) calloc( 1, sizeof( *obj
) );
24148 require_action( obj
, exit
, err
= kNoMemoryErr
);
24150 obj
->name
= strdup( inName
);
24151 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
24158 if( obj
) _SBRDomainFree( obj
);
24162 //===========================================================================================================================
24164 //===========================================================================================================================
24166 static void _SBRDomainFree( SBRDomain
*inDomain
)
24168 SBRServiceType
* type
;
24170 ForgetMem( &inDomain
->name
);
24171 while( ( type
= inDomain
->typeList
) != NULL
)
24173 inDomain
->typeList
= type
->next
;
24174 _SBRServiceTypeFree( type
);
24179 //===========================================================================================================================
24180 // _SBRServiceTypeCreate
24181 //===========================================================================================================================
24183 static OSStatus
_SBRServiceTypeCreate( const char *inName
, SBRServiceType
**outType
)
24186 SBRServiceType
* obj
;
24188 obj
= (SBRServiceType
*) calloc( 1, sizeof( *obj
) );
24189 require_action( obj
, exit
, err
= kNoMemoryErr
);
24191 obj
->name
= strdup( inName
);
24192 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
24199 if( obj
) _SBRServiceTypeFree( obj
);
24203 //===========================================================================================================================
24204 // _SBRServiceTypeFree
24205 //===========================================================================================================================
24207 static void _SBRServiceTypeFree( SBRServiceType
*inType
)
24209 SBRServiceInstance
* instance
;
24211 ForgetMem( &inType
->name
);
24212 while( ( instance
= inType
->instanceList
) != NULL
)
24214 inType
->instanceList
= instance
->next
;
24215 _SBRServiceInstanceFree( instance
);
24220 //===========================================================================================================================
24221 // _SBRServiceInstanceCreate
24222 //===========================================================================================================================
24225 _SBRServiceInstanceCreate(
24226 const char * inName
,
24227 uint32_t inInterfaceIndex
,
24228 const char * inHostname
,
24230 const uint8_t * inTXTPtr
,
24232 uint64_t inDiscoverTimeUs
,
24233 uint64_t inResolveTimeUs
,
24234 SBRServiceInstance
** outInstance
)
24237 SBRServiceInstance
* obj
;
24239 obj
= (SBRServiceInstance
*) calloc( 1, sizeof( *obj
) );
24240 require_action( obj
, exit
, err
= kNoMemoryErr
);
24242 obj
->name
= strdup( inName
);
24243 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
24247 obj
->hostname
= strdup( inHostname
);
24248 require_action( obj
->hostname
, exit
, err
= kNoMemoryErr
);
24252 obj
->txtPtr
= (uint8_t *) _memdup( inTXTPtr
, inTXTLen
);
24253 require_action( obj
->txtPtr
, exit
, err
= kNoMemoryErr
);
24254 obj
->txtLen
= inTXTLen
;
24256 obj
->discoverTimeUs
= inDiscoverTimeUs
;
24257 obj
->resolveTimeUs
= inResolveTimeUs
;
24258 obj
->ifIndex
= inInterfaceIndex
;
24259 obj
->port
= inPort
;
24261 *outInstance
= obj
;
24266 if( obj
) _SBRServiceInstanceFree( obj
);
24270 //===========================================================================================================================
24271 // _SBRServiceInstanceFree
24272 //===========================================================================================================================
24274 static void _SBRServiceInstanceFree( SBRServiceInstance
*inInstance
)
24276 SBRIPAddress
* ipaddr
;
24278 ForgetMem( &inInstance
->name
);
24279 ForgetMem( &inInstance
->hostname
);
24280 ForgetMem( &inInstance
->txtPtr
);
24281 while( ( ipaddr
= inInstance
->ipaddrList
) != NULL
)
24283 inInstance
->ipaddrList
= ipaddr
->next
;
24284 _SBRIPAddressFree( ipaddr
);
24286 free( inInstance
);
24289 //===========================================================================================================================
24290 // _SBRIPAddressCreate
24291 //===========================================================================================================================
24294 _SBRIPAddressCreate(
24295 const struct sockaddr
* inSockAddr
,
24296 uint64_t inResolveTimeUs
,
24297 SBRIPAddress
** outIPAddress
)
24300 SBRIPAddress
* obj
;
24302 obj
= (SBRIPAddress
*) calloc( 1, sizeof( *obj
) );
24303 require_action( obj
, exit
, err
= kNoMemoryErr
);
24305 SockAddrCopy( inSockAddr
, &obj
->sip
);
24306 obj
->resolveTimeUs
= inResolveTimeUs
;
24308 *outIPAddress
= obj
;
24315 //===========================================================================================================================
24316 // _SBRIPAddressFree
24317 //===========================================================================================================================
24319 static void _SBRIPAddressFree( SBRIPAddress
*inIPAddress
)
24321 free( inIPAddress
);
24324 //===========================================================================================================================
24327 // Note: This was copied from CoreUtils because the SocketWriteAll function is currently not exported in the framework.
24328 //===========================================================================================================================
24330 static OSStatus
_SocketWriteAll( SocketRef inSock
, const void *inData
, size_t inSize
, int32_t inTimeoutSecs
)
24333 const uint8_t * src
;
24334 const uint8_t * end
;
24336 struct timeval timeout
;
24339 FD_ZERO( &writeSet
);
24340 src
= (const uint8_t *) inData
;
24341 end
= src
+ inSize
;
24344 FD_SET( inSock
, &writeSet
);
24345 timeout
.tv_sec
= inTimeoutSecs
;
24346 timeout
.tv_usec
= 0;
24347 n
= select( (int)( inSock
+ 1 ), NULL
, &writeSet
, NULL
, &timeout
);
24348 if( n
== 0 ) { err
= kTimeoutErr
; goto exit
; }
24349 err
= map_socket_value_errno( inSock
, n
> 0, n
);
24350 require_noerr( err
, exit
);
24352 n
= send( inSock
, (char *) src
, (size_t)( end
- src
), 0 );
24353 err
= map_socket_value_errno( inSock
, n
>= 0, n
);
24354 if( err
== EINTR
) continue;
24355 require_noerr( err
, exit
);
24365 //===========================================================================================================================
24366 // _ParseIPv4Address
24368 // Warning: "inBuffer" may be modified even in error cases.
24370 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
24371 //===========================================================================================================================
24373 static OSStatus
_ParseIPv4Address( const char *inStr
, uint8_t inBuffer
[ 4 ], const char **outStr
)
24389 for( ; ( c
= *inStr
) != '\0'; ++inStr
)
24391 if( isdigit_safe( c
) )
24393 v
= ( *dst
* 10 ) + ( c
- '0' );
24394 require_action_quiet( v
<= 255, exit
, err
= kRangeErr
);
24395 *dst
= (uint8_t) v
;
24399 require_action_quiet( segments
<= 4, exit
, err
= kOverrunErr
);
24403 else if( ( c
== '.' ) && sawDigit
)
24405 require_action_quiet( segments
< 4, exit
, err
= kMalformedErr
);
24414 require_action_quiet( segments
== 4, exit
, err
= kUnderrunErr
);
24423 //===========================================================================================================================
24424 // _StringToIPv4Address
24426 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
24427 //===========================================================================================================================
24430 _StringToIPv4Address(
24431 const char * inStr
,
24432 StringToIPAddressFlags inFlags
,
24435 uint32_t * outSubnet
,
24436 uint32_t * outRouter
,
24437 const char ** outStr
)
24447 uint32_t subnetMask
;
24450 require_action( inStr
, exit
, err
= kParamErr
);
24452 // Parse the address-only part of the address (e.g. "1.2.3.4").
24454 err
= _ParseIPv4Address( inStr
, buf
, &inStr
);
24455 require_noerr_quiet( err
, exit
);
24456 ip
= (uint32_t)( ( buf
[ 0 ] << 24 ) | ( buf
[ 1 ] << 16 ) | ( buf
[ 2 ] << 8 ) | buf
[ 3 ] );
24459 // Parse the port (if any).
24465 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPort
), exit
, err
= kUnexpectedErr
);
24466 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) port
= ( port
* 10 ) + ( c
- '0' );
24467 require_action_quiet( port
<= 65535, exit
, err
= kRangeErr
);
24471 // Parse the prefix length (if any).
24479 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPrefix
), exit
, err
= kUnexpectedErr
);
24480 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) prefix
= ( prefix
* 10 ) + ( c
- '0' );
24481 require_action_quiet( ( prefix
>= 0 ) && ( prefix
<= 32 ), exit
, err
= kRangeErr
);
24484 subnetMask
= ( prefix
> 0 ) ? ( UINT32_C( 0xFFFFFFFF ) << ( 32 - prefix
) ) : 0;
24485 router
= ( ip
& subnetMask
) | 1;
24488 // Return the results. Only fill in port/prefix/router results if the info was found to allow for defaults.
24490 if( outIP
) *outIP
= ip
;
24491 if( outPort
&& hasPort
) *outPort
= port
;
24492 if( outSubnet
&& hasPrefix
) *outSubnet
= subnetMask
;
24493 if( outRouter
&& hasPrefix
) *outRouter
= router
;
24494 if( outStr
) *outStr
= inStr
;
24501 //===========================================================================================================================
24502 // _ParseIPv6Address
24504 // Note: Parsed according to the rules specified in RFC 3513.
24505 // Warning: "inBuffer" may be modified even in error cases.
24507 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
24508 //===========================================================================================================================
24510 static OSStatus
_ParseIPv6Address( const char *inStr
, int inAllowV4Mapped
, uint8_t inBuffer
[ 16 ], const char **outStr
)
24512 // Table to map uppercase hex characters - '0' to their numeric values.
24513 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F
24514 static const uint8_t kASCIItoHexTable
[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15 };
24519 uint8_t * colonPtr
;
24526 // Pre-zero the address to simplify handling of compressed addresses (e.g. "::1").
24528 for( i
= 0; i
< 16; ++i
) inBuffer
[ i
] = 0;
24530 // Special case leading :: (e.g. "::1") to simplify processing later.
24532 if( *inStr
== ':' )
24535 require_action_quiet( *inStr
== ':', exit
, err
= kMalformedErr
);
24538 // Parse the address.
24546 while( ( ( c
= *inStr
++ ) != '\0' ) && ( c
!= '%' ) && ( c
!= '/' ) && ( c
!= ']' ) )
24548 if( ( c
>= 'a' ) && ( c
<= 'f' ) ) c
-= ( 'a' - 'A' );
24549 if( ( ( c
>= '0' ) && ( c
<= '9' ) ) || ( ( c
>= 'A' ) && ( c
<= 'F' ) ) )
24552 check( c
< (int) countof( kASCIItoHexTable
) );
24553 v
= ( v
<< 4 ) | kASCIItoHexTable
[ c
];
24554 require_action_quiet( v
<= 0xFFFF, exit
, err
= kRangeErr
);
24563 require_action_quiet( !colonPtr
, exit
, err
= kMalformedErr
);
24567 require_action_quiet( *inStr
!= '\0', exit
, err
= kUnderrunErr
);
24568 require_action_quiet( ( dst
+ 2 ) <= lim
, exit
, err
= kOverrunErr
);
24569 *dst
++ = (uint8_t)( ( v
>> 8 ) & 0xFF );
24570 *dst
++ = (uint8_t)( v
& 0xFF );
24576 // Handle IPv4-mapped/compatible addresses (e.g. ::FFFF:1.2.3.4).
24578 if( inAllowV4Mapped
&& ( c
== '.' ) && ( ( dst
+ 4 ) <= lim
) )
24580 err
= _ParseIPv4Address( ptr
, dst
, &inStr
);
24581 require_noerr_quiet( err
, exit
);
24584 ++inStr
; // Increment because the code below expects the end to be at "inStr - 1".
24590 require_action_quiet( ( dst
+ 2 ) <= lim
, exit
, err
= kOverrunErr
);
24591 *dst
++ = (uint8_t)( ( v
>> 8 ) & 0xFF );
24592 *dst
++ = (uint8_t)( v
& 0xFF );
24594 check( dst
<= lim
);
24597 require_action_quiet( dst
< lim
, exit
, err
= kOverrunErr
);
24598 n
= (int)( dst
- colonPtr
);
24599 for( i
= 1; i
<= n
; ++i
)
24601 lim
[ -i
] = colonPtr
[ n
- i
];
24602 colonPtr
[ n
- i
] = 0;
24606 require_action_quiet( dst
== lim
, exit
, err
= kUnderrunErr
);
24608 *outStr
= inStr
- 1;
24615 //===========================================================================================================================
24618 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
24619 //===========================================================================================================================
24621 static OSStatus
_ParseIPv6Scope( const char *inStr
, uint32_t *outScope
, const char **outStr
)
24623 #if( TARGET_OS_POSIX )
24625 char scopeStr
[ 64 ];
24632 // Copy into a local NULL-terminated string since that is what if_nametoindex expects.
24635 lim
= dst
+ ( countof( scopeStr
) - 1 );
24636 while( ( ( c
= *inStr
) != '\0' ) && ( c
!= ':' ) && ( c
!= '/' ) && ( c
!= ']' ) && ( dst
< lim
) )
24641 check( dst
<= lim
);
24643 // First try to map as a name and if that fails, treat it as a numeric scope.
24645 scope
= if_nametoindex( scopeStr
);
24648 for( ptr
= scopeStr
; ( ( c
= *ptr
) >= '0' ) && ( c
<= '9' ); ++ptr
)
24650 scope
= ( scope
* 10 ) + ( ( (uint8_t) c
) - '0' );
24652 require_action_quiet( c
== '\0', exit
, err
= kMalformedErr
);
24653 require_action_quiet( ( ptr
!= scopeStr
) && ( ( (int)( ptr
- scopeStr
) ) <= 10 ), exit
, err
= kMalformedErr
);
24665 const char * start
;
24669 for( start
= inStr
; ( ( c
= *inStr
) >= '0' ) && ( c
<= '9' ); ++inStr
)
24671 scope
= ( scope
* 10 ) + ( c
- '0' );
24673 require_action_quiet( ( inStr
!= start
) && ( ( (int)( inStr
- start
) ) <= 10 ), exit
, err
= kMalformedErr
);
24684 //===========================================================================================================================
24685 // _StringToIPv6Address
24687 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
24688 //===========================================================================================================================
24691 _StringToIPv6Address(
24692 const char * inStr
,
24693 StringToIPAddressFlags inFlags
,
24694 uint8_t outIPv6
[ 16 ],
24695 uint32_t * outScope
,
24698 const char ** outStr
)
24701 uint8_t ipv6
[ 16 ];
24712 require_action( inStr
, exit
, err
= kParamErr
);
24714 if( *inStr
== '[' ) ++inStr
; // Skip a leading bracket for []-wrapped addresses (e.g. "[::1]:80").
24716 // Parse the address-only part of the address (e.g. "1::1").
24718 err
= _ParseIPv6Address( inStr
, !( inFlags
& kStringToIPAddressFlagsNoIPv4Mapped
), ipv6
, &inStr
);
24719 require_noerr_quiet( err
, exit
);
24722 // Parse the scope, port, or prefix length.
24733 if( c
== '%' ) // Scope (e.g. "%en0" or "%5")
24735 require_action_quiet( !hasScope
, exit
, err
= kMalformedErr
);
24736 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoScope
), exit
, err
= kUnexpectedErr
);
24738 err
= _ParseIPv6Scope( inStr
, &scope
, &inStr
);
24739 require_noerr_quiet( err
, exit
);
24743 else if( c
== ':' ) // Port (e.g. ":80")
24745 require_action_quiet( !hasPort
, exit
, err
= kMalformedErr
);
24746 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPort
), exit
, err
= kUnexpectedErr
);
24747 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) port
= ( port
* 10 ) + ( c
- '0' );
24748 require_action_quiet( port
<= 65535, exit
, err
= kRangeErr
);
24751 else if( c
== '/' ) // Prefix Length (e.g. "/64")
24753 require_action_quiet( !hasPrefix
, exit
, err
= kMalformedErr
);
24754 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPrefix
), exit
, err
= kUnexpectedErr
);
24755 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) prefix
= ( prefix
* 10 ) + ( c
- '0' );
24756 require_action_quiet( ( prefix
>= 0 ) && ( prefix
<= 128 ), exit
, err
= kRangeErr
);
24759 else if( c
== ']' )
24761 require_action_quiet( !hasBracket
, exit
, err
= kMalformedErr
);
24771 // Return the results. Only fill in scope/port/prefix results if the info was found to allow for defaults.
24773 if( outIPv6
) for( i
= 0; i
< 16; ++i
) outIPv6
[ i
] = ipv6
[ i
];
24774 if( outScope
&& hasScope
) *outScope
= scope
;
24775 if( outPort
&& hasPort
) *outPort
= port
;
24776 if( outPrefix
&& hasPrefix
) *outPrefix
= prefix
;
24777 if( outStr
) *outStr
= inStr
;
24784 //===========================================================================================================================
24785 // _StringArray_Free
24787 // Note: This was copied from CoreUtils because the StringArray_Free function is currently not exported in the framework.
24788 //===========================================================================================================================
24790 static void _StringArray_Free( char **inArray
, size_t inCount
)
24794 for( i
= 0; i
< inCount
; ++i
)
24796 free( inArray
[ i
] );
24798 if( inCount
> 0 ) free( inArray
);
24801 //===========================================================================================================================
24802 // _ParseQuotedEscapedString
24804 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
24805 //===========================================================================================================================
24808 _ParseQuotedEscapedString(
24809 const char * inSrc
,
24810 const char * inEnd
,
24811 const char * inDelimiters
,
24814 size_t * outCopiedLen
,
24815 size_t * outTotalLen
,
24816 const char ** outSrc
)
24818 const unsigned char * src
;
24819 const unsigned char * end
;
24820 unsigned char * dst
;
24821 unsigned char * lim
;
24825 Boolean singleQuote
;
24826 Boolean doubleQuote
;
24828 if( inEnd
== NULL
) inEnd
= inSrc
+ strlen( inSrc
);
24829 src
= (const unsigned char *) inSrc
;
24830 end
= (const unsigned char *) inEnd
;
24831 dst
= (unsigned char *) inBuf
;
24832 lim
= dst
+ inMaxLen
;
24833 while( ( src
< end
) && isspace_safe( *src
) ) ++src
; // Skip leading spaces.
24834 if( src
>= end
) return( false );
24836 // Parse each argument from the string.
24838 // See <http://resources.mpi-inf.mpg.de/departments/rg1/teaching/unixffb-ss98/quoting-guide.html> for details.
24841 singleQuote
= false;
24842 doubleQuote
= false;
24848 // Single quotes protect everything (even backslashes, newlines, etc.) except single quotes.
24852 singleQuote
= false;
24856 else if( doubleQuote
)
24858 // Double quotes protect everything except double quotes and backslashes. A backslash can be
24859 // used to protect " or \ within double quotes. A backslash-newline pair disappears completely.
24860 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
24861 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
24862 // A backslash that does not precede ", \, x, X, or a newline is taken literally.
24866 doubleQuote
= false;
24869 else if( c
== '\\' )
24874 if( ( c2
== '"' ) || ( c2
== '\\' ) )
24879 else if( c2
== '\n' )
24884 else if( ( c2
== 'x' ) || ( c2
== 'X' ) )
24888 if( ( ( end
- src
) >= 2 ) && IsHexPair( src
) )
24890 c
= HexPairToByte( src
);
24894 else if( isoctal_safe( c2
) )
24896 if( ( ( end
- src
) >= 3 ) && IsOctalTriple( src
) )
24898 c
= OctalTripleToByte( src
);
24905 else if( strchr( inDelimiters
, c
) )
24909 else if( c
== '\\' )
24911 // A backslash protects the next character, except a newline, x, X and 2 hex bytes or 3 octal bytes.
24912 // A backslash followed by a newline disappears completely.
24913 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
24914 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
24924 else if( ( c
== 'x' ) || ( c
== 'X' ) )
24927 if( ( ( end
- src
) >= 2 ) && IsHexPair( src
) )
24929 c
= HexPairToByte( src
);
24933 else if( isoctal_safe( c
) )
24935 if( ( ( end
- src
) >= 3 ) && IsOctalTriple( src
) )
24937 c
= OctalTripleToByte( src
);
24951 else if( c
== '\'' )
24953 singleQuote
= true;
24956 else if( c
== '"' )
24958 doubleQuote
= true;
24964 if( inBuf
) *dst
= c
;
24970 if( outCopiedLen
) *outCopiedLen
= (size_t)( dst
- ( (unsigned char *) inBuf
) );
24971 if( outTotalLen
) *outTotalLen
= totalLen
;
24972 if( outSrc
) *outSrc
= (const char *) src
;
24976 //===========================================================================================================================
24977 // _ServerSocketOpenEx2
24979 // Note: Based on ServerSocketOpenEx() from CoreUtils. Added parameter to not use SO_REUSEPORT.
24980 //===========================================================================================================================
24983 _ServerSocketOpenEx2(
24987 const void * inAddr
,
24991 Boolean inNoPortReuse
,
24992 SocketRef
* outSock
)
25002 port
= ( inPort
< 0 ) ? -inPort
: inPort
; // Negated port number means "try this port, but allow dynamic".
25004 sock
= socket( inFamily
, inType
, inProtocol
);
25005 err
= map_socket_creation_errno( sock
);
25006 require_noerr_quiet( err
, exit
);
25008 #if( defined( SO_NOSIGPIPE ) )
25009 setsockopt( sock
, SOL_SOCKET
, SO_NOSIGPIPE
, &(int){ 1 }, (socklen_t
) sizeof( int ) );
25012 err
= SocketMakeNonBlocking( sock
);
25013 require_noerr( err
, exit
);
25015 // Set receive buffer size. This has to be done on the listening socket *before* listen is called because
25016 // accept does not return until after the window scale option is exchanged during the 3-way handshake.
25017 // Since accept returns a new socket, the only way to use a larger window scale option is to set the buffer
25018 // size on the listening socket since SO_RCVBUF is inherited by the accepted socket. See UNPv1e3 Section 7.5.
25020 err
= SocketSetBufferSize( sock
, SO_RCVBUF
, inRcvBufSize
);
25021 check_noerr( err
);
25023 // Allow port or address reuse because we may bind separate IPv4 and IPv6 sockets to the same port.
25025 if( ( inType
!= SOCK_DGRAM
) || !inNoPortReuse
)
25028 name
= ( inType
== SOCK_DGRAM
) ? SO_REUSEPORT
: SO_REUSEADDR
;
25029 err
= setsockopt( sock
, SOL_SOCKET
, name
, (char *) &option
, (socklen_t
) sizeof( option
) );
25030 err
= map_socket_noerr_errno( sock
, err
);
25031 require_noerr( err
, exit
);
25034 if( inFamily
== AF_INET
)
25036 // Bind to the port. If it fails, retry with a dynamic port.
25038 memset( &sip
.v4
, 0, sizeof( sip
.v4
) );
25039 SIN_LEN_SET( &sip
.v4
);
25040 sip
.v4
.sin_family
= AF_INET
;
25041 sip
.v4
.sin_port
= htons( (uint16_t) port
);
25042 sip
.v4
.sin_addr
.s_addr
= inAddr
? *( (const uint32_t *) inAddr
) : htonl( INADDR_ANY
);
25043 err
= bind( sock
, &sip
.sa
, (socklen_t
) sizeof( sip
.v4
) );
25044 err
= map_socket_noerr_errno( sock
, err
);
25045 if( err
&& ( inPort
< 0 ) )
25047 sip
.v4
.sin_port
= 0;
25048 err
= bind( sock
, &sip
.sa
, (socklen_t
) sizeof( sip
.v4
) );
25049 err
= map_socket_noerr_errno( sock
, err
);
25051 require_noerr( err
, exit
);
25053 #if( defined( AF_INET6 ) )
25054 else if( inFamily
== AF_INET6
)
25056 // Restrict this socket to IPv6 only because we're going to use a separate socket for IPv4.
25059 err
= setsockopt( sock
, IPPROTO_IPV6
, IPV6_V6ONLY
, (char *) &option
, (socklen_t
) sizeof( option
) );
25060 err
= map_socket_noerr_errno( sock
, err
);
25061 require_noerr( err
, exit
);
25063 // Bind to the port. If it fails, retry with a dynamic port.
25065 memset( &sip
.v6
, 0, sizeof( sip
.v6
) );
25066 SIN6_LEN_SET( &sip
.v6
);
25067 sip
.v6
.sin6_family
= AF_INET6
;
25068 sip
.v6
.sin6_port
= htons( (uint16_t) port
);
25069 sip
.v6
.sin6_addr
= inAddr
? *( (const struct in6_addr
*) inAddr
) : in6addr_any
;
25070 err
= bind( sock
, &sip
.sa
, (socklen_t
) sizeof( sip
.v6
) );
25071 err
= map_socket_noerr_errno( sock
, err
);
25072 if( err
&& ( inPort
< 0 ) )
25074 sip
.v6
.sin6_port
= 0;
25075 err
= bind( sock
, &sip
.sa
, (socklen_t
) sizeof( sip
.v6
) );
25076 err
= map_socket_noerr_errno( sock
, err
);
25078 require_noerr( err
, exit
);
25083 dlogassert( "Unsupported family: %d", inFamily
);
25084 err
= kUnsupportedErr
;
25088 if( inType
== SOCK_STREAM
)
25090 err
= listen( sock
, SOMAXCONN
);
25091 err
= map_socket_noerr_errno( sock
, err
);
25094 err
= listen( sock
, 5 );
25095 err
= map_socket_noerr_errno( sock
, err
);
25096 require_noerr( err
, exit
);
25102 len
= (socklen_t
) sizeof( sip
);
25103 err
= getsockname( sock
, &sip
.sa
, &len
);
25104 err
= map_socket_noerr_errno( sock
, err
);
25105 require_noerr( err
, exit
);
25107 *outPort
= SockAddrGetPort( &sip
);
25110 sock
= kInvalidSocketRef
;
25113 ForgetSocket( &sock
);
25117 //===========================================================================================================================
25120 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
25121 //===========================================================================================================================
25123 static void * _memdup( const void *inPtr
, size_t inLen
)
25127 mem
= malloc( ( inLen
> 0 ) ? inLen
: 1 ); // If inLen is 0, use 1 since malloc( 0 ) is not well defined.
25128 require( mem
, exit
);
25129 if( inLen
> 0 ) memcpy( mem
, inPtr
, inLen
);
25135 //===========================================================================================================================
25138 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
25139 //===========================================================================================================================
25141 static int _memicmp( const void *inP1
, const void *inP2
, size_t inLen
)
25143 const unsigned char * p1
;
25144 const unsigned char * e1
;
25145 const unsigned char * p2
;
25149 p1
= (const unsigned char *) inP1
;
25151 p2
= (const unsigned char *) inP2
;
25156 c1
= tolower( c1
);
25157 c2
= tolower( c2
);
25158 if( c1
< c2
) return( -1 );
25159 if( c1
> c2
) return( 1 );
25164 //===========================================================================================================================
25167 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
25168 //===========================================================================================================================
25170 static uint32_t _FNV1( const void *inData
, size_t inSize
)
25172 const uint8_t * src
= (const uint8_t *) inData
;
25173 const uint8_t * const end
= src
+ inSize
;
25176 hash
= 0x811c9dc5U
;
25177 while( src
!= end
)
25179 hash
*= 0x01000193;