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"
49 //===========================================================================================================================
51 //===========================================================================================================================
53 #define kDNSSDUtilNumVersion NumVersionBuild( 2, 0, 0, kVersionStageBeta, 0 )
55 #if( !MDNSRESPONDER_PROJECT && !defined( DNSSDUTIL_SOURCE_VERSION ) )
56 #define DNSSDUTIL_SOURCE_VERSION "0.0.0"
59 #define kDNSSDUtilIdentifier "com.apple.dnssdutil"
61 //===========================================================================================================================
63 //===========================================================================================================================
65 // DNS-SD API flag descriptors
67 #define kDNSServiceFlagsDescriptors \
68 "\x00" "AutoTrigger\0" \
71 "\x03" "NoAutoRename\0" \
74 "\x06" "BrowseDomains\0" \
75 "\x07" "RegistrationDomains\0" \
76 "\x08" "LongLivedQuery\0" \
77 "\x09" "AllowRemoteQuery\0" \
78 "\x0A" "ForceMulticast\0" \
79 "\x0B" "KnownUnique\0" \
80 "\x0C" "ReturnIntermediates\0" \
81 "\x0D" "DenyConstrained\0" \
82 "\x0E" "ShareConnection\0" \
83 "\x0F" "SuppressUnusable\0" \
85 "\x11" "IncludeP2P\0" \
86 "\x12" "WakeOnResolve\0" \
87 "\x13" "BackgroundTrafficClass\0" \
88 "\x14" "IncludeAWDL\0" \
90 "\x16" "UnicastResponse\0" \
91 "\x17" "ValidateOptional\0" \
92 "\x18" "WakeOnlyService\0" \
93 "\x19" "ThresholdOne\0" \
94 "\x1A" "ThresholdFinder\0" \
95 "\x1B" "DenyCellular\0" \
96 "\x1C" "ServiceIndex\0" \
97 "\x1D" "DenyExpensive\0" \
98 "\x1E" "PathEvaluationDone\0" \
99 "\x1F" "AllowExpiredAnswers\0" \
102 #define DNSServiceFlagsToAddRmvStr( FLAGS ) ( ( (FLAGS) & kDNSServiceFlagsAdd ) ? "Add" : "Rmv" )
104 #define kDNSServiceProtocolDescriptors \
111 #define kBadDNSServiceRef ( (DNSServiceRef)(intptr_t) -1 )
113 //===========================================================================================================================
115 //===========================================================================================================================
118 #define kDNSMaxUDPMessageSize 512
119 #define kDNSMaxTCPMessageSize UINT16_MAX
121 #define kDNSRecordDataLengthMax UINT16_MAX
123 //===========================================================================================================================
125 //===========================================================================================================================
127 #define kMDNSPort 5353
129 #define kDefaultMDNSMessageID 0
130 #define kDefaultMDNSQueryFlags 0
132 #define kQClassUnicastResponseBit ( 1U << 15 )
133 #define kRRClassCacheFlushBit ( 1U << 15 )
135 // Recommended Resource Record TTL values. See <https://tools.ietf.org/html/rfc6762#section-10>.
137 #define kMDNSRecordTTL_Host 120 // TTL for resource records related to a host name, e.g., A, AAAA, SRV, etc.
138 #define kMDNSRecordTTL_Other 4500 // TTL for other resource records.
140 // Maximum mDNS Message Size. See <https://tools.ietf.org/html/rfc6762#section-17>.
142 #define kMDNSMessageSizeMax 8952 // 9000 B (Ethernet jumbo frame max size) - 40 B (IPv6 header) - 8 B (UDP header)
144 #define kLocalStr "\x05" "local"
145 #define kLocalLabel ( (const uint8_t *) kLocalStr )
146 #define kLocalName ( (const uint8_t *) kLocalStr )
147 #define kLocalNameLen sizeof( kLocalStr )
149 //===========================================================================================================================
150 // Test Address Blocks
151 //===========================================================================================================================
153 // IPv4 address block 203.0.113.0/24 (TEST-NET-3) is reserved for documentation. See <https://tools.ietf.org/html/rfc5737>.
155 #define kDNSServerBaseAddrV4 UINT32_C( 0xCB007100 ) // 203.0.113.0/24
157 // IPv6 address block 2001:db8::/32 is reserved for documentation. See <https://tools.ietf.org/html/rfc3849>.
159 static const uint8_t kDNSServerBaseAddrV6
[] =
161 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 2001:db8:1::/120
164 static const uint8_t kMDNSReplierBaseAddrV6
[] =
166 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 2001:db8:2::/96
169 check_compile_time( sizeof( kDNSServerBaseAddrV6
) == 16 );
170 check_compile_time( sizeof( kMDNSReplierBaseAddrV6
) == 16 );
172 // Bad IPv4 and IPv6 Address Blocks
173 // Used by the DNS server when it needs to respond with intentionally "bad" A/AAAA record data, i.e., IP addresses neither
174 // in 203.0.113.0/24 nor 2001:db8:1::/120.
176 #define kDNSServerBadBaseAddrV4 UINT32_C( 0x00000000 ) // 0.0.0.0/24
178 static const uint8_t kDNSServerBadBaseAddrV6
[] =
180 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 // ::ffff:0:0/120
183 check_compile_time( sizeof( kDNSServerBadBaseAddrV6
) == 16 );
185 //===========================================================================================================================
187 //===========================================================================================================================
189 #define kLowerAlphaNumericCharSet "abcdefghijklmnopqrstuvwxyz0123456789"
190 #define kLowerAlphaNumericCharSetSize sizeof_string( kLowerAlphaNumericCharSet )
192 #if( !defined( kWhiteSpaceCharSet ) )
193 #define kWhiteSpaceCharSet "\t\n\v\f\r "
196 // Note: strcpy_literal() appears in CoreUtils code, but isn't currently defined in framework headers.
198 #if( !defined( strcpy_literal ) )
199 #define strcpy_literal( DST, SRC ) memcpy( DST, SRC, sizeof( SRC ) )
202 #define _RandomStringExact( CHAR_SET, CHAR_SET_SIZE, CHAR_COUNT, OUT_STRING ) \
203 RandomString( CHAR_SET, CHAR_SET_SIZE, CHAR_COUNT, CHAR_COUNT, OUT_STRING )
205 #define kNoSuchRecordStr "No Such Record"
206 #define kNoSuchRecordAStr "No Such Record (A)"
207 #define kNoSuchRecordAAAAStr "No Such Record (AAAA)"
209 #define kRootLabel ( (const uint8_t *) "" )
211 //===========================================================================================================================
212 // Gerneral Command Options
213 //===========================================================================================================================
215 // Command option macros
217 #define Command( NAME, CALLBACK, SUB_OPTIONS, SHORT_HELP, IS_NOTCOMMON ) \
218 CLI_COMMAND_EX( NAME, CALLBACK, SUB_OPTIONS, (IS_NOTCOMMON) ? kCLIOptionFlags_NotCommon : kCLIOptionFlags_None, \
221 #define kRequiredOptionSuffix " [REQUIRED]"
223 #define MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
224 CLI_OPTION_MULTI_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, \
225 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
226 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
228 #define MultiStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
229 MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
231 #define IntegerOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
232 CLI_OPTION_INTEGER_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
233 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
234 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
236 #define DoubleOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
237 CLI_OPTION_DOUBLE_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
238 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
239 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
241 #define BooleanOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP ) \
242 CLI_OPTION_BOOLEAN( (SHORT_CHAR), (LONG_NAME), (VAL_PTR), (SHORT_HELP), NULL )
244 #define StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
245 CLI_OPTION_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
246 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
247 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
249 #define StringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
250 StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
252 #define CFStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
253 CLI_OPTION_CFSTRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
254 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
255 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
257 // DNS-SD API flag options
259 static int gDNSSDFlags
= 0;
260 static int gDNSSDFlag_AllowExpiredAnswers
= false;
261 static int gDNSSDFlag_BrowseDomains
= false;
262 static int gDNSSDFlag_DenyCellular
= false;
263 static int gDNSSDFlag_DenyConstrained
= false;
264 static int gDNSSDFlag_DenyExpensive
= false;
265 static int gDNSSDFlag_ForceMulticast
= false;
266 static int gDNSSDFlag_IncludeAWDL
= false;
267 static int gDNSSDFlag_KnownUnique
= false;
268 static int gDNSSDFlag_NoAutoRename
= false;
269 static int gDNSSDFlag_PathEvaluationDone
= false;
270 static int gDNSSDFlag_RegistrationDomains
= false;
271 static int gDNSSDFlag_ReturnIntermediates
= false;
272 static int gDNSSDFlag_Shared
= false;
273 static int gDNSSDFlag_SuppressUnusable
= false;
274 static int gDNSSDFlag_Timeout
= false;
275 static int gDNSSDFlag_UnicastResponse
= false;
276 static int gDNSSDFlag_Unique
= false;
277 static int gDNSSDFlag_WakeOnResolve
= false;
279 #define DNSSDFlagsOption() \
280 IntegerOption( 'f', "flags", &gDNSSDFlags, "flags", \
281 "DNSServiceFlags as an integer. This value is bitwise ORed with other single flag options.", false )
283 #define DNSSDFlagOption( SHORT_CHAR, FLAG_NAME ) \
284 BooleanOption( SHORT_CHAR, # FLAG_NAME, &gDNSSDFlag_ ## FLAG_NAME, "Use kDNSServiceFlags" # FLAG_NAME "." )
286 #define DNSSDFlagsOption_AllowExpiredAnswers() DNSSDFlagOption( 'X', AllowExpiredAnswers )
287 #define DNSSDFlagsOption_DenyCellular() DNSSDFlagOption( 'C', DenyCellular )
288 #define DNSSDFlagsOption_DenyConstrained() DNSSDFlagOption( 'R', DenyConstrained)
289 #define DNSSDFlagsOption_DenyExpensive() DNSSDFlagOption( 'E', DenyExpensive )
290 #define DNSSDFlagsOption_ForceMulticast() DNSSDFlagOption( 'M', ForceMulticast )
291 #define DNSSDFlagsOption_IncludeAWDL() DNSSDFlagOption( 'A', IncludeAWDL )
292 #define DNSSDFlagsOption_KnownUnique() DNSSDFlagOption( 'K', KnownUnique )
293 #define DNSSDFlagsOption_NoAutoRename() DNSSDFlagOption( 'N', NoAutoRename )
294 #define DNSSDFlagsOption_PathEvalDone() DNSSDFlagOption( 'P', PathEvaluationDone )
295 #define DNSSDFlagsOption_ReturnIntermediates() DNSSDFlagOption( 'I', ReturnIntermediates )
296 #define DNSSDFlagsOption_Shared() DNSSDFlagOption( 'S', Shared )
297 #define DNSSDFlagsOption_SuppressUnusable() DNSSDFlagOption( 'S', SuppressUnusable )
298 #define DNSSDFlagsOption_Timeout() DNSSDFlagOption( 'T', Timeout )
299 #define DNSSDFlagsOption_UnicastResponse() DNSSDFlagOption( 'U', UnicastResponse )
300 #define DNSSDFlagsOption_Unique() DNSSDFlagOption( 'U', Unique )
301 #define DNSSDFlagsOption_WakeOnResolve() DNSSDFlagOption( 'W', WakeOnResolve )
305 static const char * gInterface
= NULL
;
307 #define InterfaceOption() \
308 StringOption( 'i', "interface", &gInterface, "interface", \
309 "Network interface by name or index. Use index -1 for local-only.", false )
311 // Connection options
313 #define kConnectionArg_Normal ""
314 #define kConnectionArgPrefix_PID "pid:"
315 #define kConnectionArgPrefix_UUID "uuid:"
317 static const char * gConnectionOpt
= kConnectionArg_Normal
;
319 #define ConnectionOptions() \
320 { kCLIOptionType_String, 0, "connection", &gConnectionOpt, NULL, (intptr_t) kConnectionArg_Normal, "type", \
321 kCLIOptionFlags_OptionalArgument, NULL, NULL, NULL, NULL, \
322 "Specifies the type of main connection to use. See " kConnectionSection_Name " below.", NULL }
324 #define kConnectionSection_Name "Connection Option"
325 #define kConnectionSection_Text \
326 "The default behavior is to create a main connection with DNSServiceCreateConnection() and perform operations on\n" \
327 "the main connection using the kDNSServiceFlagsShareConnection flag. This behavior can be explicitly invoked by\n" \
328 "specifying the connection option without an argument, i.e.,\n" \
332 "To instead use a delegate connection created with DNSServiceCreateDelegateConnection(), use\n" \
334 " --connection=pid:<PID>\n" \
336 "to specify the delegator by PID, or use\n" \
338 " --connection=uuid:<UUID>\n" \
340 "to specify the delegator by UUID.\n" \
342 "To not use a main connection at all, but instead perform operations on their own implicit connections, use\n" \
346 #define ConnectionSection() CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
348 // Help text for record data options
350 #define kRDataArgPrefix_Domain "domain:"
351 #define kRDataArgPrefix_File "file:"
352 #define kRDataArgPrefix_HexString "hex:"
353 #define kRDataArgPrefix_IPv4 "ipv4:"
354 #define kRDataArgPrefix_IPv6 "ipv6:"
355 #define kRDataArgPrefix_SRV "srv:"
356 #define kRDataArgPrefix_String "string:"
357 #define kRDataArgPrefix_TXT "txt:"
359 #define kRecordDataSection_Name "Record Data Arguments"
360 #define kRecordDataSection_Text \
361 "A record data argument is specified in one of the following formats:\n" \
363 "Format Syntax Example\n" \
364 "Domain name domain:<domain name> domain:demo._test._tcp.local\n" \
365 "File containing record data file:<file path> file:/path/to/binary-rdata-file\n" \
366 "Hexadecimal string hex:<hex string> hex:c0000201 or hex:'C0 00 02 01'\n" \
367 "IPv4 address ipv4:<IPv4 address> ipv4:192.0.2.1\n" \
368 "IPv6 address ipv6:<IPv6 address> ipv6:2001:db8::1\n" \
369 "SRV record srv:<priority>,<weight>,<port>,<target> srv:0,0,64206,example.local\n" \
370 "String string:<string> string:'\\x09color=red'\n" \
371 "TXT record strings txt:<comma-delimited strings> txt:'vers=1.0,lang=en\\,es\\,fr,passreq'\n" \
373 "Note: The string format converts each \\xHH escape sequence into the octet represented by the HH hex digit pair.\n"
375 #define RecordDataSection() CLI_SECTION( kRecordDataSection_Name, kRecordDataSection_Text )
377 //===========================================================================================================================
379 //===========================================================================================================================
381 #define kOutputFormatStr_JSON "json"
382 #define kOutputFormatStr_XML "xml"
383 #define kOutputFormatStr_Binary "binary"
387 kOutputFormatType_Invalid
= 0,
388 kOutputFormatType_JSON
= 1,
389 kOutputFormatType_XML
= 2,
390 kOutputFormatType_Binary
= 3
394 #define FormatOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP, IS_REQUIRED ) \
395 StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, "format", SHORT_HELP, IS_REQUIRED, \
397 "Use '" kOutputFormatStr_JSON "' for JavaScript Object Notation (JSON).\n" \
398 "Use '" kOutputFormatStr_XML "' for property list XML version 1.0.\n" \
399 "Use '" kOutputFormatStr_Binary "' for property list binary version 1.0.\n" \
403 //===========================================================================================================================
404 // Browse Command Options
405 //===========================================================================================================================
407 static char ** gBrowse_ServiceTypes
= NULL
;
408 static size_t gBrowse_ServiceTypesCount
= 0;
409 static const char * gBrowse_Domain
= NULL
;
410 static int gBrowse_DoResolve
= false;
411 static int gBrowse_QueryTXT
= false;
412 static int gBrowse_TimeLimitSecs
= 0;
414 static CLIOption kBrowseOpts
[] =
417 MultiStringOption( 't', "type", &gBrowse_ServiceTypes
, &gBrowse_ServiceTypesCount
, "service type", "Service type(s), e.g., \"_ssh._tcp\".", true ),
418 StringOption( 'd', "domain", &gBrowse_Domain
, "domain", "Domain in which to browse for the service type(s).", false ),
420 CLI_OPTION_GROUP( "Flags" ),
422 DNSSDFlagsOption_IncludeAWDL(),
424 CLI_OPTION_GROUP( "Operation" ),
426 BooleanOption( 0 , "resolve", &gBrowse_DoResolve
, "Resolve service instances." ),
427 BooleanOption( 0 , "queryTXT", &gBrowse_QueryTXT
, "Query TXT records of service instances." ),
428 IntegerOption( 'l', "timeLimit", &gBrowse_TimeLimitSecs
, "seconds", "Specifies the max duration of the browse operation. Use '0' for no time limit.", false ),
434 //===========================================================================================================================
435 // GetAddrInfo Command Options
436 //===========================================================================================================================
438 static const char * gGetAddrInfo_Name
= NULL
;
439 static int gGetAddrInfo_ProtocolIPv4
= false;
440 static int gGetAddrInfo_ProtocolIPv6
= false;
441 static int gGetAddrInfo_OneShot
= false;
442 static int gGetAddrInfo_TimeLimitSecs
= 0;
444 static CLIOption kGetAddrInfoOpts
[] =
447 StringOption( 'n', "name", &gGetAddrInfo_Name
, "domain name", "Domain name to resolve.", true ),
448 BooleanOption( 0 , "ipv4", &gGetAddrInfo_ProtocolIPv4
, "Use kDNSServiceProtocol_IPv4." ),
449 BooleanOption( 0 , "ipv6", &gGetAddrInfo_ProtocolIPv6
, "Use kDNSServiceProtocol_IPv6." ),
451 CLI_OPTION_GROUP( "Flags" ),
453 DNSSDFlagsOption_AllowExpiredAnswers(),
454 DNSSDFlagsOption_DenyCellular(),
455 DNSSDFlagsOption_DenyConstrained(),
456 DNSSDFlagsOption_DenyExpensive(),
457 DNSSDFlagsOption_PathEvalDone(),
458 DNSSDFlagsOption_ReturnIntermediates(),
459 DNSSDFlagsOption_SuppressUnusable(),
460 DNSSDFlagsOption_Timeout(),
462 CLI_OPTION_GROUP( "Operation" ),
464 BooleanOption( 'o', "oneshot", &gGetAddrInfo_OneShot
, "Finish after first set of results." ),
465 IntegerOption( 'l', "timeLimit", &gGetAddrInfo_TimeLimitSecs
, "seconds", "Maximum duration of the GetAddrInfo operation. Use '0' for no time limit.", false ),
471 //===========================================================================================================================
472 // QueryRecord Command Options
473 //===========================================================================================================================
475 static const char * gQueryRecord_Name
= NULL
;
476 static const char * gQueryRecord_Type
= NULL
;
477 static int gQueryRecord_OneShot
= false;
478 static int gQueryRecord_TimeLimitSecs
= 0;
479 static int gQueryRecord_RawRData
= false;
481 static CLIOption kQueryRecordOpts
[] =
484 StringOption( 'n', "name", &gQueryRecord_Name
, "domain name", "Full domain name of record to query.", true ),
485 StringOption( 't', "type", &gQueryRecord_Type
, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
487 CLI_OPTION_GROUP( "Flags" ),
489 DNSSDFlagsOption_AllowExpiredAnswers(),
490 DNSSDFlagsOption_DenyCellular(),
491 DNSSDFlagsOption_DenyConstrained(),
492 DNSSDFlagsOption_DenyExpensive(),
493 DNSSDFlagsOption_ForceMulticast(),
494 DNSSDFlagsOption_IncludeAWDL(),
495 DNSSDFlagsOption_PathEvalDone(),
496 DNSSDFlagsOption_ReturnIntermediates(),
497 DNSSDFlagsOption_SuppressUnusable(),
498 DNSSDFlagsOption_Timeout(),
499 DNSSDFlagsOption_UnicastResponse(),
501 CLI_OPTION_GROUP( "Operation" ),
503 BooleanOption( 'o', "oneshot", &gQueryRecord_OneShot
, "Finish after first set of results." ),
504 IntegerOption( 'l', "timeLimit", &gQueryRecord_TimeLimitSecs
, "seconds", "Maximum duration of the query record operation. Use '0' for no time limit.", false ),
505 BooleanOption( 0 , "raw", &gQueryRecord_RawRData
, "Show record data as a hexdump." ),
511 //===========================================================================================================================
512 // Register Command Options
513 //===========================================================================================================================
515 static const char * gRegister_Name
= NULL
;
516 static const char * gRegister_Type
= NULL
;
517 static const char * gRegister_Domain
= NULL
;
518 static int gRegister_Port
= 0;
519 static const char * gRegister_TXT
= NULL
;
520 static int gRegister_LifetimeMs
= -1;
521 static const char ** gAddRecord_Types
= NULL
;
522 static size_t gAddRecord_TypesCount
= 0;
523 static const char ** gAddRecord_Data
= NULL
;
524 static size_t gAddRecord_DataCount
= 0;
525 static const char ** gAddRecord_TTLs
= NULL
;
526 static size_t gAddRecord_TTLsCount
= 0;
527 static const char * gUpdateRecord_Data
= NULL
;
528 static int gUpdateRecord_DelayMs
= 0;
529 static int gUpdateRecord_TTL
= 0;
531 static CLIOption kRegisterOpts
[] =
534 StringOption( 'n', "name", &gRegister_Name
, "service name", "Name of service.", false ),
535 StringOption( 't', "type", &gRegister_Type
, "service type", "Service type, e.g., \"_ssh._tcp\".", true ),
536 StringOption( 'd', "domain", &gRegister_Domain
, "domain", "Domain in which to advertise the service.", false ),
537 IntegerOption( 'p', "port", &gRegister_Port
, "port number", "Service's port number.", true ),
538 StringOption( 0 , "txt", &gRegister_TXT
, "record data", "The TXT record data. See " kRecordDataSection_Name
" below.", false ),
540 CLI_OPTION_GROUP( "Flags" ),
542 DNSSDFlagsOption_IncludeAWDL(),
543 DNSSDFlagsOption_KnownUnique(),
544 DNSSDFlagsOption_NoAutoRename(),
546 CLI_OPTION_GROUP( "Operation" ),
547 IntegerOption( 'l', "lifetime", &gRegister_LifetimeMs
, "ms", "Lifetime of the service registration in milliseconds.", false ),
549 CLI_OPTION_GROUP( "Options for updating the registered service's primary TXT record with DNSServiceUpdateRecord()\n" ),
550 StringOption( 0 , "updateData", &gUpdateRecord_Data
, "record data", "Record data for the record update. See " kRecordDataSection_Name
" below.", false ),
551 IntegerOption( 0 , "updateDelay", &gUpdateRecord_DelayMs
, "ms", "Number of milliseconds after registration to wait before record update.", false ),
552 IntegerOption( 0 , "updateTTL", &gUpdateRecord_TTL
, "seconds", "Time-to-live of the updated record.", false ),
554 CLI_OPTION_GROUP( "Options for adding extra record(s) to the registered service with DNSServiceAddRecord()\n" ),
555 MultiStringOption( 0 , "addType", &gAddRecord_Types
, &gAddRecord_TypesCount
, "record type", "Type of additional record by name (e.g., TXT, SRV, etc.) or number.", false ),
556 MultiStringOptionEx( 0 , "addData", &gAddRecord_Data
, &gAddRecord_DataCount
, "record data", "Additional record's data. See " kRecordDataSection_Name
" below.", false, NULL
),
557 MultiStringOption( 0 , "addTTL", &gAddRecord_TTLs
, &gAddRecord_TTLsCount
, "seconds", "Time-to-live of additional record in seconds. Use '0' for default.", false ),
563 //===========================================================================================================================
564 // RegisterRecord Command Options
565 //===========================================================================================================================
567 static const char * gRegisterRecord_Name
= NULL
;
568 static const char * gRegisterRecord_Type
= NULL
;
569 static const char * gRegisterRecord_Data
= NULL
;
570 static int gRegisterRecord_TTL
= 0;
571 static int gRegisterRecord_LifetimeMs
= -1;
572 static const char * gRegisterRecord_UpdateData
= NULL
;
573 static int gRegisterRecord_UpdateDelayMs
= 0;
574 static int gRegisterRecord_UpdateTTL
= 0;
576 static CLIOption kRegisterRecordOpts
[] =
579 StringOption( 'n', "name", &gRegisterRecord_Name
, "record name", "Fully qualified domain name of record.", true ),
580 StringOption( 't', "type", &gRegisterRecord_Type
, "record type", "Record type by name (e.g., TXT, PTR, A) or number.", true ),
581 StringOption( 'd', "data", &gRegisterRecord_Data
, "record data", "The record data. See " kRecordDataSection_Name
" below.", false ),
582 IntegerOption( 0 , "ttl", &gRegisterRecord_TTL
, "seconds", "Time-to-live in seconds. Use '0' for default.", false ),
584 CLI_OPTION_GROUP( "Flags" ),
586 DNSSDFlagsOption_IncludeAWDL(),
587 DNSSDFlagsOption_KnownUnique(),
588 DNSSDFlagsOption_Shared(),
589 DNSSDFlagsOption_Unique(),
591 CLI_OPTION_GROUP( "Operation" ),
592 IntegerOption( 'l', "lifetime", &gRegisterRecord_LifetimeMs
, "ms", "Lifetime of the service registration in milliseconds.", false ),
594 CLI_OPTION_GROUP( "Options for updating the registered record with DNSServiceUpdateRecord()\n" ),
595 StringOption( 0 , "updateData", &gRegisterRecord_UpdateData
, "record data", "Record data for the record update.", false ),
596 IntegerOption( 0 , "updateDelay", &gRegisterRecord_UpdateDelayMs
, "ms", "Number of milliseconds after registration to wait before record update.", false ),
597 IntegerOption( 0 , "updateTTL", &gRegisterRecord_UpdateTTL
, "seconds", "Time-to-live of the updated record.", false ),
603 //===========================================================================================================================
604 // Resolve Command Options
605 //===========================================================================================================================
607 static char * gResolve_Name
= NULL
;
608 static char * gResolve_Type
= NULL
;
609 static char * gResolve_Domain
= NULL
;
610 static int gResolve_TimeLimitSecs
= 0;
612 static CLIOption kResolveOpts
[] =
615 StringOption( 'n', "name", &gResolve_Name
, "service name", "Name of the service instance to resolve.", true ),
616 StringOption( 't', "type", &gResolve_Type
, "service type", "Type of the service instance to resolve.", true ),
617 StringOption( 'd', "domain", &gResolve_Domain
, "domain", "Domain of the service instance to resolve.", true ),
619 CLI_OPTION_GROUP( "Flags" ),
621 DNSSDFlagsOption_ForceMulticast(),
622 DNSSDFlagsOption_IncludeAWDL(),
623 DNSSDFlagsOption_ReturnIntermediates(),
624 DNSSDFlagsOption_WakeOnResolve(),
626 CLI_OPTION_GROUP( "Operation" ),
628 IntegerOption( 'l', "timeLimit", &gResolve_TimeLimitSecs
, "seconds", "Maximum duration of the resolve operation. Use '0' for no time limit.", false ),
634 //===========================================================================================================================
635 // Reconfirm Command Options
636 //===========================================================================================================================
638 static const char * gReconfirmRecord_Name
= NULL
;
639 static const char * gReconfirmRecord_Type
= NULL
;
640 static const char * gReconfirmRecord_Class
= NULL
;
641 static const char * gReconfirmRecord_Data
= NULL
;
643 static CLIOption kReconfirmOpts
[] =
646 StringOption( 'n', "name", &gReconfirmRecord_Name
, "record name", "Full name of the record to reconfirm.", true ),
647 StringOption( 't', "type", &gReconfirmRecord_Type
, "record type", "Type of the record to reconfirm.", true ),
648 StringOption( 'c', "class", &gReconfirmRecord_Class
, "record class", "Class of the record to reconfirm. Default class is IN.", false ),
649 StringOption( 'd', "data", &gReconfirmRecord_Data
, "record data", "The record data. See " kRecordDataSection_Name
" below.", false ),
651 CLI_OPTION_GROUP( "Flags" ),
658 //===========================================================================================================================
659 // getaddrinfo-POSIX Command Options
660 //===========================================================================================================================
662 static const char * gGAIPOSIX_HostName
= NULL
;
663 static const char * gGAIPOSIX_ServName
= NULL
;
664 static const char * gGAIPOSIX_Family
= NULL
;
665 static int gGAIPOSIXFlag_AddrConfig
= false;
666 static int gGAIPOSIXFlag_All
= false;
667 static int gGAIPOSIXFlag_CanonName
= false;
668 static int gGAIPOSIXFlag_NumericHost
= false;
669 static int gGAIPOSIXFlag_NumericServ
= false;
670 static int gGAIPOSIXFlag_Passive
= false;
671 static int gGAIPOSIXFlag_V4Mapped
= false;
672 #if( defined( AI_V4MAPPED_CFG ) )
673 static int gGAIPOSIXFlag_V4MappedCFG
= false;
675 #if( defined( AI_DEFAULT ) )
676 static int gGAIPOSIXFlag_Default
= false;
678 #if( defined( AI_UNUSABLE ) )
679 static int gGAIPOSIXFlag_Unusable
= false;
682 static CLIOption kGetAddrInfoPOSIXOpts
[] =
684 StringOption( 'n', "hostname", &gGAIPOSIX_HostName
, "hostname", "Domain name to resolve or an IPv4 or IPv6 address.", true ),
685 StringOption( 's', "servname", &gGAIPOSIX_ServName
, "servname", "Port number in decimal or service name from services(5).", false ),
687 CLI_OPTION_GROUP( "Hints" ),
688 StringOptionEx( 'f', "family", &gGAIPOSIX_Family
, "address family", "Address family to use for hints ai_family field.", false,
690 "Possible address family values are 'inet' for AF_INET, 'inet6' for AF_INET6, or 'unspec' for AF_UNSPEC. If no\n"
691 "address family is specified, then AF_UNSPEC is used.\n"
693 BooleanOption( 0 , "flag-addrconfig", &gGAIPOSIXFlag_AddrConfig
, "In hints ai_flags field, set AI_ADDRCONFIG." ),
694 BooleanOption( 0 , "flag-all", &gGAIPOSIXFlag_All
, "In hints ai_flags field, set AI_ALL." ),
695 BooleanOption( 0 , "flag-canonname", &gGAIPOSIXFlag_CanonName
, "In hints ai_flags field, set AI_CANONNAME." ),
696 BooleanOption( 0 , "flag-numerichost", &gGAIPOSIXFlag_NumericHost
, "In hints ai_flags field, set AI_NUMERICHOST." ),
697 BooleanOption( 0 , "flag-numericserv", &gGAIPOSIXFlag_NumericServ
, "In hints ai_flags field, set AI_NUMERICSERV." ),
698 BooleanOption( 0 , "flag-passive", &gGAIPOSIXFlag_Passive
, "In hints ai_flags field, set AI_PASSIVE." ),
699 BooleanOption( 0 , "flag-v4mapped", &gGAIPOSIXFlag_V4Mapped
, "In hints ai_flags field, set AI_V4MAPPED." ),
700 #if( defined( AI_V4MAPPED_CFG ) )
701 BooleanOption( 0 , "flag-v4mappedcfg", &gGAIPOSIXFlag_V4MappedCFG
, "In hints ai_flags field, set AI_V4MAPPED_CFG." ),
703 #if( defined( AI_DEFAULT ) )
704 BooleanOption( 0 , "flag-default", &gGAIPOSIXFlag_Default
, "In hints ai_flags field, set AI_DEFAULT." ),
706 #if( defined( AI_UNUSABLE ) )
707 BooleanOption( 0 , "flag-unusable", &gGAIPOSIXFlag_Unusable
, "In hints ai_flags field, set AI_UNUSABLE." ),
710 CLI_SECTION( "Notes", "See getaddrinfo(3) man page for more details.\n" ),
714 //===========================================================================================================================
715 // ReverseLookup Command Options
716 //===========================================================================================================================
718 static const char * gReverseLookup_IPAddr
= NULL
;
719 static int gReverseLookup_OneShot
= false;
720 static int gReverseLookup_TimeLimitSecs
= 0;
722 static CLIOption kReverseLookupOpts
[] =
725 StringOption( 'a', "address", &gReverseLookup_IPAddr
, "IP address", "IPv4 or IPv6 address for which to perform a reverse IP lookup.", true ),
727 CLI_OPTION_GROUP( "Flags" ),
729 DNSSDFlagsOption_ForceMulticast(),
730 DNSSDFlagsOption_ReturnIntermediates(),
731 DNSSDFlagsOption_SuppressUnusable(),
733 CLI_OPTION_GROUP( "Operation" ),
735 BooleanOption( 'o', "oneshot", &gReverseLookup_OneShot
, "Finish after first set of results." ),
736 IntegerOption( 'l', "timeLimit", &gReverseLookup_TimeLimitSecs
, "seconds", "Specifies the max duration of the query record operation. Use '0' for no time limit.", false ),
742 //===========================================================================================================================
743 // PortMapping Command Options
744 //===========================================================================================================================
746 static int gPortMapping_ProtocolTCP
= false;
747 static int gPortMapping_ProtocolUDP
= false;
748 static int gPortMapping_InternalPort
= 0;
749 static int gPortMapping_ExternalPort
= 0;
750 static int gPortMapping_TTL
= 0;
752 static CLIOption kPortMappingOpts
[] =
755 BooleanOption( 0, "tcp", &gPortMapping_ProtocolTCP
, "Use kDNSServiceProtocol_TCP." ),
756 BooleanOption( 0, "udp", &gPortMapping_ProtocolUDP
, "Use kDNSServiceProtocol_UDP." ),
757 IntegerOption( 0, "internalPort", &gPortMapping_InternalPort
, "port number", "Internal port.", false ),
758 IntegerOption( 0, "externalPort", &gPortMapping_ExternalPort
, "port number", "Requested external port. Use '0' for any external port.", false ),
759 IntegerOption( 0, "ttl", &gPortMapping_TTL
, "seconds", "Requested TTL (renewal period) in seconds. Use '0' for a default value.", false ),
761 CLI_OPTION_GROUP( "Flags" ),
764 CLI_OPTION_GROUP( "Operation" ),
771 //===========================================================================================================================
772 // BrowseAll Command Options
773 //===========================================================================================================================
775 static const char * gBrowseAll_Domain
= NULL
;
776 static const char ** gBrowseAll_ServiceTypes
= NULL
;
777 static size_t gBrowseAll_ServiceTypesCount
= 0;
778 static int gBrowseAll_BrowseTimeSecs
= 5;
779 static int gBrowseAll_ConnectTimeout
= 0;
781 static CLIOption kBrowseAllOpts
[] =
784 StringOption( 'd', "domain", &gBrowseAll_Domain
, "domain", "Domain in which to browse for the service.", false ),
785 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 ),
787 CLI_OPTION_GROUP( "Flags" ),
788 DNSSDFlagsOption_IncludeAWDL(),
790 CLI_OPTION_GROUP( "Operation" ),
791 IntegerOption( 'b', "browseTime", &gBrowseAll_BrowseTimeSecs
, "seconds", "Amount of time to spend browsing in seconds. (default: 5)", false ),
792 IntegerOption( 'c', "connectTimeout", &gBrowseAll_ConnectTimeout
, "seconds", "Timeout for connection attempts. If <= 0, no connections are attempted. (default: 0)", false ),
796 //===========================================================================================================================
797 // GetNameInfo Command Options
798 //===========================================================================================================================
800 static void GetNameInfoCmd( void );
802 static char * gGetNameInfo_IPAddress
= NULL
;
803 static int gGetNameInfoFlag_DGram
= false;
804 static int gGetNameInfoFlag_NameReqd
= false;
805 static int gGetNameInfoFlag_NoFQDN
= false;
806 static int gGetNameInfoFlag_NumericHost
= false;
807 static int gGetNameInfoFlag_NumericScope
= false;
808 static int gGetNameInfoFlag_NumericServ
= false;
810 static CLIOption kGetNameInfoOpts
[] =
812 StringOption( 'a', "address", &gGetNameInfo_IPAddress
, "IP address", "IPv4 or IPv6 address to use in sockaddr structure.", true ),
814 CLI_OPTION_GROUP( "Flags" ),
815 BooleanOption( 0 , "flag-dgram", &gGetNameInfoFlag_DGram
, "Use NI_DGRAM flag." ),
816 BooleanOption( 0 , "flag-namereqd", &gGetNameInfoFlag_NameReqd
, "Use NI_NAMEREQD flag." ),
817 BooleanOption( 0 , "flag-nofqdn", &gGetNameInfoFlag_NoFQDN
, "Use NI_NOFQDN flag." ),
818 BooleanOption( 0 , "flag-numerichost", &gGetNameInfoFlag_NumericHost
, "Use NI_NUMERICHOST flag." ),
819 BooleanOption( 0 , "flag-numericscope", &gGetNameInfoFlag_NumericScope
, "Use NI_NUMERICSCOPE flag." ),
820 BooleanOption( 0 , "flag-numericserv", &gGetNameInfoFlag_NumericServ
, "Use NI_NUMERICSERV flag." ),
822 CLI_SECTION( "Notes", "See getnameinfo(3) man page for more details.\n" ),
826 //===========================================================================================================================
827 // GetAddrInfoStress Command Options
828 //===========================================================================================================================
830 static int gGAIStress_TestDurationSecs
= 0;
831 static int gGAIStress_ConnectionCount
= 0;
832 static int gGAIStress_DurationMinMs
= 0;
833 static int gGAIStress_DurationMaxMs
= 0;
834 static int gGAIStress_RequestCountMax
= 0;
836 static CLIOption kGetAddrInfoStressOpts
[] =
840 CLI_OPTION_GROUP( "Flags" ),
841 DNSSDFlagsOption_ReturnIntermediates(),
842 DNSSDFlagsOption_SuppressUnusable(),
844 CLI_OPTION_GROUP( "Operation" ),
845 IntegerOption( 0, "testDuration", &gGAIStress_TestDurationSecs
, "seconds", "Stress test duration in seconds. Use '0' for forever.", false ),
846 IntegerOption( 0, "connectionCount", &gGAIStress_ConnectionCount
, "integer", "Number of simultaneous DNS-SD connections.", true ),
847 IntegerOption( 0, "requestDurationMin", &gGAIStress_DurationMinMs
, "ms", "Minimum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
848 IntegerOption( 0, "requestDurationMax", &gGAIStress_DurationMaxMs
, "ms", "Maximum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
849 IntegerOption( 0, "consecutiveRequestMax", &gGAIStress_RequestCountMax
, "integer", "Maximum number of requests on a connection before restarting it.", true ),
853 //===========================================================================================================================
854 // DNSQuery Command Options
855 //===========================================================================================================================
857 static char * gDNSQuery_Name
= NULL
;
858 static char * gDNSQuery_Type
= "A";
859 static char * gDNSQuery_Server
= NULL
;
860 static int gDNSQuery_TimeLimitSecs
= 5;
861 static int gDNSQuery_UseTCP
= false;
862 static int gDNSQuery_Flags
= kDNSHeaderFlag_RecursionDesired
;
863 static int gDNSQuery_RawRData
= false;
864 static int gDNSQuery_Verbose
= false;
866 #if( TARGET_OS_DARWIN )
867 #define kDNSQueryServerOptionIsRequired false
869 #define kDNSQueryServerOptionIsRequired true
872 static CLIOption kDNSQueryOpts
[] =
874 StringOption( 'n', "name", &gDNSQuery_Name
, "name", "Question name (QNAME) to put in DNS query message.", true ),
875 StringOption( 't', "type", &gDNSQuery_Type
, "type", "Question type (QTYPE) to put in DNS query message. Default value is 'A'.", false ),
876 StringOption( 's', "server", &gDNSQuery_Server
, "IP address", "DNS server's IPv4 or IPv6 address.", kDNSQueryServerOptionIsRequired
),
877 IntegerOption( 'l', "timeLimit", &gDNSQuery_TimeLimitSecs
, "seconds", "Specifies query time limit. Use '-1' for no limit and '0' to exit immediately after sending.", false ),
878 BooleanOption( 0 , "tcp", &gDNSQuery_UseTCP
, "Send the DNS query via TCP instead of UDP." ),
879 IntegerOption( 'f', "flags", &gDNSQuery_Flags
, "flags", "16-bit value for DNS header flags/codes field. Default value is 0x0100 (Recursion Desired).", false ),
880 BooleanOption( 0 , "raw", &gDNSQuery_RawRData
, "Present record data as a hexdump." ),
881 BooleanOption( 'v', "verbose", &gDNSQuery_Verbose
, "Prints the DNS message to be sent to the server." ),
885 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
886 //===========================================================================================================================
887 // DNSCrypt Command Options
888 //===========================================================================================================================
890 static char * gDNSCrypt_ProviderName
= NULL
;
891 static char * gDNSCrypt_ProviderKey
= NULL
;
892 static char * gDNSCrypt_Name
= NULL
;
893 static char * gDNSCrypt_Type
= NULL
;
894 static char * gDNSCrypt_Server
= NULL
;
895 static int gDNSCrypt_TimeLimitSecs
= 5;
896 static int gDNSCrypt_RawRData
= false;
897 static int gDNSCrypt_Verbose
= false;
899 static CLIOption kDNSCryptOpts
[] =
901 StringOption( 'p', "providerName", &gDNSCrypt_ProviderName
, "name", "The DNSCrypt provider name.", true ),
902 StringOption( 'k', "providerKey", &gDNSCrypt_ProviderKey
, "hex string", "The DNSCrypt provider's public signing key.", true ),
903 StringOption( 'n', "name", &gDNSCrypt_Name
, "name", "Question name (QNAME) to put in DNS query message.", true ),
904 StringOption( 't', "type", &gDNSCrypt_Type
, "type", "Question type (QTYPE) to put in DNS query message.", true ),
905 StringOption( 's', "server", &gDNSCrypt_Server
, "IP address", "DNS server's IPv4 or IPv6 address.", true ),
906 IntegerOption( 'l', "timeLimit", &gDNSCrypt_TimeLimitSecs
, "seconds", "Specifies query time limit. Use '-1' for no time limit and '0' to exit immediately after sending.", false ),
907 BooleanOption( 0 , "raw", &gDNSCrypt_RawRData
, "Present record data as a hexdump." ),
908 BooleanOption( 'v', "verbose", &gDNSCrypt_Verbose
, "Prints the DNS message to be sent to the server." ),
913 //===========================================================================================================================
914 // MDNSQuery Command Options
915 //===========================================================================================================================
917 static char * gMDNSQuery_Name
= NULL
;
918 static char * gMDNSQuery_Type
= NULL
;
919 static int gMDNSQuery_SourcePort
= 0;
920 static int gMDNSQuery_IsQU
= false;
921 static int gMDNSQuery_RawRData
= false;
922 static int gMDNSQuery_UseIPv4
= false;
923 static int gMDNSQuery_UseIPv6
= false;
924 static int gMDNSQuery_AllResponses
= false;
925 static int gMDNSQuery_ReceiveSecs
= 1;
927 static CLIOption kMDNSQueryOpts
[] =
929 StringOption( 'i', "interface", &gInterface
, "name or index", "Network interface by name or index.", true ),
930 StringOption( 'n', "name", &gMDNSQuery_Name
, "name", "Question name (QNAME) to put in mDNS message.", true ),
931 StringOption( 't', "type", &gMDNSQuery_Type
, "type", "Question type (QTYPE) to put in mDNS message.", true ),
932 IntegerOption( 'p', "sourcePort", &gMDNSQuery_SourcePort
, "port number", "UDP source port to use when sending mDNS messages. Default is 5353 for QM questions.", false ),
933 BooleanOption( 'u', "QU", &gMDNSQuery_IsQU
, "Set the unicast-response bit, i.e., send a QU question." ),
934 BooleanOption( 0 , "raw", &gMDNSQuery_RawRData
, "Present record data as a hexdump." ),
935 BooleanOption( 0 , "ipv4", &gMDNSQuery_UseIPv4
, "Use IPv4." ),
936 BooleanOption( 0 , "ipv6", &gMDNSQuery_UseIPv6
, "Use IPv6." ),
937 BooleanOption( 'a', "allResponses", &gMDNSQuery_AllResponses
, "Print all received mDNS messages, not just those containing answers." ),
938 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 ),
942 //===========================================================================================================================
943 // MDNSCollider Command Options
944 //===========================================================================================================================
946 #define kMDNSColliderProgramSection_Intro \
947 "Programs dictate when the collider sends out unsolicited response messages for its record and how the collider\n" \
948 "ought to react to probe queries that match its record's name, if at all.\n" \
950 "For example, suppose that the goal is to cause a specific unique record in the verified state to be renamed.\n" \
951 "The collider should be invoked such that its record's name is equal to that of the record being targeted. Also,\n" \
952 "the record's type and data should be such that no record with that name, type, and data combination currently\n" \
953 "exists. If the mDNS responder that owns the record follows sections 8.1 and 9 of RFC 6762, then the goal can be\n" \
954 "accomplished with the following program:\n" \
956 " probes 3r; send; wait 5000\n" \
958 "The first command, 'probes 3r', tells the collider to respond to the next three probe queries that match its\n" \
959 "record's name. The second command, makes the collider send an unsolicited response message that contains its\n" \
960 "record in the answer section. The third command makes the collider wait for five seconds before exiting, which\n" \
961 "is more than enough time for the collider to respond to probe queries.\n" \
963 "The send command will cause the targeted record to go into the probing state per section 9 since the collider's\n" \
964 "record conflicts with target record. Per the probes command, the subsequent probe query sent during the probing\n" \
965 "state will be answered by the collider, which will cause the record to be renamed per section 8.1.\n"
967 #define kMDNSColliderProgramSection_Probes \
968 "The probes command defines how the collider ought to react to probe queries that match its record's name.\n" \
970 "Usage: probes [<action-string>]\n" \
972 "The syntax for an action-string is\n" \
974 " <action-string> ::= <action> | <action-string> \"-\" <action>\n" \
975 " <action> ::= [<repeat-count>] <action-code>\n" \
976 " <repeat-count> ::= \"1\" | \"2\" | ... | \"10\"\n" \
977 " <action-code> ::= \"n\" | \"r\" | \"u\" | \"m\" | \"p\"\n" \
979 "An expanded action-string is defined as\n" \
981 " <expanded-action-string> ::= <action-code> | <expanded-action-string> \"-\" <action-code>\n" \
983 "The action-string argument is converted into an expanded-action-string by expanding each action with a\n" \
984 "repeat-count into an expanded-action-string consisting of exactly <repeat-count> <action-code>s. For example,\n" \
985 "2n-r expands to n-n-r. Action-strings that expand to expanded-action-strings with more than 10 action-codes\n" \
986 "are not allowed.\n" \
988 "When the probes command is executed, it does two things. Firstly, it resets to zero the collider's count of\n" \
989 "probe queries that match its record's name. Secondly, it defines how the collider ought to react to such probe\n" \
990 "queries based on the action-string argument. Specifically, the nth action-code in the expanded version of the\n" \
991 "action-string argument defines how the collider ought to react to the nth received probe query:\n" \
996 " r Respond to the probe query.\n" \
997 " u Respond to the probe query via unicast.\n" \
998 " m Respond to the probe query via multicast.\n" \
999 " p Multicast own probe query. (Useful for causing simultaneous probe scenarios.)\n" \
1001 "Note: If no action is defined for a received probe query, then the collider does nothing, i.e., it doesn't send\n" \
1002 "a response nor does it multicast its own probe query.\n"
1004 #define kMDNSColliderProgramSection_Send \
1005 "The send command multicasts an unsolicited mDNS response containing the collider's record in the answer\n" \
1006 "section, which can be used to force unique records with the same record name into the probing state.\n" \
1010 #define kMDNSColliderProgramSection_Wait \
1011 "The wait command pauses program execution for the interval of time specified by its argument.\n" \
1013 "Usage: wait <milliseconds>\n"
1015 #define kMDNSColliderProgramSection_Loop \
1016 "The loop command starts a counting loop. The done statement marks the end of the loop body. The loop command's\n" \
1017 "argument specifies the number of loop iterations. Note: Loop nesting is supported up to a depth of 16.\n" \
1019 "Usage: loop <non-zero count>; ... ; done\n" \
1021 "For example, the following program sends three unsolicited responses at an approximate rate of one per second:\n" \
1023 " loop 3; wait 1000; send; done"
1025 #define ConnectionSection() CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
1027 static const char * gMDNSCollider_Name
= NULL
;
1028 static const char * gMDNSCollider_Type
= NULL
;
1029 static const char * gMDNSCollider_RecordData
= NULL
;
1030 static int gMDNSCollider_UseIPv4
= false;
1031 static int gMDNSCollider_UseIPv6
= false;
1032 static const char * gMDNSCollider_Program
= NULL
;
1034 static CLIOption kMDNSColliderOpts
[] =
1036 StringOption( 'i', "interface", &gInterface
, "name or index", "Network interface by name or index.", true ),
1037 StringOption( 'n', "name", &gMDNSCollider_Name
, "name", "Collider's record name.", true ),
1038 StringOption( 't', "type", &gMDNSCollider_Type
, "type", "Collider's record type.", true ),
1039 StringOption( 'd', "data", &gMDNSCollider_RecordData
, "record data", "Collider's record data. See " kRecordDataSection_Name
" below.", true ),
1040 StringOption( 'p', "program", &gMDNSCollider_Program
, "program", "Program to execute. See Program section below.", true ),
1041 BooleanOption( 0 , "ipv4", &gMDNSCollider_UseIPv4
, "Use IPv4." ),
1042 BooleanOption( 0 , "ipv6", &gMDNSCollider_UseIPv6
, "Use IPv6." ),
1044 RecordDataSection(),
1045 CLI_SECTION( "Program", kMDNSColliderProgramSection_Intro
),
1046 CLI_SECTION( "Program Command: probes", kMDNSColliderProgramSection_Probes
),
1047 CLI_SECTION( "Program Command: send", kMDNSColliderProgramSection_Send
),
1048 CLI_SECTION( "Program Command: wait", kMDNSColliderProgramSection_Wait
),
1049 CLI_SECTION( "Program Command: loop", kMDNSColliderProgramSection_Loop
),
1053 static void MDNSColliderCmd( void );
1055 //===========================================================================================================================
1056 // PIDToUUID Command Options
1057 //===========================================================================================================================
1059 static int gPIDToUUID_PID
= 0;
1061 static CLIOption kPIDToUUIDOpts
[] =
1063 IntegerOption( 'p', "pid", &gPIDToUUID_PID
, "PID", "Process ID.", true ),
1067 //===========================================================================================================================
1068 // DNSServer Command Options
1069 //===========================================================================================================================
1071 #define kDNSServerInfoText_Intro \
1072 "The DNS server answers certain queries in the d.test. domain. Responses are dynamically generated based on the\n" \
1073 "presence of special labels in the query's QNAME. There are currently eight types of special labels that can be\n" \
1074 "used to generate specific responses: Alias labels, Alias-TTL labels, Count labels, Tag labels, TTL labels, the\n" \
1075 "IPv4 label, the IPv6 label, and SRV labels.\n" \
1077 "Note: Sub-strings representing integers in domain name labels are in decimal notation and without leading zeros.\n"
1079 #define kDNSServerInfoText_NameExistence \
1080 "A name is considered to exist if it's an Address name or an SRV name.\n" \
1082 "An Address name is defined as a name that ends with d.test., and the other labels, if any, and in no particular\n" \
1083 "order, unless otherwise noted, consist of\n" \
1085 " 1. at most one Alias or Alias-TTL label as the first label;\n" \
1086 " 2. at most one Count label;\n" \
1087 " 3. zero or more Tag labels;\n" \
1088 " 4. at most one TTL label; and\n" \
1089 " 5. at most one IPv4 or IPv6 label.\n" \
1091 "An SRV name is defined as a name with the following form:\n" \
1093 " _<service>._<proto>[.<parent domain>][.<SRV label 1>[.<target 1>][.<SRV label 2>[.<target 2>][...]]].d.test.\n" \
1095 "See \"SRV Names\" for details.\n"
1097 #define kDNSServerInfoText_ResourceRecords \
1098 "Currently, the server only supports CNAME, A, AAAA, and SRV records.\n" \
1100 "Address names that begin with an Alias or Alias-TTL label are aliases of canonical names, i.e., they're the\n" \
1101 "names of CNAME records. See \"Alias Labels\" and \"Alias-TTL Labels\" for details.\n" \
1103 "A canonical Address name can exclusively be the name of one or more A records, can exclusively be the name or\n" \
1104 "one or more AAAA records, or can be the name of both A and AAAA records. Address names that contain an IPv4\n" \
1105 "label have at least one A record, but no AAAA records. Address names that contain an IPv6 label, have at least\n" \
1106 "one AAAA record, but no A records. All other Address names have at least one A record and at least one AAAA\n" \
1107 "record. See \"Count Labels\" for how the number of address records for a given Address name is determined.\n" \
1109 "A records contain IPv4 addresses in the 203.0.113.0/24 block, while AAAA records contain IPv6 addresses in the\n" \
1110 "2001:db8:1::/120 block. Both of these address blocks are reserved for documentation. See\n" \
1111 "<https://tools.ietf.org/html/rfc5737> and <https://tools.ietf.org/html/rfc3849>.\n" \
1113 "SRV names are names of SRV records.\n" \
1115 "Unless otherwise specified, all resource records will use a default TTL. The default TTL can be set with the\n" \
1116 "--defaultTTL option. See \"Alias-TTL Labels\" and \"TTL Labels\" for details on how to query for CNAME, A, and\n" \
1117 "AAAA records with specific TTL values.\n"
1119 #define kDNSServerInfoText_AliasLabel \
1120 "Alias labels are of the form \"alias\" or \"alias-N\", where N is an integer in [2, 2^31 - 1].\n" \
1122 "If QNAME is an Address name and its first label is Alias label \"alias-N\", then the response will contain\n" \
1123 "exactly N CNAME records:\n" \
1125 " 1. For each i in [3, N], the response will contain a CNAME record whose name is identical to QNAME, except\n" \
1126 " that the first label is \"alias-i\" instead, and whose RDATA is the name of the other CNAME record whose\n" \
1127 " name has \"alias-(i - 1)\" as its first label.\n" \
1129 " 2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1130 " is \"alias-2\" instead, and whose RDATA is the name identical to QNAME, except that the first label is\n" \
1131 " \"alias\" instead.\n" \
1133 " 3. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1134 " is \"alias\" instead, and whose RDATA is the name identical to QNAME minus its first label.\n" \
1136 "If QNAME is an Address name and its first label is Alias label \"alias\", then the response will contain a\n" \
1137 "single CNAME record. The CNAME record's name will be equal to QNAME and its RDATA will be the name identical to\n" \
1138 "QNAME minus its first label.\n" \
1140 "Example. A response to a query with a QNAME of alias-3.count-5.d.test will contain the following CNAME\n" \
1143 " alias-4.count-5.d.test. 60 IN CNAME alias-3.count-5.d.test.\n" \
1144 " alias-3.count-5.d.test. 60 IN CNAME alias-2.count-5.d.test.\n" \
1145 " alias-2.count-5.d.test. 60 IN CNAME alias.count-5.d.test.\n" \
1146 " alias.count-5.d.test. 60 IN CNAME count-5.d.test.\n"
1148 #define kDNSServerInfoText_AliasTTLLabel \
1149 "Alias-TTL labels are of the form \"alias-ttl-T_1[-T_2[...-T_N]]\", where each T_i is an integer in\n" \
1150 "[0, 2^31 - 1] and N is a positive integer bounded by the size of the maximum legal label length (63 octets).\n" \
1152 "If QNAME is an Address name and its first label is Alias-TTL label \"alias-ttl-T_1...-T_N\", then the response\n" \
1153 "will contain exactly N CNAME records:\n" \
1155 " 1. For each i in [1, N - 1], the response will contain a CNAME record whose name is identical to QNAME,\n" \
1156 " except that the first label is \"alias-ttl-T_i...-T_N\" instead, whose TTL value is T_i, and whose RDATA\n" \
1157 " is the name of the other CNAME record whose name has \"alias-ttl-T_(i+1)...-T_N\" as its first label.\n" \
1159 " 2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1160 " is \"alias-ttl-T_N\", whose TTL is T_N, and whose RDATA is identical to QNAME stripped of its first\n" \
1163 "Example. A response to a query with a QNAME of alias-ttl-20-40-80.count-5.d.test will contain the following\n" \
1164 "CNAME records:\n" \
1166 " alias-ttl-20-40-80.count-5.d.test. 20 IN CNAME alias-ttl-40-80.count-5.d.test.\n" \
1167 " alias-ttl-40-80.count-5.d.test. 40 IN CNAME alias-ttl-80.count-5.d.test.\n" \
1168 " alias-ttl-80.count-5.d.test. 80 IN CNAME count-5.d.test.\n"
1170 #define kDNSServerInfoText_CountLabel \
1171 "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" \
1172 "an integer in [N_1, 255].\n" \
1174 "If QNAME is an Address name, contains Count label \"count-N\", and has the type of address records specified by\n" \
1175 "QTYPE, then the response will contain exactly N address records:\n" \
1177 " 1. For i in [1, N], the response will contain an address record of type QTYPE whose name is equal to QNAME\n" \
1178 " and whose RDATA is an address equal to a constant base address + i.\n" \
1180 " 2. The address records will be ordered by the address contained in RDATA in ascending order.\n" \
1182 "Example. A response to an A record query with a QNAME of alias.count-3.d.test will contain the following A\n" \
1185 " count-3.d.test. 60 IN A 203.0.113.1\n" \
1186 " count-3.d.test. 60 IN A 203.0.113.2\n" \
1187 " count-3.d.test. 60 IN A 203.0.113.3\n" \
1189 "If QNAME is an Address name, contains Count label \"count-N_1-N_2\", and has the type of address records\n" \
1190 "specified by QTYPE, then the response will contain exactly N_1 address records:\n" \
1192 " 1. Each of the address records will be of type QTYPE, have name equal to QNAME, and have as its RDATA a\n" \
1193 " unique address equal to a constant base address + i, where i is a randomly chosen integer in [1, N_2].\n" \
1195 " 2. The order of the address records will be random.\n" \
1197 "Example. A response to a AAAA record query with a QNAME of count-3-100.ttl-20.d.test could contain the\n" \
1198 "following AAAA records:\n" \
1200 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::c\n" \
1201 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::3a\n" \
1202 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::4f\n" \
1204 "If QNAME is an Address name, but doesn't have the type of address records specified by QTYPE, then the response\n" \
1205 "will contain no address records, regardless of whether it contains a Count label.\n" \
1207 "Address names that don't have a Count label are treated as though they contain a count label equal to\n" \
1210 #define kDNSServerInfoText_TagLabel \
1211 "Tag labels are labels prefixed with \"tag-\" and contain zero or more arbitrary octets after the prefix.\n" \
1213 "This type of label exists to allow testers to \"uniquify\" domain names. Tag labels can also serve as padding\n" \
1214 "to increase the sizes of domain names.\n"
1216 #define kDNSServerInfoText_TTLLabel \
1217 "TTL labels are of the form \"ttl-T\", where T is an integer in [0, 2^31 - 1].\n" \
1219 "If QNAME is an Address name and contains TTL label \"ttl-T\", then all non-CNAME records contained in the\n" \
1220 "response will have a TTL value equal to T.\n"
1222 #define kDNSServerInfoText_IPv4Label \
1223 "The IPv4 label is \"ipv4\". See \"Resource Records\" for the affect of this label.\n"
1225 #define kDNSServerInfoText_IPv6Label \
1226 "The IPv6 label is \"ipv6\". See \"Resource Records\" for the affect of this label.\n"
1228 #define kDNSServerInfoText_SRVNames \
1229 "SRV labels are of the form \"srv-R-W-P\", where R, W, and P are integers in [0, 2^16 - 1].\n" \
1231 "After the first two labels, i.e., the service and protocol labels, the sequence of labels, which may be empty,\n" \
1232 "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" \
1233 "the target hostname of each of the SRV name's SRV records.\n" \
1235 "If QNAME is an SRV name and QTYPE is SRV, then for each SRV label, the response will contain an SRV record with\n" \
1236 "priority R, weight W, port P, and target hostname <target>[.<parent domain>]., where <target> is the sequence\n" \
1237 "of labels, which may be empty, that follows the SRV label leading up to either the next SRV label or the\n" \
1238 "d.test. labels, whichever comes first.\n" \
1240 "Example. A response to an SRV record query with a QNAME of\n" \
1241 "_http._tcp.example.com.srv-0-0-80.www.srv-1-0-8080.www.d.test. will contain the following SRV records:\n" \
1243 "_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" \
1244 "_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"
1246 #define kDNSServerInfoText_BadUDPMode \
1247 "The purpose of Bad UDP mode is to test mDNSResponder's TCP fallback mechanism by which mDNSResponder reissues a\n" \
1248 "UDP query as a TCP query if the UDP response contains the expected QNAME, QTYPE, and QCLASS, but a message ID\n" \
1249 "that's not equal to the query's message ID.\n" \
1251 "This mode is identical to the normal mode except that all responses sent via UDP have a message ID equal to the\n" \
1252 "query's message ID plus one. Also, in this mode, to aid in debugging, A records in responses sent via UDP have\n" \
1253 "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" \
1254 "base address, and AAAA records in responses sent via UDP have IPv6 addresses in the ::ffff:0:0/120 block\n" \
1255 "instead of the 2001:db8:1::/120 block, i.e., ::ffff:0:0 is used as the IPv6 base address.\n"
1257 static int gDNSServer_LoopbackOnly
= false;
1258 static int gDNSServer_Foreground
= false;
1259 static int gDNSServer_ResponseDelayMs
= 0;
1260 static int gDNSServer_DefaultTTL
= 60;
1261 static int gDNSServer_Port
= kDNSPort
;
1262 static const char * gDNSServer_DomainOverride
= NULL
;
1263 #if( TARGET_OS_DARWIN )
1264 static const char * gDNSServer_FollowPID
= NULL
;
1266 static int gDNSServer_BadUDPMode
= false;
1268 static CLIOption kDNSServerOpts
[] =
1270 BooleanOption( 'l', "loopback", &gDNSServer_LoopbackOnly
, "Bind to to the loopback interface." ),
1271 BooleanOption( 'f', "foreground", &gDNSServer_Foreground
, "Direct log output to stdout instead of system logging." ),
1272 IntegerOption( 'd', "responseDelay", &gDNSServer_ResponseDelayMs
, "ms", "The amount of additional delay in milliseconds to apply to responses. (default: 0)", false ),
1273 IntegerOption( 0 , "defaultTTL", &gDNSServer_DefaultTTL
, "seconds", "Resource record TTL value to use when unspecified. (default: 60)", false ),
1274 IntegerOption( 'p', "port", &gDNSServer_Port
, "port number", "UDP/TCP port number to use. Use 0 for any port. (default: 53)", false ),
1275 StringOption( 0 , "domain", &gDNSServer_DomainOverride
, "domain", "Used to override 'd.test.' as the server's domain.", false ),
1276 #if( TARGET_OS_DARWIN )
1277 StringOption( 0 , "follow", &gDNSServer_FollowPID
, "pid", "Exit when the process, usually the parent process, specified by PID exits.", false ),
1279 BooleanOption( 0 , "badUDPMode", &gDNSServer_BadUDPMode
, "Run in Bad UDP mode to trigger mDNSResponder's TCP fallback mechanism." ),
1281 CLI_SECTION( "Intro", kDNSServerInfoText_Intro
),
1282 CLI_SECTION( "Name Existence", kDNSServerInfoText_NameExistence
),
1283 CLI_SECTION( "Resource Records", kDNSServerInfoText_ResourceRecords
),
1284 CLI_SECTION( "Alias Labels", kDNSServerInfoText_AliasLabel
),
1285 CLI_SECTION( "Alias-TTL Labels", kDNSServerInfoText_AliasTTLLabel
),
1286 CLI_SECTION( "Count Labels", kDNSServerInfoText_CountLabel
),
1287 CLI_SECTION( "Tag Labels", kDNSServerInfoText_TagLabel
),
1288 CLI_SECTION( "TTL Labels", kDNSServerInfoText_TTLLabel
),
1289 CLI_SECTION( "IPv4 Label", kDNSServerInfoText_IPv4Label
),
1290 CLI_SECTION( "IPv6 Label", kDNSServerInfoText_IPv6Label
),
1291 CLI_SECTION( "SRV Names", kDNSServerInfoText_SRVNames
),
1292 CLI_SECTION( "Bad UDP Mode", kDNSServerInfoText_BadUDPMode
),
1296 static void DNSServerCmd( void );
1298 //===========================================================================================================================
1299 // MDNSReplier Command Options
1300 //===========================================================================================================================
1302 #define kMDNSReplierPortBase 50000
1304 #define kMDNSReplierInfoText_Intro \
1305 "The mDNS replier answers mDNS queries for its authoritative records. These records are of class IN and of types\n" \
1306 "PTR, SRV, TXT, A, and AAAA as described below.\n" \
1308 "Note: Sub-strings representing integers in domain name labels are in decimal notation and without leading zeros.\n"
1310 #define kMDNSReplierInfoText_Parameters \
1311 "There are five parameters that control the replier's set of authoritative records.\n" \
1313 " 1. <hostname> is the base name used for service instance names and the names of A and AAAA records. This\n" \
1314 " parameter is specified with the --hostname option.\n" \
1315 " 2. <tag> is an arbitrary string used to uniquify service types. This parameter is specified with the --tag\n" \
1317 " 3. N_max in an integer in [1, 65535] and limits service types to those that have no more than N_max\n" \
1318 " instances. It also limits the number of hostnames to N_max, i.e., <hostname>.local.,\n" \
1319 " <hostname>-1.local., ..., <hostname>-N_max.local. This parameter is specified with the\n" \
1320 " --maxInstanceCount option.\n" \
1321 " 4. N_a is an integer in [1, 255] and the number of A records per hostname. This parameter is specified\n" \
1322 " with the --countA option.\n" \
1323 " 5. N_aaaa is an integer in [1, 255] and the number of AAAA records per hostname. This parameter is\n" \
1324 " specified with the --countAAAA option.\n"
1326 #define kMDNSReplierInfoText_PTR \
1327 "The replier's authoritative PTR records have names of the form _t-<tag>-<L>-<N>._tcp.local., where L is an\n" \
1328 "integer in [1, 65535], and N is an integer in [1, N_max].\n" \
1330 "For a given L and N, the replier has exactly N authoritative PTR records:\n" \
1332 " 1. The first PTR record is defined as\n" \
1334 " NAME: _t-<tag>-<L>-<N>._tcp.local.\n" \
1338 " RDATA: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n" \
1340 " 2. For each i in [2, N], there is one PTR record defined as\n" \
1342 " NAME: _t-<tag>-<L>-<N>._tcp.local.\n" \
1346 " RDATA: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n"
1348 #define kMDNSReplierInfoText_SRV \
1349 "The replier's authoritative SRV records have names of the form <instance name>._t-<tag>-<L>-<N>._tcp.local.,\n" \
1350 "where L is an integer in [1, 65535], N is an integer in [1, N_max], and <instance name> is <hostname> or\n" \
1351 "\"<hostname> (<i>)\", where i is in [2, N].\n" \
1353 "For a given L and N, the replier has exactly N authoritative SRV records:\n" \
1355 " 1. The first SRV record is defined as\n" \
1357 " NAME: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n" \
1364 " Port: (50000 + L) mod 2^16\n" \
1365 " Target: <hostname>.local.\n" \
1367 " 2. For each i in [2, N], there is one SRV record defined as:\n" \
1369 " NAME: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n" \
1376 " Port: (50000 + L) mod 2^16\n" \
1377 " Target: <hostname>-<i>.local.\n"
1379 #define kMDNSReplierInfoText_TXT \
1380 "The replier's authoritative TXT records have names of the form <instance name>._t-<tag>-<L>-<N>._tcp.local.,\n" \
1381 "where L is an integer in [1, 65535], N is an integer in [1, N_max], and <instance name> is <hostname> or\n" \
1382 "\"<hostname> (<i>)\", where i is in [2, N].\n" \
1384 "For a given L and N, the replier has exactly N authoritative TXT records:\n" \
1386 " 1. The first TXT record is defined as\n" \
1388 " NAME: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n" \
1393 " RDATA: <one or more strings with an aggregate length of L octets>\n" \
1395 " 2. For each i in [2, N], there is one TXT record:\n" \
1397 " NAME: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n" \
1402 " RDATA: <one or more strings with an aggregate length of L octets>\n" \
1404 "The RDATA of each TXT record is exactly L octets and consists of a repeating series of the 15-byte string\n" \
1405 "\"hash=0x<32-bit FNV-1 hash of the record name as an 8-character hexadecimal string>\". The last instance of\n" \
1406 "the string may be truncated to satisfy the TXT record data's size requirement.\n"
1408 #define kMDNSReplierInfoText_A \
1409 "The replier has exactly N_max x N_a authoritative A records:\n" \
1411 " 1. For each j in [1, N_a], an A record is defined as\n" \
1413 " NAME: <hostname>.local.\n" \
1418 " RDATA: 0.0.1.<j>\n" \
1420 " 2. For each i in [2, N_max], for each j in [1, N_a], an A record is defined as\n" \
1422 " NAME: <hostname>-<i>.local.\n" \
1427 " RDATA: 0.<ceil(i / 256)>.<i mod 256>.<j>\n"
1429 #define kMDNSReplierInfoText_AAAA \
1430 "The replier has exactly N_max x N_aaaa authoritative AAAA records:\n" \
1432 " 1. For each j in [1, N_aaaa], a AAAA record is defined as\n" \
1434 " NAME: <hostname>.local.\n" \
1439 " RDATA: 2001:db8:2::1:<j>\n" \
1441 " 2. For each i in [2, N_max], for each j in [1, N_aaaa], a AAAA record is defined as\n" \
1443 " NAME: <hostname>-<i>.local.\n" \
1448 " RDATA: 2001:db8:2::<i>:<j>\n"
1450 #define kMDNSReplierInfoText_Responses \
1451 "When generating answers for a query message, any two records pertaining to the same hostname will be grouped\n" \
1452 "together in the same response message, and any two records pertaining to different hostnames will be in\n" \
1453 "separate response messages.\n"
1455 static const char * gMDNSReplier_Hostname
= NULL
;
1456 static const char * gMDNSReplier_ServiceTypeTag
= NULL
;
1457 static int gMDNSReplier_MaxInstanceCount
= 1000;
1458 static int gMDNSReplier_NoAdditionals
= false;
1459 static int gMDNSReplier_RecordCountA
= 1;
1460 static int gMDNSReplier_RecordCountAAAA
= 1;
1461 static double gMDNSReplier_UnicastDropRate
= 0.0;
1462 static double gMDNSReplier_MulticastDropRate
= 0.0;
1463 static int gMDNSReplier_MaxDropCount
= 0;
1464 static int gMDNSReplier_UseIPv4
= false;
1465 static int gMDNSReplier_UseIPv6
= false;
1466 static int gMDNSReplier_Foreground
= false;
1467 static const char * gMDNSReplier_FollowPID
= NULL
;
1469 static CLIOption kMDNSReplierOpts
[] =
1471 StringOption( 'i', "interface", &gInterface
, "name or index", "Network interface by name or index.", true ),
1472 StringOption( 'n', "hostname", &gMDNSReplier_Hostname
, "string", "Base name to use for hostnames and service instance names.", true ),
1473 StringOption( 't', "tag", &gMDNSReplier_ServiceTypeTag
, "string", "Tag to use for service types, e.g., _t-<tag>-<TXT size>-<count>._tcp.", true ),
1474 IntegerOption( 'c', "maxInstanceCount", &gMDNSReplier_MaxInstanceCount
, "count", "Maximum number of service instances. (default: 1000)", false ),
1475 BooleanOption( 0 , "noAdditionals", &gMDNSReplier_NoAdditionals
, "When answering queries, don't include any additional records." ),
1476 IntegerOption( 0 , "countA", &gMDNSReplier_RecordCountA
, "count", "Number of A records per hostname. (default: 1)", false ),
1477 IntegerOption( 0 , "countAAAA", &gMDNSReplier_RecordCountAAAA
, "count", "Number of AAAA records per hostname. (default: 1)", false ),
1478 DoubleOption( 0 , "udrop", &gMDNSReplier_UnicastDropRate
, "probability", "Probability of dropping a unicast response. (default: 0.0)", false ),
1479 DoubleOption( 0 , "mdrop", &gMDNSReplier_MulticastDropRate
, "probability", "Probability of dropping a multicast query or response. (default: 0.0)", false ),
1480 IntegerOption( 0 , "maxDropCount", &gMDNSReplier_MaxDropCount
, "count", "If > 0, drop probabilities are limted to first <count> responses from each instance. (default: 0)", false ),
1481 BooleanOption( 0 , "ipv4", &gMDNSReplier_UseIPv4
, "Use IPv4." ),
1482 BooleanOption( 0 , "ipv6", &gMDNSReplier_UseIPv6
, "Use IPv6." ),
1483 BooleanOption( 'f', "foreground", &gMDNSReplier_Foreground
, "Direct log output to stdout instead of system logging." ),
1484 #if( TARGET_OS_DARWIN )
1485 StringOption( 0 , "follow", &gMDNSReplier_FollowPID
, "pid", "Exit when the process, usually the parent process, specified by PID exits.", false ),
1488 CLI_SECTION( "Intro", kMDNSReplierInfoText_Intro
),
1489 CLI_SECTION( "Authoritative Record Parameters", kMDNSReplierInfoText_Parameters
),
1490 CLI_SECTION( "Authoritative PTR Records", kMDNSReplierInfoText_PTR
),
1491 CLI_SECTION( "Authoritative SRV Records", kMDNSReplierInfoText_SRV
),
1492 CLI_SECTION( "Authoritative TXT Records", kMDNSReplierInfoText_TXT
),
1493 CLI_SECTION( "Authoritative A Records", kMDNSReplierInfoText_A
),
1494 CLI_SECTION( "Authoritative AAAA Records", kMDNSReplierInfoText_AAAA
),
1495 CLI_SECTION( "Responses", kMDNSReplierInfoText_Responses
),
1499 static void MDNSReplierCmd( void );
1501 //===========================================================================================================================
1502 // Test Command Options
1503 //===========================================================================================================================
1505 #define kTestExitStatusSection_Name "Exit Status"
1506 #define kTestExitStatusSection_Text \
1507 "This test command can exit with one of three status codes:\n" \
1509 "0 - The test ran to completion and passed.\n" \
1510 "1 - A fatal error prevented the test from completing.\n" \
1511 "2 - The test ran to completion, but it or a subtest failed. See test output for details.\n" \
1513 "Note: The pass/fail status applies to the correctness or results. It does not necessarily imply anything about\n" \
1516 #define TestExitStatusSection() CLI_SECTION( kTestExitStatusSection_Name, kTestExitStatusSection_Text )
1518 #define kGAIPerfTestSuiteName_Basic "basic"
1519 #define kGAIPerfTestSuiteName_Advanced "advanced"
1521 static const char * gGAIPerf_TestSuite
= NULL
;
1522 static int gGAIPerf_CallDelayMs
= 10;
1523 static int gGAIPerf_ServerDelayMs
= 10;
1524 static int gGAIPerf_SkipPathEvalulation
= false;
1525 static int gGAIPerf_BadUDPMode
= false;
1526 static int gGAIPerf_IterationCount
= 100;
1527 static int gGAIPerf_IterationTimeLimitMs
= 100;
1528 static const char * gGAIPerf_OutputFilePath
= NULL
;
1529 static const char * gGAIPerf_OutputFormat
= kOutputFormatStr_JSON
;
1530 static int gGAIPerf_OutputAppendNewline
= false;
1532 static void GAIPerfCmd( void );
1534 #define kGAIPerfSectionText_TestSuiteBasic \
1535 "This test suite consists of the following three test cases:\n" \
1537 "Test Case #1: Resolve a domain name with\n" \
1539 " 2 CNAME records, 4 A records, and 4 AAAA records\n" \
1541 "to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n" \
1542 "server queries.\n" \
1544 "Test Case #2: Resolve a domain name with\n" \
1546 " 2 CNAME records, 4 A records, and 4 AAAA records\n" \
1548 "to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n" \
1549 "requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n" \
1550 "which should ideally require no additional server queries, i.e., the results should come from the cache.\n" \
1552 "Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n" \
1553 "records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n" \
1554 "domain name in the preliminary iteration isn't counted in the performance stats.\n" \
1556 "Test Case #3: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n"
1558 #define kGAIPerfSectionText_TestSuiteAdvanced \
1559 "This test suite consists of 33 test cases. Test cases 1 through 32 can be described in the following way\n" \
1561 "Test Case #N (where N is in [1, 32] and odd): Resolve a domain name with\n" \
1563 " N_c CNAME records, N_a A records, and N_a AAAA records\n" \
1565 "to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n" \
1566 "server queries.\n" \
1568 "Test Case #N (where N is in [1, 32] and even): Resolve a domain name with\n" \
1570 " N_c CNAME records, N_a A records, and N_a AAAA records\n" \
1572 "to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n" \
1573 "requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n" \
1574 "which should ideally require no additional server queries, i.e., the results should come from the cache.\n" \
1576 "Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n" \
1577 "records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n" \
1578 "domain name in the preliminary iteration isn't counted in the performance stats.\n" \
1580 "N_c and N_a take on the following values, depending on the value of N:\n" \
1582 " N_c is 0 if N is in [1, 8].\n" \
1583 " N_c is 1 if N is in [9, 16].\n" \
1584 " N_c is 2 if N is in [17, 24].\n" \
1585 " N_c is 4 if N is in [25, 32].\n" \
1587 " N_a is 1 if N mod 8 is 1 or 2.\n" \
1588 " N_a is 2 if N mod 8 is 3 or 4.\n" \
1589 " N_a is 4 if N mod 8 is 5 or 6.\n" \
1590 " N_a is 8 if N mod 8 is 7 or 0.\n" \
1594 "Test Case #33: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n"
1596 static CLIOption kGAIPerfOpts
[] =
1598 StringOptionEx( 's', "suite", &gGAIPerf_TestSuite
, "name", "Name of the predefined test suite to run.", true,
1600 "There are currently two predefined test suites, '" kGAIPerfTestSuiteName_Basic
"' and '" kGAIPerfTestSuiteName_Advanced
"', which are described below.\n"
1603 StringOption( 'o', "output", &gGAIPerf_OutputFilePath
, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1604 FormatOption( 'f', "format", &gGAIPerf_OutputFormat
, "Specifies the test results output format. (default: " kOutputFormatStr_JSON
")", false ),
1605 BooleanOption( 'n', "appendNewline", &gGAIPerf_OutputAppendNewline
, "If the output format is JSON, output a trailing newline character." ),
1606 IntegerOption( 'i', "iterations", &gGAIPerf_IterationCount
, "count", "The number of iterations per test case. (default: 100)", false ),
1607 IntegerOption( 'l', "timeLimit", &gGAIPerf_IterationTimeLimitMs
, "ms", "Time limit for each DNSServiceGetAddrInfo() operation in milliseconds. (default: 100)", false ),
1608 IntegerOption( 0 , "callDelay", &gGAIPerf_CallDelayMs
, "ms", "Time to wait before calling DNSServiceGetAddrInfo() in milliseconds. (default: 10)", false ),
1609 BooleanOption( 0 , "skipPathEval", &gGAIPerf_SkipPathEvalulation
, "Use kDNSServiceFlagsPathEvaluationDone when calling DNSServiceGetAddrInfo()." ),
1611 CLI_OPTION_GROUP( "DNS Server Options" ),
1612 IntegerOption( 0 , "responseDelay", &gGAIPerf_ServerDelayMs
, "ms", "Additional delay in milliseconds to have the server apply to responses. (default: 10)", false ),
1613 BooleanOption( 0 , "badUDPMode", &gGAIPerf_BadUDPMode
, "Run server in Bad UDP mode to trigger mDNSResponder's TCP fallback mechanism." ),
1615 CLI_SECTION( "Test Suite \"Basic\"", kGAIPerfSectionText_TestSuiteBasic
),
1616 CLI_SECTION( "Test Suite \"Advanced\"", kGAIPerfSectionText_TestSuiteAdvanced
),
1617 TestExitStatusSection(),
1621 static void MDNSDiscoveryTestCmd( void );
1623 static int gMDNSDiscoveryTest_InstanceCount
= 100;
1624 static int gMDNSDiscoveryTest_TXTSize
= 100;
1625 static int gMDNSDiscoveryTest_BrowseTimeSecs
= 2;
1626 static int gMDNSDiscoveryTest_FlushCache
= false;
1627 static char * gMDNSDiscoveryTest_Interface
= NULL
;
1628 static int gMDNSDiscoveryTest_NoAdditionals
= false;
1629 static int gMDNSDiscoveryTest_RecordCountA
= 1;
1630 static int gMDNSDiscoveryTest_RecordCountAAAA
= 1;
1631 static double gMDNSDiscoveryTest_UnicastDropRate
= 0.0;
1632 static double gMDNSDiscoveryTest_MulticastDropRate
= 0.0;
1633 static int gMDNSDiscoveryTest_MaxDropCount
= 0;
1634 static int gMDNSDiscoveryTest_UseIPv4
= false;
1635 static int gMDNSDiscoveryTest_UseIPv6
= false;
1636 static const char * gMDNSDiscoveryTest_OutputFormat
= kOutputFormatStr_JSON
;
1637 static int gMDNSDiscoveryTest_OutputAppendNewline
= false;
1638 static const char * gMDNSDiscoveryTest_OutputFilePath
= NULL
;
1640 static CLIOption kMDNSDiscoveryTestOpts
[] =
1642 IntegerOption( 'c', "instanceCount", &gMDNSDiscoveryTest_InstanceCount
, "count", "Number of service instances to discover. (default: 100)", false ),
1643 IntegerOption( 's', "txtSize", &gMDNSDiscoveryTest_TXTSize
, "bytes", "Desired size of each service instance's TXT record data. (default: 100)", false ),
1644 IntegerOption( 'b', "browseTime", &gMDNSDiscoveryTest_BrowseTimeSecs
, "seconds", "Amount of time to spend browsing in seconds. (default: 2)", false ),
1645 BooleanOption( 0 , "flushCache", &gMDNSDiscoveryTest_FlushCache
, "Flush mDNSResponder's record cache before browsing. Requires root privileges." ),
1647 CLI_OPTION_GROUP( "mDNS Replier Parameters" ),
1648 StringOption( 'i', "interface", &gMDNSDiscoveryTest_Interface
, "name or index", "Network interface. If unspecified, any available mDNS-capable interface will be used.", false ),
1649 BooleanOption( 0 , "noAdditionals", &gMDNSDiscoveryTest_NoAdditionals
, "When answering queries, don't include any additional records." ),
1650 IntegerOption( 0 , "countA", &gMDNSDiscoveryTest_RecordCountA
, "count", "Number of A records per hostname. (default: 1)", false ),
1651 IntegerOption( 0 , "countAAAA", &gMDNSDiscoveryTest_RecordCountAAAA
, "count", "Number of AAAA records per hostname. (default: 1)", false ),
1652 DoubleOption( 0 , "udrop", &gMDNSDiscoveryTest_UnicastDropRate
, "probability", "Probability of dropping a unicast response. (default: 0.0)", false ),
1653 DoubleOption( 0 , "mdrop", &gMDNSDiscoveryTest_MulticastDropRate
, "probability", "Probability of dropping a multicast query or response. (default: 0.0)", false ),
1654 IntegerOption( 0 , "maxDropCount", &gMDNSDiscoveryTest_MaxDropCount
, "count", "If > 0, drop probabilities are limted to first <count> responses from each instance. (default: 0)", false ),
1655 BooleanOption( 0 , "ipv4", &gMDNSDiscoveryTest_UseIPv4
, "Use IPv4." ),
1656 BooleanOption( 0 , "ipv6", &gMDNSDiscoveryTest_UseIPv6
, "Use IPv6." ),
1658 CLI_OPTION_GROUP( "Results" ),
1659 FormatOption( 'f', "format", &gMDNSDiscoveryTest_OutputFormat
, "Specifies the test results output format. (default: " kOutputFormatStr_JSON
")", false ),
1660 StringOption( 'o', "output", &gMDNSDiscoveryTest_OutputFilePath
, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1662 TestExitStatusSection(),
1666 static void DotLocalTestCmd( void );
1668 static const char * gDotLocalTest_Interface
= NULL
;
1669 static const char * gDotLocalTest_OutputFormat
= kOutputFormatStr_JSON
;
1670 static const char * gDotLocalTest_OutputFilePath
= NULL
;
1672 #define kDotLocalTestSubtestDesc_GAIMDNSOnly "GAI for a dotlocal name that has only MDNS A and AAAA records."
1673 #define kDotLocalTestSubtestDesc_GAIDNSOnly "GAI for a dotlocal name that has only DNS A and AAAA records."
1674 #define kDotLocalTestSubtestDesc_GAIBoth "GAI for a dotlocal name that has both mDNS and DNS A and AAAA records."
1675 #define kDotLocalTestSubtestDesc_GAINeither "GAI for a dotlocal name that has no A or AAAA records."
1676 #define kDotLocalTestSubtestDesc_GAINoSuchRecord \
1677 "GAI for a dotlocal name that has no A or AAAA records, but is a subdomain name of a search domain."
1678 #define kDotLocalTestSubtestDesc_QuerySRV "SRV query for a dotlocal name that has only a DNS SRV record."
1680 #define kDotLocalTestSectionText_Description \
1681 "The goal of the dotlocal test is to verify that mDNSResponder properly handles queries for domain names in the\n" \
1682 "local domain when a local SOA record exists. As part of the test setup, a test DNS server and an mdnsreplier are\n" \
1683 "spawned, and a dummy local SOA record is registered with DNSServiceRegisterRecord(). The server is invoked such\n" \
1684 "that its domain is a second-level subdomain of the local domain, i.e., <some label>.local, while the mdnsreplier is\n" \
1685 "invoked such that its base hostname is equal to the server's domain, e.g., if the server's domain is test.local.,\n" \
1686 "then the mdnsreplier's base hostname is test.local.\n" \
1688 "The dotlocal test consists of six subtests that perform either a DNSServiceGetAddrInfo (GAI) operation for a\n" \
1689 "hostname in the local domain or a DNSServiceQueryRecord operation to query for an SRV record in the local domain:\n" \
1691 "1. " kDotLocalTestSubtestDesc_GAIMDNSOnly "\n" \
1692 "2. " kDotLocalTestSubtestDesc_GAIDNSOnly "\n" \
1693 "3. " kDotLocalTestSubtestDesc_GAIBoth "\n" \
1694 "4. " kDotLocalTestSubtestDesc_GAINeither "\n" \
1695 "5. " kDotLocalTestSubtestDesc_GAINoSuchRecord "\n" \
1696 "6. " kDotLocalTestSubtestDesc_QuerySRV "\n" \
1698 "Each subtest runs for five seconds.\n"
1700 static CLIOption kDotLocalTestOpts
[] =
1702 StringOption( 'i', "interface", &gDotLocalTest_Interface
, "name or index", "mdnsreplier's network interface. If not set, any mDNS-capable interface will be used.", false ),
1704 CLI_OPTION_GROUP( "Results" ),
1705 FormatOption( 'f', "format", &gDotLocalTest_OutputFormat
, "Specifies the test results output format. (default: " kOutputFormatStr_JSON
")", false ),
1706 StringOption( 'o', "output", &gDotLocalTest_OutputFilePath
, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1708 CLI_SECTION( "Description", kDotLocalTestSectionText_Description
),
1709 TestExitStatusSection(),
1713 static void ProbeConflictTestCmd( void );
1715 static const char * gProbeConflictTest_Interface
= NULL
;
1716 static int gProbeConflictTest_UseComputerName
= false;
1717 static const char * gProbeConflictTest_OutputFormat
= kOutputFormatStr_JSON
;
1718 static const char * gProbeConflictTest_OutputFilePath
= NULL
;
1720 static CLIOption kProbeConflictTestOpts
[] =
1722 StringOption( 'i', "interface", &gProbeConflictTest_Interface
, "name or index", "mdnsreplier's network interface. If not set, any mDNS-capable interface will be used.", false ),
1723 BooleanOption( 'c', "useComputerName", &gProbeConflictTest_UseComputerName
, "Use the device's \"computer name\" for the test service's name." ),
1725 CLI_OPTION_GROUP( "Results" ),
1726 FormatOption( 'f', "format", &gProbeConflictTest_OutputFormat
, "Specifies the test report output format. (default: " kOutputFormatStr_JSON
")", false ),
1727 StringOption( 'o', "output", &gProbeConflictTest_OutputFilePath
, "path", "Path of the file to write test report to instead of standard output (stdout).", false ),
1729 TestExitStatusSection(),
1733 static void RegistrationTestCmd( void );
1735 static int gRegistrationTest_BATSEnvironment
= false;
1736 static const char * gRegistrationTest_OutputFormat
= kOutputFormatStr_JSON
;
1737 static const char * gRegistrationTest_OutputFilePath
= NULL
;
1739 static CLIOption kRegistrationTestOpts
[] =
1741 CLI_OPTION_BOOLEAN( 0, "bats", &gRegistrationTest_BATSEnvironment
, "Informs the test that it's running in a BATS environment.",
1743 "This option allows the test to take special measures while running in a BATS environment. Currently, this option\n"
1744 "only has an effect on watchOS. Because it has been observed that the Wi-Fi interface sometimes goes down during\n"
1745 "watchOS BATS testing, for watchOS, when a service is registered using kDNSServiceInterfaceIndexAny,\n"
1747 " 1. missing browse and query \"add\" results for Wi-Fi interfaces aren't enough for a subtest to fail; and\n"
1748 " 2. unexpected browse and query results for Wi-Fi interfaces are ignored.\n"
1750 CLI_OPTION_GROUP( "Results" ),
1751 FormatOption( 'f', "format", &gRegistrationTest_OutputFormat
, "Specifies the test results output format. (default: " kOutputFormatStr_JSON
")", false ),
1752 StringOption( 'o', "output", &gRegistrationTest_OutputFilePath
, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1754 TestExitStatusSection(),
1758 static void ExpensiveConstrainedTestCmd( void );
1760 static const char * gExpensiveConstrainedTest_Interface
= NULL
;
1761 static const char * gExpensiveConstrainedTest_Name
= NULL
;
1762 static Boolean gExpensiveConstrainedTest_DenyExpensive
= false;
1763 static Boolean gExpensiveConstrainedTest_DenyConstrained
= false;
1764 static Boolean gExpensiveConstrainedTest_StartFromExpensive
= false;
1765 static int gExpensiveConstrainedTest_ProtocolIPv4
= false;
1766 static int gExpensiveConstrainedTest_ProtocolIPv6
= false;
1767 static const char * gExpensiveConstrainedTest_OutputFormat
= kOutputFormatStr_JSON
;
1768 static const char * gExpensiveConstrainedTest_OutputFilePath
= NULL
;
1770 static CLIOption kExpensiveConstrainedTestOpts
[] =
1772 CLI_OPTION_GROUP( "Results" ),
1773 FormatOption( 'f', "format", &gExpensiveConstrainedTest_OutputFormat
, "Specifies the test results output format. (default: " kOutputFormatStr_JSON
")", false ),
1774 StringOption( 'o', "output", &gExpensiveConstrainedTest_OutputFilePath
, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1776 TestExitStatusSection(),
1780 static CLIOption kTestOpts
[] =
1782 Command( "gaiperf", GAIPerfCmd
, kGAIPerfOpts
, "Runs DNSServiceGetAddrInfo() performance tests.", false ),
1783 Command( "mdnsdiscovery", MDNSDiscoveryTestCmd
, kMDNSDiscoveryTestOpts
, "Tests mDNS service discovery for correctness.", false ),
1784 Command( "dotlocal", DotLocalTestCmd
, kDotLocalTestOpts
, "Tests DNS and mDNS queries for domain names in the local domain.", false ),
1785 Command( "probeconflicts", ProbeConflictTestCmd
, kProbeConflictTestOpts
, "Tests various probing conflict scenarios.", false ),
1786 Command( "registration", RegistrationTestCmd
, kRegistrationTestOpts
, "Tests service registrations.", false ),
1787 Command( "expensive_constrained_updates", ExpensiveConstrainedTestCmd
, kExpensiveConstrainedTestOpts
, "Tests if the mDNSResponder can handle expensive and constrained property change correctly", false),
1791 //===========================================================================================================================
1792 // SSDP Command Options
1793 //===========================================================================================================================
1795 static int gSSDPDiscover_MX
= 1;
1796 static const char * gSSDPDiscover_ST
= "ssdp:all";
1797 static int gSSDPDiscover_ReceiveSecs
= 1;
1798 static int gSSDPDiscover_UseIPv4
= false;
1799 static int gSSDPDiscover_UseIPv6
= false;
1800 static int gSSDPDiscover_Verbose
= false;
1802 static CLIOption kSSDPDiscoverOpts
[] =
1804 StringOption( 'i', "interface", &gInterface
, "name or index", "Network interface by name or index.", true ),
1805 IntegerOption( 'm', "mx", &gSSDPDiscover_MX
, "seconds", "MX value in search request, i.e., max response delay in seconds. (Default: 1 second)", false ),
1806 StringOption( 's', "st", &gSSDPDiscover_ST
, "string", "ST value in search request, i.e., the search target. (Default: \"ssdp:all\")", false ),
1807 IntegerOption( 'r', "receiveTime", &gSSDPDiscover_ReceiveSecs
, "seconds", "Amount of time to spend receiving responses. -1 means unlimited. (Default: 1 second)", false ),
1808 BooleanOption( 0 , "ipv4", &gSSDPDiscover_UseIPv4
, "Use IPv4, i.e., multicast to 239.255.255.250:1900." ),
1809 BooleanOption( 0 , "ipv6", &gSSDPDiscover_UseIPv6
, "Use IPv6, i.e., multicast to [ff02::c]:1900" ),
1810 BooleanOption( 'v', "verbose", &gSSDPDiscover_Verbose
, "Prints the search request(s) that were sent." ),
1814 static void SSDPDiscoverCmd( void );
1816 static CLIOption kSSDPOpts
[] =
1818 Command( "discover", SSDPDiscoverCmd
, kSSDPDiscoverOpts
, "Crafts and multicasts an SSDP search message.", false ),
1822 #if( TARGET_OS_DARWIN )
1823 //===========================================================================================================================
1824 // res_query Command Options
1825 //===========================================================================================================================
1827 static void ResQueryCmd( void );
1829 static const char * gResQuery_Name
= NULL
;
1830 static const char * gResQuery_Type
= NULL
;
1831 static const char * gResQuery_Class
= NULL
;
1832 static int gResQuery_UseLibInfo
= false;
1834 static CLIOption kResQueryOpts
[] =
1836 StringOption( 'n', "name", &gResQuery_Name
, "domain name", "Full domain name of record to query.", true ),
1837 StringOption( 't', "type", &gResQuery_Type
, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
1838 StringOption( 'c', "class", &gResQuery_Class
, "record class", "Record class by name or number. Default class is IN.", false ),
1839 BooleanOption( 0 , "libinfo", &gResQuery_UseLibInfo
, "Use res_query from libinfo instead of libresolv." ),
1843 //===========================================================================================================================
1844 // dns_query Command Options
1845 //===========================================================================================================================
1847 static void ResolvDNSQueryCmd( void );
1849 static const char * gResolvDNSQuery_Name
= NULL
;
1850 static const char * gResolvDNSQuery_Type
= NULL
;
1851 static const char * gResolvDNSQuery_Class
= NULL
;
1852 static const char * gResolvDNSQuery_Path
= NULL
;
1854 static CLIOption kResolvDNSQueryOpts
[] =
1856 StringOption( 'n', "name", &gResolvDNSQuery_Name
, "domain name", "Full domain name of record to query.", true ),
1857 StringOption( 't', "type", &gResolvDNSQuery_Type
, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
1858 StringOption( 'c', "class", &gResolvDNSQuery_Class
, "record class", "Record class by name or number. Default class is IN.", false ),
1859 StringOption( 'p', "path", &gResolvDNSQuery_Path
, "file path", "The path argument to pass to dns_open() before calling dns_query(). Default value is NULL.", false ),
1863 //===========================================================================================================================
1864 // CFHost Command Options
1865 //===========================================================================================================================
1867 static void CFHostCmd( void );
1869 static const char * gCFHost_Name
= NULL
;
1870 static int gCFHost_WaitSecs
= 0;
1872 static CLIOption kCFHostOpts
[] =
1874 StringOption( 'n', "name", &gCFHost_Name
, "hostname", "Hostname to resolve.", true ),
1875 IntegerOption( 'w', "wait", &gCFHost_WaitSecs
, "seconds", "Time in seconds to wait before a normal exit. (default: 0)", false ),
1879 static CLIOption kLegacyOpts
[] =
1881 Command( "res_query", ResQueryCmd
, kResQueryOpts
, "Uses res_query() from either libresolv or libinfo to query for a record.", true ),
1882 Command( "dns_query", ResolvDNSQueryCmd
, kResolvDNSQueryOpts
, "Uses dns_query() from libresolv to query for a record.", true ),
1883 Command( "cfhost", CFHostCmd
, kCFHostOpts
, "Uses CFHost to resolve a hostname.", true ),
1887 //===========================================================================================================================
1888 // DNSConfigAdd Command Options
1889 //===========================================================================================================================
1891 static void DNSConfigAddCmd( void );
1893 static CFStringRef gDNSConfigAdd_ID
= NULL
;
1894 static char ** gDNSConfigAdd_IPAddrArray
= NULL
;
1895 static size_t gDNSConfigAdd_IPAddrCount
= 0;
1896 static char ** gDNSConfigAdd_DomainArray
= NULL
;
1897 static size_t gDNSConfigAdd_DomainCount
= 0;
1898 static const char * gDNSConfigAdd_Interface
= NULL
;
1900 static CLIOption kDNSConfigAddOpts
[] =
1902 CFStringOption( 0 , "id", &gDNSConfigAdd_ID
, "ID", "Arbitrary ID to use for resolver entry.", true ),
1903 MultiStringOption( 'a', "address", &gDNSConfigAdd_IPAddrArray
, &gDNSConfigAdd_IPAddrCount
, "IP address", "DNS server IP address(es). Can be specified more than once.", true ),
1904 MultiStringOption( 'd', "domain", &gDNSConfigAdd_DomainArray
, &gDNSConfigAdd_DomainCount
, "domain", "Specific domain(s) for the resolver entry. Can be specified more than once.", false ),
1905 StringOption( 'i', "interface", &gDNSConfigAdd_Interface
, "interface name", "Specific interface for the resolver entry.", false ),
1907 CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ),
1911 //===========================================================================================================================
1912 // DNSConfigRemove Command Options
1913 //===========================================================================================================================
1915 static void DNSConfigRemoveCmd( void );
1917 static CFStringRef gDNSConfigRemove_ID
= NULL
;
1919 static CLIOption kDNSConfigRemoveOpts
[] =
1921 CFStringOption( 0, "id", &gDNSConfigRemove_ID
, "ID", "ID of resolver entry to remove.", true ),
1923 CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ),
1927 static CLIOption kDNSConfigOpts
[] =
1929 Command( "add", DNSConfigAddCmd
, kDNSConfigAddOpts
, "Add a supplemental resolver entry to the system's DNS configuration.", true ),
1930 Command( "remove", DNSConfigRemoveCmd
, kDNSConfigRemoveOpts
, "Remove a supplemental resolver entry from the system's DNS configuration.", true ),
1934 //===========================================================================================================================
1936 //===========================================================================================================================
1938 static void XPCSendCmd( void );
1940 static const char * gXPCSend_ServiceName
= NULL
;
1941 static const char * gXPCSend_MessageStr
= NULL
;
1943 static const char kXPCSendMessageSection_Name
[] = "Message Argument";
1944 static const char kXPCSendMessageSection_Text
[] =
1945 "XPC messages are described as a string using the following syntax.\n"
1947 "With the exception of the top-most XPC message dictionary, dictionaries begin with a '{' and end with a '}'.\n"
1948 "Key-value pairs are of the form <key>=<value>, where <key> is a string and <value> is a value of any of the\n"
1949 "currently supported XPC types.\n"
1951 "Arrays begin with a '[' and end with a ']'.\n"
1953 "The following non-container XPC types are supported:\n"
1955 "Type Syntax Example\n"
1956 "bool bool:<string> bool:true (or yes/y/on/1), bool:false (or no/n/off/0)\n"
1957 "data data:<hex string> data:C0000201\n"
1958 "int64 (signed 64-bit integer) int:<pos. or neg. integer> int:-1\n"
1959 "string string:<string> string:hello\\ world\n"
1960 "uint64 (unsigned 64-bit integer) uint:<pos. integer> uint:1024 or uint:0x400\n"
1961 "UUID uuid:<UUID> uuid:dab10183-84b5-4859-9de6-4bee287cfea3\n"
1963 "Example 1: 'cmd=string:add make=string:Apple model=string:Macintosh aliases=[string:Mac string:Macintosh\\ 128K]'\n"
1964 "Example 2: 'cmd=string:search features={portable=bool:yes solar=bool:no} priceMin=uint:100 priceMax=uint:200'\n";
1966 static CLIOption kXPCSendOpts
[] =
1968 StringOption( 's', "service", &gXPCSend_ServiceName
, "service name", "XPC service name.", true ),
1969 StringOption( 'm', "message", &gXPCSend_MessageStr
, "message", "XPC message as a string.", true ),
1971 CLI_SECTION( kXPCSendMessageSection_Name
, kXPCSendMessageSection_Text
),
1974 #endif // TARGET_OS_DARWIN
1976 #if ( MDNSRESPONDER_PROJECT )
1977 //===========================================================================================================================
1979 //===========================================================================================================================
1981 static void InterfaceMonitorCmd( void );
1983 static CLIOption kInterfaceMonitorOpts
[] =
1985 StringOption( 'i', "interface", &gInterface
, "name or index", "Network interface by name or index.", true ),
1989 //===========================================================================================================================
1991 //===========================================================================================================================
1993 static void DNSProxyCmd( void );
1995 static char ** gDNSProxy_InputInterfaces
= NULL
;
1996 static size_t gDNSProxy_InputInterfaceCount
= 0;
1997 static const char * gDNSProxy_OutputInterface
= NULL
;
1999 static CLIOption kDNSProxyOpts
[] =
2001 MultiStringOption( 'i', "inputInterface", &gDNSProxy_InputInterfaces
, &gDNSProxy_InputInterfaceCount
, "name or index", "Interface to accept queries on. Can be specified more than once.", true ),
2002 StringOption( 'o', "outputInterface", &gDNSProxy_OutputInterface
, "name or index", "Interface to forward queries over. Use '0' for primary interface. (default: 0)", false ),
2005 #endif // MDNSRESPONDER_PROJECT
2007 //===========================================================================================================================
2009 //===========================================================================================================================
2011 static OSStatus
VersionOptionCallback( CLIOption
*inOption
, const char *inArg
, int inUnset
);
2013 static void BrowseCmd( void );
2014 static void GetAddrInfoCmd( void );
2015 static void QueryRecordCmd( void );
2016 static void RegisterCmd( void );
2017 static void RegisterRecordCmd( void );
2018 static void ResolveCmd( void );
2019 static void ReconfirmCmd( void );
2020 static void GetAddrInfoPOSIXCmd( void );
2021 static void ReverseLookupCmd( void );
2022 static void PortMappingCmd( void );
2023 static void BrowseAllCmd( void );
2024 static void GetAddrInfoStressCmd( void );
2025 static void DNSQueryCmd( void );
2026 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
2027 static void DNSCryptCmd( void );
2029 static void MDNSQueryCmd( void );
2030 static void PIDToUUIDCmd( void );
2031 static void DaemonVersionCmd( void );
2033 static CLIOption kGlobalOpts
[] =
2035 CLI_OPTION_CALLBACK_EX( 'V', "version", VersionOptionCallback
, NULL
, NULL
,
2036 kCLIOptionFlags_NoArgument
| kCLIOptionFlags_GlobalOnly
, "Displays the version of this tool.", NULL
),
2041 Command( "browse", BrowseCmd
, kBrowseOpts
, "Uses DNSServiceBrowse() to browse for one or more service types.", false ),
2042 Command( "getAddrInfo", GetAddrInfoCmd
, kGetAddrInfoOpts
, "Uses DNSServiceGetAddrInfo() to resolve a hostname to IP addresses.", false ),
2043 Command( "queryRecord", QueryRecordCmd
, kQueryRecordOpts
, "Uses DNSServiceQueryRecord() to query for an arbitrary DNS record.", false ),
2044 Command( "register", RegisterCmd
, kRegisterOpts
, "Uses DNSServiceRegister() to register a service.", false ),
2045 Command( "registerRecord", RegisterRecordCmd
, kRegisterRecordOpts
, "Uses DNSServiceRegisterRecord() to register a record.", false ),
2046 Command( "resolve", ResolveCmd
, kResolveOpts
, "Uses DNSServiceResolve() to resolve a service.", false ),
2047 Command( "reconfirm", ReconfirmCmd
, kReconfirmOpts
, "Uses DNSServiceReconfirmRecord() to reconfirm a record.", false ),
2048 Command( "getaddrinfo-posix", GetAddrInfoPOSIXCmd
, kGetAddrInfoPOSIXOpts
, "Uses getaddrinfo() to resolve a hostname to IP addresses.", false ),
2049 Command( "reverseLookup", ReverseLookupCmd
, kReverseLookupOpts
, "Uses DNSServiceQueryRecord() to perform a reverse IP address lookup.", false ),
2050 Command( "portMapping", PortMappingCmd
, kPortMappingOpts
, "Uses DNSServiceNATPortMappingCreate() to create a port mapping.", false ),
2051 Command( "browseAll", BrowseAllCmd
, kBrowseAllOpts
, "Browse and resolve all (or specific) services and, optionally, attempt connections.", false ),
2053 // Uncommon commands.
2055 Command( "getnameinfo", GetNameInfoCmd
, kGetNameInfoOpts
, "Calls getnameinfo() and prints results.", true ),
2056 Command( "getAddrInfoStress", GetAddrInfoStressCmd
, kGetAddrInfoStressOpts
, "Runs DNSServiceGetAddrInfo() stress testing.", true ),
2057 Command( "DNSQuery", DNSQueryCmd
, kDNSQueryOpts
, "Crafts and sends a DNS query.", true ),
2058 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
2059 Command( "DNSCrypt", DNSCryptCmd
, kDNSCryptOpts
, "Crafts and sends a DNSCrypt query.", true ),
2061 Command( "mdnsquery", MDNSQueryCmd
, kMDNSQueryOpts
, "Crafts and sends an mDNS query over the specified interface.", true ),
2062 Command( "mdnscollider", MDNSColliderCmd
, kMDNSColliderOpts
, "Creates record name collision scenarios.", true ),
2063 Command( "pid2uuid", PIDToUUIDCmd
, kPIDToUUIDOpts
, "Prints the UUID of a process.", true ),
2064 Command( "server", DNSServerCmd
, kDNSServerOpts
, "DNS server for testing.", true ),
2065 Command( "mdnsreplier", MDNSReplierCmd
, kMDNSReplierOpts
, "Responds to mDNS queries for a set of authoritative resource records.", true ),
2066 Command( "test", NULL
, kTestOpts
, "Commands for testing DNS-SD.", true ),
2067 Command( "ssdp", NULL
, kSSDPOpts
, "Simple Service Discovery Protocol (SSDP).", true ),
2068 #if( TARGET_OS_DARWIN )
2069 Command( "legacy", NULL
, kLegacyOpts
, "Legacy DNS API.", true ),
2070 Command( "dnsconfig", NULL
, kDNSConfigOpts
, "Add/remove a supplemental resolver entry to/from the system's DNS configuration.", true ),
2071 Command( "xpcsend", XPCSendCmd
, kXPCSendOpts
, "Sends a message to an XPC service.", true ),
2073 #if ( MDNSRESPONDER_PROJECT )
2074 Command( "interfaceMonitor", InterfaceMonitorCmd
, kInterfaceMonitorOpts
, "mDNSResponder's interface monitor.", true ),
2075 Command( "dnsproxy", DNSProxyCmd
, kDNSProxyOpts
, "Enables mDNSResponder's DNS proxy.", true ),
2077 Command( "daemonVersion", DaemonVersionCmd
, NULL
, "Prints the version of the DNS-SD daemon.", true ),
2083 //===========================================================================================================================
2084 // Helper Prototypes
2085 //===========================================================================================================================
2087 #define kExitReason_OneShotDone "one-shot done"
2088 #define kExitReason_ReceivedResponse "received response"
2089 #define kExitReason_SIGINT "interrupt signal"
2090 #define kExitReason_Timeout "timeout"
2091 #define kExitReason_TimeLimit "time limit"
2093 static void Exit( void *inContext
) ATTRIBUTE_NORETURN
;
2095 static DNSServiceFlags
GetDNSSDFlagsFromOpts( void );
2099 kConnectionType_None
= 0,
2100 kConnectionType_Normal
= 1,
2101 kConnectionType_DelegatePID
= 2,
2102 kConnectionType_DelegateUUID
= 3
2108 ConnectionType type
;
2119 CreateConnectionFromArgString(
2120 const char * inString
,
2121 dispatch_queue_t inQueue
,
2122 DNSServiceRef
* outSDRef
,
2123 ConnectionDesc
* outDesc
);
2124 static OSStatus
InterfaceIndexFromArgString( const char *inString
, uint32_t *outIndex
);
2125 static OSStatus
RecordDataFromArgString( const char *inString
, uint8_t **outDataPtr
, size_t *outDataLen
);
2126 static OSStatus
RecordTypeFromArgString( const char *inString
, uint16_t *outValue
);
2127 static OSStatus
RecordClassFromArgString( const char *inString
, uint16_t *outValue
);
2129 #define kInterfaceNameBufLen ( Max( IF_NAMESIZE, 16 ) + 1 )
2131 static char * InterfaceIndexToName( uint32_t inIfIndex
, char inNameBuf
[ kInterfaceNameBufLen
] );
2132 static const char * RecordTypeToString( unsigned int inValue
);
2135 DNSRecordDataToString(
2136 const void * inRDataPtr
,
2138 unsigned int inRDataType
,
2139 const void * inMsgPtr
,
2141 char ** outString
);
2145 const uint8_t * inMsgPtr
,
2152 WriteDNSQueryMessage(
2153 uint8_t inMsg
[ kDNSQueryMessageMaxLen
],
2156 const char * inQName
,
2159 size_t * outMsgLen
);
2163 typedef void ( *DispatchHandler
)( void *inContext
);
2166 DispatchSignalSourceCreate(
2168 DispatchHandler inEventHandler
,
2170 dispatch_source_t
* outSource
);
2172 DispatchSocketSourceCreate(
2174 dispatch_source_type_t inType
,
2175 dispatch_queue_t inQueue
,
2176 DispatchHandler inEventHandler
,
2177 DispatchHandler inCancelHandler
,
2179 dispatch_source_t
* outSource
);
2181 #define DispatchReadSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
2182 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_READ, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
2184 #define DispatchWriteSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
2185 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_WRITE, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
2188 DispatchTimerCreate(
2189 dispatch_time_t inStart
,
2190 uint64_t inIntervalNs
,
2191 uint64_t inLeewayNs
,
2192 dispatch_queue_t inQueue
,
2193 DispatchHandler inEventHandler
,
2194 DispatchHandler inCancelHandler
,
2196 dispatch_source_t
* outTimer
);
2198 #define DispatchTimerOneShotCreate( IN_START, IN_LEEWAY, IN_QUEUE, IN_EVENT_HANDLER, IN_CONTEXT, OUT_TIMER ) \
2199 DispatchTimerCreate( IN_START, DISPATCH_TIME_FOREVER, IN_LEEWAY, IN_QUEUE, IN_EVENT_HANDLER, NULL, IN_CONTEXT, OUT_TIMER )
2202 DispatchProcessMonitorCreate(
2204 unsigned long inFlags
,
2205 dispatch_queue_t inQueue
,
2206 DispatchHandler inEventHandler
,
2207 DispatchHandler inCancelHandler
,
2209 dispatch_source_t
* outMonitor
);
2211 static const char * ServiceTypeDescription( const char *inName
);
2215 SocketRef sock
; // Socket.
2216 void * userContext
; // User context.
2217 int32_t refCount
; // Reference count.
2221 static OSStatus
SocketContextCreate( SocketRef inSock
, void * inUserContext
, SocketContext
**outContext
);
2222 static SocketContext
* SocketContextRetain( SocketContext
*inContext
);
2223 static void SocketContextRelease( SocketContext
*inContext
);
2224 static void SocketContextCancelHandler( void *inContext
);
2226 #define ForgetSocketContext( X ) ForgetCustom( X, SocketContextRelease )
2228 static OSStatus
StringToInt32( const char *inString
, int32_t *outValue
);
2229 static OSStatus
StringToUInt32( const char *inString
, uint32_t *outValue
);
2230 #if( TARGET_OS_DARWIN )
2231 static int64_t _StringToInt64( const char *inString
, OSStatus
*outError
);
2232 static uint64_t _StringToUInt64( const char *inString
, OSStatus
*outError
);
2233 static pid_t
_StringToPID( const char *inString
, OSStatus
*outError
);
2235 _ParseEscapedString(
2238 const char * inDelimiters
,
2241 size_t * outCopiedLen
,
2242 size_t * outActualLen
,
2243 const char ** outPtr
);
2245 static OSStatus
StringToARecordData( const char *inString
, uint8_t **outPtr
, size_t *outLen
);
2246 static OSStatus
StringToAAAARecordData( const char *inString
, uint8_t **outPtr
, size_t *outLen
);
2247 static OSStatus
StringToDomainName( const char *inString
, uint8_t **outPtr
, size_t *outLen
);
2248 #if( TARGET_OS_DARWIN )
2249 static OSStatus
GetDefaultDNSServer( sockaddr_ip
*outAddr
);
2252 _ServerSocketOpenEx2(
2256 const void * inAddr
,
2260 Boolean inNoPortReuse
,
2261 SocketRef
* outSock
);
2263 static const struct sockaddr
* GetMDNSMulticastAddrV4( void );
2264 static const struct sockaddr
* GetMDNSMulticastAddrV6( void );
2267 CreateMulticastSocket(
2268 const struct sockaddr
* inAddr
,
2270 const char * inIfName
,
2274 SocketRef
* outSock
);
2276 static OSStatus
DecimalTextToUInt32( const char *inSrc
, const char *inEnd
, uint32_t *outValue
, const char **outPtr
);
2277 static OSStatus
CheckIntegerArgument( int inArgValue
, const char *inArgName
, int inMin
, int inMax
);
2278 static OSStatus
CheckDoubleArgument( double inArgValue
, const char *inArgName
, double inMin
, double inMax
);
2279 static OSStatus
CheckRootUser( void );
2280 static OSStatus
SpawnCommand( pid_t
*outPID
, const char *inFormat
, ... );
2281 static OSStatus
OutputFormatFromArgString( const char *inArgString
, OutputFormatType
*outFormat
);
2282 static OSStatus
OutputPropertyList( CFPropertyListRef inPList
, OutputFormatType inType
, const char *inOutputFilePath
);
2283 static OSStatus
CreateSRVRecordDataFromString( const char *inString
, uint8_t **outPtr
, size_t *outLen
);
2284 static OSStatus
CreateTXTRecordDataFromString( const char *inString
, int inDelimiter
, uint8_t **outPtr
, size_t *outLen
);
2286 CreateNSECRecordData(
2287 const uint8_t * inNextDomainName
,
2290 unsigned int inTypeCount
,
2295 const uint8_t * inNamePtr
,
2300 const uint8_t * inMName
,
2301 const uint8_t * inRName
,
2306 uint32_t inMinimumTTL
,
2309 CreateSOARecordData(
2310 const uint8_t * inMName
,
2311 const uint8_t * inRName
,
2316 uint32_t inMinimumTTL
,
2320 _DataBuffer_AppendDNSQuestion(
2322 const uint8_t * inNamePtr
,
2327 _DataBuffer_AppendDNSRecord(
2329 const uint8_t * inNamePtr
,
2334 const uint8_t * inRDataPtr
,
2335 size_t inRDataLen
);
2336 static char * _NanoTime64ToTimestamp( NanoTime64 inTime
, char *inBuf
, size_t inMaxLen
);
2338 typedef struct MDNSInterfaceItem MDNSInterfaceItem
;
2339 struct MDNSInterfaceItem
2341 MDNSInterfaceItem
* next
;
2352 kMDNSInterfaceSubset_All
= 0, // All mDNS-capable interfaces.
2353 kMDNSInterfaceSubset_AWDL
= 1, // All mDNS-capable AWDL interfaces.
2354 kMDNSInterfaceSubset_NonAWDL
= 2 // All mDNS-capable non-AWDL iterfaces.
2356 } MDNSInterfaceSubset
;
2358 static OSStatus
_MDNSInterfaceListCreate( MDNSInterfaceSubset inSubset
, size_t inItemSize
, MDNSInterfaceItem
**outList
);
2359 static void _MDNSInterfaceListFree( MDNSInterfaceItem
*inList
);
2360 #define _MDNSInterfaceListForget( X ) ForgetCustom( X, _MDNSInterfaceListFree )
2361 static OSStatus
_MDNSInterfaceGetAny( MDNSInterfaceSubset inSubset
, char inNameBuf
[ IF_NAMESIZE
+ 1 ], uint32_t *outIndex
);
2363 static OSStatus
_SetComputerName( CFStringRef inComputerName
, CFStringEncoding inEncoding
);
2364 static OSStatus
_SetComputerNameWithUTF8CString( const char *inComputerName
);
2365 static OSStatus
_SetLocalHostName( CFStringRef inLocalHostName
);
2366 static OSStatus
_SetLocalHostNameWithUTF8CString( const char *inLocalHostName
);
2368 static OSStatus
_SocketWriteAll( SocketRef inSock
, const void *inData
, size_t inSize
, int32_t inTimeoutSecs
);
2370 _StringToIPv4Address(
2372 StringToIPAddressFlags inFlags
,
2375 uint32_t * outSubnet
,
2376 uint32_t * outRouter
,
2377 const char ** outStr
);
2378 static void _StringArray_Free( char **inArray
, size_t inCount
);
2380 _StringToIPv6Address(
2382 StringToIPAddressFlags inFlags
,
2383 uint8_t outIPv6
[ 16 ],
2384 uint32_t * outScope
,
2387 const char ** outStr
);
2389 _ParseQuotedEscapedString(
2392 const char * inDelimiters
,
2395 size_t * outCopiedLen
,
2396 size_t * outTotalLen
,
2397 const char ** outSrc
);
2398 static void * _memdup( const void *inPtr
, size_t inLen
);
2399 static int _memicmp( const void *inP1
, const void *inP2
, size_t inLen
);
2400 static uint32_t _FNV1( const void *inData
, size_t inSize
);
2402 #define Unused( X ) (void)(X)
2404 //===========================================================================================================================
2406 //===========================================================================================================================
2408 typedef struct MDNSColliderPrivate
* MDNSColliderRef
;
2410 typedef uint32_t MDNSColliderProtocols
;
2411 #define kMDNSColliderProtocol_None 0
2412 #define kMDNSColliderProtocol_IPv4 ( 1 << 0 )
2413 #define kMDNSColliderProtocol_IPv6 ( 1 << 1 )
2415 typedef void ( *MDNSColliderStopHandler_f
)( void *inContext
, OSStatus inError
);
2417 static OSStatus
MDNSColliderCreate( dispatch_queue_t inQueue
, MDNSColliderRef
*outCollider
);
2418 static OSStatus
MDNSColliderStart( MDNSColliderRef inCollider
);
2419 static void MDNSColliderStop( MDNSColliderRef inCollider
);
2420 static void MDNSColliderSetProtocols( MDNSColliderRef inCollider
, MDNSColliderProtocols inProtocols
);
2421 static void MDNSColliderSetInterfaceIndex( MDNSColliderRef inCollider
, uint32_t inInterfaceIndex
);
2422 static OSStatus
MDNSColliderSetProgram( MDNSColliderRef inCollider
, const char *inProgramStr
);
2424 MDNSColliderSetStopHandler(
2425 MDNSColliderRef inCollider
,
2426 MDNSColliderStopHandler_f inStopHandler
,
2427 void * inStopContext
);
2429 MDNSColliderSetRecord(
2430 MDNSColliderRef inCollider
,
2431 const uint8_t * inName
,
2433 const void * inRDataPtr
,
2434 size_t inRDataLen
);
2435 static CFTypeID
MDNSColliderGetTypeID( void );
2437 //===========================================================================================================================
2439 //===========================================================================================================================
2441 typedef struct ServiceBrowserPrivate
* ServiceBrowserRef
;
2442 typedef struct ServiceBrowserResults ServiceBrowserResults
;
2443 typedef struct SBRDomain SBRDomain
;
2444 typedef struct SBRServiceType SBRServiceType
;
2445 typedef struct SBRServiceInstance SBRServiceInstance
;
2446 typedef struct SBRIPAddress SBRIPAddress
;
2448 typedef void ( *ServiceBrowserCallback_f
)( ServiceBrowserResults
*inResults
, OSStatus inError
, void *inContext
);
2450 struct ServiceBrowserResults
2452 SBRDomain
* domainList
; // List of domains in which services were found.
2457 SBRDomain
* next
; // Next domain in list.
2458 char * name
; // Name of domain represented by this object.
2459 SBRServiceType
* typeList
; // List of service types in this domain.
2462 struct SBRServiceType
2464 SBRServiceType
* next
; // Next service type in list.
2465 char * name
; // Name of service type represented by this object.
2466 SBRServiceInstance
* instanceList
; // List of service instances of this service type.
2469 struct SBRServiceInstance
2471 SBRServiceInstance
* next
; // Next service instance in list.
2472 char * name
; // Name of service instance represented by this object.
2473 char * hostname
; // Target from service instance's SRV record.
2474 uint32_t ifIndex
; // Index of interface over which this service instance was discovered.
2475 uint16_t port
; // Port from service instance's SRV record.
2476 uint8_t * txtPtr
; // Service instance's TXT record data.
2477 size_t txtLen
; // Service instance's TXT record data length.
2478 SBRIPAddress
* ipaddrList
; // List of IP addresses that the hostname resolved to.
2479 uint64_t discoverTimeUs
; // Time it took to discover this service instance in microseconds.
2480 uint64_t resolveTimeUs
; // Time it took to resolve this service instance in microseconds.
2485 SBRIPAddress
* next
; // Next IP address in list.
2486 sockaddr_ip sip
; // IPv4 or IPv6 address.
2487 uint64_t resolveTimeUs
; // Time it took to resolve this IP address in microseconds.
2490 static CFTypeID
ServiceBrowserGetTypeID( void );
2492 ServiceBrowserCreate(
2493 dispatch_queue_t inQueue
,
2494 uint32_t inInterfaceIndex
,
2495 const char * inDomain
,
2496 unsigned int inBrowseTimeSecs
,
2497 Boolean inIncludeAWDL
,
2498 ServiceBrowserRef
* outBrowser
);
2499 static void ServiceBrowserStart( ServiceBrowserRef inBrowser
);
2500 static OSStatus
ServiceBrowserAddServiceType( ServiceBrowserRef inBrowser
, const char *inServiceType
);
2502 ServiceBrowserSetCallback(
2503 ServiceBrowserRef inBrowser
,
2504 ServiceBrowserCallback_f inCallback
,
2506 static void ServiceBrowserResultsRetain( ServiceBrowserResults
*inResults
);
2507 static void ServiceBrowserResultsRelease( ServiceBrowserResults
*inResults
);
2509 #define ForgetServiceBrowserResults( X ) ForgetCustom( X, ServiceBrowserResultsRelease )
2511 //===========================================================================================================================
2513 //===========================================================================================================================
2515 #define _PRINTF_EXTENSION_HANDLER_DECLARE( NAME ) \
2517 _PrintFExtension ## NAME ## Handler( \
2518 PrintFContext * inContext, \
2519 PrintFFormat * inFormat, \
2520 PrintFVAList * inArgs, \
2521 void * inUserContext )
2523 _PRINTF_EXTENSION_HANDLER_DECLARE( Timestamp
);
2524 _PRINTF_EXTENSION_HANDLER_DECLARE( DNSMessage
);
2525 _PRINTF_EXTENSION_HANDLER_DECLARE( CallbackFlags
);
2526 _PRINTF_EXTENSION_HANDLER_DECLARE( DNSRecordData
);
2528 int main( int argc
, const char **argv
)
2532 // Route DebugServices logging output to stderr.
2534 dlog_control( "DebugServices:output=file;stderr" );
2536 PrintFRegisterExtension( "du:time", _PrintFExtensionTimestampHandler
, NULL
);
2537 PrintFRegisterExtension( "du:dnsmsg", _PrintFExtensionDNSMessageHandler
, NULL
);
2538 PrintFRegisterExtension( "du:cbflags", _PrintFExtensionCallbackFlagsHandler
, NULL
);
2539 PrintFRegisterExtension( "du:rdata", _PrintFExtensionDNSRecordDataHandler
, NULL
);
2540 CLIInit( argc
, argv
);
2541 err
= CLIParse( kGlobalOpts
, kCLIFlags_None
);
2542 if( err
) exit( 1 );
2544 return( gExitCode
);
2547 //===========================================================================================================================
2548 // VersionOptionCallback
2549 //===========================================================================================================================
2551 static OSStatus
VersionOptionCallback( CLIOption
*inOption
, const char *inArg
, int inUnset
)
2553 const char * srcVers
;
2554 #if( MDNSRESPONDER_PROJECT )
2562 #if( MDNSRESPONDER_PROJECT )
2563 srcVers
= SourceVersionToCString( _DNS_SD_H
, srcStr
);
2565 srcVers
= DNSSDUTIL_SOURCE_VERSION
;
2567 FPrintF( stdout
, "%s version %v (%s)\n", gProgramName
, kDNSSDUtilNumVersion
, srcVers
);
2569 return( kEndingErr
);
2572 //===========================================================================================================================
2574 //===========================================================================================================================
2576 typedef struct BrowseResolveOp BrowseResolveOp
;
2578 struct BrowseResolveOp
2580 BrowseResolveOp
* next
; // Next resolve operation in list.
2581 DNSServiceRef sdRef
; // sdRef of the DNSServiceResolve or DNSServiceQueryRecord operation.
2582 char * fullName
; // Full name of the service to resolve.
2583 uint32_t interfaceIndex
; // Interface index of the DNSServiceResolve or DNSServiceQueryRecord operation.
2588 DNSServiceRef mainRef
; // Main sdRef for shared connection.
2589 DNSServiceRef
* opRefs
; // Array of sdRefs for individual Browse operarions.
2590 size_t opRefsCount
; // Count of array of sdRefs for non-shared connections.
2591 const char * domain
; // Domain for DNSServiceBrowse operation(s).
2592 DNSServiceFlags flags
; // Flags for DNSServiceBrowse operation(s).
2593 char ** serviceTypes
; // Array of service types to browse for.
2594 size_t serviceTypesCount
; // Count of array of service types to browse for.
2595 int timeLimitSecs
; // Time limit of DNSServiceBrowse operation in seconds.
2596 BrowseResolveOp
* resolveList
; // List of resolve and/or TXT record query operations.
2597 uint32_t ifIndex
; // Interface index of DNSServiceBrowse operation(s).
2598 Boolean printedHeader
; // True if results header has been printed.
2599 Boolean doResolve
; // True if service instances are to be resolved.
2600 Boolean doResolveTXTOnly
; // True if TXT records of service instances are to be queried.
2604 static void BrowsePrintPrologue( const BrowseContext
*inContext
);
2605 static void BrowseContextFree( BrowseContext
*inContext
);
2606 static OSStatus
BrowseResolveOpCreate( const char *inFullName
, uint32_t inInterfaceIndex
, BrowseResolveOp
**outOp
);
2607 static void BrowseResolveOpFree( BrowseResolveOp
*inOp
);
2608 static void DNSSD_API
2610 DNSServiceRef inSDRef
,
2611 DNSServiceFlags inFlags
,
2612 uint32_t inInterfaceIndex
,
2613 DNSServiceErrorType inError
,
2614 const char * inName
,
2615 const char * inRegType
,
2616 const char * inDomain
,
2618 static void DNSSD_API
2619 BrowseResolveCallback(
2620 DNSServiceRef inSDRef
,
2621 DNSServiceFlags inFlags
,
2622 uint32_t inInterfaceIndex
,
2623 DNSServiceErrorType inError
,
2624 const char * inFullName
,
2625 const char * inHostname
,
2628 const unsigned char * inTXTPtr
,
2630 static void DNSSD_API
2631 BrowseQueryRecordCallback(
2632 DNSServiceRef inSDRef
,
2633 DNSServiceFlags inFlags
,
2634 uint32_t inInterfaceIndex
,
2635 DNSServiceErrorType inError
,
2636 const char * inFullName
,
2639 uint16_t inRDataLen
,
2640 const void * inRDataPtr
,
2644 static void BrowseCmd( void )
2648 BrowseContext
* context
= NULL
;
2649 dispatch_source_t signalSource
= NULL
;
2650 int useMainConnection
;
2652 // Set up SIGINT handler.
2654 signal( SIGINT
, SIG_IGN
);
2655 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
2656 require_noerr( err
, exit
);
2657 dispatch_resume( signalSource
);
2661 context
= (BrowseContext
*) calloc( 1, sizeof( *context
) );
2662 require_action( context
, exit
, err
= kNoMemoryErr
);
2664 context
->opRefs
= (DNSServiceRef
*) calloc( gBrowse_ServiceTypesCount
, sizeof( DNSServiceRef
) );
2665 require_action( context
->opRefs
, exit
, err
= kNoMemoryErr
);
2666 context
->opRefsCount
= gBrowse_ServiceTypesCount
;
2668 // Check command parameters.
2670 if( gBrowse_TimeLimitSecs
< 0 )
2672 FPrintF( stderr
, "Invalid time limit: %d seconds.\n", gBrowse_TimeLimitSecs
);
2677 // Create main connection.
2679 if( gConnectionOpt
)
2681 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
2682 require_noerr_quiet( err
, exit
);
2683 useMainConnection
= true;
2687 useMainConnection
= false;
2692 context
->flags
= GetDNSSDFlagsFromOpts();
2693 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
2697 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
2698 require_noerr_quiet( err
, exit
);
2700 // Set remaining parameters.
2702 context
->serviceTypes
= gBrowse_ServiceTypes
;
2703 context
->serviceTypesCount
= gBrowse_ServiceTypesCount
;
2704 context
->domain
= gBrowse_Domain
;
2705 context
->doResolve
= gBrowse_DoResolve
? true : false;
2706 context
->timeLimitSecs
= gBrowse_TimeLimitSecs
;
2707 context
->doResolveTXTOnly
= gBrowse_QueryTXT
? true : false;
2711 BrowsePrintPrologue( context
);
2713 // Start operation(s).
2715 for( i
= 0; i
< context
->serviceTypesCount
; ++i
)
2717 DNSServiceRef sdRef
;
2719 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
2720 err
= DNSServiceBrowse( &sdRef
, context
->flags
, context
->ifIndex
, context
->serviceTypes
[ i
], context
->domain
,
2721 BrowseCallback
, context
);
2722 require_noerr( err
, exit
);
2724 context
->opRefs
[ i
] = sdRef
;
2725 if( !useMainConnection
)
2727 err
= DNSServiceSetDispatchQueue( context
->opRefs
[ i
], dispatch_get_main_queue() );
2728 require_noerr( err
, exit
);
2734 if( context
->timeLimitSecs
> 0 )
2736 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
2737 kExitReason_TimeLimit
, Exit
);
2742 dispatch_source_forget( &signalSource
);
2743 if( context
) BrowseContextFree( context
);
2744 if( err
) exit( 1 );
2747 //===========================================================================================================================
2748 // BrowsePrintPrologue
2749 //===========================================================================================================================
2751 static void BrowsePrintPrologue( const BrowseContext
*inContext
)
2753 const int timeLimitSecs
= inContext
->timeLimitSecs
;
2754 const char * const * ptr
= (const char **) inContext
->serviceTypes
;
2755 const char * const * const end
= (const char **) inContext
->serviceTypes
+ inContext
->serviceTypesCount
;
2756 char ifName
[ kInterfaceNameBufLen
];
2758 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
2760 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
2761 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
2762 FPrintF( stdout
, "Service types: %s", *ptr
++ );
2763 while( ptr
< end
) FPrintF( stdout
, ", %s", *ptr
++ );
2764 FPrintF( stdout
, "\n" );
2765 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
? inContext
->domain
: "<NULL> (default domains)" );
2766 FPrintF( stdout
, "Time limit: " );
2767 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
2768 else FPrintF( stdout
, "∞\n" );
2769 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
2770 FPrintF( stdout
, "---\n" );
2773 //===========================================================================================================================
2774 // BrowseContextFree
2775 //===========================================================================================================================
2777 static void BrowseContextFree( BrowseContext
*inContext
)
2781 for( i
= 0; i
< inContext
->opRefsCount
; ++i
)
2783 DNSServiceForget( &inContext
->opRefs
[ i
] );
2785 if( inContext
->serviceTypes
)
2787 _StringArray_Free( inContext
->serviceTypes
, inContext
->serviceTypesCount
);
2788 inContext
->serviceTypes
= NULL
;
2789 inContext
->serviceTypesCount
= 0;
2791 DNSServiceForget( &inContext
->mainRef
);
2795 //===========================================================================================================================
2796 // BrowseResolveOpCreate
2797 //===========================================================================================================================
2799 static OSStatus
BrowseResolveOpCreate( const char *inFullName
, uint32_t inInterfaceIndex
, BrowseResolveOp
**outOp
)
2802 BrowseResolveOp
* resolveOp
;
2804 resolveOp
= (BrowseResolveOp
*) calloc( 1, sizeof( *resolveOp
) );
2805 require_action( resolveOp
, exit
, err
= kNoMemoryErr
);
2807 resolveOp
->fullName
= strdup( inFullName
);
2808 require_action( resolveOp
->fullName
, exit
, err
= kNoMemoryErr
);
2810 resolveOp
->interfaceIndex
= inInterfaceIndex
;
2817 if( resolveOp
) BrowseResolveOpFree( resolveOp
);
2821 //===========================================================================================================================
2822 // BrowseResolveOpFree
2823 //===========================================================================================================================
2825 static void BrowseResolveOpFree( BrowseResolveOp
*inOp
)
2827 DNSServiceForget( &inOp
->sdRef
);
2828 ForgetMem( &inOp
->fullName
);
2832 //===========================================================================================================================
2834 //===========================================================================================================================
2836 static void DNSSD_API
2838 DNSServiceRef inSDRef
,
2839 DNSServiceFlags inFlags
,
2840 uint32_t inInterfaceIndex
,
2841 DNSServiceErrorType inError
,
2842 const char * inName
,
2843 const char * inRegType
,
2844 const char * inDomain
,
2847 BrowseContext
* const context
= (BrowseContext
*) inContext
;
2849 BrowseResolveOp
* newOp
= NULL
;
2850 BrowseResolveOp
** p
;
2851 char fullName
[ kDNSServiceMaxDomainName
];
2856 gettimeofday( &now
, NULL
);
2859 require_noerr( err
, exit
);
2861 if( !context
->printedHeader
)
2863 FPrintF( stdout
, "%-26s %-16s IF %-20s %-20s Instance Name\n", "Timestamp", "Flags", "Domain", "Service Type" );
2864 context
->printedHeader
= true;
2866 FPrintF( stdout
, "%{du:time} %{du:cbflags} %2d %-20s %-20s %s\n",
2867 &now
, inFlags
, (int32_t) inInterfaceIndex
, inDomain
, inRegType
, inName
);
2869 if( !context
->doResolve
&& !context
->doResolveTXTOnly
) goto exit
;
2871 err
= DNSServiceConstructFullName( fullName
, inName
, inRegType
, inDomain
);
2872 require_noerr( err
, exit
);
2874 if( inFlags
& kDNSServiceFlagsAdd
)
2876 DNSServiceRef sdRef
;
2877 DNSServiceFlags flags
;
2879 err
= BrowseResolveOpCreate( fullName
, inInterfaceIndex
, &newOp
);
2880 require_noerr( err
, exit
);
2882 if( context
->mainRef
)
2884 sdRef
= context
->mainRef
;
2885 flags
= kDNSServiceFlagsShareConnection
;
2891 if( context
->doResolve
)
2893 err
= DNSServiceResolve( &sdRef
, flags
, inInterfaceIndex
, inName
, inRegType
, inDomain
, BrowseResolveCallback
,
2895 require_noerr( err
, exit
);
2899 err
= DNSServiceQueryRecord( &sdRef
, flags
, inInterfaceIndex
, fullName
, kDNSServiceType_TXT
, kDNSServiceClass_IN
,
2900 BrowseQueryRecordCallback
, NULL
);
2901 require_noerr( err
, exit
);
2904 newOp
->sdRef
= sdRef
;
2905 if( !context
->mainRef
)
2907 err
= DNSServiceSetDispatchQueue( newOp
->sdRef
, dispatch_get_main_queue() );
2908 require_noerr( err
, exit
);
2910 for( p
= &context
->resolveList
; *p
; p
= &( *p
)->next
) {}
2916 BrowseResolveOp
* resolveOp
;
2918 for( p
= &context
->resolveList
; ( resolveOp
= *p
) != NULL
; p
= &resolveOp
->next
)
2920 if( ( resolveOp
->interfaceIndex
== inInterfaceIndex
) && ( strcasecmp( resolveOp
->fullName
, fullName
) == 0 ) )
2927 *p
= resolveOp
->next
;
2928 BrowseResolveOpFree( resolveOp
);
2933 if( newOp
) BrowseResolveOpFree( newOp
);
2934 if( err
) exit( 1 );
2937 //===========================================================================================================================
2938 // BrowseQueryRecordCallback
2939 //===========================================================================================================================
2941 static void DNSSD_API
2942 BrowseQueryRecordCallback(
2943 DNSServiceRef inSDRef
,
2944 DNSServiceFlags inFlags
,
2945 uint32_t inInterfaceIndex
,
2946 DNSServiceErrorType inError
,
2947 const char * inFullName
,
2950 uint16_t inRDataLen
,
2951 const void * inRDataPtr
,
2961 Unused( inContext
);
2963 gettimeofday( &now
, NULL
);
2966 require_noerr( err
, exit
);
2967 require_action( inType
== kDNSServiceType_TXT
, exit
, err
= kTypeErr
);
2969 FPrintF( stdout
, "%{du:time} %s %s TXT on interface %d\n TXT: %#{txt}\n",
2970 &now
, DNSServiceFlagsToAddRmvStr( inFlags
), inFullName
, (int32_t) inInterfaceIndex
, inRDataPtr
,
2971 (size_t) inRDataLen
);
2974 if( err
) exit( 1 );
2977 //===========================================================================================================================
2978 // BrowseResolveCallback
2979 //===========================================================================================================================
2981 static void DNSSD_API
2982 BrowseResolveCallback(
2983 DNSServiceRef inSDRef
,
2984 DNSServiceFlags inFlags
,
2985 uint32_t inInterfaceIndex
,
2986 DNSServiceErrorType inError
,
2987 const char * inFullName
,
2988 const char * inHostname
,
2991 const unsigned char * inTXTPtr
,
2995 char errorStr
[ 64 ];
2999 Unused( inContext
);
3001 gettimeofday( &now
, NULL
);
3003 if( inError
) SNPrintF( errorStr
, sizeof( errorStr
), " error %#m", inError
);
3005 FPrintF( stdout
, "%{du:time} %s can be reached at %s:%u (interface %d)%?s\n",
3006 &now
, inFullName
, inHostname
, ntohs( inPort
), (int32_t) inInterfaceIndex
, inError
, errorStr
);
3009 FPrintF( stdout
, " TXT record: %#H\n", inTXTPtr
, (int) inTXTLen
, INT_MAX
);
3013 FPrintF( stdout
, " TXT record: %#{txt}\n", inTXTPtr
, (size_t) inTXTLen
);
3017 //===========================================================================================================================
3019 //===========================================================================================================================
3023 DNSServiceRef mainRef
; // Main sdRef for shared connection.
3024 DNSServiceRef opRef
; // sdRef for the DNSServiceGetAddrInfo operation.
3025 const char * name
; // Hostname to resolve.
3026 DNSServiceFlags flags
; // Flags argument for DNSServiceGetAddrInfo().
3027 DNSServiceProtocol protocols
; // Protocols argument for DNSServiceGetAddrInfo().
3028 uint32_t ifIndex
; // Interface index argument for DNSServiceGetAddrInfo().
3029 int timeLimitSecs
; // Time limit for the DNSServiceGetAddrInfo() operation in seconds.
3030 Boolean printedHeader
; // True if the results header has been printed.
3031 Boolean oneShotMode
; // True if command is done after the first set of results (one-shot mode).
3032 Boolean needIPv4
; // True if in one-shot mode and an IPv4 result is needed.
3033 Boolean needIPv6
; // True if in one-shot mode and an IPv6 result is needed.
3035 } GetAddrInfoContext
;
3037 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext
*inContext
);
3038 static void GetAddrInfoContextFree( GetAddrInfoContext
*inContext
);
3039 static void DNSSD_API
3040 GetAddrInfoCallback(
3041 DNSServiceRef inSDRef
,
3042 DNSServiceFlags inFlags
,
3043 uint32_t inInterfaceIndex
,
3044 DNSServiceErrorType inError
,
3045 const char * inHostname
,
3046 const struct sockaddr
* inSockAddr
,
3050 static void GetAddrInfoCmd( void )
3053 DNSServiceRef sdRef
;
3054 GetAddrInfoContext
* context
= NULL
;
3055 dispatch_source_t signalSource
= NULL
;
3056 int useMainConnection
;
3058 // Set up SIGINT handler.
3060 signal( SIGINT
, SIG_IGN
);
3061 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
3062 require_noerr( err
, exit
);
3063 dispatch_resume( signalSource
);
3065 // Check command parameters.
3067 if( gGetAddrInfo_TimeLimitSecs
< 0 )
3069 FPrintF( stderr
, "Invalid time limit: %d s.\n", gGetAddrInfo_TimeLimitSecs
);
3076 context
= (GetAddrInfoContext
*) calloc( 1, sizeof( *context
) );
3077 require_action( context
, exit
, err
= kNoMemoryErr
);
3079 // Create main connection.
3081 if( gConnectionOpt
)
3083 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
3084 require_noerr_quiet( err
, exit
);
3085 useMainConnection
= true;
3089 useMainConnection
= false;
3094 context
->flags
= GetDNSSDFlagsFromOpts();
3095 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
3099 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3100 require_noerr_quiet( err
, exit
);
3102 // Set remaining parameters.
3104 context
->name
= gGetAddrInfo_Name
;
3105 context
->timeLimitSecs
= gGetAddrInfo_TimeLimitSecs
;
3106 if( gGetAddrInfo_ProtocolIPv4
) context
->protocols
|= kDNSServiceProtocol_IPv4
;
3107 if( gGetAddrInfo_ProtocolIPv6
) context
->protocols
|= kDNSServiceProtocol_IPv6
;
3108 if( gGetAddrInfo_OneShot
)
3110 context
->oneShotMode
= true;
3111 context
->needIPv4
= ( gGetAddrInfo_ProtocolIPv4
|| !gGetAddrInfo_ProtocolIPv6
) ? true : false;
3112 context
->needIPv6
= ( gGetAddrInfo_ProtocolIPv6
|| !gGetAddrInfo_ProtocolIPv4
) ? true : false;
3117 GetAddrInfoPrintPrologue( context
);
3121 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
3122 err
= DNSServiceGetAddrInfo( &sdRef
, context
->flags
, context
->ifIndex
, context
->protocols
, context
->name
,
3123 GetAddrInfoCallback
, context
);
3124 require_noerr( err
, exit
);
3126 context
->opRef
= sdRef
;
3127 if( !useMainConnection
)
3129 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
3130 require_noerr( err
, exit
);
3135 if( context
->timeLimitSecs
> 0 )
3137 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
3138 kExitReason_TimeLimit
, Exit
);
3143 dispatch_source_forget( &signalSource
);
3144 if( context
) GetAddrInfoContextFree( context
);
3145 if( err
) exit( 1 );
3148 //===========================================================================================================================
3149 // GetAddrInfoPrintPrologue
3150 //===========================================================================================================================
3152 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext
*inContext
)
3154 const int timeLimitSecs
= inContext
->timeLimitSecs
;
3155 char ifName
[ kInterfaceNameBufLen
];
3157 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
3159 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
3160 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
3161 FPrintF( stdout
, "Protocols: %#{flags}\n", inContext
->protocols
, kDNSServiceProtocolDescriptors
);
3162 FPrintF( stdout
, "Name: %s\n", inContext
->name
);
3163 FPrintF( stdout
, "Mode: %s\n", inContext
->oneShotMode
? "one-shot" : "continuous" );
3164 FPrintF( stdout
, "Time limit: " );
3165 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
3166 else FPrintF( stdout
, "∞\n" );
3167 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
3168 FPrintF( stdout
, "---\n" );
3171 //===========================================================================================================================
3172 // GetAddrInfoContextFree
3173 //===========================================================================================================================
3175 static void GetAddrInfoContextFree( GetAddrInfoContext
*inContext
)
3177 DNSServiceForget( &inContext
->opRef
);
3178 DNSServiceForget( &inContext
->mainRef
);
3182 //===========================================================================================================================
3183 // GetAddrInfoCallback
3184 //===========================================================================================================================
3186 static void DNSSD_API
3187 GetAddrInfoCallback(
3188 DNSServiceRef inSDRef
,
3189 DNSServiceFlags inFlags
,
3190 uint32_t inInterfaceIndex
,
3191 DNSServiceErrorType inError
,
3192 const char * inHostname
,
3193 const struct sockaddr
* inSockAddr
,
3197 GetAddrInfoContext
* const context
= (GetAddrInfoContext
*) inContext
;
3200 const char * addrStr
;
3201 char addrStrBuf
[ kSockAddrStringMaxSize
];
3205 gettimeofday( &now
, NULL
);
3209 case kDNSServiceErr_NoError
:
3210 case kDNSServiceErr_NoSuchRecord
:
3214 case kDNSServiceErr_Timeout
:
3215 Exit( kExitReason_Timeout
);
3222 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
3224 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
3231 err
= SockAddrToString( inSockAddr
, kSockAddrStringFlagsNone
, addrStrBuf
);
3232 require_noerr( err
, exit
);
3233 addrStr
= addrStrBuf
;
3237 addrStr
= ( inSockAddr
->sa_family
== AF_INET
) ? kNoSuchRecordAStr
: kNoSuchRecordAAAAStr
;
3240 if( !context
->printedHeader
)
3242 FPrintF( stdout
, "%-26s %-16s IF %-30s %-34s %6s\n", "Timestamp", "Flags", "Hostname", "Address", "TTL" );
3243 context
->printedHeader
= true;
3245 FPrintF( stdout
, "%{du:time} %{du:cbflags} %2d %-30s %-34s %6u\n",
3246 &now
, inFlags
, (int32_t) inInterfaceIndex
, inHostname
, addrStr
, inTTL
);
3248 if( context
->oneShotMode
)
3250 if( inFlags
& kDNSServiceFlagsAdd
)
3252 if( inSockAddr
->sa_family
== AF_INET
) context
->needIPv4
= false;
3253 else context
->needIPv6
= false;
3255 if( !( inFlags
& kDNSServiceFlagsMoreComing
) && !context
->needIPv4
&& !context
->needIPv6
)
3257 Exit( kExitReason_OneShotDone
);
3262 if( err
) exit( 1 );
3265 //===========================================================================================================================
3267 //===========================================================================================================================
3271 DNSServiceRef mainRef
; // Main sdRef for shared connection.
3272 DNSServiceRef opRef
; // sdRef for the DNSServiceQueryRecord operation.
3273 const char * recordName
; // Resource record name argument for DNSServiceQueryRecord().
3274 DNSServiceFlags flags
; // Flags argument for DNSServiceQueryRecord().
3275 uint32_t ifIndex
; // Interface index argument for DNSServiceQueryRecord().
3276 int timeLimitSecs
; // Time limit for the DNSServiceQueryRecord() operation in seconds.
3277 uint16_t recordType
; // Resource record type argument for DNSServiceQueryRecord().
3278 Boolean printedHeader
; // True if the results header was printed.
3279 Boolean oneShotMode
; // True if command is done after the first set of results (one-shot mode).
3280 Boolean gotRecord
; // True if in one-shot mode and received at least one record of the desired type.
3281 Boolean printRawRData
; // True if RDATA results are not to be formatted when printed.
3283 } QueryRecordContext
;
3285 static void QueryRecordPrintPrologue( const QueryRecordContext
*inContext
);
3286 static void QueryRecordContextFree( QueryRecordContext
*inContext
);
3287 static void DNSSD_API
3288 QueryRecordCallback(
3289 DNSServiceRef inSDRef
,
3290 DNSServiceFlags inFlags
,
3291 uint32_t inInterfaceIndex
,
3292 DNSServiceErrorType inError
,
3293 const char * inFullName
,
3296 uint16_t inRDataLen
,
3297 const void * inRDataPtr
,
3301 static void QueryRecordCmd( void )
3304 DNSServiceRef sdRef
;
3305 QueryRecordContext
* context
= NULL
;
3306 dispatch_source_t signalSource
= NULL
;
3307 int useMainConnection
;
3309 // Set up SIGINT handler.
3311 signal( SIGINT
, SIG_IGN
);
3312 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
3313 require_noerr( err
, exit
);
3314 dispatch_resume( signalSource
);
3318 context
= (QueryRecordContext
*) calloc( 1, sizeof( *context
) );
3319 require_action( context
, exit
, err
= kNoMemoryErr
);
3321 // Check command parameters.
3323 if( gQueryRecord_TimeLimitSecs
< 0 )
3325 FPrintF( stderr
, "Invalid time limit: %d seconds.\n", gQueryRecord_TimeLimitSecs
);
3330 // Create main connection.
3332 if( gConnectionOpt
)
3334 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
3335 require_noerr_quiet( err
, exit
);
3336 useMainConnection
= true;
3340 useMainConnection
= false;
3345 context
->flags
= GetDNSSDFlagsFromOpts();
3346 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
3350 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3351 require_noerr_quiet( err
, exit
);
3355 err
= RecordTypeFromArgString( gQueryRecord_Type
, &context
->recordType
);
3356 require_noerr( err
, exit
);
3358 // Set remaining parameters.
3360 context
->recordName
= gQueryRecord_Name
;
3361 context
->timeLimitSecs
= gQueryRecord_TimeLimitSecs
;
3362 context
->oneShotMode
= gQueryRecord_OneShot
? true : false;
3363 context
->printRawRData
= gQueryRecord_RawRData
? true : false;
3367 QueryRecordPrintPrologue( context
);
3371 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
3372 err
= DNSServiceQueryRecord( &sdRef
, context
->flags
, context
->ifIndex
, context
->recordName
, context
->recordType
,
3373 kDNSServiceClass_IN
, QueryRecordCallback
, context
);
3374 require_noerr( err
, exit
);
3376 context
->opRef
= sdRef
;
3377 if( !useMainConnection
)
3379 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
3380 require_noerr( err
, exit
);
3385 if( context
->timeLimitSecs
> 0 )
3387 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(), kExitReason_TimeLimit
,
3393 dispatch_source_forget( &signalSource
);
3394 if( context
) QueryRecordContextFree( context
);
3395 if( err
) exit( 1 );
3398 //===========================================================================================================================
3399 // QueryRecordContextFree
3400 //===========================================================================================================================
3402 static void QueryRecordContextFree( QueryRecordContext
*inContext
)
3404 DNSServiceForget( &inContext
->opRef
);
3405 DNSServiceForget( &inContext
->mainRef
);
3409 //===========================================================================================================================
3410 // QueryRecordPrintPrologue
3411 //===========================================================================================================================
3413 static void QueryRecordPrintPrologue( const QueryRecordContext
*inContext
)
3415 const int timeLimitSecs
= inContext
->timeLimitSecs
;
3416 char ifName
[ kInterfaceNameBufLen
];
3418 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
3420 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
3421 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
3422 FPrintF( stdout
, "Name: %s\n", inContext
->recordName
);
3423 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->recordType
), inContext
->recordType
);
3424 FPrintF( stdout
, "Mode: %s\n", inContext
->oneShotMode
? "one-shot" : "continuous" );
3425 FPrintF( stdout
, "Time limit: " );
3426 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
3427 else FPrintF( stdout
, "∞\n" );
3428 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
3429 FPrintF( stdout
, "---\n" );
3433 //===========================================================================================================================
3434 // QueryRecordCallback
3435 //===========================================================================================================================
3437 static void DNSSD_API
3438 QueryRecordCallback(
3439 DNSServiceRef inSDRef
,
3440 DNSServiceFlags inFlags
,
3441 uint32_t inInterfaceIndex
,
3442 DNSServiceErrorType inError
,
3443 const char * inFullName
,
3446 uint16_t inRDataLen
,
3447 const void * inRDataPtr
,
3451 QueryRecordContext
* const context
= (QueryRecordContext
*) inContext
;
3454 char * rdataStr
= NULL
;
3458 gettimeofday( &now
, NULL
);
3462 case kDNSServiceErr_NoError
:
3463 case kDNSServiceErr_NoSuchRecord
:
3467 case kDNSServiceErr_Timeout
:
3468 Exit( kExitReason_Timeout
);
3475 if( inError
!= kDNSServiceErr_NoSuchRecord
)
3477 if( !context
->printRawRData
) DNSRecordDataToString( inRDataPtr
, inRDataLen
, inType
, NULL
, 0, &rdataStr
);
3480 ASPrintF( &rdataStr
, "%#H", inRDataPtr
, inRDataLen
, INT_MAX
);
3481 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
3485 if( !context
->printedHeader
)
3487 FPrintF( stdout
, "%-26s %-16s IF %-32s %-5s %-5s %6s RData\n",
3488 "Timestamp", "Flags", "Name", "Type", "Class", "TTL" );
3489 context
->printedHeader
= true;
3491 FPrintF( stdout
, "%{du:time} %{du:cbflags} %2d %-32s %-5s %?-5s%?5u %6u %s\n",
3492 &now
, inFlags
, (int32_t) inInterfaceIndex
, inFullName
, RecordTypeToString( inType
),
3493 ( inClass
== kDNSServiceClass_IN
), "IN", ( inClass
!= kDNSServiceClass_IN
), inClass
, inTTL
,
3494 rdataStr
? rdataStr
: kNoSuchRecordStr
);
3496 if( context
->oneShotMode
)
3498 if( ( inFlags
& kDNSServiceFlagsAdd
) &&
3499 ( ( context
->recordType
== kDNSServiceType_ANY
) || ( context
->recordType
== inType
) ) )
3501 context
->gotRecord
= true;
3503 if( !( inFlags
& kDNSServiceFlagsMoreComing
) && context
->gotRecord
) Exit( kExitReason_OneShotDone
);
3507 FreeNullSafe( rdataStr
);
3508 if( err
) exit( 1 );
3511 //===========================================================================================================================
3513 //===========================================================================================================================
3517 DNSRecordRef recordRef
; // Reference returned by DNSServiceAddRecord().
3518 uint8_t * dataPtr
; // Record data.
3519 size_t dataLen
; // Record data length.
3520 uint32_t ttl
; // Record TTL value.
3521 uint16_t type
; // Record type.
3527 DNSServiceRef opRef
; // sdRef for DNSServiceRegister operation.
3528 const char * name
; // Service name argument for DNSServiceRegister().
3529 const char * type
; // Service type argument for DNSServiceRegister().
3530 const char * domain
; // Domain in which advertise the service.
3531 uint8_t * txtPtr
; // Service TXT record data. (malloc'd)
3532 size_t txtLen
; // Service TXT record data len.
3533 ExtraRecord
* extraRecords
; // Array of extra records to add to registered service.
3534 size_t extraRecordsCount
; // Number of extra records.
3535 uint8_t * updateTXTPtr
; // Pointer to record data for TXT record update. (malloc'd)
3536 size_t updateTXTLen
; // Length of record data for TXT record update.
3537 uint32_t updateTTL
; // TTL of updated TXT record.
3538 int updateDelayMs
; // Post-registration TXT record update delay in milliseconds.
3539 DNSServiceFlags flags
; // Flags argument for DNSServiceRegister().
3540 uint32_t ifIndex
; // Interface index argument for DNSServiceRegister().
3541 int lifetimeMs
; // Lifetime of the record registration in milliseconds.
3542 uint16_t port
; // Service instance's port number.
3543 Boolean printedHeader
; // True if results header was printed.
3544 Boolean didRegister
; // True if service was registered.
3548 static void RegisterPrintPrologue( const RegisterContext
*inContext
);
3549 static void RegisterContextFree( RegisterContext
*inContext
);
3550 static void DNSSD_API
3552 DNSServiceRef inSDRef
,
3553 DNSServiceFlags inFlags
,
3554 DNSServiceErrorType inError
,
3555 const char * inName
,
3556 const char * inType
,
3557 const char * inDomain
,
3559 static void RegisterUpdate( void *inContext
);
3561 static void RegisterCmd( void )
3564 RegisterContext
* context
= NULL
;
3565 dispatch_source_t signalSource
= NULL
;
3567 // Set up SIGINT handler.
3569 signal( SIGINT
, SIG_IGN
);
3570 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
3571 require_noerr( err
, exit
);
3572 dispatch_resume( signalSource
);
3576 context
= (RegisterContext
*) calloc( 1, sizeof( *context
) );
3577 require_action( context
, exit
, err
= kNoMemoryErr
);
3579 // Check command parameters.
3581 if( ( gRegister_Port
< 0 ) || ( gRegister_Port
> UINT16_MAX
) )
3583 FPrintF( stderr
, "Port number %d is out-of-range.\n", gRegister_Port
);
3588 if( ( gAddRecord_DataCount
!= gAddRecord_TypesCount
) || ( gAddRecord_TTLsCount
!= gAddRecord_TypesCount
) )
3590 FPrintF( stderr
, "There are missing additional record parameters.\n" );
3597 context
->flags
= GetDNSSDFlagsFromOpts();
3599 // Get interface index.
3601 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3602 require_noerr_quiet( err
, exit
);
3604 // Get TXT record data.
3608 err
= RecordDataFromArgString( gRegister_TXT
, &context
->txtPtr
, &context
->txtLen
);
3609 require_noerr_quiet( err
, exit
);
3612 // Set remaining parameters.
3614 context
->name
= gRegister_Name
;
3615 context
->type
= gRegister_Type
;
3616 context
->domain
= gRegister_Domain
;
3617 context
->port
= (uint16_t) gRegister_Port
;
3618 context
->lifetimeMs
= gRegister_LifetimeMs
;
3620 if( gAddRecord_TypesCount
> 0 )
3624 context
->extraRecords
= (ExtraRecord
*) calloc( gAddRecord_TypesCount
, sizeof( ExtraRecord
) );
3625 require_action( context
, exit
, err
= kNoMemoryErr
);
3626 context
->extraRecordsCount
= gAddRecord_TypesCount
;
3628 for( i
= 0; i
< gAddRecord_TypesCount
; ++i
)
3630 ExtraRecord
* const extraRecord
= &context
->extraRecords
[ i
];
3632 err
= RecordTypeFromArgString( gAddRecord_Types
[ i
], &extraRecord
->type
);
3633 require_noerr( err
, exit
);
3635 err
= StringToUInt32( gAddRecord_TTLs
[ i
], &extraRecord
->ttl
);
3638 FPrintF( stderr
, "Invalid TTL value: %s\n", gAddRecord_TTLs
[ i
] );
3643 err
= RecordDataFromArgString( gAddRecord_Data
[ i
], &extraRecord
->dataPtr
, &extraRecord
->dataLen
);
3644 require_noerr_quiet( err
, exit
);
3648 if( gUpdateRecord_Data
)
3650 err
= RecordDataFromArgString( gUpdateRecord_Data
, &context
->updateTXTPtr
, &context
->updateTXTLen
);
3651 require_noerr_quiet( err
, exit
);
3653 context
->updateTTL
= (uint32_t) gUpdateRecord_TTL
;
3654 context
->updateDelayMs
= gUpdateRecord_DelayMs
;
3659 RegisterPrintPrologue( context
);
3663 err
= DNSServiceRegister( &context
->opRef
, context
->flags
, context
->ifIndex
, context
->name
, context
->type
,
3664 context
->domain
, NULL
, htons( context
->port
), (uint16_t) context
->txtLen
, context
->txtPtr
,
3665 RegisterCallback
, context
);
3666 ForgetMem( &context
->txtPtr
);
3667 require_noerr( err
, exit
);
3669 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
3670 require_noerr( err
, exit
);
3675 dispatch_source_forget( &signalSource
);
3676 if( context
) RegisterContextFree( context
);
3677 if( err
) exit( 1 );
3680 //===========================================================================================================================
3681 // RegisterPrintPrologue
3682 //===========================================================================================================================
3684 static void RegisterPrintPrologue( const RegisterContext
*inContext
)
3688 char ifName
[ kInterfaceNameBufLen
];
3690 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
3692 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
3693 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
3694 FPrintF( stdout
, "Name: %s\n", inContext
->name
? inContext
->name
: "<NULL>" );
3695 FPrintF( stdout
, "Type: %s\n", inContext
->type
);
3696 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
? inContext
->domain
: "<NULL> (default domains)" );
3697 FPrintF( stdout
, "Port: %u\n", inContext
->port
);
3698 FPrintF( stdout
, "TXT data: %#{txt}\n", inContext
->txtPtr
, inContext
->txtLen
);
3699 infinite
= ( inContext
->lifetimeMs
< 0 ) ? true : false;
3700 FPrintF( stdout
, "Lifetime: %?s%?d ms\n", infinite
, "∞", !infinite
, inContext
->lifetimeMs
);
3701 if( inContext
->updateTXTPtr
)
3703 FPrintF( stdout
, "\nUpdate record:\n" );
3704 FPrintF( stdout
, " Delay: %d ms\n", ( inContext
->updateDelayMs
> 0 ) ? inContext
->updateDelayMs
: 0 );
3705 FPrintF( stdout
, " TTL: %u%?s\n",
3706 inContext
->updateTTL
, inContext
->updateTTL
== 0, " (system will use a default value.)" );
3707 FPrintF( stdout
, " TXT data: %#{txt}\n", inContext
->updateTXTPtr
, inContext
->updateTXTLen
);
3709 if( inContext
->extraRecordsCount
> 0 ) FPrintF( stdout
, "\n" );
3710 for( i
= 0; i
< inContext
->extraRecordsCount
; ++i
)
3712 const ExtraRecord
* record
= &inContext
->extraRecords
[ i
];
3714 FPrintF( stdout
, "Extra record %zu:\n", i
+ 1 );
3715 FPrintF( stdout
, " Type: %s (%u)\n", RecordTypeToString( record
->type
), record
->type
);
3716 FPrintF( stdout
, " TTL: %u%?s\n", record
->ttl
, record
->ttl
== 0, " (system will use a default value.)" );
3717 FPrintF( stdout
, " RData: %#H\n\n", record
->dataPtr
, (int) record
->dataLen
, INT_MAX
);
3719 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
3720 FPrintF( stdout
, "---\n" );
3723 //===========================================================================================================================
3724 // RegisterContextFree
3725 //===========================================================================================================================
3727 static void RegisterContextFree( RegisterContext
*inContext
)
3729 ExtraRecord
* record
;
3730 const ExtraRecord
* const end
= inContext
->extraRecords
+ inContext
->extraRecordsCount
;
3732 DNSServiceForget( &inContext
->opRef
);
3733 ForgetMem( &inContext
->txtPtr
);
3734 for( record
= inContext
->extraRecords
; record
< end
; ++record
)
3736 check( !record
->recordRef
);
3737 ForgetMem( &record
->dataPtr
);
3739 ForgetMem( &inContext
->extraRecords
);
3740 ForgetMem( &inContext
->updateTXTPtr
);
3744 //===========================================================================================================================
3746 //===========================================================================================================================
3748 static void DNSSD_API
3750 DNSServiceRef inSDRef
,
3751 DNSServiceFlags inFlags
,
3752 DNSServiceErrorType inError
,
3753 const char * inName
,
3754 const char * inType
,
3755 const char * inDomain
,
3758 RegisterContext
* const context
= (RegisterContext
*) inContext
;
3764 gettimeofday( &now
, NULL
);
3766 if( !context
->printedHeader
)
3768 FPrintF( stdout
, "%-26s %-16s Service\n", "Timestamp", "Flags" );
3769 context
->printedHeader
= true;
3771 FPrintF( stdout
, "%{du:time} %{du:cbflags} %s.%s%s %?#m\n", &now
, inFlags
, inName
, inType
, inDomain
, inError
, inError
);
3773 require_noerr_action_quiet( inError
, exit
, err
= inError
);
3775 if( !context
->didRegister
&& ( inFlags
& kDNSServiceFlagsAdd
) )
3777 context
->didRegister
= true;
3778 if( context
->updateTXTPtr
)
3780 if( context
->updateDelayMs
> 0 )
3782 dispatch_after_f( dispatch_time_milliseconds( context
->updateDelayMs
), dispatch_get_main_queue(),
3783 context
, RegisterUpdate
);
3787 RegisterUpdate( context
);
3790 if( context
->extraRecordsCount
> 0 )
3792 ExtraRecord
* record
;
3793 const ExtraRecord
* const end
= context
->extraRecords
+ context
->extraRecordsCount
;
3795 for( record
= context
->extraRecords
; record
< end
; ++record
)
3797 err
= DNSServiceAddRecord( context
->opRef
, &record
->recordRef
, 0, record
->type
,
3798 (uint16_t) record
->dataLen
, record
->dataPtr
, record
->ttl
);
3799 require_noerr( err
, exit
);
3802 if( context
->lifetimeMs
== 0 )
3804 Exit( kExitReason_TimeLimit
);
3806 else if( context
->lifetimeMs
> 0 )
3808 dispatch_after_f( dispatch_time_milliseconds( context
->lifetimeMs
), dispatch_get_main_queue(),
3809 kExitReason_TimeLimit
, Exit
);
3815 if( err
) exit( 1 );
3818 //===========================================================================================================================
3820 //===========================================================================================================================
3822 static void RegisterUpdate( void *inContext
)
3825 RegisterContext
* const context
= (RegisterContext
*) inContext
;
3827 err
= DNSServiceUpdateRecord( context
->opRef
, NULL
, 0, (uint16_t) context
->updateTXTLen
, context
->updateTXTPtr
,
3828 context
->updateTTL
);
3829 require_noerr( err
, exit
);
3832 if( err
) exit( 1 );
3835 //===========================================================================================================================
3836 // RegisterRecordCmd
3837 //===========================================================================================================================
3841 DNSServiceRef conRef
; // sdRef to be initialized by DNSServiceCreateConnection().
3842 DNSRecordRef recordRef
; // Registered record reference.
3843 const char * recordName
; // Name of resource record.
3844 uint8_t * dataPtr
; // Pointer to resource record data.
3845 size_t dataLen
; // Length of resource record data.
3846 uint32_t ttl
; // TTL value of resource record in seconds.
3847 uint32_t ifIndex
; // Interface index argument for DNSServiceRegisterRecord().
3848 DNSServiceFlags flags
; // Flags argument for DNSServiceRegisterRecord().
3849 int lifetimeMs
; // Lifetime of the record registration in milliseconds.
3850 uint16_t recordType
; // Resource record type.
3851 uint8_t * updateDataPtr
; // Pointer to data for record update. (malloc'd)
3852 size_t updateDataLen
; // Length of data for record update.
3853 uint32_t updateTTL
; // TTL for updated record.
3854 int updateDelayMs
; // Post-registration record update delay in milliseconds.
3855 Boolean didRegister
; // True if the record was registered.
3857 } RegisterRecordContext
;
3859 static void RegisterRecordPrintPrologue( const RegisterRecordContext
*inContext
);
3860 static void RegisterRecordContextFree( RegisterRecordContext
*inContext
);
3861 static void DNSSD_API
3862 RegisterRecordCallback(
3863 DNSServiceRef inSDRef
,
3864 DNSRecordRef inRecordRef
,
3865 DNSServiceFlags inFlags
,
3866 DNSServiceErrorType inError
,
3868 static void RegisterRecordUpdate( void *inContext
);
3870 static void RegisterRecordCmd( void )
3873 RegisterRecordContext
* context
= NULL
;
3874 dispatch_source_t signalSource
= NULL
;
3876 // Set up SIGINT handler.
3878 signal( SIGINT
, SIG_IGN
);
3879 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
3880 require_noerr( err
, exit
);
3881 dispatch_resume( signalSource
);
3885 context
= (RegisterRecordContext
*) calloc( 1, sizeof( *context
) );
3886 require_action( context
, exit
, err
= kNoMemoryErr
);
3888 // Create connection.
3890 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->conRef
, NULL
);
3891 require_noerr_quiet( err
, exit
);
3895 context
->flags
= GetDNSSDFlagsFromOpts();
3899 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3900 require_noerr_quiet( err
, exit
);
3904 err
= RecordTypeFromArgString( gRegisterRecord_Type
, &context
->recordType
);
3905 require_noerr( err
, exit
);
3909 if( gRegisterRecord_Data
)
3911 err
= RecordDataFromArgString( gRegisterRecord_Data
, &context
->dataPtr
, &context
->dataLen
);
3912 require_noerr_quiet( err
, exit
);
3915 // Set remaining parameters.
3917 context
->recordName
= gRegisterRecord_Name
;
3918 context
->ttl
= (uint32_t) gRegisterRecord_TTL
;
3919 context
->lifetimeMs
= gRegisterRecord_LifetimeMs
;
3923 if( gRegisterRecord_UpdateData
)
3925 err
= RecordDataFromArgString( gRegisterRecord_UpdateData
, &context
->updateDataPtr
, &context
->updateDataLen
);
3926 require_noerr_quiet( err
, exit
);
3928 context
->updateTTL
= (uint32_t) gRegisterRecord_UpdateTTL
;
3929 context
->updateDelayMs
= gRegisterRecord_UpdateDelayMs
;
3934 RegisterRecordPrintPrologue( context
);
3938 err
= DNSServiceRegisterRecord( context
->conRef
, &context
->recordRef
, context
->flags
, context
->ifIndex
,
3939 context
->recordName
, context
->recordType
, kDNSServiceClass_IN
, (uint16_t) context
->dataLen
, context
->dataPtr
,
3940 context
->ttl
, RegisterRecordCallback
, context
);
3943 FPrintF( stderr
, "DNSServiceRegisterRecord() returned %#m\n", err
);
3950 dispatch_source_forget( &signalSource
);
3951 if( context
) RegisterRecordContextFree( context
);
3952 if( err
) exit( 1 );
3955 //===========================================================================================================================
3956 // RegisterRecordPrintPrologue
3957 //===========================================================================================================================
3959 static void RegisterRecordPrintPrologue( const RegisterRecordContext
*inContext
)
3962 char ifName
[ kInterfaceNameBufLen
];
3964 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
3966 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
3967 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
3968 FPrintF( stdout
, "Name: %s\n", inContext
->recordName
);
3969 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->recordType
), inContext
->recordType
);
3970 FPrintF( stdout
, "TTL: %u\n", inContext
->ttl
);
3971 FPrintF( stdout
, "Data: %#H\n", inContext
->dataPtr
, (int) inContext
->dataLen
, INT_MAX
);
3972 infinite
= ( inContext
->lifetimeMs
< 0 ) ? true : false;
3973 FPrintF( stdout
, "Lifetime: %?s%?d ms\n", infinite
, "∞", !infinite
, inContext
->lifetimeMs
);
3974 if( inContext
->updateDataPtr
)
3976 FPrintF( stdout
, "\nUpdate record:\n" );
3977 FPrintF( stdout
, " Delay: %d ms\n", ( inContext
->updateDelayMs
>= 0 ) ? inContext
->updateDelayMs
: 0 );
3978 FPrintF( stdout
, " TTL: %u%?s\n",
3979 inContext
->updateTTL
, inContext
->updateTTL
== 0, " (system will use a default value.)" );
3980 FPrintF( stdout
, " RData: %#H\n", inContext
->updateDataPtr
, (int) inContext
->updateDataLen
, INT_MAX
);
3982 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
3983 FPrintF( stdout
, "---\n" );
3986 //===========================================================================================================================
3987 // RegisterRecordContextFree
3988 //===========================================================================================================================
3990 static void RegisterRecordContextFree( RegisterRecordContext
*inContext
)
3992 DNSServiceForget( &inContext
->conRef
);
3993 ForgetMem( &inContext
->dataPtr
);
3994 ForgetMem( &inContext
->updateDataPtr
);
3998 //===========================================================================================================================
3999 // RegisterRecordCallback
4000 //===========================================================================================================================
4003 RegisterRecordCallback(
4004 DNSServiceRef inSDRef
,
4005 DNSRecordRef inRecordRef
,
4006 DNSServiceFlags inFlags
,
4007 DNSServiceErrorType inError
,
4010 RegisterRecordContext
* context
= (RegisterRecordContext
*) inContext
;
4014 Unused( inRecordRef
);
4018 gettimeofday( &now
, NULL
);
4019 FPrintF( stdout
, "%{du:time} Record registration result (error %#m)\n", &now
, inError
);
4021 if( !context
->didRegister
&& !inError
)
4023 context
->didRegister
= true;
4024 if( context
->updateDataPtr
)
4026 if( context
->updateDelayMs
> 0 )
4028 dispatch_after_f( dispatch_time_milliseconds( context
->updateDelayMs
), dispatch_get_main_queue(),
4029 context
, RegisterRecordUpdate
);
4033 RegisterRecordUpdate( context
);
4036 if( context
->lifetimeMs
== 0 )
4038 Exit( kExitReason_TimeLimit
);
4040 else if( context
->lifetimeMs
> 0 )
4042 dispatch_after_f( dispatch_time_milliseconds( context
->lifetimeMs
), dispatch_get_main_queue(),
4043 kExitReason_TimeLimit
, Exit
);
4048 //===========================================================================================================================
4049 // RegisterRecordUpdate
4050 //===========================================================================================================================
4052 static void RegisterRecordUpdate( void *inContext
)
4055 RegisterRecordContext
* const context
= (RegisterRecordContext
*) inContext
;
4057 err
= DNSServiceUpdateRecord( context
->conRef
, context
->recordRef
, 0, (uint16_t) context
->updateDataLen
,
4058 context
->updateDataPtr
, context
->updateTTL
);
4059 require_noerr( err
, exit
);
4062 if( err
) exit( 1 );
4065 //===========================================================================================================================
4067 //===========================================================================================================================
4071 DNSServiceRef mainRef
; // Main sdRef for shared connections.
4072 DNSServiceRef opRef
; // sdRef for the DNSServiceResolve operation.
4073 DNSServiceFlags flags
; // Flags argument for DNSServiceResolve().
4074 const char * name
; // Service name argument for DNSServiceResolve().
4075 const char * type
; // Service type argument for DNSServiceResolve().
4076 const char * domain
; // Domain argument for DNSServiceResolve().
4077 uint32_t ifIndex
; // Interface index argument for DNSServiceResolve().
4078 int timeLimitSecs
; // Time limit for the DNSServiceResolve operation in seconds.
4082 static void ResolvePrintPrologue( const ResolveContext
*inContext
);
4083 static void ResolveContextFree( ResolveContext
*inContext
);
4084 static void DNSSD_API
4086 DNSServiceRef inSDRef
,
4087 DNSServiceFlags inFlags
,
4088 uint32_t inInterfaceIndex
,
4089 DNSServiceErrorType inError
,
4090 const char * inFullName
,
4091 const char * inHostname
,
4094 const unsigned char * inTXTPtr
,
4097 static void ResolveCmd( void )
4100 DNSServiceRef sdRef
;
4101 ResolveContext
* context
= NULL
;
4102 dispatch_source_t signalSource
= NULL
;
4103 int useMainConnection
;
4105 // Set up SIGINT handler.
4107 signal( SIGINT
, SIG_IGN
);
4108 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
4109 require_noerr( err
, exit
);
4110 dispatch_resume( signalSource
);
4114 context
= (ResolveContext
*) calloc( 1, sizeof( *context
) );
4115 require_action( context
, exit
, err
= kNoMemoryErr
);
4117 // Check command parameters.
4119 if( gResolve_TimeLimitSecs
< 0 )
4121 FPrintF( stderr
, "Invalid time limit: %d seconds.\n", gResolve_TimeLimitSecs
);
4126 // Create main connection.
4128 if( gConnectionOpt
)
4130 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
4131 require_noerr_quiet( err
, exit
);
4132 useMainConnection
= true;
4136 useMainConnection
= false;
4141 context
->flags
= GetDNSSDFlagsFromOpts();
4142 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
4144 // Get interface index.
4146 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
4147 require_noerr_quiet( err
, exit
);
4149 // Set remaining parameters.
4151 context
->name
= gResolve_Name
;
4152 context
->type
= gResolve_Type
;
4153 context
->domain
= gResolve_Domain
;
4154 context
->timeLimitSecs
= gResolve_TimeLimitSecs
;
4158 ResolvePrintPrologue( context
);
4162 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
4163 err
= DNSServiceResolve( &sdRef
, context
->flags
, context
->ifIndex
, context
->name
, context
->type
, context
->domain
,
4164 ResolveCallback
, NULL
);
4165 require_noerr( err
, exit
);
4167 context
->opRef
= sdRef
;
4168 if( !useMainConnection
)
4170 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
4171 require_noerr( err
, exit
);
4176 if( context
->timeLimitSecs
> 0 )
4178 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
4179 kExitReason_TimeLimit
, Exit
);
4184 dispatch_source_forget( &signalSource
);
4185 if( context
) ResolveContextFree( context
);
4186 if( err
) exit( 1 );
4189 //===========================================================================================================================
4191 //===========================================================================================================================
4193 static void ReconfirmCmd( void )
4196 uint8_t * rdataPtr
= NULL
;
4197 size_t rdataLen
= 0;
4198 DNSServiceFlags flags
;
4200 uint16_t type
, class;
4201 char ifName
[ kInterfaceNameBufLen
];
4205 flags
= GetDNSSDFlagsFromOpts();
4207 // Get interface index.
4209 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
4210 require_noerr_quiet( err
, exit
);
4214 err
= RecordTypeFromArgString( gReconfirmRecord_Type
, &type
);
4215 require_noerr( err
, exit
);
4219 if( gReconfirmRecord_Data
)
4221 err
= RecordDataFromArgString( gReconfirmRecord_Data
, &rdataPtr
, &rdataLen
);
4222 require_noerr_quiet( err
, exit
);
4225 // Get record class.
4227 if( gReconfirmRecord_Class
)
4229 err
= RecordClassFromArgString( gReconfirmRecord_Class
, &class );
4230 require_noerr( err
, exit
);
4234 class = kDNSServiceClass_IN
;
4239 FPrintF( stdout
, "Flags: %#{flags}\n", flags
, kDNSServiceFlagsDescriptors
);
4240 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) ifIndex
, InterfaceIndexToName( ifIndex
, ifName
) );
4241 FPrintF( stdout
, "Name: %s\n", gReconfirmRecord_Name
);
4242 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( type
), type
);
4243 FPrintF( stdout
, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN
) ? "IN" : "???", class );
4244 FPrintF( stdout
, "Data: %#H\n", rdataPtr
, (int) rdataLen
, INT_MAX
);
4245 FPrintF( stdout
, "---\n" );
4247 err
= DNSServiceReconfirmRecord( flags
, ifIndex
, gReconfirmRecord_Name
, type
, class, (uint16_t) rdataLen
, rdataPtr
);
4248 FPrintF( stdout
, "Error: %#m\n", err
);
4251 FreeNullSafe( rdataPtr
);
4252 if( err
) exit( 1 );
4255 //===========================================================================================================================
4256 // ResolvePrintPrologue
4257 //===========================================================================================================================
4259 static void ResolvePrintPrologue( const ResolveContext
*inContext
)
4261 const int timeLimitSecs
= inContext
->timeLimitSecs
;
4262 char ifName
[ kInterfaceNameBufLen
];
4264 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
4266 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
4267 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
4268 FPrintF( stdout
, "Name: %s\n", inContext
->name
);
4269 FPrintF( stdout
, "Type: %s\n", inContext
->type
);
4270 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
);
4271 FPrintF( stdout
, "Time limit: " );
4272 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
4273 else FPrintF( stdout
, "∞\n" );
4274 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
4275 FPrintF( stdout
, "---\n" );
4278 //===========================================================================================================================
4279 // ResolveContextFree
4280 //===========================================================================================================================
4282 static void ResolveContextFree( ResolveContext
*inContext
)
4284 DNSServiceForget( &inContext
->opRef
);
4285 DNSServiceForget( &inContext
->mainRef
);
4289 //===========================================================================================================================
4291 //===========================================================================================================================
4293 static void DNSSD_API
4295 DNSServiceRef inSDRef
,
4296 DNSServiceFlags inFlags
,
4297 uint32_t inInterfaceIndex
,
4298 DNSServiceErrorType inError
,
4299 const char * inFullName
,
4300 const char * inHostname
,
4303 const unsigned char * inTXTPtr
,
4307 char errorStr
[ 64 ];
4311 Unused( inContext
);
4313 gettimeofday( &now
, NULL
);
4315 if( inError
) SNPrintF( errorStr
, sizeof( errorStr
), " error %#m", inError
);
4317 FPrintF( stdout
, "%{du:time}: %s can be reached at %s:%u (interface %d)%?s\n",
4318 &now
, inFullName
, inHostname
, ntohs( inPort
), (int32_t) inInterfaceIndex
, inError
, errorStr
);
4321 FPrintF( stdout
, " TXT record: %#H\n", inTXTPtr
, (int) inTXTLen
, INT_MAX
);
4325 FPrintF( stdout
, " TXT record: %#{txt}\n", inTXTPtr
, (size_t) inTXTLen
);
4329 //===========================================================================================================================
4330 // GetAddrInfoPOSIXCmd
4331 //===========================================================================================================================
4333 #define AddressFamilyStr( X ) ( \
4334 ( (X) == AF_INET ) ? "inet" : \
4335 ( (X) == AF_INET6 ) ? "inet6" : \
4336 ( (X) == AF_UNSPEC ) ? "unspec" : \
4346 #define CaseFlagStringify( X ) { (X), # X }
4348 const FlagStringPair kGAIPOSIXFlagStringPairs
[] =
4350 #if( defined( AI_UNUSABLE ) )
4351 CaseFlagStringify( AI_UNUSABLE
),
4353 CaseFlagStringify( AI_NUMERICSERV
),
4354 CaseFlagStringify( AI_V4MAPPED
),
4355 CaseFlagStringify( AI_ADDRCONFIG
),
4356 #if( defined( AI_V4MAPPED_CFG ) )
4357 CaseFlagStringify( AI_V4MAPPED_CFG
),
4359 CaseFlagStringify( AI_ALL
),
4360 CaseFlagStringify( AI_NUMERICHOST
),
4361 CaseFlagStringify( AI_CANONNAME
),
4362 CaseFlagStringify( AI_PASSIVE
),
4366 static void GetAddrInfoPOSIXCmd( void )
4369 struct addrinfo hints
;
4371 const struct addrinfo
* addrInfo
;
4372 struct addrinfo
* addrInfoList
= NULL
;
4373 const FlagStringPair
* pair
;
4375 memset( &hints
, 0, sizeof( hints
) );
4376 hints
.ai_socktype
= SOCK_STREAM
;
4378 // Set hints address family.
4380 if( !gGAIPOSIX_Family
) hints
.ai_family
= AF_UNSPEC
;
4381 else if( strcasecmp( gGAIPOSIX_Family
, "inet" ) == 0 ) hints
.ai_family
= AF_INET
;
4382 else if( strcasecmp( gGAIPOSIX_Family
, "inet6" ) == 0 ) hints
.ai_family
= AF_INET6
;
4383 else if( strcasecmp( gGAIPOSIX_Family
, "unspec" ) == 0 ) hints
.ai_family
= AF_UNSPEC
;
4386 FPrintF( stderr
, "Invalid address family: %s.\n", gGAIPOSIX_Family
);
4393 if( gGAIPOSIXFlag_AddrConfig
) hints
.ai_flags
|= AI_ADDRCONFIG
;
4394 if( gGAIPOSIXFlag_All
) hints
.ai_flags
|= AI_ALL
;
4395 if( gGAIPOSIXFlag_CanonName
) hints
.ai_flags
|= AI_CANONNAME
;
4396 if( gGAIPOSIXFlag_NumericHost
) hints
.ai_flags
|= AI_NUMERICHOST
;
4397 if( gGAIPOSIXFlag_NumericServ
) hints
.ai_flags
|= AI_NUMERICSERV
;
4398 if( gGAIPOSIXFlag_Passive
) hints
.ai_flags
|= AI_PASSIVE
;
4399 if( gGAIPOSIXFlag_V4Mapped
) hints
.ai_flags
|= AI_V4MAPPED
;
4400 #if( defined( AI_V4MAPPED_CFG ) )
4401 if( gGAIPOSIXFlag_V4MappedCFG
) hints
.ai_flags
|= AI_V4MAPPED_CFG
;
4403 #if( defined( AI_DEFAULT ) )
4404 if( gGAIPOSIXFlag_Default
) hints
.ai_flags
|= AI_DEFAULT
;
4406 #if( defined( AI_UNUSABLE ) )
4407 if( gGAIPOSIXFlag_Unusable
) hints
.ai_flags
|= AI_UNUSABLE
;
4412 FPrintF( stdout
, "Hostname: %s\n", gGAIPOSIX_HostName
);
4413 FPrintF( stdout
, "Servname: %s\n", gGAIPOSIX_ServName
);
4414 FPrintF( stdout
, "Address family: %s\n", AddressFamilyStr( hints
.ai_family
) );
4415 FPrintF( stdout
, "Flags: 0x%X < ", hints
.ai_flags
);
4416 for( pair
= kGAIPOSIXFlagStringPairs
; pair
->str
!= NULL
; ++pair
)
4418 if( ( (unsigned int) hints
.ai_flags
) & pair
->flag
) FPrintF( stdout
, "%s ", pair
->str
);
4420 FPrintF( stdout
, ">\n" );
4421 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
4422 FPrintF( stdout
, "---\n" );
4424 // Call getaddrinfo().
4426 err
= getaddrinfo( gGAIPOSIX_HostName
, gGAIPOSIX_ServName
, &hints
, &addrInfoList
);
4427 gettimeofday( &now
, NULL
);
4430 FPrintF( stderr
, "Error %d: %s.\n", err
, gai_strerror( err
) );
4436 for( addrInfo
= addrInfoList
; addrInfo
; addrInfo
= addrInfo
->ai_next
) { ++addrCount
; }
4438 FPrintF( stdout
, "Addresses (%d total):\n", addrCount
);
4439 for( addrInfo
= addrInfoList
; addrInfo
; addrInfo
= addrInfo
->ai_next
)
4441 FPrintF( stdout
, "%##a\n", addrInfo
->ai_addr
);
4444 FPrintF( stdout
, "---\n" );
4445 FPrintF( stdout
, "End time: %{du:time}\n", &now
);
4448 if( addrInfoList
) freeaddrinfo( addrInfoList
);
4449 if( err
) exit( 1 );
4452 //===========================================================================================================================
4454 //===========================================================================================================================
4456 #define kIP6ARPADomainStr "ip6.arpa."
4458 static void ReverseLookupCmd( void )
4461 QueryRecordContext
* context
= NULL
;
4462 DNSServiceRef sdRef
;
4463 dispatch_source_t signalSource
= NULL
;
4465 uint8_t ipv6Addr
[ 16 ];
4466 char recordName
[ ( 16 * 4 ) + sizeof( kIP6ARPADomainStr
) ];
4467 int useMainConnection
;
4468 const char * endPtr
;
4470 // Set up SIGINT handler.
4472 signal( SIGINT
, SIG_IGN
);
4473 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
4474 require_noerr( err
, exit
);
4475 dispatch_resume( signalSource
);
4479 context
= (QueryRecordContext
*) calloc( 1, sizeof( *context
) );
4480 require_action( context
, exit
, err
= kNoMemoryErr
);
4482 // Check command parameters.
4484 if( gReverseLookup_TimeLimitSecs
< 0 )
4486 FPrintF( stderr
, "Invalid time limit: %d s.\n", gReverseLookup_TimeLimitSecs
);
4491 // Create main connection.
4493 if( gConnectionOpt
)
4495 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
4496 require_noerr_quiet( err
, exit
);
4497 useMainConnection
= true;
4501 useMainConnection
= false;
4506 context
->flags
= GetDNSSDFlagsFromOpts();
4507 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
4509 // Get interface index.
4511 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
4512 require_noerr_quiet( err
, exit
);
4514 // Create reverse lookup record name.
4516 err
= _StringToIPv4Address( gReverseLookup_IPAddr
, kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
,
4517 &ipv4Addr
, NULL
, NULL
, NULL
, &endPtr
);
4518 if( err
|| ( *endPtr
!= '\0' ) )
4523 err
= _StringToIPv6Address( gReverseLookup_IPAddr
,
4524 kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
| kStringToIPAddressFlagsNoScope
,
4525 ipv6Addr
, NULL
, NULL
, NULL
, &endPtr
);
4526 if( err
|| ( *endPtr
!= '\0' ) )
4528 FPrintF( stderr
, "Invalid IP address: \"%s\".\n", gReverseLookup_IPAddr
);
4533 for( i
= 15; i
>= 0; --i
)
4535 *dst
++ = kHexDigitsLowercase
[ ipv6Addr
[ i
] & 0x0F ];
4537 *dst
++ = kHexDigitsLowercase
[ ipv6Addr
[ i
] >> 4 ];
4540 strcpy_literal( dst
, kIP6ARPADomainStr
);
4541 check( ( strlen( recordName
) + 1 ) <= sizeof( recordName
) );
4545 SNPrintF( recordName
, sizeof( recordName
), "%u.%u.%u.%u.in-addr.arpa.",
4547 ( ipv4Addr
>> 8 ) & 0xFF,
4548 ( ipv4Addr
>> 16 ) & 0xFF,
4549 ( ipv4Addr
>> 24 ) & 0xFF );
4552 // Set remaining parameters.
4554 context
->recordName
= recordName
;
4555 context
->recordType
= kDNSServiceType_PTR
;
4556 context
->timeLimitSecs
= gReverseLookup_TimeLimitSecs
;
4557 context
->oneShotMode
= gReverseLookup_OneShot
? true : false;
4561 QueryRecordPrintPrologue( context
);
4565 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
4566 err
= DNSServiceQueryRecord( &sdRef
, context
->flags
, context
->ifIndex
, context
->recordName
, context
->recordType
,
4567 kDNSServiceClass_IN
, QueryRecordCallback
, context
);
4568 require_noerr( err
, exit
);
4570 context
->opRef
= sdRef
;
4571 if( !useMainConnection
)
4573 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
4574 require_noerr( err
, exit
);
4579 if( context
->timeLimitSecs
> 0 )
4581 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
4582 kExitReason_TimeLimit
, Exit
);
4587 dispatch_source_forget( &signalSource
);
4588 if( context
) QueryRecordContextFree( context
);
4589 if( err
) exit( 1 );
4592 //===========================================================================================================================
4594 //===========================================================================================================================
4598 DNSServiceRef mainRef
; // Main sdRef for shared connection.
4599 DNSServiceRef opRef
; // sdRef for the DNSServiceNATPortMappingCreate operation.
4600 DNSServiceFlags flags
; // Flags for DNSServiceNATPortMappingCreate operation.
4601 uint32_t ifIndex
; // Interface index argument for DNSServiceNATPortMappingCreate operation.
4602 DNSServiceProtocol protocols
; // Protocols argument for DNSServiceNATPortMappingCreate operation.
4603 uint32_t ttl
; // TTL argument for DNSServiceNATPortMappingCreate operation.
4604 uint16_t internalPort
; // Internal port argument for DNSServiceNATPortMappingCreate operation.
4605 uint16_t externalPort
; // External port argument for DNSServiceNATPortMappingCreate operation.
4606 Boolean printedHeader
; // True if results header was printed.
4608 } PortMappingContext
;
4610 static void PortMappingPrintPrologue( const PortMappingContext
*inContext
);
4611 static void PortMappingContextFree( PortMappingContext
*inContext
);
4612 static void DNSSD_API
4613 PortMappingCallback(
4614 DNSServiceRef inSDRef
,
4615 DNSServiceFlags inFlags
,
4616 uint32_t inInterfaceIndex
,
4617 DNSServiceErrorType inError
,
4618 uint32_t inExternalIPv4Address
,
4619 DNSServiceProtocol inProtocol
,
4620 uint16_t inInternalPort
,
4621 uint16_t inExternalPort
,
4625 static void PortMappingCmd( void )
4628 PortMappingContext
* context
= NULL
;
4629 DNSServiceRef sdRef
;
4630 dispatch_source_t signalSource
= NULL
;
4631 int useMainConnection
;
4633 // Set up SIGINT handler.
4635 signal( SIGINT
, SIG_IGN
);
4636 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
4637 require_noerr( err
, exit
);
4638 dispatch_resume( signalSource
);
4642 context
= (PortMappingContext
*) calloc( 1, sizeof( *context
) );
4643 require_action( context
, exit
, err
= kNoMemoryErr
);
4645 // Check command parameters.
4647 if( ( gPortMapping_InternalPort
< 0 ) || ( gPortMapping_InternalPort
> UINT16_MAX
) )
4649 FPrintF( stderr
, "Internal port number %d is out-of-range.\n", gPortMapping_InternalPort
);
4654 if( ( gPortMapping_ExternalPort
< 0 ) || ( gPortMapping_ExternalPort
> UINT16_MAX
) )
4656 FPrintF( stderr
, "External port number %d is out-of-range.\n", gPortMapping_ExternalPort
);
4661 // Create main connection.
4663 if( gConnectionOpt
)
4665 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
4666 require_noerr_quiet( err
, exit
);
4667 useMainConnection
= true;
4671 useMainConnection
= false;
4676 context
->flags
= GetDNSSDFlagsFromOpts();
4677 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
4679 // Get interface index.
4681 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
4682 require_noerr_quiet( err
, exit
);
4684 // Set remaining parameters.
4686 if( gPortMapping_ProtocolTCP
) context
->protocols
|= kDNSServiceProtocol_TCP
;
4687 if( gPortMapping_ProtocolUDP
) context
->protocols
|= kDNSServiceProtocol_UDP
;
4688 context
->ttl
= (uint32_t) gPortMapping_TTL
;
4689 context
->internalPort
= (uint16_t) gPortMapping_InternalPort
;
4690 context
->externalPort
= (uint16_t) gPortMapping_ExternalPort
;
4694 PortMappingPrintPrologue( context
);
4698 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
4699 err
= DNSServiceNATPortMappingCreate( &sdRef
, context
->flags
, context
->ifIndex
, context
->protocols
,
4700 htons( context
->internalPort
), htons( context
->externalPort
), context
->ttl
, PortMappingCallback
, context
);
4701 require_noerr( err
, exit
);
4703 context
->opRef
= sdRef
;
4704 if( !useMainConnection
)
4706 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
4707 require_noerr( err
, exit
);
4713 dispatch_source_forget( &signalSource
);
4714 if( context
) PortMappingContextFree( context
);
4715 if( err
) exit( 1 );
4718 //===========================================================================================================================
4719 // PortMappingPrintPrologue
4720 //===========================================================================================================================
4722 static void PortMappingPrintPrologue( const PortMappingContext
*inContext
)
4724 char ifName
[ kInterfaceNameBufLen
];
4726 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
4728 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
4729 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
4730 FPrintF( stdout
, "Protocols: %#{flags}\n", inContext
->protocols
, kDNSServiceProtocolDescriptors
);
4731 FPrintF( stdout
, "Internal Port: %u\n", inContext
->internalPort
);
4732 FPrintF( stdout
, "External Port: %u\n", inContext
->externalPort
);
4733 FPrintF( stdout
, "TTL: %u%?s\n", inContext
->ttl
, !inContext
->ttl
,
4734 " (system will use a default value.)" );
4735 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
4736 FPrintF( stdout
, "---\n" );
4740 //===========================================================================================================================
4741 // PortMappingContextFree
4742 //===========================================================================================================================
4744 static void PortMappingContextFree( PortMappingContext
*inContext
)
4746 DNSServiceForget( &inContext
->opRef
);
4747 DNSServiceForget( &inContext
->mainRef
);
4751 //===========================================================================================================================
4752 // PortMappingCallback
4753 //===========================================================================================================================
4755 static void DNSSD_API
4756 PortMappingCallback(
4757 DNSServiceRef inSDRef
,
4758 DNSServiceFlags inFlags
,
4759 uint32_t inInterfaceIndex
,
4760 DNSServiceErrorType inError
,
4761 uint32_t inExternalIPv4Address
,
4762 DNSServiceProtocol inProtocol
,
4763 uint16_t inInternalPort
,
4764 uint16_t inExternalPort
,
4768 PortMappingContext
* const context
= (PortMappingContext
*) inContext
;
4770 char errorStr
[ 128 ];
4775 gettimeofday( &now
, NULL
);
4777 if( inError
) SNPrintF( errorStr
, sizeof( errorStr
), " (error: %#m)", inError
);
4778 if( !context
->printedHeader
)
4780 FPrintF( stdout
, "%-26s IF %7s %15s %7s %6s Protocol\n", "Timestamp", "IntPort", "ExtAddr", "ExtPort", "TTL" );
4781 context
->printedHeader
= true;
4783 FPrintF( stdout
, "%{du:time} %2u %7u %15.4a %7u %6u %#{flags}%?s\n",
4784 &now
, inInterfaceIndex
, ntohs( inInternalPort
), &inExternalIPv4Address
, ntohs( inExternalPort
), inTTL
,
4785 inProtocol
, kDNSServiceProtocolDescriptors
, inError
, errorStr
);
4788 //===========================================================================================================================
4790 //===========================================================================================================================
4792 typedef struct BrowseAllConnection BrowseAllConnection
;
4796 ServiceBrowserRef browser
; // Service browser.
4797 ServiceBrowserResults
* results
; // Results from the service browser.
4798 BrowseAllConnection
* connectionList
; // List of connections.
4799 dispatch_source_t connectionTimer
; // Timer for connection timeout.
4800 int connectionPendingCount
; // Number of pending connections.
4801 int connectionTimeoutSecs
; // Timeout value for connections in seconds.
4805 struct BrowseAllConnection
4807 BrowseAllConnection
* next
; // Next connection object in list.
4808 sockaddr_ip sip
; // IPv4 or IPv6 address to connect to.
4809 uint16_t port
; // TCP port to connect to.
4810 AsyncConnectionRef asyncCnx
; // AsyncConnection object to handle the actual connection.
4811 OSStatus status
; // Status of connection. NoErr means connection succeeded.
4812 CFTimeInterval connectTimeSecs
; // Time it took to connect in seconds.
4813 int32_t refCount
; // This object's reference count.
4814 BrowseAllContext
* context
; // Back pointer to parent context.
4817 static void _BrowseAllContextFree( BrowseAllContext
*inContext
);
4818 static void _BrowseAllServiceBrowserCallback( ServiceBrowserResults
*inResults
, OSStatus inError
, void *inContext
);
4820 _BrowseAllConnectionCreate(
4821 const struct sockaddr
* inSockAddr
,
4823 BrowseAllContext
* inContext
,
4824 BrowseAllConnection
** outConnection
);
4825 static void _BrowseAllConnectionRetain( BrowseAllConnection
*inConnection
);
4826 static void _BrowseAllConnectionRelease( BrowseAllConnection
*inConnection
);
4827 static void _BrowseAllConnectionProgress( int inPhase
, const void *inDetails
, void *inArg
);
4828 static void _BrowseAllConnectionHandler( SocketRef inSock
, OSStatus inError
, void *inArg
);
4829 static void _BrowseAllExit( void *inContext
);
4831 static Boolean
_IsServiceTypeTCP( const char *inServiceType
);
4833 static void BrowseAllCmd( void )
4836 BrowseAllContext
* context
= NULL
;
4839 char ifName
[ kInterfaceNameBufLen
];
4841 // Check parameters.
4843 if( gBrowseAll_BrowseTimeSecs
<= 0 )
4845 FPrintF( stdout
, "Invalid browse time: %d seconds.\n", gBrowseAll_BrowseTimeSecs
);
4850 context
= (BrowseAllContext
*) calloc( 1, sizeof( *context
) );
4851 require_action( context
, exit
, err
= kNoMemoryErr
);
4853 context
->connectionTimeoutSecs
= gBrowseAll_ConnectTimeout
;
4854 #if( TARGET_OS_POSIX )
4855 // Increase the open file descriptor limit for connection sockets.
4857 if( context
->connectionTimeoutSecs
> 0 )
4859 struct rlimit fdLimits
;
4861 err
= getrlimit( RLIMIT_NOFILE
, &fdLimits
);
4862 err
= map_global_noerr_errno( err
);
4863 require_noerr( err
, exit
);
4865 if( fdLimits
.rlim_cur
< 4096 )
4867 fdLimits
.rlim_cur
= 4096;
4868 err
= setrlimit( RLIMIT_NOFILE
, &fdLimits
);
4869 err
= map_global_noerr_errno( err
);
4870 require_noerr( err
, exit
);
4875 // Get interface index.
4877 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
4878 require_noerr_quiet( err
, exit
);
4882 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) ifIndex
, InterfaceIndexToName( ifIndex
, ifName
) );
4883 FPrintF( stdout
, "Service types: ");
4884 if( gBrowseAll_ServiceTypesCount
> 0 )
4886 FPrintF( stdout
, "%s", gBrowseAll_ServiceTypes
[ 0 ] );
4887 for( i
= 1; i
< gBrowseAll_ServiceTypesCount
; ++i
)
4889 FPrintF( stdout
, ", %s", gBrowseAll_ServiceTypes
[ i
] );
4891 FPrintF( stdout
, "\n" );
4895 FPrintF( stdout
, "all services\n" );
4897 FPrintF( stdout
, "Domain: %s\n", gBrowseAll_Domain
? gBrowseAll_Domain
: "default domains" );
4898 FPrintF( stdout
, "Browse time: %d second%?c\n", gBrowseAll_BrowseTimeSecs
, gBrowseAll_BrowseTimeSecs
!= 1, 's' );
4899 FPrintF( stdout
, "Connect timeout: %d second%?c\n",
4900 context
->connectionTimeoutSecs
, context
->connectionTimeoutSecs
!= 1, 's' );
4901 FPrintF( stdout
, "IncludeAWDL: %s\n", gDNSSDFlag_IncludeAWDL
? "yes" : "no" );
4902 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
4903 FPrintF( stdout
, "---\n" );
4905 err
= ServiceBrowserCreate( dispatch_get_main_queue(), ifIndex
, gBrowseAll_Domain
,
4906 (unsigned int) gBrowseAll_BrowseTimeSecs
, gDNSSDFlag_IncludeAWDL
? true : false, &context
->browser
);
4907 require_noerr( err
, exit
);
4909 for( i
= 0; i
< gBrowseAll_ServiceTypesCount
; ++i
)
4911 err
= ServiceBrowserAddServiceType( context
->browser
, gBrowseAll_ServiceTypes
[ i
] );
4912 require_noerr( err
, exit
);
4914 ServiceBrowserSetCallback( context
->browser
, _BrowseAllServiceBrowserCallback
, context
);
4915 ServiceBrowserStart( context
->browser
);
4919 if( context
) _BrowseAllContextFree( context
);
4920 if( err
) exit( 1 );
4923 //===========================================================================================================================
4924 // _BrowseAllContextFree
4925 //===========================================================================================================================
4927 static void _BrowseAllContextFree( BrowseAllContext
*inContext
)
4929 check( !inContext
->browser
);
4930 check( !inContext
->connectionTimer
);
4931 check( !inContext
->connectionList
);
4932 ForgetServiceBrowserResults( &inContext
->results
);
4936 //===========================================================================================================================
4937 // _BrowseAllServiceBrowserCallback
4938 //===========================================================================================================================
4940 #define kDiscardProtocolPort 9
4942 static void _BrowseAllServiceBrowserCallback( ServiceBrowserResults
*inResults
, OSStatus inError
, void *inContext
)
4945 BrowseAllContext
* const context
= (BrowseAllContext
*) inContext
;
4947 SBRServiceType
* type
;
4948 SBRServiceInstance
* instance
;
4949 SBRIPAddress
* ipaddr
;
4953 require_action( inResults
, exit
, err
= kUnexpectedErr
);
4955 check( !context
->results
);
4956 context
->results
= inResults
;
4957 ServiceBrowserResultsRetain( context
->results
);
4959 check( context
->connectionPendingCount
== 0 );
4960 if( context
->connectionTimeoutSecs
> 0 )
4962 BrowseAllConnection
* connection
;
4963 BrowseAllConnection
** connectionPtr
= &context
->connectionList
;
4964 char destination
[ kSockAddrStringMaxSize
];
4966 for( domain
= context
->results
->domainList
; domain
; domain
= domain
->next
)
4968 for( type
= domain
->typeList
; type
; type
= type
->next
)
4970 if( !_IsServiceTypeTCP( type
->name
) ) continue;
4971 for( instance
= type
->instanceList
; instance
; instance
= instance
->next
)
4973 if( instance
->port
== kDiscardProtocolPort
) continue;
4974 for( ipaddr
= instance
->ipaddrList
; ipaddr
; ipaddr
= ipaddr
->next
)
4976 err
= _BrowseAllConnectionCreate( &ipaddr
->sip
.sa
, instance
->port
, context
, &connection
);
4977 require_noerr( err
, exit
);
4979 *connectionPtr
= connection
;
4980 connectionPtr
= &connection
->next
;
4982 err
= SockAddrToString( &ipaddr
->sip
, kSockAddrStringFlagsNoPort
, destination
);
4986 err
= AsyncConnection_Connect( &connection
->asyncCnx
, destination
, -instance
->port
,
4987 kAsyncConnectionFlag_P2P
, kAsyncConnectionNoTimeout
,
4988 kSocketBufferSize_DontSet
, kSocketBufferSize_DontSet
,
4989 _BrowseAllConnectionProgress
, connection
, _BrowseAllConnectionHandler
, connection
,
4990 dispatch_get_main_queue() );
4995 _BrowseAllConnectionRetain( connection
);
4996 connection
->status
= kInProgressErr
;
4997 ++context
->connectionPendingCount
;
5001 connection
->status
= err
;
5009 if( context
->connectionPendingCount
> 0 )
5011 check( !context
->connectionTimer
);
5012 err
= DispatchTimerCreate( dispatch_time_seconds( context
->connectionTimeoutSecs
), DISPATCH_TIME_FOREVER
,
5013 100 * kNanosecondsPerMillisecond
, NULL
, _BrowseAllExit
, NULL
, context
, &context
->connectionTimer
);
5014 require_noerr( err
, exit
);
5015 dispatch_resume( context
->connectionTimer
);
5019 dispatch_async_f( dispatch_get_main_queue(), context
, _BrowseAllExit
);
5024 ForgetCF( &context
->browser
);
5025 if( err
) exit( 1 );
5028 //===========================================================================================================================
5029 // _BrowseAllConnectionCreate
5030 //===========================================================================================================================
5033 _BrowseAllConnectionCreate(
5034 const struct sockaddr
* inSockAddr
,
5036 BrowseAllContext
* inContext
,
5037 BrowseAllConnection
** outConnection
)
5040 BrowseAllConnection
* obj
;
5042 obj
= (BrowseAllConnection
*) calloc( 1, sizeof( *obj
) );
5043 require_action( obj
, exit
, err
= kNoMemoryErr
);
5046 SockAddrCopy( inSockAddr
, &obj
->sip
);
5048 obj
->context
= inContext
;
5050 *outConnection
= obj
;
5057 //===========================================================================================================================
5058 // _BrowseAllConnectionRetain
5059 //===========================================================================================================================
5061 static void _BrowseAllConnectionRetain( BrowseAllConnection
*inConnection
)
5063 ++inConnection
->refCount
;
5066 //===========================================================================================================================
5067 // _BrowseAllConnectionRelease
5068 //===========================================================================================================================
5070 static void _BrowseAllConnectionRelease( BrowseAllConnection
*inConnection
)
5072 if( --inConnection
->refCount
== 0 ) free( inConnection
);
5075 //===========================================================================================================================
5076 // _BrowseAllConnectionProgress
5077 //===========================================================================================================================
5079 static void _BrowseAllConnectionProgress( int inPhase
, const void *inDetails
, void *inArg
)
5081 BrowseAllConnection
* const connection
= (BrowseAllConnection
*) inArg
;
5083 if( inPhase
== kAsyncConnectionPhase_Connected
)
5085 const AsyncConnectedInfo
* const info
= (AsyncConnectedInfo
*) inDetails
;
5087 connection
->connectTimeSecs
= info
->connectSecs
;
5091 //===========================================================================================================================
5092 // _BrowseAllConnectionHandler
5093 //===========================================================================================================================
5095 static void _BrowseAllConnectionHandler( SocketRef inSock
, OSStatus inError
, void *inArg
)
5097 BrowseAllConnection
* const connection
= (BrowseAllConnection
*) inArg
;
5098 BrowseAllContext
* const context
= connection
->context
;
5100 connection
->status
= inError
;
5101 ForgetSocket( &inSock
);
5104 check( context
->connectionPendingCount
> 0 );
5105 if( ( --context
->connectionPendingCount
== 0 ) && context
->connectionTimer
)
5107 dispatch_source_forget( &context
->connectionTimer
);
5108 dispatch_async_f( dispatch_get_main_queue(), context
, _BrowseAllExit
);
5111 _BrowseAllConnectionRelease( connection
);
5114 //===========================================================================================================================
5116 //===========================================================================================================================
5118 #define Indent( X ) ( (X) * 4 ), ""
5120 static void _BrowseAllExit( void *inContext
)
5122 BrowseAllContext
* const context
= (BrowseAllContext
*) inContext
;
5124 SBRServiceType
* type
;
5125 SBRServiceInstance
* instance
;
5126 SBRIPAddress
* ipaddr
;
5127 char textBuf
[ 512 ];
5128 #if( TARGET_OS_POSIX )
5129 const Boolean useColor
= isatty( STDOUT_FILENO
) ? true : false;
5132 dispatch_source_forget( &context
->connectionTimer
);
5134 for( domain
= context
->results
->domainList
; domain
; domain
= domain
->next
)
5136 FPrintF( stdout
, "%s\n\n", domain
->name
);
5138 for( type
= domain
->typeList
; type
; type
= type
->next
)
5140 const char * description
;
5141 const Boolean serviceTypeIsTCP
= _IsServiceTypeTCP( type
->name
);
5143 description
= ServiceTypeDescription( type
->name
);
5144 if( description
) FPrintF( stdout
, "%*s" "%s (%s)\n\n", Indent( 1 ), description
, type
->name
);
5145 else FPrintF( stdout
, "%*s" "%s\n\n", Indent( 1 ), type
->name
);
5147 for( instance
= type
->instanceList
; instance
; instance
= instance
->next
)
5149 char * dst
= textBuf
;
5150 char * const lim
= &textBuf
[ countof( textBuf
) ];
5151 char ifname
[ IF_NAMESIZE
+ 1 ];
5153 SNPrintF_Add( &dst
, lim
, "%s via ", instance
->name
);
5154 if( instance
->ifIndex
== 0 )
5156 SNPrintF_Add( &dst
, lim
, "the Internet" );
5158 else if( if_indextoname( instance
->ifIndex
, ifname
) )
5160 NetTransportType netType
;
5162 SocketGetInterfaceInfo( kInvalidSocketRef
, ifname
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &netType
);
5163 SNPrintF_Add( &dst
, lim
, "%s (%s)",
5164 ( netType
== kNetTransportType_Ethernet
) ? "Ethernet" : NetTransportTypeToString( netType
),
5169 SNPrintF_Add( &dst
, lim
, "interface index %u", instance
->ifIndex
);
5171 FPrintF( stdout
, "%*s" "%-55s %4llu.%03llu ms\n\n",
5172 Indent( 2 ), textBuf
, instance
->discoverTimeUs
/ 1000, instance
->discoverTimeUs
% 1000 );
5174 if( instance
->hostname
)
5176 SNPrintF( textBuf
, sizeof( textBuf
), "%s:%u", instance
->hostname
, instance
->port
);
5177 FPrintF( stdout
, "%*s" "%-51s %4llu.%03llu ms\n",
5178 Indent( 3 ), textBuf
, instance
->resolveTimeUs
/ 1000, instance
->resolveTimeUs
% 1000 );
5182 FPrintF( stdout
, "%*s" "%s:%u\n", Indent( 3 ), instance
->hostname
, instance
->port
);
5185 for( ipaddr
= instance
->ipaddrList
; ipaddr
; ipaddr
= ipaddr
->next
)
5187 BrowseAllConnection
* conn
;
5188 BrowseAllConnection
** connPtr
;
5190 FPrintF( stdout
, "%*s" "%-##47a %4llu.%03llu ms",
5191 Indent( 4 ), &ipaddr
->sip
.sa
, ipaddr
->resolveTimeUs
/ 1000, ipaddr
->resolveTimeUs
% 1000 );
5194 if( serviceTypeIsTCP
&& ( instance
->port
!= kDiscardProtocolPort
) )
5196 for( connPtr
= &context
->connectionList
; ( conn
= *connPtr
) != NULL
; connPtr
= &conn
->next
)
5198 if( ( conn
->port
== instance
->port
) &&
5199 ( SockAddrCompareAddr( &conn
->sip
, &ipaddr
->sip
) == 0 ) ) break;
5203 if( conn
->status
== kInProgressErr
) conn
->status
= kTimeoutErr
;
5204 *connPtr
= conn
->next
;
5205 conn
->context
= NULL
;
5206 AsyncConnection_Forget( &conn
->asyncCnx
);
5212 if( conn
->status
== kNoErr
)
5214 FPrintF( stdout
, " (%sconnected%s in %.3f ms)\n",
5215 useColor
? kANSIGreen
: "", useColor
? kANSINormal
: "", conn
->connectTimeSecs
* 1000 );
5219 FPrintF( stdout
, " (%scould not connect%s: %m)\n",
5220 useColor
? kANSIRed
: "", useColor
? kANSINormal
: "", conn
->status
);
5222 _BrowseAllConnectionRelease( conn
);
5226 FPrintF( stdout
, " (no connection attempted)\n" );
5230 FPrintF( stdout
, "\n" );
5231 if( instance
->txtLen
== 0 ) continue;
5233 FPrintF( stdout
, "%*s" "TXT record (%zu byte%?c):\n",
5234 Indent( 3 ), instance
->txtLen
, instance
->txtLen
!= 1, 's' );
5235 if( instance
->txtLen
> 1 )
5237 FPrintF( stdout
, "%3{txt}", instance
->txtPtr
, instance
->txtLen
);
5241 FPrintF( stdout
, "%*s" "%#H\n", Indent( 3 ), instance
->txtPtr
, (int) instance
->txtLen
, INT_MAX
);
5243 FPrintF( stdout
, "\n" );
5245 FPrintF( stdout
, "\n" );
5249 _BrowseAllContextFree( context
);
5253 //===========================================================================================================================
5254 // _IsServiceTypeTCP
5255 //===========================================================================================================================
5257 static Boolean
_IsServiceTypeTCP( const char *inServiceType
)
5260 const uint8_t * secondLabel
;
5261 uint8_t name
[ kDomainNameLengthMax
];
5263 err
= DomainNameFromString( name
, inServiceType
, NULL
);
5266 secondLabel
= DomainNameGetNextLabel( name
);
5267 if( secondLabel
&& DomainNameEqual( secondLabel
, (const uint8_t *) "\x04" "_tcp" ) ) return( true );
5272 //===========================================================================================================================
5274 //===========================================================================================================================
5276 const FlagStringPair kGetNameInfoFlagStringPairs
[] =
5278 CaseFlagStringify( NI_NUMERICSCOPE
),
5279 CaseFlagStringify( NI_DGRAM
),
5280 CaseFlagStringify( NI_NUMERICSERV
),
5281 CaseFlagStringify( NI_NAMEREQD
),
5282 CaseFlagStringify( NI_NUMERICHOST
),
5283 CaseFlagStringify( NI_NOFQDN
),
5287 static void GetNameInfoCmd( void )
5293 const FlagStringPair
* pair
;
5295 char host
[ NI_MAXHOST
];
5296 char serv
[ NI_MAXSERV
];
5298 err
= StringToSockAddr( gGetNameInfo_IPAddress
, &sip
, sizeof( sip
), &sockAddrLen
);
5302 FPrintF( stderr
, "Failed to convert \"%s\" to a sockaddr.\n", gGetNameInfo_IPAddress
);
5307 if( gGetNameInfoFlag_DGram
) flags
|= NI_DGRAM
;
5308 if( gGetNameInfoFlag_NameReqd
) flags
|= NI_NAMEREQD
;
5309 if( gGetNameInfoFlag_NoFQDN
) flags
|= NI_NOFQDN
;
5310 if( gGetNameInfoFlag_NumericHost
) flags
|= NI_NUMERICHOST
;
5311 if( gGetNameInfoFlag_NumericScope
) flags
|= NI_NUMERICSCOPE
;
5312 if( gGetNameInfoFlag_NumericServ
) flags
|= NI_NUMERICSERV
;
5316 FPrintF( stdout
, "SockAddr: %##a\n", &sip
.sa
);
5317 FPrintF( stdout
, "Flags: 0x%X < ", flags
);
5318 for( pair
= kGetNameInfoFlagStringPairs
; pair
->str
!= NULL
; ++pair
)
5320 if( flags
& pair
->flag
) FPrintF( stdout
, "%s ", pair
->str
);
5322 FPrintF( stdout
, ">\n" );
5323 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
5324 FPrintF( stdout
, "---\n" );
5326 // Call getnameinfo().
5328 err
= getnameinfo( &sip
.sa
, (socklen_t
) sockAddrLen
, host
, (socklen_t
) sizeof( host
), serv
, (socklen_t
) sizeof( serv
),
5330 gettimeofday( &now
, NULL
);
5333 FPrintF( stderr
, "Error %d: %s.\n", err
, gai_strerror( err
) );
5337 FPrintF( stdout
, "host: %s\n", host
);
5338 FPrintF( stdout
, "serv: %s\n", serv
);
5340 FPrintF( stdout
, "---\n" );
5341 FPrintF( stdout
, "End time: %{du:time}\n", &now
);
5344 gExitCode
= err
? 1 : 0;
5347 //===========================================================================================================================
5348 // GetAddrInfoStressCmd
5349 //===========================================================================================================================
5353 DNSServiceRef mainRef
;
5354 DNSServiceRef sdRef
;
5355 DNSServiceFlags flags
;
5356 unsigned int interfaceIndex
;
5357 unsigned int connectionNumber
;
5358 unsigned int requestCount
;
5359 unsigned int requestCountMax
;
5360 unsigned int requestCountLimit
;
5361 unsigned int durationMinMs
;
5362 unsigned int durationMaxMs
;
5366 static void GetAddrInfoStressEvent( void *inContext
);
5367 static void DNSSD_API
5368 GetAddrInfoStressCallback(
5369 DNSServiceRef inSDRef
,
5370 DNSServiceFlags inFlags
,
5371 uint32_t inInterfaceIndex
,
5372 DNSServiceErrorType inError
,
5373 const char * inHostname
,
5374 const struct sockaddr
* inSockAddr
,
5378 static void GetAddrInfoStressCmd( void )
5381 GAIStressContext
* context
= NULL
;
5383 DNSServiceFlags flags
;
5385 char ifName
[ kInterfaceNameBufLen
];
5387 if( gGAIStress_TestDurationSecs
< 0 )
5389 FPrintF( stdout
, "Invalid test duration: %d s.\n", gGAIStress_TestDurationSecs
);
5393 if( gGAIStress_ConnectionCount
<= 0 )
5395 FPrintF( stdout
, "Invalid simultaneous connection count: %d.\n", gGAIStress_ConnectionCount
);
5399 if( gGAIStress_DurationMinMs
<= 0 )
5401 FPrintF( stdout
, "Invalid minimum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMinMs
);
5405 if( gGAIStress_DurationMaxMs
<= 0 )
5407 FPrintF( stdout
, "Invalid maximum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMaxMs
);
5411 if( gGAIStress_DurationMinMs
> gGAIStress_DurationMaxMs
)
5413 FPrintF( stdout
, "Invalid minimum and maximum DNSServiceGetAddrInfo() durations: %d ms and %d ms.\n",
5414 gGAIStress_DurationMinMs
, gGAIStress_DurationMaxMs
);
5418 if( gGAIStress_RequestCountMax
<= 0 )
5420 FPrintF( stdout
, "Invalid maximum request count: %d.\n", gGAIStress_RequestCountMax
);
5427 flags
= GetDNSSDFlagsFromOpts();
5429 // Set interface index.
5431 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
5432 require_noerr_quiet( err
, exit
);
5434 for( i
= 0; i
< gGAIStress_ConnectionCount
; ++i
)
5436 context
= (GAIStressContext
*) calloc( 1, sizeof( *context
) );
5437 require_action( context
, exit
, err
= kNoMemoryErr
);
5439 context
->flags
= flags
;
5440 context
->interfaceIndex
= ifIndex
;
5441 context
->connectionNumber
= (unsigned int)( i
+ 1 );
5442 context
->requestCountMax
= (unsigned int) gGAIStress_RequestCountMax
;
5443 context
->durationMinMs
= (unsigned int) gGAIStress_DurationMinMs
;
5444 context
->durationMaxMs
= (unsigned int) gGAIStress_DurationMaxMs
;
5446 dispatch_async_f( dispatch_get_main_queue(), context
, GetAddrInfoStressEvent
);
5450 if( gGAIStress_TestDurationSecs
> 0 )
5452 dispatch_after_f( dispatch_time_seconds( gGAIStress_TestDurationSecs
), dispatch_get_main_queue(), NULL
, Exit
);
5455 FPrintF( stdout
, "Flags: %#{flags}\n", flags
, kDNSServiceFlagsDescriptors
);
5456 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) ifIndex
, InterfaceIndexToName( ifIndex
, ifName
) );
5457 FPrintF( stdout
, "Test duration: " );
5458 if( gGAIStress_TestDurationSecs
== 0 )
5460 FPrintF( stdout
, "∞\n" );
5464 FPrintF( stdout
, "%d s\n", gGAIStress_TestDurationSecs
);
5466 FPrintF( stdout
, "Connection count: %d\n", gGAIStress_ConnectionCount
);
5467 FPrintF( stdout
, "Request duration min: %d ms\n", gGAIStress_DurationMinMs
);
5468 FPrintF( stdout
, "Request duration max: %d ms\n", gGAIStress_DurationMaxMs
);
5469 FPrintF( stdout
, "Request count max: %d\n", gGAIStress_RequestCountMax
);
5470 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
5471 FPrintF( stdout
, "---\n" );
5476 FreeNullSafe( context
);
5477 if( err
) exit( 1 );
5480 //===========================================================================================================================
5481 // GetAddrInfoStressEvent
5482 //===========================================================================================================================
5484 #define kStressRandStrLen 5
5486 #define kLowercaseAlphaCharSet "abcdefghijklmnopqrstuvwxyz"
5488 static void GetAddrInfoStressEvent( void *inContext
)
5490 GAIStressContext
* const context
= (GAIStressContext
*) inContext
;
5492 DNSServiceRef sdRef
;
5493 unsigned int nextMs
;
5494 char randomStr
[ kStressRandStrLen
+ 1 ];
5495 char hostname
[ kStressRandStrLen
+ 4 + 1 ];
5496 Boolean isConnectionNew
= false;
5497 static Boolean printedHeader
= false;
5499 if( !context
->mainRef
|| ( context
->requestCount
>= context
->requestCountLimit
) )
5501 DNSServiceForget( &context
->mainRef
);
5502 context
->sdRef
= NULL
;
5503 context
->requestCount
= 0;
5504 context
->requestCountLimit
= RandomRange( 1, context
->requestCountMax
);
5506 err
= DNSServiceCreateConnection( &context
->mainRef
);
5507 require_noerr( err
, exit
);
5509 err
= DNSServiceSetDispatchQueue( context
->mainRef
, dispatch_get_main_queue() );
5510 require_noerr( err
, exit
);
5512 isConnectionNew
= true;
5515 RandomString( kLowercaseAlphaCharSet
, sizeof_string( kLowercaseAlphaCharSet
), 2, kStressRandStrLen
, randomStr
);
5516 SNPrintF( hostname
, sizeof( hostname
), "%s.com", randomStr
);
5518 nextMs
= RandomRange( context
->durationMinMs
, context
->durationMaxMs
);
5520 if( !printedHeader
)
5522 FPrintF( stdout
, "%-26s Conn Hostname Dur (ms)\n", "Timestamp" );
5523 printedHeader
= true;
5525 FPrintF( stdout
, "%{du:time} %3u%c %9s %8u\n",
5526 NULL
, context
->connectionNumber
, isConnectionNew
? '*': ' ', hostname
, nextMs
);
5528 DNSServiceForget( &context
->sdRef
);
5529 sdRef
= context
->mainRef
;
5530 err
= DNSServiceGetAddrInfo( &sdRef
, context
->flags
| kDNSServiceFlagsShareConnection
, context
->interfaceIndex
,
5531 kDNSServiceProtocol_IPv4
| kDNSServiceProtocol_IPv6
, hostname
, GetAddrInfoStressCallback
, NULL
);
5532 require_noerr( err
, exit
);
5533 context
->sdRef
= sdRef
;
5535 context
->requestCount
++;
5537 dispatch_after_f( dispatch_time_milliseconds( nextMs
), dispatch_get_main_queue(), context
, GetAddrInfoStressEvent
);
5540 if( err
) exit( 1 );
5543 //===========================================================================================================================
5544 // GetAddrInfoStressCallback
5545 //===========================================================================================================================
5547 static void DNSSD_API
5548 GetAddrInfoStressCallback(
5549 DNSServiceRef inSDRef
,
5550 DNSServiceFlags inFlags
,
5551 uint32_t inInterfaceIndex
,
5552 DNSServiceErrorType inError
,
5553 const char * inHostname
,
5554 const struct sockaddr
* inSockAddr
,
5560 Unused( inInterfaceIndex
);
5562 Unused( inHostname
);
5563 Unused( inSockAddr
);
5565 Unused( inContext
);
5568 //===========================================================================================================================
5570 //===========================================================================================================================
5574 sockaddr_ip serverAddr
;
5580 dispatch_source_t readSource
;
5587 Boolean printRawRData
; // True if RDATA results are not to be formatted.
5588 uint8_t msgBuf
[ 512 ];
5592 static void DNSQueryPrintPrologue( const DNSQueryContext
*inContext
);
5593 static void DNSQueryReadHandler( void *inContext
);
5594 static void DNSQueryCancelHandler( void *inContext
);
5596 static void DNSQueryCmd( void )
5599 DNSQueryContext
* context
= NULL
;
5601 size_t msgLen
, sendLen
;
5603 // Check command parameters.
5605 if( gDNSQuery_TimeLimitSecs
< -1 )
5607 FPrintF( stdout
, "Invalid time limit: %d seconds.\n", gDNSQuery_TimeLimitSecs
);
5611 if( ( gDNSQuery_Flags
< INT16_MIN
) || ( gDNSQuery_Flags
> UINT16_MAX
) )
5613 FPrintF( stdout
, "DNS flags-and-codes value is out of the unsigned 16-bit range: 0x%08X.\n", gDNSQuery_Flags
);
5620 context
= (DNSQueryContext
*) calloc( 1, sizeof( *context
) );
5621 require_action( context
, exit
, err
= kNoMemoryErr
);
5623 context
->name
= gDNSQuery_Name
;
5624 context
->sock
= kInvalidSocketRef
;
5625 context
->timeLimitSecs
= gDNSQuery_TimeLimitSecs
;
5626 context
->queryID
= (uint16_t) Random32();
5627 context
->useTCP
= gDNSQuery_UseTCP
? true : false;
5628 context
->printRawRData
= gDNSQuery_RawRData
? true : false;
5630 #if( TARGET_OS_DARWIN )
5631 if( gDNSQuery_Server
)
5634 err
= StringToSockAddr( gDNSQuery_Server
, &context
->serverAddr
, sizeof( context
->serverAddr
), NULL
);
5635 require_noerr( err
, exit
);
5637 #if( TARGET_OS_DARWIN )
5640 err
= GetDefaultDNSServer( &context
->serverAddr
);
5641 require_noerr( err
, exit
);
5644 if( SockAddrGetPort( &context
->serverAddr
) == 0 ) SockAddrSetPort( &context
->serverAddr
, kDNSPort
);
5646 err
= RecordTypeFromArgString( gDNSQuery_Type
, &context
->type
);
5647 require_noerr( err
, exit
);
5649 // Write query message.
5651 check_compile_time_code( sizeof( context
->msgBuf
) >= ( kDNSQueryMessageMaxLen
+ 2 ) );
5653 msgPtr
= context
->useTCP
? &context
->msgBuf
[ 2 ] : context
->msgBuf
;
5654 err
= WriteDNSQueryMessage( msgPtr
, context
->queryID
, (uint16_t) gDNSQuery_Flags
, context
->name
, context
->type
,
5655 kDNSServiceClass_IN
, &msgLen
);
5656 require_noerr( err
, exit
);
5657 check( msgLen
<= UINT16_MAX
);
5659 if( context
->useTCP
)
5661 WriteBig16( context
->msgBuf
, msgLen
);
5662 sendLen
= 2 + msgLen
;
5669 DNSQueryPrintPrologue( context
);
5671 if( gDNSQuery_Verbose
)
5673 FPrintF( stdout
, "DNS message to send:\n\n%{du:dnsmsg}", msgPtr
, msgLen
);
5674 FPrintF( stdout
, "---\n" );
5677 if( context
->useTCP
)
5679 // Create TCP socket.
5681 context
->sock
= socket( context
->serverAddr
.sa
.sa_family
, SOCK_STREAM
, IPPROTO_TCP
);
5682 err
= map_socket_creation_errno( context
->sock
);
5683 require_noerr( err
, exit
);
5685 err
= SocketConnect( context
->sock
, &context
->serverAddr
, 5 );
5686 require_noerr( err
, exit
);
5690 // Create UDP socket.
5692 err
= UDPClientSocketOpen( AF_UNSPEC
, &context
->serverAddr
, 0, -1, NULL
, &context
->sock
);
5693 require_noerr( err
, exit
);
5696 context
->sendTicks
= UpTicks();
5697 err
= _SocketWriteAll( context
->sock
, context
->msgBuf
, sendLen
, 5 );
5698 require_noerr( err
, exit
);
5700 if( context
->timeLimitSecs
== 0 ) goto exit
;
5702 err
= DispatchReadSourceCreate( context
->sock
, NULL
, DNSQueryReadHandler
, DNSQueryCancelHandler
, context
,
5703 &context
->readSource
);
5704 require_noerr( err
, exit
);
5705 dispatch_resume( context
->readSource
);
5707 if( context
->timeLimitSecs
> 0 )
5709 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
5717 dispatch_source_forget( &context
->readSource
);
5718 ForgetSocket( &context
->sock
);
5721 if( err
) exit( 1 );
5724 //===========================================================================================================================
5725 // DNSQueryPrintPrologue
5726 //===========================================================================================================================
5728 static void DNSQueryPrintPrologue( const DNSQueryContext
*inContext
)
5730 const int timeLimitSecs
= inContext
->timeLimitSecs
;
5732 FPrintF( stdout
, "Name: %s\n", inContext
->name
);
5733 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->type
), inContext
->type
);
5734 FPrintF( stdout
, "Server: %##a\n", &inContext
->serverAddr
);
5735 FPrintF( stdout
, "Transport: %s\n", inContext
->useTCP
? "TCP" : "UDP" );
5736 FPrintF( stdout
, "Time limit: " );
5737 if( timeLimitSecs
>= 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
5738 else FPrintF( stdout
, "∞\n" );
5739 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
5740 FPrintF( stdout
, "---\n" );
5743 //===========================================================================================================================
5744 // DNSQueryReadHandler
5745 //===========================================================================================================================
5747 static void DNSQueryReadHandler( void *inContext
)
5751 const uint64_t nowTicks
= UpTicks();
5752 DNSQueryContext
* const context
= (DNSQueryContext
*) inContext
;
5754 gettimeofday( &now
, NULL
);
5756 if( context
->useTCP
)
5758 if( !context
->haveTCPLen
)
5760 err
= SocketReadData( context
->sock
, &context
->msgBuf
, 2, &context
->msgOffset
);
5761 if( err
== EWOULDBLOCK
) { err
= kNoErr
; goto exit
; }
5762 require_noerr( err
, exit
);
5764 context
->msgOffset
= 0;
5765 context
->msgLen
= ReadBig16( context
->msgBuf
);
5766 context
->haveTCPLen
= true;
5767 if( context
->msgLen
<= sizeof( context
->msgBuf
) )
5769 context
->msgPtr
= context
->msgBuf
;
5773 context
->msgPtr
= (uint8_t *) malloc( context
->msgLen
);
5774 require_action( context
->msgPtr
, exit
, err
= kNoMemoryErr
);
5778 err
= SocketReadData( context
->sock
, context
->msgPtr
, context
->msgLen
, &context
->msgOffset
);
5779 if( err
== EWOULDBLOCK
) { err
= kNoErr
; goto exit
; }
5780 require_noerr( err
, exit
);
5781 context
->msgOffset
= 0;
5782 context
->haveTCPLen
= false;
5786 sockaddr_ip fromAddr
;
5788 context
->msgPtr
= context
->msgBuf
;
5789 err
= SocketRecvFrom( context
->sock
, context
->msgPtr
, sizeof( context
->msgBuf
), &context
->msgLen
, &fromAddr
,
5790 sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
5791 require_noerr( err
, exit
);
5793 check( SockAddrCompareAddr( &fromAddr
, &context
->serverAddr
) == 0 );
5796 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
5797 FPrintF( stdout
, "Source: %##a\n", &context
->serverAddr
);
5798 FPrintF( stdout
, "Message size: %zu\n", context
->msgLen
);
5799 FPrintF( stdout
, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks
- context
->sendTicks
) );
5800 FPrintF( stdout
, "%.*{du:dnsmsg}", context
->printRawRData
? 1 : 0, context
->msgPtr
, context
->msgLen
);
5802 if( ( context
->msgLen
>= kDNSHeaderLength
) && ( DNSHeaderGetID( (DNSHeader
*) context
->msgPtr
) == context
->queryID
) )
5804 Exit( kExitReason_ReceivedResponse
);
5808 if( err
) dispatch_source_forget( &context
->readSource
);
5811 //===========================================================================================================================
5812 // DNSQueryCancelHandler
5813 //===========================================================================================================================
5815 static void DNSQueryCancelHandler( void *inContext
)
5817 DNSQueryContext
* const context
= (DNSQueryContext
*) inContext
;
5819 check( !context
->readSource
);
5820 ForgetSocket( &context
->sock
);
5821 if( context
->msgPtr
!= context
->msgBuf
) ForgetMem( &context
->msgPtr
);
5823 dispatch_async_f( dispatch_get_main_queue(), NULL
, Exit
);
5826 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
5827 //===========================================================================================================================
5829 //===========================================================================================================================
5831 #define kDNSCryptPort 443
5833 #define kDNSCryptMinPadLength 8
5834 #define kDNSCryptMaxPadLength 256
5835 #define kDNSCryptBlockSize 64
5836 #define kDNSCryptCertMinimumLength 124
5837 #define kDNSCryptClientMagicLength 8
5838 #define kDNSCryptResolverMagicLength 8
5839 #define kDNSCryptHalfNonceLength 12
5840 #define kDNSCryptCertMagicLength 4
5842 check_compile_time( ( kDNSCryptHalfNonceLength
* 2 ) == crypto_box_NONCEBYTES
);
5844 static const uint8_t kDNSCryptCertMagic
[ kDNSCryptCertMagicLength
] = { 'D', 'N', 'S', 'C' };
5845 static const uint8_t kDNSCryptResolverMagic
[ kDNSCryptResolverMagicLength
] =
5847 0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38
5852 uint8_t certMagic
[ kDNSCryptCertMagicLength
];
5853 uint8_t esVersion
[ 2 ];
5854 uint8_t minorVersion
[ 2 ];
5855 uint8_t signature
[ crypto_sign_BYTES
];
5856 uint8_t publicKey
[ crypto_box_PUBLICKEYBYTES
];
5857 uint8_t clientMagic
[ kDNSCryptClientMagicLength
];
5858 uint8_t serial
[ 4 ];
5859 uint8_t startTime
[ 4 ];
5860 uint8_t endTime
[ 4 ];
5861 uint8_t extensions
[ 1 ]; // Variably-sized extension data.
5865 check_compile_time( offsetof( DNSCryptCert
, extensions
) == kDNSCryptCertMinimumLength
);
5869 uint8_t clientMagic
[ kDNSCryptClientMagicLength
];
5870 uint8_t clientPublicKey
[ crypto_box_PUBLICKEYBYTES
];
5871 uint8_t clientNonce
[ kDNSCryptHalfNonceLength
];
5872 uint8_t poly1305MAC
[ 16 ];
5874 } DNSCryptQueryHeader
;
5876 check_compile_time( sizeof( DNSCryptQueryHeader
) == 68 );
5877 check_compile_time( sizeof( DNSCryptQueryHeader
) >= crypto_box_ZEROBYTES
);
5878 check_compile_time( ( sizeof( DNSCryptQueryHeader
) - crypto_box_ZEROBYTES
+ crypto_box_BOXZEROBYTES
) ==
5879 offsetof( DNSCryptQueryHeader
, poly1305MAC
) );
5883 uint8_t resolverMagic
[ kDNSCryptResolverMagicLength
];
5884 uint8_t clientNonce
[ kDNSCryptHalfNonceLength
];
5885 uint8_t resolverNonce
[ kDNSCryptHalfNonceLength
];
5886 uint8_t poly1305MAC
[ 16 ];
5888 } DNSCryptResponseHeader
;
5890 check_compile_time( sizeof( DNSCryptResponseHeader
) == 48 );
5891 check_compile_time( offsetof( DNSCryptResponseHeader
, poly1305MAC
) >= crypto_box_BOXZEROBYTES
);
5892 check_compile_time( ( offsetof( DNSCryptResponseHeader
, poly1305MAC
) - crypto_box_BOXZEROBYTES
+ crypto_box_ZEROBYTES
) ==
5893 sizeof( DNSCryptResponseHeader
) );
5897 sockaddr_ip serverAddr
;
5899 const char * providerName
;
5901 const uint8_t * certPtr
;
5903 dispatch_source_t readSource
;
5908 Boolean printRawRData
;
5909 uint8_t serverPublicSignKey
[ crypto_sign_PUBLICKEYBYTES
];
5910 uint8_t serverPublicKey
[ crypto_box_PUBLICKEYBYTES
];
5911 uint8_t clientPublicKey
[ crypto_box_PUBLICKEYBYTES
];
5912 uint8_t clientSecretKey
[ crypto_box_SECRETKEYBYTES
];
5913 uint8_t clientMagic
[ kDNSCryptClientMagicLength
];
5914 uint8_t clientNonce
[ kDNSCryptHalfNonceLength
];
5915 uint8_t nmKey
[ crypto_box_BEFORENMBYTES
];
5916 uint8_t msgBuf
[ 512 ];
5920 static void DNSCryptReceiveCertHandler( void *inContext
);
5921 static void DNSCryptReceiveResponseHandler( void *inContext
);
5922 static void DNSCryptProceed( void *inContext
);
5923 static OSStatus
DNSCryptProcessCert( DNSCryptContext
*inContext
);
5924 static OSStatus
DNSCryptBuildQuery( DNSCryptContext
*inContext
);
5925 static OSStatus
DNSCryptSendQuery( DNSCryptContext
*inContext
);
5926 static void DNSCryptPrintCertificate( const DNSCryptCert
*inCert
, size_t inLen
);
5928 static void DNSCryptCmd( void )
5931 DNSCryptContext
* context
= NULL
;
5932 size_t writtenBytes
;
5934 SocketContext
* sockCtx
;
5935 SocketRef sock
= kInvalidSocketRef
;
5938 // Check command parameters.
5940 if( gDNSCrypt_TimeLimitSecs
< -1 )
5942 FPrintF( stdout
, "Invalid time limit: %d seconds.\n", gDNSCrypt_TimeLimitSecs
);
5949 context
= (DNSCryptContext
*) calloc( 1, sizeof( *context
) );
5950 require_action( context
, exit
, err
= kNoMemoryErr
);
5952 context
->providerName
= gDNSCrypt_ProviderName
;
5953 context
->qname
= gDNSCrypt_Name
;
5954 context
->timeLimitSecs
= gDNSCrypt_TimeLimitSecs
;
5955 context
->printRawRData
= gDNSCrypt_RawRData
? true : false;
5957 err
= crypto_box_keypair( context
->clientPublicKey
, context
->clientSecretKey
);
5958 require_noerr( err
, exit
);
5960 err
= HexToData( gDNSCrypt_ProviderKey
, kSizeCString
, kHexToData_DefaultFlags
,
5961 context
->serverPublicSignKey
, sizeof( context
->serverPublicSignKey
), &writtenBytes
, &totalBytes
, &ptr
);
5962 if( err
|| ( *ptr
!= '\0' ) )
5964 FPrintF( stderr
, "Failed to parse public signing key hex string (%s).\n", gDNSCrypt_ProviderKey
);
5967 else if( totalBytes
!= sizeof( context
->serverPublicSignKey
) )
5969 FPrintF( stderr
, "Public signing key contains incorrect number of hex bytes (%zu != %zu)\n",
5970 totalBytes
, sizeof( context
->serverPublicSignKey
) );
5974 check( writtenBytes
== totalBytes
);
5976 err
= StringToSockAddr( gDNSCrypt_Server
, &context
->serverAddr
, sizeof( context
->serverAddr
), NULL
);
5977 require_noerr( err
, exit
);
5978 if( SockAddrGetPort( &context
->serverAddr
) == 0 ) SockAddrSetPort( &context
->serverAddr
, kDNSCryptPort
);
5980 err
= RecordTypeFromArgString( gDNSCrypt_Type
, &context
->qtype
);
5981 require_noerr( err
, exit
);
5983 // Write query message.
5985 context
->queryID
= (uint16_t) Random32();
5986 err
= WriteDNSQueryMessage( context
->msgBuf
, context
->queryID
, kDNSHeaderFlag_RecursionDesired
, context
->providerName
,
5987 kDNSServiceType_TXT
, kDNSServiceClass_IN
, &context
->msgLen
);
5988 require_noerr( err
, exit
);
5990 // Create UDP socket.
5992 err
= UDPClientSocketOpen( AF_UNSPEC
, &context
->serverAddr
, 0, -1, NULL
, &sock
);
5993 require_noerr( err
, exit
);
5997 context
->sendTicks
= UpTicks();
5998 err
= _SocketWriteAll( sock
, context
->msgBuf
, context
->msgLen
, 5 );
5999 require_noerr( err
, exit
);
6001 err
= SocketContextCreate( sock
, context
, &sockCtx
);
6002 require_noerr( err
, exit
);
6003 sock
= kInvalidSocketRef
;
6005 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, DNSCryptReceiveCertHandler
, SocketContextCancelHandler
, sockCtx
,
6006 &context
->readSource
);
6007 if( err
) ForgetSocketContext( &sockCtx
);
6008 require_noerr( err
, exit
);
6010 dispatch_resume( context
->readSource
);
6012 if( context
->timeLimitSecs
> 0 )
6014 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
6020 if( context
) free( context
);
6021 ForgetSocket( &sock
);
6022 if( err
) exit( 1 );
6025 //===========================================================================================================================
6026 // DNSCryptReceiveCertHandler
6027 //===========================================================================================================================
6029 static void DNSCryptReceiveCertHandler( void *inContext
)
6033 const uint64_t nowTicks
= UpTicks();
6034 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
6035 DNSCryptContext
* const context
= (DNSCryptContext
*) sockCtx
->userContext
;
6036 const DNSHeader
* hdr
;
6037 sockaddr_ip fromAddr
;
6038 const uint8_t * ptr
;
6039 const uint8_t * txtPtr
;
6041 unsigned int answerCount
, i
;
6042 uint8_t targetName
[ kDomainNameLengthMax
];
6044 gettimeofday( &now
, NULL
);
6046 dispatch_source_forget( &context
->readSource
);
6048 err
= SocketRecvFrom( sockCtx
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &context
->msgLen
,
6049 &fromAddr
, sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
6050 require_noerr( err
, exit
);
6051 check( SockAddrCompareAddr( &fromAddr
, &context
->serverAddr
) == 0 );
6053 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
6054 FPrintF( stdout
, "Source: %##a\n", &context
->serverAddr
);
6055 FPrintF( stdout
, "Message size: %zu\n", context
->msgLen
);
6056 FPrintF( stdout
, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks
- context
->sendTicks
) );
6057 FPrintF( stdout
, "%.*{du:dnsmsg}", context
->printRawRData
? 1 : 0, context
->msgBuf
, context
->msgLen
);
6059 require_action_quiet( context
->msgLen
>= kDNSHeaderLength
, exit
, err
= kSizeErr
);
6061 hdr
= (DNSHeader
*) context
->msgBuf
;
6062 require_action_quiet( DNSHeaderGetID( hdr
) == context
->queryID
, exit
, err
= kMismatchErr
);
6064 err
= DNSMessageGetAnswerSection( context
->msgBuf
, context
->msgLen
, &ptr
);
6065 require_noerr( err
, exit
);
6067 err
= DomainNameFromString( targetName
, context
->providerName
, NULL
);
6068 require_noerr( err
, exit
);
6070 answerCount
= DNSHeaderGetAnswerCount( hdr
);
6071 for( i
= 0; i
< answerCount
; ++i
)
6075 uint8_t name
[ kDomainNameLengthMax
];
6077 err
= DNSMessageExtractRecord( context
->msgBuf
, context
->msgLen
, ptr
, name
, &type
, &class, NULL
, &txtPtr
, &txtLen
,
6079 require_noerr( err
, exit
);
6081 if( ( type
== kDNSServiceType_TXT
) && ( class == kDNSServiceClass_IN
) && DomainNameEqual( name
, targetName
) )
6087 if( txtLen
< ( 1 + kDNSCryptCertMinimumLength
) )
6089 FPrintF( stderr
, "TXT record length is too short (%u < %u)\n", txtLen
, kDNSCryptCertMinimumLength
+ 1 );
6093 if( txtPtr
[ 0 ] < kDNSCryptCertMinimumLength
)
6095 FPrintF( stderr
, "TXT record value length is too short (%u < %u)\n", txtPtr
[ 0 ], kDNSCryptCertMinimumLength
);
6100 context
->certLen
= txtPtr
[ 0 ];
6101 context
->certPtr
= &txtPtr
[ 1 ];
6103 dispatch_async_f( dispatch_get_main_queue(), context
, DNSCryptProceed
);
6106 if( err
) Exit( NULL
);
6109 //===========================================================================================================================
6110 // DNSCryptReceiveResponseHandler
6111 //===========================================================================================================================
6113 static void DNSCryptReceiveResponseHandler( void *inContext
)
6117 const uint64_t nowTicks
= UpTicks();
6118 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
6119 DNSCryptContext
* const context
= (DNSCryptContext
*) sockCtx
->userContext
;
6120 sockaddr_ip fromAddr
;
6121 DNSCryptResponseHeader
* hdr
;
6122 const uint8_t * end
;
6123 uint8_t * ciphertext
;
6124 uint8_t * plaintext
;
6125 const uint8_t * response
;
6126 uint8_t nonce
[ crypto_box_NONCEBYTES
];
6128 gettimeofday( &now
, NULL
);
6130 dispatch_source_forget( &context
->readSource
);
6132 err
= SocketRecvFrom( sockCtx
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &context
->msgLen
,
6133 &fromAddr
, sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
6134 require_noerr( err
, exit
);
6135 check( SockAddrCompareAddr( &fromAddr
, &context
->serverAddr
) == 0 );
6137 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
6138 FPrintF( stdout
, "Source: %##a\n", &context
->serverAddr
);
6139 FPrintF( stdout
, "Message size: %zu\n", context
->msgLen
);
6140 FPrintF( stdout
, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks
- context
->sendTicks
) );
6142 if( context
->msgLen
< sizeof( DNSCryptResponseHeader
) )
6144 FPrintF( stderr
, "DNSCrypt response is too short.\n" );
6149 hdr
= (DNSCryptResponseHeader
*) context
->msgBuf
;
6151 if( memcmp( hdr
->resolverMagic
, kDNSCryptResolverMagic
, kDNSCryptResolverMagicLength
) != 0 )
6153 FPrintF( stderr
, "DNSCrypt response resolver magic %#H != %#H\n",
6154 hdr
->resolverMagic
, kDNSCryptResolverMagicLength
, INT_MAX
,
6155 kDNSCryptResolverMagic
, kDNSCryptResolverMagicLength
, INT_MAX
);
6160 if( memcmp( hdr
->clientNonce
, context
->clientNonce
, kDNSCryptHalfNonceLength
) != 0 )
6162 FPrintF( stderr
, "DNSCrypt response client nonce mismatch.\n" );
6167 memcpy( nonce
, hdr
->clientNonce
, crypto_box_NONCEBYTES
);
6169 ciphertext
= hdr
->poly1305MAC
- crypto_box_BOXZEROBYTES
;
6170 memset( ciphertext
, 0, crypto_box_BOXZEROBYTES
);
6172 plaintext
= (uint8_t *)( hdr
+ 1 ) - crypto_box_ZEROBYTES
;
6173 check( plaintext
== ciphertext
);
6175 end
= context
->msgBuf
+ context
->msgLen
;
6177 err
= crypto_box_open_afternm( plaintext
, ciphertext
, (size_t)( end
- ciphertext
), nonce
, context
->nmKey
);
6178 require_noerr( err
, exit
);
6180 response
= plaintext
+ crypto_box_ZEROBYTES
;
6181 FPrintF( stdout
, "%.*{du:dnsmsg}", context
->printRawRData
? 1 : 0, response
, (size_t)( end
- response
) );
6182 Exit( kExitReason_ReceivedResponse
);
6185 if( err
) Exit( NULL
);
6188 //===========================================================================================================================
6190 //===========================================================================================================================
6192 static void DNSCryptProceed( void *inContext
)
6195 DNSCryptContext
* const context
= (DNSCryptContext
*) inContext
;
6197 err
= DNSCryptProcessCert( context
);
6198 require_noerr_quiet( err
, exit
);
6200 err
= DNSCryptBuildQuery( context
);
6201 require_noerr_quiet( err
, exit
);
6203 err
= DNSCryptSendQuery( context
);
6204 require_noerr_quiet( err
, exit
);
6207 if( err
) Exit( NULL
);
6210 //===========================================================================================================================
6211 // DNSCryptProcessCert
6212 //===========================================================================================================================
6214 static OSStatus
DNSCryptProcessCert( DNSCryptContext
*inContext
)
6217 const DNSCryptCert
* const cert
= (DNSCryptCert
*) inContext
->certPtr
;
6218 const uint8_t * const certEnd
= inContext
->certPtr
+ inContext
->certLen
;
6220 time_t startTimeSecs
, endTimeSecs
;
6223 unsigned long long tempLen
;
6225 DNSCryptPrintCertificate( cert
, inContext
->certLen
);
6227 if( memcmp( cert
->certMagic
, kDNSCryptCertMagic
, kDNSCryptCertMagicLength
) != 0 )
6229 FPrintF( stderr
, "DNSCrypt certificate magic %#H != %#H\n",
6230 cert
->certMagic
, kDNSCryptCertMagicLength
, INT_MAX
,
6231 kDNSCryptCertMagic
, kDNSCryptCertMagicLength
, INT_MAX
);
6236 startTimeSecs
= (time_t) ReadBig32( cert
->startTime
);
6237 endTimeSecs
= (time_t) ReadBig32( cert
->endTime
);
6239 gettimeofday( &now
, NULL
);
6240 if( now
.tv_sec
< startTimeSecs
)
6242 FPrintF( stderr
, "DNSCrypt certificate start time is in the future.\n" );
6246 if( now
.tv_sec
>= endTimeSecs
)
6248 FPrintF( stderr
, "DNSCrypt certificate has expired.\n" );
6253 signedLen
= (size_t)( certEnd
- cert
->signature
);
6254 tempBuf
= (uint8_t *) malloc( signedLen
);
6255 require_action( tempBuf
, exit
, err
= kNoMemoryErr
);
6256 err
= crypto_sign_open( tempBuf
, &tempLen
, cert
->signature
, signedLen
, inContext
->serverPublicSignKey
);
6260 FPrintF( stderr
, "DNSCrypt certificate failed verification.\n" );
6261 err
= kAuthenticationErr
;
6265 memcpy( inContext
->serverPublicKey
, cert
->publicKey
, crypto_box_PUBLICKEYBYTES
);
6266 memcpy( inContext
->clientMagic
, cert
->clientMagic
, kDNSCryptClientMagicLength
);
6268 err
= crypto_box_beforenm( inContext
->nmKey
, inContext
->serverPublicKey
, inContext
->clientSecretKey
);
6269 require_noerr( err
, exit
);
6271 inContext
->certPtr
= NULL
;
6272 inContext
->certLen
= 0;
6273 inContext
->msgLen
= 0;
6279 //===========================================================================================================================
6280 // DNSCryptBuildQuery
6281 //===========================================================================================================================
6283 static OSStatus
DNSCryptPadQuery( uint8_t *inMsgPtr
, size_t inMsgLen
, size_t inMaxLen
, size_t *outPaddedLen
);
6285 static OSStatus
DNSCryptBuildQuery( DNSCryptContext
*inContext
)
6288 DNSCryptQueryHeader
* const hdr
= (DNSCryptQueryHeader
*) inContext
->msgBuf
;
6289 uint8_t * const queryPtr
= (uint8_t *)( hdr
+ 1 );
6291 size_t paddedQueryLen
;
6292 const uint8_t * const msgLimit
= inContext
->msgBuf
+ sizeof( inContext
->msgBuf
);
6293 const uint8_t * padLimit
;
6294 uint8_t nonce
[ crypto_box_NONCEBYTES
];
6296 check_compile_time_code( sizeof( inContext
->msgBuf
) >= ( sizeof( DNSCryptQueryHeader
) + kDNSQueryMessageMaxLen
) );
6298 inContext
->queryID
= (uint16_t) Random32();
6299 err
= WriteDNSQueryMessage( queryPtr
, inContext
->queryID
, kDNSHeaderFlag_RecursionDesired
, inContext
->qname
,
6300 inContext
->qtype
, kDNSServiceClass_IN
, &queryLen
);
6301 require_noerr( err
, exit
);
6303 padLimit
= &queryPtr
[ queryLen
+ kDNSCryptMaxPadLength
];
6304 if( padLimit
> msgLimit
) padLimit
= msgLimit
;
6306 err
= DNSCryptPadQuery( queryPtr
, queryLen
, (size_t)( padLimit
- queryPtr
), &paddedQueryLen
);
6307 require_noerr( err
, exit
);
6309 memset( queryPtr
- crypto_box_ZEROBYTES
, 0, crypto_box_ZEROBYTES
);
6310 RandomBytes( inContext
->clientNonce
, kDNSCryptHalfNonceLength
);
6311 memcpy( nonce
, inContext
->clientNonce
, kDNSCryptHalfNonceLength
);
6312 memset( &nonce
[ kDNSCryptHalfNonceLength
], 0, kDNSCryptHalfNonceLength
);
6314 err
= crypto_box_afternm( queryPtr
- crypto_box_ZEROBYTES
, queryPtr
- crypto_box_ZEROBYTES
,
6315 paddedQueryLen
+ crypto_box_ZEROBYTES
, nonce
, inContext
->nmKey
);
6316 require_noerr( err
, exit
);
6318 memcpy( hdr
->clientMagic
, inContext
->clientMagic
, kDNSCryptClientMagicLength
);
6319 memcpy( hdr
->clientPublicKey
, inContext
->clientPublicKey
, crypto_box_PUBLICKEYBYTES
);
6320 memcpy( hdr
->clientNonce
, nonce
, kDNSCryptHalfNonceLength
);
6322 inContext
->msgLen
= (size_t)( &queryPtr
[ paddedQueryLen
] - inContext
->msgBuf
);
6328 static OSStatus
DNSCryptPadQuery( uint8_t *inMsgPtr
, size_t inMsgLen
, size_t inMaxLen
, size_t *outPaddedLen
)
6333 require_action_quiet( ( inMsgLen
+ kDNSCryptMinPadLength
) <= inMaxLen
, exit
, err
= kSizeErr
);
6335 paddedLen
= inMsgLen
+ kDNSCryptMinPadLength
+
6336 arc4random_uniform( (uint32_t)( inMaxLen
- ( inMsgLen
+ kDNSCryptMinPadLength
) + 1 ) );
6337 paddedLen
+= ( kDNSCryptBlockSize
- ( paddedLen
% kDNSCryptBlockSize
) );
6338 if( paddedLen
> inMaxLen
) paddedLen
= inMaxLen
;
6340 inMsgPtr
[ inMsgLen
] = 0x80;
6341 memset( &inMsgPtr
[ inMsgLen
+ 1 ], 0, paddedLen
- ( inMsgLen
+ 1 ) );
6343 if( outPaddedLen
) *outPaddedLen
= paddedLen
;
6350 //===========================================================================================================================
6351 // DNSCryptSendQuery
6352 //===========================================================================================================================
6354 static OSStatus
DNSCryptSendQuery( DNSCryptContext
*inContext
)
6357 SocketContext
* sockCtx
;
6358 SocketRef sock
= kInvalidSocketRef
;
6360 check( inContext
->msgLen
> 0 );
6361 check( !inContext
->readSource
);
6363 err
= UDPClientSocketOpen( AF_UNSPEC
, &inContext
->serverAddr
, 0, -1, NULL
, &sock
);
6364 require_noerr( err
, exit
);
6366 inContext
->sendTicks
= UpTicks();
6367 err
= _SocketWriteAll( sock
, inContext
->msgBuf
, inContext
->msgLen
, 5 );
6368 require_noerr( err
, exit
);
6370 err
= SocketContextCreate( sock
, inContext
, &sockCtx
);
6371 require_noerr( err
, exit
);
6372 sock
= kInvalidSocketRef
;
6374 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, DNSCryptReceiveResponseHandler
, SocketContextCancelHandler
, sockCtx
,
6375 &inContext
->readSource
);
6376 if( err
) ForgetSocketContext( &sockCtx
);
6377 require_noerr( err
, exit
);
6379 dispatch_resume( inContext
->readSource
);
6382 ForgetSocket( &sock
);
6386 //===========================================================================================================================
6387 // DNSCryptPrintCertificate
6388 //===========================================================================================================================
6390 #define kCertTimeStrBufLen 32
6392 static char * CertTimeStr( time_t inTime
, char inBuffer
[ kCertTimeStrBufLen
] );
6394 static void DNSCryptPrintCertificate( const DNSCryptCert
*inCert
, size_t inLen
)
6396 time_t startTime
, endTime
;
6398 char timeBuf
[ kCertTimeStrBufLen
];
6400 check( inLen
>= kDNSCryptCertMinimumLength
);
6402 startTime
= (time_t) ReadBig32( inCert
->startTime
);
6403 endTime
= (time_t) ReadBig32( inCert
->endTime
);
6405 FPrintF( stdout
, "DNSCrypt certificate (%zu bytes):\n", inLen
);
6406 FPrintF( stdout
, "Cert Magic: %#H\n", inCert
->certMagic
, kDNSCryptCertMagicLength
, INT_MAX
);
6407 FPrintF( stdout
, "ES Version: %u\n", ReadBig16( inCert
->esVersion
) );
6408 FPrintF( stdout
, "Minor Version: %u\n", ReadBig16( inCert
->minorVersion
) );
6409 FPrintF( stdout
, "Signature: %H\n", inCert
->signature
, crypto_sign_BYTES
/ 2, INT_MAX
);
6410 FPrintF( stdout
, " %H\n", &inCert
->signature
[ crypto_sign_BYTES
/ 2 ], crypto_sign_BYTES
/ 2, INT_MAX
);
6411 FPrintF( stdout
, "Public Key: %H\n", inCert
->publicKey
, sizeof( inCert
->publicKey
), INT_MAX
);
6412 FPrintF( stdout
, "Client Magic: %H\n", inCert
->clientMagic
, kDNSCryptClientMagicLength
, INT_MAX
);
6413 FPrintF( stdout
, "Serial: %u\n", ReadBig32( inCert
->serial
) );
6414 FPrintF( stdout
, "Start Time: %u (%s)\n", (uint32_t) startTime
, CertTimeStr( startTime
, timeBuf
) );
6415 FPrintF( stdout
, "End Time: %u (%s)\n", (uint32_t) endTime
, CertTimeStr( endTime
, timeBuf
) );
6417 if( inLen
> kDNSCryptCertMinimumLength
)
6419 extLen
= (int)( inLen
- kDNSCryptCertMinimumLength
);
6420 FPrintF( stdout
, "Extensions: %.1H\n", inCert
->extensions
, extLen
, extLen
);
6422 FPrintF( stdout
, "\n" );
6425 static char * CertTimeStr( time_t inTime
, char inBuffer
[ kCertTimeStrBufLen
] )
6429 tm
= localtime( &inTime
);
6432 dlogassert( "localtime() returned a NULL pointer.\n" );
6437 strftime( inBuffer
, kCertTimeStrBufLen
, "%a %b %d %H:%M:%S %Z %Y", tm
);
6443 #endif // DNSSDUTIL_INCLUDE_DNSCRYPT
6445 //===========================================================================================================================
6447 //===========================================================================================================================
6451 const char * qnameStr
; // Name (QNAME) of the record being queried as a C string.
6452 dispatch_source_t readSourceV4
; // Read dispatch source for IPv4 socket.
6453 dispatch_source_t readSourceV6
; // Read dispatch source for IPv6 socket.
6454 int localPort
; // The port number to which the sockets are bound.
6455 int receiveSecs
; // After send, the amount of time to spend receiving.
6456 uint32_t ifIndex
; // Index of the interface over which to send the query.
6457 uint16_t qtype
; // The type (QTYPE) of the record being queried.
6458 Boolean isQU
; // True if the query is QU, i.e., requests unicast responses.
6459 Boolean allResponses
; // True if all mDNS messages received should be printed.
6460 Boolean printRawRData
; // True if RDATA should be printed as hexdumps.
6461 Boolean useIPv4
; // True if the query should be sent via IPv4 multicast.
6462 Boolean useIPv6
; // True if the query should be sent via IPv6 multicast.
6463 char ifName
[ IF_NAMESIZE
+ 1 ]; // Name of the interface over which to send the query.
6464 uint8_t qname
[ kDomainNameLengthMax
]; // Buffer to hold the QNAME in DNS label format.
6465 uint8_t msgBuf
[ kMDNSMessageSizeMax
]; // mDNS message buffer.
6469 static void MDNSQueryPrintPrologue( const MDNSQueryContext
*inContext
);
6470 static void MDNSQueryReadHandler( void *inContext
);
6472 static void MDNSQueryCmd( void )
6475 MDNSQueryContext
* context
;
6476 SocketRef sockV4
= kInvalidSocketRef
;
6477 SocketRef sockV6
= kInvalidSocketRef
;
6479 const char * ifname
;
6481 unsigned int sendCount
;
6483 // Check command parameters.
6485 if( gMDNSQuery_ReceiveSecs
< -1 )
6487 FPrintF( stdout
, "Invalid receive time value: %d seconds.\n", gMDNSQuery_ReceiveSecs
);
6492 context
= (MDNSQueryContext
*) calloc( 1, sizeof( *context
) );
6493 require_action( context
, exit
, err
= kNoMemoryErr
);
6495 context
->qnameStr
= gMDNSQuery_Name
;
6496 context
->receiveSecs
= gMDNSQuery_ReceiveSecs
;
6497 context
->isQU
= gMDNSQuery_IsQU
? true : false;
6498 context
->allResponses
= gMDNSQuery_AllResponses
? true : false;
6499 context
->printRawRData
= gMDNSQuery_RawRData
? true : false;
6500 context
->useIPv4
= ( gMDNSQuery_UseIPv4
|| !gMDNSQuery_UseIPv6
) ? true : false;
6501 context
->useIPv6
= ( gMDNSQuery_UseIPv6
|| !gMDNSQuery_UseIPv4
) ? true : false;
6503 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
6504 require_noerr_quiet( err
, exit
);
6506 ifname
= if_indextoname( context
->ifIndex
, context
->ifName
);
6507 require_action( ifname
, exit
, err
= kNameErr
);
6509 err
= RecordTypeFromArgString( gMDNSQuery_Type
, &context
->qtype
);
6510 require_noerr( err
, exit
);
6512 // Set up IPv4 socket.
6514 if( context
->useIPv4
)
6516 err
= CreateMulticastSocket( GetMDNSMulticastAddrV4(),
6517 gMDNSQuery_SourcePort
? gMDNSQuery_SourcePort
: ( context
->isQU
? context
->localPort
: kMDNSPort
),
6518 ifname
, context
->ifIndex
, !context
->isQU
, &context
->localPort
, &sockV4
);
6519 require_noerr( err
, exit
);
6522 // Set up IPv6 socket.
6524 if( context
->useIPv6
)
6526 err
= CreateMulticastSocket( GetMDNSMulticastAddrV6(),
6527 gMDNSQuery_SourcePort
? gMDNSQuery_SourcePort
: ( context
->isQU
? context
->localPort
: kMDNSPort
),
6528 ifname
, context
->ifIndex
, !context
->isQU
, &context
->localPort
, &sockV6
);
6529 require_noerr( err
, exit
);
6532 // Craft mDNS query message.
6534 check_compile_time_code( sizeof( context
->msgBuf
) >= kDNSQueryMessageMaxLen
);
6535 err
= WriteDNSQueryMessage( context
->msgBuf
, kDefaultMDNSMessageID
, kDefaultMDNSQueryFlags
, context
->qnameStr
,
6536 context
->qtype
, context
->isQU
? ( kDNSServiceClass_IN
| kQClassUnicastResponseBit
) : kDNSServiceClass_IN
, &msgLen
);
6537 require_noerr( err
, exit
);
6541 MDNSQueryPrintPrologue( context
);
6543 // Send mDNS query message.
6546 if( IsValidSocket( sockV4
) )
6548 const struct sockaddr
* const mcastAddr4
= GetMDNSMulticastAddrV4();
6550 n
= sendto( sockV4
, context
->msgBuf
, msgLen
, 0, mcastAddr4
, SockAddrGetSize( mcastAddr4
) );
6551 err
= map_socket_value_errno( sockV4
, n
== (ssize_t
) msgLen
, n
);
6554 FPrintF( stderr
, "*** Failed to send query on IPv4 socket with error %#m\n", err
);
6555 ForgetSocket( &sockV4
);
6562 if( IsValidSocket( sockV6
) )
6564 const struct sockaddr
* const mcastAddr6
= GetMDNSMulticastAddrV6();
6566 n
= sendto( sockV6
, context
->msgBuf
, msgLen
, 0, mcastAddr6
, SockAddrGetSize( mcastAddr6
) );
6567 err
= map_socket_value_errno( sockV6
, n
== (ssize_t
) msgLen
, n
);
6570 FPrintF( stderr
, "*** Failed to send query on IPv6 socket with error %#m\n", err
);
6571 ForgetSocket( &sockV6
);
6578 require_action_quiet( sendCount
> 0, exit
, err
= kUnexpectedErr
);
6580 // If there's no wait period after the send, then exit.
6582 if( context
->receiveSecs
== 0 ) goto exit
;
6584 // Create dispatch read sources for socket(s).
6586 if( IsValidSocket( sockV4
) )
6588 SocketContext
* sockCtx
;
6590 err
= SocketContextCreate( sockV4
, context
, &sockCtx
);
6591 require_noerr( err
, exit
);
6592 sockV4
= kInvalidSocketRef
;
6594 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, MDNSQueryReadHandler
, SocketContextCancelHandler
, sockCtx
,
6595 &context
->readSourceV4
);
6596 if( err
) ForgetSocketContext( &sockCtx
);
6597 require_noerr( err
, exit
);
6599 dispatch_resume( context
->readSourceV4
);
6602 if( IsValidSocket( sockV6
) )
6604 SocketContext
* sockCtx
;
6606 err
= SocketContextCreate( sockV6
, context
, &sockCtx
);
6607 require_noerr( err
, exit
);
6608 sockV6
= kInvalidSocketRef
;
6610 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, MDNSQueryReadHandler
, SocketContextCancelHandler
, sockCtx
,
6611 &context
->readSourceV6
);
6612 if( err
) ForgetSocketContext( &sockCtx
);
6613 require_noerr( err
, exit
);
6615 dispatch_resume( context
->readSourceV6
);
6618 if( context
->receiveSecs
> 0 )
6620 dispatch_after_f( dispatch_time_seconds( context
->receiveSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
6626 ForgetSocket( &sockV4
);
6627 ForgetSocket( &sockV6
);
6628 if( err
) exit( 1 );
6631 //===========================================================================================================================
6633 //===========================================================================================================================
6635 static void _MDNSColliderCmdStopHandler( void *inContext
, OSStatus inError
);
6637 static void MDNSColliderCmd( void )
6640 MDNSColliderRef collider
= NULL
;
6641 uint8_t * rdataPtr
= NULL
;
6642 size_t rdataLen
= 0;
6643 const char * ifname
;
6645 MDNSColliderProtocols protocols
;
6647 char ifName
[ IF_NAMESIZE
+ 1 ];
6648 uint8_t name
[ kDomainNameLengthMax
];
6650 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
6651 require_noerr_quiet( err
, exit
);
6653 ifname
= if_indextoname( ifIndex
, ifName
);
6656 FPrintF( stderr
, "error: Invalid interface name or index: %s\n", gInterface
);
6661 err
= DomainNameFromString( name
, gMDNSCollider_Name
, NULL
);
6664 FPrintF( stderr
, "error: Invalid record name: %s\n", gMDNSCollider_Name
);
6668 err
= RecordTypeFromArgString( gMDNSCollider_Type
, &type
);
6669 require_noerr_quiet( err
, exit
);
6671 if( gMDNSCollider_RecordData
)
6673 err
= RecordDataFromArgString( gMDNSCollider_RecordData
, &rdataPtr
, &rdataLen
);
6674 require_noerr_quiet( err
, exit
);
6677 err
= MDNSColliderCreate( dispatch_get_main_queue(), &collider
);
6678 require_noerr( err
, exit
);
6680 err
= MDNSColliderSetProgram( collider
, gMDNSCollider_Program
);
6683 FPrintF( stderr
, "error: Failed to set program string: '%s'\n", gMDNSCollider_Program
);
6687 err
= MDNSColliderSetRecord( collider
, name
, type
, rdataPtr
, rdataLen
);
6688 require_noerr( err
, exit
);
6689 ForgetMem( &rdataPtr
);
6691 protocols
= kMDNSColliderProtocol_None
;
6692 if( gMDNSCollider_UseIPv4
|| !gMDNSCollider_UseIPv6
) protocols
|= kMDNSColliderProtocol_IPv4
;
6693 if( gMDNSCollider_UseIPv6
|| !gMDNSCollider_UseIPv4
) protocols
|= kMDNSColliderProtocol_IPv6
;
6694 MDNSColliderSetProtocols( collider
, protocols
);
6695 MDNSColliderSetInterfaceIndex( collider
, ifIndex
);
6696 MDNSColliderSetStopHandler( collider
, _MDNSColliderCmdStopHandler
, collider
);
6698 err
= MDNSColliderStart( collider
);
6699 require_noerr( err
, exit
);
6704 FreeNullSafe( rdataPtr
);
6705 CFReleaseNullSafe( collider
);
6706 if( err
) exit( 1 );
6709 static void _MDNSColliderCmdStopHandler( void *inContext
, OSStatus inError
)
6711 MDNSColliderRef
const collider
= (MDNSColliderRef
) inContext
;
6713 CFRelease( collider
);
6714 exit( inError
? 1 : 0 );
6717 //===========================================================================================================================
6718 // MDNSQueryPrintPrologue
6719 //===========================================================================================================================
6721 static void MDNSQueryPrintPrologue( const MDNSQueryContext
*inContext
)
6723 const int receiveSecs
= inContext
->receiveSecs
;
6725 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, inContext
->ifName
);
6726 FPrintF( stdout
, "Name: %s\n", inContext
->qnameStr
);
6727 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->qtype
), inContext
->qtype
);
6728 FPrintF( stdout
, "Class: IN (%s)\n", inContext
->isQU
? "QU" : "QM" );
6729 FPrintF( stdout
, "Local port: %d\n", inContext
->localPort
);
6730 FPrintF( stdout
, "IP protocols: %?s%?s%?s\n",
6731 inContext
->useIPv4
, "IPv4", ( inContext
->useIPv4
&& inContext
->useIPv6
), ", ", inContext
->useIPv6
, "IPv6" );
6732 FPrintF( stdout
, "Receive duration: " );
6733 if( receiveSecs
>= 0 ) FPrintF( stdout
, "%d second%?c\n", receiveSecs
, receiveSecs
!= 1, 's' );
6734 else FPrintF( stdout
, "∞\n" );
6735 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
6738 //===========================================================================================================================
6739 // MDNSQueryReadHandler
6740 //===========================================================================================================================
6742 static void MDNSQueryReadHandler( void *inContext
)
6746 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
6747 MDNSQueryContext
* const context
= (MDNSQueryContext
*) sockCtx
->userContext
;
6749 sockaddr_ip fromAddr
;
6750 Boolean foundAnswer
= false;
6752 gettimeofday( &now
, NULL
);
6754 err
= SocketRecvFrom( sockCtx
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &msgLen
, &fromAddr
,
6755 sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
6756 require_noerr( err
, exit
);
6758 if( !context
->allResponses
&& ( msgLen
>= kDNSHeaderLength
) )
6760 const uint8_t * ptr
;
6761 const DNSHeader
* const hdr
= (DNSHeader
*) context
->msgBuf
;
6762 unsigned int rrCount
, i
;
6763 uint16_t type
, class;
6764 uint8_t name
[ kDomainNameLengthMax
];
6766 err
= DNSMessageGetAnswerSection( context
->msgBuf
, msgLen
, &ptr
);
6767 require_noerr( err
, exit
);
6769 if( context
->qname
[ 0 ] == 0 )
6771 err
= DomainNameAppendString( context
->qname
, context
->qnameStr
, NULL
);
6772 require_noerr( err
, exit
);
6775 rrCount
= DNSHeaderGetAnswerCount( hdr
) + DNSHeaderGetAuthorityCount( hdr
) + DNSHeaderGetAdditionalCount( hdr
);
6776 for( i
= 0; i
< rrCount
; ++i
)
6778 err
= DNSMessageExtractRecord( context
->msgBuf
, msgLen
, ptr
, name
, &type
, &class, NULL
, NULL
, NULL
, &ptr
);
6779 require_noerr( err
, exit
);
6781 if( ( ( context
->qtype
== kDNSServiceType_ANY
) || ( type
== context
->qtype
) ) &&
6782 DomainNameEqual( name
, context
->qname
) )
6789 if( context
->allResponses
|| foundAnswer
)
6791 FPrintF( stdout
, "---\n" );
6792 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
6793 FPrintF( stdout
, "Source: %##a\n", &fromAddr
);
6794 FPrintF( stdout
, "Message size: %zu\n\n%#.*{du:dnsmsg}",
6795 msgLen
, context
->printRawRData
? 1 : 0, context
->msgBuf
, msgLen
);
6799 if( err
) exit( 1 );
6802 //===========================================================================================================================
6804 //===========================================================================================================================
6806 static void PIDToUUIDCmd( void )
6810 struct proc_uniqidentifierinfo info
;
6812 n
= proc_pidinfo( gPIDToUUID_PID
, PROC_PIDUNIQIDENTIFIERINFO
, 1, &info
, sizeof( info
) );
6813 require_action_quiet( n
== (int) sizeof( info
), exit
, err
= kUnknownErr
);
6815 FPrintF( stdout
, "%#U\n", info
.p_uuid
);
6819 if( err
) exit( 1 );
6822 //===========================================================================================================================
6824 //===========================================================================================================================
6826 typedef struct DNSServerPrivate
* DNSServerRef
;
6830 DNSServerRef server
; // Reference to the DNS server.
6831 dispatch_source_t sigIntSource
; // Dispatch SIGINT source.
6832 dispatch_source_t sigTermSource
; // Dispatch SIGTERM source.
6833 const char * domainOverride
; // If non-NULL, the server is to use this domain instead of "d.test.".
6834 #if( TARGET_OS_DARWIN )
6835 dispatch_source_t processMonitor
; // Process monitor source for process being followed, if any.
6836 pid_t followPID
; // PID of process being followed, if any. (If it exits, we exit).
6837 Boolean addedResolver
; // True if system DNS settings contains a resolver entry for server.
6839 Boolean loopbackOnly
; // True if the server should be bound to the loopback interface.
6841 } DNSServerCmdContext
;
6845 kDNSServerEvent_Started
= 1,
6846 kDNSServerEvent_Stopped
= 2
6848 } DNSServerEventType
;
6850 typedef void ( *DNSServerEventHandler_f
)( DNSServerEventType inType
, uintptr_t inEventData
, void *inContext
);
6852 CFTypeID
DNSServerGetTypeID( void );
6855 dispatch_queue_t inQueue
,
6856 DNSServerEventHandler_f inEventHandler
,
6857 void * inEventContext
,
6858 unsigned int inResponseDelayMs
,
6859 uint32_t inDefaultTTL
,
6861 Boolean inLoopbackOnly
,
6862 const char * inDomain
,
6863 Boolean inBadUDPMode
,
6864 DNSServerRef
* outServer
);
6865 static void DNSServerStart( DNSServerRef inServer
);
6866 static void DNSServerStop( DNSServerRef inServer
);
6868 #define ForgetDNSServer( X ) ForgetCustomEx( X, DNSServerStop, CFRelease )
6870 static void DNSServerCmdContextFree( DNSServerCmdContext
*inContext
);
6871 static void DNSServerCmdEventHandler( DNSServerEventType inType
, uintptr_t inEventData
, void *inContext
);
6872 static void DNSServerCmdSigIntHandler( void *inContext
);
6873 static void DNSServerCmdSigTermHandler( void *inContext
);
6874 #if( TARGET_OS_DARWIN )
6875 static void DNSServerCmdFollowedProcessHandler( void *inContext
);
6878 ulog_define_ex( kDNSSDUtilIdentifier
, DNSServer
, kLogLevelInfo
, kLogFlags_None
, "DNSServer", NULL
);
6879 #define ds_ulog( LEVEL, ... ) ulog( &log_category_from_name( DNSServer ), (LEVEL), __VA_ARGS__ )
6881 static void DNSServerCmd( void )
6884 DNSServerCmdContext
* context
= NULL
;
6886 if( gDNSServer_Foreground
)
6888 LogControl( "DNSServer:output=file;stdout,DNSServer:flags=time;prefix" );
6891 err
= CheckIntegerArgument( gDNSServer_ResponseDelayMs
, "response delay (ms)", 0, INT_MAX
);
6892 require_noerr_quiet( err
, exit
);
6894 err
= CheckIntegerArgument( gDNSServer_DefaultTTL
, "default TTL", 0, INT32_MAX
);
6895 require_noerr_quiet( err
, exit
);
6897 err
= CheckIntegerArgument( gDNSServer_Port
, "port number", -UINT16_MAX
, UINT16_MAX
);
6898 require_noerr_quiet( err
, exit
);
6900 context
= (DNSServerCmdContext
*) calloc( 1, sizeof( *context
) );
6901 require_action( context
, exit
, err
= kNoMemoryErr
);
6903 context
->domainOverride
= gDNSServer_DomainOverride
;
6904 context
->loopbackOnly
= gDNSServer_LoopbackOnly
? true : false;
6906 #if( TARGET_OS_DARWIN )
6907 if( gDNSServer_FollowPID
)
6909 context
->followPID
= _StringToPID( gDNSServer_FollowPID
, &err
);
6910 if( err
|| ( context
->followPID
< 0 ) )
6912 FPrintF( stderr
, "error: Invalid follow PID: %s\n", gDNSServer_FollowPID
);
6917 err
= DispatchProcessMonitorCreate( context
->followPID
, DISPATCH_PROC_EXIT
, dispatch_get_main_queue(),
6918 DNSServerCmdFollowedProcessHandler
, NULL
, context
, &context
->processMonitor
);
6919 require_noerr( err
, exit
);
6920 dispatch_resume( context
->processMonitor
);
6924 context
->followPID
= -1;
6928 signal( SIGINT
, SIG_IGN
);
6929 err
= DispatchSignalSourceCreate( SIGINT
, DNSServerCmdSigIntHandler
, context
, &context
->sigIntSource
);
6930 require_noerr( err
, exit
);
6931 dispatch_resume( context
->sigIntSource
);
6933 signal( SIGTERM
, SIG_IGN
);
6934 err
= DispatchSignalSourceCreate( SIGTERM
, DNSServerCmdSigTermHandler
, context
, &context
->sigTermSource
);
6935 require_noerr( err
, exit
);
6936 dispatch_resume( context
->sigTermSource
);
6938 err
= DNSServerCreate( dispatch_get_main_queue(), DNSServerCmdEventHandler
, context
,
6939 (unsigned int) gDNSServer_ResponseDelayMs
, (uint32_t) gDNSServer_DefaultTTL
, gDNSServer_Port
, context
->loopbackOnly
,
6940 context
->domainOverride
, gDNSServer_BadUDPMode
? true : false, &context
->server
);
6941 require_noerr( err
, exit
);
6943 DNSServerStart( context
->server
);
6947 FPrintF( stderr
, "Failed to start DNS server: %#m\n", err
);
6948 if( context
) DNSServerCmdContextFree( context
);
6949 if( err
) exit( 1 );
6952 //===========================================================================================================================
6953 // DNSServerCmdContextFree
6954 //===========================================================================================================================
6956 static void DNSServerCmdContextFree( DNSServerCmdContext
*inContext
)
6958 ForgetCF( &inContext
->server
);
6959 dispatch_source_forget( &inContext
->sigIntSource
);
6960 dispatch_source_forget( &inContext
->sigTermSource
);
6961 #if( TARGET_OS_DARWIN )
6962 dispatch_source_forget( &inContext
->processMonitor
);
6967 //===========================================================================================================================
6968 // DNSServerCmdEventHandler
6969 //===========================================================================================================================
6971 #if( TARGET_OS_DARWIN )
6972 static OSStatus
_DNSServerCmdLoopbackResolverAdd( const char *inDomain
, int inPort
);
6973 static OSStatus
_DNSServerCmdLoopbackResolverRemove( void );
6976 static void DNSServerCmdEventHandler( DNSServerEventType inType
, uintptr_t inEventData
, void *inContext
)
6979 DNSServerCmdContext
* const context
= (DNSServerCmdContext
*) inContext
;
6981 if( inType
== kDNSServerEvent_Started
)
6983 #if( TARGET_OS_DARWIN )
6984 const int port
= (int) inEventData
;
6986 err
= _DNSServerCmdLoopbackResolverAdd( context
->domainOverride
? context
->domainOverride
: "d.test.", port
);
6989 ds_ulog( kLogLevelError
, "Failed to add loopback resolver to DNS configuration for \"d.test.\" domain: %#m\n",
6991 if( context
->loopbackOnly
) ForgetDNSServer( &context
->server
);
6995 context
->addedResolver
= true;
6999 else if( inType
== kDNSServerEvent_Stopped
)
7001 const OSStatus stopError
= (OSStatus
) inEventData
;
7003 if( stopError
) ds_ulog( kLogLevelError
, "The server stopped unexpectedly with error: %#m.\n", stopError
);
7006 #if( TARGET_OS_DARWIN )
7007 if( context
->addedResolver
)
7009 err
= _DNSServerCmdLoopbackResolverRemove();
7012 ds_ulog( kLogLevelError
, "Failed to remove loopback resolver from DNS configuration: %#m\n", err
);
7016 context
->addedResolver
= false;
7019 else if( context
->loopbackOnly
)
7024 DNSServerCmdContextFree( context
);
7025 exit( ( stopError
|| err
) ? 1 : 0 );
7029 #if( TARGET_OS_DARWIN )
7030 //===========================================================================================================================
7031 // _DNSServerCmdLoopbackResolverAdd
7032 //===========================================================================================================================
7034 static OSStatus
_DNSServerCmdLoopbackResolverAdd( const char *inDomain
, int inPort
)
7037 SCDynamicStoreRef store
;
7038 CFPropertyListRef plist
= NULL
;
7039 CFStringRef key
= NULL
;
7040 const uint32_t loopbackV4
= htonl( INADDR_LOOPBACK
);
7043 store
= SCDynamicStoreCreate( NULL
, CFSTR( kDNSSDUtilIdentifier
), NULL
, NULL
);
7044 err
= map_scerror( store
);
7045 require_noerr( err
, exit
);
7047 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
7062 kSCPropNetDNSSupplementalMatchDomains
, inDomain
,
7063 kSCPropNetDNSServerAddresses
, &loopbackV4
, in6addr_loopback
.s6_addr
,
7064 kSCPropNetDNSServerPort
, inPort
,
7065 kSCPropInterfaceName
, CFSTR( "lo0" ),
7066 kSCPropNetDNSConfirmedServiceID
, CFSTR( "com.apple.dnssdutil.server" ) );
7067 require_noerr( err
, exit
);
7069 key
= SCDynamicStoreKeyCreateNetworkServiceEntity( NULL
, kSCDynamicStoreDomainState
,
7070 CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS
);
7071 require_action( key
, exit
, err
= kUnknownErr
);
7073 success
= SCDynamicStoreSetValue( store
, key
, plist
);
7074 require_action( success
, exit
, err
= kUnknownErr
);
7077 CFReleaseNullSafe( store
);
7078 CFReleaseNullSafe( plist
);
7079 CFReleaseNullSafe( key
);
7083 //===========================================================================================================================
7084 // _DNSServerCmdLoopbackResolverRemove
7085 //===========================================================================================================================
7087 static OSStatus
_DNSServerCmdLoopbackResolverRemove( void )
7090 SCDynamicStoreRef store
;
7091 CFStringRef key
= NULL
;
7094 store
= SCDynamicStoreCreate( NULL
, CFSTR( kDNSSDUtilIdentifier
), NULL
, NULL
);
7095 err
= map_scerror( store
);
7096 require_noerr( err
, exit
);
7098 key
= SCDynamicStoreKeyCreateNetworkServiceEntity( NULL
, kSCDynamicStoreDomainState
,
7099 CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS
);
7100 require_action( key
, exit
, err
= kUnknownErr
);
7102 success
= SCDynamicStoreRemoveValue( store
, key
);
7103 require_action( success
, exit
, err
= kUnknownErr
);
7106 CFReleaseNullSafe( store
);
7107 CFReleaseNullSafe( key
);
7112 //===========================================================================================================================
7113 // DNSServerCmdSigIntHandler
7114 //===========================================================================================================================
7116 static void _DNSServerCmdShutdown( DNSServerCmdContext
*inContext
, int inSignal
);
7118 static void DNSServerCmdSigIntHandler( void *inContext
)
7120 _DNSServerCmdShutdown( (DNSServerCmdContext
*) inContext
, SIGINT
);
7123 //===========================================================================================================================
7124 // DNSServerCmdSigTermHandler
7125 //===========================================================================================================================
7127 static void DNSServerCmdSigTermHandler( void *inContext
)
7129 _DNSServerCmdShutdown( (DNSServerCmdContext
*) inContext
, SIGTERM
);
7132 #if( TARGET_OS_DARWIN )
7133 //===========================================================================================================================
7134 // DNSServerCmdFollowedProcessHandler
7135 //===========================================================================================================================
7137 static void DNSServerCmdFollowedProcessHandler( void *inContext
)
7139 DNSServerCmdContext
* const context
= (DNSServerCmdContext
*) inContext
;
7141 if( dispatch_source_get_data( context
->processMonitor
) & DISPATCH_PROC_EXIT
) _DNSServerCmdShutdown( context
, 0 );
7145 //===========================================================================================================================
7146 // _DNSServerCmdExternalExit
7147 //===========================================================================================================================
7149 #define SignalNumberToString( X ) ( \
7150 ( (X) == SIGINT ) ? "SIGINT" : \
7151 ( (X) == SIGTERM ) ? "SIGTERM" : \
7154 static void _DNSServerCmdShutdown( DNSServerCmdContext
*inContext
, int inSignal
)
7156 dispatch_source_forget( &inContext
->sigIntSource
);
7157 dispatch_source_forget( &inContext
->sigTermSource
);
7158 #if( TARGET_OS_DARWIN )
7159 dispatch_source_forget( &inContext
->processMonitor
);
7163 ds_ulog( kLogLevelNotice
, "Exiting: followed process (%lld) exited\n", (int64_t) inContext
->followPID
);
7168 ds_ulog( kLogLevelNotice
, "Exiting: received signal %d (%s)\n", inSignal
, SignalNumberToString( inSignal
) );
7171 ForgetDNSServer( &inContext
->server
);
7174 //===========================================================================================================================
7176 //===========================================================================================================================
7178 #define kDDotTestDomainName (const uint8_t *) "\x01" "d" "\x04" "test"
7180 typedef struct DNSDelayedResponse DNSDelayedResponse
;
7181 struct DNSDelayedResponse
7183 DNSDelayedResponse
* next
;
7184 sockaddr_ip destAddr
;
7185 uint64_t targetTicks
;
7190 struct DNSServerPrivate
7192 CFRuntimeBase base
; // CF object base.
7193 uint8_t * domain
; // Parent domain of server's resource records.
7194 dispatch_queue_t queue
; // Queue for DNS server's events.
7195 dispatch_source_t readSourceUDPv4
; // Read source for IPv4 UDP socket.
7196 dispatch_source_t readSourceUDPv6
; // Read source for IPv6 UDP socket.
7197 dispatch_source_t readSourceTCPv4
; // Read source for IPv4 TCP socket.
7198 dispatch_source_t readSourceTCPv6
; // Read source for IPv6 TCP socket.
7199 SocketRef sockUDPv4
;
7200 SocketRef sockUDPv6
;
7201 DNSServerEventHandler_f eventHandler
;
7202 void * eventContext
;
7203 DNSDelayedResponse
* responseList
;
7204 dispatch_source_t responseTimer
;
7205 unsigned int responseDelayMs
;
7206 uint32_t defaultTTL
;
7207 uint32_t serial
; // Serial number for SOA record.
7208 int port
; // Port to use for receiving and sending DNS messages.
7211 Boolean loopbackOnly
;
7212 Boolean badUDPMode
; // True if the server runs in Bad UDP mode.
7215 static void _DNSServerUDPReadHandler( void *inContext
);
7216 static void _DNSServerTCPReadHandler( void *inContext
);
7217 static void _DNSDelayedResponseFree( DNSDelayedResponse
*inResponse
);
7218 static void _DNSDelayedResponseFreeList( DNSDelayedResponse
*inList
);
7220 CF_CLASS_DEFINE( DNSServer
);
7224 dispatch_queue_t inQueue
,
7225 DNSServerEventHandler_f inEventHandler
,
7226 void * inEventContext
,
7227 unsigned int inResponseDelayMs
,
7228 uint32_t inDefaultTTL
,
7230 Boolean inLoopbackOnly
,
7231 const char * inDomain
,
7232 Boolean inBadUDPMode
,
7233 DNSServerRef
* outServer
)
7236 DNSServerRef obj
= NULL
;
7238 require_action_quiet( inDefaultTTL
<= INT32_MAX
, exit
, err
= kRangeErr
);
7240 CF_OBJECT_CREATE( DNSServer
, obj
, err
, exit
);
7242 ReplaceDispatchQueue( &obj
->queue
, inQueue
);
7243 obj
->eventHandler
= inEventHandler
;
7244 obj
->eventContext
= inEventContext
;
7245 obj
->responseDelayMs
= inResponseDelayMs
;
7246 obj
->defaultTTL
= inDefaultTTL
;
7248 obj
->loopbackOnly
= inLoopbackOnly
;
7249 obj
->badUDPMode
= inBadUDPMode
;
7253 err
= StringToDomainName( inDomain
, &obj
->domain
, NULL
);
7254 require_noerr_quiet( err
, exit
);
7258 err
= DomainNameDup( kDDotTestDomainName
, &obj
->domain
, NULL
);
7259 require_noerr_quiet( err
, exit
);
7267 CFReleaseNullSafe( obj
);
7271 //===========================================================================================================================
7272 // _DNSServerFinalize
7273 //===========================================================================================================================
7275 static void _DNSServerFinalize( CFTypeRef inObj
)
7277 DNSServerRef
const me
= (DNSServerRef
) inObj
;
7279 check( !me
->readSourceUDPv4
);
7280 check( !me
->readSourceUDPv6
);
7281 check( !me
->readSourceTCPv4
);
7282 check( !me
->readSourceTCPv6
);
7283 check( !me
->responseTimer
);
7284 ForgetMem( &me
->domain
);
7285 dispatch_forget( &me
->queue
);
7288 //===========================================================================================================================
7290 //===========================================================================================================================
7292 static void _DNSServerStart( void *inContext
);
7293 static void _DNSServerStop( void *inContext
, OSStatus inError
);
7295 static void DNSServerStart( DNSServerRef me
)
7298 dispatch_async_f( me
->queue
, me
, _DNSServerStart
);
7301 static void _DNSServerStart( void *inContext
)
7305 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7306 SocketRef sock
= kInvalidSocketRef
;
7307 SocketContext
* sockCtx
= NULL
;
7308 const uint32_t loopbackV4
= htonl( INADDR_LOOPBACK
);
7309 int year
, month
, day
;
7311 // Create IPv4 UDP socket.
7312 // Initially, me->port is the port requested by the user. If it's 0, then the user wants any available ephemeral port.
7313 // If it's negative, then the user would like a port number equal to its absolute value, but will settle for any
7314 // available ephemeral port, if it's not available. The actual port number that was used will be stored in me->port and
7315 // used for the remaining sockets.
7317 err
= _ServerSocketOpenEx2( AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
, me
->loopbackOnly
? &loopbackV4
: NULL
,
7318 me
->port
, &me
->port
, kSocketBufferSize_DontSet
, me
->loopbackOnly
? true : false, &sock
);
7319 require_noerr( err
, exit
);
7320 check( me
->port
> 0 );
7322 // Create read source for IPv4 UDP socket.
7324 err
= SocketContextCreate( sock
, me
, &sockCtx
);
7325 require_noerr( err
, exit
);
7326 sock
= kInvalidSocketRef
;
7328 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _DNSServerUDPReadHandler
, SocketContextCancelHandler
, sockCtx
,
7329 &me
->readSourceUDPv4
);
7330 require_noerr( err
, exit
);
7331 dispatch_resume( me
->readSourceUDPv4
);
7332 me
->sockUDPv4
= sockCtx
->sock
;
7335 // Create IPv6 UDP socket.
7337 err
= _ServerSocketOpenEx2( AF_INET6
, SOCK_DGRAM
, IPPROTO_UDP
, me
->loopbackOnly
? &in6addr_loopback
: NULL
,
7338 me
->port
, NULL
, kSocketBufferSize_DontSet
, me
->loopbackOnly
? true : false, &sock
);
7339 require_noerr( err
, exit
);
7341 // Create read source for IPv6 UDP socket.
7343 err
= SocketContextCreate( sock
, me
, &sockCtx
);
7344 require_noerr( err
, exit
);
7345 sock
= kInvalidSocketRef
;
7347 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _DNSServerUDPReadHandler
, SocketContextCancelHandler
, sockCtx
,
7348 &me
->readSourceUDPv6
);
7349 require_noerr( err
, exit
);
7350 dispatch_resume( me
->readSourceUDPv6
);
7351 me
->sockUDPv6
= sockCtx
->sock
;
7354 // Create IPv4 TCP socket.
7356 err
= _ServerSocketOpenEx2( AF_INET
, SOCK_STREAM
, IPPROTO_TCP
, me
->loopbackOnly
? &loopbackV4
: NULL
,
7357 me
->port
, NULL
, kSocketBufferSize_DontSet
, false, &sock
);
7358 require_noerr( err
, exit
);
7360 // Create read source for IPv4 TCP socket.
7362 err
= SocketContextCreate( sock
, me
, &sockCtx
);
7363 require_noerr( err
, exit
);
7364 sock
= kInvalidSocketRef
;
7366 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _DNSServerTCPReadHandler
, SocketContextCancelHandler
, sockCtx
,
7367 &me
->readSourceTCPv4
);
7368 require_noerr( err
, exit
);
7369 dispatch_resume( me
->readSourceTCPv4
);
7372 // Create IPv6 TCP socket.
7374 err
= _ServerSocketOpenEx2( AF_INET6
, SOCK_STREAM
, IPPROTO_TCP
, me
->loopbackOnly
? &in6addr_loopback
: NULL
,
7375 me
->port
, NULL
, kSocketBufferSize_DontSet
, false, &sock
);
7376 require_noerr( err
, exit
);
7378 // Create read source for IPv6 TCP socket.
7380 err
= SocketContextCreate( sock
, me
, &sockCtx
);
7381 require_noerr( err
, exit
);
7382 sock
= kInvalidSocketRef
;
7384 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _DNSServerTCPReadHandler
, SocketContextCancelHandler
, sockCtx
,
7385 &me
->readSourceTCPv6
);
7386 require_noerr( err
, exit
);
7387 dispatch_resume( me
->readSourceTCPv6
);
7390 ds_ulog( kLogLevelInfo
, "Server is using port %d.\n", me
->port
);
7391 if( me
->eventHandler
) me
->eventHandler( kDNSServerEvent_Started
, (uintptr_t) me
->port
, me
->eventContext
);
7393 // Create the serial number for the server's SOA record in the YYYMMDDnn convention recommended by
7394 // <https://tools.ietf.org/html/rfc1912#section-2.2> using the current time.
7396 gettimeofday( &now
, NULL
);
7397 SecondsToYMD_HMS( ( INT64_C_safe( kDaysToUnixEpoch
) * kSecondsPerDay
) + now
.tv_sec
, &year
, &month
, &day
,
7399 me
->serial
= (uint32_t)( ( year
* 1000000 ) + ( month
* 10000 ) + ( day
* 100 ) + 1 );
7402 ForgetSocket( &sock
);
7403 if( sockCtx
) SocketContextRelease( sockCtx
);
7404 if( err
) _DNSServerStop( me
, err
);
7407 //===========================================================================================================================
7409 //===========================================================================================================================
7411 static void _DNSServerUserStop( void *inContext
);
7412 static void _DNSServerStop2( void *inContext
);
7414 static void DNSServerStop( DNSServerRef me
)
7417 dispatch_async_f( me
->queue
, me
, _DNSServerUserStop
);
7420 static void _DNSServerUserStop( void *inContext
)
7422 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7424 _DNSServerStop( me
, kNoErr
);
7428 static void _DNSServerStop( void *inContext
, OSStatus inError
)
7430 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7432 me
->stopError
= inError
;
7433 dispatch_source_forget( &me
->readSourceUDPv4
);
7434 dispatch_source_forget( &me
->readSourceUDPv6
);
7435 dispatch_source_forget( &me
->readSourceTCPv4
);
7436 dispatch_source_forget( &me
->readSourceTCPv6
);
7437 dispatch_source_forget( &me
->responseTimer
);
7438 me
->sockUDPv4
= kInvalidSocketRef
;
7439 me
->sockUDPv6
= kInvalidSocketRef
;
7441 if( me
->responseList
)
7443 _DNSDelayedResponseFreeList( me
->responseList
);
7444 me
->responseList
= NULL
;
7446 dispatch_async_f( me
->queue
, me
, _DNSServerStop2
);
7449 static void _DNSServerStop2( void *inContext
)
7451 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7456 if( me
->eventHandler
) me
->eventHandler( kDNSServerEvent_Stopped
, (uintptr_t) me
->stopError
, me
->eventContext
);
7462 //===========================================================================================================================
7463 // _DNSDelayedResponseFree
7464 //===========================================================================================================================
7466 static void _DNSDelayedResponseFree( DNSDelayedResponse
*inResponse
)
7468 ForgetMem( &inResponse
->msgPtr
);
7472 //===========================================================================================================================
7473 // _DNSDelayedResponseFreeList
7474 //===========================================================================================================================
7476 static void _DNSDelayedResponseFreeList( DNSDelayedResponse
*inList
)
7478 DNSDelayedResponse
* response
;
7480 while( ( response
= inList
) != NULL
)
7482 inList
= response
->next
;
7483 _DNSDelayedResponseFree( response
);
7487 //===========================================================================================================================
7488 // _DNSServerUDPReadHandler
7489 //===========================================================================================================================
7492 _DNSServerAnswerQuery(
7493 DNSServerRef inServer
,
7494 const uint8_t * inQueryPtr
,
7497 uint8_t ** outResponsePtr
,
7498 size_t * outResponseLen
);
7500 #define _DNSServerAnswerQueryForUDP( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
7501 _DNSServerAnswerQuery( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, false, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
7503 #define _DNSServerAnswerQueryForTCP( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
7504 _DNSServerAnswerQuery( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, true, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
7507 _DNSServerScheduleDelayedResponse(
7508 DNSServerRef inServer
,
7509 const struct sockaddr
* inDestAddr
,
7512 static void _DNSServerUDPDelayedSend( void *inContext
);
7514 static void _DNSServerUDPReadHandler( void *inContext
)
7517 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
7518 DNSServerRef
const me
= (DNSServerRef
) sockCtx
->userContext
;
7521 sockaddr_ip clientAddr
;
7522 socklen_t clientAddrLen
;
7523 uint8_t * responsePtr
= NULL
; // malloc'd
7527 gettimeofday( &now
, NULL
);
7531 clientAddrLen
= (socklen_t
) sizeof( clientAddr
);
7532 n
= recvfrom( sockCtx
->sock
, (char *) msg
, sizeof( msg
), 0, &clientAddr
.sa
, &clientAddrLen
);
7533 err
= map_socket_value_errno( sockCtx
->sock
, n
>= 0, n
);
7534 require_noerr( err
, exit
);
7536 ds_ulog( kLogLevelInfo
, "UDP server received %zd bytes from %##a at %{du:time}.\n", n
, &clientAddr
, &now
);
7538 if( n
< kDNSHeaderLength
)
7540 ds_ulog( kLogLevelInfo
, "UDP DNS message is too small (%zd < %d).\n", n
, kDNSHeaderLength
);
7544 ds_ulog( kLogLevelInfo
, "UDP received message:\n\n%1{du:dnsmsg}", msg
, (size_t) n
);
7548 err
= _DNSServerAnswerQueryForUDP( me
, msg
, (size_t) n
, &responsePtr
, &responseLen
);
7549 require_noerr_quiet( err
, exit
);
7551 // Schedule response.
7553 if( me
->responseDelayMs
> 0 )
7555 err
= _DNSServerScheduleDelayedResponse( me
, &clientAddr
.sa
, responsePtr
, responseLen
);
7556 require_noerr( err
, exit
);
7561 ds_ulog( kLogLevelInfo
, "UDP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen
, responsePtr
, responseLen
);
7563 n
= sendto( sockCtx
->sock
, (char *) responsePtr
, responseLen
, 0, &clientAddr
.sa
, clientAddrLen
);
7564 err
= map_socket_value_errno( sockCtx
->sock
, n
== (ssize_t
) responseLen
, n
);
7565 require_noerr( err
, exit
);
7569 FreeNullSafe( responsePtr
);
7574 _DNSServerScheduleDelayedResponse(
7576 const struct sockaddr
* inDestAddr
,
7581 DNSDelayedResponse
* response
;
7582 DNSDelayedResponse
** responsePtr
;
7583 DNSDelayedResponse
* newResponse
;
7584 uint64_t targetTicks
;
7586 targetTicks
= UpTicks() + MillisecondsToUpTicks( me
->responseDelayMs
);
7588 newResponse
= (DNSDelayedResponse
*) calloc( 1, sizeof( *newResponse
) );
7589 require_action( newResponse
, exit
, err
= kNoMemoryErr
);
7591 if( !me
->responseList
|| ( targetTicks
< me
->responseList
->targetTicks
) )
7593 dispatch_source_forget( &me
->responseTimer
);
7595 err
= DispatchTimerCreate( dispatch_time_milliseconds( me
->responseDelayMs
), DISPATCH_TIME_FOREVER
,
7596 ( (uint64_t) me
->responseDelayMs
) * kNanosecondsPerMillisecond
/ 10, me
->queue
, _DNSServerUDPDelayedSend
,
7597 NULL
, me
, &me
->responseTimer
);
7598 require_noerr( err
, exit
);
7599 dispatch_resume( me
->responseTimer
);
7602 SockAddrCopy( inDestAddr
, &newResponse
->destAddr
);
7603 newResponse
->targetTicks
= targetTicks
;
7604 newResponse
->msgPtr
= inMsgPtr
;
7605 newResponse
->msgLen
= inMsgLen
;
7607 for( responsePtr
= &me
->responseList
; ( response
= *responsePtr
) != NULL
; responsePtr
= &response
->next
)
7609 if( newResponse
->targetTicks
< response
->targetTicks
) break;
7611 newResponse
->next
= response
;
7612 *responsePtr
= newResponse
;
7617 if( newResponse
) _DNSDelayedResponseFree( newResponse
);
7621 static void _DNSServerUDPDelayedSend( void *inContext
)
7624 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7625 DNSDelayedResponse
* response
;
7629 uint64_t remainingNs
;
7630 DNSDelayedResponse
* freeList
= NULL
;
7632 dispatch_source_forget( &me
->responseTimer
);
7634 nowTicks
= UpTicks();
7635 while( ( ( response
= me
->responseList
) != NULL
) && ( response
->targetTicks
<= nowTicks
) )
7637 me
->responseList
= response
->next
;
7639 ds_ulog( kLogLevelInfo
, "UDP sending %zu byte response (delayed):\n\n%1{du:dnsmsg}",
7640 response
->msgLen
, response
->msgPtr
, response
->msgLen
);
7642 sock
= ( response
->destAddr
.sa
.sa_family
== AF_INET
) ? me
->sockUDPv4
: me
->sockUDPv6
;
7643 n
= sendto( sock
, (char *) response
->msgPtr
, response
->msgLen
, 0, &response
->destAddr
.sa
,
7644 SockAddrGetSize( &response
->destAddr
) );
7645 err
= map_socket_value_errno( sock
, n
== (ssize_t
) response
->msgLen
, n
);
7648 response
->next
= freeList
;
7649 freeList
= response
;
7650 nowTicks
= UpTicks();
7655 check( response
->targetTicks
> nowTicks
);
7656 remainingNs
= UpTicksToNanoseconds( response
->targetTicks
- nowTicks
);
7657 if( remainingNs
> INT64_MAX
) remainingNs
= INT64_MAX
;
7659 err
= DispatchTimerCreate( dispatch_time( DISPATCH_TIME_NOW
, (int64_t) remainingNs
), DISPATCH_TIME_FOREVER
, 0,
7660 me
->queue
, _DNSServerUDPDelayedSend
, NULL
, me
, &me
->responseTimer
);
7661 require_noerr( err
, exit
);
7662 dispatch_resume( me
->responseTimer
);
7666 if( freeList
) _DNSDelayedResponseFreeList( freeList
);
7669 //===========================================================================================================================
7670 // _DNSServerAnswerQuery
7671 //===========================================================================================================================
7673 #define kLabelPrefix_Alias "alias"
7674 #define kLabelPrefix_AliasTTL "alias-ttl"
7675 #define kLabelPrefix_Count "count-"
7676 #define kLabelPrefix_Tag "tag-"
7677 #define kLabelPrefix_TTL "ttl-"
7678 #define kLabel_IPv4 "ipv4"
7679 #define kLabel_IPv6 "ipv6"
7680 #define kLabelPrefix_SRV "srv-"
7682 #define kMaxAliasTTLCount ( ( kDomainLabelLengthMax - sizeof_string( kLabelPrefix_AliasTTL ) ) / 2 )
7683 #define kMaxParsedSRVCount ( kDomainNameLengthMax / ( 1 + sizeof_string( kLabelPrefix_SRV ) + 5 ) )
7687 uint16_t priority
; // Priority from SRV label.
7688 uint16_t weight
; // Weight from SRV label.
7689 uint16_t port
; // Port number from SRV label.
7690 uint16_t targetLen
; // Total length of the target hostname labels that follow an SRV label.
7691 const uint8_t * targetPtr
; // Pointer to the target hostname embedded in a domain name.
7696 _DNSServerInitializeResponseMessage(
7699 unsigned int inFlags
,
7700 const uint8_t * inQName
,
7701 unsigned int inQType
,
7702 unsigned int inQClass
);
7704 _DNSServerAnswerQueryDynamically(
7705 DNSServerRef inServer
,
7706 const uint8_t * inQName
,
7707 unsigned int inQType
,
7708 unsigned int inQClass
,
7710 DataBuffer
* inDB
);
7712 _DNSServerNameIsSRVName(
7713 DNSServerRef inServer
,
7714 const uint8_t * inName
,
7715 const uint8_t ** outDomainPtr
,
7716 size_t * outDomainLen
,
7717 ParsedSRV inSRVArray
[ kMaxParsedSRVCount
],
7718 size_t * outSRVCount
);
7720 _DNSServerNameIsHostname(
7721 DNSServerRef inServer
,
7722 const uint8_t * inName
,
7723 uint32_t * outAliasCount
,
7724 uint32_t inAliasTTLs
[ kMaxAliasTTLCount
],
7725 size_t * outAliasTTLCount
,
7726 unsigned int * outCount
,
7727 unsigned int * outRandCount
,
7730 Boolean
* outHasAAAA
,
7731 Boolean
* outHasSOA
);
7734 _DNSServerAnswerQuery(
7736 const uint8_t * const inQueryPtr
,
7737 const size_t inQueryLen
,
7739 uint8_t ** outResponsePtr
,
7740 size_t * outResponseLen
)
7744 const uint8_t * ptr
;
7745 const uint8_t * const queryEnd
= &inQueryPtr
[ inQueryLen
];
7746 const DNSHeader
* qhdr
;
7747 const dns_fixed_fields_question
* fields
;
7748 unsigned int msgID
, qflags
, qtype
, qclass
, rflags
;
7749 uint8_t qname
[ kDomainNameLengthMax
];
7751 DataBuffer_Init( &dataBuf
, NULL
, 0, kDNSMaxTCPMessageSize
);
7753 require_action_quiet( inQueryLen
>= kDNSHeaderLength
, exit
, err
= kUnderrunErr
);
7755 qhdr
= (const DNSHeader
*) inQueryPtr
;
7756 msgID
= DNSHeaderGetID( qhdr
);
7757 qflags
= DNSHeaderGetFlags( qhdr
);
7759 // Minimal checking of the query message's header.
7761 if( ( qflags
& kDNSHeaderFlag_Response
) || // The message must be a query, not a response.
7762 ( DNSFlagsGetOpCode( qflags
) != kDNSOpCode_Query
) || // OPCODE must be QUERY (standard query).
7763 ( DNSHeaderGetQuestionCount( qhdr
) != 1 ) ) // There should be a single question.
7771 ptr
= (const uint8_t *) &qhdr
[ 1 ];
7772 err
= DNSMessageExtractDomainName( inQueryPtr
, inQueryLen
, ptr
, qname
, &ptr
);
7773 require_noerr( err
, exit
);
7775 // Get QTYPE and QCLASS.
7777 require_action_quiet( ( (size_t)( queryEnd
- ptr
) ) >= sizeof( *fields
), exit
, err
= kUnderrunErr
);
7778 fields
= (const dns_fixed_fields_question
*) ptr
;
7779 qtype
= dns_fixed_fields_question_get_type( fields
);
7780 qclass
= dns_fixed_fields_question_get_class( fields
);
7782 // Create a tentative response message.
7784 rflags
= kDNSHeaderFlag_Response
;
7785 if( qflags
& kDNSHeaderFlag_RecursionDesired
) rflags
|= kDNSHeaderFlag_RecursionDesired
;
7786 DNSFlagsSetOpCode( rflags
, kDNSOpCode_Query
);
7788 if( me
->badUDPMode
&& !inForTCP
) msgID
= (uint16_t)( msgID
+ 1 );
7789 err
= _DNSServerInitializeResponseMessage( &dataBuf
, msgID
, rflags
, qname
, qtype
, qclass
);
7790 require_noerr( err
, exit
);
7792 err
= _DNSServerAnswerQueryDynamically( me
, qname
, qtype
, qclass
, inForTCP
, &dataBuf
);
7795 DNSFlagsSetRCode( rflags
, kDNSRCode_ServerFailure
);
7796 err
= _DNSServerInitializeResponseMessage( &dataBuf
, msgID
, rflags
, qname
, qtype
, qclass
);
7797 require_noerr( err
, exit
);
7800 err
= DataBuffer_Detach( &dataBuf
, outResponsePtr
, outResponseLen
);
7801 require_noerr( err
, exit
);
7804 DataBuffer_Free( &dataBuf
);
7809 _DNSServerInitializeResponseMessage(
7812 unsigned int inFlags
,
7813 const uint8_t * inQName
,
7814 unsigned int inQType
,
7815 unsigned int inQClass
)
7820 DataBuffer_Reset( inDB
);
7822 memset( &header
, 0, sizeof( header
) );
7823 DNSHeaderSetID( &header
, inID
);
7824 DNSHeaderSetFlags( &header
, inFlags
);
7825 DNSHeaderSetQuestionCount( &header
, 1 );
7827 err
= DataBuffer_Append( inDB
, &header
, sizeof( header
) );
7828 require_noerr( err
, exit
);
7830 err
= _DataBuffer_AppendDNSQuestion( inDB
, inQName
, DomainNameLength( inQName
), (uint16_t) inQType
,
7831 (uint16_t) inQClass
);
7832 require_noerr( err
, exit
);
7839 _DNSServerAnswerQueryDynamically(
7841 const uint8_t * const inQName
,
7842 const unsigned int inQType
,
7843 const unsigned int inQClass
,
7844 const Boolean inForTCP
,
7845 DataBuffer
* const inDB
)
7849 unsigned int flags
, rcode
;
7850 uint32_t aliasCount
, i
;
7851 uint32_t aliasTTLs
[ kMaxAliasTTLCount
];
7852 size_t aliasTTLCount
;
7853 unsigned int addrCount
, randCount
;
7855 ParsedSRV srvArray
[ kMaxParsedSRVCount
];
7857 const uint8_t * srvDomainPtr
;
7858 size_t srvDomainLen
;
7859 unsigned int answerCount
;
7860 Boolean notImplemented
, truncated
;
7861 Boolean useAliasTTLs
, nameExists
, nameHasA
, nameHasAAAA
, nameHasSRV
, nameHasSOA
;
7862 uint8_t namePtr
[ 2 ];
7863 dns_fixed_fields_record fields
;
7868 require_action_quiet( inQClass
== kDNSServiceClass_IN
, done
, notImplemented
= true );
7870 notImplemented
= false;
7873 nameHasAAAA
= false;
7875 useAliasTTLs
= false;
7880 if( _DNSServerNameIsHostname( me
, inQName
, &aliasCount
, aliasTTLs
, &aliasTTLCount
, &addrCount
, &randCount
, &ttl
,
7881 &nameHasA
, &nameHasAAAA
, &nameHasSOA
) )
7883 check( !( ( aliasCount
> 0 ) && ( aliasTTLCount
> 0 ) ) );
7884 check( ( addrCount
>= 1 ) && ( addrCount
<= 255 ) );
7885 check( ( randCount
== 0 ) || ( ( randCount
>= addrCount
) && ( randCount
<= 255 ) ) );
7886 check( nameHasA
|| nameHasAAAA
);
7888 if( aliasTTLCount
> 0 )
7890 aliasCount
= (uint32_t) aliasTTLCount
;
7891 useAliasTTLs
= true;
7895 else if( _DNSServerNameIsSRVName( me
, inQName
, &srvDomainPtr
, &srvDomainLen
, srvArray
, &srvCount
) )
7900 require_quiet( nameExists
, done
);
7902 if( aliasCount
> 0 )
7905 uint8_t rdataLabel
[ 1 + kDomainLabelLengthMax
+ 1 ];
7907 // If aliasCount is non-zero, then the first label of QNAME is either "alias" or "alias-<N>". superPtr is a name
7908 // compression pointer to the second label of QNAME, i.e., the immediate superdomain name of QNAME. It's used for
7909 // the RDATA of CNAME records whose canonical name ends with the superdomain name. It may also be used to construct
7910 // CNAME record names, when the offset to the previous CNAME's RDATA doesn't fit in a compression pointer.
7912 const uint8_t superPtr
[ 2 ] = { 0xC0, (uint8_t)( kDNSHeaderLength
+ 1 + inQName
[ 0 ] ) };
7914 // The name of the first CNAME record is equal to QNAME, so nameOffset is set to offset of QNAME.
7916 nameOffset
= kDNSHeaderLength
;
7918 for( i
= aliasCount
; i
>= 1; --i
)
7924 uint8_t nameLabel
[ 1 + kDomainLabelLengthMax
];
7926 if( nameOffset
<= kDNSCompressionOffsetMax
)
7928 DNSMessageWriteLabelPointer( namePtr
, nameOffset
);
7929 nameLen
= sizeof( namePtr
);
7933 memcpy( nameLabel
, rdataLabel
, 1 + rdataLabel
[ 0 ] );
7934 nameLen
= 1 + nameLabel
[ 0 ] + sizeof( superPtr
);
7939 char * dst
= (char *) &rdataLabel
[ 1 ];
7940 char * const lim
= (char *) &rdataLabel
[ countof( rdataLabel
) ];
7944 err
= SNPrintF_Add( &dst
, lim
, kLabelPrefix_AliasTTL
);
7945 require_noerr( err
, exit
);
7947 for( j
= aliasCount
- ( i
- 1 ); j
< aliasCount
; ++j
)
7949 err
= SNPrintF_Add( &dst
, lim
, "-%u", aliasTTLs
[ j
] );
7950 require_noerr( err
, exit
);
7955 err
= SNPrintF_Add( &dst
, lim
, kLabelPrefix_Alias
"%?{end}-%u", i
== 2, i
- 1 );
7956 require_noerr( err
, exit
);
7958 rdataLabel
[ 0 ] = (uint8_t)( dst
- (char *) &rdataLabel
[ 1 ] );
7959 rdataLen
= 1 + rdataLabel
[ 0 ] + sizeof( superPtr
);
7963 rdataLen
= sizeof( superPtr
);
7968 size_t recordLen
= nameLen
+ sizeof( fields
) + rdataLen
;
7970 if( ( DataBuffer_GetLen( inDB
) + recordLen
) > kDNSMaxUDPMessageSize
)
7978 // Set CNAME record's NAME.
7980 if( nameOffset
<= kDNSCompressionOffsetMax
)
7982 err
= DataBuffer_Append( inDB
, namePtr
, sizeof( namePtr
) );
7983 require_noerr( err
, exit
);
7987 err
= DataBuffer_Append( inDB
, nameLabel
, 1 + nameLabel
[ 0 ] );
7988 require_noerr( err
, exit
);
7990 err
= DataBuffer_Append( inDB
, superPtr
, sizeof( superPtr
) );
7991 require_noerr( err
, exit
);
7994 // Set CNAME record's TYPE, CLASS, TTL, and RDLENGTH.
7996 aliasTTL
= useAliasTTLs
? aliasTTLs
[ aliasCount
- i
] : me
->defaultTTL
;
7997 dns_fixed_fields_record_init( &fields
, kDNSServiceType_CNAME
, kDNSServiceClass_IN
, aliasTTL
,
7998 (uint16_t) rdataLen
);
7999 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
8000 require_noerr( err
, exit
);
8002 // Save offset of CNAME record's RDATA, which may be used for the name of the next CNAME record.
8004 nameOffset
= DataBuffer_GetLen( inDB
);
8006 // Set CNAME record's RDATA.
8010 err
= DataBuffer_Append( inDB
, rdataLabel
, 1 + rdataLabel
[ 0 ] );
8011 require_noerr( err
, exit
);
8013 err
= DataBuffer_Append( inDB
, superPtr
, sizeof( superPtr
) );
8014 require_noerr( err
, exit
);
8017 namePtr
[ 0 ] = superPtr
[ 0 ];
8018 namePtr
[ 1 ] = superPtr
[ 1 ];
8022 // There are no aliases, so initialize the name compression pointer to point to QNAME.
8024 DNSMessageWriteLabelPointer( namePtr
, kDNSHeaderLength
);
8027 if( ( inQType
== kDNSServiceType_A
) || ( inQType
== kDNSServiceType_AAAA
) )
8029 uint8_t * lsb
; // Pointer to the least significant byte of record data.
8030 size_t recordLen
; // Length of the entire record.
8031 size_t rdataLen
; // Length of record's RDATA.
8032 uint8_t rdata
[ 16 ]; // A buffer that's big enough for either A or AAAA RDATA.
8033 uint8_t randIntegers
[ 255 ]; // Array for random integers in [1, 255].
8034 const int useBadAddrs
= ( me
->badUDPMode
&& !inForTCP
) ? true : false;
8036 if( inQType
== kDNSServiceType_A
)
8038 const uint32_t baseAddrV4
= useBadAddrs
? kDNSServerBadBaseAddrV4
: kDNSServerBaseAddrV4
;
8040 require_quiet( nameHasA
, done
);
8043 WriteBig32( rdata
, baseAddrV4
);
8048 const uint8_t * const baseAddrV6
= useBadAddrs
? kDNSServerBadBaseAddrV6
: kDNSServerBaseAddrV6
;
8050 require_quiet( nameHasAAAA
, done
);
8053 memcpy( rdata
, baseAddrV6
, 16 );
8059 // Populate the array with all integers between 1 and <randCount>, inclusive.
8061 for( i
= 0; i
< randCount
; ++i
) randIntegers
[ i
] = (uint8_t)( i
+ 1 );
8063 // Prevent dubious static analyzer warning.
8064 // Note: _DNSServerNameIsHostname() already enforces randCount >= addrCount. Also, this require_fatal() check
8065 // needs to be placed right before the next for-loop. Any earlier, and the static analyzer warning will persist
8068 require_fatal( addrCount
<= randCount
, "Invalid Count label values: addrCount %u > randCount %u",
8069 addrCount
, randCount
);
8071 // Create a contiguous subarray starting at index 0 that contains <addrCount> randomly chosen integers between
8072 // 1 and <randCount>, inclusive.
8073 // Loop invariant 1: Array elements with indexes in [0, i - 1] have been randomly chosen.
8074 // Loop invariant 2: Array elements with indexes in [i, randCount - 1] are candidates for being chosen.
8076 for( i
= 0; i
< addrCount
; ++i
)
8081 j
= RandomRange( i
, randCount
- 1 );
8084 tmp
= randIntegers
[ i
];
8085 randIntegers
[ i
] = randIntegers
[ j
];
8086 randIntegers
[ j
] = tmp
;
8091 recordLen
= sizeof( namePtr
) + sizeof( fields
) + rdataLen
;
8092 for( i
= 0; i
< addrCount
; ++i
)
8094 if( !inForTCP
&& ( ( DataBuffer_GetLen( inDB
) + recordLen
) > kDNSMaxUDPMessageSize
) )
8102 err
= DataBuffer_Append( inDB
, namePtr
, sizeof( namePtr
) );
8103 require_noerr( err
, exit
);
8105 // Set record TYPE, CLASS, TTL, and RDLENGTH.
8107 dns_fixed_fields_record_init( &fields
, (uint16_t) inQType
, kDNSServiceClass_IN
, ttl
, (uint16_t) rdataLen
);
8108 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
8109 require_noerr( err
, exit
);
8111 // Set record RDATA.
8113 *lsb
= ( randCount
> 0 ) ? randIntegers
[ i
] : ( *lsb
+ 1 );
8115 err
= DataBuffer_Append( inDB
, rdata
, rdataLen
);
8116 require_noerr( err
, exit
);
8121 else if( inQType
== kDNSServiceType_SRV
)
8123 require_quiet( nameHasSRV
, done
);
8125 dns_fixed_fields_record_init( &fields
, kDNSServiceType_SRV
, kDNSServiceClass_IN
, me
->defaultTTL
, 0 );
8127 for( i
= 0; i
< srvCount
; ++i
)
8129 dns_fixed_fields_srv fieldsSRV
;
8132 const ParsedSRV
* const srv
= &srvArray
[ i
];
8134 rdataLen
= sizeof( fieldsSRV
) + srvDomainLen
+ srv
->targetLen
+ 1;
8135 recordLen
= sizeof( namePtr
) + sizeof( fields
) + rdataLen
;
8137 if( !inForTCP
&& ( ( DataBuffer_GetLen( inDB
) + recordLen
) > kDNSMaxUDPMessageSize
) )
8143 // Append record NAME.
8145 err
= DataBuffer_Append( inDB
, namePtr
, sizeof( namePtr
) );
8146 require_noerr( err
, exit
);
8148 // Append record TYPE, CLASS, TTL, and RDLENGTH.
8150 WriteBig16( fields
.rdlength
, rdataLen
);
8151 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
8152 require_noerr( err
, exit
);
8154 // Append SRV RDATA.
8156 dns_fixed_fields_srv_init( &fieldsSRV
, srv
->priority
, srv
->weight
, srv
->port
);
8158 err
= DataBuffer_Append( inDB
, &fieldsSRV
, sizeof( fieldsSRV
) );
8159 require_noerr( err
, exit
);
8161 if( srv
->targetLen
> 0 )
8163 err
= DataBuffer_Append( inDB
, srv
->targetPtr
, srv
->targetLen
);
8164 require_noerr( err
, exit
);
8167 if( srvDomainLen
> 0 )
8169 err
= DataBuffer_Append( inDB
, srvDomainPtr
, srvDomainLen
);
8170 require_noerr( err
, exit
);
8173 err
= DataBuffer_Append( inDB
, "", 1 ); // Append root label.
8174 require_noerr( err
, exit
);
8179 else if( inQType
== kDNSServiceType_SOA
)
8181 size_t nameLen
, recordLen
;
8183 require_quiet( nameHasSOA
, done
);
8185 nameLen
= DomainNameLength( me
->domain
);
8188 err
= AppendSOARecord( NULL
, me
->domain
, nameLen
, 0, 0, 0, kRootLabel
, kRootLabel
, 0, 0, 0, 0, 0, &recordLen
);
8189 require_noerr( err
, exit
);
8191 if( ( DataBuffer_GetLen( inDB
) + recordLen
) > kDNSMaxUDPMessageSize
)
8198 err
= AppendSOARecord( inDB
, me
->domain
, nameLen
, kDNSServiceType_SOA
, kDNSServiceClass_IN
, me
->defaultTTL
,
8199 kRootLabel
, kRootLabel
, me
->serial
, 1 * kSecondsPerDay
, 2 * kSecondsPerHour
, 1000 * kSecondsPerHour
,
8200 me
->defaultTTL
, NULL
);
8201 require_noerr( err
, exit
);
8207 hdr
= (DNSHeader
*) DataBuffer_GetPtr( inDB
);
8208 flags
= DNSHeaderGetFlags( hdr
);
8209 if( notImplemented
)
8211 rcode
= kDNSRCode_NotImplemented
;
8215 flags
|= kDNSHeaderFlag_AuthAnswer
;
8216 if( truncated
) flags
|= kDNSHeaderFlag_Truncation
;
8217 rcode
= nameExists
? kDNSRCode_NoError
: kDNSRCode_NXDomain
;
8219 DNSFlagsSetRCode( flags
, rcode
);
8220 DNSHeaderSetFlags( hdr
, flags
);
8221 DNSHeaderSetAnswerCount( hdr
, answerCount
);
8229 _DNSServerNameIsHostname(
8231 const uint8_t * inName
,
8232 uint32_t * outAliasCount
,
8233 uint32_t inAliasTTLs
[ kMaxAliasTTLCount
],
8234 size_t * outAliasTTLCount
,
8235 unsigned int * outCount
,
8236 unsigned int * outRandCount
,
8239 Boolean
* outHasAAAA
,
8240 Boolean
* outHasSOA
)
8243 const uint8_t * label
;
8244 const uint8_t * nextLabel
;
8245 uint32_t aliasCount
= 0; // Arg from Alias label. Valid values are in [2, 2^31 - 1].
8246 unsigned int count
= 0; // First arg from Count label. Valid values are in [1, 255].
8247 unsigned int randCount
= 0; // Second arg from Count label. Valid values are in [count, 255].
8248 int32_t ttl
= -1; // Arg from TTL label. Valid values are in [0, 2^31 - 1].
8249 size_t aliasTTLCount
= 0; // Count of TTL args from Alias-TTL label.
8250 int hasTagLabel
= false;
8251 int hasIPv4Label
= false;
8252 int hasIPv6Label
= false;
8253 int isNameValid
= false;
8255 for( label
= inName
; label
[ 0 ]; label
= nextLabel
)
8259 nextLabel
= &label
[ 1 + label
[ 0 ] ];
8261 // Check if the first label is a valid alias TTL sequence label.
8263 if( ( label
== inName
) && ( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_AliasTTL
) == 0 ) )
8265 const char * ptr
= (const char *) &label
[ 1 + sizeof_string( kLabelPrefix_AliasTTL
) ];
8266 const char * const end
= (const char *) nextLabel
;
8269 check( label
[ 0 ] <= kDomainLabelLengthMax
);
8273 if( *ptr
!= '-' ) break;
8275 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &next
);
8276 if( err
|| ( arg
> INT32_MAX
) ) break; // TTL must be in [0, 2^31 - 1].
8277 inAliasTTLs
[ aliasTTLCount
++ ] = arg
;
8280 if( ( aliasTTLCount
== 0 ) || ( ptr
!= end
) ) break;
8283 // Check if the first label is a valid alias label.
8285 else if( ( label
== inName
) && ( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_Alias
) == 0 ) )
8287 const char * ptr
= (const char *) &label
[ 1 + sizeof_string( kLabelPrefix_Alias
) ];
8288 const char * const end
= (const char *) nextLabel
;
8292 if( *ptr
++ != '-' ) break;
8293 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8294 if( err
|| ( arg
< 2 ) || ( arg
> INT32_MAX
) ) break; // Alias count must be in [2, 2^31 - 1].
8296 if( ptr
!= end
) break;
8304 // Check if this label is a valid count label.
8306 else if( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_Count
) == 0 )
8308 const char * ptr
= (const char *) &label
[ 1 + sizeof_string( kLabelPrefix_Count
) ];
8309 const char * const end
= (const char *) nextLabel
;
8311 if( count
> 0 ) break; // Count cannot be specified more than once.
8313 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8314 if( err
|| ( arg
< 1 ) || ( arg
> 255 ) ) break; // Count must be in [1, 255].
8315 count
= (unsigned int) arg
;
8319 if( *ptr
++ != '-' ) break;
8320 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8321 if( err
|| ( arg
< (uint32_t) count
) || ( arg
> 255 ) ) break; // Rand count must be in [count, 255].
8322 randCount
= (unsigned int) arg
;
8323 if( ptr
!= end
) break;
8327 // Check if this label is a valid TTL label.
8329 else if( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_TTL
) == 0 )
8331 const char * ptr
= (const char *) &label
[ 1 + sizeof_string( kLabelPrefix_TTL
) ];
8332 const char * const end
= (const char *) nextLabel
;
8334 if( ttl
>= 0 ) break; // TTL cannot be specified more than once.
8336 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8337 if( err
|| ( arg
> INT32_MAX
) ) break; // TTL must be in [0, 2^31 - 1].
8338 ttl
= (int32_t) arg
;
8339 if( ptr
!= end
) break;
8342 // Check if this label is a valid IPv4 label.
8344 else if( strnicmpx( &label
[ 1 ], label
[ 0 ], kLabel_IPv4
) == 0 )
8346 if( hasIPv4Label
|| hasIPv6Label
) break; // Valid names have at most one IPv4 or IPv6 label.
8347 hasIPv4Label
= true;
8350 // Check if this label is a valid IPv6 label.
8352 else if( strnicmpx( &label
[ 1 ], label
[ 0 ], kLabel_IPv6
) == 0 )
8354 if( hasIPv4Label
|| hasIPv6Label
) break; // Valid names have at most one IPv4 or IPv6 label.
8355 hasIPv6Label
= true;
8358 // Check if this label is a valid tag label.
8360 else if( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_Tag
) == 0 )
8365 // If this and the remaining labels are equal to "d.test.", then the name exists. Otherwise, this label is invalid.
8366 // In both cases, there are no more labels to check.
8370 if( DomainNameEqual( label
, me
->domain
) ) isNameValid
= true;
8374 require_quiet( isNameValid
, exit
);
8376 if( outAliasCount
) *outAliasCount
= aliasCount
;
8377 if( outAliasTTLCount
) *outAliasTTLCount
= aliasTTLCount
;
8378 if( outCount
) *outCount
= ( count
> 0 ) ? count
: 1;
8379 if( outRandCount
) *outRandCount
= randCount
;
8380 if( outTTL
) *outTTL
= ( ttl
>= 0 ) ? ( (uint32_t) ttl
) : me
->defaultTTL
;
8381 if( outHasA
) *outHasA
= ( hasIPv4Label
|| !hasIPv6Label
) ? true : false;
8382 if( outHasAAAA
) *outHasAAAA
= ( hasIPv6Label
|| !hasIPv4Label
) ? true : false;
8385 *outHasSOA
= ( !count
&& ( ttl
< 0 ) && !hasIPv4Label
&& !hasIPv6Label
&& !hasTagLabel
) ? true : false;
8389 return( isNameValid
? true : false );
8393 _DNSServerNameIsSRVName(
8395 const uint8_t * inName
,
8396 const uint8_t ** outDomainPtr
,
8397 size_t * outDomainLen
,
8398 ParsedSRV inSRVArray
[ kMaxParsedSRVCount
],
8399 size_t * outSRVCount
)
8402 const uint8_t * label
;
8403 const uint8_t * domainPtr
;
8407 int isNameValid
= false;
8411 // Ensure that first label, i.e, the service label, begins with a '_' character.
8413 require_quiet( ( label
[ 0 ] > 0 ) && ( label
[ 1 ] == '_' ), exit
);
8414 label
= DomainNameGetNextLabel( label
);
8416 // Ensure that the second label, i.e., the proto label, begins with a '_' character (usually _tcp or _udp).
8418 require_quiet( ( label
[ 0 ] > 0 ) && ( label
[ 1 ] == '_' ), exit
);
8419 label
= DomainNameGetNextLabel( label
);
8421 // Parse the domain name, if any.
8426 if( DomainNameEqual( label
, me
->domain
) ||
8427 ( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_SRV
) == 0 ) ) break;
8428 label
= DomainNameGetNextLabel( label
);
8430 require_quiet( *label
, exit
);
8432 domainLen
= (size_t)( label
- domainPtr
);
8434 // Parse SRV labels, if any.
8437 while( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_SRV
) == 0 )
8439 const uint8_t * const nextLabel
= DomainNameGetNextLabel( label
);
8440 const char * ptr
= (const char *) &label
[ 1 + sizeof_string( kLabelPrefix_SRV
) ];
8441 const char * const end
= (const char *) nextLabel
;
8442 const uint8_t * target
;
8443 unsigned int priority
, weight
, port
;
8445 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8446 require_quiet( !err
&& ( arg
<= UINT16_MAX
), exit
);
8447 priority
= (unsigned int) arg
;
8449 require_quiet( ( ptr
< end
) && ( *ptr
== '-' ), exit
);
8452 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8453 require_quiet( !err
&& ( arg
<= UINT16_MAX
), exit
);
8454 weight
= (unsigned int) arg
;
8456 require_quiet( ( ptr
< end
) && ( *ptr
== '-' ), exit
);
8459 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8460 require_quiet( !err
&& ( arg
<= UINT16_MAX
), exit
);
8461 port
= (unsigned int) arg
;
8463 require_quiet( ptr
== end
, exit
);
8466 for( label
= nextLabel
; *label
; label
= DomainNameGetNextLabel( label
) )
8468 if( DomainNameEqual( label
, me
->domain
) ||
8469 ( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_SRV
) == 0 ) ) break;
8471 require_quiet( *label
, exit
);
8475 inSRVArray
[ srvCount
].priority
= (uint16_t) priority
;
8476 inSRVArray
[ srvCount
].weight
= (uint16_t) weight
;
8477 inSRVArray
[ srvCount
].port
= (uint16_t) port
;
8478 inSRVArray
[ srvCount
].targetPtr
= target
;
8479 inSRVArray
[ srvCount
].targetLen
= (uint16_t)( label
- target
);
8483 require_quiet( DomainNameEqual( label
, me
->domain
), exit
);
8486 if( outDomainPtr
) *outDomainPtr
= domainPtr
;
8487 if( outDomainLen
) *outDomainLen
= domainLen
;
8488 if( outSRVCount
) *outSRVCount
= srvCount
;
8491 return( isNameValid
? true : false );
8494 //===========================================================================================================================
8495 // _DNSServerTCPReadHandler
8496 //===========================================================================================================================
8500 DNSServerRef server
; // Reference to DNS server object.
8501 sockaddr_ip clientAddr
; // Client's address.
8502 dispatch_source_t readSource
; // Dispatch read source for client socket.
8503 dispatch_source_t writeSource
; // Dispatch write source for client socket.
8504 size_t offset
; // Offset into receive buffer.
8505 void * msgPtr
; // Pointer to dynamically allocated message buffer.
8506 size_t msgLen
; // Length of message buffer.
8507 Boolean readSuspended
; // True if the read source is currently suspended.
8508 Boolean writeSuspended
; // True if the write source is currently suspended.
8509 Boolean receivedLength
; // True if receiving DNS message as opposed to the message length.
8510 uint8_t lenBuf
[ 2 ]; // Buffer for two-octet message length field.
8511 iovec_t iov
[ 2 ]; // IO vector for writing response message.
8512 iovec_t
* iovPtr
; // Vector pointer for SocketWriteData().
8513 int iovCount
; // Vector count for SocketWriteData().
8515 } TCPConnectionContext
;
8517 static void TCPConnectionStop( TCPConnectionContext
*inContext
);
8518 static void TCPConnectionContextFree( TCPConnectionContext
*inContext
);
8519 static void TCPConnectionReadHandler( void *inContext
);
8520 static void TCPConnectionWriteHandler( void *inContext
);
8522 #define TCPConnectionForget( X ) ForgetCustomEx( X, TCPConnectionStop, TCPConnectionContextFree )
8524 static void _DNSServerTCPReadHandler( void *inContext
)
8527 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
8528 DNSServerRef
const me
= (DNSServerRef
) sockCtx
->userContext
;
8529 TCPConnectionContext
* connection
;
8530 socklen_t clientAddrLen
;
8531 SocketRef newSock
= kInvalidSocketRef
;
8532 SocketContext
* newSockCtx
= NULL
;
8534 connection
= (TCPConnectionContext
*) calloc( 1, sizeof( *connection
) );
8535 require_action( connection
, exit
, err
= kNoMemoryErr
);
8538 connection
->server
= me
;
8540 clientAddrLen
= (socklen_t
) sizeof( connection
->clientAddr
);
8541 newSock
= accept( sockCtx
->sock
, &connection
->clientAddr
.sa
, &clientAddrLen
);
8542 err
= map_socket_creation_errno( newSock
);
8543 require_noerr( err
, exit
);
8545 err
= SocketContextCreate( newSock
, connection
, &newSockCtx
);
8546 require_noerr( err
, exit
);
8547 newSock
= kInvalidSocketRef
;
8549 err
= DispatchReadSourceCreate( newSockCtx
->sock
, me
->queue
, TCPConnectionReadHandler
, SocketContextCancelHandler
,
8550 newSockCtx
, &connection
->readSource
);
8551 require_noerr( err
, exit
);
8552 SocketContextRetain( newSockCtx
);
8553 dispatch_resume( connection
->readSource
);
8555 err
= DispatchWriteSourceCreate( newSockCtx
->sock
, me
->queue
, TCPConnectionWriteHandler
, SocketContextCancelHandler
,
8556 newSockCtx
, &connection
->writeSource
);
8557 require_noerr( err
, exit
);
8558 SocketContextRetain( newSockCtx
);
8559 connection
->writeSuspended
= true;
8563 ForgetSocket( &newSock
);
8564 SocketContextRelease( newSockCtx
);
8565 TCPConnectionForget( &connection
);
8568 //===========================================================================================================================
8569 // TCPConnectionStop
8570 //===========================================================================================================================
8572 static void TCPConnectionStop( TCPConnectionContext
*inContext
)
8574 dispatch_source_forget_ex( &inContext
->readSource
, &inContext
->readSuspended
);
8575 dispatch_source_forget_ex( &inContext
->writeSource
, &inContext
->writeSuspended
);
8578 //===========================================================================================================================
8579 // TCPConnectionContextFree
8580 //===========================================================================================================================
8582 static void TCPConnectionContextFree( TCPConnectionContext
*inContext
)
8584 check( !inContext
->readSource
);
8585 check( !inContext
->writeSource
);
8586 ForgetCF( &inContext
->server
);
8587 ForgetMem( &inContext
->msgPtr
);
8591 //===========================================================================================================================
8592 // TCPConnectionReadHandler
8593 //===========================================================================================================================
8595 static void TCPConnectionReadHandler( void *inContext
)
8598 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
8599 TCPConnectionContext
* connection
= (TCPConnectionContext
*) sockCtx
->userContext
;
8601 uint8_t * responsePtr
= NULL
; // malloc'd
8604 // Receive message length.
8606 if( !connection
->receivedLength
)
8608 err
= SocketReadData( sockCtx
->sock
, connection
->lenBuf
, sizeof( connection
->lenBuf
), &connection
->offset
);
8609 if( err
== EWOULDBLOCK
) goto exit
;
8610 require_noerr( err
, exit
);
8612 connection
->offset
= 0;
8613 connection
->msgLen
= ReadBig16( connection
->lenBuf
);
8614 connection
->msgPtr
= malloc( connection
->msgLen
);
8615 require_action( connection
->msgPtr
, exit
, err
= kNoMemoryErr
);
8616 connection
->receivedLength
= true;
8621 err
= SocketReadData( sockCtx
->sock
, connection
->msgPtr
, connection
->msgLen
, &connection
->offset
);
8622 if( err
== EWOULDBLOCK
) goto exit
;
8623 require_noerr( err
, exit
);
8625 gettimeofday( &now
, NULL
);
8626 dispatch_suspend( connection
->readSource
);
8627 connection
->readSuspended
= true;
8629 ds_ulog( kLogLevelInfo
, "TCP server received %zu bytes from %##a at %{du:time}.\n",
8630 connection
->msgLen
, &connection
->clientAddr
, &now
);
8632 if( connection
->msgLen
< kDNSHeaderLength
)
8634 ds_ulog( kLogLevelInfo
, "TCP DNS message is too small (%zu < %d).\n", connection
->msgLen
, kDNSHeaderLength
);
8638 ds_ulog( kLogLevelInfo
, "TCP received message:\n\n%1{du:dnsmsg}", connection
->msgPtr
, connection
->msgLen
);
8642 err
= _DNSServerAnswerQueryForTCP( connection
->server
, connection
->msgPtr
, connection
->msgLen
, &responsePtr
,
8644 require_noerr_quiet( err
, exit
);
8648 ds_ulog( kLogLevelInfo
, "TCP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen
, responsePtr
, responseLen
);
8650 free( connection
->msgPtr
);
8651 connection
->msgPtr
= responsePtr
;
8652 connection
->msgLen
= responseLen
;
8655 check( connection
->msgLen
<= UINT16_MAX
);
8656 WriteBig16( connection
->lenBuf
, connection
->msgLen
);
8657 connection
->iov
[ 0 ].iov_base
= connection
->lenBuf
;
8658 connection
->iov
[ 0 ].iov_len
= sizeof( connection
->lenBuf
);
8659 connection
->iov
[ 1 ].iov_base
= connection
->msgPtr
;
8660 connection
->iov
[ 1 ].iov_len
= connection
->msgLen
;
8662 connection
->iovPtr
= connection
->iov
;
8663 connection
->iovCount
= 2;
8665 check( connection
->writeSuspended
);
8666 dispatch_resume( connection
->writeSource
);
8667 connection
->writeSuspended
= false;
8670 FreeNullSafe( responsePtr
);
8671 if( err
&& ( err
!= EWOULDBLOCK
) ) TCPConnectionForget( &connection
);
8674 //===========================================================================================================================
8675 // TCPConnectionWriteHandler
8676 //===========================================================================================================================
8678 static void TCPConnectionWriteHandler( void *inContext
)
8681 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
8682 TCPConnectionContext
* connection
= (TCPConnectionContext
*) sockCtx
->userContext
;
8684 err
= SocketWriteData( sockCtx
->sock
, &connection
->iovPtr
, &connection
->iovCount
);
8685 if( err
== EWOULDBLOCK
) goto exit
;
8688 TCPConnectionForget( &connection
);
8694 //===========================================================================================================================
8696 //===========================================================================================================================
8700 uint8_t * hostname
; // Used as the base name for hostnames and service names.
8701 uint8_t * serviceLabel
; // Label containing the base service name.
8702 unsigned int maxInstanceCount
; // Maximum number of service instances and hostnames.
8703 uint64_t * bitmaps
; // Array of 64-bit bitmaps for keeping track of needed responses.
8704 size_t bitmapCount
; // Number of 64-bit bitmaps.
8705 dispatch_source_t readSourceV4
; // Read dispatch source for IPv4 socket.
8706 dispatch_source_t readSourceV6
; // Read dispatch source for IPv6 socket.
8707 uint32_t ifIndex
; // Index of the interface to run on.
8708 unsigned int recordCountA
; // Number of A records per hostname.
8709 unsigned int recordCountAAAA
; // Number of AAAA records per hostname.
8710 unsigned int maxDropCount
; // If > 0, the drop rates apply to only the first <maxDropCount> responses.
8711 double ucastDropRate
; // Probability of dropping a unicast response.
8712 double mcastDropRate
; // Probability of dropping a multicast query or response.
8713 uint8_t * dropCounters
; // If maxDropCount > 0, array of <maxInstanceCount> response drop counters.
8714 Boolean noAdditionals
; // True if responses are to not include additional records.
8715 Boolean useIPv4
; // True if the replier is to use IPv4.
8716 Boolean useIPv6
; // True if the replier is to use IPv6.
8717 uint8_t msgBuf
[ kMDNSMessageSizeMax
]; // Buffer for received mDNS message.
8718 #if( TARGET_OS_DARWIN )
8719 dispatch_source_t processMonitor
; // Process monitor source for process being followed, if any.
8720 pid_t followPID
; // PID of process being followed, if any. (If it exits, we exit).
8723 } MDNSReplierContext
;
8725 typedef struct MRResourceRecord MRResourceRecord
;
8726 struct MRResourceRecord
8728 MRResourceRecord
* next
; // Next item in list.
8729 uint8_t * name
; // Resource record name.
8730 uint16_t type
; // Resource record type.
8731 uint16_t class; // Resource record class.
8732 uint32_t ttl
; // Resource record TTL.
8733 uint16_t rdlength
; // Resource record data length.
8734 uint8_t * rdata
; // Resource record data.
8735 const uint8_t * target
; // For SRV records, pointer to target in RDATA.
8738 typedef struct MRNameOffsetItem MRNameOffsetItem
;
8739 struct MRNameOffsetItem
8741 MRNameOffsetItem
* next
; // Next item in list.
8742 uint16_t offset
; // Offset of domain name in response message.
8743 uint8_t name
[ 1 ]; // Variable-length array for domain name.
8746 #if( TARGET_OS_DARWIN )
8747 static void _MDNSReplierFollowedProcessHandler( void *inContext
);
8749 static void _MDNSReplierReadHandler( void *inContext
);
8751 _MDNSReplierAnswerQuery(
8752 MDNSReplierContext
* inContext
,
8753 const uint8_t * inQueryPtr
,
8755 sockaddr_ip
* inSender
,
8757 unsigned int inIndex
);
8759 _MDNSReplierAnswerListAdd(
8760 MDNSReplierContext
* inContext
,
8761 MRResourceRecord
** inAnswerList
,
8762 unsigned int inIndex
,
8763 const uint8_t * inName
,
8764 unsigned int inType
,
8765 unsigned int inClass
);
8767 _MDNSReplierAnswerListRemovePTR(
8768 MRResourceRecord
** inAnswerListPtr
,
8769 const uint8_t * inName
,
8770 const uint8_t * inRData
);
8772 _MDNSReplierSendOrDropResponse(
8773 MDNSReplierContext
* inContext
,
8774 MRResourceRecord
* inAnswerList
,
8775 sockaddr_ip
* inQuerier
,
8777 unsigned int inIndex
,
8778 Boolean inUnicast
);
8780 _MDNSReplierCreateResponse(
8781 MDNSReplierContext
* inContext
,
8782 MRResourceRecord
* inAnswerList
,
8783 unsigned int inIndex
,
8784 uint8_t ** outResponsePtr
,
8785 size_t * outResponseLen
);
8787 _MDNSReplierAppendNameToResponse(
8788 DataBuffer
* inResponse
,
8789 const uint8_t * inName
,
8790 MRNameOffsetItem
** inNameOffsetListPtr
);
8792 _MDNSReplierServiceTypeMatch(
8793 const MDNSReplierContext
* inContext
,
8794 const uint8_t * inName
,
8795 unsigned int * outTXTSize
,
8796 unsigned int * outCount
);
8798 _MDNSReplierServiceInstanceNameMatch(
8799 const MDNSReplierContext
* inContext
,
8800 const uint8_t * inName
,
8801 unsigned int * outIndex
,
8802 unsigned int * outTXTSize
,
8803 unsigned int * outCount
);
8804 static Boolean
_MDNSReplierAboutRecordNameMatch( const MDNSReplierContext
*inContext
, const uint8_t *inName
);
8806 _MDNSReplierHostnameMatch(
8807 const MDNSReplierContext
* inContext
,
8808 const uint8_t * inName
,
8809 unsigned int * outIndex
);
8810 static OSStatus
_MDNSReplierCreateTXTRecord( const uint8_t *inRecordName
, size_t inSize
, uint8_t **outTXT
);
8812 _MRResourceRecordCreate(
8817 uint16_t inRDLength
,
8819 MRResourceRecord
** outRecord
);
8820 static void _MRResourceRecordFree( MRResourceRecord
*inRecord
);
8821 static void _MRResourceRecordFreeList( MRResourceRecord
*inList
);
8822 static OSStatus
_MRNameOffsetItemCreate( const uint8_t *inName
, uint16_t inOffset
, MRNameOffsetItem
**outItem
);
8823 static void _MRNameOffsetItemFree( MRNameOffsetItem
*inItem
);
8824 static void _MRNameOffsetItemFreeList( MRNameOffsetItem
*inList
);
8826 ulog_define_ex( kDNSSDUtilIdentifier
, MDNSReplier
, kLogLevelInfo
, kLogFlags_None
, "MDNSReplier", NULL
);
8827 #define mr_ulog( LEVEL, ... ) ulog( &log_category_from_name( MDNSReplier ), (LEVEL), __VA_ARGS__ )
8829 static void MDNSReplierCmd( void )
8832 MDNSReplierContext
* context
;
8833 SocketRef sockV4
= kInvalidSocketRef
;
8834 SocketRef sockV6
= kInvalidSocketRef
;
8835 const char * ifname
;
8837 uint8_t name
[ 1 + kDomainLabelLengthMax
+ 1 ];
8838 char ifnameBuf
[ IF_NAMESIZE
+ 1 ];
8840 err
= CheckIntegerArgument( gMDNSReplier_MaxInstanceCount
, "max instance count", 1, UINT16_MAX
);
8841 require_noerr_quiet( err
, exit
);
8843 err
= CheckIntegerArgument( gMDNSReplier_RecordCountA
, "A record count", 0, 255 );
8844 require_noerr_quiet( err
, exit
);
8846 err
= CheckIntegerArgument( gMDNSReplier_RecordCountAAAA
, "AAAA record count", 0, 255 );
8847 require_noerr_quiet( err
, exit
);
8849 err
= CheckDoubleArgument( gMDNSReplier_UnicastDropRate
, "unicast drop rate", 0.0, 1.0 );
8850 require_noerr_quiet( err
, exit
);
8852 err
= CheckDoubleArgument( gMDNSReplier_MulticastDropRate
, "multicast drop rate", 0.0, 1.0 );
8853 require_noerr_quiet( err
, exit
);
8855 err
= CheckIntegerArgument( gMDNSReplier_MaxDropCount
, "drop count", 0, 255 );
8856 require_noerr_quiet( err
, exit
);
8858 if( gMDNSReplier_Foreground
)
8860 LogControl( "MDNSReplier:output=file;stdout,MDNSReplier:flags=time;prefix" );
8863 context
= (MDNSReplierContext
*) calloc( 1, sizeof( *context
) );
8864 require_action( context
, exit
, err
= kNoMemoryErr
);
8866 context
->maxInstanceCount
= (unsigned int) gMDNSReplier_MaxInstanceCount
;
8867 context
->recordCountA
= (unsigned int) gMDNSReplier_RecordCountA
;
8868 context
->recordCountAAAA
= (unsigned int) gMDNSReplier_RecordCountAAAA
;
8869 context
->maxDropCount
= (unsigned int) gMDNSReplier_MaxDropCount
;
8870 context
->ucastDropRate
= gMDNSReplier_UnicastDropRate
;
8871 context
->mcastDropRate
= gMDNSReplier_MulticastDropRate
;
8872 context
->noAdditionals
= gMDNSReplier_NoAdditionals
? true : false;
8873 context
->useIPv4
= ( gMDNSReplier_UseIPv4
|| !gMDNSReplier_UseIPv6
) ? true : false;
8874 context
->useIPv6
= ( gMDNSReplier_UseIPv6
|| !gMDNSReplier_UseIPv4
) ? true : false;
8875 context
->bitmapCount
= ( context
->maxInstanceCount
+ 63 ) / 64;
8877 #if( TARGET_OS_DARWIN )
8878 if( gMDNSReplier_FollowPID
)
8880 context
->followPID
= _StringToPID( gMDNSReplier_FollowPID
, &err
);
8881 if( err
|| ( context
->followPID
< 0 ) )
8883 FPrintF( stderr
, "error: Invalid follow PID: %s\n", gMDNSReplier_FollowPID
);
8887 err
= DispatchProcessMonitorCreate( context
->followPID
, DISPATCH_PROC_EXIT
, dispatch_get_main_queue(),
8888 _MDNSReplierFollowedProcessHandler
, NULL
, context
, &context
->processMonitor
);
8889 require_noerr( err
, exit
);
8890 dispatch_resume( context
->processMonitor
);
8894 context
->followPID
= -1;
8898 if( context
->maxDropCount
> 0 )
8900 context
->dropCounters
= (uint8_t *) calloc( context
->maxInstanceCount
, sizeof( *context
->dropCounters
) );
8901 require_action( context
->dropCounters
, exit
, err
= kNoMemoryErr
);
8904 context
->bitmaps
= (uint64_t *) calloc( context
->bitmapCount
, sizeof( *context
->bitmaps
) );
8905 require_action( context
->bitmaps
, exit
, err
= kNoMemoryErr
);
8907 // Create the base hostname label.
8909 len
= strlen( gMDNSReplier_Hostname
);
8910 if( context
->maxInstanceCount
> 1 )
8912 unsigned int maxInstanceCount
, digitCount
;
8914 // When there's more than one instance, extra bytes are needed to append " (<instance index>)" or
8915 // "-<instance index>" to the base hostname.
8917 maxInstanceCount
= context
->maxInstanceCount
;
8918 for( digitCount
= 0; maxInstanceCount
> 0; ++digitCount
) maxInstanceCount
/= 10;
8919 len
+= ( 3 + digitCount
);
8922 if( len
<= kDomainLabelLengthMax
)
8924 uint8_t * dst
= &name
[ 1 ];
8925 uint8_t * lim
= &name
[ countof( name
) ];
8927 SNPrintF_Add( (char **) &dst
, (char *) lim
, "%s", gMDNSReplier_Hostname
);
8928 name
[ 0 ] = (uint8_t)( dst
- &name
[ 1 ] );
8930 err
= DomainNameDupLower( name
, &context
->hostname
, NULL
);
8931 require_noerr( err
, exit
);
8935 FPrintF( stderr
, "error: Base name \"%s\" is too long for max instance count of %u.\n",
8936 gMDNSReplier_Hostname
, context
->maxInstanceCount
);
8940 // Create the service label.
8942 len
= strlen( gMDNSReplier_ServiceTypeTag
) + 3; // We need three extra bytes for the service type prefix "_t-".
8943 if( len
<= kDomainLabelLengthMax
)
8945 uint8_t * dst
= &name
[ 1 ];
8946 uint8_t * lim
= &name
[ countof( name
) ];
8948 SNPrintF_Add( (char **) &dst
, (char *) lim
, "_t-%s", gMDNSReplier_ServiceTypeTag
);
8949 name
[ 0 ] = (uint8_t)( dst
- &name
[ 1 ] );
8951 err
= DomainNameDupLower( name
, &context
->serviceLabel
, NULL
);
8952 require_noerr( err
, exit
);
8956 FPrintF( stderr
, "error: Service type tag is too long.\n" );
8960 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
8961 require_noerr_quiet( err
, exit
);
8963 ifname
= if_indextoname( context
->ifIndex
, ifnameBuf
);
8964 require_action( ifname
, exit
, err
= kNameErr
);
8966 // Set up IPv4 socket.
8968 if( context
->useIPv4
)
8970 err
= CreateMulticastSocket( GetMDNSMulticastAddrV4(), kMDNSPort
, ifname
, context
->ifIndex
, true, NULL
, &sockV4
);
8971 require_noerr( err
, exit
);
8974 // Set up IPv6 socket.
8976 if( context
->useIPv6
)
8978 err
= CreateMulticastSocket( GetMDNSMulticastAddrV6(), kMDNSPort
, ifname
, context
->ifIndex
, true, NULL
, &sockV6
);
8979 require_noerr( err
, exit
);
8982 // Create dispatch read sources for socket(s).
8984 if( IsValidSocket( sockV4
) )
8986 SocketContext
* sockCtx
;
8988 err
= SocketContextCreate( sockV4
, context
, &sockCtx
);
8989 require_noerr( err
, exit
);
8990 sockV4
= kInvalidSocketRef
;
8992 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, _MDNSReplierReadHandler
, SocketContextCancelHandler
, sockCtx
,
8993 &context
->readSourceV4
);
8994 if( err
) ForgetSocketContext( &sockCtx
);
8995 require_noerr( err
, exit
);
8997 dispatch_resume( context
->readSourceV4
);
9000 if( IsValidSocket( sockV6
) )
9002 SocketContext
* sockCtx
;
9004 err
= SocketContextCreate( sockV6
, context
, &sockCtx
);
9005 require_noerr( err
, exit
);
9006 sockV6
= kInvalidSocketRef
;
9008 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, _MDNSReplierReadHandler
, SocketContextCancelHandler
, sockCtx
,
9009 &context
->readSourceV6
);
9010 if( err
) ForgetSocketContext( &sockCtx
);
9011 require_noerr( err
, exit
);
9013 dispatch_resume( context
->readSourceV6
);
9019 ForgetSocket( &sockV4
);
9020 ForgetSocket( &sockV6
);
9024 #if( TARGET_OS_DARWIN )
9025 //===========================================================================================================================
9026 // _MDNSReplierFollowedProcessHandler
9027 //===========================================================================================================================
9029 static void _MDNSReplierFollowedProcessHandler( void *inContext
)
9031 MDNSReplierContext
* const context
= (MDNSReplierContext
*) inContext
;
9033 if( dispatch_source_get_data( context
->processMonitor
) & DISPATCH_PROC_EXIT
)
9035 mr_ulog( kLogLevelNotice
, "Exiting: followed process (%lld) exited.\n", (int64_t) context
->followPID
);
9041 //===========================================================================================================================
9042 // _MDNSReplierReadHandler
9043 //===========================================================================================================================
9045 #define ShouldDrop( P ) ( ( (P) > 0.0 ) && ( ( (P) >= 1.0 ) || RandomlyTrue( P ) ) )
9047 static void _MDNSReplierReadHandler( void *inContext
)
9050 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
9051 MDNSReplierContext
* const context
= (MDNSReplierContext
*) sockCtx
->userContext
;
9054 const DNSHeader
* hdr
;
9055 unsigned int flags
, questionCount
, i
, j
;
9056 const uint8_t * ptr
;
9057 int drop
, isMetaQuery
;
9059 err
= SocketRecvFrom( sockCtx
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &msgLen
, &sender
, sizeof( sender
),
9060 NULL
, NULL
, NULL
, NULL
);
9061 require_noerr( err
, exit
);
9063 if( msgLen
< kDNSHeaderLength
)
9065 mr_ulog( kLogLevelInfo
, "Message is too small (%zu < %d).\n", msgLen
, kDNSHeaderLength
);
9069 // Perform header field checks.
9070 // The message ID and most flag bits are silently ignored (see <https://tools.ietf.org/html/rfc6762#section-18>).
9072 hdr
= (DNSHeader
*) context
->msgBuf
;
9073 flags
= DNSHeaderGetFlags( hdr
);
9074 require_quiet( ( flags
& kDNSHeaderFlag_Response
) == 0, exit
); // Reject responses.
9075 require_quiet( DNSFlagsGetOpCode( flags
) == kDNSOpCode_Query
, exit
); // Reject opcodes other than standard query.
9076 require_quiet( DNSFlagsGetRCode( flags
) == kDNSRCode_NoError
, exit
); // Reject non-zero rcodes.
9078 drop
= ( !context
->maxDropCount
&& ShouldDrop( context
->mcastDropRate
) ) ? true : false;
9080 mr_ulog( kLogLevelInfo
, "Received %zu byte message from %##a%?s:\n\n%#1{du:dnsmsg}",
9081 msgLen
, &sender
, drop
, " (dropping)", context
->msgBuf
, msgLen
);
9083 // Based on the QNAMEs in the query message, determine from which sets of records we may possibly need answers.
9085 questionCount
= DNSHeaderGetQuestionCount( hdr
);
9086 require_quiet( questionCount
> 0, exit
);
9088 memset( context
->bitmaps
, 0, context
->bitmapCount
* sizeof_element( context
->bitmaps
) );
9090 isMetaQuery
= false;
9091 ptr
= (const uint8_t *) &hdr
[ 1 ];
9092 for( i
= 0; i
< questionCount
; ++i
)
9094 unsigned int count
, index
;
9095 uint16_t qtype
, qclass
;
9096 uint8_t qname
[ kDomainNameLengthMax
];
9098 err
= DNSMessageExtractQuestion( context
->msgBuf
, msgLen
, ptr
, qname
, &qtype
, &qclass
, &ptr
);
9099 require_noerr_quiet( err
, exit
);
9101 if( ( qclass
& ~kQClassUnicastResponseBit
) != kDNSServiceClass_IN
) continue;
9103 if( _MDNSReplierHostnameMatch( context
, qname
, &index
) ||
9104 _MDNSReplierServiceInstanceNameMatch( context
, qname
, &index
, NULL
, NULL
) )
9106 if( ( index
>= 1 ) && ( index
<= context
->maxInstanceCount
) )
9108 context
->bitmaps
[ ( index
- 1 ) / 64 ] |= ( UINT64_C( 1 ) << ( ( index
- 1 ) % 64 ) );
9111 else if( _MDNSReplierServiceTypeMatch( context
, qname
, NULL
, &count
) )
9113 if( ( count
>= 1 ) && ( count
<= context
->maxInstanceCount
) )
9115 for( j
= 0; j
< (unsigned int) context
->bitmapCount
; ++j
)
9119 context
->bitmaps
[ j
] |= ( ( UINT64_C( 1 ) << count
) - 1 );
9124 context
->bitmaps
[ j
] = ~UINT64_C( 0 );
9130 else if( _MDNSReplierAboutRecordNameMatch( context
, qname
) )
9136 // Attempt to answer the query message using selected record sets.
9140 err
= _MDNSReplierAnswerQuery( context
, context
->msgBuf
, msgLen
, &sender
, sockCtx
->sock
, 0 );
9143 if( drop
) goto exit
;
9145 for( i
= 0; i
< context
->bitmapCount
; ++i
)
9147 for( j
= 0; ( context
->bitmaps
[ i
] != 0 ) && ( j
< 64 ); ++j
)
9149 const uint64_t bitmask
= UINT64_C( 1 ) << j
;
9151 if( context
->bitmaps
[ i
] & bitmask
)
9153 context
->bitmaps
[ i
] &= ~bitmask
;
9155 err
= _MDNSReplierAnswerQuery( context
, context
->msgBuf
, msgLen
, &sender
, sockCtx
->sock
,
9156 ( i
* 64 ) + j
+ 1 );
9166 //===========================================================================================================================
9167 // _MDNSReplierAnswerQuery
9168 //===========================================================================================================================
9171 _MDNSReplierAnswerQuery(
9172 MDNSReplierContext
* inContext
,
9173 const uint8_t * inQueryPtr
,
9175 sockaddr_ip
* inSender
,
9177 unsigned int inIndex
)
9180 const DNSHeader
* hdr
;
9181 const uint8_t * ptr
;
9182 unsigned int questionCount
, answerCount
, i
;
9183 MRResourceRecord
* ucastAnswerList
= NULL
;
9184 MRResourceRecord
* mcastAnswerList
= NULL
;
9186 require_action( inIndex
<= inContext
->maxInstanceCount
, exit
, err
= kRangeErr
);
9188 // Get answers for questions.
9190 check( inQueryLen
>= kDNSHeaderLength
);
9191 hdr
= (const DNSHeader
*) inQueryPtr
;
9192 questionCount
= DNSHeaderGetQuestionCount( hdr
);
9194 ptr
= (const uint8_t *) &hdr
[ 1 ];
9195 for( i
= 0; i
< questionCount
; ++i
)
9197 MRResourceRecord
** answerListPtr
;
9198 uint16_t qtype
, qclass
;
9199 uint8_t qname
[ kDomainNameLengthMax
];
9201 err
= DNSMessageExtractQuestion( inQueryPtr
, inQueryLen
, ptr
, qname
, &qtype
, &qclass
, &ptr
);
9202 require_noerr_quiet( err
, exit
);
9204 if( qclass
& kQClassUnicastResponseBit
)
9206 qclass
&= ~kQClassUnicastResponseBit
;
9207 answerListPtr
= &ucastAnswerList
;
9211 answerListPtr
= &mcastAnswerList
;
9214 err
= _MDNSReplierAnswerListAdd( inContext
, answerListPtr
, inIndex
, qname
, qtype
, qclass
);
9215 require_noerr( err
, exit
);
9217 require_action_quiet( mcastAnswerList
|| ucastAnswerList
, exit
, err
= kNoErr
);
9219 // Suppress known answers.
9220 // Records in the Answer section of the query message are known answers, so remove them from the answer lists.
9221 // See <https://tools.ietf.org/html/rfc6762#section-7.1>.
9223 answerCount
= DNSHeaderGetAnswerCount( hdr
);
9224 for( i
= 0; i
< answerCount
; ++i
)
9226 const uint8_t * rdataPtr
;
9227 const uint8_t * recordPtr
;
9228 uint16_t type
, class;
9229 uint8_t name
[ kDomainNameLengthMax
];
9230 uint8_t instance
[ kDomainNameLengthMax
];
9233 err
= DNSMessageExtractRecord( inQueryPtr
, inQueryLen
, ptr
, NULL
, &type
, &class, NULL
, NULL
, NULL
, &ptr
);
9234 require_noerr_quiet( err
, exit
);
9236 if( ( type
!= kDNSServiceType_PTR
) || ( class != kDNSServiceClass_IN
) ) continue;
9238 err
= DNSMessageExtractRecord( inQueryPtr
, inQueryLen
, recordPtr
, name
, NULL
, NULL
, NULL
, &rdataPtr
, NULL
, NULL
);
9239 require_noerr( err
, exit
);
9241 err
= DNSMessageExtractDomainName( inQueryPtr
, inQueryLen
, rdataPtr
, instance
, NULL
);
9242 require_noerr_quiet( err
, exit
);
9244 if( ucastAnswerList
) _MDNSReplierAnswerListRemovePTR( &ucastAnswerList
, name
, instance
);
9245 if( mcastAnswerList
) _MDNSReplierAnswerListRemovePTR( &mcastAnswerList
, name
, instance
);
9247 require_action_quiet( mcastAnswerList
|| ucastAnswerList
, exit
, err
= kNoErr
);
9249 // Send or drop responses.
9251 if( ucastAnswerList
)
9253 err
= _MDNSReplierSendOrDropResponse( inContext
, ucastAnswerList
, inSender
, inSock
, inIndex
, true );
9254 require_noerr( err
, exit
);
9257 if( mcastAnswerList
)
9259 err
= _MDNSReplierSendOrDropResponse( inContext
, mcastAnswerList
, inSender
, inSock
, inIndex
, false );
9260 require_noerr( err
, exit
);
9265 _MRResourceRecordFreeList( ucastAnswerList
);
9266 _MRResourceRecordFreeList( mcastAnswerList
);
9270 //===========================================================================================================================
9271 // _MDNSReplierAnswerListAdd
9272 //===========================================================================================================================
9275 _MDNSReplierAnswerListAdd(
9276 MDNSReplierContext
* inContext
,
9277 MRResourceRecord
** inAnswerList
,
9278 unsigned int inIndex
,
9279 const uint8_t * inName
,
9280 unsigned int inType
,
9281 unsigned int inClass
)
9284 uint8_t * recordName
= NULL
;
9285 uint8_t * rdataPtr
= NULL
;
9287 MRResourceRecord
* answer
;
9288 MRResourceRecord
** answerPtr
;
9289 const uint8_t * const hostname
= inContext
->hostname
;
9292 unsigned int count
, txtSize
;
9294 require_action( inIndex
<= inContext
->maxInstanceCount
, exit
, err
= kRangeErr
);
9295 require_action_quiet( inClass
== kDNSServiceClass_IN
, exit
, err
= kNoErr
);
9297 for( answerPtr
= inAnswerList
; ( answer
= *answerPtr
) != NULL
; answerPtr
= &answer
->next
)
9299 if( ( answer
->type
== inType
) && DomainNameEqual( answer
->name
, inName
) )
9306 // Index 0 is reserved for answering queries about the mdnsreplier, while all other index values up to the maximum
9307 // instance count are for answering queries about service instances.
9311 if( _MDNSReplierAboutRecordNameMatch( inContext
, inName
) )
9313 int listHasTXT
= false;
9315 if( inType
== kDNSServiceType_ANY
)
9317 for( answer
= *inAnswerList
; answer
; answer
= answer
->next
)
9319 if( ( answer
->type
== kDNSServiceType_TXT
) && DomainNameEqual( answer
->name
, inName
) )
9327 if( ( inType
== kDNSServiceType_TXT
) || ( ( inType
== kDNSServiceType_ANY
) && !listHasTXT
) )
9329 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9330 require_noerr( err
, exit
);
9332 err
= CreateTXTRecordDataFromString( "ready=yes", ',', &rdataPtr
, &rdataLen
);
9333 require_noerr( err
, exit
);
9335 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_TXT
, kDNSServiceClass_IN
, kMDNSRecordTTL_Other
,
9336 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9337 require_noerr( err
, exit
);
9341 *answerPtr
= answer
;
9343 else if( inType
== kDNSServiceType_NSEC
)
9345 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9346 require_noerr( err
, exit
);
9348 err
= CreateNSECRecordData( recordName
, &rdataPtr
, &rdataLen
, 1, kDNSServiceType_TXT
);
9349 require_noerr( err
, exit
);
9351 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_NSEC
, kDNSServiceClass_IN
, kMDNSRecordTTL_Host
,
9352 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9353 require_noerr( err
, exit
);
9357 *answerPtr
= answer
;
9361 else if( _MDNSReplierHostnameMatch( inContext
, inName
, &index
) && ( index
== inIndex
) )
9363 int listHasA
= false;
9364 int listHasAAAA
= false;
9366 if( inType
== kDNSServiceType_ANY
)
9368 for( answer
= *inAnswerList
; answer
; answer
= answer
->next
)
9370 if( answer
->type
== kDNSServiceType_A
)
9372 if( !listHasA
&& DomainNameEqual( answer
->name
, inName
) ) listHasA
= true;
9374 else if( answer
->type
== kDNSServiceType_AAAA
)
9376 if( !listHasAAAA
&& DomainNameEqual( answer
->name
, inName
) ) listHasAAAA
= true;
9378 if( listHasA
&& listHasAAAA
) break;
9382 if( ( inType
== kDNSServiceType_A
) || ( ( inType
== kDNSServiceType_ANY
) && !listHasA
) )
9384 for( i
= 1; i
<= inContext
->recordCountA
; ++i
)
9386 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9387 require_noerr( err
, exit
);
9390 rdataPtr
= (uint8_t *) malloc( rdataLen
);
9391 require_action( rdataPtr
, exit
, err
= kNoMemoryErr
);
9394 WriteBig16( &rdataPtr
[ 1 ], inIndex
);
9395 rdataPtr
[ 3 ] = (uint8_t) i
;
9397 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_A
, kDNSServiceClass_IN
, kMDNSRecordTTL_Host
,
9398 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9399 require_noerr( err
, exit
);
9403 *answerPtr
= answer
;
9404 answerPtr
= &answer
->next
;
9408 if( ( inType
== kDNSServiceType_AAAA
) || ( ( inType
== kDNSServiceType_ANY
) && !listHasAAAA
) )
9410 for( i
= 1; i
<= inContext
->recordCountAAAA
; ++i
)
9412 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9413 require_noerr( err
, exit
);
9416 rdataPtr
= (uint8_t *) _memdup( kMDNSReplierBaseAddrV6
, rdataLen
);
9417 require_action( rdataPtr
, exit
, err
= kNoMemoryErr
);
9419 WriteBig16( &rdataPtr
[ 12 ], inIndex
);
9420 rdataPtr
[ 15 ] = (uint8_t) i
;
9422 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_AAAA
, kDNSServiceClass_IN
, kMDNSRecordTTL_Host
,
9423 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9424 require_noerr( err
, exit
);
9428 *answerPtr
= answer
;
9429 answerPtr
= &answer
->next
;
9432 else if( inType
== kDNSServiceType_NSEC
)
9434 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9435 require_noerr( err
, exit
);
9437 if( ( inContext
->recordCountA
> 0 ) && ( inContext
->recordCountAAAA
> 0 ) )
9439 err
= CreateNSECRecordData( recordName
, &rdataPtr
, &rdataLen
, 2, kDNSServiceType_A
, kDNSServiceType_AAAA
);
9440 require_noerr( err
, exit
);
9442 else if( inContext
->recordCountA
> 0 )
9444 err
= CreateNSECRecordData( recordName
, &rdataPtr
, &rdataLen
, 1, kDNSServiceType_A
);
9445 require_noerr( err
, exit
);
9447 else if( inContext
->recordCountAAAA
> 0 )
9449 err
= CreateNSECRecordData( recordName
, &rdataPtr
, &rdataLen
, 1, kDNSServiceType_AAAA
);
9450 require_noerr( err
, exit
);
9454 err
= CreateNSECRecordData( recordName
, &rdataPtr
, &rdataLen
, 0 );
9455 require_noerr( err
, exit
);
9458 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_NSEC
, kDNSServiceClass_IN
, kMDNSRecordTTL_Host
,
9459 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9460 require_noerr( err
, exit
);
9464 *answerPtr
= answer
;
9467 else if( _MDNSReplierServiceTypeMatch( inContext
, inName
, NULL
, &count
) && ( count
>= inIndex
) )
9469 int listHasPTR
= false;
9471 if( inType
== kDNSServiceType_ANY
)
9473 for( answer
= *inAnswerList
; answer
; answer
= answer
->next
)
9475 if( ( answer
->type
== kDNSServiceType_PTR
) && DomainNameEqual( answer
->name
, inName
) )
9483 if( ( inType
== kDNSServiceType_PTR
) || ( ( inType
== kDNSServiceType_ANY
) && !listHasPTR
) )
9485 size_t recordNameLen
;
9489 err
= DomainNameDupLower( inName
, &recordName
, &recordNameLen
);
9490 require_noerr( err
, exit
);
9492 rdataLen
= 1 + hostname
[ 0 ] + 10 + recordNameLen
;
9493 rdataPtr
= (uint8_t *) malloc( rdataLen
);
9494 require_action( rdataPtr
, exit
, err
= kNoMemoryErr
);
9496 lim
= &rdataPtr
[ rdataLen
];
9498 ptr
= &rdataPtr
[ 1 ];
9499 memcpy( ptr
, &hostname
[ 1 ], hostname
[ 0 ] );
9500 ptr
+= hostname
[ 0 ];
9501 if( inIndex
!= 1 ) SNPrintF_Add( (char **) &ptr
, (char *) lim
, " (%u)", inIndex
);
9502 rdataPtr
[ 0 ] = (uint8_t)( ptr
- &rdataPtr
[ 1 ] );
9504 check( (size_t)( lim
- ptr
) >= recordNameLen
);
9505 memcpy( ptr
, recordName
, recordNameLen
);
9506 ptr
+= recordNameLen
;
9508 rdataLen
= (size_t)( ptr
- rdataPtr
);
9510 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_PTR
, kDNSServiceClass_IN
, kMDNSRecordTTL_Other
,
9511 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9512 require_noerr( err
, exit
);
9516 *answerPtr
= answer
;
9519 else if( _MDNSReplierServiceInstanceNameMatch( inContext
, inName
, &index
, &txtSize
, &count
) &&
9520 ( index
== inIndex
) && ( count
>= inIndex
) )
9522 int listHasSRV
= false;
9523 int listHasTXT
= false;
9525 if( inType
== kDNSServiceType_ANY
)
9527 for( answer
= *inAnswerList
; answer
; answer
= answer
->next
)
9529 if( answer
->type
== kDNSServiceType_SRV
)
9531 if( !listHasSRV
&& DomainNameEqual( answer
->name
, inName
) ) listHasSRV
= true;
9533 else if( answer
->type
== kDNSServiceType_TXT
)
9535 if( !listHasTXT
&& DomainNameEqual( answer
->name
, inName
) ) listHasTXT
= true;
9537 if( listHasSRV
&& listHasTXT
) break;
9541 if( ( inType
== kDNSServiceType_SRV
) || ( ( inType
== kDNSServiceType_ANY
) && !listHasSRV
) )
9543 dns_fixed_fields_srv
* fields
;
9546 uint8_t * targetPtr
;
9548 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9549 require_noerr( err
, exit
);
9551 rdataLen
= sizeof( dns_fixed_fields_srv
) + 1 + hostname
[ 0 ] + 10 + kLocalNameLen
;
9552 rdataPtr
= (uint8_t *) malloc( rdataLen
);
9553 require_action( rdataPtr
, exit
, err
= kNoMemoryErr
);
9555 lim
= &rdataPtr
[ rdataLen
];
9557 fields
= (dns_fixed_fields_srv
*) rdataPtr
;
9558 dns_fixed_fields_srv_init( fields
, 0, 0, (uint16_t)( kMDNSReplierPortBase
+ txtSize
) );
9560 targetPtr
= (uint8_t *) &fields
[ 1 ];
9562 ptr
= &targetPtr
[ 1 ];
9563 memcpy( ptr
, &hostname
[ 1 ], hostname
[ 0 ] );
9564 ptr
+= hostname
[ 0 ];
9565 if( inIndex
!= 1 ) SNPrintF_Add( (char **) &ptr
, (char *) lim
, "-%u", inIndex
);
9566 targetPtr
[ 0 ] = (uint8_t)( ptr
- &targetPtr
[ 1 ] );
9568 check( (size_t)( lim
- ptr
) >= kLocalNameLen
);
9569 memcpy( ptr
, kLocalName
, kLocalNameLen
);
9570 ptr
+= kLocalNameLen
;
9572 rdataLen
= (size_t)( ptr
- rdataPtr
);
9574 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_SRV
, kDNSServiceClass_IN
, kMDNSRecordTTL_Host
,
9575 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9576 require_noerr( err
, exit
);
9580 *answerPtr
= answer
;
9581 answerPtr
= &answer
->next
;
9584 if( ( inType
== kDNSServiceType_TXT
) || ( ( inType
== kDNSServiceType_ANY
) && !listHasTXT
) )
9586 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9587 require_noerr( err
, exit
);
9590 err
= _MDNSReplierCreateTXTRecord( inName
, rdataLen
, &rdataPtr
);
9591 require_noerr( err
, exit
);
9593 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_TXT
, kDNSServiceClass_IN
, kMDNSRecordTTL_Other
,
9594 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9595 require_noerr( err
, exit
);
9599 *answerPtr
= answer
;
9601 else if( inType
== kDNSServiceType_NSEC
)
9603 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9604 require_noerr( err
, exit
);
9606 err
= CreateNSECRecordData( recordName
, &rdataPtr
, &rdataLen
, 2, kDNSServiceType_TXT
, kDNSServiceType_SRV
);
9607 require_noerr( err
, exit
);
9609 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_NSEC
, kDNSServiceClass_IN
, kMDNSRecordTTL_Host
,
9610 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9611 require_noerr( err
, exit
);
9615 *answerPtr
= answer
;
9621 FreeNullSafe( recordName
);
9622 FreeNullSafe( rdataPtr
);
9626 //===========================================================================================================================
9627 // _MDNSReplierAnswerListRemovePTR
9628 //===========================================================================================================================
9631 _MDNSReplierAnswerListRemovePTR(
9632 MRResourceRecord
** inAnswerListPtr
,
9633 const uint8_t * inName
,
9634 const uint8_t * inRData
)
9636 MRResourceRecord
* answer
;
9637 MRResourceRecord
** answerPtr
;
9639 for( answerPtr
= inAnswerListPtr
; ( answer
= *answerPtr
) != NULL
; answerPtr
= &answer
->next
)
9641 if( ( answer
->type
== kDNSServiceType_PTR
) && ( answer
->class == kDNSServiceClass_IN
) &&
9642 DomainNameEqual( answer
->name
, inName
) && DomainNameEqual( answer
->rdata
, inRData
) ) break;
9646 *answerPtr
= answer
->next
;
9647 _MRResourceRecordFree( answer
);
9651 //===========================================================================================================================
9652 // _MDNSReplierSendOrDropResponse
9653 //===========================================================================================================================
9656 _MDNSReplierSendOrDropResponse(
9657 MDNSReplierContext
* inContext
,
9658 MRResourceRecord
* inAnswerList
,
9659 sockaddr_ip
* inQuerier
,
9661 unsigned int inIndex
,
9665 uint8_t * responsePtr
= NULL
;
9667 const struct sockaddr
* destAddr
;
9669 const double dropRate
= inUnicast
? inContext
->ucastDropRate
: inContext
->mcastDropRate
;
9672 check( inIndex
<= inContext
->maxInstanceCount
);
9674 // If maxDropCount > 0, then the drop rates apply only to the first maxDropCount responses. Otherwise, all messages are
9675 // subject to their respective drop rate. Also, responses to queries about mDNS replier itself (indicated by index 0),
9676 // as opposed to those for service instance records, are never dropped.
9681 if( inContext
->maxDropCount
> 0 )
9683 uint8_t * const dropCount
= &inContext
->dropCounters
[ inIndex
- 1 ];
9685 if( *dropCount
< inContext
->maxDropCount
)
9687 if( ShouldDrop( dropRate
) ) drop
= true;
9691 else if( ShouldDrop( dropRate
) )
9697 err
= _MDNSReplierCreateResponse( inContext
, inAnswerList
, inIndex
, &responsePtr
, &responseLen
);
9698 require_noerr( err
, exit
);
9702 destAddr
= &inQuerier
->sa
;
9706 destAddr
= ( inQuerier
->sa
.sa_family
== AF_INET
) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
9709 mr_ulog( kLogLevelInfo
, "%s %zu byte response to %##a:\n\n%#1{du:dnsmsg}",
9710 drop
? "Dropping" : "Sending", responseLen
, destAddr
, responsePtr
, responseLen
);
9714 n
= sendto( inSock
, (char *) responsePtr
, responseLen
, 0, destAddr
, SockAddrGetSize( destAddr
) );
9715 err
= map_socket_value_errno( inSock
, n
== (ssize_t
) responseLen
, n
);
9716 require_noerr( err
, exit
);
9720 FreeNullSafe( responsePtr
);
9724 //===========================================================================================================================
9725 // _MDNSReplierCreateResponse
9726 //===========================================================================================================================
9729 _MDNSReplierCreateResponse(
9730 MDNSReplierContext
* inContext
,
9731 MRResourceRecord
* inAnswerList
,
9732 unsigned int inIndex
,
9733 uint8_t ** outResponsePtr
,
9734 size_t * outResponseLen
)
9737 DataBuffer responseDB
;
9739 MRResourceRecord
* answer
;
9740 uint8_t * responsePtr
;
9741 size_t responseLen
, len
;
9742 unsigned int answerCount
, recordCount
;
9743 MRNameOffsetItem
* nameOffsetList
= NULL
;
9745 DataBuffer_Init( &responseDB
, NULL
, 0, SIZE_MAX
);
9747 // The current answers in the answer list will make up the response's Answer Record Section.
9750 for( answer
= inAnswerList
; answer
; answer
= answer
->next
) { ++answerCount
; }
9752 // Unless configured not to, add any additional answers to the answer list for the Additional Record Section.
9754 if( !inContext
->noAdditionals
)
9756 for( answer
= inAnswerList
; answer
; answer
= answer
->next
)
9758 switch( answer
->type
)
9760 case kDNSServiceType_PTR
:
9761 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->rdata
, kDNSServiceType_SRV
,
9763 require_noerr( err
, exit
);
9765 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->rdata
, kDNSServiceType_TXT
,
9767 require_noerr( err
, exit
);
9770 case kDNSServiceType_SRV
:
9771 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->target
, kDNSServiceType_A
,
9773 require_noerr( err
, exit
);
9775 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->target
, kDNSServiceType_AAAA
,
9777 require_noerr( err
, exit
);
9779 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->name
, kDNSServiceType_NSEC
,
9781 require_noerr( err
, exit
);
9784 case kDNSServiceType_TXT
:
9785 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->name
, kDNSServiceType_NSEC
,
9787 require_noerr( err
, exit
);
9790 case kDNSServiceType_A
:
9791 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->name
, kDNSServiceType_AAAA
,
9793 require_noerr( err
, exit
);
9795 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->name
, kDNSServiceType_NSEC
,
9797 require_noerr( err
, exit
);
9800 case kDNSServiceType_AAAA
:
9801 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->name
, kDNSServiceType_A
,
9803 require_noerr( err
, exit
);
9805 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->name
, kDNSServiceType_NSEC
,
9807 require_noerr( err
, exit
);
9816 // Append a provisional header to the response message.
9818 memset( &hdr
, 0, sizeof( hdr
) );
9819 DNSHeaderSetFlags( &hdr
, kDNSHeaderFlag_Response
| kDNSHeaderFlag_AuthAnswer
);
9821 err
= DataBuffer_Append( &responseDB
, &hdr
, sizeof( hdr
) );
9822 require_noerr( err
, exit
);
9824 // Append answers to response message.
9826 responseLen
= DataBuffer_GetLen( &responseDB
);
9828 for( answer
= inAnswerList
; answer
; answer
= answer
->next
)
9830 dns_fixed_fields_record fields
;
9833 // Append record NAME.
9835 err
= _MDNSReplierAppendNameToResponse( &responseDB
, answer
->name
, &nameOffsetList
);
9836 require_noerr( err
, exit
);
9838 // Append record TYPE, CLASS, TTL, and provisional RDLENGTH.
9840 class = answer
->class;
9841 if( ( answer
->type
== kDNSServiceType_SRV
) || ( answer
->type
== kDNSServiceType_TXT
) ||
9842 ( answer
->type
== kDNSServiceType_A
) || ( answer
->type
== kDNSServiceType_AAAA
) ||
9843 ( answer
->type
== kDNSServiceType_NSEC
) )
9845 class |= kRRClassCacheFlushBit
;
9848 dns_fixed_fields_record_init( &fields
, answer
->type
, (uint16_t) class, answer
->ttl
, (uint16_t) answer
->rdlength
);
9849 err
= DataBuffer_Append( &responseDB
, &fields
, sizeof( fields
) );
9850 require_noerr( err
, exit
);
9852 // Append record RDATA.
9853 // The RDATA of PTR, SRV, and NSEC records contain domain names, which are subject to name compression.
9855 if( ( answer
->type
== kDNSServiceType_PTR
) || ( answer
->type
== kDNSServiceType_SRV
) ||
9856 ( answer
->type
== kDNSServiceType_NSEC
) )
9859 uint8_t * rdLengthPtr
;
9860 const size_t rdLengthOffset
= DataBuffer_GetLen( &responseDB
) - 2;
9861 const size_t rdataOffset
= DataBuffer_GetLen( &responseDB
);
9863 if( answer
->type
== kDNSServiceType_PTR
)
9865 err
= _MDNSReplierAppendNameToResponse( &responseDB
, answer
->rdata
, &nameOffsetList
);
9866 require_noerr( err
, exit
);
9868 else if( answer
->type
== kDNSServiceType_SRV
)
9870 require_fatal( answer
->target
== &answer
->rdata
[ 6 ], "Bad SRV record target pointer." );
9872 err
= DataBuffer_Append( &responseDB
, answer
->rdata
, (size_t)( answer
->target
- answer
->rdata
) );
9873 require_noerr( err
, exit
);
9875 err
= _MDNSReplierAppendNameToResponse( &responseDB
, answer
->target
, &nameOffsetList
);
9876 require_noerr( err
, exit
);
9880 const size_t nameLen
= DomainNameLength( answer
->rdata
);
9882 err
= _MDNSReplierAppendNameToResponse( &responseDB
, answer
->rdata
, &nameOffsetList
);
9883 require_noerr( err
, exit
);
9885 require_fatal( answer
->rdlength
> nameLen
, "Bad NSEC record data length." );
9887 err
= DataBuffer_Append( &responseDB
, &answer
->rdata
[ nameLen
], answer
->rdlength
- nameLen
);
9888 require_noerr( err
, exit
);
9891 // Set the actual RDLENGTH, which may be less than the original due to name compression.
9893 rdlength
= DataBuffer_GetLen( &responseDB
) - rdataOffset
;
9894 check( rdlength
<= UINT16_MAX
);
9896 rdLengthPtr
= DataBuffer_GetPtr( &responseDB
) + rdLengthOffset
;
9897 WriteBig16( rdLengthPtr
, rdlength
);
9901 err
= DataBuffer_Append( &responseDB
, answer
->rdata
, answer
->rdlength
);
9902 require_noerr( err
, exit
);
9905 if( DataBuffer_GetLen( &responseDB
) > kMDNSMessageSizeMax
) break;
9906 responseLen
= DataBuffer_GetLen( &responseDB
);
9910 // Set the response header's Answer and Additional record counts.
9911 // Note: recordCount may be less than answerCount if including all answerCount records would cause the size of the
9912 // response message to exceed the maximum mDNS message size.
9914 if( recordCount
<= answerCount
)
9916 DNSHeaderSetAnswerCount( (DNSHeader
*) DataBuffer_GetPtr( &responseDB
), recordCount
);
9920 DNSHeaderSetAnswerCount( (DNSHeader
*) DataBuffer_GetPtr( &responseDB
), answerCount
);
9921 DNSHeaderSetAdditionalCount( (DNSHeader
*) DataBuffer_GetPtr( &responseDB
), recordCount
- answerCount
);
9924 err
= DataBuffer_Detach( &responseDB
, &responsePtr
, &len
);
9925 require_noerr( err
, exit
);
9927 if( outResponsePtr
) *outResponsePtr
= responsePtr
;
9928 if( outResponseLen
) *outResponseLen
= responseLen
;
9931 _MRNameOffsetItemFreeList( nameOffsetList
);
9932 DataBuffer_Free( &responseDB
);
9936 //===========================================================================================================================
9937 // _MDNSReplierAppendNameToResponse
9938 //===========================================================================================================================
9941 _MDNSReplierAppendNameToResponse(
9942 DataBuffer
* inResponse
,
9943 const uint8_t * inName
,
9944 MRNameOffsetItem
** inNameOffsetListPtr
)
9947 const uint8_t * subname
;
9948 const uint8_t * limit
;
9950 MRNameOffsetItem
* item
;
9951 uint8_t compressionPtr
[ 2 ];
9953 nameOffset
= DataBuffer_GetLen( inResponse
);
9955 // Find the name's longest subname (more accurately, its longest sub-FQDN) in the name compression list.
9957 for( subname
= inName
; subname
[ 0 ] != 0; subname
+= ( 1 + subname
[ 0 ] ) )
9959 for( item
= *inNameOffsetListPtr
; item
; item
= item
->next
)
9961 if( DomainNameEqual( item
->name
, subname
) ) break;
9964 // If an item was found for this subname, then append a name compression pointer and we're done. Otherwise, append
9965 // the subname's first label.
9969 DNSMessageWriteLabelPointer( compressionPtr
, item
->offset
);
9971 err
= DataBuffer_Append( inResponse
, compressionPtr
, sizeof( compressionPtr
) );
9972 require_noerr( err
, exit
);
9977 err
= DataBuffer_Append( inResponse
, subname
, 1 + subname
[ 0 ] );
9978 require_noerr( err
, exit
);
9982 // 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
9983 // label were appended to the response message, so a root label is needed to terminate the complete name.
9985 if( subname
[ 0 ] == 0 )
9987 err
= DataBuffer_Append( inResponse
, "", 1 );
9988 require_noerr( err
, exit
);
9991 // Add subnames that weren't able to be compressed and their offsets to the name compression list.
9994 for( subname
= inName
; subname
< limit
; subname
+= ( 1 + subname
[ 0 ] ) )
9996 const size_t subnameOffset
= nameOffset
+ (size_t)( subname
- inName
);
9998 if( subnameOffset
> kDNSCompressionOffsetMax
) break;
10000 err
= _MRNameOffsetItemCreate( subname
, (uint16_t) subnameOffset
, &item
);
10001 require_noerr( err
, exit
);
10003 item
->next
= *inNameOffsetListPtr
;
10004 *inNameOffsetListPtr
= item
;
10012 //===========================================================================================================================
10013 // _MDNSReplierServiceTypeMatch
10014 //===========================================================================================================================
10017 _MDNSReplierServiceTypeMatch(
10018 const MDNSReplierContext
* inContext
,
10019 const uint8_t * inName
,
10020 unsigned int * outTXTSize
,
10021 unsigned int * outCount
)
10026 uint32_t txtSize
, count
;
10027 const uint8_t * const serviceLabel
= inContext
->serviceLabel
;
10028 int nameMatches
= false;
10030 require_quiet( inName
[ 0 ] >= serviceLabel
[ 0 ], exit
);
10031 if( _memicmp( &inName
[ 1 ], &serviceLabel
[ 1 ], serviceLabel
[ 0 ] ) != 0 ) goto exit
;
10033 ptr
= (const char *) &inName
[ 1 + serviceLabel
[ 0 ] ];
10034 end
= (const char *) &inName
[ 1 + inName
[ 0 ] ];
10036 require_quiet( ( ptr
< end
) && ( *ptr
== '-' ), exit
);
10039 err
= DecimalTextToUInt32( ptr
, end
, &txtSize
, &ptr
);
10040 require_noerr_quiet( err
, exit
);
10041 require_quiet( txtSize
<= UINT16_MAX
, exit
);
10043 require_quiet( ( ptr
< end
) && ( *ptr
== '-' ), exit
);
10046 err
= DecimalTextToUInt32( ptr
, end
, &count
, &ptr
);
10047 require_noerr_quiet( err
, exit
);
10048 require_quiet( count
<= UINT16_MAX
, exit
);
10049 require_quiet( ptr
== end
, exit
);
10051 if( !DomainNameEqual( (const uint8_t *) ptr
, (const uint8_t *) "\x04" "_tcp" "\x05" "local" ) ) goto exit
;
10052 nameMatches
= true;
10054 if( outTXTSize
) *outTXTSize
= txtSize
;
10055 if( outCount
) *outCount
= count
;
10058 return( nameMatches
? true : false );
10061 //===========================================================================================================================
10062 // _MDNSReplierServiceInstanceNameMatch
10063 //===========================================================================================================================
10066 _MDNSReplierServiceInstanceNameMatch(
10067 const MDNSReplierContext
* inContext
,
10068 const uint8_t * inName
,
10069 unsigned int * outIndex
,
10070 unsigned int * outTXTSize
,
10071 unsigned int * outCount
)
10074 const uint8_t * ptr
;
10075 const uint8_t * end
;
10077 unsigned int txtSize
, count
;
10078 const uint8_t * const hostname
= inContext
->hostname
;
10079 int nameMatches
= false;
10081 require_quiet( inName
[ 0 ] >= hostname
[ 0 ], exit
);
10082 if( _memicmp( &inName
[ 1 ], &hostname
[ 1 ], hostname
[ 0 ] ) != 0 ) goto exit
;
10084 ptr
= &inName
[ 1 + hostname
[ 0 ] ];
10085 end
= &inName
[ 1 + inName
[ 0 ] ];
10088 require_quiet( ( end
- ptr
) >= 2, exit
);
10089 require_quiet( ( ptr
[ 0 ] == ' ' ) && ( ptr
[ 1 ] == '(' ), exit
);
10092 err
= DecimalTextToUInt32( (const char *) ptr
, (const char *) end
, &index
, (const char **) &ptr
);
10093 require_noerr_quiet( err
, exit
);
10094 require_quiet( ( index
>= 2 ) && ( index
<= UINT16_MAX
), exit
);
10096 require_quiet( ( ( end
- ptr
) == 1 ) && ( *ptr
== ')' ), exit
);
10104 if( !_MDNSReplierServiceTypeMatch( inContext
, ptr
, &txtSize
, &count
) ) goto exit
;
10105 nameMatches
= true;
10107 if( outIndex
) *outIndex
= index
;
10108 if( outTXTSize
) *outTXTSize
= txtSize
;
10109 if( outCount
) *outCount
= count
;
10112 return( nameMatches
? true : false );
10115 //===========================================================================================================================
10116 // _MDNSReplierAboutRecordNameMatch
10117 //===========================================================================================================================
10119 #define _MemIEqual( PTR1, LEN1, PTR2, LEN2 ) \
10120 ( ( ( LEN1 ) == ( LEN2 ) ) && ( _memicmp( ( PTR1 ), ( PTR2 ), ( LEN1 ) ) == 0 ) )
10122 static Boolean
_MDNSReplierAboutRecordNameMatch( const MDNSReplierContext
*inContext
, const uint8_t *inName
)
10124 const uint8_t * subname
;
10125 const uint8_t * const hostname
= inContext
->hostname
;
10126 int nameMatches
= false;
10128 if( strnicmpx( &inName
[ 1 ], inName
[ 0 ], "about" ) != 0 ) goto exit
;
10129 subname
= DomainNameGetNextLabel( inName
);
10131 if( !_MemIEqual( &subname
[ 1 ], subname
[ 0 ], &hostname
[ 1 ], hostname
[ 0 ] ) ) goto exit
;
10132 subname
= DomainNameGetNextLabel( subname
);
10134 if( !DomainNameEqual( subname
, kLocalName
) ) goto exit
;
10135 nameMatches
= true;
10138 return( nameMatches
? true : false );
10141 //===========================================================================================================================
10142 // _MDNSReplierHostnameMatch
10143 //===========================================================================================================================
10146 _MDNSReplierHostnameMatch(
10147 const MDNSReplierContext
* inContext
,
10148 const uint8_t * inName
,
10149 unsigned int * outIndex
)
10152 const uint8_t * ptr
;
10153 const uint8_t * end
;
10155 const uint8_t * const hostname
= inContext
->hostname
;
10156 int nameMatches
= false;
10158 require_quiet( inName
[ 0 ] >= hostname
[ 0 ], exit
);
10159 if( _memicmp( &inName
[ 1 ], &hostname
[ 1 ], hostname
[ 0 ] ) != 0 ) goto exit
;
10161 ptr
= &inName
[ 1 + hostname
[ 0 ] ];
10162 end
= &inName
[ 1 + inName
[ 0 ] ];
10165 require_quiet( *ptr
== '-', exit
);
10168 err
= DecimalTextToUInt32( (const char *) ptr
, (const char *) end
, &index
, (const char **) &ptr
);
10169 require_noerr_quiet( err
, exit
);
10170 require_quiet( ( index
>= 2 ) && ( index
<= UINT16_MAX
), exit
);
10171 require_quiet( ptr
== end
, exit
);
10178 if( !DomainNameEqual( ptr
, kLocalName
) ) goto exit
;
10179 nameMatches
= true;
10181 if( outIndex
) *outIndex
= index
;
10184 return( nameMatches
? true : false );
10187 //===========================================================================================================================
10188 // _MDNSReplierCreateTXTRecord
10189 //===========================================================================================================================
10191 static OSStatus
_MDNSReplierCreateTXTRecord( const uint8_t *inRecordName
, size_t inSize
, uint8_t **outTXT
)
10196 size_t i
, wholeCount
, remCount
;
10199 uint8_t txtStr
[ 16 ];
10201 require_action_quiet( inSize
> 0, exit
, err
= kSizeErr
);
10203 txt
= (uint8_t *) malloc( inSize
);
10204 require_action( txt
, exit
, err
= kNoMemoryErr
);
10206 hash
= _FNV1( inRecordName
, DomainNameLength( inRecordName
) );
10209 n
= MemPrintF( &txtStr
[ 1 ], 15, "hash=0x%08X", hash
);
10213 wholeCount
= inSize
/ 16;
10214 for( i
= 0; i
< wholeCount
; ++i
)
10216 memcpy( ptr
, txtStr
, 16 );
10220 remCount
= inSize
% 16;
10223 txtStr
[ 0 ] = (uint8_t)( remCount
- 1 );
10224 memcpy( ptr
, txtStr
, remCount
);
10227 check( ptr
== &txt
[ inSize
] );
10236 //===========================================================================================================================
10237 // _MRResourceRecordCreate
10238 //===========================================================================================================================
10241 _MRResourceRecordCreate(
10246 uint16_t inRDLength
,
10248 MRResourceRecord
** outRecord
)
10251 MRResourceRecord
* obj
;
10253 obj
= (MRResourceRecord
*) calloc( 1, sizeof( *obj
) );
10254 require_action( obj
, exit
, err
= kNoMemoryErr
);
10256 obj
->name
= inName
;
10257 obj
->type
= inType
;
10258 obj
->class = inClass
;
10260 obj
->rdlength
= inRDLength
;
10261 obj
->rdata
= inRData
;
10263 if( inType
== kDNSServiceType_SRV
)
10265 require_action_quiet( obj
->rdlength
> sizeof( dns_fixed_fields_srv
), exit
, err
= kMalformedErr
);
10266 obj
->target
= obj
->rdata
+ sizeof( dns_fixed_fields_srv
);
10274 FreeNullSafe( obj
);
10278 //===========================================================================================================================
10279 // _MRResourceRecordFree
10280 //===========================================================================================================================
10282 static void _MRResourceRecordFree( MRResourceRecord
*inRecord
)
10284 ForgetMem( &inRecord
->name
);
10285 ForgetMem( &inRecord
->rdata
);
10289 //===========================================================================================================================
10290 // _MRResourceRecordFreeList
10291 //===========================================================================================================================
10293 static void _MRResourceRecordFreeList( MRResourceRecord
*inList
)
10295 MRResourceRecord
* record
;
10297 while( ( record
= inList
) != NULL
)
10299 inList
= record
->next
;
10300 _MRResourceRecordFree( record
);
10304 //===========================================================================================================================
10305 // _MRNameOffsetItemCreate
10306 //===========================================================================================================================
10308 static OSStatus
_MRNameOffsetItemCreate( const uint8_t *inName
, uint16_t inOffset
, MRNameOffsetItem
**outItem
)
10311 MRNameOffsetItem
* obj
;
10314 require_action_quiet( inOffset
<= kDNSCompressionOffsetMax
, exit
, err
= kSizeErr
);
10316 nameLen
= DomainNameLength( inName
);
10317 obj
= (MRNameOffsetItem
*) calloc( 1, offsetof( MRNameOffsetItem
, name
) + nameLen
);
10318 require_action( obj
, exit
, err
= kNoMemoryErr
);
10320 obj
->offset
= inOffset
;
10321 memcpy( obj
->name
, inName
, nameLen
);
10330 //===========================================================================================================================
10331 // _MRNameOffsetItemFree
10332 //===========================================================================================================================
10334 static void _MRNameOffsetItemFree( MRNameOffsetItem
*inItem
)
10339 //===========================================================================================================================
10340 // _MRNameOffsetItemFreeList
10341 //===========================================================================================================================
10343 static void _MRNameOffsetItemFreeList( MRNameOffsetItem
*inList
)
10345 MRNameOffsetItem
* item
;
10347 while( ( item
= inList
) != NULL
)
10349 inList
= item
->next
;
10350 _MRNameOffsetItemFree( item
);
10354 //===========================================================================================================================
10356 //===========================================================================================================================
10358 #define kGAIPerfStandardTTL ( 1 * kSecondsPerHour )
10360 typedef struct GAITesterPrivate
* GAITesterRef
;
10361 typedef struct GAITestCase GAITestCase
;
10365 const char * name
; // Domain name that was resolved.
10366 uint64_t connectionTimeUs
; // Time in microseconds that it took to create a DNS-SD connection.
10367 uint64_t firstTimeUs
; // Time in microseconds that it took to get the first address result.
10368 uint64_t timeUs
; // Time in microseconds that it took to get all expected address results.
10371 } GAITestItemResult
;
10373 typedef void ( *GAITesterStopHandler_f
)( void *inContext
, OSStatus inError
);
10375 ( *GAITesterResultsHandler_f
)(
10376 const char * inCaseTitle
,
10377 NanoTime64 inCaseStartTime
,
10378 NanoTime64 inCaseEndTime
,
10379 const GAITestItemResult
* inResultArray
,
10380 size_t inResultCount
,
10381 void * inContext
);
10383 typedef unsigned int GAITestAddrType
;
10384 #define kGAITestAddrType_None 0
10385 #define kGAITestAddrType_IPv4 ( 1U << 0 )
10386 #define kGAITestAddrType_IPv6 ( 1U << 1 )
10387 #define kGAITestAddrType_Both ( kGAITestAddrType_IPv4 | kGAITestAddrType_IPv6 )
10389 #define GAITestAddrTypeIsValid( X ) \
10390 ( ( (X) & kGAITestAddrType_Both ) && ( ( (X) & ~kGAITestAddrType_Both ) == 0 ) )
10394 GAITesterRef tester
; // GAI tester object.
10395 CFMutableArrayRef testCaseResults
; // Array of test case results.
10396 unsigned int iterTimeLimitMs
; // Amount of time to allow each iteration to complete.
10397 unsigned int callDelayMs
; // Amount of time to wait before calling DNSServiceGetAddrInfo().
10398 unsigned int serverDelayMs
; // Amount of additional time to have server delay its responses.
10399 unsigned int defaultIterCount
; // Default test case iteration count.
10400 dispatch_source_t sigIntSource
; // Dispatch source for SIGINT.
10401 dispatch_source_t sigTermSource
; // Dispatch source for SIGTERM.
10402 char * outputFilePath
; // File to write test results to. If NULL, then write to stdout.
10403 OutputFormatType outputFormat
; // Format of test results output.
10404 Boolean skipPathEval
; // True if DNSServiceGetAddrInfo() path evaluation is to be skipped.
10405 Boolean badUDPMode
; // True if the test DNS server is to run in Bad UDP mode.
10406 Boolean testFailed
; // True if at least one test case iteration failed.
10410 static void GAIPerfContextFree( GAIPerfContext
*inContext
);
10411 static OSStatus
GAIPerfAddAdvancedTestCases( GAIPerfContext
*inContext
);
10412 static OSStatus
GAIPerfAddBasicTestCases( GAIPerfContext
*inContext
);
10413 static void GAIPerfTesterStopHandler( void *inContext
, OSStatus inError
);
10415 GAIPerfResultsHandler(
10416 const char * inCaseTitle
,
10417 NanoTime64 inCaseStartTime
,
10418 NanoTime64 inCaseEndTime
,
10419 const GAITestItemResult
* inResultArray
,
10420 size_t inResultCount
,
10421 void * inContext
);
10422 static void GAIPerfSignalHandler( void *inContext
);
10424 CFTypeID
GAITesterGetTypeID( void );
10427 dispatch_queue_t inQueue
,
10428 unsigned int inCallDelayMs
,
10429 int inServerDelayMs
,
10430 int inServerDefaultTTL
,
10431 Boolean inSkipPathEvaluation
,
10432 Boolean inBadUDPMode
,
10433 GAITesterRef
* outTester
);
10434 static void GAITesterStart( GAITesterRef inTester
);
10435 static void GAITesterStop( GAITesterRef inTester
);
10436 static OSStatus
GAITesterAddTestCase( GAITesterRef inTester
, GAITestCase
*inCase
);
10438 GAITesterSetStopHandler(
10439 GAITesterRef inTester
,
10440 GAITesterStopHandler_f inEventHandler
,
10441 void * inEventContext
);
10443 GAITesterSetResultsHandler(
10444 GAITesterRef inTester
,
10445 GAITesterResultsHandler_f inResultsHandler
,
10446 void * inResultsContext
);
10448 static OSStatus
GAITestCaseCreate( const char *inTitle
, GAITestCase
**outCase
);
10449 static void GAITestCaseFree( GAITestCase
*inCase
);
10451 GAITestCaseAddItem(
10452 GAITestCase
* inCase
,
10453 unsigned int inAliasCount
,
10454 unsigned int inAddressCount
,
10456 GAITestAddrType inHasAddrs
,
10457 GAITestAddrType inWantAddrs
,
10458 unsigned int inTimeLimitMs
,
10459 unsigned int inItemCount
);
10461 GAITestCaseAddLocalHostItem(
10462 GAITestCase
* inCase
,
10463 GAITestAddrType inWantAddrs
,
10464 unsigned int inTimeLimitMs
,
10465 unsigned int inItemCount
);
10467 static void GAIPerfCmd( void )
10470 GAIPerfContext
* context
= NULL
;
10472 err
= CheckRootUser();
10473 require_noerr_quiet( err
, exit
);
10475 err
= CheckIntegerArgument( gGAIPerf_CallDelayMs
, "call delay (ms)", 0, INT_MAX
);
10476 require_noerr_quiet( err
, exit
);
10478 err
= CheckIntegerArgument( gGAIPerf_ServerDelayMs
, "server delay (ms)", 0, INT_MAX
);
10479 require_noerr_quiet( err
, exit
);
10481 err
= CheckIntegerArgument( gGAIPerf_IterationCount
, "iteration count", 1, INT_MAX
);
10482 require_noerr_quiet( err
, exit
);
10484 err
= CheckIntegerArgument( gGAIPerf_IterationTimeLimitMs
, "iteration time limit (ms)", 0, INT_MAX
);
10485 require_noerr_quiet( err
, exit
);
10487 context
= (GAIPerfContext
*) calloc( 1, sizeof( *context
) );
10488 require_action( context
, exit
, err
= kNoMemoryErr
);
10490 context
->testCaseResults
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
10491 require_action( context
->testCaseResults
, exit
, err
= kNoMemoryErr
);
10493 context
->iterTimeLimitMs
= (unsigned int) gGAIPerf_IterationTimeLimitMs
;
10494 context
->callDelayMs
= (unsigned int) gGAIPerf_CallDelayMs
;
10495 context
->serverDelayMs
= (unsigned int) gGAIPerf_ServerDelayMs
;
10496 context
->defaultIterCount
= (unsigned int) gGAIPerf_IterationCount
;
10497 context
->skipPathEval
= gGAIPerf_SkipPathEvalulation
? true : false;
10498 context
->badUDPMode
= gGAIPerf_BadUDPMode
? true : false;
10500 if( gGAIPerf_OutputFilePath
)
10502 context
->outputFilePath
= strdup( gGAIPerf_OutputFilePath
);
10503 require_action( context
->outputFilePath
, exit
, err
= kNoMemoryErr
);
10506 err
= OutputFormatFromArgString( gGAIPerf_OutputFormat
, &context
->outputFormat
);
10507 require_noerr_quiet( err
, exit
);
10509 err
= GAITesterCreate( dispatch_get_main_queue(), context
->callDelayMs
, (int) context
->serverDelayMs
,
10510 kGAIPerfStandardTTL
, context
->skipPathEval
, context
->badUDPMode
, &context
->tester
);
10511 require_noerr( err
, exit
);
10513 check( gGAIPerf_TestSuite
);
10514 if( strcasecmp( gGAIPerf_TestSuite
, kGAIPerfTestSuiteName_Basic
) == 0 )
10516 err
= GAIPerfAddBasicTestCases( context
);
10517 require_noerr( err
, exit
);
10519 else if( strcasecmp( gGAIPerf_TestSuite
, kGAIPerfTestSuiteName_Advanced
) == 0 )
10521 err
= GAIPerfAddAdvancedTestCases( context
);
10522 require_noerr( err
, exit
);
10526 FPrintF( stderr
, "error: Invalid test suite name: %s.\n", gGAIPerf_TestSuite
);
10530 GAITesterSetStopHandler( context
->tester
, GAIPerfTesterStopHandler
, context
);
10531 GAITesterSetResultsHandler( context
->tester
, GAIPerfResultsHandler
, context
);
10533 signal( SIGINT
, SIG_IGN
);
10534 err
= DispatchSignalSourceCreate( SIGINT
, GAIPerfSignalHandler
, context
, &context
->sigIntSource
);
10535 require_noerr( err
, exit
);
10536 dispatch_resume( context
->sigIntSource
);
10538 signal( SIGTERM
, SIG_IGN
);
10539 err
= DispatchSignalSourceCreate( SIGTERM
, GAIPerfSignalHandler
, context
, &context
->sigTermSource
);
10540 require_noerr( err
, exit
);
10541 dispatch_resume( context
->sigTermSource
);
10543 GAITesterStart( context
->tester
);
10547 if( context
) GAIPerfContextFree( context
);
10551 //===========================================================================================================================
10552 // GAIPerfContextFree
10553 //===========================================================================================================================
10555 static void GAIPerfContextFree( GAIPerfContext
*inContext
)
10557 ForgetCF( &inContext
->tester
);
10558 ForgetCF( &inContext
->testCaseResults
);
10559 ForgetMem( &inContext
->outputFilePath
);
10560 dispatch_source_forget( &inContext
->sigIntSource
);
10561 dispatch_source_forget( &inContext
->sigTermSource
);
10565 //===========================================================================================================================
10566 // GAIPerfAddAdvancedTestCases
10567 //===========================================================================================================================
10569 #define kTestCaseTitleBufferSize 128
10572 _GAIPerfWriteTestCaseTitle(
10573 char inBuffer
[ kTestCaseTitleBufferSize
],
10574 unsigned int inCNAMERecordCount
,
10575 unsigned int inARecordCount
,
10576 unsigned int inAAAARecordCount
,
10577 GAITestAddrType inRequested
,
10578 unsigned int inIterationCount
,
10579 Boolean inIterationsAreUnique
);
10581 _GAIPerfWriteLocalHostTestCaseTitle(
10582 char inBuffer
[ kTestCaseTitleBufferSize
],
10583 GAITestAddrType inRequested
,
10584 unsigned int inIterationCount
);
10586 #define kGAIPerfAdvancedTestSuite_MaxAliasCount 4
10587 #define kGAIPerfAdvancedTestSuite_MaxAddrCount 8
10589 static OSStatus
GAIPerfAddAdvancedTestCases( GAIPerfContext
*inContext
)
10592 unsigned int aliasCount
, addressCount
, i
;
10593 GAITestCase
* testCase
= NULL
;
10594 char title
[ kTestCaseTitleBufferSize
];
10597 while( aliasCount
<= kGAIPerfAdvancedTestSuite_MaxAliasCount
)
10599 for( addressCount
= 1; addressCount
<= kGAIPerfAdvancedTestSuite_MaxAddrCount
; addressCount
*= 2 )
10601 // Add a test case to resolve a domain name with
10603 // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
10605 // to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which
10606 // requires server queries.
10608 _GAIPerfWriteTestCaseTitle( title
, aliasCount
, addressCount
, addressCount
, kGAITestAddrType_Both
,
10609 inContext
->defaultIterCount
, true );
10611 err
= GAITestCaseCreate( title
, &testCase
);
10612 require_noerr( err
, exit
);
10614 for( i
= 0; i
< inContext
->defaultIterCount
; ++i
)
10616 err
= GAITestCaseAddItem( testCase
, aliasCount
, addressCount
, kGAIPerfStandardTTL
,
10617 kGAITestAddrType_Both
, kGAITestAddrType_Both
, inContext
->iterTimeLimitMs
, 1 );
10618 require_noerr( err
, exit
);
10621 err
= GAITesterAddTestCase( inContext
->tester
, testCase
);
10622 require_noerr( err
, exit
);
10625 // Add a test case to resolve a domain name with
10627 // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
10629 // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique domain name, which requires a server
10630 // query. The subsequent iterations resolve the same domain name as the preliminary iteration, which should
10631 // ideally require no server queries, i.e., the results should come from the cache.
10633 _GAIPerfWriteTestCaseTitle( title
, aliasCount
, addressCount
, addressCount
, kGAITestAddrType_Both
,
10634 inContext
->defaultIterCount
, false );
10636 err
= GAITestCaseCreate( title
, &testCase
);
10637 require_noerr( err
, exit
);
10639 err
= GAITestCaseAddItem( testCase
, aliasCount
, addressCount
, kGAIPerfStandardTTL
,
10640 kGAITestAddrType_Both
, kGAITestAddrType_Both
, inContext
->iterTimeLimitMs
, inContext
->defaultIterCount
+ 1 );
10641 require_noerr( err
, exit
);
10643 err
= GAITesterAddTestCase( inContext
->tester
, testCase
);
10644 require_noerr( err
, exit
);
10648 aliasCount
= ( aliasCount
== 0 ) ? 1 : ( 2 * aliasCount
);
10651 // Finally, add a test case to resolve localhost to its IPv4 and IPv6 addresses.
10653 _GAIPerfWriteLocalHostTestCaseTitle( title
, kGAITestAddrType_Both
, inContext
->defaultIterCount
);
10655 err
= GAITestCaseCreate( title
, &testCase
);
10656 require_noerr( err
, exit
);
10658 err
= GAITestCaseAddLocalHostItem( testCase
, kGAITestAddrType_Both
, inContext
->iterTimeLimitMs
,
10659 inContext
->defaultIterCount
);
10660 require_noerr( err
, exit
);
10662 err
= GAITesterAddTestCase( inContext
->tester
, testCase
);
10663 require_noerr( err
, exit
);
10667 if( testCase
) GAITestCaseFree( testCase
);
10671 //===========================================================================================================================
10672 // _GAIPerfWriteTestCaseTitle
10673 //===========================================================================================================================
10675 #define GAITestAddrTypeToRequestKeyValue( X ) ( \
10676 ( (X) == kGAITestAddrType_Both ) ? "ipv4\\,ipv6" : \
10677 ( (X) == kGAITestAddrType_IPv4 ) ? "ipv4" : \
10678 ( (X) == kGAITestAddrType_IPv6 ) ? "ipv6" : \
10682 _GAIPerfWriteTestCaseTitle(
10683 char inBuffer
[ kTestCaseTitleBufferSize
],
10684 unsigned int inCNAMERecordCount
,
10685 unsigned int inARecordCount
,
10686 unsigned int inAAAARecordCount
,
10687 GAITestAddrType inRequested
,
10688 unsigned int inIterationCount
,
10689 Boolean inIterationsAreUnique
)
10691 SNPrintF( inBuffer
, kTestCaseTitleBufferSize
, "name=dynamic,cname=%u,a=%u,aaaa=%u,req=%s,iterations=%u%?s",
10692 inCNAMERecordCount
, inARecordCount
, inAAAARecordCount
, GAITestAddrTypeToRequestKeyValue( inRequested
),
10693 inIterationCount
, inIterationsAreUnique
, ",unique" );
10696 //===========================================================================================================================
10697 // _GAIPerfWriteLocalHostTestCaseTitle
10698 //===========================================================================================================================
10701 _GAIPerfWriteLocalHostTestCaseTitle(
10702 char inBuffer
[ kTestCaseTitleBufferSize
],
10703 GAITestAddrType inRequested
,
10704 unsigned int inIterationCount
)
10706 SNPrintF( inBuffer
, kTestCaseTitleBufferSize
, "name=localhost,req=%s,iterations=%u",
10707 GAITestAddrTypeToRequestKeyValue( inRequested
), inIterationCount
);
10710 //===========================================================================================================================
10711 // GAIPerfAddBasicTestCases
10712 //===========================================================================================================================
10714 #define kGAIPerfBasicTestSuite_AliasCount 2
10715 #define kGAIPerfBasicTestSuite_AddrCount 4
10717 static OSStatus
GAIPerfAddBasicTestCases( GAIPerfContext
*inContext
)
10720 GAITestCase
* testCase
= NULL
;
10721 char title
[ kTestCaseTitleBufferSize
];
10725 // Resolve a domain name with
10727 // 2 CNAME records, 4 A records, and 4 AAAA records
10729 // to its IPv4 and IPv6 addresses. Each of the iterations resolves a unique domain name, which requires server
10732 _GAIPerfWriteTestCaseTitle( title
, kGAIPerfBasicTestSuite_AliasCount
,
10733 kGAIPerfBasicTestSuite_AddrCount
, kGAIPerfBasicTestSuite_AddrCount
, kGAITestAddrType_Both
,
10734 inContext
->defaultIterCount
, true );
10736 err
= GAITestCaseCreate( title
, &testCase
);
10737 require_noerr( err
, exit
);
10739 for( i
= 0; i
< inContext
->defaultIterCount
; ++i
)
10741 err
= GAITestCaseAddItem( testCase
, kGAIPerfBasicTestSuite_AliasCount
, kGAIPerfBasicTestSuite_AddrCount
,
10742 kGAIPerfStandardTTL
, kGAITestAddrType_Both
, kGAITestAddrType_Both
, inContext
->iterTimeLimitMs
, 1 );
10743 require_noerr( err
, exit
);
10746 err
= GAITesterAddTestCase( inContext
->tester
, testCase
);
10747 require_noerr( err
, exit
);
10751 // Resolve a domain name with
10753 // 2 CNAME records, 4 A records, and 4 AAAA records
10755 // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which
10756 // requires server queries. Each of the subsequent iterations resolves the same domain name as the preliminary
10757 // iteration, which should ideally require no additional server queries, i.e., the results should come from the cache.
10759 _GAIPerfWriteTestCaseTitle( title
, kGAIPerfBasicTestSuite_AliasCount
,
10760 kGAIPerfBasicTestSuite_AddrCount
, kGAIPerfBasicTestSuite_AddrCount
, kGAITestAddrType_Both
,
10761 inContext
->defaultIterCount
, false );
10763 err
= GAITestCaseCreate( title
, &testCase
);
10764 require_noerr( err
, exit
);
10766 err
= GAITestCaseAddItem( testCase
, kGAIPerfBasicTestSuite_AliasCount
, kGAIPerfBasicTestSuite_AddrCount
,
10767 kGAIPerfStandardTTL
, kGAITestAddrType_Both
, kGAITestAddrType_Both
, inContext
->iterTimeLimitMs
,
10768 inContext
->defaultIterCount
+ 1 );
10769 require_noerr( err
, exit
);
10771 err
= GAITesterAddTestCase( inContext
->tester
, testCase
);
10772 require_noerr( err
, exit
);
10776 // Each iteration resolves localhost to its IPv4 and IPv6 addresses.
10778 _GAIPerfWriteLocalHostTestCaseTitle( title
, kGAITestAddrType_Both
, inContext
->defaultIterCount
);
10780 err
= GAITestCaseCreate( title
, &testCase
);
10781 require_noerr( err
, exit
);
10783 err
= GAITestCaseAddLocalHostItem( testCase
, kGAITestAddrType_Both
, inContext
->iterTimeLimitMs
,
10784 inContext
->defaultIterCount
);
10785 require_noerr( err
, exit
);
10787 err
= GAITesterAddTestCase( inContext
->tester
, testCase
);
10788 require_noerr( err
, exit
);
10792 if( testCase
) GAITestCaseFree( testCase
);
10796 //===========================================================================================================================
10797 // GAIPerfTesterStopHandler
10798 //===========================================================================================================================
10800 #define kGAIPerfResultsKey_Info CFSTR( "info" )
10801 #define kGAIPerfResultsKey_TestCases CFSTR( "testCases" )
10802 #define kGAIPerfResultsKey_Success CFSTR( "success" )
10804 #define kGAIPerfInfoKey_CallDelay CFSTR( "callDelayMs" )
10805 #define kGAIPerfInfoKey_ServerDelay CFSTR( "serverDelayMs" )
10806 #define kGAIPerfInfoKey_SkippedPathEval CFSTR( "skippedPathEval" )
10807 #define kGAIPerfInfoKey_UsedBadUDPMode CFSTR( "usedBadUPDMode" )
10809 static void GAIPerfTesterStopHandler( void *inContext
, OSStatus inError
)
10812 GAIPerfContext
* const context
= (GAIPerfContext
*) inContext
;
10813 CFPropertyListRef plist
;
10817 require_noerr_quiet( err
, exit
);
10819 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
10823 "%kO=%lli" // callDelayMs
10824 "%kO=%lli" // serverDelayMs
10825 "%kO=%b" // skippedPathEval
10826 "%kO=%b" // usedBadUPDMode
10828 "%kO=%O" // testCases
10829 "%kO=%b" // success
10831 kGAIPerfResultsKey_Info
,
10832 kGAIPerfInfoKey_CallDelay
, (int64_t) context
->callDelayMs
,
10833 kGAIPerfInfoKey_ServerDelay
, (int64_t) context
->serverDelayMs
,
10834 kGAIPerfInfoKey_SkippedPathEval
, context
->skipPathEval
,
10835 kGAIPerfInfoKey_UsedBadUDPMode
, context
->badUDPMode
,
10836 kGAIPerfResultsKey_TestCases
, context
->testCaseResults
,
10837 kGAIPerfResultsKey_Success
, !context
->testFailed
);
10838 require_noerr( err
, exit
);
10840 err
= OutputPropertyList( plist
, context
->outputFormat
, context
->outputFilePath
);
10841 CFRelease( plist
);
10842 require_noerr( err
, exit
);
10845 exitCode
= err
? 1 : ( context
->testFailed
? 2 : 0 );
10846 GAIPerfContextFree( context
);
10850 //===========================================================================================================================
10851 // GAIPerfResultsHandler
10852 //===========================================================================================================================
10854 // Keys for test case dictionary
10856 #define kGAIPerfTestCaseKey_Title CFSTR( "title" )
10857 #define kGAIPerfTestCaseKey_StartTime CFSTR( "startTime" )
10858 #define kGAIPerfTestCaseKey_EndTime CFSTR( "endTime" )
10859 #define kGAIPerfTestCaseKey_Results CFSTR( "results" )
10860 #define kGAIPerfTestCaseKey_FirstStats CFSTR( "firstStats" )
10861 #define kGAIPerfTestCaseKey_ConnectionStats CFSTR( "connectionStats" )
10862 #define kGAIPerfTestCaseKey_Stats CFSTR( "stats" )
10864 // Keys for test case results array entry dictionaries
10866 #define kGAIPerfTestCaseResultKey_Name CFSTR( "name" )
10867 #define kGAIPerfTestCaseResultKey_ConnectionTime CFSTR( "connectionTimeUs" )
10868 #define kGAIPerfTestCaseResultKey_FirstTime CFSTR( "firstTimeUs" )
10869 #define kGAIPerfTestCaseResultKey_Time CFSTR( "timeUs" )
10871 // Keys for test case stats dictionaries
10873 #define kGAIPerfTestCaseStatsKey_Count CFSTR( "count" )
10874 #define kGAIPerfTestCaseStatsKey_Min CFSTR( "min" )
10875 #define kGAIPerfTestCaseStatsKey_Max CFSTR( "max" )
10876 #define kGAIPerfTestCaseStatsKey_Mean CFSTR( "mean" )
10877 #define kGAIPerfTestCaseStatsKey_StdDev CFSTR( "sd" )
10888 #define GAIPerfStatsInit( X ) \
10889 do { (X)->min = DBL_MAX; (X)->max = DBL_MIN; (X)->mean = 0.0; (X)->stdDev = 0.0; } while( 0 )
10892 GAIPerfResultsHandler(
10893 const char * inCaseTitle
,
10894 NanoTime64 inCaseStartTime
,
10895 NanoTime64 inCaseEndTime
,
10896 const GAITestItemResult
* inResultArray
,
10897 size_t inResultCount
,
10901 GAIPerfContext
* const context
= (GAIPerfContext
*) inContext
;
10902 int namesAreDynamic
, namesAreUnique
;
10904 size_t count
, startIndex
;
10905 CFMutableArrayRef results
= NULL
;
10906 GAIPerfStats stats
, firstStats
, connStats
;
10907 double sum
, firstSum
, connSum
;
10908 size_t keyValueLen
, i
;
10909 char keyValue
[ 16 ]; // Size must be at least strlen( "name=dynamic" ) + 1 bytes.
10910 char startTime
[ 32 ];
10911 char endTime
[ 32 ];
10912 const GAITestItemResult
* result
;
10914 // If this test case resolves the same "d.test." name in each iteration (title contains the "name=dynamic" key-value
10915 // pair, but not the "unique" key), then don't count the first iteration, whose purpose is to populate the cache with
10916 // the domain name's CNAME, A, and AAAA records.
10918 namesAreDynamic
= false;
10919 namesAreUnique
= false;
10921 while( _ParseQuotedEscapedString( ptr
, NULL
, ",", keyValue
, sizeof( keyValue
), &keyValueLen
, NULL
, &ptr
) )
10923 if( strnicmpx( keyValue
, keyValueLen
, "name=dynamic" ) == 0 )
10925 namesAreDynamic
= true;
10927 else if( strnicmpx( keyValue
, keyValueLen
, "unique" ) == 0 )
10929 namesAreUnique
= true;
10931 if( namesAreDynamic
&& namesAreUnique
) break;
10934 startIndex
= ( ( inResultCount
> 0 ) && namesAreDynamic
&& !namesAreUnique
) ? 1 : 0;
10935 results
= CFArrayCreateMutable( NULL
, (CFIndex
)( inResultCount
- startIndex
), &kCFTypeArrayCallBacks
);
10936 require_action( results
, exit
, err
= kNoMemoryErr
);
10938 GAIPerfStatsInit( &stats
);
10939 GAIPerfStatsInit( &firstStats
);
10940 GAIPerfStatsInit( &connStats
);
10946 for( i
= startIndex
; i
< inResultCount
; ++i
)
10950 result
= &inResultArray
[ i
];
10952 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, results
,
10955 "%kO=%lli" // connectionTimeUs
10956 "%kO=%lli" // firstTimeUs
10957 "%kO=%lli" // timeUs
10958 "%kO=%lli" // error
10960 kGAIPerfTestCaseResultKey_Name
, result
->name
,
10961 kGAIPerfTestCaseResultKey_ConnectionTime
, (int64_t) result
->connectionTimeUs
,
10962 kGAIPerfTestCaseResultKey_FirstTime
, (int64_t) result
->firstTimeUs
,
10963 kGAIPerfTestCaseResultKey_Time
, (int64_t) result
->timeUs
,
10964 CFSTR( "error" ), (int64_t) result
->error
);
10965 require_noerr( err
, exit
);
10967 if( !result
->error
)
10969 value
= (double) result
->timeUs
;
10970 if( value
< stats
.min
) stats
.min
= value
;
10971 if( value
> stats
.max
) stats
.max
= value
;
10974 value
= (double) result
->firstTimeUs
;
10975 if( value
< firstStats
.min
) firstStats
.min
= value
;
10976 if( value
> firstStats
.max
) firstStats
.max
= value
;
10979 value
= (double) result
->connectionTimeUs
;
10980 if( value
< connStats
.min
) connStats
.min
= value
;
10981 if( value
> connStats
.max
) connStats
.max
= value
;
10988 context
->testFailed
= true;
10994 stats
.mean
= sum
/ count
;
10995 firstStats
.mean
= firstSum
/ count
;
10996 connStats
.mean
= connSum
/ count
;
11001 for( i
= startIndex
; i
< inResultCount
; ++i
)
11005 result
= &inResultArray
[ i
];
11006 if( result
->error
) continue;
11008 diff
= stats
.mean
- (double) result
->timeUs
;
11009 sum
+= ( diff
* diff
);
11011 diff
= firstStats
.mean
- (double) result
->firstTimeUs
;
11012 firstSum
+= ( diff
* diff
);
11014 diff
= connStats
.mean
- (double) result
->connectionTimeUs
;
11015 connSum
+= ( diff
* diff
);
11017 stats
.stdDev
= sqrt( sum
/ count
);
11018 firstStats
.stdDev
= sqrt( firstSum
/ count
);
11019 connStats
.stdDev
= sqrt( connSum
/ count
);
11022 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, context
->testCaseResults
,
11053 kGAIPerfTestCaseKey_Title
, inCaseTitle
,
11054 kGAIPerfTestCaseKey_StartTime
, _NanoTime64ToTimestamp( inCaseStartTime
, startTime
, sizeof( startTime
) ),
11055 kGAIPerfTestCaseKey_EndTime
, _NanoTime64ToTimestamp( inCaseEndTime
, endTime
, sizeof( endTime
) ),
11056 kGAIPerfTestCaseKey_Results
, results
,
11057 kGAIPerfTestCaseKey_Stats
,
11058 kGAIPerfTestCaseStatsKey_Count
, (int64_t) count
,
11059 kGAIPerfTestCaseStatsKey_Min
, stats
.min
,
11060 kGAIPerfTestCaseStatsKey_Max
, stats
.max
,
11061 kGAIPerfTestCaseStatsKey_Mean
, stats
.mean
,
11062 kGAIPerfTestCaseStatsKey_StdDev
, stats
.stdDev
,
11063 kGAIPerfTestCaseKey_FirstStats
,
11064 kGAIPerfTestCaseStatsKey_Count
, (int64_t) count
,
11065 kGAIPerfTestCaseStatsKey_Min
, firstStats
.min
,
11066 kGAIPerfTestCaseStatsKey_Max
, firstStats
.max
,
11067 kGAIPerfTestCaseStatsKey_Mean
, firstStats
.mean
,
11068 kGAIPerfTestCaseStatsKey_StdDev
, firstStats
.stdDev
,
11069 kGAIPerfTestCaseKey_ConnectionStats
,
11070 kGAIPerfTestCaseStatsKey_Count
, (int64_t) count
,
11071 kGAIPerfTestCaseStatsKey_Min
, connStats
.min
,
11072 kGAIPerfTestCaseStatsKey_Max
, connStats
.max
,
11073 kGAIPerfTestCaseStatsKey_Mean
, connStats
.mean
,
11074 kGAIPerfTestCaseStatsKey_StdDev
, connStats
.stdDev
);
11075 require_noerr( err
, exit
);
11078 CFReleaseNullSafe( results
);
11079 if( err
) exit( 1 );
11082 //===========================================================================================================================
11083 // GAIPerfSignalHandler
11084 //===========================================================================================================================
11086 static void GAIPerfSignalHandler( void *inContext
)
11088 GAIPerfContext
* const context
= (GAIPerfContext
*) inContext
;
11090 if( !context
->tester
) exit( 1 );
11091 GAITesterStop( context
->tester
);
11092 context
->tester
= NULL
;
11095 //===========================================================================================================================
11097 //===========================================================================================================================
11099 // A character set of lower-case alphabet characters and digits and a string length of six allows for 36^6 = 2,176,782,336
11100 // possible strings to use in the Tag label.
11102 #define kGAITesterTagStringLen 6
11104 typedef struct GAITestItem GAITestItem
;
11107 GAITestItem
* next
; // Next test item in list.
11108 char * name
; // Domain name to resolve.
11109 uint64_t connectionTimeUs
; // Time in microseconds that it took to create a DNS-SD connection.
11110 uint64_t firstTimeUs
; // Time in microseconds that it took to get the first address result.
11111 uint64_t timeUs
; // Time in microseconds that it took to get all expected address results.
11112 unsigned int addressCount
; // Address count of the domain name, i.e., the Count label argument.
11113 OSStatus error
; // Current status/error.
11114 unsigned int timeLimitMs
; // Time limit in milliseconds for the test item's completion.
11115 Boolean hasV4
; // True if the domain name has one or more IPv4 addresses.
11116 Boolean hasV6
; // True if the domain name has one or more IPv6 addresses.
11117 Boolean wantV4
; // True if DNSServiceGetAddrInfo() should be called to get IPv4 addresses.
11118 Boolean wantV6
; // True if DNSServiceGetAddrInfo() should be called to get IPv6 addresses.
11123 GAITestCase
* next
; // Next test case in list.
11124 GAITestItem
* itemList
; // List of test items.
11125 char * title
; // Title of the test case.
11128 struct GAITesterPrivate
11130 CFRuntimeBase base
; // CF object base.
11131 dispatch_queue_t queue
; // Serial work queue.
11132 DNSServiceRef connection
; // Reference to the shared DNS-SD connection.
11133 DNSServiceRef getAddrInfo
; // Reference to the current DNSServiceGetAddrInfo operation.
11134 GAITestCase
* caseList
; // List of test cases.
11135 GAITestCase
* currentCase
; // Pointer to the current test case.
11136 GAITestItem
* currentItem
; // Pointer to the current test item.
11137 NanoTime64 caseStartTime
; // Start time of current test case in Unix time as nanoseconds.
11138 NanoTime64 caseEndTime
; // End time of current test case in Unix time as nanoseconds.
11139 unsigned int callDelayMs
; // Amount of time to wait before calling DNSServiceGetAddrInfo().
11140 Boolean skipPathEval
; // True if DNSServiceGetAddrInfo() path evaluation is to be skipped.
11141 Boolean stopped
; // True if the tester has been stopped.
11142 Boolean badUDPMode
; // True if the test DNS server is to run in Bad UDP mode.
11143 dispatch_source_t timer
; // Timer for enforcing a test item's time limit.
11144 pcap_t
* pcap
; // Captures traffic between mDNSResponder and test DNS server.
11145 pid_t serverPID
; // PID of the test DNS server.
11146 int serverDelayMs
; // Additional time to have the server delay its responses by.
11147 int serverDefaultTTL
; // Default TTL for the server's records.
11148 GAITesterStopHandler_f stopHandler
; // User's stop handler.
11149 void * stopContext
; // User's event handler context.
11150 GAITesterResultsHandler_f resultsHandler
; // User's results handler.
11151 void * resultsContext
; // User's results handler context.
11153 // Variables for current test item.
11155 uint64_t bitmapV4
; // Bitmap of IPv4 results that have yet to be received.
11156 uint64_t bitmapV6
; // Bitmap of IPv6 results that have yet to be received.
11157 uint64_t startTicks
; // Start ticks of DNSServiceGetAddrInfo().
11158 uint64_t connTicks
; // Ticks when the connection was created.
11159 uint64_t firstTicks
; // Ticks when the first DNSServiceGetAddrInfo result was received.
11160 uint64_t endTicks
; // Ticks when the last DNSServiceGetAddrInfo result was received.
11161 Boolean gotFirstResult
; // True if the first result has been received.
11164 CF_CLASS_DEFINE( GAITester
);
11166 static void _GAITesterStartNextTest( GAITesterRef inTester
);
11167 static OSStatus
_GAITesterCreatePacketCapture( pcap_t
**outPCap
);
11168 static void _GAITesterFirstGAITimeout( void *inContext
);
11169 static void _GAITesterTimeout( void *inContext
);
11170 static void DNSSD_API
11171 _GAITesterFirstGAICallback(
11172 DNSServiceRef inSDRef
,
11173 DNSServiceFlags inFlags
,
11174 uint32_t inInterfaceIndex
,
11175 DNSServiceErrorType inError
,
11176 const char * inHostname
,
11177 const struct sockaddr
* inSockAddr
,
11179 void * inContext
);
11180 static void DNSSD_API
11181 _GAITesterGetAddrInfoCallback(
11182 DNSServiceRef inSDRef
,
11183 DNSServiceFlags inFlags
,
11184 uint32_t inInterfaceIndex
,
11185 DNSServiceErrorType inError
,
11186 const char * inHostname
,
11187 const struct sockaddr
* inSockAddr
,
11189 void * inContext
);
11190 static void _GAITesterCompleteCurrentTest( GAITesterRef inTester
, OSStatus inError
);
11192 #define ForgetPacketCapture( X ) ForgetCustom( X, pcap_close )
11196 const char * inName
,
11197 unsigned int inAddressCount
,
11198 GAITestAddrType inHasAddrs
,
11199 GAITestAddrType inWantAddrs
,
11200 unsigned int inTimeLimitMs
,
11201 GAITestItem
** outItem
);
11202 static OSStatus
GAITestItemDup( const GAITestItem
*inItem
, GAITestItem
**outItem
);
11203 static void GAITestItemFree( GAITestItem
*inItem
);
11207 dispatch_queue_t inQueue
,
11208 unsigned int inCallDelayMs
,
11209 int inServerDelayMs
,
11210 int inServerDefaultTTL
,
11211 Boolean inSkipPathEvaluation
,
11212 Boolean inBadUDPMode
,
11213 GAITesterRef
* outTester
)
11216 GAITesterRef obj
= NULL
;
11218 CF_OBJECT_CREATE( GAITester
, obj
, err
, exit
);
11220 ReplaceDispatchQueue( &obj
->queue
, inQueue
);
11221 obj
->callDelayMs
= inCallDelayMs
;
11222 obj
->serverPID
= -1;
11223 obj
->serverDelayMs
= inServerDelayMs
;
11224 obj
->serverDefaultTTL
= inServerDefaultTTL
;
11225 obj
->skipPathEval
= inSkipPathEvaluation
;
11226 obj
->badUDPMode
= inBadUDPMode
;
11233 CFReleaseNullSafe( obj
);
11237 //===========================================================================================================================
11238 // _GAITesterFinalize
11239 //===========================================================================================================================
11241 static void _GAITesterFinalize( CFTypeRef inObj
)
11243 GAITesterRef
const me
= (GAITesterRef
) inObj
;
11244 GAITestCase
* testCase
;
11246 check( !me
->getAddrInfo
);
11247 check( !me
->connection
);
11248 check( !me
->timer
);
11249 dispatch_forget( &me
->queue
);
11250 while( ( testCase
= me
->caseList
) != NULL
)
11252 me
->caseList
= testCase
->next
;
11253 GAITestCaseFree( testCase
);
11257 //===========================================================================================================================
11259 //===========================================================================================================================
11261 static void _GAITesterStart( void *inContext
);
11262 static void _GAITesterStop( GAITesterRef me
, OSStatus inError
);
11264 static void GAITesterStart( GAITesterRef me
)
11267 dispatch_async_f( me
->queue
, me
, _GAITesterStart
);
11270 #define kGAITesterFirstGAITimeoutSecs 4
11272 static void _GAITesterStart( void *inContext
)
11275 GAITesterRef
const me
= (GAITesterRef
) inContext
;
11276 DNSServiceFlags flags
;
11278 char tag
[ kGAITesterTagStringLen
+ 1 ];
11280 err
= SpawnCommand( &me
->serverPID
, "dnssdutil server --loopback --follow %lld%?s%?d%?s%?d%?s",
11281 (int64_t) getpid(),
11282 me
->serverDefaultTTL
>= 0, " --defaultTTL ",
11283 me
->serverDefaultTTL
>= 0, me
->serverDefaultTTL
,
11284 me
->serverDelayMs
>= 0, " --responseDelay ",
11285 me
->serverDelayMs
>= 0, me
->serverDelayMs
,
11286 me
->badUDPMode
, " --badUDPMode" );
11287 require_noerr_quiet( err
, exit
);
11289 SNPrintF( name
, sizeof( name
), "tag-gaitester-probe-%s.ipv4.d.test",
11290 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
11293 if( me
->skipPathEval
) flags
|= kDNSServiceFlagsPathEvaluationDone
;
11295 err
= DNSServiceGetAddrInfo( &me
->getAddrInfo
, flags
, kDNSServiceInterfaceIndexAny
, kDNSServiceProtocol_IPv4
, name
,
11296 _GAITesterFirstGAICallback
, me
);
11297 require_noerr( err
, exit
);
11299 err
= DNSServiceSetDispatchQueue( me
->getAddrInfo
, me
->queue
);
11300 require_noerr( err
, exit
);
11302 err
= DispatchTimerOneShotCreate( dispatch_time_seconds( kGAITesterFirstGAITimeoutSecs
),
11303 UINT64_C_safe( kGAITesterFirstGAITimeoutSecs
) * kNanosecondsPerSecond
/ 10, me
->queue
,
11304 _GAITesterFirstGAITimeout
, me
, &me
->timer
);
11305 require_noerr( err
, exit
);
11306 dispatch_resume( me
->timer
);
11309 if( err
) _GAITesterStop( me
, err
);
11312 //===========================================================================================================================
11314 //===========================================================================================================================
11316 static void _GAITesterUserStop( void *inContext
);
11318 static void GAITesterStop( GAITesterRef me
)
11321 dispatch_async_f( me
->queue
, me
, _GAITesterUserStop
);
11324 static void _GAITesterUserStop( void *inContext
)
11326 GAITesterRef
const me
= (GAITesterRef
) inContext
;
11328 _GAITesterStop( me
, kCanceledErr
);
11332 static void _GAITesterStop( GAITesterRef me
, OSStatus inError
)
11336 ForgetPacketCapture( &me
->pcap
);
11337 dispatch_source_forget( &me
->timer
);
11338 DNSServiceForget( &me
->getAddrInfo
);
11339 DNSServiceForget( &me
->connection
);
11340 if( me
->serverPID
!= -1 )
11342 err
= kill( me
->serverPID
, SIGTERM
);
11343 err
= map_global_noerr_errno( err
);
11344 check_noerr( err
);
11345 me
->serverPID
= -1;
11350 me
->stopped
= true;
11351 if( me
->stopHandler
) me
->stopHandler( me
->stopContext
, inError
);
11356 //===========================================================================================================================
11357 // GAITesterAddTestCase
11358 //===========================================================================================================================
11360 static OSStatus
GAITesterAddTestCase( GAITesterRef me
, GAITestCase
*inCase
)
11363 GAITestCase
** ptr
;
11365 require_action_quiet( inCase
->itemList
, exit
, err
= kCountErr
);
11367 for( ptr
= &me
->caseList
; *ptr
; ptr
= &( *ptr
)->next
) {}
11375 //===========================================================================================================================
11376 // GAITesterSetStopHandler
11377 //===========================================================================================================================
11379 static void GAITesterSetStopHandler( GAITesterRef me
, GAITesterStopHandler_f inStopHandler
, void *inStopContext
)
11381 me
->stopHandler
= inStopHandler
;
11382 me
->stopContext
= inStopContext
;
11385 //===========================================================================================================================
11386 // GAITesterSetResultsHandler
11387 //===========================================================================================================================
11389 static void GAITesterSetResultsHandler( GAITesterRef me
, GAITesterResultsHandler_f inResultsHandler
, void *inResultsContext
)
11391 me
->resultsHandler
= inResultsHandler
;
11392 me
->resultsContext
= inResultsContext
;
11395 //===========================================================================================================================
11396 // _GAITesterStartNextTest
11397 //===========================================================================================================================
11399 static void _GAITesterStartNextTest( GAITesterRef me
)
11402 GAITestItem
* item
;
11403 DNSServiceFlags flags
;
11404 DNSServiceProtocol protocols
;
11407 if( me
->currentItem
) me
->currentItem
= me
->currentItem
->next
;
11409 if( !me
->currentItem
)
11411 if( me
->currentCase
)
11413 // No more test items means that the current test case has completed.
11415 me
->caseEndTime
= NanoTimeGetCurrent();
11417 if( me
->resultsHandler
)
11419 size_t resultCount
, i
;
11420 GAITestItemResult
* resultArray
;
11423 for( item
= me
->currentCase
->itemList
; item
; item
= item
->next
) ++resultCount
;
11424 check( resultCount
> 0 );
11426 resultArray
= (GAITestItemResult
*) calloc( resultCount
, sizeof( *resultArray
) );
11427 require_action( resultArray
, exit
, err
= kNoMemoryErr
);
11429 item
= me
->currentCase
->itemList
;
11430 for( i
= 0; i
< resultCount
; ++i
)
11432 resultArray
[ i
].name
= item
->name
;
11433 resultArray
[ i
].connectionTimeUs
= item
->connectionTimeUs
;
11434 resultArray
[ i
].firstTimeUs
= item
->firstTimeUs
;
11435 resultArray
[ i
].timeUs
= item
->timeUs
;
11436 resultArray
[ i
].error
= item
->error
;
11439 me
->resultsHandler( me
->currentCase
->title
, me
->caseStartTime
, me
->caseEndTime
, resultArray
, resultCount
,
11440 me
->resultsContext
);
11441 ForgetMem( &resultArray
);
11444 me
->currentCase
= me
->currentCase
->next
;
11445 if( !me
->currentCase
)
11454 me
->currentCase
= me
->caseList
;
11456 require_action_quiet( me
->currentCase
->itemList
, exit
, err
= kInternalErr
);
11457 me
->currentItem
= me
->currentCase
->itemList
;
11460 item
= me
->currentItem
;
11461 check( ( item
->addressCount
>= 1 ) && ( item
->addressCount
<= 64 ) );
11463 if( !item
->wantV4
) me
->bitmapV4
= 0;
11464 else if( !item
->hasV4
) me
->bitmapV4
= 1;
11465 else if( item
->addressCount
< 64 ) me
->bitmapV4
= ( UINT64_C( 1 ) << item
->addressCount
) - 1;
11466 else me
->bitmapV4
= ~UINT64_C( 0 );
11468 if( !item
->wantV6
) me
->bitmapV6
= 0;
11469 else if( !item
->hasV6
) me
->bitmapV6
= 1;
11470 else if( item
->addressCount
< 64 ) me
->bitmapV6
= ( UINT64_C( 1 ) << item
->addressCount
) - 1;
11471 else me
->bitmapV6
= ~UINT64_C( 0 );
11472 check( ( me
->bitmapV4
!= 0 ) || ( me
->bitmapV6
!= 0 ) );
11473 me
->gotFirstResult
= false;
11475 // Perform preliminary tasks if this is the start of a new test case.
11477 if( item
== me
->currentCase
->itemList
)
11479 // Flush mDNSResponder's cache.
11481 err
= systemf( NULL
, "killall -HUP mDNSResponder" );
11482 require_noerr( err
, exit
);
11485 me
->caseStartTime
= NanoTimeGetCurrent();
11486 me
->caseEndTime
= kNanoTime_Invalid
;
11489 // Start a packet capture.
11491 check( !me
->pcap
);
11492 err
= _GAITesterCreatePacketCapture( &me
->pcap
);
11493 require_noerr( err
, exit
);
11495 // Start timer for test item's time limit.
11497 check( !me
->timer
);
11498 if( item
->timeLimitMs
> 0 )
11500 unsigned int timeLimitMs
;
11502 timeLimitMs
= item
->timeLimitMs
;
11503 if( me
->callDelayMs
> 0 ) timeLimitMs
+= (unsigned int) me
->callDelayMs
;
11504 if( me
->serverDelayMs
> 0 ) timeLimitMs
+= (unsigned int) me
->serverDelayMs
;
11506 err
= DispatchTimerCreate( dispatch_time_milliseconds( timeLimitMs
), DISPATCH_TIME_FOREVER
,
11507 ( (uint64_t) timeLimitMs
) * kNanosecondsPerMillisecond
/ 10,
11508 me
->queue
, _GAITesterTimeout
, NULL
, me
, &me
->timer
);
11509 require_noerr( err
, exit
);
11510 dispatch_resume( me
->timer
);
11513 // Call DNSServiceGetAddrInfo().
11515 if( me
->callDelayMs
> 0 ) usleep( ( (useconds_t
) me
->callDelayMs
) * kMicrosecondsPerMillisecond
);
11517 flags
= kDNSServiceFlagsShareConnection
| kDNSServiceFlagsReturnIntermediates
;
11518 if( me
->skipPathEval
) flags
|= kDNSServiceFlagsPathEvaluationDone
;
11521 if( item
->wantV4
) protocols
|= kDNSServiceProtocol_IPv4
;
11522 if( item
->wantV6
) protocols
|= kDNSServiceProtocol_IPv6
;
11524 me
->startTicks
= UpTicks();
11526 check( !me
->connection
);
11527 err
= DNSServiceCreateConnection( &me
->connection
);
11528 require_noerr( err
, exit
);
11530 err
= DNSServiceSetDispatchQueue( me
->connection
, me
->queue
);
11531 require_noerr( err
, exit
);
11533 me
->connTicks
= UpTicks();
11535 check( !me
->getAddrInfo
);
11536 me
->getAddrInfo
= me
->connection
;
11537 err
= DNSServiceGetAddrInfo( &me
->getAddrInfo
, flags
, kDNSServiceInterfaceIndexAny
, protocols
, item
->name
,
11538 _GAITesterGetAddrInfoCallback
, me
);
11539 require_noerr( err
, exit
);
11542 if( err
|| done
) _GAITesterStop( me
, err
);
11545 //===========================================================================================================================
11546 // _GAITesterCreatePacketCapture
11547 //===========================================================================================================================
11549 static OSStatus
_GAITesterCreatePacketCapture( pcap_t
**outPCap
)
11553 struct bpf_program program
;
11554 char errBuf
[ PCAP_ERRBUF_SIZE
];
11556 pcap
= pcap_create( "lo0", errBuf
);
11557 require_action_string( pcap
, exit
, err
= kUnknownErr
, errBuf
);
11559 err
= pcap_set_buffer_size( pcap
, 512 * kBytesPerKiloByte
);
11560 require_noerr_action( err
, exit
, err
= kUnknownErr
);
11562 err
= pcap_set_snaplen( pcap
, 512 );
11563 require_noerr_action( err
, exit
, err
= kUnknownErr
);
11565 err
= pcap_set_immediate_mode( pcap
, 0 );
11566 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
11568 err
= pcap_activate( pcap
);
11569 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
11571 err
= pcap_setdirection( pcap
, PCAP_D_INOUT
);
11572 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
11574 err
= pcap_setnonblock( pcap
, 1, errBuf
);
11575 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, errBuf
);
11577 err
= pcap_compile( pcap
, &program
, "udp port 53", 1, PCAP_NETMASK_UNKNOWN
);
11578 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
11580 err
= pcap_setfilter( pcap
, &program
);
11581 pcap_freecode( &program
);
11582 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
11588 if( pcap
) pcap_close( pcap
);
11592 //===========================================================================================================================
11593 // _GAITesterFirstGAITimeout
11594 //===========================================================================================================================
11596 static void _GAITesterFirstGAITimeout( void *inContext
)
11598 GAITesterRef
const me
= (GAITesterRef
) inContext
;
11600 _GAITesterStop( me
, kNoResourcesErr
);
11603 //===========================================================================================================================
11604 // _GAITesterTimeout
11605 //===========================================================================================================================
11607 static void _GAITesterTimeout( void *inContext
)
11609 GAITesterRef
const me
= (GAITesterRef
) inContext
;
11611 _GAITesterCompleteCurrentTest( me
, kTimeoutErr
);
11614 //===========================================================================================================================
11615 // _GAITesterFirstGAICallback
11616 //===========================================================================================================================
11618 static void DNSSD_API
11619 _GAITesterFirstGAICallback(
11620 DNSServiceRef inSDRef
,
11621 DNSServiceFlags inFlags
,
11622 uint32_t inInterfaceIndex
,
11623 DNSServiceErrorType inError
,
11624 const char * inHostname
,
11625 const struct sockaddr
* inSockAddr
,
11629 GAITesterRef
const me
= (GAITesterRef
) inContext
;
11632 Unused( inInterfaceIndex
);
11633 Unused( inHostname
);
11634 Unused( inSockAddr
);
11637 if( ( inFlags
& kDNSServiceFlagsAdd
) && !inError
)
11639 dispatch_source_forget( &me
->timer
);
11640 DNSServiceForget( &me
->getAddrInfo
);
11642 _GAITesterStartNextTest( me
);
11646 //===========================================================================================================================
11647 // _GAITesterGetAddrInfoCallback
11648 //===========================================================================================================================
11650 static void DNSSD_API
11651 _GAITesterGetAddrInfoCallback(
11652 DNSServiceRef inSDRef
,
11653 DNSServiceFlags inFlags
,
11654 uint32_t inInterfaceIndex
,
11655 DNSServiceErrorType inError
,
11656 const char * inHostname
,
11657 const struct sockaddr
* inSockAddr
,
11662 GAITesterRef
const me
= (GAITesterRef
) inContext
;
11663 GAITestItem
* const item
= me
->currentItem
;
11664 const sockaddr_ip
* const sip
= (const sockaddr_ip
*) inSockAddr
;
11666 uint64_t * bitmapPtr
;
11671 Unused( inInterfaceIndex
);
11672 Unused( inHostname
);
11675 nowTicks
= UpTicks();
11677 require_action_quiet( inFlags
& kDNSServiceFlagsAdd
, exit
, err
= kFlagErr
);
11679 // Check if we were expecting an IP address result of this type.
11681 if( sip
->sa
.sa_family
== AF_INET
)
11683 bitmapPtr
= &me
->bitmapV4
;
11684 hasAddr
= item
->hasV4
;
11686 else if( sip
->sa
.sa_family
== AF_INET6
)
11688 bitmapPtr
= &me
->bitmapV6
;
11689 hasAddr
= item
->hasV6
;
11700 uint32_t addrOffset
;
11702 require_noerr_action_quiet( inError
, exit
, err
= inError
);
11704 if( sip
->sa
.sa_family
== AF_INET
)
11706 const uint32_t addrV4
= ntohl( sip
->v4
.sin_addr
.s_addr
);
11708 if( strcasecmp( item
->name
, "localhost." ) == 0 )
11710 if( addrV4
== INADDR_LOOPBACK
) bitmask
= 1;
11714 addrOffset
= addrV4
- kDNSServerBaseAddrV4
;
11715 if( ( addrOffset
>= 1 ) && ( addrOffset
<= item
->addressCount
) )
11717 bitmask
= UINT64_C( 1 ) << ( addrOffset
- 1 );
11723 const uint8_t * const addrV6
= sip
->v6
.sin6_addr
.s6_addr
;
11725 if( strcasecmp( item
->name
, "localhost." ) == 0 )
11727 if( memcmp( addrV6
, in6addr_loopback
.s6_addr
, 16 ) == 0 ) bitmask
= 1;
11729 else if( memcmp( addrV6
, kDNSServerBaseAddrV6
, 15 ) == 0 )
11731 addrOffset
= addrV6
[ 15 ];
11732 if( ( addrOffset
>= 1 ) && ( addrOffset
<= item
->addressCount
) )
11734 bitmask
= UINT64_C( 1 ) << ( addrOffset
- 1 );
11741 require_action_quiet( inError
== kDNSServiceErr_NoSuchRecord
, exit
, err
= inError
? inError
: kUnexpectedErr
);
11744 require_action_quiet( bitmask
!= 0, exit
, err
= kValueErr
);
11745 require_action_quiet( *bitmapPtr
& bitmask
, exit
, err
= kDuplicateErr
);
11747 *bitmapPtr
&= ~bitmask
;
11748 if( !me
->gotFirstResult
)
11750 me
->firstTicks
= nowTicks
;
11751 me
->gotFirstResult
= true;
11756 if( err
|| ( ( me
->bitmapV4
== 0 ) && ( me
->bitmapV6
== 0 ) ) )
11758 me
->endTicks
= nowTicks
;
11759 _GAITesterCompleteCurrentTest( me
, err
);
11763 //===========================================================================================================================
11764 // _GAITesterCompleteCurrentTest
11765 //===========================================================================================================================
11768 _GAITesterGetDNSMessageFromPacket(
11769 const uint8_t * inPacketPtr
,
11770 size_t inPacketLen
,
11771 const uint8_t ** outMsgPtr
,
11772 size_t * outMsgLen
);
11774 static void _GAITesterCompleteCurrentTest( GAITesterRef me
, OSStatus inError
)
11777 GAITestItem
* const item
= me
->currentItem
;
11778 struct timeval timeStamps
[ 4 ];
11779 struct timeval
* tsPtr
;
11780 struct timeval
* tsQA
= NULL
;
11781 struct timeval
* tsQAAAA
= NULL
;
11782 struct timeval
* tsRA
= NULL
;
11783 struct timeval
* tsRAAAA
= NULL
;
11784 struct timeval
* t1
;
11785 struct timeval
* t2
;
11786 int64_t idleTimeUs
;
11787 uint8_t name
[ kDomainNameLengthMax
];
11789 dispatch_source_forget( &me
->timer
);
11790 DNSServiceForget( &me
->getAddrInfo
);
11791 DNSServiceForget( &me
->connection
);
11793 item
->error
= inError
;
11800 err
= DomainNameFromString( name
, item
->name
, NULL
);
11801 require_noerr( err
, exit
);
11803 tsPtr
= &timeStamps
[ 0 ];
11807 struct pcap_pkthdr
* pktHdr
;
11808 const uint8_t * packet
;
11809 const uint8_t * msgPtr
;
11811 const DNSHeader
* hdr
;
11812 unsigned int flags
;
11813 const uint8_t * ptr
;
11814 uint16_t qtype
, qclass
;
11815 uint8_t qname
[ kDomainNameLengthMax
];
11817 status
= pcap_next_ex( me
->pcap
, &pktHdr
, &packet
);
11818 if( status
!= 1 ) break;
11819 if( _GAITesterGetDNSMessageFromPacket( packet
, pktHdr
->caplen
, &msgPtr
, &msgLen
) != kNoErr
) continue;
11820 if( msgLen
< kDNSHeaderLength
) continue;
11822 hdr
= (const DNSHeader
*) msgPtr
;
11823 flags
= DNSHeaderGetFlags( hdr
);
11824 if( DNSFlagsGetOpCode( flags
) != kDNSOpCode_Query
) continue;
11825 if( DNSHeaderGetQuestionCount( hdr
) < 1 ) continue;
11827 ptr
= (const uint8_t *) &hdr
[ 1 ];
11828 if( DNSMessageExtractQuestion( msgPtr
, msgLen
, ptr
, qname
, &qtype
, &qclass
, NULL
) != kNoErr
) continue;
11829 if( qclass
!= kDNSServiceClass_IN
) continue;
11830 if( !DomainNameEqual( qname
, name
) ) continue;
11832 if( item
->wantV4
&& ( qtype
== kDNSServiceType_A
) )
11834 if( flags
& kDNSHeaderFlag_Response
)
11836 if( tsQA
&& !tsRA
)
11839 *tsRA
= pktHdr
->ts
;
11845 *tsQA
= pktHdr
->ts
;
11848 else if( item
->wantV6
&& ( qtype
== kDNSServiceType_AAAA
) )
11850 if( flags
& kDNSHeaderFlag_Response
)
11852 if( tsQAAAA
&& !tsRAAAA
)
11855 *tsRAAAA
= pktHdr
->ts
;
11858 else if( !tsQAAAA
)
11861 *tsQAAAA
= pktHdr
->ts
;
11866 // t1 is the time when the last query was sent.
11868 if( tsQA
&& tsQAAAA
) t1
= TIMEVAL_GT( *tsQA
, *tsQAAAA
) ? tsQA
: tsQAAAA
;
11869 else t1
= tsQA
? tsQA
: tsQAAAA
;
11871 // t2 is when the first response was received.
11873 if( tsRA
&& tsRAAAA
) t2
= TIMEVAL_LT( *tsRA
, *tsRAAAA
) ? tsRA
: tsRAAAA
;
11874 else t2
= tsRA
? tsRA
: tsRAAAA
;
11878 idleTimeUs
= TIMEVAL_USEC64_DIFF( *t2
, *t1
);
11879 if( idleTimeUs
< 0 ) idleTimeUs
= 0;
11886 item
->connectionTimeUs
= UpTicksToMicroseconds( me
->connTicks
- me
->startTicks
);
11887 item
->firstTimeUs
= UpTicksToMicroseconds( me
->firstTicks
- me
->connTicks
) - (uint64_t) idleTimeUs
;
11888 item
->timeUs
= UpTicksToMicroseconds( me
->endTicks
- me
->connTicks
) - (uint64_t) idleTimeUs
;
11891 ForgetPacketCapture( &me
->pcap
);
11892 if( err
) _GAITesterStop( me
, err
);
11893 else _GAITesterStartNextTest( me
);
11896 //===========================================================================================================================
11897 // _GAITesterGetDNSMessageFromPacket
11898 //===========================================================================================================================
11900 #define kHeaderSizeNullLink 4
11901 #define kHeaderSizeIPv4Min 20
11902 #define kHeaderSizeIPv6 40
11903 #define kHeaderSizeUDP 8
11905 #define kIPProtocolUDP 0x11
11908 _GAITesterGetDNSMessageFromPacket(
11909 const uint8_t * inPacketPtr
,
11910 size_t inPacketLen
,
11911 const uint8_t ** outMsgPtr
,
11912 size_t * outMsgLen
)
11915 const uint8_t * nullLink
;
11916 uint32_t addressFamily
;
11917 const uint8_t * ip
;
11920 const uint8_t * msg
;
11921 const uint8_t * const end
= &inPacketPtr
[ inPacketLen
];
11923 nullLink
= &inPacketPtr
[ 0 ];
11924 require_action_quiet( ( end
- nullLink
) >= kHeaderSizeNullLink
, exit
, err
= kUnderrunErr
);
11925 addressFamily
= ReadHost32( &nullLink
[ 0 ] );
11927 ip
= &nullLink
[ kHeaderSizeNullLink
];
11928 if( addressFamily
== AF_INET
)
11930 require_action_quiet( ( end
- ip
) >= kHeaderSizeIPv4Min
, exit
, err
= kUnderrunErr
);
11931 ipHeaderLen
= ( ip
[ 0 ] & 0x0F ) * 4;
11932 protocol
= ip
[ 9 ];
11934 else if( addressFamily
== AF_INET6
)
11936 require_action_quiet( ( end
- ip
) >= kHeaderSizeIPv6
, exit
, err
= kUnderrunErr
);
11937 ipHeaderLen
= kHeaderSizeIPv6
;
11938 protocol
= ip
[ 6 ];
11945 require_action_quiet( protocol
== kIPProtocolUDP
, exit
, err
= kTypeErr
);
11946 require_action_quiet( ( end
- ip
) >= ( ipHeaderLen
+ kHeaderSizeUDP
), exit
, err
= kUnderrunErr
);
11948 msg
= &ip
[ ipHeaderLen
+ kHeaderSizeUDP
];
11951 *outMsgLen
= (size_t)( end
- msg
);
11958 //===========================================================================================================================
11959 // GAITestCaseCreate
11960 //===========================================================================================================================
11962 static OSStatus
GAITestCaseCreate( const char *inTitle
, GAITestCase
**outCase
)
11967 obj
= (GAITestCase
*) calloc( 1, sizeof( *obj
) );
11968 require_action( obj
, exit
, err
= kNoMemoryErr
);
11970 obj
->title
= strdup( inTitle
);
11971 require_action( obj
->title
, exit
, err
= kNoMemoryErr
);
11978 if( obj
) GAITestCaseFree( obj
);
11982 //===========================================================================================================================
11984 //===========================================================================================================================
11986 static void GAITestCaseFree( GAITestCase
*inCase
)
11988 GAITestItem
* item
;
11990 while( ( item
= inCase
->itemList
) != NULL
)
11992 inCase
->itemList
= item
->next
;
11993 GAITestItemFree( item
);
11995 ForgetMem( &inCase
->title
);
11999 //===========================================================================================================================
12000 // GAITestCaseAddItem
12001 //===========================================================================================================================
12004 GAITestCaseAddItem(
12005 GAITestCase
* inCase
,
12006 unsigned int inAliasCount
,
12007 unsigned int inAddressCount
,
12009 GAITestAddrType inHasAddrs
,
12010 GAITestAddrType inWantAddrs
,
12011 unsigned int inTimeLimitMs
,
12012 unsigned int inItemCount
)
12015 GAITestItem
* item
;
12016 GAITestItem
* item2
;
12017 GAITestItem
* newItemList
= NULL
;
12018 GAITestItem
** itemPtr
;
12023 char tag
[ kGAITesterTagStringLen
+ 1 ];
12025 require_action_quiet( inItemCount
> 0, exit
, err
= kNoErr
);
12027 // Limit address count to 64 because we use 64-bit bitmaps for keeping track of addresses.
12029 require_action_quiet( ( inAddressCount
>= 1 ) && ( inAddressCount
<= 64 ), exit
, err
= kCountErr
);
12030 require_action_quiet( ( inAliasCount
>= 0 ) && ( inAliasCount
<= INT32_MAX
), exit
, err
= kCountErr
);
12031 require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs
), exit
, err
= kValueErr
);
12034 end
= &name
[ countof( name
) ];
12036 // Add Alias label.
12038 if( inAliasCount
== 1 ) SNPrintF_Add( &ptr
, end
, "alias." );
12039 else if( inAliasCount
>= 2 ) SNPrintF_Add( &ptr
, end
, "alias-%u.", inAliasCount
);
12041 // Add Count label.
12043 SNPrintF_Add( &ptr
, end
, "count-%u.", inAddressCount
);
12047 if( inTTL
>= 0 ) SNPrintF_Add( &ptr
, end
, "ttl-%d.", inTTL
);
12051 SNPrintF_Add( &ptr
, end
, "tag-%s.",
12052 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
12054 // Add IPv4 or IPv6 label if necessary.
12056 if( inHasAddrs
== kGAITestAddrType_IPv4
) SNPrintF_Add( &ptr
, end
, "ipv4." );
12057 else if( inHasAddrs
== kGAITestAddrType_IPv6
) SNPrintF_Add( &ptr
, end
, "ipv6." );
12059 // Finally, add the d.test. labels.
12061 SNPrintF_Add( &ptr
, end
, "d.test." );
12065 err
= GAITestItemCreate( name
, inAddressCount
, inHasAddrs
, inWantAddrs
, inTimeLimitMs
, &item
);
12066 require_noerr( err
, exit
);
12068 newItemList
= item
;
12069 itemPtr
= &item
->next
;
12071 // Create repeat items.
12073 for( i
= 1; i
< inItemCount
; ++i
)
12075 err
= GAITestItemDup( item
, &item2
);
12076 require_noerr( err
, exit
);
12079 itemPtr
= &item2
->next
;
12082 // Append to test case's item list.
12084 for( itemPtr
= &inCase
->itemList
; *itemPtr
; itemPtr
= &( *itemPtr
)->next
) {}
12085 *itemPtr
= newItemList
;
12086 newItemList
= NULL
;
12089 while( ( item
= newItemList
) != NULL
)
12091 newItemList
= item
->next
;
12092 GAITestItemFree( item
);
12097 //===========================================================================================================================
12098 // GAITestCaseAddLocalHostItem
12099 //===========================================================================================================================
12102 GAITestCaseAddLocalHostItem(
12103 GAITestCase
* inCase
,
12104 GAITestAddrType inWantAddrs
,
12105 unsigned int inTimeLimitMs
,
12106 unsigned int inItemCount
)
12109 GAITestItem
* item
;
12110 GAITestItem
* item2
;
12111 GAITestItem
* newItemList
= NULL
;
12112 GAITestItem
** itemPtr
;
12115 require_action_quiet( inItemCount
> 1, exit
, err
= kNoErr
);
12117 err
= GAITestItemCreate( "localhost.", 1, kGAITestAddrType_Both
, inWantAddrs
, inTimeLimitMs
, &item
);
12118 require_noerr( err
, exit
);
12120 newItemList
= item
;
12121 itemPtr
= &item
->next
;
12123 // Create repeat items.
12125 for( i
= 1; i
< inItemCount
; ++i
)
12127 err
= GAITestItemDup( item
, &item2
);
12128 require_noerr( err
, exit
);
12131 itemPtr
= &item2
->next
;
12134 for( itemPtr
= &inCase
->itemList
; *itemPtr
; itemPtr
= &( *itemPtr
)->next
) {}
12135 *itemPtr
= newItemList
;
12136 newItemList
= NULL
;
12139 while( ( item
= newItemList
) != NULL
)
12141 newItemList
= item
->next
;
12142 GAITestItemFree( item
);
12147 //===========================================================================================================================
12148 // GAITestItemCreate
12149 //===========================================================================================================================
12153 const char * inName
,
12154 unsigned int inAddressCount
,
12155 GAITestAddrType inHasAddrs
,
12156 GAITestAddrType inWantAddrs
,
12157 unsigned int inTimeLimitMs
,
12158 GAITestItem
** outItem
)
12161 GAITestItem
* obj
= NULL
;
12163 require_action_quiet( inAddressCount
>= 1, exit
, err
= kCountErr
);
12164 require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs
), exit
, err
= kValueErr
);
12165 require_action_quiet( GAITestAddrTypeIsValid( inWantAddrs
), exit
, err
= kValueErr
);
12167 obj
= (GAITestItem
*) calloc( 1, sizeof( *obj
) );
12168 require_action( obj
, exit
, err
= kNoMemoryErr
);
12170 obj
->name
= strdup( inName
);
12171 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
12173 obj
->addressCount
= inAddressCount
;
12174 obj
->hasV4
= ( inHasAddrs
& kGAITestAddrType_IPv4
) ? true : false;
12175 obj
->hasV6
= ( inHasAddrs
& kGAITestAddrType_IPv6
) ? true : false;
12176 obj
->wantV4
= ( inWantAddrs
& kGAITestAddrType_IPv4
) ? true : false;
12177 obj
->wantV6
= ( inWantAddrs
& kGAITestAddrType_IPv6
) ? true : false;
12178 obj
->error
= kInProgressErr
;
12179 obj
->timeLimitMs
= inTimeLimitMs
;
12186 if( obj
) GAITestItemFree( obj
);
12190 //===========================================================================================================================
12192 //===========================================================================================================================
12194 static OSStatus
GAITestItemDup( const GAITestItem
*inItem
, GAITestItem
**outItem
)
12199 obj
= (GAITestItem
*) calloc( 1, sizeof( *obj
) );
12200 require_action( obj
, exit
, err
= kNoMemoryErr
);
12206 obj
->name
= strdup( inItem
->name
);
12207 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
12215 if( obj
) GAITestItemFree( obj
);
12219 //===========================================================================================================================
12221 //===========================================================================================================================
12223 static void GAITestItemFree( GAITestItem
*inItem
)
12225 ForgetMem( &inItem
->name
);
12229 //===========================================================================================================================
12230 // MDNSDiscoveryTestCmd
12231 //===========================================================================================================================
12233 #define kMDNSDiscoveryTestFirstQueryTimeoutSecs 4
12237 DNSServiceRef query
; // Reference to DNSServiceQueryRecord for replier's "about" TXT record.
12238 dispatch_source_t queryTimer
; // Used to time out the "about" TXT record query.
12239 NanoTime64 startTime
; // When the test started.
12240 NanoTime64 endTime
; // When the test ended.
12241 pid_t replierPID
; // PID of mDNS replier.
12242 uint32_t ifIndex
; // Index of interface to run the replier on.
12243 unsigned int instanceCount
; // Desired number of service instances.
12244 unsigned int txtSize
; // Desired size of each service instance's TXT record data.
12245 unsigned int recordCountA
; // Desired number of A records per replier hostname.
12246 unsigned int recordCountAAAA
; // Desired number of AAAA records per replier hostname.
12247 unsigned int maxDropCount
; // Replier's --maxDropCount option argument.
12248 double ucastDropRate
; // Replier's probability of dropping a unicast response.
12249 double mcastDropRate
; // Replier's probability of dropping a multicast query or response.
12250 Boolean noAdditionals
; // True if the replier is to not include additional records in responses.
12251 Boolean useIPv4
; // True if the replier is to use IPv4.
12252 Boolean useIPv6
; // True if the replier is to use IPv6.
12253 Boolean flushedCache
; // True if mDNSResponder's record cache was flushed before testing.
12254 char * replierCommand
; // Command used to run the replier.
12255 char * serviceType
; // Type of services to browse for.
12256 ServiceBrowserRef browser
; // Service browser.
12257 unsigned int browseTimeSecs
; // Amount of time to spend browsing in seconds.
12258 const char * outputFilePath
; // File to write test results to. If NULL, then write to stdout.
12259 OutputFormatType outputFormat
; // Format of test results output.
12260 Boolean outputAppendNewline
; // True if a newline character should be appended to JSON output.
12261 char hostname
[ 32 + 1 ]; // Base hostname that the replier is to use for instance and host names.
12262 char tag
[ 4 + 1 ]; // Tag that the replier is to use in its service types.
12264 } MDNSDiscoveryTestContext
;
12266 static void _MDNSDiscoveryTestFirstQueryTimeout( void *inContext
);
12267 static void DNSSD_API
12268 _MDNSDiscoveryTestAboutQueryCallback(
12269 DNSServiceRef inSDRef
,
12270 DNSServiceFlags inFlags
,
12271 uint32_t inInterfaceIndex
,
12272 DNSServiceErrorType inError
,
12273 const char * inFullName
,
12276 uint16_t inRDataLen
,
12277 const void * inRDataPtr
,
12279 void * inContext
);
12281 _MDNSDiscoveryTestServiceBrowserCallback(
12282 ServiceBrowserResults
* inResults
,
12284 void * inContext
);
12285 static Boolean
_MDNSDiscoveryTestTXTRecordIsValid( const uint8_t *inRecordName
, const uint8_t *inTXTPtr
, size_t inTXTLen
);
12287 static void MDNSDiscoveryTestCmd( void )
12290 MDNSDiscoveryTestContext
* context
;
12291 char queryName
[ sizeof_field( MDNSDiscoveryTestContext
, hostname
) + 15 ];
12293 context
= (MDNSDiscoveryTestContext
*) calloc( 1, sizeof( *context
) );
12294 require_action( context
, exit
, err
= kNoMemoryErr
);
12296 err
= CheckIntegerArgument( gMDNSDiscoveryTest_InstanceCount
, "instance count", 1, UINT16_MAX
);
12297 require_noerr_quiet( err
, exit
);
12299 err
= CheckIntegerArgument( gMDNSDiscoveryTest_TXTSize
, "TXT size", 1, UINT16_MAX
);
12300 require_noerr_quiet( err
, exit
);
12302 err
= CheckIntegerArgument( gMDNSDiscoveryTest_BrowseTimeSecs
, "browse time (seconds)", 1, INT_MAX
);
12303 require_noerr_quiet( err
, exit
);
12305 err
= CheckIntegerArgument( gMDNSDiscoveryTest_RecordCountA
, "A record count", 0, 64 );
12306 require_noerr_quiet( err
, exit
);
12308 err
= CheckIntegerArgument( gMDNSDiscoveryTest_RecordCountAAAA
, "AAAA record count", 0, 64 );
12309 require_noerr_quiet( err
, exit
);
12311 err
= CheckDoubleArgument( gMDNSDiscoveryTest_UnicastDropRate
, "unicast drop rate", 0.0, 1.0 );
12312 require_noerr_quiet( err
, exit
);
12314 err
= CheckDoubleArgument( gMDNSDiscoveryTest_MulticastDropRate
, "multicast drop rate", 0.0, 1.0 );
12315 require_noerr_quiet( err
, exit
);
12317 err
= CheckIntegerArgument( gMDNSDiscoveryTest_MaxDropCount
, "drop count", 0, 255 );
12318 require_noerr_quiet( err
, exit
);
12320 context
->replierPID
= -1;
12321 context
->instanceCount
= (unsigned int) gMDNSDiscoveryTest_InstanceCount
;
12322 context
->txtSize
= (unsigned int) gMDNSDiscoveryTest_TXTSize
;
12323 context
->browseTimeSecs
= (unsigned int) gMDNSDiscoveryTest_BrowseTimeSecs
;
12324 context
->recordCountA
= (unsigned int) gMDNSDiscoveryTest_RecordCountA
;
12325 context
->recordCountAAAA
= (unsigned int) gMDNSDiscoveryTest_RecordCountAAAA
;
12326 context
->ucastDropRate
= gMDNSDiscoveryTest_UnicastDropRate
;
12327 context
->mcastDropRate
= gMDNSDiscoveryTest_MulticastDropRate
;
12328 context
->maxDropCount
= (unsigned int) gMDNSDiscoveryTest_MaxDropCount
;
12329 context
->outputFilePath
= gMDNSDiscoveryTest_OutputFilePath
;
12330 context
->outputAppendNewline
= gMDNSDiscoveryTest_OutputAppendNewline
? true : false;
12331 context
->noAdditionals
= gMDNSDiscoveryTest_NoAdditionals
? true : false;
12332 context
->useIPv4
= ( gMDNSDiscoveryTest_UseIPv4
|| !gMDNSDiscoveryTest_UseIPv6
) ? true : false;
12333 context
->useIPv6
= ( gMDNSDiscoveryTest_UseIPv6
|| !gMDNSDiscoveryTest_UseIPv4
) ? true : false;
12335 if( gMDNSDiscoveryTest_Interface
)
12337 err
= InterfaceIndexFromArgString( gMDNSDiscoveryTest_Interface
, &context
->ifIndex
);
12338 require_noerr_quiet( err
, exit
);
12342 err
= _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All
, NULL
, &context
->ifIndex
);
12343 require_noerr_quiet( err
, exit
);
12346 err
= OutputFormatFromArgString( gMDNSDiscoveryTest_OutputFormat
, &context
->outputFormat
);
12347 require_noerr_quiet( err
, exit
);
12349 if( gMDNSDiscoveryTest_FlushCache
)
12351 err
= CheckRootUser();
12352 require_noerr_quiet( err
, exit
);
12354 err
= systemf( NULL
, "killall -HUP mDNSResponder" );
12355 require_noerr( err
, exit
);
12357 context
->flushedCache
= true;
12360 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( context
->hostname
) - 1,
12361 context
->hostname
);
12362 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( context
->tag
) - 1, context
->tag
);
12364 ASPrintF( &context
->serviceType
, "_t-%s-%u-%u._tcp", context
->tag
, context
->txtSize
, context
->instanceCount
);
12365 require_action( context
->serviceType
, exit
, err
= kUnknownErr
);
12367 ASPrintF( &context
->replierCommand
,
12368 "dnssdutil mdnsreplier --follow %lld --interface %u --hostname %s --tag %s --maxInstanceCount %u "
12369 "--countA %u --countAAAA %u --udrop %.1f --mdrop %.1f --maxDropCount %u %?s%?s%?s",
12370 (int64_t) getpid(),
12374 context
->instanceCount
,
12375 context
->recordCountA
,
12376 context
->recordCountAAAA
,
12377 context
->ucastDropRate
,
12378 context
->mcastDropRate
,
12379 context
->maxDropCount
,
12380 context
->noAdditionals
, " --noAdditionals",
12381 context
->useIPv4
, " --ipv4",
12382 context
->useIPv6
, " --ipv6" );
12383 require_action_quiet( context
->replierCommand
, exit
, err
= kUnknownErr
);
12385 err
= SpawnCommand( &context
->replierPID
, "%s", context
->replierCommand
);
12386 require_noerr_quiet( err
, exit
);
12388 // Query for the replier's about TXT record. A response means it's fully up and running.
12390 SNPrintF( queryName
, sizeof( queryName
), "about.%s.local.", context
->hostname
);
12391 err
= DNSServiceQueryRecord( &context
->query
, kDNSServiceFlagsForceMulticast
, context
->ifIndex
, queryName
,
12392 kDNSServiceType_TXT
, kDNSServiceClass_IN
, _MDNSDiscoveryTestAboutQueryCallback
, context
);
12393 require_noerr( err
, exit
);
12395 err
= DNSServiceSetDispatchQueue( context
->query
, dispatch_get_main_queue() );
12396 require_noerr( err
, exit
);
12398 err
= DispatchTimerCreate( dispatch_time_seconds( kMDNSDiscoveryTestFirstQueryTimeoutSecs
),
12399 DISPATCH_TIME_FOREVER
, UINT64_C_safe( kMDNSDiscoveryTestFirstQueryTimeoutSecs
) * kNanosecondsPerSecond
/ 10, NULL
,
12400 _MDNSDiscoveryTestFirstQueryTimeout
, NULL
, context
, &context
->queryTimer
);
12401 require_noerr( err
, exit
);
12402 dispatch_resume( context
->queryTimer
);
12404 context
->startTime
= NanoTimeGetCurrent();
12411 //===========================================================================================================================
12412 // _MDNSDiscoveryTestFirstQueryTimeout
12413 //===========================================================================================================================
12415 static void _MDNSDiscoveryTestFirstQueryTimeout( void *inContext
)
12417 MDNSDiscoveryTestContext
* const context
= (MDNSDiscoveryTestContext
*) inContext
;
12419 dispatch_source_forget( &context
->queryTimer
);
12421 FPrintF( stderr
, "error: Query for mdnsreplier's \"about\" TXT record timed out.\n" );
12425 //===========================================================================================================================
12426 // _MDNSDiscoveryTestAboutQueryCallback
12427 //===========================================================================================================================
12429 static void DNSSD_API
12430 _MDNSDiscoveryTestAboutQueryCallback(
12431 DNSServiceRef inSDRef
,
12432 DNSServiceFlags inFlags
,
12433 uint32_t inInterfaceIndex
,
12434 DNSServiceErrorType inError
,
12435 const char * inFullName
,
12438 uint16_t inRDataLen
,
12439 const void * inRDataPtr
,
12444 MDNSDiscoveryTestContext
* const context
= (MDNSDiscoveryTestContext
*) inContext
;
12447 Unused( inInterfaceIndex
);
12448 Unused( inFullName
);
12451 Unused( inRDataLen
);
12452 Unused( inRDataPtr
);
12456 require_noerr( err
, exit
);
12457 require_quiet( inFlags
& kDNSServiceFlagsAdd
, exit
);
12459 DNSServiceForget( &context
->query
);
12460 dispatch_source_forget( &context
->queryTimer
);
12462 err
= ServiceBrowserCreate( dispatch_get_main_queue(), 0, "local.", context
->browseTimeSecs
, false, &context
->browser
);
12463 require_noerr( err
, exit
);
12465 err
= ServiceBrowserAddServiceType( context
->browser
, context
->serviceType
);
12466 require_noerr( err
, exit
);
12468 ServiceBrowserSetCallback( context
->browser
, _MDNSDiscoveryTestServiceBrowserCallback
, context
);
12469 ServiceBrowserStart( context
->browser
);
12472 if( err
) exit( 1 );
12475 //===========================================================================================================================
12476 // _MDNSDiscoveryTestServiceBrowserCallback
12477 //===========================================================================================================================
12479 #define kMDNSDiscoveryTestResultsKey_ReplierInfo CFSTR( "replierInfo" )
12480 #define kMDNSDiscoveryTestResultsKey_StartTime CFSTR( "startTime" )
12481 #define kMDNSDiscoveryTestResultsKey_EndTime CFSTR( "endTime" )
12482 #define kMDNSDiscoveryTestResultsKey_BrowseTimeSecs CFSTR( "browseTimeSecs" )
12483 #define kMDNSDiscoveryTestResultsKey_ServiceType CFSTR( "serviceType" )
12484 #define kMDNSDiscoveryTestResultsKey_FlushedCache CFSTR( "flushedCache" )
12485 #define kMDNSDiscoveryTestResultsKey_UnexpectedInstances CFSTR( "unexpectedInstances" )
12486 #define kMDNSDiscoveryTestResultsKey_MissingInstances CFSTR( "missingInstances" )
12487 #define kMDNSDiscoveryTestResultsKey_IncorrectInstances CFSTR( "incorrectInstances" )
12488 #define kMDNSDiscoveryTestResultsKey_Success CFSTR( "success" )
12489 #define kMDNSDiscoveryTestResultsKey_TotalResolveTime CFSTR( "totalResolveTimeUs" )
12491 #define kMDNSDiscoveryTestReplierInfoKey_Command CFSTR( "command" )
12492 #define kMDNSDiscoveryTestReplierInfoKey_InstanceCount CFSTR( "instanceCount" )
12493 #define kMDNSDiscoveryTestReplierInfoKey_TXTSize CFSTR( "txtSize" )
12494 #define kMDNSDiscoveryTestReplierInfoKey_RecordCountA CFSTR( "recordCountA" )
12495 #define kMDNSDiscoveryTestReplierInfoKey_RecordCountAAAA CFSTR( "recordCountAAAA" )
12496 #define kMDNSDiscoveryTestReplierInfoKey_Hostname CFSTR( "hostname" )
12497 #define kMDNSDiscoveryTestReplierInfoKey_NoAdditionals CFSTR( "noAdditionals" )
12498 #define kMDNSDiscoveryTestReplierInfoKey_UnicastDropRate CFSTR( "ucastDropRate" )
12499 #define kMDNSDiscoveryTestReplierInfoKey_MulticastDropRate CFSTR( "mcastDropRate" )
12500 #define kMDNSDiscoveryTestReplierInfoKey_MaxDropCount CFSTR( "maxDropCount" )
12502 #define kMDNSDiscoveryTestUnexpectedInstanceKey_Name CFSTR( "name" )
12503 #define kMDNSDiscoveryTestUnexpectedInstanceKey_InterfaceIndex CFSTR( "interfaceIndex" )
12505 #define kMDNSDiscoveryTestIncorrectInstanceKey_Name CFSTR( "name" )
12506 #define kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve CFSTR( "didResolve" )
12507 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadHostname CFSTR( "badHostname" )
12508 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadPort CFSTR( "badPort" )
12509 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadTXT CFSTR( "badTXT" )
12510 #define kMDNSDiscoveryTestIncorrectInstanceKey_UnexpectedAddrs CFSTR( "unexpectedAddrs" )
12511 #define kMDNSDiscoveryTestIncorrectInstanceKey_MissingAddrs CFSTR( "missingAddrs" )
12513 static void _MDNSDiscoveryTestServiceBrowserCallback( ServiceBrowserResults
*inResults
, OSStatus inError
, void *inContext
)
12516 MDNSDiscoveryTestContext
* const context
= (MDNSDiscoveryTestContext
*) inContext
;
12517 const SBRDomain
* domain
;
12518 const SBRServiceType
* type
;
12519 const SBRServiceInstance
* instance
;
12520 const SBRServiceInstance
** instanceArray
= NULL
;
12521 const SBRIPAddress
* ipaddr
;
12522 size_t hostnameLen
;
12527 CFMutableArrayRef unexpectedInstances
;
12528 CFMutableArrayRef missingInstances
;
12529 CFMutableArrayRef incorrectInstances
;
12530 CFMutableDictionaryRef plist
= NULL
;
12531 CFMutableDictionaryRef badDict
= NULL
;
12532 CFMutableArrayRef unexpectedAddrs
= NULL
;
12533 CFMutableArrayRef missingAddrs
= NULL
;
12534 uint64_t maxResolveTimeUs
;
12535 int success
= false;
12536 char startTime
[ 32 ];
12537 char endTime
[ 32 ];
12539 context
->endTime
= NanoTimeGetCurrent();
12542 require_noerr( err
, exit
);
12544 _NanoTime64ToTimestamp( context
->startTime
, startTime
, sizeof( startTime
) );
12545 _NanoTime64ToTimestamp( context
->endTime
, endTime
, sizeof( endTime
) );
12546 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
12550 "%kO=%s" // replierCommand
12551 "%kO=%lli" // txtSize
12552 "%kO=%lli" // instanceCount
12553 "%kO=%lli" // recordCountA
12554 "%kO=%lli" // recordCountAAAA
12555 "%kO=%s" // hostname
12556 "%kO=%b" // noAdditionals
12557 "%kO=%f" // ucastDropRate
12558 "%kO=%f" // mcastDropRate
12559 "%kO=%i" // maxDropCount
12561 "%kO=%s" // startTime
12562 "%kO=%s" // endTime
12563 "%kO=%lli" // browseTimeSecs
12564 "%kO=%s" // serviceType
12565 "%kO=%b" // flushedCache
12566 "%kO=[%@]" // unexpectedInstances
12567 "%kO=[%@]" // missingInstances
12568 "%kO=[%@]" // incorrectInstances
12570 kMDNSDiscoveryTestResultsKey_ReplierInfo
,
12571 kMDNSDiscoveryTestReplierInfoKey_Command
, context
->replierCommand
,
12572 kMDNSDiscoveryTestReplierInfoKey_InstanceCount
, (int64_t) context
->instanceCount
,
12573 kMDNSDiscoveryTestReplierInfoKey_TXTSize
, (int64_t) context
->txtSize
,
12574 kMDNSDiscoveryTestReplierInfoKey_RecordCountA
, (int64_t) context
->recordCountA
,
12575 kMDNSDiscoveryTestReplierInfoKey_RecordCountAAAA
, (int64_t) context
->recordCountAAAA
,
12576 kMDNSDiscoveryTestReplierInfoKey_Hostname
, context
->hostname
,
12577 kMDNSDiscoveryTestReplierInfoKey_NoAdditionals
, context
->noAdditionals
,
12578 kMDNSDiscoveryTestReplierInfoKey_UnicastDropRate
, context
->ucastDropRate
,
12579 kMDNSDiscoveryTestReplierInfoKey_MulticastDropRate
, context
->mcastDropRate
,
12580 kMDNSDiscoveryTestReplierInfoKey_MaxDropCount
, context
->maxDropCount
,
12581 kMDNSDiscoveryTestResultsKey_StartTime
, startTime
,
12582 kMDNSDiscoveryTestResultsKey_EndTime
, endTime
,
12583 kMDNSDiscoveryTestResultsKey_BrowseTimeSecs
, (int64_t) context
->browseTimeSecs
,
12584 kMDNSDiscoveryTestResultsKey_ServiceType
, context
->serviceType
,
12585 kMDNSDiscoveryTestResultsKey_FlushedCache
, context
->flushedCache
,
12586 kMDNSDiscoveryTestResultsKey_UnexpectedInstances
, &unexpectedInstances
,
12587 kMDNSDiscoveryTestResultsKey_MissingInstances
, &missingInstances
,
12588 kMDNSDiscoveryTestResultsKey_IncorrectInstances
, &incorrectInstances
);
12589 require_noerr( err
, exit
);
12591 for( domain
= inResults
->domainList
; domain
&& ( strcasecmp( domain
->name
, "local." ) != 0 ); domain
= domain
->next
) {}
12592 require_action( domain
, exit
, err
= kInternalErr
);
12594 for( type
= domain
->typeList
; type
&& ( strcasecmp( type
->name
, context
->serviceType
) != 0 ); type
= type
->next
) {}
12595 require_action( type
, exit
, err
= kInternalErr
);
12597 instanceArray
= (const SBRServiceInstance
**) calloc( context
->instanceCount
, sizeof( *instanceArray
) );
12598 require_action( instanceArray
, exit
, err
= kNoMemoryErr
);
12600 hostnameLen
= strlen( context
->hostname
);
12601 for( instance
= type
->instanceList
; instance
; instance
= instance
->next
)
12603 unsigned int instanceNumber
= 0;
12605 if( strcmp_prefix( instance
->name
, context
->hostname
) == 0 )
12607 ptr
= &instance
->name
[ hostnameLen
];
12608 if( ( ptr
[ 0 ] == ' ' ) && ( ptr
[ 1 ] == '(' ) )
12611 for( end
= ptr
; isdigit_safe( *end
); ++end
) {}
12612 if( DecimalTextToUInt32( ptr
, end
, &u32
, &ptr
) == kNoErr
)
12614 if( ( u32
>= 2 ) && ( u32
<= context
->instanceCount
) && ( ptr
[ 0 ] == ')' ) && ( ptr
[ 1 ] == '\0' ) )
12616 instanceNumber
= u32
;
12620 else if( *ptr
== '\0' )
12622 instanceNumber
= 1;
12625 if( ( instanceNumber
!= 0 ) && ( instance
->ifIndex
== context
->ifIndex
) )
12627 check( !instanceArray
[ instanceNumber
- 1 ] );
12628 instanceArray
[ instanceNumber
- 1 ] = instance
;
12632 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, unexpectedInstances
,
12637 kMDNSDiscoveryTestUnexpectedInstanceKey_Name
, instance
->name
,
12638 kMDNSDiscoveryTestUnexpectedInstanceKey_InterfaceIndex
, (int64_t) instance
->ifIndex
);
12639 require_noerr( err
, exit
);
12643 maxResolveTimeUs
= 0;
12644 for( i
= 1; i
<= context
->instanceCount
; ++i
)
12646 int isHostnameValid
;
12649 instance
= instanceArray
[ i
- 1 ];
12654 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, missingInstances
, "%s", context
->hostname
);
12655 require_noerr( err
, exit
);
12659 char * instanceName
= NULL
;
12661 ASPrintF( &instanceName
, "%s (%u)", context
->hostname
, i
);
12662 require_action( instanceName
, exit
, err
= kUnknownErr
);
12664 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, missingInstances
, "%s", instanceName
);
12665 free( instanceName
);
12666 require_noerr( err
, exit
);
12671 if( !instance
->hostname
)
12673 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, incorrectInstances
,
12678 kMDNSDiscoveryTestIncorrectInstanceKey_Name
, instance
->name
,
12679 kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve
, false );
12680 require_noerr( err
, exit
);
12684 badDict
= CFDictionaryCreateMutable( NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
12685 require_action( badDict
, exit
, err
= kNoMemoryErr
);
12687 isHostnameValid
= false;
12688 if( strcmp_prefix( instance
->hostname
, context
->hostname
) == 0 )
12690 ptr
= &instance
->hostname
[ hostnameLen
];
12693 if( strcmp( ptr
, ".local." ) == 0 ) isHostnameValid
= true;
12695 else if( *ptr
== '-' )
12698 for( end
= ptr
; isdigit_safe( *end
); ++end
) {}
12699 if( DecimalTextToUInt32( ptr
, end
, &u32
, &ptr
) == kNoErr
)
12701 if( ( u32
== i
) && ( strcmp( ptr
, ".local." ) == 0 ) ) isHostnameValid
= true;
12705 if( !isHostnameValid
)
12707 err
= CFDictionarySetCString( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_BadHostname
, instance
->hostname
,
12709 require_noerr( err
, exit
);
12712 if( instance
->port
!= (uint16_t)( kMDNSReplierPortBase
+ context
->txtSize
) )
12714 err
= CFDictionarySetInt64( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_BadPort
, instance
->port
);
12715 require_noerr( err
, exit
);
12718 isTXTValid
= false;
12719 if( instance
->txtLen
== context
->txtSize
)
12721 uint8_t name
[ kDomainNameLengthMax
];
12723 err
= DomainNameFromString( name
, instance
->name
, NULL
);
12724 require_noerr( err
, exit
);
12726 err
= DomainNameAppendString( name
, type
->name
, NULL
);
12727 require_noerr( err
, exit
);
12729 err
= DomainNameAppendString( name
, "local", NULL
);
12730 require_noerr( err
, exit
);
12732 if( _MDNSDiscoveryTestTXTRecordIsValid( name
, instance
->txtPtr
, instance
->txtLen
) ) isTXTValid
= true;
12736 char * hexStr
= NULL
;
12738 ASPrintF( &hexStr
, "%.4H", instance
->txtPtr
, (int) instance
->txtLen
, (int) instance
->txtLen
);
12739 require_action( hexStr
, exit
, err
= kUnknownErr
);
12741 err
= CFDictionarySetCString( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_BadTXT
, hexStr
, kSizeCString
);
12743 require_noerr( err
, exit
);
12746 if( isHostnameValid
)
12748 uint64_t addrV4Bitmap
, addrV6Bitmap
, bitmask
, resolveTimeUs
;
12750 uint8_t addrV4
[ 4 ];
12751 uint8_t addrV6
[ 16 ];
12753 if( context
->recordCountA
< 64 ) addrV4Bitmap
= ( UINT64_C( 1 ) << context
->recordCountA
) - 1;
12754 else addrV4Bitmap
= ~UINT64_C( 0 );
12756 if( context
->recordCountAAAA
< 64 ) addrV6Bitmap
= ( UINT64_C( 1 ) << context
->recordCountAAAA
) - 1;
12757 else addrV6Bitmap
= ~UINT64_C( 0 );
12760 WriteBig16( &addrV4
[ 1 ], i
);
12763 memcpy( addrV6
, kMDNSReplierBaseAddrV6
, 16 );
12764 WriteBig16( &addrV6
[ 12 ], i
);
12766 unexpectedAddrs
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
12767 require_action( unexpectedAddrs
, exit
, err
= kNoMemoryErr
);
12770 for( ipaddr
= instance
->ipaddrList
; ipaddr
; ipaddr
= ipaddr
->next
)
12772 const uint8_t * addrPtr
;
12774 int isAddrValid
= false;
12776 if( ipaddr
->sip
.sa
.sa_family
== AF_INET
)
12778 addrPtr
= (const uint8_t *) &ipaddr
->sip
.v4
.sin_addr
.s_addr
;
12779 lsb
= addrPtr
[ 3 ];
12780 if( ( memcmp( addrPtr
, addrV4
, 3 ) == 0 ) && ( lsb
>= 1 ) && ( lsb
<= context
->recordCountA
) )
12782 bitmask
= UINT64_C( 1 ) << ( lsb
- 1 );
12783 addrV4Bitmap
&= ~bitmask
;
12784 isAddrValid
= true;
12787 else if( ipaddr
->sip
.sa
.sa_family
== AF_INET6
)
12789 addrPtr
= ipaddr
->sip
.v6
.sin6_addr
.s6_addr
;
12790 lsb
= addrPtr
[ 15 ];
12791 if( ( memcmp( addrPtr
, addrV6
, 15 ) == 0 ) && ( lsb
>= 1 ) && ( lsb
<= context
->recordCountAAAA
) )
12793 bitmask
= UINT64_C( 1 ) << ( lsb
- 1 );
12794 addrV6Bitmap
&= ~bitmask
;
12795 isAddrValid
= true;
12800 if( ipaddr
->resolveTimeUs
> resolveTimeUs
) resolveTimeUs
= ipaddr
->resolveTimeUs
;
12804 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, unexpectedAddrs
, "%##a", &ipaddr
->sip
);
12805 require_noerr( err
, exit
);
12809 resolveTimeUs
+= ( instance
->discoverTimeUs
+ instance
->resolveTimeUs
);
12810 if( resolveTimeUs
> maxResolveTimeUs
) maxResolveTimeUs
= resolveTimeUs
;
12812 if( CFArrayGetCount( unexpectedAddrs
) > 0 )
12814 CFDictionarySetValue( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_UnexpectedAddrs
, unexpectedAddrs
);
12816 ForgetCF( &unexpectedAddrs
);
12818 missingAddrs
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
12819 require_action( missingAddrs
, exit
, err
= kNoMemoryErr
);
12821 for( j
= 1; addrV4Bitmap
!= 0; ++j
)
12823 bitmask
= UINT64_C( 1 ) << ( j
- 1 );
12824 if( addrV4Bitmap
& bitmask
)
12826 addrV4Bitmap
&= ~bitmask
;
12827 addrV4
[ 3 ] = (uint8_t) j
;
12828 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, missingAddrs
, "%.4a", addrV4
);
12829 require_noerr( err
, exit
);
12832 for( j
= 1; addrV6Bitmap
!= 0; ++j
)
12834 bitmask
= UINT64_C( 1 ) << ( j
- 1 );
12835 if( addrV6Bitmap
& bitmask
)
12837 addrV6Bitmap
&= ~bitmask
;
12838 addrV6
[ 15 ] = (uint8_t) j
;
12839 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, missingAddrs
, "%.16a", addrV6
);
12840 require_noerr( err
, exit
);
12844 if( CFArrayGetCount( missingAddrs
) > 0 )
12846 CFDictionarySetValue( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_MissingAddrs
, missingAddrs
);
12848 ForgetCF( &missingAddrs
);
12851 if( CFDictionaryGetCount( badDict
) > 0 )
12853 err
= CFDictionarySetCString( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_Name
, instance
->name
,
12855 require_noerr( err
, exit
);
12857 CFDictionarySetBoolean( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve
, true );
12858 CFArrayAppendValue( incorrectInstances
, badDict
);
12860 ForgetCF( &badDict
);
12863 if( ( CFArrayGetCount( unexpectedInstances
) == 0 ) &&
12864 ( CFArrayGetCount( missingInstances
) == 0 ) &&
12865 ( CFArrayGetCount( incorrectInstances
) == 0 ) )
12867 err
= CFDictionarySetInt64( plist
, kMDNSDiscoveryTestResultsKey_TotalResolveTime
, (int64_t) maxResolveTimeUs
);
12868 require_noerr( err
, exit
);
12875 CFDictionarySetBoolean( plist
, kMDNSDiscoveryTestResultsKey_Success
, success
);
12877 err
= OutputPropertyList( plist
, context
->outputFormat
, context
->outputFilePath
);
12878 require_noerr_quiet( err
, exit
);
12881 ForgetCF( &context
->browser
);
12882 if( context
->replierPID
!= -1 )
12884 kill( context
->replierPID
, SIGTERM
);
12885 context
->replierPID
= -1;
12887 FreeNullSafe( instanceArray
);
12888 CFReleaseNullSafe( plist
);
12889 CFReleaseNullSafe( badDict
);
12890 CFReleaseNullSafe( unexpectedAddrs
);
12891 CFReleaseNullSafe( missingAddrs
);
12892 exit( err
? 1 : ( success
? 0 : 2 ) );
12895 //===========================================================================================================================
12896 // _MDNSDiscoveryTestTXTRecordIsValid
12897 //===========================================================================================================================
12899 static Boolean
_MDNSDiscoveryTestTXTRecordIsValid( const uint8_t *inRecordName
, const uint8_t *inTXTPtr
, size_t inTXTLen
)
12903 const uint8_t * ptr
;
12904 size_t i
, wholeCount
, remCount
;
12905 uint8_t txtStr
[ 16 ];
12907 if( inTXTLen
== 0 ) return( false );
12909 hash
= _FNV1( inRecordName
, DomainNameLength( inRecordName
) );
12912 n
= MemPrintF( &txtStr
[ 1 ], 15, "hash=0x%08X", hash
);
12916 wholeCount
= inTXTLen
/ 16;
12917 for( i
= 0; i
< wholeCount
; ++i
)
12919 if( memcmp( ptr
, txtStr
, 16 ) != 0 ) return( false );
12923 remCount
= inTXTLen
% 16;
12926 txtStr
[ 0 ] = (uint8_t)( remCount
- 1 );
12927 if( memcmp( ptr
, txtStr
, remCount
) != 0 ) return( false );
12930 check( ptr
== &inTXTPtr
[ inTXTLen
] );
12934 //===========================================================================================================================
12936 //===========================================================================================================================
12938 #define kDotLocalTestPreparationTimeLimitSecs 5
12939 #define kDotLocalTestSubtestDurationSecs 5
12941 // Constants for SRV record query subtest.
12943 #define kDotLocalTestSRV_Priority 1
12944 #define kDotLocalTestSRV_Weight 0
12945 #define kDotLocalTestSRV_Port 80
12946 #define kDotLocalTestSRV_TargetName ( (const uint8_t *) "\x03" "www" "\x07" "example" "\x03" "com" )
12947 #define kDotLocalTestSRV_TargetStr "www.example.com."
12948 #define kDotLocalTestSRV_ResultStr "1 0 80 " kDotLocalTestSRV_TargetStr
12952 kDotLocalTestState_Unset
= 0,
12953 kDotLocalTestState_Preparing
= 1,
12954 kDotLocalTestState_GAIMDNSOnly
= 2,
12955 kDotLocalTestState_GAIDNSOnly
= 3,
12956 kDotLocalTestState_GAIBoth
= 4,
12957 kDotLocalTestState_GAINeither
= 5,
12958 kDotLocalTestState_GAINoSuchRecord
= 6,
12959 kDotLocalTestState_QuerySRV
= 7,
12960 kDotLocalTestState_Done
= 8
12962 } DotLocalTestState
;
12966 const char * testDesc
; // Description of the current subtest.
12967 char * queryName
; // Query name for GetAddrInfo or QueryRecord operation.
12968 dispatch_source_t timer
; // Timer used for limiting the time for each subtest.
12969 NanoTime64 startTime
; // Timestamp of when the subtest started.
12970 NanoTime64 endTime
; // Timestamp of when the subtest ended.
12971 CFMutableArrayRef correctResults
; // Operation results that were expected.
12972 CFMutableArrayRef duplicateResults
; // Operation results that were expected, but were already received.
12973 CFMutableArrayRef unexpectedResults
; // Operation results that were unexpected.
12974 OSStatus error
; // Subtest's error code.
12975 uint32_t addrDNSv4
; // If hasDNSv4 is true, the expected DNS IPv4 address for queryName.
12976 uint32_t addrMDNSv4
; // If hasMDNSv4 is true, the expected MDNS IPv4 address for queryName.
12977 uint8_t addrDNSv6
[ 16 ]; // If hasDNSv6 is true, the expected DNS IPv6 address for queryName.
12978 uint8_t addrMDNSv6
[ 16 ]; // If hasMDNSv6 is true, the expected MDNS IPv6 address for queryName.
12979 Boolean hasDNSv4
; // True if queryName has a DNS IPv4 address.
12980 Boolean hasDNSv6
; // True if queryName has a DNS IPv6 address.
12981 Boolean hasMDNSv4
; // True if queryName has an MDNS IPv4 address.
12982 Boolean hasMDNSv6
; // True if queryName has an MDNS IPv6 address.
12983 Boolean needDNSv4
; // True if operation is expecting, but hasn't received a DNS IPv4 result.
12984 Boolean needDNSv6
; // True if operation is expecting, but hasn't received a DNS IPv6 result.
12985 Boolean needMDNSv4
; // True if operation is expecting, but hasn't received an MDNS IPv4 result.
12986 Boolean needMDNSv6
; // True if operation is expecting, but hasn't received an MDNS IPv6 result.
12987 Boolean needSRV
; // True if operation is expecting, but hasn't received an SRV result.
12993 dispatch_source_t timer
; // Timer used for limiting the time for each state/subtest.
12994 DotLocalSubtest
* subtest
; // Current subtest's state.
12995 DNSServiceRef connection
; // Shared connection for DNS-SD operations.
12996 DNSServiceRef op
; // Reference for the current DNS-SD operation.
12997 DNSServiceRef op2
; // Reference for mdnsreplier probe query used during preparing state.
12998 DNSRecordRef localSOARef
; // Reference returned by DNSServiceRegisterRecord() for local. SOA record.
12999 char * replierCmd
; // Command used to invoke the mdnsreplier.
13000 char * serverCmd
; // Command used to invoke the test DNS server.
13001 CFMutableArrayRef reportsGAI
; // Reports for subtests that use DNSServiceGetAddrInfo.
13002 CFMutableArrayRef reportsQuerySRV
; // Reports for subtests that use DNSServiceQueryRecord for SRV records.
13003 NanoTime64 startTime
; // Timestamp for when the test started.
13004 NanoTime64 endTime
; // Timestamp for when the test ended.
13005 DotLocalTestState state
; // The test's current state.
13006 pid_t replierPID
; // PID of spawned mdnsreplier.
13007 pid_t serverPID
; // PID of spawned test DNS server.
13008 uint32_t ifIndex
; // Interface index used for mdnsreplier.
13009 char * outputFilePath
; // File to write test results to. If NULL, then write to stdout.
13010 OutputFormatType outputFormat
; // Format of test results output.
13011 Boolean registeredSOA
; // True if the dummy local. SOA record was successfully registered.
13012 Boolean serverIsReady
; // True if response was received for test DNS server probe query.
13013 Boolean replierIsReady
; // True if response was received for mdnsreplier probe query.
13014 Boolean testFailed
; // True if at least one subtest failed.
13015 char labelStr
[ 20 + 1 ]; // Unique label string used for for making the query names used by subtests.
13016 // The format of this string is "dotlocal-test-<six random chars>".
13017 } DotLocalTestContext
;
13019 static void _DotLocalTestStateMachine( DotLocalTestContext
*inContext
);
13020 static void DNSSD_API
13021 _DotLocalTestProbeQueryRecordCallback(
13022 DNSServiceRef inSDRef
,
13023 DNSServiceFlags inFlags
,
13024 uint32_t inInterfaceIndex
,
13025 DNSServiceErrorType inError
,
13026 const char * inFullName
,
13029 uint16_t inRDataLen
,
13030 const void * inRDataPtr
,
13032 void * inContext
);
13033 static void DNSSD_API
13034 _DotLocalTestRegisterRecordCallback(
13035 DNSServiceRef inSDRef
,
13036 DNSRecordRef inRecordRef
,
13037 DNSServiceFlags inFlags
,
13038 DNSServiceErrorType inError
,
13039 void * inContext
);
13040 static void _DotLocalTestTimerHandler( void *inContext
);
13041 static void DNSSD_API
13042 _DotLocalTestGAICallback(
13043 DNSServiceRef inSDRef
,
13044 DNSServiceFlags inFlags
,
13045 uint32_t inInterfaceIndex
,
13046 DNSServiceErrorType inError
,
13047 const char * inHostname
,
13048 const struct sockaddr
* inSockAddr
,
13050 void * inContext
);
13051 static void DNSSD_API
13052 _DotLocalTestQueryRecordCallback(
13053 DNSServiceRef inSDRef
,
13054 DNSServiceFlags inFlags
,
13055 uint32_t inInterfaceIndex
,
13056 DNSServiceErrorType inError
,
13057 const char * inFullName
,
13060 uint16_t inRDataLen
,
13061 const void * inRDataPtr
,
13063 void * inContext
);
13065 static void DotLocalTestCmd( void )
13068 DotLocalTestContext
* context
;
13069 uint8_t * rdataPtr
;
13071 DNSServiceFlags flags
;
13072 char queryName
[ 64 ];
13073 char randBuf
[ 6 + 1 ]; // Large enough for four and six character random strings below.
13075 context
= (DotLocalTestContext
*) calloc( 1, sizeof( *context
) );
13076 require_action( context
, exit
, err
= kNoMemoryErr
);
13078 context
->startTime
= NanoTimeGetCurrent();
13079 context
->endTime
= kNanoTime_Invalid
;
13081 context
->state
= kDotLocalTestState_Preparing
;
13083 if( gDotLocalTest_Interface
)
13085 err
= InterfaceIndexFromArgString( gDotLocalTest_Interface
, &context
->ifIndex
);
13086 require_noerr_quiet( err
, exit
);
13090 err
= _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All
, NULL
, &context
->ifIndex
);
13091 require_noerr_quiet( err
, exit
);
13094 if( gDotLocalTest_OutputFilePath
)
13096 context
->outputFilePath
= strdup( gDotLocalTest_OutputFilePath
);
13097 require_action( context
->outputFilePath
, exit
, err
= kNoMemoryErr
);
13100 err
= OutputFormatFromArgString( gDotLocalTest_OutputFormat
, &context
->outputFormat
);
13101 require_noerr_quiet( err
, exit
);
13103 context
->reportsGAI
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13104 require_action( context
->reportsGAI
, exit
, err
= kNoMemoryErr
);
13106 context
->reportsQuerySRV
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13107 require_action( context
->reportsQuerySRV
, exit
, err
= kNoMemoryErr
);
13109 SNPrintF( context
->labelStr
, sizeof( context
->labelStr
), "dotlocal-test-%s",
13110 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, 6, randBuf
) );
13112 // Spawn an mdnsreplier.
13114 ASPrintF( &context
->replierCmd
,
13115 "dnssdutil mdnsreplier --follow %lld --interface %u --hostname %s --tag %s --maxInstanceCount 2 --countA 1"
13117 (int64_t) getpid(), context
->ifIndex
, context
->labelStr
,
13118 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, 4, randBuf
) );
13119 require_action_quiet( context
->replierCmd
, exit
, err
= kUnknownErr
);
13121 err
= SpawnCommand( &context
->replierPID
, "%s", context
->replierCmd
);
13122 require_noerr( err
, exit
);
13124 // Spawn a test DNS server
13126 ASPrintF( &context
->serverCmd
,
13127 "dnssdutil server --loopback --follow %lld --port 0 --defaultTTL 300 --domain %s.local.",
13128 (int64_t) getpid(), context
->labelStr
);
13129 require_action_quiet( context
->serverCmd
, exit
, err
= kUnknownErr
);
13131 err
= SpawnCommand( &context
->serverPID
, "%s", context
->serverCmd
);
13132 require_noerr( err
, exit
);
13134 // Create a shared DNS-SD connection.
13136 err
= DNSServiceCreateConnection( &context
->connection
);
13137 require_noerr( err
, exit
);
13139 err
= DNSServiceSetDispatchQueue( context
->connection
, dispatch_get_main_queue() );
13140 require_noerr( err
, exit
);
13142 // Create probe query for DNS server, i.e., query for any name that has an A record.
13144 SNPrintF( queryName
, sizeof( queryName
), "tag-dotlocal-test-probe.ipv4.%s.local.", context
->labelStr
);
13146 flags
= kDNSServiceFlagsShareConnection
;
13147 #if( TARGET_OS_WATCH )
13148 flags
|= kDNSServiceFlagsPathEvaluationDone
;
13151 context
->op
= context
->connection
;
13152 err
= DNSServiceQueryRecord( &context
->op
, flags
, kDNSServiceInterfaceIndexAny
, queryName
, kDNSServiceType_A
,
13153 kDNSServiceClass_IN
, _DotLocalTestProbeQueryRecordCallback
, context
);
13154 require_noerr( err
, exit
);
13156 // Create probe query for mdnsreplier's "about" TXT record.
13158 SNPrintF( queryName
, sizeof( queryName
), "about.%s.local.", context
->labelStr
);
13160 flags
= kDNSServiceFlagsShareConnection
| kDNSServiceFlagsForceMulticast
;
13161 #if( TARGET_OS_WATCH )
13162 flags
|= kDNSServiceFlagsPathEvaluationDone
;
13165 context
->op2
= context
->connection
;
13166 err
= DNSServiceQueryRecord( &context
->op2
, flags
, context
->ifIndex
, queryName
, kDNSServiceType_TXT
, kDNSServiceClass_IN
,
13167 _DotLocalTestProbeQueryRecordCallback
, context
);
13168 require_noerr( err
, exit
);
13170 // Register a dummy local. SOA record.
13172 err
= CreateSOARecordData( kRootLabel
, kRootLabel
, 1976040101, 1 * kSecondsPerDay
, 2 * kSecondsPerHour
,
13173 1000 * kSecondsPerHour
, 2 * kSecondsPerDay
, &rdataPtr
, &rdataLen
);
13174 require_noerr( err
, exit
);
13176 err
= DNSServiceRegisterRecord( context
->connection
, &context
->localSOARef
, kDNSServiceFlagsUnique
,
13177 kDNSServiceInterfaceIndexLocalOnly
, "local.", kDNSServiceType_SOA
, kDNSServiceClass_IN
, 1,
13178 rdataPtr
, 1 * kSecondsPerHour
, _DotLocalTestRegisterRecordCallback
, context
);
13179 require_noerr( err
, exit
);
13181 // Start timer for probe responses and SOA record registration.
13183 err
= DispatchTimerOneShotCreate( dispatch_time_seconds( kDotLocalTestPreparationTimeLimitSecs
),
13184 INT64_C_safe( kDotLocalTestPreparationTimeLimitSecs
) * kNanosecondsPerSecond
/ 10, dispatch_get_main_queue(),
13185 _DotLocalTestTimerHandler
, context
, &context
->timer
);
13186 require_noerr( err
, exit
);
13187 dispatch_resume( context
->timer
);
13192 if( err
) ErrQuit( 1, "error: %#m\n", err
);
13195 //===========================================================================================================================
13196 // _DotLocalTestStateMachine
13197 //===========================================================================================================================
13199 static OSStatus
_DotLocalSubtestCreate( DotLocalSubtest
**outSubtest
);
13200 static void _DotLocalSubtestFree( DotLocalSubtest
*inSubtest
);
13201 static OSStatus
_DotLocalTestStartSubtest( DotLocalTestContext
*inContext
);
13202 static OSStatus
_DotLocalTestFinalizeSubtest( DotLocalTestContext
*inContext
);
13203 static void _DotLocalTestFinalizeAndExit( DotLocalTestContext
*inContext
) ATTRIBUTE_NORETURN
;
13205 static void _DotLocalTestStateMachine( DotLocalTestContext
*inContext
)
13208 DotLocalTestState nextState
;
13210 DNSServiceForget( &inContext
->op
);
13211 DNSServiceForget( &inContext
->op2
);
13212 dispatch_source_forget( &inContext
->timer
);
13214 switch( inContext
->state
)
13216 case kDotLocalTestState_Preparing
: nextState
= kDotLocalTestState_GAIMDNSOnly
; break;
13217 case kDotLocalTestState_GAIMDNSOnly
: nextState
= kDotLocalTestState_GAIDNSOnly
; break;
13218 case kDotLocalTestState_GAIDNSOnly
: nextState
= kDotLocalTestState_GAIBoth
; break;
13219 case kDotLocalTestState_GAIBoth
: nextState
= kDotLocalTestState_GAINeither
; break;
13220 case kDotLocalTestState_GAINeither
: nextState
= kDotLocalTestState_GAINoSuchRecord
; break;
13221 case kDotLocalTestState_GAINoSuchRecord
: nextState
= kDotLocalTestState_QuerySRV
; break;
13222 case kDotLocalTestState_QuerySRV
: nextState
= kDotLocalTestState_Done
; break;
13223 default: err
= kStateErr
; goto exit
;
13226 if( inContext
->state
== kDotLocalTestState_Preparing
)
13228 if( !inContext
->registeredSOA
|| !inContext
->serverIsReady
|| !inContext
->replierIsReady
)
13230 FPrintF( stderr
, "Preparation timed out: Registered SOA? %s. Server ready? %s. mdnsreplier ready? %s.\n",
13231 YesNoStr( inContext
->registeredSOA
),
13232 YesNoStr( inContext
->serverIsReady
),
13233 YesNoStr( inContext
->replierIsReady
) );
13234 err
= kNotPreparedErr
;
13240 err
= _DotLocalTestFinalizeSubtest( inContext
);
13241 require_noerr( err
, exit
);
13244 inContext
->state
= nextState
;
13245 if( inContext
->state
== kDotLocalTestState_Done
) _DotLocalTestFinalizeAndExit( inContext
);
13246 err
= _DotLocalTestStartSubtest( inContext
);
13249 if( err
) ErrQuit( 1, "error: %#m\n", err
);
13252 //===========================================================================================================================
13253 // _DotLocalSubtestCreate
13254 //===========================================================================================================================
13256 static OSStatus
_DotLocalSubtestCreate( DotLocalSubtest
**outSubtest
)
13259 DotLocalSubtest
* obj
;
13261 obj
= (DotLocalSubtest
*) calloc( 1, sizeof( *obj
) );
13262 require_action( obj
, exit
, err
= kNoMemoryErr
);
13264 obj
->correctResults
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13265 require_action( obj
->correctResults
, exit
, err
= kNoMemoryErr
);
13267 obj
->duplicateResults
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13268 require_action( obj
->duplicateResults
, exit
, err
= kNoMemoryErr
);
13270 obj
->unexpectedResults
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13271 require_action( obj
->unexpectedResults
, exit
, err
= kNoMemoryErr
);
13278 if( obj
) _DotLocalSubtestFree( obj
);
13282 //===========================================================================================================================
13283 // _DotLocalSubtestFree
13284 //===========================================================================================================================
13286 static void _DotLocalSubtestFree( DotLocalSubtest
*inSubtest
)
13288 ForgetMem( &inSubtest
->queryName
);
13289 ForgetCF( &inSubtest
->correctResults
);
13290 ForgetCF( &inSubtest
->duplicateResults
);
13291 ForgetCF( &inSubtest
->unexpectedResults
);
13295 //===========================================================================================================================
13296 // _DotLocalTestStartSubtest
13297 //===========================================================================================================================
13299 static OSStatus
_DotLocalTestStartSubtest( DotLocalTestContext
*inContext
)
13302 DotLocalSubtest
* subtest
= NULL
;
13303 DNSServiceRef op
= NULL
;
13304 DNSServiceFlags flags
;
13306 err
= _DotLocalSubtestCreate( &subtest
);
13307 require_noerr( err
, exit
);
13309 if( inContext
->state
== kDotLocalTestState_GAIMDNSOnly
)
13311 ASPrintF( &subtest
->queryName
, "%s-2.local.", inContext
->labelStr
);
13312 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13314 subtest
->hasMDNSv4
= subtest
->needMDNSv4
= true;
13315 subtest
->hasMDNSv6
= subtest
->needMDNSv6
= true;
13317 subtest
->addrMDNSv4
= htonl( 0x00000201 ); // 0.0.2.1
13318 memcpy( subtest
->addrMDNSv6
, kMDNSReplierBaseAddrV6
, 16 ); // 2001:db8:2::2:1
13319 subtest
->addrMDNSv6
[ 13 ] = 2;
13320 subtest
->addrMDNSv6
[ 15 ] = 1;
13322 subtest
->testDesc
= kDotLocalTestSubtestDesc_GAIMDNSOnly
;
13325 else if( inContext
->state
== kDotLocalTestState_GAIDNSOnly
)
13327 ASPrintF( &subtest
->queryName
, "tag-dns-only.%s.local.", inContext
->labelStr
);
13328 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13330 subtest
->hasDNSv4
= subtest
->needDNSv4
= true;
13331 subtest
->hasDNSv6
= subtest
->needDNSv6
= true;
13333 subtest
->addrDNSv4
= htonl( kDNSServerBaseAddrV4
+ 1 ); // 203.0.113.1
13334 memcpy( subtest
->addrDNSv6
, kDNSServerBaseAddrV6
, 16 ); // 2001:db8:1::1
13335 subtest
->addrDNSv6
[ 15 ] = 1;
13337 subtest
->testDesc
= kDotLocalTestSubtestDesc_GAIDNSOnly
;
13340 else if( inContext
->state
== kDotLocalTestState_GAIBoth
)
13342 ASPrintF( &subtest
->queryName
, "%s.local.", inContext
->labelStr
);
13343 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13345 subtest
->hasDNSv4
= subtest
->needDNSv4
= true;
13346 subtest
->hasDNSv6
= subtest
->needDNSv6
= true;
13347 subtest
->hasMDNSv4
= subtest
->needMDNSv4
= true;
13348 subtest
->hasMDNSv6
= subtest
->needMDNSv6
= true;
13350 subtest
->addrDNSv4
= htonl( kDNSServerBaseAddrV4
+ 1 ); // 203.0.113.1
13351 memcpy( subtest
->addrDNSv6
, kDNSServerBaseAddrV6
, 16 ); // 2001:db8:1::1
13352 subtest
->addrDNSv6
[ 15 ] = 1;
13354 subtest
->addrMDNSv4
= htonl( 0x00000101 ); // 0.0.1.1
13355 memcpy( subtest
->addrMDNSv6
, kMDNSReplierBaseAddrV6
, 16 ); // 2001:db8:2::1:1
13356 subtest
->addrMDNSv6
[ 13 ] = 1;
13357 subtest
->addrMDNSv6
[ 15 ] = 1;
13359 subtest
->testDesc
= kDotLocalTestSubtestDesc_GAIBoth
;
13362 else if( inContext
->state
== kDotLocalTestState_GAINeither
)
13364 ASPrintF( &subtest
->queryName
, "doesnotexit-%s.local.", inContext
->labelStr
);
13365 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13367 subtest
->testDesc
= kDotLocalTestSubtestDesc_GAINeither
;
13370 else if( inContext
->state
== kDotLocalTestState_GAINoSuchRecord
)
13372 ASPrintF( &subtest
->queryName
, "doesnotexit-dns.%s.local.", inContext
->labelStr
);
13373 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13375 subtest
->hasDNSv4
= subtest
->needDNSv4
= true;
13376 subtest
->hasDNSv6
= subtest
->needDNSv6
= true;
13377 subtest
->testDesc
= kDotLocalTestSubtestDesc_GAINoSuchRecord
;
13380 else if( inContext
->state
== kDotLocalTestState_QuerySRV
)
13382 ASPrintF( &subtest
->queryName
, "_http._tcp.srv-%u-%u-%u.%s%s.local.",
13383 kDotLocalTestSRV_Priority
, kDotLocalTestSRV_Weight
, kDotLocalTestSRV_Port
, kDotLocalTestSRV_TargetStr
,
13384 inContext
->labelStr
);
13385 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13387 subtest
->needSRV
= true;
13388 subtest
->testDesc
= kDotLocalTestSubtestDesc_QuerySRV
;
13397 // Start new operation.
13399 flags
= kDNSServiceFlagsShareConnection
| kDNSServiceFlagsReturnIntermediates
;
13400 #if( TARGET_OS_WATCH )
13401 flags
|= kDNSServiceFlagsPathEvaluationDone
;
13404 subtest
->startTime
= NanoTimeGetCurrent();
13405 subtest
->endTime
= kNanoTime_Invalid
;
13407 if( inContext
->state
== kDotLocalTestState_QuerySRV
)
13409 op
= inContext
->connection
;
13410 err
= DNSServiceQueryRecord( &op
, flags
, kDNSServiceInterfaceIndexAny
, subtest
->queryName
,
13411 kDNSServiceType_SRV
, kDNSServiceClass_IN
, _DotLocalTestQueryRecordCallback
, inContext
);
13412 require_noerr( err
, exit
);
13416 op
= inContext
->connection
;
13417 err
= DNSServiceGetAddrInfo( &op
, flags
, kDNSServiceInterfaceIndexAny
,
13418 kDNSServiceProtocol_IPv4
| kDNSServiceProtocol_IPv6
, subtest
->queryName
, _DotLocalTestGAICallback
, inContext
);
13419 require_noerr( err
, exit
);
13424 check( !inContext
->timer
);
13425 err
= DispatchTimerOneShotCreate( dispatch_time_seconds( kDotLocalTestSubtestDurationSecs
),
13426 INT64_C_safe( kDotLocalTestSubtestDurationSecs
) * kNanosecondsPerSecond
/ 10, dispatch_get_main_queue(),
13427 _DotLocalTestTimerHandler
, inContext
, &inContext
->timer
);
13428 require_noerr( err
, exit
);
13429 dispatch_resume( inContext
->timer
);
13431 check( !inContext
->op
);
13432 inContext
->op
= op
;
13435 check( !inContext
->subtest
);
13436 inContext
->subtest
= subtest
;
13440 if( subtest
) _DotLocalSubtestFree( subtest
);
13441 if( op
) DNSServiceRefDeallocate( op
);
13445 //===========================================================================================================================
13446 // _DotLocalTestFinalizeSubtest
13447 //===========================================================================================================================
13449 #define kDotLocalTestReportKey_StartTime CFSTR( "startTime" ) // String.
13450 #define kDotLocalTestReportKey_EndTime CFSTR( "endTime" ) // String.
13451 #define kDotLocalTestReportKey_Success CFSTR( "success" ) // Boolean.
13452 #define kDotLocalTestReportKey_MDNSReplierCmd CFSTR( "replierCmd" ) // String.
13453 #define kDotLocalTestReportKey_DNSServerCmd CFSTR( "serverCmd" ) // String.
13454 #define kDotLocalTestReportKey_GetAddrInfoTests CFSTR( "testsGAI" ) // Array of Dictionaries.
13455 #define kDotLocalTestReportKey_QuerySRVTests CFSTR( "testsQuerySRV" ) // Array of Dictionaries.
13456 #define kDotLocalTestReportKey_Description CFSTR( "description" ) // String.
13457 #define kDotLocalTestReportKey_QueryName CFSTR( "queryName" ) // String.
13458 #define kDotLocalTestReportKey_Error CFSTR( "error" ) // Integer.
13459 #define kDotLocalTestReportKey_Results CFSTR( "results" ) // Dictionary of Arrays.
13460 #define kDotLocalTestReportKey_CorrectResults CFSTR( "correct" ) // Array of Strings
13461 #define kDotLocalTestReportKey_DuplicateResults CFSTR( "duplicates" ) // Array of Strings.
13462 #define kDotLocalTestReportKey_UnexpectedResults CFSTR( "unexpected" ) // Array of Strings.
13463 #define kDotLocalTestReportKey_MissingResults CFSTR( "missing" ) // Array of Strings.
13465 static OSStatus
_DotLocalTestFinalizeSubtest( DotLocalTestContext
*inContext
)
13468 DotLocalSubtest
* subtest
;
13469 CFMutableDictionaryRef reportDict
;
13470 CFMutableDictionaryRef resultsDict
;
13471 CFMutableArrayRef missingResults
, reportArray
;
13472 char startTime
[ 32 ];
13473 char endTime
[ 32 ];
13475 subtest
= inContext
->subtest
;
13476 inContext
->subtest
= NULL
;
13478 subtest
->endTime
= NanoTimeGetCurrent();
13479 _NanoTime64ToTimestamp( subtest
->startTime
, startTime
, sizeof( startTime
) );
13480 _NanoTime64ToTimestamp( subtest
->endTime
, endTime
, sizeof( endTime
) );
13483 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &reportDict
,
13485 "%kO=%s" // startTime
13486 "%kO=%s" // endTime
13487 "%kO=%s" // queryName
13488 "%kO=%s" // description
13489 "%kO={%@}" // results
13491 kDotLocalTestReportKey_StartTime
, startTime
,
13492 kDotLocalTestReportKey_EndTime
, endTime
,
13493 kDotLocalTestReportKey_QueryName
, subtest
->queryName
,
13494 kDotLocalTestReportKey_Description
, subtest
->testDesc
,
13495 kDotLocalTestReportKey_Results
, &resultsDict
);
13496 require_noerr( err
, exit
);
13498 missingResults
= NULL
;
13499 switch( inContext
->state
)
13501 case kDotLocalTestState_GAIMDNSOnly
:
13502 case kDotLocalTestState_GAIDNSOnly
:
13503 case kDotLocalTestState_GAIBoth
:
13504 case kDotLocalTestState_GAINeither
:
13505 if( subtest
->needDNSv4
|| subtest
->needDNSv6
|| subtest
->needMDNSv4
|| subtest
->needMDNSv6
)
13507 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &missingResults
,
13509 "%.4a" // Expected DNS IPv4 address
13510 "%.16a" // Expected DNS IPv6 address
13511 "%.4a" // Expected MDNS IPv4 address
13512 "%.16a" // Expected MDNS IPv6 address
13514 subtest
->needDNSv4
? &subtest
->addrDNSv4
: NULL
,
13515 subtest
->needDNSv6
? subtest
->addrDNSv6
: NULL
,
13516 subtest
->needMDNSv4
? &subtest
->addrMDNSv4
: NULL
,
13517 subtest
->needMDNSv6
? subtest
->addrMDNSv6
: NULL
);
13518 require_noerr( err
, exit
);
13522 case kDotLocalTestState_QuerySRV
:
13523 if( subtest
->needSRV
)
13525 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &missingResults
,
13527 "%s" // Expected SRV record data as a string.
13529 kDotLocalTestSRV_ResultStr
);
13530 require_noerr( err
, exit
);
13534 case kDotLocalTestState_GAINoSuchRecord
:
13535 if( subtest
->needDNSv4
|| subtest
->needDNSv6
)
13537 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &missingResults
,
13539 "%s" // No Such Record (A)
13540 "%s" // No Such Record (AAAA)
13542 subtest
->needDNSv4
? kNoSuchRecordAStr
: NULL
,
13543 subtest
->needDNSv6
? kNoSuchRecordAAAAStr
: NULL
);
13544 require_noerr( err
, exit
);
13553 CFDictionarySetValue( resultsDict
, kDotLocalTestReportKey_CorrectResults
, subtest
->correctResults
);
13555 if( missingResults
)
13557 CFDictionarySetValue( resultsDict
, kDotLocalTestReportKey_MissingResults
, missingResults
);
13558 ForgetCF( &missingResults
);
13559 if( !subtest
->error
) subtest
->error
= kNotFoundErr
;
13562 if( CFArrayGetCount( subtest
->unexpectedResults
) > 0 )
13564 CFDictionarySetValue( resultsDict
, kDotLocalTestReportKey_UnexpectedResults
, subtest
->unexpectedResults
);
13565 if( !subtest
->error
) subtest
->error
= kUnexpectedErr
;
13568 if( CFArrayGetCount( subtest
->duplicateResults
) > 0 )
13570 CFDictionarySetValue( resultsDict
, kDotLocalTestReportKey_DuplicateResults
, subtest
->duplicateResults
);
13571 if( !subtest
->error
) subtest
->error
= kDuplicateErr
;
13574 if( subtest
->error
) inContext
->testFailed
= true;
13575 err
= CFDictionarySetInt64( reportDict
, kDotLocalTestReportKey_Error
, subtest
->error
);
13576 require_noerr( err
, exit
);
13578 reportArray
= ( inContext
->state
== kDotLocalTestState_QuerySRV
) ? inContext
->reportsQuerySRV
: inContext
->reportsGAI
;
13579 CFArrayAppendValue( reportArray
, reportDict
);
13582 _DotLocalSubtestFree( subtest
);
13583 CFReleaseNullSafe( reportDict
);
13587 //===========================================================================================================================
13588 // _DotLocalTestFinalizeAndExit
13589 //===========================================================================================================================
13591 static void _DotLocalTestFinalizeAndExit( DotLocalTestContext
*inContext
)
13594 CFPropertyListRef plist
;
13595 char timestampStart
[ 32 ];
13596 char timestampEnd
[ 32 ];
13598 check( !inContext
->subtest
);
13599 inContext
->endTime
= NanoTimeGetCurrent();
13601 if( inContext
->replierPID
!= -1 )
13603 kill( inContext
->replierPID
, SIGTERM
);
13604 inContext
->replierPID
= -1;
13606 if( inContext
->serverPID
!= -1 )
13608 kill( inContext
->serverPID
, SIGTERM
);
13609 inContext
->serverPID
= -1;
13611 err
= DNSServiceRemoveRecord( inContext
->connection
, inContext
->localSOARef
, 0 );
13612 require_noerr( err
, exit
);
13614 _NanoTime64ToTimestamp( inContext
->startTime
, timestampStart
, sizeof( timestampStart
) );
13615 _NanoTime64ToTimestamp( inContext
->endTime
, timestampEnd
, sizeof( timestampEnd
) );
13617 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
13619 "%kO=%s" // startTime
13620 "%kO=%s" // endTime
13621 "%kO=%O" // testsGAI
13622 "%kO=%O" // testsQuerySRV
13623 "%kO=%b" // success
13624 "%kO=%s" // replierCmd
13625 "%kO=%s" // serverCmd
13627 kDotLocalTestReportKey_StartTime
, timestampStart
,
13628 kDotLocalTestReportKey_EndTime
, timestampEnd
,
13629 kDotLocalTestReportKey_GetAddrInfoTests
, inContext
->reportsGAI
,
13630 kDotLocalTestReportKey_QuerySRVTests
, inContext
->reportsQuerySRV
,
13631 kDotLocalTestReportKey_Success
, inContext
->testFailed
? false : true,
13632 kDotLocalTestReportKey_MDNSReplierCmd
, inContext
->replierCmd
,
13633 kDotLocalTestReportKey_DNSServerCmd
, inContext
->serverCmd
);
13634 require_noerr( err
, exit
);
13636 ForgetCF( &inContext
->reportsGAI
);
13637 ForgetCF( &inContext
->reportsQuerySRV
);
13639 err
= OutputPropertyList( plist
, inContext
->outputFormat
, inContext
->outputFilePath
);
13640 CFRelease( plist
);
13641 require_noerr( err
, exit
);
13643 exit( inContext
->testFailed
? 2 : 0 );
13646 ErrQuit( 1, "error: %#m\n", err
);
13649 //===========================================================================================================================
13650 // _DotLocalTestProbeQueryRecordCallback
13651 //===========================================================================================================================
13653 static void DNSSD_API
13654 _DotLocalTestProbeQueryRecordCallback(
13655 DNSServiceRef inSDRef
,
13656 DNSServiceFlags inFlags
,
13657 uint32_t inInterfaceIndex
,
13658 DNSServiceErrorType inError
,
13659 const char * inFullName
,
13662 uint16_t inRDataLen
,
13663 const void * inRDataPtr
,
13667 DotLocalTestContext
* const context
= (DotLocalTestContext
*) inContext
;
13669 Unused( inInterfaceIndex
);
13670 Unused( inFullName
);
13673 Unused( inRDataLen
);
13674 Unused( inRDataPtr
);
13677 check( context
->state
== kDotLocalTestState_Preparing
);
13679 require_quiet( ( inFlags
& kDNSServiceFlagsAdd
) && !inError
, exit
);
13681 if( inSDRef
== context
->op
)
13683 DNSServiceForget( &context
->op
);
13684 context
->serverIsReady
= true;
13686 else if( inSDRef
== context
->op2
)
13688 DNSServiceForget( &context
->op2
);
13689 context
->replierIsReady
= true;
13692 if( context
->registeredSOA
&& context
->serverIsReady
&& context
->replierIsReady
)
13694 _DotLocalTestStateMachine( context
);
13701 //===========================================================================================================================
13702 // _DotLocalTestRegisterRecordCallback
13703 //===========================================================================================================================
13705 static void DNSSD_API
13706 _DotLocalTestRegisterRecordCallback(
13707 DNSServiceRef inSDRef
,
13708 DNSRecordRef inRecordRef
,
13709 DNSServiceFlags inFlags
,
13710 DNSServiceErrorType inError
,
13713 DotLocalTestContext
* const context
= (DotLocalTestContext
*) inContext
;
13716 Unused( inRecordRef
);
13719 if( inError
) ErrQuit( 1, "error: local. SOA record registration failed: %#m\n", inError
);
13721 if( !context
->registeredSOA
)
13723 context
->registeredSOA
= true;
13724 if( context
->serverIsReady
&& context
->replierIsReady
) _DotLocalTestStateMachine( context
);
13728 //===========================================================================================================================
13729 // _DotLocalTestTimerHandler
13730 //===========================================================================================================================
13732 static void _DotLocalTestTimerHandler( void *inContext
)
13734 _DotLocalTestStateMachine( (DotLocalTestContext
*) inContext
);
13737 //===========================================================================================================================
13738 // _DotLocalTestGAICallback
13739 //===========================================================================================================================
13741 static void DNSSD_API
13742 _DotLocalTestGAICallback(
13743 DNSServiceRef inSDRef
,
13744 DNSServiceFlags inFlags
,
13745 uint32_t inInterfaceIndex
,
13746 DNSServiceErrorType inError
,
13747 const char * inHostname
,
13748 const struct sockaddr
* inSockAddr
,
13753 DotLocalTestContext
* const context
= (DotLocalTestContext
*) inContext
;
13754 DotLocalSubtest
* const subtest
= context
->subtest
;
13755 const sockaddr_ip
* const sip
= (const sockaddr_ip
*) inSockAddr
;
13758 Unused( inInterfaceIndex
);
13759 Unused( inHostname
);
13762 require_action_quiet( inFlags
& kDNSServiceFlagsAdd
, exit
, err
= kFlagErr
);
13763 require_action_quiet( ( sip
->sa
.sa_family
== AF_INET
) || ( sip
->sa
.sa_family
== AF_INET6
), exit
, err
= kTypeErr
);
13765 if( context
->state
== kDotLocalTestState_GAINoSuchRecord
)
13767 if( inError
== kDNSServiceErr_NoSuchRecord
)
13769 CFMutableArrayRef array
= NULL
;
13770 const char * noSuchRecordStr
;
13772 if( sip
->sa
.sa_family
== AF_INET
)
13774 array
= subtest
->needDNSv4
? subtest
->correctResults
: subtest
->duplicateResults
;
13775 subtest
->needDNSv4
= false;
13777 noSuchRecordStr
= kNoSuchRecordAStr
;
13781 array
= subtest
->needDNSv6
? subtest
->correctResults
: subtest
->duplicateResults
;
13782 subtest
->needDNSv6
= false;
13784 noSuchRecordStr
= kNoSuchRecordAAAAStr
;
13786 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, array
, "%s", noSuchRecordStr
);
13787 require_noerr( err
, fatal
);
13789 else if( !inError
)
13791 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, subtest
->unexpectedResults
, "%##a", sip
);
13792 require_noerr( err
, fatal
);
13804 CFMutableArrayRef array
= NULL
;
13806 if( sip
->sa
.sa_family
== AF_INET
)
13808 const uint32_t addrV4
= sip
->v4
.sin_addr
.s_addr
;
13810 if( subtest
->hasDNSv4
&& ( addrV4
== subtest
->addrDNSv4
) )
13812 array
= subtest
->needDNSv4
? subtest
->correctResults
: subtest
->duplicateResults
;
13813 subtest
->needDNSv4
= false;
13815 else if( subtest
->hasMDNSv4
&& ( addrV4
== subtest
->addrMDNSv4
) )
13817 array
= subtest
->needMDNSv4
? subtest
->correctResults
: subtest
->duplicateResults
;
13818 subtest
->needMDNSv4
= false;
13823 const uint8_t * const addrV6
= sip
->v6
.sin6_addr
.s6_addr
;
13825 if( subtest
->hasDNSv6
&& ( memcmp( addrV6
, subtest
->addrDNSv6
, 16 ) == 0 ) )
13827 array
= subtest
->needDNSv6
? subtest
->correctResults
: subtest
->duplicateResults
;
13828 subtest
->needDNSv6
= false;
13830 else if( subtest
->hasMDNSv6
&& ( memcmp( addrV6
, subtest
->addrMDNSv6
, 16 ) == 0 ) )
13832 array
= subtest
->needMDNSv6
? subtest
->correctResults
: subtest
->duplicateResults
;
13833 subtest
->needMDNSv6
= false;
13836 if( !array
) array
= subtest
->unexpectedResults
;
13837 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, array
, "%##a", sip
);
13838 require_noerr( err
, fatal
);
13840 else if( inError
== kDNSServiceErr_NoSuchRecord
)
13842 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, subtest
->unexpectedResults
, "%s",
13843 ( sip
->sa
.sa_family
== AF_INET
) ? kNoSuchRecordAStr
: kNoSuchRecordAAAAStr
);
13844 require_noerr( err
, fatal
);
13856 subtest
->error
= err
;
13857 _DotLocalTestStateMachine( context
);
13862 ErrQuit( 1, "error: %#m\n", err
);
13865 //===========================================================================================================================
13866 // _DotLocalTestQueryRecordCallback
13867 //===========================================================================================================================
13869 static void DNSSD_API
13870 _DotLocalTestQueryRecordCallback(
13871 DNSServiceRef inSDRef
,
13872 DNSServiceFlags inFlags
,
13873 uint32_t inInterfaceIndex
,
13874 DNSServiceErrorType inError
,
13875 const char * inFullName
,
13878 uint16_t inRDataLen
,
13879 const void * inRDataPtr
,
13884 DotLocalTestContext
* const context
= (DotLocalTestContext
*) inContext
;
13885 DotLocalSubtest
* const subtest
= context
->subtest
;
13886 const dns_fixed_fields_srv
* fields
;
13887 const uint8_t * target
;
13888 const uint8_t * ptr
;
13889 const uint8_t * end
;
13891 unsigned int priority
, weight
, port
;
13892 CFMutableArrayRef array
;
13895 Unused( inInterfaceIndex
);
13896 Unused( inFullName
);
13899 check( context
->state
== kDotLocalTestState_QuerySRV
);
13902 require_noerr_quiet( err
, exit
);
13903 require_action_quiet( inFlags
& kDNSServiceFlagsAdd
, exit
, err
= kFlagErr
);
13904 require_action_quiet( ( inType
== kDNSServiceType_SRV
) && ( inClass
== kDNSServiceClass_IN
), exit
, err
= kTypeErr
);
13905 require_action_quiet( inRDataLen
> sizeof( dns_fixed_fields_srv
), exit
, err
= kSizeErr
);
13907 fields
= (const dns_fixed_fields_srv
*) inRDataPtr
;
13908 priority
= dns_fixed_fields_srv_get_priority( fields
);
13909 weight
= dns_fixed_fields_srv_get_weight( fields
);
13910 port
= dns_fixed_fields_srv_get_port( fields
);
13911 target
= (const uint8_t *) &fields
[ 1 ];
13912 end
= ( (const uint8_t *) inRDataPtr
) + inRDataLen
;
13913 for( ptr
= target
; ( ptr
< end
) && ( *ptr
!= 0 ); ptr
+= ( 1 + *ptr
) ) {}
13915 if( ( priority
== kDotLocalTestSRV_Priority
) &&
13916 ( weight
== kDotLocalTestSRV_Weight
) &&
13917 ( port
== kDotLocalTestSRV_Port
) &&
13918 ( ptr
< end
) && DomainNameEqual( target
, kDotLocalTestSRV_TargetName
) )
13920 array
= subtest
->needSRV
? subtest
->correctResults
: subtest
->duplicateResults
;
13921 subtest
->needSRV
= false;
13925 array
= subtest
->unexpectedResults
;
13929 DNSRecordDataToString( inRDataPtr
, inRDataLen
, kDNSServiceType_SRV
, NULL
, 0, &rdataStr
);
13932 ASPrintF( &rdataStr
, "%#H", inRDataPtr
, inRDataLen
, inRDataLen
);
13933 require_action( rdataStr
, fatal
, err
= kNoMemoryErr
);
13936 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, array
, "%s", rdataStr
);
13938 require_noerr( err
, fatal
);
13943 subtest
->error
= err
;
13944 _DotLocalTestStateMachine( context
);
13949 ErrQuit( 1, "error: %#m\n", err
);
13952 //===========================================================================================================================
13953 // ProbeConflictTestCmd
13954 //===========================================================================================================================
13956 #define kProbeConflictTestService_DefaultName "pctest-name"
13957 #define kProbeConflictTestService_Port 60000
13959 #define kProbeConflictTestTXTPtr "\x13" "PROBE-CONFLICT-TEST"
13960 #define kProbeConflictTestTXTLen sizeof_string( kProbeConflictTestTXTPtr )
13964 const char * description
;
13965 const char * program
;
13966 Boolean expectsRename
;
13968 } ProbeConflictTestCase
;
13970 #define kPCTProgPreWait "wait 1000;" // Wait 1 second before sending gratuitous response.
13971 #define kPCTProgPostWait "wait 8000;" // Wait 8 seconds after sending gratuitous response.
13972 // This allows ~2.75 seconds for probing and ~5 seconds for a rename.
13974 static const ProbeConflictTestCase kProbeConflictTestCases
[] =
13978 { "No probe conflicts.", kPCTProgPreWait
"probes n-n-n;" "send;" kPCTProgPostWait
, false },
13980 // One multicast probe conflict
13982 { "One multicast probe conflict (1).", kPCTProgPreWait
"probes m;" "send;" kPCTProgPostWait
, false },
13983 { "One multicast probe conflict (2).", kPCTProgPreWait
"probes n-m;" "send;" kPCTProgPostWait
, false },
13984 { "One multicast probe conflict (3).", kPCTProgPreWait
"probes n-n-m;" "send;" kPCTProgPostWait
, false },
13986 // One unicast probe conflict
13988 { "One unicast probe conflict (1).", kPCTProgPreWait
"probes u;" "send;" kPCTProgPostWait
, true },
13989 { "One unicast probe conflict (2).", kPCTProgPreWait
"probes n-u;" "send;" kPCTProgPostWait
, true },
13990 { "One unicast probe conflict (3).", kPCTProgPreWait
"probes n-n-u;" "send;" kPCTProgPostWait
, true },
13992 // One multicast and one unicast probe conflict
13994 { "Multicast and unicast probe conflict (1).", kPCTProgPreWait
"probes m-u;" "send;" kPCTProgPostWait
, true },
13995 { "Multicast and unicast probe conflict (2).", kPCTProgPreWait
"probes m-n-u;" "send;" kPCTProgPostWait
, true },
13996 { "Multicast and unicast probe conflict (3).", kPCTProgPreWait
"probes m-n-n-u;" "send;" kPCTProgPostWait
, true },
13997 { "Multicast and unicast probe conflict (4).", kPCTProgPreWait
"probes n-m-u;" "send;" kPCTProgPostWait
, true },
13998 { "Multicast and unicast probe conflict (5).", kPCTProgPreWait
"probes n-m-n-u;" "send;" kPCTProgPostWait
, true },
13999 { "Multicast and unicast probe conflict (6).", kPCTProgPreWait
"probes n-m-n-n-u;" "send;" kPCTProgPostWait
, true },
14000 { "Multicast and unicast probe conflict (7).", kPCTProgPreWait
"probes n-n-m-u;" "send;" kPCTProgPostWait
, true },
14001 { "Multicast and unicast probe conflict (8).", kPCTProgPreWait
"probes n-n-m-n-u;" "send;" kPCTProgPostWait
, true },
14002 { "Multicast and unicast probe conflict (9).", kPCTProgPreWait
"probes n-n-m-n-n-u;" "send;" kPCTProgPostWait
, true },
14004 // Two multicast probe conflicts
14006 { "Two multicast probe conflicts (1).", kPCTProgPreWait
"probes m-m;" "send;" kPCTProgPostWait
, true },
14007 { "Two multicast probe conflicts (2).", kPCTProgPreWait
"probes m-n-m;" "send;" kPCTProgPostWait
, true },
14008 { "Two multicast probe conflicts (3).", kPCTProgPreWait
"probes m-n-n-m;" "send;" kPCTProgPostWait
, true },
14009 { "Two multicast probe conflicts (4).", kPCTProgPreWait
"probes n-m-m;" "send;" kPCTProgPostWait
, true },
14010 { "Two multicast probe conflicts (5).", kPCTProgPreWait
"probes n-m-n-m-n;" "send;" kPCTProgPostWait
, true },
14011 { "Two multicast probe conflicts (6).", kPCTProgPreWait
"probes n-m-n-n-m;" "send;" kPCTProgPostWait
, true },
14012 { "Two multicast probe conflicts (7).", kPCTProgPreWait
"probes n-n-m-m;" "send;" kPCTProgPostWait
, true },
14013 { "Two multicast probe conflicts (8).", kPCTProgPreWait
"probes n-n-m-n-m;" "send;" kPCTProgPostWait
, true },
14014 { "Two multicast probe conflicts (9).", kPCTProgPreWait
"probes n-n-m-n-n-m;" "send;" kPCTProgPostWait
, true },
14017 #define kProbeConflictTestCaseCount countof( kProbeConflictTestCases )
14021 DNSServiceRef registration
; // Test service registration.
14022 NanoTime64 testStartTime
; // Test's start time.
14023 NanoTime64 startTime
; // Current test case's start time.
14024 MDNSColliderRef collider
; // mDNS collider object.
14025 CFMutableArrayRef results
; // Array of test case results.
14026 char * serviceName
; // Test service's instance name as a string. (malloced)
14027 char * serviceType
; // Test service's service type as a string. (malloced)
14028 uint8_t * recordName
; // FQDN of collider's record (same as test service's SRV+TXT records). (malloced)
14029 unsigned int testCaseIndex
; // Index of the current test case.
14030 uint32_t ifIndex
; // Index of the interface that the collider is to operate on.
14031 char * outputFilePath
; // File to write test results to. If NULL, then write to stdout. (malloced)
14032 OutputFormatType outputFormat
; // Format of test report output.
14033 Boolean registered
; // True if the test service instance is currently registered.
14034 Boolean testFailed
; // True if at least one test case failed.
14036 } ProbeConflictTestContext
;
14038 static void DNSSD_API
14039 _ProbeConflictTestRegisterCallback(
14040 DNSServiceRef inSDRef
,
14041 DNSServiceFlags inFlags
,
14042 DNSServiceErrorType inError
,
14043 const char * inName
,
14044 const char * inType
,
14045 const char * inDomain
,
14046 void * inContext
);
14047 static void _ProbeConflictTestColliderStopHandler( void *inContext
, OSStatus inError
);
14048 static OSStatus
_ProbeConflictTestStartNextTest( ProbeConflictTestContext
*inContext
);
14049 static OSStatus
_ProbeConflictTestStopCurrentTest( ProbeConflictTestContext
*inContext
, Boolean inRenamed
);
14050 static void _ProbeConflictTestFinalizeAndExit( ProbeConflictTestContext
*inContext
) ATTRIBUTE_NORETURN
;
14052 static void ProbeConflictTestCmd( void )
14055 ProbeConflictTestContext
* context
;
14056 const char * serviceName
;
14059 context
= (ProbeConflictTestContext
*) calloc( 1, sizeof( *context
) );
14060 require_action( context
, exit
, err
= kNoMemoryErr
);
14062 if( gProbeConflictTest_Interface
)
14064 err
= InterfaceIndexFromArgString( gProbeConflictTest_Interface
, &context
->ifIndex
);
14065 require_noerr_quiet( err
, exit
);
14069 err
= _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All
, NULL
, &context
->ifIndex
);
14070 require_noerr_quiet( err
, exit
);
14073 if( gProbeConflictTest_OutputFilePath
)
14075 context
->outputFilePath
= strdup( gProbeConflictTest_OutputFilePath
);
14076 require_action( context
->outputFilePath
, exit
, err
= kNoMemoryErr
);
14079 err
= OutputFormatFromArgString( gProbeConflictTest_OutputFormat
, &context
->outputFormat
);
14080 require_noerr_quiet( err
, exit
);
14082 context
->results
= CFArrayCreateMutable( NULL
, kProbeConflictTestCaseCount
, &kCFTypeArrayCallBacks
);
14083 require_action( context
->results
, exit
, err
= kNoMemoryErr
);
14085 serviceName
= gProbeConflictTest_UseComputerName
? NULL
: kProbeConflictTestService_DefaultName
;
14087 ASPrintF( &context
->serviceType
, "_pctest-%s._udp",
14088 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
14089 require_action( context
->serviceType
, exit
, err
= kNoMemoryErr
);
14091 context
->testStartTime
= NanoTimeGetCurrent();
14092 err
= DNSServiceRegister( &context
->registration
, 0, context
->ifIndex
, serviceName
, context
->serviceType
, "local.",
14093 NULL
, htons( kProbeConflictTestService_Port
), 0, NULL
, _ProbeConflictTestRegisterCallback
, context
);
14094 require_noerr( err
, exit
);
14096 err
= DNSServiceSetDispatchQueue( context
->registration
, dispatch_get_main_queue() );
14097 require_noerr( err
, exit
);
14105 //===========================================================================================================================
14106 // _ProbeConflictTestRegisterCallback
14107 //===========================================================================================================================
14109 static void DNSSD_API
14110 _ProbeConflictTestRegisterCallback(
14111 DNSServiceRef inSDRef
,
14112 DNSServiceFlags inFlags
,
14113 DNSServiceErrorType inError
,
14114 const char * inName
,
14115 const char * inType
,
14116 const char * inDomain
,
14120 ProbeConflictTestContext
* const context
= (ProbeConflictTestContext
*) inContext
;
14124 Unused( inDomain
);
14127 require_noerr( err
, exit
);
14129 if( !context
->registered
)
14131 if( inFlags
& kDNSServiceFlagsAdd
)
14134 size_t recordNameLen
;
14136 uint8_t name
[ kDomainNameLengthMax
];
14138 context
->registered
= true;
14140 FreeNullSafe( context
->serviceName
);
14141 context
->serviceName
= strdup( inName
);
14142 require_action( context
->serviceName
, exit
, err
= kNoMemoryErr
);
14144 err
= DomainNameFromString( name
, context
->serviceName
, NULL
);
14145 require_noerr( err
, exit
);
14147 err
= DomainNameAppendString( name
, context
->serviceType
, NULL
);
14148 require_noerr( err
, exit
);
14150 err
= DomainNameAppendString( name
, "local", NULL
);
14151 require_noerr( err
, exit
);
14153 ForgetMem( &context
->recordName
);
14154 err
= DomainNameDup( name
, &context
->recordName
, &recordNameLen
);
14155 require_noerr( err
, exit
);
14156 require_fatal( recordNameLen
> 0, "Record name length is zero." ); // Prevents dubious static analyzer warning.
14158 // Make the first label all caps so that it's easier to spot in system logs.
14160 ptr
= context
->recordName
;
14161 for( len
= *ptr
++; len
> 0; --len
, ++ptr
) *ptr
= (uint8_t) toupper_safe( *ptr
);
14163 err
= _ProbeConflictTestStartNextTest( context
);
14164 require_noerr( err
, exit
);
14169 if( !( inFlags
& kDNSServiceFlagsAdd
) )
14171 context
->registered
= false;
14172 err
= _ProbeConflictTestStopCurrentTest( context
, true );
14173 require_noerr( err
, exit
);
14179 if( err
) exit( 1 );
14182 //===========================================================================================================================
14183 // _ProbeConflictTestColliderStopHandler
14184 //===========================================================================================================================
14186 static void _ProbeConflictTestColliderStopHandler( void *inContext
, OSStatus inError
)
14189 ProbeConflictTestContext
* const context
= (ProbeConflictTestContext
*) inContext
;
14192 require_noerr_quiet( err
, exit
);
14194 ForgetCF( &context
->collider
);
14196 err
= _ProbeConflictTestStopCurrentTest( context
, false );
14197 require_noerr( err
, exit
);
14199 err
= _ProbeConflictTestStartNextTest( context
);
14200 require_noerr( err
, exit
);
14203 if( err
) exit( 1 );
14206 //===========================================================================================================================
14207 // _ProbeConflictTestStartNextTest
14208 //===========================================================================================================================
14210 static OSStatus
_ProbeConflictTestStartNextTest( ProbeConflictTestContext
*inContext
)
14213 const ProbeConflictTestCase
* testCase
;
14215 check( !inContext
->collider
);
14217 if( inContext
->testCaseIndex
< kProbeConflictTestCaseCount
)
14219 testCase
= &kProbeConflictTestCases
[ inContext
->testCaseIndex
];
14223 _ProbeConflictTestFinalizeAndExit( inContext
);
14226 err
= MDNSColliderCreate( dispatch_get_main_queue(), &inContext
->collider
);
14227 require_noerr( err
, exit
);
14229 err
= MDNSColliderSetProgram( inContext
->collider
, testCase
->program
);
14230 require_noerr( err
, exit
);
14232 err
= MDNSColliderSetRecord( inContext
->collider
, inContext
->recordName
, kDNSServiceType_TXT
,
14233 kProbeConflictTestTXTPtr
, kProbeConflictTestTXTLen
);
14234 require_noerr( err
, exit
);
14236 MDNSColliderSetProtocols( inContext
->collider
, kMDNSColliderProtocol_IPv4
);
14237 MDNSColliderSetInterfaceIndex( inContext
->collider
, inContext
->ifIndex
);
14238 MDNSColliderSetStopHandler( inContext
->collider
, _ProbeConflictTestColliderStopHandler
, inContext
);
14240 inContext
->startTime
= NanoTimeGetCurrent();
14241 err
= MDNSColliderStart( inContext
->collider
);
14242 require_noerr( err
, exit
);
14248 //===========================================================================================================================
14249 // _ProbeConflictTestStopCurrentTest
14250 //===========================================================================================================================
14252 #define kProbeConflictTestCaseResultKey_Description CFSTR( "description" )
14253 #define kProbeConflictTestCaseResultKey_StartTime CFSTR( "startTime" )
14254 #define kProbeConflictTestCaseResultKey_EndTime CFSTR( "endTime" )
14255 #define kProbeConflictTestCaseResultKey_ExpectedRename CFSTR( "expectedRename" )
14256 #define kProbeConflictTestCaseResultKey_ServiceName CFSTR( "serviceName" )
14257 #define kProbeConflictTestCaseResultKey_Passed CFSTR( "passed" )
14259 static OSStatus
_ProbeConflictTestStopCurrentTest( ProbeConflictTestContext
*inContext
, Boolean inRenamed
)
14262 const ProbeConflictTestCase
* testCase
;
14265 char startTime
[ 32 ];
14266 char endTime
[ 32 ];
14268 now
= NanoTimeGetCurrent();
14270 if( inContext
->collider
)
14272 MDNSColliderSetStopHandler( inContext
->collider
, NULL
, NULL
);
14273 MDNSColliderStop( inContext
->collider
);
14274 CFRelease( inContext
->collider
);
14275 inContext
->collider
= NULL
;
14278 testCase
= &kProbeConflictTestCases
[ inContext
->testCaseIndex
];
14279 passed
= ( ( testCase
->expectsRename
&& inRenamed
) || ( !testCase
->expectsRename
&& !inRenamed
) ) ? true : false;
14280 if( !passed
) inContext
->testFailed
= true;
14282 _NanoTime64ToTimestamp( inContext
->startTime
, startTime
, sizeof( startTime
) );
14283 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
14285 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, inContext
->results
,
14287 "%kO=%s" // description
14288 "%kO=%b" // expectedRename
14289 "%kO=%s" // startTime
14290 "%kO=%s" // endTime
14291 "%kO=%s" // serviceName
14294 kProbeConflictTestCaseResultKey_Description
, testCase
->description
,
14295 kProbeConflictTestCaseResultKey_ExpectedRename
, testCase
->expectsRename
,
14296 kProbeConflictTestCaseResultKey_StartTime
, startTime
,
14297 kProbeConflictTestCaseResultKey_EndTime
, endTime
,
14298 kProbeConflictTestCaseResultKey_ServiceName
, inContext
->serviceName
,
14299 kProbeConflictTestCaseResultKey_Passed
, passed
);
14300 require_noerr( err
, exit
);
14302 ++inContext
->testCaseIndex
;
14308 //===========================================================================================================================
14309 // _ProbeConflictTestFinalizeAndExit
14310 //===========================================================================================================================
14312 #define kProbeConflictTestReportKey_StartTime CFSTR( "startTime" )
14313 #define kProbeConflictTestReportKey_EndTime CFSTR( "endTime" )
14314 #define kProbeConflictTestReportKey_ServiceType CFSTR( "serviceType" )
14315 #define kProbeConflictTestReportKey_Results CFSTR( "results" )
14316 #define kProbeConflictTestReportKey_Passed CFSTR( "passed" )
14318 static void _ProbeConflictTestFinalizeAndExit( ProbeConflictTestContext
*inContext
)
14321 CFPropertyListRef plist
;
14323 char startTime
[ 32 ];
14324 char endTime
[ 32 ];
14326 now
= NanoTimeGetCurrent();
14328 check( !inContext
->collider
);
14330 _NanoTime64ToTimestamp( inContext
->testStartTime
, startTime
, sizeof( startTime
) );
14331 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
14333 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
14335 "%kO=%s" // startTime
14336 "%kO=%s" // endTime
14337 "%kO=%s" // serviceType
14338 "%kO=%O" // results
14341 kProbeConflictTestReportKey_StartTime
, startTime
,
14342 kProbeConflictTestReportKey_EndTime
, endTime
,
14343 kProbeConflictTestReportKey_ServiceType
, inContext
->serviceType
,
14344 kProbeConflictTestReportKey_Results
, inContext
->results
,
14345 kProbeConflictTestReportKey_Passed
, inContext
->testFailed
? false : true );
14346 require_noerr( err
, exit
);
14347 ForgetCF( &inContext
->results
);
14349 err
= OutputPropertyList( plist
, inContext
->outputFormat
, inContext
->outputFilePath
);
14350 CFRelease( plist
);
14351 require_noerr( err
, exit
);
14353 exit( inContext
->testFailed
? 2 : 0 );
14356 ErrQuit( 1, "error: %#m\n", err
);
14359 //===========================================================================================================================
14360 // ExpensiveConstrainedsTestCmd
14361 //===========================================================================================================================
14363 #define NOTIFICATION_TIME_THRESHOLD 1500 // The maximum wating time allowed before notification happens
14364 #define TEST_REPETITION 2 // the number of repetition that one test has to passed
14365 #define LOOPBACK_INTERFACE_NAME "lo0"
14366 #define WIFI_TEST_QUESTION_NAME "www.example.com"
14367 #define EXPENSIVE_CONSTRAINED_MAX_RETRIES 1
14368 #define EXPENSIVE_CONSTRAINED_TEST_INTERVAL 5
14369 // Use "-n tag-expensive-test.ttl-86400.d.test." to run the test locally
14370 // #define LOOPBACK_TEST_QUESTION_NAME "tag-expensive-test.ttl-86400.d.test."
14372 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_START_TIME CFSTR( "Start Time" )
14373 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_END_TIME CFSTR( "End Time" )
14374 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_ALL_PASSED CFSTR( "All Tests Passed" )
14375 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_SUBTEST_RESULT CFSTR( "Subtest Results" )
14377 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME CFSTR( "Start Time" )
14378 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME CFSTR( "End Time" )
14379 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME CFSTR( "Question Name" )
14380 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS CFSTR( "DNS Service Flags" )
14381 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS CFSTR( "Protocols" )
14382 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX CFSTR( "Interface Index" )
14383 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME CFSTR( "Interface Name" )
14384 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT CFSTR( "Result" )
14385 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_ERROR CFSTR( "Error Description" )
14386 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS CFSTR( "Test Progress" )
14388 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_START_TIME CFSTR( "Start Time" )
14389 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_END_TIME CFSTR( "End Time" )
14390 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_STATE CFSTR( "State" )
14391 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPECT_RESULT CFSTR( "Expected Result" )
14392 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_ACTUAL_RESULT CFSTR( "Actual Result" )
14393 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPENSIVE_PREV_NOW CFSTR( "Expensive Prev->Now" )
14394 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CONSTRAINED_PREV_NOW CFSTR( "Constrained Prev->Now" )
14395 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CALL_BACK CFSTR( "Call Back" )
14397 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_TIMESTAMP CFSTR( "Timestamp" )
14398 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_NAME CFSTR( "Answer Name" )
14399 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_FLAGS CFSTR( "Add or Remove" )
14400 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_INTERFACE CFSTR( "Interface Index" )
14401 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_ADDRESS CFSTR( "Address" )
14403 // All the states that ends with _PREPARE represents the state where the test state is reset and initialized.
14404 enum ExpensiveConstrainedTestState
14407 TEST_EXPENSIVE_PREPARE
,
14408 TEST_EXPENSIVE
, // Test if mDNSResponder can handle "expensive" status change of the corresponding interface
14409 TEST_CONSTRAINED_PREPARE
,
14410 TEST_CONSTRAINED
, // Test if mDNSResponder can handle "constrained" status change of the corresponding interface
14411 TEST_EXPENSIVE_CONSTRAINED_PREPARE
,
14412 TEST_EXPENSIVE_CONSTRAINED
, // Test if mDNSResponder can handle "expensive" and "constrained" status change of the corresponding interface at the same time
14416 enum ExpensiveConstrainedTestOperation
14418 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.
14419 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.
14420 NO_UPDATE
// no status update notification
14425 uint32_t subtestIndex
; // The index of parameter for the subtest
14426 DNSServiceRef opRef
; // sdRef for the DNSServiceGetAddrInfo operation.
14427 const char * name
; // Hostname to resolve.
14428 DNSServiceFlags flags
; // Flags argument for DNSServiceGetAddrInfo().
14429 DNSServiceProtocol protocols
; // Protocols argument for DNSServiceGetAddrInfo().
14430 uint32_t ifIndex
; // Interface index argument for DNSServiceGetAddrInfo().
14431 char ifName
[IFNAMSIZ
]; // Interface name for the given interface index.
14432 dispatch_source_t timer
; // The test will check if the current behavior is valid, which is called by
14433 // the timer per 2s.
14435 Boolean isExpensivePrev
; // If the interface is expensive in the previous test step.
14436 Boolean isExpensiveNow
; // If the interface is expensive now.
14437 Boolean isConstrainedPrev
; // If the interface is constrained in the previous test step.
14438 Boolean isConstrainedNow
; // If the interface is constrained now.
14439 Boolean startFromExpensive
; // All the test will start from expensive/constrained interface, so there won's be an answer until the interface is changed.
14440 uint8_t numOfRetries
; // the number of retries we can have if the test fail
14441 struct timeval updateTime
; // The time when interface status(expensive or constrained) is changed.
14442 struct timeval notificationTime
; // The time when callback function, which is passed to DNSServiceGetAddrInfo, gets called.
14443 uint32_t counter
; // To record how many times the test has repeated.
14444 enum ExpensiveConstrainedTestState state
; // The current test state.
14445 enum ExpensiveConstrainedTestOperation expectedOperation
; // the test expects this kind of notification
14446 enum ExpensiveConstrainedTestOperation operation
; // represents what notification the callback function gets.
14448 NanoTime64 testReport_startTime
; // when the entire test starts
14449 CFMutableArrayRef subtestReport
; // stores the log message for every subtest
14450 NanoTime64 subtestReport_startTime
; // when the subtest starts
14451 CFMutableArrayRef subtestProgress
; // one test iteration
14452 NanoTime64 subtestProgress_startTime
; // when the test iteration starts
14453 CFMutableArrayRef subtestProgress_callBack
; // array of ADD/REMOVE events
14454 char * outputFilePath
; // File to write test results to. If NULL, then write to stdout. (malloced)
14455 OutputFormatType outputFormat
; // Format of test report output.
14456 } ExpensiveConstrainedContext
;
14458 // structure that controls how the subtest is run
14461 const char *qname
; // the name of the query, when the ends with ".d.test.", test will send query to local DNS server
14462 Boolean deny_expensive
; // if the query should avoid using expensive interface
14463 Boolean deny_constrained
; // if the query should avoid using constrained interface
14464 Boolean start_from_expensive
; // if the query should starts from using an expensive interface
14465 Boolean ipv4_query
; // only allow IPv4 query
14466 Boolean ipv6_query
; // only allow IPv6 query
14467 int8_t test_passed
; // if the subtest passes
14468 } ExpensiveConstrainedTestParams
;
14470 static ExpensiveConstrainedTestParams ExpensiveConstrainedSubtestParams
[] =
14472 // qname deny_expensive deny_constrained start_from_expensive ipv4_query ipv6_query
14473 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, true, true, -1},
14474 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, true, true, -1},
14475 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, true, true, -1},
14476 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, true, true, -1},
14477 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, true, true, -1},
14478 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, true, true, -1},
14480 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, true, false, -1},
14481 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, true, false, -1},
14482 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, true, false, -1},
14483 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, true, false, -1},
14484 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, true, false, -1},
14485 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, true, false, -1},
14487 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, false, true, -1},
14488 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, false, true, -1},
14489 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, false, true, -1},
14490 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, false, true, -1},
14491 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, false, true, -1},
14492 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, false, true, -1}
14495 static void ExpensiveConstrainedSetupLocalDNSServer( ExpensiveConstrainedContext
*context
);
14496 static void ExpensiveConstrainedStartTestHandler( ExpensiveConstrainedContext
*context
);
14497 static void ExpensiveConstrainedStopTestHandler( ExpensiveConstrainedContext
*context
);
14498 static void ExpensiveConstrainedSetupTimer( ExpensiveConstrainedContext
*context
, uint32_t second
);
14499 static void ExpensiveConstrainedTestTimerEventHandler( ExpensiveConstrainedContext
*context
);
14500 static void DNSSD_API
14501 ExpensiveConstrainedCallback(
14502 DNSServiceRef inSDRef
,
14503 DNSServiceFlags inFlags
,
14504 uint32_t inInterfaceIndex
,
14505 DNSServiceErrorType inError
,
14506 const char * inHostname
,
14507 const struct sockaddr
* inSockAddr
,
14509 void * inContext
);
14510 static void ExpensiveConstrainedInitializeContext( ExpensiveConstrainedContext
*context
);
14511 static void ExpensiveConstrainedStopAndCleanTheTest( ExpensiveConstrainedContext
*context
);
14512 static void ExpensiveConstrainedSubtestProgressReport( ExpensiveConstrainedContext
*context
);
14513 static void ExpensiveConstrainedSubtestReport( ExpensiveConstrainedContext
*context
, const char *error_description
);
14514 static void ExpensiveConstrainedFinalResultReport( ExpensiveConstrainedContext
*context
, Boolean allPassed
);
14515 static const char *ExpensiveConstrainedProtocolString(DNSServiceProtocol protocol
);
14516 static const char *ExpensiveConstrainedStateString(enum ExpensiveConstrainedTestState state
);
14517 static const char *ExpensiveConstrainedOperationString(enum ExpensiveConstrainedTestOperation operation
);
14518 static Boolean
expensiveConstrainedEndsWith( const char *str
, const char *suffix
);
14520 //===========================================================================================================================
14521 // ExpensiveConstrainedTestCmd
14522 //===========================================================================================================================
14523 static void ExpensiveConstrainedTestCmd( void )
14526 dispatch_source_t signalSource
= NULL
;
14527 ExpensiveConstrainedContext
* context
= NULL
;
14529 // Set up SIGINT handler.
14530 signal( SIGINT
, SIG_IGN
);
14531 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
14532 require_noerr( err
, exit
);
14533 dispatch_resume( signalSource
);
14535 // create the test context
14536 context
= (ExpensiveConstrainedContext
*) calloc( 1, sizeof(*context
) );
14537 require_action( context
, exit
, err
= kNoMemoryErr
);
14539 // get the command line option
14540 err
= OutputFormatFromArgString( gExpensiveConstrainedTest_OutputFormat
, &context
->outputFormat
);
14541 require_noerr_quiet( err
, exit
);
14542 if ( gExpensiveConstrainedTest_OutputFilePath
)
14544 context
->outputFilePath
= strdup( gExpensiveConstrainedTest_OutputFilePath
);
14545 require_noerr_quiet( context
->outputFilePath
, exit
);
14548 // initialize context
14549 context
->subtestIndex
= 0;
14550 context
->numOfRetries
= EXPENSIVE_CONSTRAINED_MAX_RETRIES
;
14552 // initialize the CFArray used to store the log
14553 context
->subtestReport
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14554 context
->testReport_startTime
= NanoTimeGetCurrent();
14556 // setup local DNS server
14557 ExpensiveConstrainedSetupLocalDNSServer( context
);
14559 ExpensiveConstrainedStartTestHandler( context
);
14567 //===========================================================================================================================
14568 // ExpensiveConstrainedSetupLocalDNSServer
14569 //===========================================================================================================================
14571 static void ExpensiveConstrainedSetupLocalDNSServer( ExpensiveConstrainedContext
*context
)
14573 pid_t current_pid
= getpid();
14574 OSStatus err
= SpawnCommand( &context
->serverPID
, "dnssdutil server -l --follow %d", current_pid
);
14577 FPrintF( stdout
, "dnssdutil server -l --follow <PID> failed, error: %d\n", err
);
14583 //===========================================================================================================================
14584 // ExpensiveConstrainedStartTestHandler
14585 //===========================================================================================================================
14587 static void ExpensiveConstrainedStartTestHandler( ExpensiveConstrainedContext
*context
)
14590 ExpensiveConstrainedSetupTimer( context
, EXPENSIVE_CONSTRAINED_TEST_INTERVAL
);
14592 // set the event handler for the 3s timer
14593 dispatch_source_set_event_handler( context
->timer
, ^{
14594 ExpensiveConstrainedTestTimerEventHandler( context
);
14597 dispatch_resume( context
->timer
);
14600 //===========================================================================================================================
14601 // ExpensiveConstrainedStartTestHandler
14602 //===========================================================================================================================
14604 static void ExpensiveConstrainedStopTestHandler( ExpensiveConstrainedContext
*context
)
14606 dispatch_cancel( context
->timer
);
14607 dispatch_release( context
->timer
);
14608 context
->timer
= NULL
;
14611 //===========================================================================================================================
14612 // ExpensiveConstrainedSetupTimer
14613 //===========================================================================================================================
14615 static void ExpensiveConstrainedSetupTimer( ExpensiveConstrainedContext
*context
, uint32_t second
)
14617 // set the timer source, the event handler will be called for every "second" seconds
14618 context
->timer
= dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, dispatch_get_main_queue() );
14619 if ( context
->timer
== NULL
)
14621 FPrintF( stdout
, "dispatch_source_create:DISPATCH_SOURCE_TYPE_TIMER failed\n" );
14624 // the first block will be put into the queue "second"s after calling dispatch_resume
14625 dispatch_source_set_timer( context
->timer
, dispatch_time( DISPATCH_TIME_NOW
, second
* NSEC_PER_SEC
),
14626 (unsigned long long)(second
) * NSEC_PER_SEC
, 100ull * NSEC_PER_MSEC
);
14629 //===========================================================================================================================
14630 // ExpensiveConstrainedTestTimerEventHandler
14631 //===========================================================================================================================
14633 static void ExpensiveConstrainedTestTimerEventHandler( ExpensiveConstrainedContext
*context
)
14636 char buffer
[ 1024 ];
14637 const char *errorDescription
= NULL
;
14639 // do not log the state if we are in transition state
14640 if (context
->state
!= TEST_BEGIN
14641 && context
->state
!= TEST_SUCCEEDED
14642 && context
->state
!= TEST_CONSTRAINED_PREPARE
14643 && context
->state
!= TEST_EXPENSIVE_CONSTRAINED_PREPARE
)
14644 ExpensiveConstrainedSubtestProgressReport( context
);
14646 switch ( context
->state
) {
14649 ExpensiveConstrainedStopTestHandler( context
);
14651 // clear mDNSResponder cache
14652 err
= systemf( NULL
, "killall -HUP mDNSResponder" );
14653 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed");
14655 // initialize the global parameters
14656 ExpensiveConstrainedInitializeContext( context
);
14658 // The local DNS server is set up on the local only interface.
14659 gExpensiveConstrainedTest_Interface
= LOOPBACK_INTERFACE_NAME
;
14660 strncpy( context
->ifName
, gExpensiveConstrainedTest_Interface
, sizeof( context
->ifName
) );
14662 // The local DNS server is unscoped, so we must set our question to unscoped.
14663 context
->ifIndex
= kDNSServiceInterfaceIndexAny
;
14665 // The question name must end with "d.test.", "tag-expensive-test.ttl-86400.d.test." for example, then the test will
14666 // use the local dns server set up previously to run the test locally.
14667 require_action( gExpensiveConstrainedTest_Name
!= NULL
&& expensiveConstrainedEndsWith( gExpensiveConstrainedTest_Name
, "d.test." ), test_failed
,
14668 SNPrintF( buffer
, sizeof( buffer
), "The question name (%s) must end with \"d.test.\".\n", gExpensiveConstrainedTest_Name
);
14669 errorDescription
= buffer
);
14671 // get the quesion name
14672 context
->name
= gExpensiveConstrainedTest_Name
;
14674 // set the initial state for the interface
14675 context
->startFromExpensive
= gExpensiveConstrainedTest_StartFromExpensive
;
14676 err
= systemf( NULL
, "ifconfig %s %sexpensive && ifconfig %s -constrained", context
->ifName
, context
->startFromExpensive
? "" : "-", context
->ifName
);
14677 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed");
14678 sleep( 5 ); // wait for 5s to allow the interface change event de delivered to others
14680 // get question flag
14681 if ( gExpensiveConstrainedTest_DenyExpensive
) context
->flags
|= kDNSServiceFlagsDenyExpensive
;
14682 if ( gExpensiveConstrainedTest_DenyConstrained
) context
->flags
|= kDNSServiceFlagsDenyConstrained
;
14683 if ( gExpensiveConstrainedTest_ProtocolIPv4
) context
->protocols
|= kDNSServiceProtocol_IPv4
;
14684 if ( gExpensiveConstrainedTest_ProtocolIPv6
) context
->protocols
|= kDNSServiceProtocol_IPv6
;
14686 // prevent mDNSResponder from doing extra path evaluation and changing the interface to others(such as Bluetooth)
14687 #if( TARGET_OS_WATCH )
14688 context
->flags
|= kDNSServiceFlagsPathEvaluationDone
;
14692 DNSServiceGetAddrInfo( &context
->opRef
, context
->flags
, context
->ifIndex
, context
->protocols
, context
->name
, ExpensiveConstrainedCallback
, context
);
14694 // set the initial test status
14695 context
->subtestReport_startTime
= NanoTimeGetCurrent();
14696 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
14697 context
->state
= TEST_EXPENSIVE_PREPARE
; // start from expensive test
14698 context
->isExpensiveNow
= context
->startFromExpensive
? true : false;
14699 context
->isConstrainedNow
= false;
14700 context
->expectedOperation
= context
->isExpensiveNow
&& ( context
->flags
& kDNSServiceFlagsDenyExpensive
) ? NO_UPDATE
: RESULT_ADD
;
14701 context
->operation
= NO_UPDATE
;
14702 context
->subtestProgress
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14703 require_action( context
->subtestProgress
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14704 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14705 require_action( context
->subtestProgress
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14707 // set the queue where the callback will be called when there is an answer for the query
14708 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
14709 require_noerr( err
, test_failed
);
14711 ExpensiveConstrainedStartTestHandler( context
);
14714 case TEST_EXPENSIVE_PREPARE
:
14715 require_action( context
->isConstrainedNow
== false, test_failed
,
14716 SNPrintF( buffer
, sizeof( buffer
), "Interface %s should be unconstrained.\n", context
->ifName
);
14717 errorDescription
= buffer
);
14718 require_action( context
->expectedOperation
== context
->operation
, test_failed
,
14719 errorDescription
= "Operation is not expected" );
14721 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
14722 context
->state
= TEST_EXPENSIVE
; // begin to test expensive flag
14723 context
->counter
= 0; // the number of test repetition that has passed
14724 context
->isExpensivePrev
= context
->isExpensiveNow
;
14725 context
->isExpensiveNow
= !context
->isExpensiveNow
; // flip the expensive status
14726 context
->isConstrainedPrev
= false; // the interface is currently unconstrained
14727 context
->isConstrainedNow
= false; // the interface will be unconstrained in the current test
14728 if ( gExpensiveConstrainedTest_DenyExpensive
)
14729 context
->expectedOperation
= context
->isExpensiveNow
? RESULT_RMV
: RESULT_ADD
;
14731 context
->expectedOperation
= NO_UPDATE
;
14732 context
->operation
= NO_UPDATE
; // NO_UPDATE means the call back function has not been called
14733 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14734 require_action( context
->subtestProgress_callBack
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14736 err
= systemf( NULL
, "ifconfig %s %sexpensive", context
->ifName
, context
->isExpensiveNow
? "" : "-" );
14737 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
14739 // record the starting timestamp
14740 gettimeofday( &context
->updateTime
, NULL
);
14743 case TEST_EXPENSIVE
:
14744 // Since we are testing expensive flag, we should always turn the expensive flag on and off.
14745 require_action( context
->isExpensivePrev
^ context
->isExpensiveNow
, test_failed
,
14746 SNPrintF( buffer
, sizeof( buffer
), "The current expensive status should be different with the previous one: %d -> %d\n", context
->isExpensivePrev
, context
->isExpensiveNow
);
14747 errorDescription
= buffer
);
14748 // constrained flag is always turned off when testing expensive
14749 require_action( context
->isConstrainedNow
== false, test_failed
,
14750 SNPrintF( buffer
, sizeof( buffer
), "The interface %s should be unconstrained when testing \"expensive\"\n", context
->ifName
);
14751 errorDescription
= buffer
);
14752 require_action( context
->expectedOperation
== context
->operation
, test_failed
, errorDescription
= "Operation is not expected" );
14754 context
->counter
++; // one test repetition has passed
14755 if ( context
->counter
== TEST_REPETITION
) // expensive test finished
14757 // prepare to test constrained flag
14758 context
->state
= TEST_CONSTRAINED_PREPARE
;
14760 // reset the interface
14761 err
= systemf( NULL
, "ifconfig %s -expensive && ifconfig %s -constrained", context
->ifName
, context
->ifName
);
14762 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
14764 context
->isExpensiveNow
= false;
14765 context
->isConstrainedNow
= false;
14766 gettimeofday( &context
->updateTime
, NULL
);
14770 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
14771 context
->isExpensivePrev
= context
->isExpensiveNow
;
14772 context
->isExpensiveNow
= !context
->isExpensiveNow
; // flip the expensive status
14773 if ( gExpensiveConstrainedTest_DenyExpensive
)
14774 context
->expectedOperation
= context
->isExpensiveNow
? RESULT_RMV
: RESULT_ADD
;
14776 context
->expectedOperation
= NO_UPDATE
;
14777 context
->operation
= NO_UPDATE
;
14778 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14779 require_action( context
->subtestProgress_callBack
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14781 err
= systemf( NULL
, "ifconfig %s %sexpensive", context
->ifName
, context
->isExpensiveNow
? "" : "-" );
14782 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
14784 gettimeofday( &context
->updateTime
, NULL
);
14787 case TEST_CONSTRAINED_PREPARE
:
14788 // The interface should be inexpensive and unconstrained when the constrained test starts
14789 require_action( context
->isExpensiveNow
== false, test_failed
, SNPrintF( buffer
, sizeof( buffer
), "Interface %s should be inexpensive.", context
->ifName
);
14790 errorDescription
= buffer
);
14791 require_action( context
->isConstrainedNow
== false, test_failed
, SNPrintF( buffer
, sizeof( buffer
), "Interface %s should be unconstrained.\n", context
->ifName
);
14792 errorDescription
= buffer
);
14794 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
14795 context
->state
= TEST_CONSTRAINED
; // constrained interface is now under testing
14796 context
->counter
= 0;
14797 context
->isExpensivePrev
= false;
14798 context
->isExpensiveNow
= false;
14799 context
->isConstrainedPrev
= false;
14800 context
->isConstrainedNow
= true; // will set constrained flag on the interface
14801 if ( gExpensiveConstrainedTest_DenyConstrained
)
14802 context
->expectedOperation
= RESULT_RMV
;
14804 context
->expectedOperation
= NO_UPDATE
;
14805 context
->operation
= NO_UPDATE
;
14806 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14807 require_action( context
->subtestProgress_callBack
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14809 // change interface to the constrained one
14810 err
= systemf( NULL
, "ifconfig %s -expensive && ifconfig %s constrained", context
->ifName
, context
->ifName
);
14811 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
14813 gettimeofday( &context
->updateTime
, NULL
);
14815 case TEST_CONSTRAINED
:
14816 // Since we are testing constrained flag, we should always turn the constrained flag on and off.
14817 require_action( context
->isConstrainedPrev
^ context
->isConstrainedNow
, test_failed
,
14818 SNPrintF( buffer
, sizeof( buffer
), "The current constrained status should be different with the previous one: %d -> %d\n", context
->isConstrainedPrev
, context
->isConstrainedNow
);
14819 errorDescription
= buffer
);
14820 require_action( context
->isExpensiveNow
== false, test_failed
,
14821 SNPrintF( buffer
, sizeof( buffer
), "The interface %s should be inexpensive when testing \"constrained\"\n", context
->ifName
);
14822 errorDescription
= buffer
);
14823 require_action( context
->expectedOperation
== context
->operation
, test_failed
, errorDescription
= "Operation is not expected");
14825 context
->counter
++;
14826 if (context
->counter
== TEST_REPETITION
)
14828 // test changing expensive and constrained flags at the same time
14829 context
->state
= TEST_EXPENSIVE_CONSTRAINED_PREPARE
;
14832 err
= systemf( NULL
, "ifconfig %s -expensive && ifconfig %s -constrained", context
->ifName
, context
->ifName
);
14833 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
14835 context
->isExpensiveNow
= false;
14836 context
->isConstrainedNow
= false;
14837 gettimeofday( &context
->updateTime
, NULL
);
14841 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
14842 context
->isConstrainedPrev
= context
->isConstrainedNow
;
14843 context
->isConstrainedNow
= !context
->isConstrainedNow
; // flip constrained flag
14844 if ( gExpensiveConstrainedTest_DenyConstrained
)
14845 context
->expectedOperation
= context
->isConstrainedNow
? RESULT_RMV
: RESULT_ADD
;
14847 context
->expectedOperation
= NO_UPDATE
;
14848 context
->operation
= NO_UPDATE
;
14849 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14850 require_action( context
->subtestProgress_callBack
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14852 err
= systemf( NULL
, "ifconfig %s %sconstrained", context
->ifName
, context
->isConstrainedNow
? "" : "-" );
14853 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
14855 gettimeofday(&context
->updateTime
, NULL
);
14858 case TEST_EXPENSIVE_CONSTRAINED_PREPARE
:
14859 // The interface should be inexpensive and unconstrained when the constrained test starts
14860 require_action( context
->isExpensiveNow
== false, test_failed
,
14861 SNPrintF( buffer
, sizeof( buffer
), "Interface %s should be inexpensive.\n", context
->ifName
);
14862 errorDescription
= buffer
);
14863 require_action( context
->isConstrainedNow
== false, test_failed
,
14864 SNPrintF(buffer
, sizeof( buffer
), "Interface %s should be unconstrained.\n", context
->ifName
);
14865 errorDescription
= buffer
);
14867 // now flip expensive and constrained at the same time
14868 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
14869 context
->state
= TEST_EXPENSIVE_CONSTRAINED
;
14870 context
->counter
= 0;
14871 context
->isExpensivePrev
= false;
14872 context
->isExpensiveNow
= true;
14873 context
->isConstrainedPrev
= false;
14874 context
->isConstrainedNow
= true;
14875 if (gExpensiveConstrainedTest_DenyConstrained
|| gExpensiveConstrainedTest_DenyExpensive
)
14876 context
->expectedOperation
= RESULT_RMV
;
14878 context
->expectedOperation
= NO_UPDATE
;
14879 context
->operation
= NO_UPDATE
;
14880 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14881 require_action( context
->subtestProgress_callBack
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14883 err
= systemf(NULL
, "ifconfig %s expensive && ifconfig %s constrained", context
->ifName
, context
->ifName
);
14884 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
14886 gettimeofday( &context
->updateTime
, NULL
);
14888 case TEST_EXPENSIVE_CONSTRAINED
:
14889 // expensive and constrained flag should always be changed
14890 require_action( ( context
->isExpensivePrev
^ context
->isExpensiveNow
) && ( context
->isConstrainedPrev
^ context
->isConstrainedNow
), test_failed
,
14891 SNPrintF( buffer
, sizeof( buffer
), "Both expensive and constrained status need to be changed" );
14892 errorDescription
= buffer
);
14893 require_action( context
->isExpensiveNow
== context
->isConstrainedNow
, test_failed
, errorDescription
= "context->isExpensiveNow != context->isConstrainedNow" );
14894 require_action( context
->expectedOperation
== context
->operation
, test_failed
, errorDescription
= "Operation is not expected" );
14896 context
->counter
++;
14897 if ( context
->counter
== TEST_REPETITION
)
14899 context
->state
= TEST_SUCCEEDED
;
14903 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
14904 context
->isExpensivePrev
= context
->isExpensiveNow
;
14905 context
->isExpensiveNow
= !context
->isExpensiveNow
;
14906 context
->isConstrainedPrev
= context
->isConstrainedNow
;
14907 context
->isConstrainedNow
= !context
->isConstrainedNow
;
14908 if (gExpensiveConstrainedTest_DenyConstrained
|| gExpensiveConstrainedTest_DenyExpensive
)
14909 context
->expectedOperation
= context
->isExpensiveNow
? RESULT_RMV
: RESULT_ADD
;
14911 context
->expectedOperation
= NO_UPDATE
;
14912 context
->operation
= NO_UPDATE
;
14913 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14914 require_action( context
->subtestProgress_callBack
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14916 err
= systemf( NULL
, "ifconfig %s %sexpensive && ifconfig %s %sconstrained", context
->ifName
, context
->isExpensiveNow
? "" : "-", context
->ifName
, context
->isConstrainedNow
? "" : "-" );
14917 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
14919 gettimeofday( &context
->updateTime
, NULL
);
14924 ExpensiveConstrainedSubtestReport( context
, errorDescription
);
14925 ExpensiveConstrainedStopAndCleanTheTest( context
);
14926 if ( context
->numOfRetries
> 0 )
14928 context
->state
= TEST_BEGIN
;
14929 context
->numOfRetries
--;
14932 ExpensiveConstrainedSubtestParams
[context
->subtestIndex
++].test_passed
= 0;
14933 if (context
->subtestIndex
== (int) countof( ExpensiveConstrainedSubtestParams
))
14935 ExpensiveConstrainedFinalResultReport( context
, false );
14938 if (context
->timer
== NULL
)
14940 // If timer is NULL, it means that we encounter error before we set up the test handler, which is unrecoverable.
14941 ExpensiveConstrainedFinalResultReport( context
, false );
14944 context
->state
= TEST_BEGIN
;
14946 case TEST_SUCCEEDED
:
14947 ExpensiveConstrainedSubtestReport( context
, NULL
);
14948 ExpensiveConstrainedStopAndCleanTheTest( context
);
14949 ExpensiveConstrainedSubtestParams
[context
->subtestIndex
++].test_passed
= 1;
14950 if (context
->subtestIndex
== (int) countof( ExpensiveConstrainedSubtestParams
))
14952 // all the subtests have been run
14953 Boolean hasFailed
= false;
14954 for ( int i
= 0; i
< (int) countof( ExpensiveConstrainedSubtestParams
) && !hasFailed
; i
++ )
14955 hasFailed
= ( ExpensiveConstrainedSubtestParams
[i
].test_passed
!= 1 );
14957 ExpensiveConstrainedFinalResultReport( context
, !hasFailed
);
14958 exit( hasFailed
? 2 : 0 );
14960 context
->state
= TEST_BEGIN
;
14963 FPrintF( stdout
, "unknown error\n" );
14968 //===========================================================================================================================
14969 // ExpensiveConstrainedCallback
14970 //===========================================================================================================================
14972 static void DNSSD_API
14973 ExpensiveConstrainedCallback(
14974 __unused DNSServiceRef inSDRef
,
14975 DNSServiceFlags inFlags
,
14976 uint32_t inInterfaceIndex
,
14977 DNSServiceErrorType inError
,
14978 const char * inHostname
,
14979 const struct sockaddr
* inSockAddr
,
14980 __unused
uint32_t inTTL
,
14983 ExpensiveConstrainedContext
* const context
= (ExpensiveConstrainedContext
*)inContext
;
14985 const char * addrStr
;
14986 char addrStrBuf
[ kSockAddrStringMaxSize
];
14987 char inFlagsDescription
[ 128 ];
14989 char nowTimestamp
[ 32 ];
14991 switch ( inError
) {
14992 case kDNSServiceErr_NoError
:
14993 case kDNSServiceErr_NoSuchRecord
:
14996 case kDNSServiceErr_Timeout
:
14997 Exit( kExitReason_Timeout
);
15004 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
15006 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
15013 err
= SockAddrToString( inSockAddr
, kSockAddrStringFlagsNone
, addrStrBuf
);
15014 require_noerr( err
, exit
);
15015 addrStr
= addrStrBuf
;
15019 addrStr
= ( inSockAddr
->sa_family
== AF_INET
) ? kNoSuchRecordAStr
: kNoSuchRecordAAAAStr
;
15022 now
= NanoTimeGetCurrent();
15023 _NanoTime64ToTimestamp( now
, nowTimestamp
, sizeof( nowTimestamp
) );
15024 SNPrintF( inFlagsDescription
, sizeof( inFlagsDescription
), "%{du:cbflags}", inFlags
);
15025 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, context
->subtestProgress_callBack
,
15033 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_TIMESTAMP
, nowTimestamp
,
15034 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_NAME
, inHostname
,
15035 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_FLAGS
, inFlagsDescription
,
15036 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_INTERFACE
, (int64_t) inInterfaceIndex
,
15037 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_ADDRESS
, addrStr
15039 require_noerr_quiet( err
, exit
);
15041 if ( inFlags
& kDNSServiceFlagsMoreComing
)
15044 if ( inFlags
& kDNSServiceFlagsAdd
)
15045 context
->operation
= RESULT_ADD
;
15047 context
->operation
= RESULT_RMV
;
15049 gettimeofday(&context
->notificationTime
, NULL
);
15051 if( err
) exit( 1 );
15054 //===========================================================================================================================
15055 // ExpensiveConstrainedInitializeContext
15056 //===========================================================================================================================
15058 static void ExpensiveConstrainedInitializeContext( ExpensiveConstrainedContext
*context
)
15060 // clear the flags of the previous subtest
15061 context
->flags
= 0;
15062 context
->protocols
= 0;
15064 // get the parameter for the current subtest
15065 const ExpensiveConstrainedTestParams
*param
= &ExpensiveConstrainedSubtestParams
[context
->subtestIndex
];
15066 gExpensiveConstrainedTest_Name
= param
->qname
;
15067 gExpensiveConstrainedTest_DenyExpensive
= param
->deny_expensive
;
15068 gExpensiveConstrainedTest_DenyConstrained
= param
->deny_constrained
;
15069 gExpensiveConstrainedTest_StartFromExpensive
= param
->start_from_expensive
;
15070 gExpensiveConstrainedTest_ProtocolIPv4
= param
->ipv4_query
;
15071 gExpensiveConstrainedTest_ProtocolIPv6
= param
->ipv6_query
;
15074 //===========================================================================================================================
15075 // ExpensiveConstrainedStopAndCleanTheTest
15076 //===========================================================================================================================
15078 static void ExpensiveConstrainedStopAndCleanTheTest( ExpensiveConstrainedContext
*context
)
15080 // Stop the ongoing query
15081 if ( context
->opRef
!= NULL
)
15082 DNSServiceRefDeallocate( context
->opRef
);
15084 context
->opRef
= NULL
;
15085 context
->flags
= 0;
15086 context
->protocols
= 0;
15089 //===========================================================================================================================
15090 // ExpensiveConstrainedSubtestProgressReport
15091 //===========================================================================================================================
15093 static void ExpensiveConstrainedSubtestProgressReport( ExpensiveConstrainedContext
*context
)
15097 char startTime
[ 32 ];
15098 char endTime
[ 32 ];
15099 char expensive
[ 32 ];
15100 char constrained
[ 32 ];
15102 now
= NanoTimeGetCurrent();
15103 _NanoTime64ToTimestamp( context
->subtestProgress_startTime
, startTime
, sizeof( startTime
) );
15104 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
15106 snprintf( expensive
, sizeof( expensive
), "%s -> %s", context
->isExpensivePrev
? "True" : "False", context
->isExpensiveNow
? "True" : "False" );
15107 snprintf( constrained
, sizeof( constrained
), "%s -> %s", context
->isConstrainedPrev
? "True" : "False", context
->isConstrainedNow
? "True" : "False" );
15109 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, context
->subtestProgress
,
15120 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_START_TIME
, startTime
,
15121 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_END_TIME
, endTime
,
15122 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_STATE
, ExpensiveConstrainedStateString(context
->state
),
15123 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPECT_RESULT
, ExpensiveConstrainedOperationString(context
->expectedOperation
),
15124 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_ACTUAL_RESULT
, ExpensiveConstrainedOperationString(context
->operation
),
15125 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPENSIVE_PREV_NOW
, expensive
,
15126 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CONSTRAINED_PREV_NOW
, constrained
,
15127 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CALL_BACK
, context
->subtestProgress_callBack
15129 require_noerr( err
, exit
);
15130 ForgetCF( &context
->subtestProgress_callBack
);
15134 ErrQuit( 1, "error: %#m\n", err
);
15137 //===========================================================================================================================
15138 // ExpensiveConstrainedFinalSubtestReport
15139 //===========================================================================================================================
15141 static void ExpensiveConstrainedSubtestReport( ExpensiveConstrainedContext
*context
, const char *error_description
)
15145 char startTime
[ 32 ];
15146 char endTime
[ 32 ];
15147 char flagDescription
[ 1024 ];
15149 now
= NanoTimeGetCurrent();
15150 _NanoTime64ToTimestamp( context
->subtestReport_startTime
, startTime
, sizeof( startTime
) );
15151 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
15152 SNPrintF( flagDescription
, sizeof( flagDescription
), "%#{flags}", context
->flags
, kDNSServiceFlagsDescriptors
);
15154 if (error_description
!= NULL
)
15156 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, context
->subtestReport
,
15169 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME
, startTime
,
15170 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME
, endTime
,
15171 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME
, context
->name
,
15172 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS
, flagDescription
,
15173 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS
, ExpensiveConstrainedProtocolString( context
->protocols
),
15174 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX
, (int64_t) context
->ifIndex
,
15175 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME
, context
->ifName
,
15176 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT
, CFSTR( "Fail" ),
15177 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_ERROR
, error_description
,
15178 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS
, context
->subtestProgress
15183 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, context
->subtestReport
,
15195 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME
, startTime
,
15196 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME
, endTime
,
15197 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME
, context
->name
,
15198 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS
, flagDescription
,
15199 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS
, ExpensiveConstrainedProtocolString( context
->protocols
),
15200 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX
, (int64_t) context
->ifIndex
,
15201 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME
, context
->ifName
,
15202 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT
, CFSTR( "Pass" ),
15203 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS
, context
->subtestProgress
15207 require_noerr( err
, exit
);
15208 ForgetCF( &context
->subtestProgress
);
15211 ErrQuit( 1, "error: %#m\n", err
);
15214 //===========================================================================================================================
15215 // ExpensiveConstrainedFinalResultReport
15216 //===========================================================================================================================
15218 static void ExpensiveConstrainedFinalResultReport( ExpensiveConstrainedContext
*context
, Boolean allPassed
)
15221 CFPropertyListRef plist
;
15223 char startTime
[ 32 ];
15224 char endTime
[ 32 ];
15226 now
= NanoTimeGetCurrent();
15227 _NanoTime64ToTimestamp( context
->testReport_startTime
, startTime
, sizeof( startTime
) );
15228 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
15230 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
15237 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_START_TIME
, startTime
,
15238 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_END_TIME
, endTime
,
15239 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_ALL_PASSED
, allPassed
,
15240 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_SUBTEST_RESULT
, context
->subtestReport
15242 require_noerr( err
, exit
);
15243 ForgetCF( &context
->subtestReport
);
15245 err
= OutputPropertyList( plist
, context
->outputFormat
, context
->outputFilePath
);
15246 CFRelease( plist
);
15247 require_noerr( err
, exit
);
15251 ErrQuit( 1, "error: %#m\n", err
);
15254 //===========================================================================================================================
15255 // ExpensiveConstrainedProtocolString
15256 //===========================================================================================================================
15258 static const char *ExpensiveConstrainedProtocolString( DNSServiceProtocol protocol
)
15260 const char *str
= NULL
;
15261 switch ( protocol
) {
15262 case kDNSServiceProtocol_IPv4
:
15265 case kDNSServiceProtocol_IPv6
:
15268 case kDNSServiceProtocol_IPv4
| kDNSServiceProtocol_IPv6
:
15269 str
= "IPv4 & IPv6";
15277 //===========================================================================================================================
15278 // ExpensiveConstrainedStateString
15279 //===========================================================================================================================
15281 static const char *ExpensiveConstrainedStateString( enum ExpensiveConstrainedTestState state
)
15283 const char *str
= NULL
;
15286 str
= "TEST_BEGIN";
15288 case TEST_EXPENSIVE_PREPARE
:
15289 str
= "TEST_EXPENSIVE_PREPARE";
15291 case TEST_EXPENSIVE
:
15292 str
= "TEST_EXPENSIVE";
15294 case TEST_CONSTRAINED_PREPARE
:
15295 str
= "TEST_CONSTRAINED_PREPARE";
15297 case TEST_CONSTRAINED
:
15298 str
= "TEST_CONSTRAINED";
15300 case TEST_EXPENSIVE_CONSTRAINED_PREPARE
:
15301 str
= "TEST_EXPENSIVE_CONSTRAINED_PREPARE";
15303 case TEST_EXPENSIVE_CONSTRAINED
:
15304 str
= "TEST_EXPENSIVE_CONSTRAINED";
15307 str
= "TEST_FAILED";
15309 case TEST_SUCCEEDED
:
15310 str
= "TEST_SUCCEEDED";
15320 //===========================================================================================================================
15321 // ExpensiveConstrainedOperationString
15322 //===========================================================================================================================
15324 static const char *ExpensiveConstrainedOperationString( enum ExpensiveConstrainedTestOperation operation
)
15326 const char *str
= NULL
;
15327 switch ( operation
) {
15329 str
= "RESULT_ADD";
15332 str
= "RESULT_RMV";
15344 //===========================================================================================================================
15345 // expensiveConstrainedEndsWith
15346 //===========================================================================================================================
15347 static Boolean
expensiveConstrainedEndsWith( const char *str
, const char *suffix
)
15349 if ( !str
|| !suffix
)
15351 size_t lenstr
= strlen( str
);
15352 size_t lensuffix
= strlen( suffix
);
15353 if ( lensuffix
> lenstr
)
15355 return strncmp( str
+ lenstr
- lensuffix
, suffix
, lensuffix
) == 0;
15358 //===========================================================================================================================
15359 // RegistrationTestCmd
15360 //===========================================================================================================================
15362 typedef struct RegistrationSubtest RegistrationSubtest
;
15366 CFMutableArrayRef subtestReports
; // Array of subtest reports.
15367 dispatch_source_t timer
; // Timer to enforce subtest durations.
15368 dispatch_source_t sigSourceINT
; // SIGINT signal handler for a clean test exit.
15369 dispatch_source_t sigSourceTERM
; // SIGTERM signal handler for a clean test exit.
15370 RegistrationSubtest
* subtest
; // Current subtest.
15371 char * outputFilePath
; // Path of test result output file. If NULL, stdout will be used.
15372 OutputFormatType outputFormat
; // Format of test results output.
15373 CFStringRef computerNamePrev
; // Previous ComputerName.
15374 CFStringRef localHostNamePrev
; // Previous LocalHostName.
15375 NanoTime64 startTime
; // Test's start time.
15376 char * computerName
; // Temporary ComputerName to set during testing. (malloc'd)
15377 char * localHostName
; // Temporary LocalHostName to set during testing. (malloc'd)
15378 CFStringEncoding computerNamePrevEncoding
; // Previous ComputerName's encoding.
15379 int subtestIndex
; // Index of current subtest.
15380 Boolean computerNameSet
; // True if a temporary ComputerName was set.
15381 Boolean localHostNameSet
; // True if a temporary LocalHostName was set.
15382 Boolean failed
; // True if at least one non-skipped subtest failed.
15383 Boolean forBATS
; // True if the test is running in a BATS environment.
15385 } RegistrationTest
;
15389 kRegistrationInterfaceSet_Null
= 0,
15390 kRegistrationInterfaceSet_All
= 1,
15391 kRegistrationInterfaceSet_AllPlusAWDL
= 2,
15392 kRegistrationInterfaceSet_LoopbackOnly
= 3,
15393 kRegistrationInterfaceSet_AWDLOnly
= 4
15395 } RegistrationInterfaceSet
;
15399 RegistrationInterfaceSet interfaceSet
; // Interfaces to register the service over.
15400 Boolean useDefaultName
; // True if registration is to use the default service name.
15401 Boolean useLODiscovery
; // True if discovery is to use kDNSServiceInterfaceIndexLocalOnly.
15403 } RegistrationSubtestParams
;
15405 static const RegistrationSubtestParams kRegistrationSubtestParams
[] =
15407 { kRegistrationInterfaceSet_All
, true, false },
15408 { kRegistrationInterfaceSet_All
, false, false },
15409 { kRegistrationInterfaceSet_AllPlusAWDL
, true, false },
15410 { kRegistrationInterfaceSet_AllPlusAWDL
, false, false },
15411 { kRegistrationInterfaceSet_LoopbackOnly
, true, false },
15412 { kRegistrationInterfaceSet_LoopbackOnly
, false, false },
15413 { kRegistrationInterfaceSet_AWDLOnly
, true, false },
15414 { kRegistrationInterfaceSet_AWDLOnly
, false, false },
15415 { kRegistrationInterfaceSet_All
, true, true },
15416 { kRegistrationInterfaceSet_All
, false, true },
15417 { kRegistrationInterfaceSet_AllPlusAWDL
, true, true },
15418 { kRegistrationInterfaceSet_AllPlusAWDL
, false, true },
15419 { kRegistrationInterfaceSet_LoopbackOnly
, true, true },
15420 { kRegistrationInterfaceSet_LoopbackOnly
, false, true },
15421 { kRegistrationInterfaceSet_AWDLOnly
, true, true },
15422 { kRegistrationInterfaceSet_AWDLOnly
, false, true }
15427 NanoTime64 browseResultTime
; // Per-interface browse result time.
15428 NanoTime64 querySRVResultTime
; // Per-interface SRV record query result time.
15429 NanoTime64 queryTXTResultTime
; // Per-interface TXT record query result time.
15431 } RegistrationResultTimes
;
15435 MDNSInterfaceItem base
; // Underlying MDNSInterface linked-list item.
15436 RegistrationResultTimes times
; // Per-interface result times.
15438 } RegistrationInterfaceItem
;
15440 struct RegistrationSubtest
15442 DNSServiceRef registration
; // DNS-SD service registration.
15443 DNSServiceRef connection
; // Shared DNS-SD connection.
15444 DNSServiceRef browse
; // DNS-SD browse for service's type.
15445 DNSServiceRef querySRV
; // DNS-SD query request for service's SRV record.
15446 DNSServiceRef queryTXT
; // DNS-SD query request for service's TXT record.
15447 CFMutableArrayRef unexpected
; // Array of unexpected registration, browse, and query results.
15448 #if( TARGET_OS_WATCH )
15449 CFMutableArrayRef ignored
; // Array of unexpected, but ignored, browse and query results.
15451 const char * serviceName
; // Service's name.
15452 char * serviceNameCustom
; // Service's name if using a custom name. (malloc'd)
15453 char * serviceType
; // Service's service type. (malloc'd)
15454 size_t serviceTypeLen
; // C string length of service's service type.
15455 char * serviceFQDN
; // Service's FQDN, i.e., name of its SRV and TXT records.
15456 uint8_t * txtPtr
; // Pointer to service's TXT record data. (malloc'd)
15457 size_t txtLen
; // Length of service's TXT record data.
15458 RegistrationInterfaceItem
* ifList
; // If ifIndex == 0, interfaces that service should register over.
15459 RegistrationResultTimes ifTimes
; // If ifIndex != 0, result times for interface with that index.
15460 RegistrationTest
* test
; // Pointer to parent test.
15461 NanoTime64 startTime
; // Subtest's start time.
15462 char * description
; // Subtest's description. (malloc'd)
15463 uint32_t ifIndex
; // Interface index used for service registration.
15464 uint16_t port
; // Service's port number.
15465 Boolean useLODiscovery
; // True if discovery is to use kDNSServiceInterfaceIndexLocalOnly.
15466 Boolean includeAWDL
; // True if the IncludeAWDL flag was used during registration.
15467 Boolean ifIsAWDL
; // True if ifIndex is the index of an AWDL interface.
15468 Boolean skipped
; // True if this subtest is to be skipped.
15469 Boolean registered
; // True if the test service was successfully registered.
15470 Boolean useDefaultName
; // True if the service is to use the default service name.
15473 static OSStatus
_RegistrationTestCreate( RegistrationTest
**outTest
);
15474 static void _RegistrationTestFree( RegistrationTest
*inTest
);
15475 static void _RegistrationTestBegin( void *inContext
);
15476 static void _RegistrationTestProceed( RegistrationTest
*inTest
);
15477 static OSStatus
_RegistrationTestStart( RegistrationTest
*inTest
);
15478 static void _RegistrationTestStop( RegistrationTest
*inTest
);
15479 #define _RegistrationTestForget( X ) ForgetCustomEx( X, _RegistrationTestStop, _RegistrationTestFree )
15481 _RegistrationTestStartSubtest(
15482 RegistrationTest
* inTest
,
15483 const RegistrationSubtestParams
* inParams
,
15484 Boolean
* outSkipped
);
15485 static OSStatus
_RegistrationTestEndSubtest( RegistrationTest
*inTest
);
15486 static void _RegistrationTestEnd( RegistrationTest
*inTest
) ATTRIBUTE_NORETURN
;
15487 static void _RegistrationTestExit( RegistrationTest
*inTest
, OSStatus inError
) ATTRIBUTE_NORETURN
;
15488 static OSStatus
_RegistrationSubtestCreate( RegistrationSubtest
**outSubtest
);
15489 static void _RegistrationSubtestStop( RegistrationSubtest
*inSubtest
);
15490 static void _RegistrationSubtestFree( RegistrationSubtest
*inSubtest
);
15491 #define _RegistrationSubtestForget( X ) ForgetCustomEx( X, _RegistrationSubtestStop, _RegistrationSubtestFree )
15492 static OSStatus
_RegistrationTestInterfaceListCreate( Boolean inIncludeAWDL
, RegistrationInterfaceItem
**outList
);
15494 _RegistrationTestCreateRandomTXTRecord(
15497 uint8_t ** outTXTPtr
,
15498 size_t * outTXTLen
);
15499 static void DNSSD_API
15500 _RegistrationSubtestRegisterCallback(
15501 DNSServiceRef inSDRef
,
15502 DNSServiceFlags inFlags
,
15503 DNSServiceErrorType inError
,
15504 const char * inName
,
15505 const char * inType
,
15506 const char * inDomain
,
15507 void * inContext
);
15508 static void DNSSD_API
15509 _RegistrationSubtestBrowseCallback(
15510 DNSServiceRef inSDRef
,
15511 DNSServiceFlags inFlags
,
15512 uint32_t inIfIndex
,
15513 DNSServiceErrorType inError
,
15514 const char * inServiceName
,
15515 const char * inServiceType
,
15516 const char * inDomain
,
15517 void * inContext
);
15518 static void DNSSD_API
15519 _RegistrationSubtestQueryCallback(
15520 DNSServiceRef inSDRef
,
15521 DNSServiceFlags inFlags
,
15522 uint32_t inIfIndex
,
15523 DNSServiceErrorType inError
,
15524 const char * inName
,
15527 uint16_t inRDataLen
,
15528 const void * inRDataPtr
,
15530 void * inContext
);
15531 static Boolean
_RegistrationSubtestValidServiceType( const RegistrationSubtest
*inSubtest
, const char *inServiceType
);
15532 static RegistrationResultTimes
*
15533 _RegistrationSubtestGetInterfaceResultTimes(
15534 RegistrationSubtest
* inSubtest
,
15535 uint32_t inIfIndex
,
15536 Boolean
* outIsAWDL
);
15537 static void _RegistrationTestTimerHandler( void *inContext
);
15538 #if( TARGET_OS_WATCH )
15539 static Boolean
_RegistrationTestInterfaceIsWiFi( const char *inIfName
);
15542 static void RegistrationTestCmd( void )
15545 RegistrationTest
* test
;
15547 err
= _RegistrationTestCreate( &test
);
15548 require_noerr( err
, exit
);
15550 if( gRegistrationTest_BATSEnvironment
) test
->forBATS
= true;
15551 if( gRegistrationTest_OutputFilePath
)
15553 test
->outputFilePath
= strdup( gRegistrationTest_OutputFilePath
);
15554 require_action( test
->outputFilePath
, exit
, err
= kNoMemoryErr
);
15557 err
= OutputFormatFromArgString( gRegistrationTest_OutputFormat
, &test
->outputFormat
);
15558 require_noerr_quiet( err
, exit
);
15560 dispatch_async_f( dispatch_get_main_queue(), test
, _RegistrationTestBegin
);
15564 if( test
) _RegistrationTestFree( test
);
15565 ErrQuit( 1, "error: %#m\n", err
);
15568 //===========================================================================================================================
15570 static OSStatus
_RegistrationTestCreate( RegistrationTest
**outTest
)
15573 RegistrationTest
* obj
;
15575 obj
= (RegistrationTest
*) calloc( 1, sizeof( *obj
) );
15576 require_action( obj
, exit
, err
= kNoMemoryErr
);
15578 obj
->subtestReports
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
15579 require_action( obj
->subtestReports
, exit
, err
= kNoMemoryErr
);
15586 if( obj
) _RegistrationTestFree( obj
);
15590 //===========================================================================================================================
15592 static void _RegistrationTestFree( RegistrationTest
*inTest
)
15594 check( !inTest
->timer
);
15595 check( !inTest
->sigSourceINT
);
15596 check( !inTest
->sigSourceTERM
);
15597 check( !inTest
->computerNameSet
);
15598 check( !inTest
->localHostNameSet
);
15599 check( !inTest
->subtest
);
15600 ForgetCF( &inTest
->subtestReports
);
15601 ForgetMem( &inTest
->outputFilePath
);
15602 ForgetCF( &inTest
->computerNamePrev
);
15603 ForgetCF( &inTest
->localHostNamePrev
);
15604 ForgetMem( &inTest
->computerName
);
15605 ForgetMem( &inTest
->localHostName
);
15608 //===========================================================================================================================
15610 static void _RegistrationTestBegin( void *inContext
)
15612 _RegistrationTestProceed( (RegistrationTest
*) inContext
);
15615 //===========================================================================================================================
15617 static void _RegistrationTestProceed( RegistrationTest
*inTest
)
15620 Boolean skippedSubtest
;
15626 if( !inTest
->startTime
)
15628 err
= _RegistrationTestStart( inTest
);
15629 require_noerr_quiet( err
, exit
);
15631 inTest
->startTime
= NanoTimeGetCurrent();
15635 err
= _RegistrationTestEndSubtest( inTest
);
15636 require_noerr( err
, exit
);
15638 ++inTest
->subtestIndex
;
15641 subtestIndex
= inTest
->subtestIndex
;
15642 if( subtestIndex
< (int) countof( kRegistrationSubtestParams
) )
15644 err
= _RegistrationTestStartSubtest( inTest
, &kRegistrationSubtestParams
[ subtestIndex
], &skippedSubtest
);
15645 require_noerr_quiet( err
, exit
);
15649 _RegistrationTestEnd( inTest
);
15652 } while( skippedSubtest
);
15655 if( err
) _RegistrationTestExit( inTest
, err
);
15658 //===========================================================================================================================
15660 static void _RegistrationTestSignalHandler( void *inContext
);
15662 static OSStatus
_RegistrationTestStart( RegistrationTest
*inTest
)
15667 // Save original ComputerName and LocalHostName.
15669 check( !inTest
->computerNamePrev
);
15670 inTest
->computerNamePrev
= SCDynamicStoreCopyComputerName( NULL
, &inTest
->computerNamePrevEncoding
);
15671 err
= map_scerror( inTest
->computerNamePrev
);
15672 require_noerr( err
, exit
);
15674 check( !inTest
->localHostNamePrev
);
15675 inTest
->localHostNamePrev
= SCDynamicStoreCopyLocalHostName( NULL
);
15676 err
= map_scerror( inTest
->localHostNamePrev
);
15677 require_noerr( err
, exit
);
15679 // Generate a unique test ComputerName.
15681 check( !inTest
->computerName
);
15682 ASPrintF( &inTest
->computerName
, "dnssdutil-regtest-computer-name-%s",
15683 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
15684 require_action( inTest
->computerName
, exit
, err
= kNoMemoryErr
);
15686 // Generate a unique test LocalHostName.
15688 check( !inTest
->localHostName
);
15689 ASPrintF( &inTest
->localHostName
, "dnssdutil-regtest-local-hostname-%s",
15690 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
15691 require_action( inTest
->localHostName
, exit
, err
= kNoMemoryErr
);
15693 // Set up SIGINT signal handler.
15695 signal( SIGINT
, SIG_IGN
);
15696 check( !inTest
->sigSourceINT
);
15697 err
= DispatchSignalSourceCreate( SIGINT
, _RegistrationTestSignalHandler
, inTest
, &inTest
->sigSourceINT
);
15698 require_noerr( err
, exit
);
15699 dispatch_resume( inTest
->sigSourceINT
);
15701 // Set up SIGTERM signal handler.
15703 signal( SIGTERM
, SIG_IGN
);
15704 check( !inTest
->sigSourceTERM
);
15705 err
= DispatchSignalSourceCreate( SIGTERM
, _RegistrationTestSignalHandler
, inTest
, &inTest
->sigSourceTERM
);
15706 require_noerr( err
, exit
);
15707 dispatch_resume( inTest
->sigSourceTERM
);
15709 // Set test ComputerName.
15711 check( !inTest
->computerNameSet
);
15712 err
= _SetComputerNameWithUTF8CString( inTest
->computerName
);
15713 require_noerr( err
, exit
);
15714 inTest
->computerNameSet
= true;
15716 // Set test LocalHostName.
15718 check( !inTest
->localHostNameSet
);
15719 err
= _SetLocalHostNameWithUTF8CString( inTest
->localHostName
);
15720 require_noerr( err
, exit
);
15721 inTest
->localHostNameSet
= true;
15724 if( err
) _RegistrationTestStop( inTest
);
15728 static void _RegistrationTestSignalHandler( void *inContext
)
15730 RegistrationTest
* const test
= (RegistrationTest
*) inContext
;
15732 FPrintF( stderr
, "Registration test got a SIGINT or SIGTERM signal, exiting..." );
15734 _RegistrationTestExit( test
, kCanceledErr
);
15737 //===========================================================================================================================
15739 static void _RegistrationTestStop( RegistrationTest
*inTest
)
15743 dispatch_source_forget( &inTest
->timer
);
15744 dispatch_source_forget( &inTest
->sigSourceINT
);
15745 dispatch_source_forget( &inTest
->sigSourceTERM
);
15746 _RegistrationSubtestForget( &inTest
->subtest
);
15747 if( inTest
->computerNameSet
)
15749 err
= _SetComputerName( inTest
->computerNamePrev
, inTest
->computerNamePrevEncoding
);
15750 check_noerr( err
);
15751 if( !err
) inTest
->computerNameSet
= false;
15753 if( inTest
->localHostNameSet
)
15755 err
= _SetLocalHostName( inTest
->localHostNamePrev
);
15756 check_noerr( err
);
15757 if( !err
) inTest
->localHostNameSet
= false;
15761 //===========================================================================================================================
15763 #define kRegistrationTestSubtestDurationSecs 5
15766 _RegistrationTestStartSubtest(
15767 RegistrationTest
* inTest
,
15768 const RegistrationSubtestParams
* inParams
,
15769 Boolean
* outSkipped
)
15772 RegistrationSubtest
* subtest
;
15773 const char * interfaceDesc
;
15774 DNSServiceFlags flags
;
15778 err
= _RegistrationSubtestCreate( &subtest
);
15779 require_noerr( err
, exit
);
15781 subtest
->test
= inTest
;
15782 subtest
->useDefaultName
= inParams
->useDefaultName
;
15783 subtest
->useLODiscovery
= inParams
->useLODiscovery
;
15785 // Determine registration interfaces.
15787 switch( inParams
->interfaceSet
)
15789 case kRegistrationInterfaceSet_All
:
15790 subtest
->ifIndex
= kDNSServiceInterfaceIndexAny
;
15792 if( !subtest
->useLODiscovery
)
15794 err
= _RegistrationTestInterfaceListCreate( false, &subtest
->ifList
);
15795 require_noerr( err
, exit
);
15797 interfaceDesc
= "all interfaces (excluding AWDL)";
15800 case kRegistrationInterfaceSet_AllPlusAWDL
:
15801 subtest
->ifIndex
= kDNSServiceInterfaceIndexAny
;
15802 subtest
->includeAWDL
= true;
15804 if( !subtest
->useLODiscovery
)
15806 err
= _RegistrationTestInterfaceListCreate( true, &subtest
->ifList
);
15807 require_noerr( err
, exit
);
15809 interfaceDesc
= "all interfaces (including AWDL)";
15812 case kRegistrationInterfaceSet_LoopbackOnly
:
15813 subtest
->ifIndex
= if_nametoindex( "lo0" );
15814 if( subtest
->ifIndex
== 0 )
15816 FPrintF( stderr
, "Failed to get index for loopback interface lo0.\n" );
15817 err
= kNoResourcesErr
;
15820 interfaceDesc
= "loopback interface";
15823 case kRegistrationInterfaceSet_AWDLOnly
:
15824 err
= _MDNSInterfaceGetAny( kMDNSInterfaceSubset_AWDL
, NULL
, &subtest
->ifIndex
);
15825 if( err
== kNotFoundErr
)
15827 FPrintF( stderr
, "Warning: No mDNS-capable AWDL interface is available.\n" );
15828 subtest
->skipped
= true;
15831 require_noerr( err
, exit
);
15833 subtest
->ifIsAWDL
= true;
15834 interfaceDesc
= "AWDL interface";
15842 // Create description.
15844 ASPrintF( &subtest
->description
, "Service registration over %s using %s service name.%s",
15845 interfaceDesc
, subtest
->useDefaultName
? "default" : "custom",
15846 subtest
->useLODiscovery
? " (LocalOnly discovery)" : "" );
15847 require_action( subtest
->description
, exit
, err
= kNoMemoryErr
);
15849 if( subtest
->skipped
)
15851 subtest
->startTime
= NanoTimeGetCurrent();
15855 // Generate a service name.
15857 if( subtest
->useDefaultName
)
15859 subtest
->serviceName
= inTest
->computerName
;
15863 ASPrintF( &subtest
->serviceNameCustom
, "dnssdutil-regtest-service-name-%s",
15864 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
15865 require_action( subtest
->serviceNameCustom
, exit
, err
= kNoMemoryErr
);
15867 subtest
->serviceName
= subtest
->serviceNameCustom
;
15870 // Generate a service type.
15872 ASPrintF( &subtest
->serviceType
, "_regtest-%s._udp",
15873 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
15874 require_action( subtest
->serviceType
, exit
, err
= kNoMemoryErr
);
15876 subtest
->serviceTypeLen
= strlen( subtest
->serviceType
);
15878 // Create SRV and TXT record name FQDN.
15880 ASPrintF( &subtest
->serviceFQDN
, "%s.%s.local.", subtest
->serviceName
, subtest
->serviceType
);
15881 require_action( subtest
->serviceFQDN
, exit
, err
= kNoMemoryErr
);
15883 // Generate a port number.
15885 subtest
->port
= (uint16_t) RandomRange( 60000, 65535 );
15887 // Generate TXT record data.
15889 err
= _RegistrationTestCreateRandomTXTRecord( 100, 1000, &subtest
->txtPtr
, &subtest
->txtLen
);
15890 require_noerr( err
, exit
);
15892 // Register service.
15894 subtest
->startTime
= NanoTimeGetCurrent();
15896 flags
= kDNSServiceFlagsNoAutoRename
;
15897 if( subtest
->includeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
15898 err
= DNSServiceRegister( &subtest
->registration
, flags
, subtest
->ifIndex
,
15899 subtest
->useDefaultName
? NULL
: subtest
->serviceNameCustom
, subtest
->serviceType
, "local.",
15900 NULL
, htons( subtest
->port
), (uint16_t) subtest
->txtLen
, subtest
->txtPtr
,
15901 _RegistrationSubtestRegisterCallback
, subtest
);
15902 require_noerr( err
, exit
);
15904 err
= DNSServiceSetDispatchQueue( subtest
->registration
, dispatch_get_main_queue() );
15905 require_noerr( err
, exit
);
15909 check( !inTest
->timer
);
15910 err
= DispatchTimerOneShotCreate( dispatch_time_seconds( kRegistrationTestSubtestDurationSecs
),
15911 INT64_C_safe( kRegistrationTestSubtestDurationSecs
) * kNanosecondsPerSecond
/ 10, dispatch_get_main_queue(),
15912 _RegistrationTestTimerHandler
, inTest
, &inTest
->timer
);
15913 require_noerr( err
, exit
);
15914 dispatch_resume( inTest
->timer
);
15917 *outSkipped
= subtest
->skipped
;
15919 check( !inTest
->subtest
);
15920 inTest
->subtest
= subtest
;
15924 _RegistrationSubtestForget( &subtest
);
15928 //===========================================================================================================================
15930 #define kRegistrationTestReportKey_ComputerName CFSTR( "computerName" ) // String
15931 #define kRegistrationTestReportKey_Description CFSTR( "description" ) // String
15932 #define kRegistrationTestReportKey_Domain CFSTR( "domain" ) // String
15933 #define kRegistrationTestReportKey_EndTime CFSTR( "endTime" ) // String
15934 #define kRegistrationTestReportKey_Error CFSTR( "error" ) // Integer
15935 #define kRegistrationTestReportKey_Flags CFSTR( "flags" ) // Integer
15936 #define kRegistrationTestReportKey_IgnoredResults CFSTR( "ignoredResults" ) // Array of dictionaries
15937 #define kRegistrationTestReportKey_InterfaceIndex CFSTR( "ifIndex" ) // Integer
15938 #define kRegistrationTestReportKey_InterfaceName CFSTR( "ifName" ) // String
15939 #define kRegistrationTestReportKey_LocalHostName CFSTR( "localHostName" ) // String
15940 #define kRegistrationTestReportKey_MissingResults CFSTR( "missingResults" ) // Array of dictionaries
15941 #define kRegistrationTestReportKey_Pass CFSTR( "pass" ) // Boolean
15942 #define kRegistrationTestReportKey_Port CFSTR( "port" ) // Integer
15943 #define kRegistrationTestReportKey_RDataFormatted CFSTR( "rdataFormatted" ) // String
15944 #define kRegistrationTestReportKey_RDataHexString CFSTR( "rdataHexString" ) // String
15945 #define kRegistrationTestReportKey_RecordClass CFSTR( "recordClass" ) // Integer
15946 #define kRegistrationTestReportKey_RecordType CFSTR( "recordType" ) // Integer
15947 #define kRegistrationTestReportKey_Registered CFSTR( "registered" ) // Boolean
15948 #define kRegistrationTestReportKey_ResultType CFSTR( "resultType" ) // String
15949 #define kRegistrationTestReportKey_ServiceFQDN CFSTR( "serviceFQDN" ) // String
15950 #define kRegistrationTestReportKey_ServiceName CFSTR( "serviceName" ) // String
15951 #define kRegistrationTestReportKey_ServiceType CFSTR( "serviceType" ) // String
15952 #define kRegistrationTestReportKey_Skipped CFSTR( "skipped" ) // Boolean
15953 #define kRegistrationTestReportKey_StartTime CFSTR( "startTime" ) // String
15954 #define kRegistrationTestReportKey_Subtests CFSTR( "subtests" ) // Array of dictionaries
15955 #define kRegistrationTestReportKey_Timestamp CFSTR( "timestamp" ) // String
15956 #define kRegistrationTestReportKey_TXT CFSTR( "txt" ) // String
15957 #define kRegistrationTestReportKey_UnexpectedResults CFSTR( "unexpectedResults" ) // Array of dictionaries
15958 #define kRegistrationTestReportKey_UsedDefaultName CFSTR( "usedDefaultName" ) // Boolean
15959 #define kRegistrationTestReportKey_UsedLODiscovery CFSTR( "usedLODiscovery" ) // Boolean
15961 #define kRegistrationTestResultType_Browse CFSTR( "browse" )
15962 #define kRegistrationTestResultType_Query CFSTR( "query" )
15963 #define kRegistrationTestResultType_QuerySRV CFSTR( "querySRV" )
15964 #define kRegistrationTestResultType_QueryTXT CFSTR( "queryTXT" )
15965 #define kRegistrationTestResultType_Registration CFSTR( "registration" )
15968 _RegistrationTestAppendMissingResults(
15969 CFMutableArrayRef inMissingResults
,
15970 const RegistrationResultTimes
* inTimes
,
15971 uint32_t inIfIndex
,
15972 const char * inIfName
);
15974 static OSStatus
_RegistrationTestEndSubtest( RegistrationTest
*inTest
)
15977 RegistrationSubtest
* subtest
;
15978 CFMutableDictionaryRef subtestReport
;
15979 CFMutableArrayRef missing
;
15982 Boolean subtestFailed
;
15983 char startTime
[ 32 ];
15984 char endTime
[ 32 ];
15985 char ifNameBuf
[ IF_NAMESIZE
+ 1 ];
15987 now
= NanoTimeGetCurrent();
15989 subtest
= inTest
->subtest
;
15990 inTest
->subtest
= NULL
;
15991 _RegistrationSubtestStop( subtest
);
15994 subtestReport
= NULL
;
15996 if( subtest
->txtPtr
)
15998 err
= DNSRecordDataToString( subtest
->txtPtr
, subtest
->txtLen
, kDNSServiceType_TXT
, NULL
, 0, &txtStr
);
15999 require_noerr( err
, exit
);
16001 _NanoTime64ToTimestamp( subtest
->startTime
, startTime
, sizeof( startTime
) );
16002 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
16003 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &subtestReport
,
16005 "%kO=%s" // description
16006 "%kO=%s" // startTime
16007 "%kO=%s" // endTime
16008 "%kO=%s" // serviceFQDN
16009 "%kO=%lli" // ifIndex
16013 "%kO=%b" // registered
16014 "%kO=%b" // usedDefaultName
16015 "%kO=%b" // usedLODiscovery
16017 kRegistrationTestReportKey_Description
, subtest
->description
,
16018 kRegistrationTestReportKey_StartTime
, startTime
,
16019 kRegistrationTestReportKey_EndTime
, endTime
,
16020 kRegistrationTestReportKey_ServiceFQDN
, subtest
->serviceFQDN
,
16021 kRegistrationTestReportKey_InterfaceIndex
, (int64_t) subtest
->ifIndex
,
16022 kRegistrationTestReportKey_InterfaceName
, if_indextoname( subtest
->ifIndex
, ifNameBuf
),
16023 kRegistrationTestReportKey_Port
, (int64_t) subtest
->port
,
16024 kRegistrationTestReportKey_TXT
, txtStr
,
16025 kRegistrationTestReportKey_Registered
, (int) subtest
->registered
,
16026 kRegistrationTestReportKey_UsedDefaultName
, (int) subtest
->useDefaultName
,
16027 kRegistrationTestReportKey_UsedLODiscovery
, (int) subtest
->useLODiscovery
);
16028 ForgetMem( &txtStr
);
16029 require_noerr( err
, exit
);
16031 if( !subtest
->skipped
&& subtest
->registered
)
16033 missing
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
16034 require_action( missing
, exit
, err
= kNoMemoryErr
);
16036 if( subtest
->ifList
)
16038 RegistrationInterfaceItem
* item
;
16040 for( item
= subtest
->ifList
; item
; item
= (RegistrationInterfaceItem
*) item
->base
.next
)
16042 #if( TARGET_OS_WATCH )
16043 if( inTest
->forBATS
&& item
->base
.isWiFi
) continue;
16045 err
= _RegistrationTestAppendMissingResults( missing
, &item
->times
, item
->base
.ifIndex
, item
->base
.ifName
);
16046 require_noerr( err
, exit
);
16051 err
= _RegistrationTestAppendMissingResults( missing
, &subtest
->ifTimes
, subtest
->ifIndex
, NULL
);
16052 require_noerr( err
, exit
);
16055 subtestFailed
= false;
16056 if( CFArrayGetCount( missing
) > 0 )
16058 subtestFailed
= true;
16059 CFDictionarySetValue( subtestReport
, kRegistrationTestReportKey_MissingResults
, missing
);
16061 if( CFArrayGetCount( subtest
->unexpected
) > 0 )
16063 subtestFailed
= true;
16064 CFDictionarySetValue( subtestReport
, kRegistrationTestReportKey_UnexpectedResults
, subtest
->unexpected
);
16066 #if( TARGET_OS_WATCH )
16067 if( CFArrayGetCount( subtest
->ignored
) > 0 )
16069 CFDictionarySetValue( subtestReport
, kRegistrationTestReportKey_IgnoredResults
, subtest
->ignored
);
16075 subtestFailed
= true;
16078 CFDictionarySetBoolean( subtestReport
, kRegistrationTestReportKey_Pass
, subtestFailed
? false : true );
16079 if( subtestFailed
)
16081 CFDictionarySetBoolean( subtestReport
, kRegistrationTestReportKey_Skipped
, subtest
->skipped
);
16082 if( !subtest
->skipped
) inTest
->failed
= true;
16084 CFArrayAppendValue( inTest
->subtestReports
, subtestReport
);
16087 CFReleaseNullSafe( missing
);
16088 CFReleaseNullSafe( subtestReport
);
16089 _RegistrationSubtestFree( subtest
);
16094 _RegistrationTestAppendMissingResult(
16095 CFMutableArrayRef inMissingResults
,
16096 CFStringRef inType
,
16097 uint32_t inIfIndex
,
16098 const char * inIfName
);
16101 _RegistrationTestAppendMissingResults(
16102 CFMutableArrayRef inMissingResults
,
16103 const RegistrationResultTimes
* inTimes
,
16104 uint32_t inIfIndex
,
16105 const char * inIfName
)
16109 if( !inTimes
->browseResultTime
)
16111 err
= _RegistrationTestAppendMissingResult( inMissingResults
, kRegistrationTestResultType_Browse
,
16112 inIfIndex
, inIfName
);
16113 require_noerr( err
, exit
);
16115 if( !inTimes
->querySRVResultTime
)
16117 err
= _RegistrationTestAppendMissingResult( inMissingResults
, kRegistrationTestResultType_QuerySRV
,
16118 inIfIndex
, inIfName
);
16119 require_noerr( err
, exit
);
16121 if( !inTimes
->queryTXTResultTime
)
16123 err
= _RegistrationTestAppendMissingResult( inMissingResults
, kRegistrationTestResultType_QueryTXT
,
16124 inIfIndex
, inIfName
);
16125 require_noerr( err
, exit
);
16134 _RegistrationTestAppendMissingResult(
16135 CFMutableArrayRef inMissingResults
,
16136 CFStringRef inType
,
16137 uint32_t inIfIndex
,
16138 const char * inIfName
)
16141 char ifName
[ IF_NAMESIZE
+ 1 ];
16143 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, inMissingResults
,
16145 "%kO=%O" // resultType
16146 "%kO=%lli" // ifIndex
16149 kRegistrationTestReportKey_ResultType
, inType
,
16150 kRegistrationTestReportKey_InterfaceIndex
, (int64_t) inIfIndex
,
16151 kRegistrationTestReportKey_InterfaceName
, inIfName
? inIfName
: if_indextoname( inIfIndex
, ifName
) );
16155 //===========================================================================================================================
16157 static void _RegistrationTestEnd( RegistrationTest
*inTest
)
16161 CFPropertyListRef plist
;
16162 char startTime
[ 32 ];
16163 char endTime
[ 32 ];
16165 now
= NanoTimeGetCurrent();
16166 _NanoTime64ToTimestamp( inTest
->startTime
, startTime
, sizeof( startTime
) );
16167 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
16169 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
16171 "%kO=%s" // startTime
16172 "%kO=%s" // endTime
16173 "%kO=%s" // computerName
16174 "%kO=%s" // localHostName
16175 "%kO=%O" // subtests
16178 kRegistrationTestReportKey_StartTime
, startTime
,
16179 kRegistrationTestReportKey_EndTime
, endTime
,
16180 kRegistrationTestReportKey_ComputerName
, inTest
->computerName
,
16181 kRegistrationTestReportKey_LocalHostName
, inTest
->localHostName
,
16182 kRegistrationTestReportKey_Subtests
, inTest
->subtestReports
,
16183 kRegistrationTestReportKey_Pass
, inTest
->failed
? false : true );
16184 require_noerr( err
, exit
);
16186 err
= OutputPropertyList( plist
, inTest
->outputFormat
, inTest
->outputFilePath
);
16187 CFRelease( plist
);
16188 require_noerr( err
, exit
);
16191 _RegistrationTestExit( inTest
, err
);
16194 //===========================================================================================================================
16196 static void _RegistrationTestExit( RegistrationTest
*inTest
, OSStatus inError
)
16202 FPrintF( stderr
, "error: %#m\n", inError
);
16207 exitCode
= inTest
->failed
? 2 : 0;
16209 _RegistrationTestForget( &inTest
);
16213 //===========================================================================================================================
16215 static OSStatus
_RegistrationSubtestCreate( RegistrationSubtest
**outSubtest
)
16218 RegistrationSubtest
* obj
;
16220 obj
= (RegistrationSubtest
*) calloc( 1, sizeof( *obj
) );
16221 require_action( obj
, exit
, err
= kNoMemoryErr
);
16223 obj
->unexpected
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
16224 require_action( obj
->unexpected
, exit
, err
= kNoMemoryErr
);
16226 #if( TARGET_OS_WATCH )
16227 obj
->ignored
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
16228 require_action( obj
->ignored
, exit
, err
= kNoMemoryErr
);
16236 if( obj
) _RegistrationSubtestFree( obj
);
16240 //===========================================================================================================================
16242 static void _RegistrationSubtestFree( RegistrationSubtest
*inSubtest
)
16244 check( !inSubtest
->registration
);
16245 check( !inSubtest
->browse
);
16246 check( !inSubtest
->querySRV
);
16247 check( !inSubtest
->queryTXT
);
16248 check( !inSubtest
->connection
);
16249 ForgetMem( &inSubtest
->serviceNameCustom
);
16250 ForgetMem( &inSubtest
->serviceType
);
16251 ForgetMem( &inSubtest
->serviceFQDN
);
16252 ForgetMem( &inSubtest
->txtPtr
);
16253 ForgetCF( &inSubtest
->unexpected
);
16254 #if( TARGET_OS_WATCH )
16255 ForgetCF( &inSubtest
->ignored
);
16257 _MDNSInterfaceListForget( (MDNSInterfaceItem
**) &inSubtest
->ifList
);
16258 ForgetMem( &inSubtest
->description
);
16262 //===========================================================================================================================
16264 static void _RegistrationSubtestStop( RegistrationSubtest
*inSubtest
)
16266 DNSServiceForget( &inSubtest
->registration
);
16267 DNSServiceForget( &inSubtest
->browse
);
16268 DNSServiceForget( &inSubtest
->querySRV
);
16269 DNSServiceForget( &inSubtest
->queryTXT
);
16270 DNSServiceForget( &inSubtest
->connection
);
16273 //===========================================================================================================================
16275 static OSStatus
_RegistrationTestInterfaceListCreate( Boolean inIncludeAWDL
, RegistrationInterfaceItem
**outList
)
16278 RegistrationInterfaceItem
* list
;
16279 const MDNSInterfaceSubset subset
= inIncludeAWDL
? kMDNSInterfaceSubset_All
: kMDNSInterfaceSubset_NonAWDL
;
16281 err
= _MDNSInterfaceListCreate( subset
, sizeof( *list
), (MDNSInterfaceItem
**) &list
);
16282 require_noerr( err
, exit
);
16290 //===========================================================================================================================
16293 _RegistrationTestCreateRandomTXTRecord(
16296 uint8_t ** outTXTPtr
,
16297 size_t * outTXTLen
)
16301 const uint8_t * txtEnd
;
16302 uint8_t * txtPtr
= NULL
;
16305 require_action_quiet( inMinLen
<= inMaxLen
, exit
, err
= kSizeErr
);
16307 txtLen
= RandomRange( inMinLen
, inMaxLen
);
16308 txtPtr
= (uint8_t *) malloc( txtLen
+ 1 );
16309 require_action( txtPtr
, exit
, err
= kNoMemoryErr
);
16311 _RandomStringExact( kAlphaNumericCharSet
, sizeof_string( kAlphaNumericCharSet
), txtLen
, (char *)txtPtr
);
16314 txtEnd
= &txtPtr
[ txtLen
];
16315 while( ptr
< txtEnd
)
16317 size_t maxLen
, len
;
16319 maxLen
= ( (size_t)( txtEnd
- ptr
) ) - 1;
16320 len
= RandomRange( 1, 255 );
16321 if( len
> maxLen
) len
= maxLen
;
16323 *ptr
= (uint8_t) len
;
16324 ptr
+= ( 1 + len
);
16326 check( ptr
== txtEnd
);
16330 *outTXTPtr
= txtPtr
;
16333 if( outTXTLen
) *outTXTLen
= txtLen
;
16337 FreeNullSafe( txtPtr
);
16341 //===========================================================================================================================
16343 static void DNSSD_API
16344 _RegistrationSubtestRegisterCallback(
16345 DNSServiceRef inSDRef
,
16346 DNSServiceFlags inFlags
,
16347 DNSServiceErrorType inError
,
16348 const char * inServiceName
,
16349 const char * inServiceType
,
16350 const char * inDomain
,
16354 const NanoTime64 now
= NanoTimeGetCurrent();
16355 RegistrationSubtest
* const subtest
= (RegistrationSubtest
*) inContext
;
16359 if( ( inFlags
& kDNSServiceFlagsAdd
) && !inError
&&
16360 ( strcasecmp( inServiceName
, subtest
->serviceName
) == 0 ) &&
16361 _RegistrationSubtestValidServiceType( subtest
, inServiceType
) &&
16362 ( strcasecmp( inDomain
, "local." ) == 0 ) )
16364 if( !subtest
->registered
)
16366 DNSServiceRef sdRef
;
16367 const DNSServiceFlags flags
= kDNSServiceFlagsShareConnection
| kDNSServiceFlagsIncludeAWDL
;
16369 subtest
->registered
= true;
16371 // Create shared connection.
16373 check( !subtest
->connection
);
16374 err
= DNSServiceCreateConnection( &subtest
->connection
);
16375 require_noerr( err
, exit
);
16377 err
= DNSServiceSetDispatchQueue( subtest
->connection
, dispatch_get_main_queue() );
16378 require_noerr( err
, exit
);
16382 check( !subtest
->browse
);
16383 sdRef
= subtest
->connection
;
16384 err
= DNSServiceBrowse( &sdRef
, flags
,
16385 subtest
->useLODiscovery
? kDNSServiceInterfaceIndexLocalOnly
: kDNSServiceInterfaceIndexAny
,
16386 subtest
->serviceType
, "local.", _RegistrationSubtestBrowseCallback
, subtest
);
16387 require_noerr( err
, exit
);
16389 subtest
->browse
= sdRef
;
16394 char timestamp
[ 32 ];
16396 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, subtest
->unexpected
,
16398 "%kO=%O" // resultType
16399 "%kO=%s" // timestamp
16400 "%kO=%lli" // flags
16401 "%kO=%lli" // error
16402 "%kO=%s" // serviceName
16403 "%kO=%s" // serviceType
16406 kRegistrationTestReportKey_ResultType
, kRegistrationTestResultType_Registration
,
16407 kRegistrationTestReportKey_Timestamp
, _NanoTime64ToTimestamp( now
, timestamp
, sizeof( timestamp
) ),
16408 kRegistrationTestReportKey_Flags
, (int64_t) inFlags
,
16409 kRegistrationTestReportKey_Error
, (int64_t) inError
,
16410 kRegistrationTestReportKey_ServiceName
, inServiceName
,
16411 kRegistrationTestReportKey_ServiceType
, inServiceType
,
16412 kRegistrationTestReportKey_Domain
, inDomain
);
16413 require_noerr( err
, exit
);
16418 if( err
) _RegistrationTestExit( subtest
->test
, err
);
16421 //===========================================================================================================================
16423 static void DNSSD_API
16424 _RegistrationSubtestBrowseCallback(
16425 DNSServiceRef inSDRef
,
16426 DNSServiceFlags inFlags
,
16427 uint32_t inIfIndex
,
16428 DNSServiceErrorType inError
,
16429 const char * inServiceName
,
16430 const char * inServiceType
,
16431 const char * inDomain
,
16436 RegistrationSubtest
* const subtest
= (RegistrationSubtest
*) inContext
;
16437 Boolean serviceIsCorrect
, resultIsExpected
;
16441 now
= NanoTimeGetCurrent();
16442 if( !inError
&& ( strcasecmp( inServiceName
, subtest
->serviceName
) == 0 ) &&
16443 _RegistrationSubtestValidServiceType( subtest
, inServiceType
) && ( strcasecmp( inDomain
, "local." ) == 0 ) )
16445 serviceIsCorrect
= true;
16449 serviceIsCorrect
= false;
16452 resultIsExpected
= false;
16453 if( serviceIsCorrect
&& ( inFlags
& kDNSServiceFlagsAdd
) )
16455 RegistrationResultTimes
* times
;
16457 times
= _RegistrationSubtestGetInterfaceResultTimes( subtest
, inIfIndex
, NULL
);
16460 DNSServiceRef sdRef
;
16461 const DNSServiceFlags flags
= kDNSServiceFlagsShareConnection
| kDNSServiceFlagsIncludeAWDL
;
16464 resultIsExpected
= true;
16465 if( !times
->browseResultTime
) times
->browseResultTime
= now
;
16467 ifIndex
= subtest
->useLODiscovery
? kDNSServiceInterfaceIndexLocalOnly
: kDNSServiceInterfaceIndexAny
;
16468 if( !subtest
->querySRV
)
16470 // Start SRV record query.
16472 sdRef
= subtest
->connection
;
16473 err
= DNSServiceQueryRecord( &sdRef
, flags
, ifIndex
, subtest
->serviceFQDN
, kDNSServiceType_SRV
,
16474 kDNSServiceClass_IN
, _RegistrationSubtestQueryCallback
, subtest
);
16475 require_noerr( err
, exit
);
16477 subtest
->querySRV
= sdRef
;
16479 if( !subtest
->queryTXT
)
16481 // Start TXT record query.
16483 sdRef
= subtest
->connection
;
16484 err
= DNSServiceQueryRecord( &sdRef
, flags
, ifIndex
, subtest
->serviceFQDN
, kDNSServiceType_TXT
,
16485 kDNSServiceClass_IN
, _RegistrationSubtestQueryCallback
, subtest
);
16486 require_noerr( err
, exit
);
16488 subtest
->queryTXT
= sdRef
;
16493 if( !resultIsExpected
)
16495 CFMutableArrayRef resultArray
;
16496 char timestamp
[ 32 ];
16497 const char * ifNamePtr
;
16498 char ifNameBuf
[ IF_NAMESIZE
+ 1 ];
16500 ifNamePtr
= if_indextoname( inIfIndex
, ifNameBuf
);
16501 resultArray
= subtest
->unexpected
;
16502 #if( TARGET_OS_WATCH )
16503 if( subtest
->test
->forBATS
&& ( subtest
->ifIndex
== kDNSServiceInterfaceIndexAny
) &&
16504 ifNamePtr
&& _RegistrationTestInterfaceIsWiFi( ifNamePtr
) && serviceIsCorrect
)
16506 resultArray
= subtest
->ignored
;
16509 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, resultArray
,
16511 "%kO=%O" // resultType
16512 "%kO=%s" // timestamp
16513 "%kO=%lli" // flags
16514 "%kO=%lli" // ifIndex
16516 "%kO=%lli" // error
16517 "%kO=%s" // serviceName
16518 "%kO=%s" // serviceType
16521 kRegistrationTestReportKey_ResultType
, kRegistrationTestResultType_Browse
,
16522 kRegistrationTestReportKey_Timestamp
, _NanoTime64ToTimestamp( now
, timestamp
, sizeof( timestamp
) ),
16523 kRegistrationTestReportKey_Flags
, (int64_t) inFlags
,
16524 kRegistrationTestReportKey_InterfaceIndex
, (int64_t) inIfIndex
,
16525 kRegistrationTestReportKey_InterfaceName
, ifNamePtr
,
16526 kRegistrationTestReportKey_Error
, (int64_t) inError
,
16527 kRegistrationTestReportKey_ServiceName
, inServiceName
,
16528 kRegistrationTestReportKey_ServiceType
, inServiceType
,
16529 kRegistrationTestReportKey_Domain
, inDomain
);
16530 require_noerr( err
, exit
);
16535 if( err
) _RegistrationTestExit( subtest
->test
, err
);
16538 //===========================================================================================================================
16541 _RegistrationSubtestIsSRVRecordDataValid(
16542 RegistrationSubtest
* inSubtest
,
16543 const uint8_t * inRDataPtr
,
16545 Boolean inExpectRandHostname
);
16547 static void DNSSD_API
16548 _RegistrationSubtestQueryCallback(
16549 DNSServiceRef inSDRef
,
16550 DNSServiceFlags inFlags
,
16551 uint32_t inIfIndex
,
16552 DNSServiceErrorType inError
,
16553 const char * inName
,
16556 uint16_t inRDataLen
,
16557 const void * inRDataPtr
,
16563 RegistrationSubtest
* const subtest
= (RegistrationSubtest
*) inContext
;
16564 Boolean resultIsExpected
;
16569 now
= NanoTimeGetCurrent();
16570 resultIsExpected
= false;
16571 if( ( inFlags
& kDNSServiceFlagsAdd
) && !inError
&& ( strcasecmp( inName
, subtest
->serviceFQDN
) == 0 ) &&
16572 ( inClass
== kDNSServiceClass_IN
) )
16574 RegistrationResultTimes
* times
;
16577 times
= _RegistrationSubtestGetInterfaceResultTimes( subtest
, inIfIndex
, &isAWDL
);
16580 if( inType
== kDNSServiceType_SRV
)
16582 Boolean expectRandHostname
;
16584 if( isAWDL
|| ( ( subtest
->ifIndex
== kDNSServiceInterfaceIndexAny
) && subtest
->includeAWDL
) )
16586 expectRandHostname
= true;
16590 expectRandHostname
= false;
16592 if( _RegistrationSubtestIsSRVRecordDataValid( subtest
, inRDataPtr
, inRDataLen
, expectRandHostname
) )
16594 resultIsExpected
= true;
16595 if( !times
->querySRVResultTime
) times
->querySRVResultTime
= now
;
16598 else if( inType
== kDNSServiceType_TXT
)
16600 if( MemEqual( inRDataPtr
, inRDataLen
, subtest
->txtPtr
, subtest
->txtLen
) )
16602 resultIsExpected
= true;
16603 if( !times
->queryTXTResultTime
) times
->queryTXTResultTime
= now
;
16609 if( !resultIsExpected
)
16611 CFMutableArrayRef resultArray
;
16612 CFMutableDictionaryRef resultDict
;
16613 CFStringRef rdataKey
;
16615 const char * ifNamePtr
;
16616 char timestamp
[ 32 ];
16617 char ifNameBuf
[ IF_NAMESIZE
+ 1 ];
16619 ifNamePtr
= if_indextoname( inIfIndex
, ifNameBuf
);
16620 resultArray
= subtest
->unexpected
;
16621 #if( TARGET_OS_WATCH )
16622 if( subtest
->test
->forBATS
&& ( subtest
->ifIndex
== kDNSServiceInterfaceIndexAny
) &&
16623 ifNamePtr
&& _RegistrationTestInterfaceIsWiFi( ifNamePtr
) && !inError
&&
16624 ( strcasecmp( inName
, subtest
->serviceFQDN
) == 0 ) )
16626 if( inType
== kDNSServiceType_SRV
)
16628 const Boolean expectRandHostname
= subtest
->includeAWDL
? true : false;
16630 if( _RegistrationSubtestIsSRVRecordDataValid( subtest
, inRDataPtr
, inRDataLen
, expectRandHostname
) )
16632 resultArray
= subtest
->ignored
;
16635 else if( inType
== kDNSServiceType_TXT
)
16637 if( MemEqual( inRDataPtr
, inRDataLen
, subtest
->txtPtr
, subtest
->txtLen
) )
16639 resultArray
= subtest
->ignored
;
16644 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &resultDict
,
16646 "%kO=%O" // resultType
16647 "%kO=%s" // timestamp
16648 "%kO=%lli" // flags
16649 "%kO=%lli" // ifIndex
16651 "%kO=%lli" // error
16652 "%kO=%s" // serviceFQDN
16653 "%kO=%lli" // recordType
16654 "%kO=%lli" // class
16656 kRegistrationTestReportKey_ResultType
, kRegistrationTestResultType_Query
,
16657 kRegistrationTestReportKey_Timestamp
, _NanoTime64ToTimestamp( now
, timestamp
, sizeof( timestamp
) ),
16658 kRegistrationTestReportKey_Flags
, (int64_t) inFlags
,
16659 kRegistrationTestReportKey_InterfaceIndex
, (int64_t) inIfIndex
,
16660 kRegistrationTestReportKey_InterfaceName
, ifNamePtr
,
16661 kRegistrationTestReportKey_Error
, (int64_t) inError
,
16662 kRegistrationTestReportKey_ServiceFQDN
, inName
,
16663 kRegistrationTestReportKey_RecordType
, (int64_t) inType
,
16664 kRegistrationTestReportKey_RecordClass
, (int64_t) inClass
);
16665 require_noerr( err
, exit
);
16668 DNSRecordDataToString( inRDataPtr
, inRDataLen
, inType
, NULL
, 0, &rdataStr
);
16671 rdataKey
= kRegistrationTestReportKey_RDataFormatted
;
16675 ASPrintF( &rdataStr
, "%#H", inRDataPtr
, inRDataLen
, inRDataLen
);
16676 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
16678 rdataKey
= kRegistrationTestReportKey_RDataHexString
;
16680 err
= CFDictionarySetCString( resultDict
, rdataKey
, rdataStr
, kSizeCString
);
16681 ForgetMem( &rdataStr
);
16682 if( err
) CFRelease( resultDict
);
16683 require_noerr( err
, exit
);
16685 CFArrayAppendValue( resultArray
, resultDict
);
16686 CFRelease( resultDict
);
16691 if( err
) _RegistrationTestExit( subtest
->test
, err
);
16695 _RegistrationSubtestIsSRVRecordDataValid(
16696 RegistrationSubtest
* inSubtest
,
16697 const uint8_t * inRDataPtr
,
16699 Boolean inExpectRandHostname
)
16701 const dns_fixed_fields_srv
* fields
;
16702 const uint8_t * const end
= &inRDataPtr
[ inRDataLen
];
16703 const uint8_t * label
;
16709 require_quiet( inRDataLen
>= sizeof( dns_fixed_fields_srv
), exit
);
16711 fields
= (const dns_fixed_fields_srv
*) inRDataPtr
;
16712 port
= dns_fixed_fields_srv_get_port( fields
);
16713 require_quiet( port
== inSubtest
->port
, exit
);
16715 // First target label should be a UUID string for the AWDL interface.
16717 label
= (const uint8_t *) &fields
[ 1 ];
16718 require_quiet( ( end
- label
) >= 1, exit
);
16721 require_quiet( ( (size_t)( end
- label
) ) >= ( 1 + len
), exit
);
16723 if( inExpectRandHostname
)
16725 if( StringToUUID( (const char *) &label
[ 1 ], len
, false, NULL
) != kNoErr
) goto exit
;
16729 if( strnicmpx( &label
[ 1 ], len
, inSubtest
->test
->localHostName
) != 0 ) goto exit
;
16732 // Second target label should be "local".
16734 label
= &label
[ 1 + len
];
16735 require_quiet( ( end
- label
) >= 1, exit
);
16738 require_quiet( ( (size_t)( end
- label
) ) >= ( 1 + len
), exit
);
16740 if( ( len
!= kLocalLabel
[ 0 ] ) || ( _memicmp( &label
[ 1 ], &kLocalLabel
[ 1 ], kLocalLabel
[ 0 ] ) != 0 ) ) goto exit
;
16742 // Third target label should be the root label.
16744 label
= &label
[ 1 + len
];
16745 require_quiet( ( end
- label
) >= 1, exit
);
16748 if( len
!= 0 ) goto exit
;
16756 //===========================================================================================================================
16758 static Boolean
_RegistrationSubtestValidServiceType( const RegistrationSubtest
*inSubtest
, const char *inServiceType
)
16760 if( stricmp_prefix( inServiceType
, inSubtest
->serviceType
) == 0 )
16762 const char * const ptr
= &inServiceType
[ inSubtest
->serviceTypeLen
];
16764 if( ( ptr
[ 0 ] == '\0' ) || ( ( ptr
[ 0 ] == '.' ) && ( ptr
[ 1 ] == '\0' ) ) ) return( true );
16769 //===========================================================================================================================
16771 static RegistrationResultTimes
*
16772 _RegistrationSubtestGetInterfaceResultTimes(
16773 RegistrationSubtest
* inSubtest
,
16774 uint32_t inIfIndex
,
16775 Boolean
* outIsAWDL
)
16777 if( inSubtest
->ifList
)
16779 RegistrationInterfaceItem
* item
;
16781 for( item
= inSubtest
->ifList
; item
; item
= (RegistrationInterfaceItem
*) item
->base
.next
)
16783 if( inIfIndex
== item
->base
.ifIndex
)
16785 if( outIsAWDL
) *outIsAWDL
= item
->base
.isAWDL
? true : false;
16786 return( &item
->times
);
16792 if( inIfIndex
== inSubtest
->ifIndex
)
16794 if( outIsAWDL
) *outIsAWDL
= inSubtest
->ifIsAWDL
? true : false;
16795 return( &inSubtest
->ifTimes
);
16801 //===========================================================================================================================
16803 static void _RegistrationTestTimerHandler( void *inContext
)
16805 RegistrationTest
* const test
= (RegistrationTest
*) inContext
;
16807 dispatch_source_forget( &test
->timer
);
16808 _RegistrationTestProceed( test
);
16811 //===========================================================================================================================
16813 #if( TARGET_OS_WATCH )
16814 static Boolean
_RegistrationTestInterfaceIsWiFi( const char *inIfName
)
16816 NetTransportType type
= kNetTransportType_Undefined
;
16818 SocketGetInterfaceInfo( kInvalidSocketRef
, inIfName
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &type
);
16819 return( ( type
== kNetTransportType_WiFi
) ? true : false );
16823 //===========================================================================================================================
16825 //===========================================================================================================================
16827 #define kSSDPPort 1900
16831 HTTPHeader header
; // HTTP header object for sending and receiving.
16832 dispatch_source_t readSourceV4
; // Read dispatch source for IPv4 socket.
16833 dispatch_source_t readSourceV6
; // Read dispatch source for IPv6 socket.
16834 int receiveSecs
; // After send, the amount of time to spend receiving.
16835 uint32_t ifindex
; // Index of the interface over which to send the query.
16836 Boolean useIPv4
; // True if the query should be sent via IPv4 multicast.
16837 Boolean useIPv6
; // True if the query should be sent via IPv6 multicast.
16839 } SSDPDiscoverContext
;
16841 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext
*inContext
);
16842 static void SSDPDiscoverReadHandler( void *inContext
);
16843 static int SocketToPortNumber( SocketRef inSock
);
16844 static OSStatus
WriteSSDPSearchRequest( HTTPHeader
*inHeader
, const void *inHostSA
, int inMX
, const char *inST
);
16846 static void SSDPDiscoverCmd( void )
16849 struct timeval now
;
16850 SSDPDiscoverContext
* context
;
16851 dispatch_source_t signalSource
= NULL
;
16852 SocketRef sockV4
= kInvalidSocketRef
;
16853 SocketRef sockV6
= kInvalidSocketRef
;
16857 // Set up SIGINT handler.
16859 signal( SIGINT
, SIG_IGN
);
16860 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
16861 require_noerr( err
, exit
);
16862 dispatch_resume( signalSource
);
16864 // Check command parameters.
16866 if( gSSDPDiscover_ReceiveSecs
< -1 )
16868 FPrintF( stdout
, "Invalid receive time: %d seconds.\n", gSSDPDiscover_ReceiveSecs
);
16875 context
= (SSDPDiscoverContext
*) calloc( 1, sizeof( *context
) );
16876 require_action( context
, exit
, err
= kNoMemoryErr
);
16878 context
->receiveSecs
= gSSDPDiscover_ReceiveSecs
;
16879 context
->useIPv4
= ( gSSDPDiscover_UseIPv4
|| !gSSDPDiscover_UseIPv6
) ? true : false;
16880 context
->useIPv6
= ( gSSDPDiscover_UseIPv6
|| !gSSDPDiscover_UseIPv4
) ? true : false;
16882 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifindex
);
16883 require_noerr_quiet( err
, exit
);
16885 // Set up IPv4 socket.
16887 if( context
->useIPv4
)
16890 err
= UDPClientSocketOpen( AF_INET
, NULL
, 0, -1, &port
, &sockV4
);
16891 require_noerr( err
, exit
);
16893 err
= SocketSetMulticastInterface( sockV4
, NULL
, context
->ifindex
);
16894 require_noerr( err
, exit
);
16896 err
= setsockopt( sockV4
, IPPROTO_IP
, IP_MULTICAST_LOOP
, (char *) &(uint8_t){ 1 }, (socklen_t
) sizeof( uint8_t ) );
16897 err
= map_socket_noerr_errno( sockV4
, err
);
16898 require_noerr( err
, exit
);
16901 // Set up IPv6 socket.
16903 if( context
->useIPv6
)
16905 err
= UDPClientSocketOpen( AF_INET6
, NULL
, 0, -1, NULL
, &sockV6
);
16906 require_noerr( err
, exit
);
16908 err
= SocketSetMulticastInterface( sockV6
, NULL
, context
->ifindex
);
16909 require_noerr( err
, exit
);
16911 err
= setsockopt( sockV6
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, (char *) &(int){ 1 }, (socklen_t
) sizeof( int ) );
16912 err
= map_socket_noerr_errno( sockV6
, err
);
16913 require_noerr( err
, exit
);
16918 SSDPDiscoverPrintPrologue( context
);
16920 // Send mDNS query message.
16923 if( IsValidSocket( sockV4
) )
16925 struct sockaddr_in mcastAddr4
;
16927 memset( &mcastAddr4
, 0, sizeof( mcastAddr4
) );
16928 SIN_LEN_SET( &mcastAddr4
);
16929 mcastAddr4
.sin_family
= AF_INET
;
16930 mcastAddr4
.sin_port
= htons( kSSDPPort
);
16931 mcastAddr4
.sin_addr
.s_addr
= htonl( 0xEFFFFFFA ); // 239.255.255.250
16933 err
= WriteSSDPSearchRequest( &context
->header
, &mcastAddr4
, gSSDPDiscover_MX
, gSSDPDiscover_ST
);
16934 require_noerr( err
, exit
);
16936 n
= sendto( sockV4
, context
->header
.buf
, context
->header
.len
, 0, (const struct sockaddr
*) &mcastAddr4
,
16937 (socklen_t
) sizeof( mcastAddr4
) );
16938 err
= map_socket_value_errno( sockV4
, n
== (ssize_t
) context
->header
.len
, n
);
16941 FPrintF( stderr
, "*** Failed to send query on IPv4 socket with error %#m\n", err
);
16942 ForgetSocket( &sockV4
);
16946 if( gSSDPDiscover_Verbose
)
16948 gettimeofday( &now
, NULL
);
16949 FPrintF( stdout
, "---\n" );
16950 FPrintF( stdout
, "Send time: %{du:time}\n", &now
);
16951 FPrintF( stdout
, "Source Port: %d\n", SocketToPortNumber( sockV4
) );
16952 FPrintF( stdout
, "Destination: %##a\n", &mcastAddr4
);
16953 FPrintF( stdout
, "Message size: %zu\n", context
->header
.len
);
16954 FPrintF( stdout
, "HTTP header:\n%1{text}", context
->header
.buf
, context
->header
.len
);
16960 if( IsValidSocket( sockV6
) )
16962 struct sockaddr_in6 mcastAddr6
;
16964 memset( &mcastAddr6
, 0, sizeof( mcastAddr6
) );
16965 SIN6_LEN_SET( &mcastAddr6
);
16966 mcastAddr6
.sin6_family
= AF_INET6
;
16967 mcastAddr6
.sin6_port
= htons( kSSDPPort
);
16968 mcastAddr6
.sin6_addr
.s6_addr
[ 0 ] = 0xFF; // SSDP IPv6 link-local multicast address FF02::C
16969 mcastAddr6
.sin6_addr
.s6_addr
[ 1 ] = 0x02;
16970 mcastAddr6
.sin6_addr
.s6_addr
[ 15 ] = 0x0C;
16972 err
= WriteSSDPSearchRequest( &context
->header
, &mcastAddr6
, gSSDPDiscover_MX
, gSSDPDiscover_ST
);
16973 require_noerr( err
, exit
);
16975 n
= sendto( sockV6
, context
->header
.buf
, context
->header
.len
, 0, (const struct sockaddr
*) &mcastAddr6
,
16976 (socklen_t
) sizeof( mcastAddr6
) );
16977 err
= map_socket_value_errno( sockV6
, n
== (ssize_t
) context
->header
.len
, n
);
16980 FPrintF( stderr
, "*** Failed to send query on IPv6 socket with error %#m\n", err
);
16981 ForgetSocket( &sockV6
);
16985 if( gSSDPDiscover_Verbose
)
16987 gettimeofday( &now
, NULL
);
16988 FPrintF( stdout
, "---\n" );
16989 FPrintF( stdout
, "Send time: %{du:time}\n", &now
);
16990 FPrintF( stdout
, "Source Port: %d\n", SocketToPortNumber( sockV6
) );
16991 FPrintF( stdout
, "Destination: %##a\n", &mcastAddr6
);
16992 FPrintF( stdout
, "Message size: %zu\n", context
->header
.len
);
16993 FPrintF( stdout
, "HTTP header:\n%1{text}", context
->header
.buf
, context
->header
.len
);
16998 require_action_quiet( sendCount
> 0, exit
, err
= kUnexpectedErr
);
17000 // If there's no wait period after the send, then exit.
17002 if( context
->receiveSecs
== 0 ) goto exit
;
17004 // Create dispatch read sources for socket(s).
17006 if( IsValidSocket( sockV4
) )
17008 SocketContext
* sockCtx
;
17010 err
= SocketContextCreate( sockV4
, context
, &sockCtx
);
17011 require_noerr( err
, exit
);
17012 sockV4
= kInvalidSocketRef
;
17014 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, SSDPDiscoverReadHandler
, SocketContextCancelHandler
, sockCtx
,
17015 &context
->readSourceV4
);
17016 if( err
) ForgetSocketContext( &sockCtx
);
17017 require_noerr( err
, exit
);
17019 dispatch_resume( context
->readSourceV4
);
17022 if( IsValidSocket( sockV6
) )
17024 SocketContext
* sockCtx
;
17026 err
= SocketContextCreate( sockV6
, context
, &sockCtx
);
17027 require_noerr( err
, exit
);
17028 sockV6
= kInvalidSocketRef
;
17030 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, SSDPDiscoverReadHandler
, SocketContextCancelHandler
, sockCtx
,
17031 &context
->readSourceV6
);
17032 if( err
) ForgetSocketContext( &sockCtx
);
17033 require_noerr( err
, exit
);
17035 dispatch_resume( context
->readSourceV6
);
17038 if( context
->receiveSecs
> 0 )
17040 dispatch_after_f( dispatch_time_seconds( context
->receiveSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
17046 ForgetSocket( &sockV4
);
17047 ForgetSocket( &sockV6
);
17048 dispatch_source_forget( &signalSource
);
17049 exit( err
? 1 : 0 );
17052 static int SocketToPortNumber( SocketRef inSock
)
17058 len
= (socklen_t
) sizeof( sip
);
17059 err
= getsockname( inSock
, &sip
.sa
, &len
);
17060 err
= map_socket_noerr_errno( inSock
, err
);
17061 check_noerr( err
);
17062 return( err
? -1 : SockAddrGetPort( &sip
) );
17065 static OSStatus
WriteSSDPSearchRequest( HTTPHeader
*inHeader
, const void *inHostSA
, int inMX
, const char *inST
)
17069 err
= HTTPHeader_InitRequest( inHeader
, "M-SEARCH", "*", "HTTP/1.1" );
17070 require_noerr( err
, exit
);
17072 err
= HTTPHeader_SetField( inHeader
, "Host", "%##a", inHostSA
);
17073 require_noerr( err
, exit
);
17075 err
= HTTPHeader_SetField( inHeader
, "ST", "%s", inST
? inST
: "ssdp:all" );
17076 require_noerr( err
, exit
);
17078 err
= HTTPHeader_SetField( inHeader
, "Man", "\"ssdp:discover\"" );
17079 require_noerr( err
, exit
);
17081 err
= HTTPHeader_SetField( inHeader
, "MX", "%d", inMX
);
17082 require_noerr( err
, exit
);
17084 err
= HTTPHeader_Commit( inHeader
);
17085 require_noerr( err
, exit
);
17091 //===========================================================================================================================
17092 // SSDPDiscoverPrintPrologue
17093 //===========================================================================================================================
17095 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext
*inContext
)
17097 const int receiveSecs
= inContext
->receiveSecs
;
17098 const char * ifName
;
17099 char ifNameBuf
[ IF_NAMESIZE
+ 1 ];
17100 NetTransportType ifType
;
17102 ifName
= if_indextoname( inContext
->ifindex
, ifNameBuf
);
17104 ifType
= kNetTransportType_Undefined
;
17105 if( ifName
) SocketGetInterfaceInfo( kInvalidSocketRef
, ifName
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &ifType
);
17107 FPrintF( stdout
, "Interface: %s/%d/%s\n",
17108 ifName
? ifName
: "?", inContext
->ifindex
, NetTransportTypeToString( ifType
) );
17109 FPrintF( stdout
, "IP protocols: %?s%?s%?s\n",
17110 inContext
->useIPv4
, "IPv4", ( inContext
->useIPv4
&& inContext
->useIPv6
), ", ", inContext
->useIPv6
, "IPv6" );
17111 FPrintF( stdout
, "Receive duration: " );
17112 if( receiveSecs
>= 0 ) FPrintF( stdout
, "%d second%?c\n", receiveSecs
, receiveSecs
!= 1, 's' );
17113 else FPrintF( stdout
, "∞\n" );
17114 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
17117 //===========================================================================================================================
17118 // SSDPDiscoverReadHandler
17119 //===========================================================================================================================
17121 static Boolean
_HTTPHeader_Validate( HTTPHeader
*inHeader
);
17123 static void SSDPDiscoverReadHandler( void *inContext
)
17126 struct timeval now
;
17127 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
17128 SSDPDiscoverContext
* const context
= (SSDPDiscoverContext
*) sockCtx
->userContext
;
17129 HTTPHeader
* const header
= &context
->header
;
17130 sockaddr_ip fromAddr
;
17133 gettimeofday( &now
, NULL
);
17135 err
= SocketRecvFrom( sockCtx
->sock
, header
->buf
, sizeof( header
->buf
), &msgLen
, &fromAddr
, sizeof( fromAddr
),
17136 NULL
, NULL
, NULL
, NULL
);
17137 require_noerr( err
, exit
);
17139 FPrintF( stdout
, "---\n" );
17140 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
17141 FPrintF( stdout
, "Source: %##a\n", &fromAddr
);
17142 FPrintF( stdout
, "Message size: %zu\n", msgLen
);
17143 header
->len
= msgLen
;
17144 if( _HTTPHeader_Validate( header
) )
17146 FPrintF( stdout
, "HTTP header:\n%1{text}", header
->buf
, header
->len
);
17147 if( header
->extraDataLen
> 0 )
17149 FPrintF( stdout
, "HTTP body: %1.1H", header
->extraDataPtr
, (int) header
->extraDataLen
, INT_MAX
);
17154 FPrintF( stdout
, "Invalid HTTP message:\n%1.1H", header
->buf
, (int) msgLen
, INT_MAX
);
17159 if( err
) exit( 1 );
17162 //===========================================================================================================================
17163 // _HTTPHeader_Validate
17165 // Parses for the end of an HTTP header and updates the HTTPHeader structure so it's ready to parse. Returns true if valid.
17166 // This assumes the "buf" and "len" fields are set. The other fields are set by this function.
17168 // Note: This was copied from CoreUtils because the HTTPHeader_Validate function is currently not exported in the framework.
17169 //===========================================================================================================================
17171 static Boolean
_HTTPHeader_Validate( HTTPHeader
*inHeader
)
17176 // Check for interleaved binary data (4 byte header that begins with $). See RFC 2326 section 10.12.
17178 require( inHeader
->len
< sizeof( inHeader
->buf
), exit
);
17179 src
= inHeader
->buf
;
17180 end
= src
+ inHeader
->len
;
17181 if( ( ( end
- src
) >= 4 ) && ( src
[ 0 ] == '$' ) )
17187 // Search for an empty line (HTTP-style header/body separator). CRLFCRLF, LFCRLF, or LFLF accepted.
17188 // $$$ TO DO: Start from the last search location to avoid re-searching the same data over and over.
17192 while( ( src
< end
) && ( src
[ 0 ] != '\n' ) ) ++src
;
17193 if( src
>= end
) goto exit
;
17195 if( ( ( end
- src
) >= 2 ) && ( src
[ 0 ] == '\r' ) && ( src
[ 1 ] == '\n' ) ) // CFLFCRLF or LFCRLF
17200 else if( ( ( end
- src
) >= 1 ) && ( src
[ 0 ] == '\n' ) ) // LFLF
17207 inHeader
->extraDataPtr
= src
;
17208 inHeader
->extraDataLen
= (size_t)( end
- src
);
17209 inHeader
->len
= (size_t)( src
- inHeader
->buf
);
17216 #if( TARGET_OS_DARWIN )
17217 //===========================================================================================================================
17219 //===========================================================================================================================
17221 // res_query() from libresolv is actually called res_9_query (see /usr/include/resolv.h).
17223 SOFT_LINK_LIBRARY_EX( "/usr/lib", resolv
);
17224 SOFT_LINK_FUNCTION_EX( resolv
, res_9_query
,
17226 ( const char *dname
, int class, int type
, u_char
*answer
, int anslen
),
17227 ( dname
, class, type
, answer
, anslen
) );
17229 // res_query() from libinfo
17231 SOFT_LINK_LIBRARY_EX( "/usr/lib", info
);
17232 SOFT_LINK_FUNCTION_EX( info
, res_query
,
17234 ( const char *dname
, int class, int type
, u_char
*answer
, int anslen
),
17235 ( dname
, class, type
, answer
, anslen
) );
17237 typedef int ( *res_query_f
)( const char *dname
, int class, int type
, u_char
*answer
, int anslen
);
17239 static void ResQueryCmd( void )
17242 res_query_f res_query_ptr
;
17244 uint16_t type
, class;
17245 uint8_t answer
[ 1024 ];
17247 // Get pointer to one of the res_query() functions.
17249 if( gResQuery_UseLibInfo
)
17251 if( !SOFT_LINK_HAS_FUNCTION( info
, res_query
) )
17253 FPrintF( stderr
, "Failed to soft link res_query from libinfo.\n" );
17254 err
= kNotFoundErr
;
17257 res_query_ptr
= soft_res_query
;
17261 if( !SOFT_LINK_HAS_FUNCTION( resolv
, res_9_query
) )
17263 FPrintF( stderr
, "Failed to soft link res_query from libresolv.\n" );
17264 err
= kNotFoundErr
;
17267 res_query_ptr
= soft_res_9_query
;
17270 // Get record type.
17272 err
= RecordTypeFromArgString( gResQuery_Type
, &type
);
17273 require_noerr( err
, exit
);
17275 // Get record class.
17277 if( gResQuery_Class
)
17279 err
= RecordClassFromArgString( gResQuery_Class
, &class );
17280 require_noerr( err
, exit
);
17284 class = kDNSServiceClass_IN
;
17289 FPrintF( stdout
, "Name: %s\n", gResQuery_Name
);
17290 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( type
), type
);
17291 FPrintF( stdout
, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN
) ? "IN" : "???", class );
17292 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
17293 FPrintF( stdout
, "---\n" );
17295 // Call res_query().
17297 n
= res_query_ptr( gResQuery_Name
, class, type
, (u_char
*) answer
, (int) sizeof( answer
) );
17300 FPrintF( stderr
, "res_query() failed with error: %d (%s).\n", h_errno
, hstrerror( h_errno
) );
17307 FPrintF( stdout
, "Message size: %d\n\n%{du:dnsmsg}", n
, answer
, (size_t) n
);
17310 if( err
) exit( 1 );
17313 //===========================================================================================================================
17314 // ResolvDNSQueryCmd
17315 //===========================================================================================================================
17317 // 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
17318 // avoid including the header file.
17320 typedef void * dns_handle_t
;
17322 SOFT_LINK_FUNCTION_EX( resolv
, dns_open
, dns_handle_t
, ( const char *path
), ( path
) );
17323 SOFT_LINK_FUNCTION_VOID_RETURN_EX( resolv
, dns_free
, ( dns_handle_t
*dns
), ( dns
) );
17324 SOFT_LINK_FUNCTION_EX( resolv
, dns_query
,
17332 struct sockaddr
* from
,
17333 uint32_t * fromlen
),
17334 ( dns
, name
, dnsclass
, dnstype
, buf
, len
, from
, fromlen
) );
17336 static void ResolvDNSQueryCmd( void )
17340 dns_handle_t dns
= NULL
;
17341 uint16_t type
, class;
17344 uint8_t answer
[ 1024 ];
17346 // Make sure that the required symbols are available.
17348 if( !SOFT_LINK_HAS_FUNCTION( resolv
, dns_open
) )
17350 FPrintF( stderr
, "Failed to soft link dns_open from libresolv.\n" );
17351 err
= kNotFoundErr
;
17355 if( !SOFT_LINK_HAS_FUNCTION( resolv
, dns_free
) )
17357 FPrintF( stderr
, "Failed to soft link dns_free from libresolv.\n" );
17358 err
= kNotFoundErr
;
17362 if( !SOFT_LINK_HAS_FUNCTION( resolv
, dns_query
) )
17364 FPrintF( stderr
, "Failed to soft link dns_query from libresolv.\n" );
17365 err
= kNotFoundErr
;
17369 // Get record type.
17371 err
= RecordTypeFromArgString( gResolvDNSQuery_Type
, &type
);
17372 require_noerr( err
, exit
);
17374 // Get record class.
17376 if( gResolvDNSQuery_Class
)
17378 err
= RecordClassFromArgString( gResolvDNSQuery_Class
, &class );
17379 require_noerr( err
, exit
);
17383 class = kDNSServiceClass_IN
;
17388 dns
= soft_dns_open( gResolvDNSQuery_Path
);
17391 FPrintF( stderr
, "dns_open( %s ) failed.\n", gResolvDNSQuery_Path
);
17398 FPrintF( stdout
, "Name: %s\n", gResolvDNSQuery_Name
);
17399 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( type
), type
);
17400 FPrintF( stdout
, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN
) ? "IN" : "???", class );
17401 FPrintF( stdout
, "Path: %s\n", gResolvDNSQuery_Path
? gResolvDNSQuery_Name
: "<NULL>" );
17402 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
17403 FPrintF( stdout
, "---\n" );
17405 // Call dns_query().
17407 memset( &from
, 0, sizeof( from
) );
17408 fromLen
= (uint32_t) sizeof( from
);
17409 n
= soft_dns_query( dns
, gResolvDNSQuery_Name
, class, type
, (char *) answer
, (uint32_t) sizeof( answer
), &from
.sa
,
17413 FPrintF( stderr
, "dns_query() failed with error: %d (%s).\n", h_errno
, hstrerror( h_errno
) );
17420 FPrintF( stdout
, "From: %##a\n", &from
);
17421 FPrintF( stdout
, "Message size: %d\n\n%{du:dnsmsg}", n
, answer
, (size_t) n
);
17424 if( dns
) soft_dns_free( dns
);
17425 if( err
) exit( 1 );
17428 //===========================================================================================================================
17430 //===========================================================================================================================
17433 _CFHostResolveCallback(
17435 CFHostInfoType inInfoType
,
17436 const CFStreamError
* inError
,
17439 static void CFHostCmd( void )
17444 CFHostRef host
= NULL
;
17445 CFHostClientContext context
;
17446 CFStreamError streamErr
;
17448 name
= CFStringCreateWithCString( kCFAllocatorDefault
, gCFHost_Name
, kCFStringEncodingUTF8
);
17449 require_action( name
, exit
, err
= kUnknownErr
);
17451 host
= CFHostCreateWithName( kCFAllocatorDefault
, name
);
17453 require_action( host
, exit
, err
= kUnknownErr
);
17455 memset( &context
, 0, sizeof( context
) );
17456 success
= CFHostSetClient( host
, _CFHostResolveCallback
, &context
);
17457 require_action( success
, exit
, err
= kUnknownErr
);
17459 CFHostScheduleWithRunLoop( host
, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode
);
17463 FPrintF( stdout
, "Hostname: %s\n", gCFHost_Name
);
17464 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
17465 FPrintF( stdout
, "---\n" );
17467 success
= CFHostStartInfoResolution( host
, kCFHostAddresses
, &streamErr
);
17468 require_action( success
, exit
, err
= kUnknownErr
);
17474 CFReleaseNullSafe( host
);
17475 if( err
) exit( 1 );
17478 static void _CFHostResolveCallback( CFHostRef inHost
, CFHostInfoType inInfoType
, const CFStreamError
*inError
, void *inInfo
)
17481 struct timeval now
;
17483 gettimeofday( &now
, NULL
);
17485 Unused( inInfoType
);
17488 if( inError
&& ( inError
->domain
!= 0 ) && ( inError
->error
) )
17490 err
= inError
->error
;
17491 if( inError
->domain
== kCFStreamErrorDomainNetDB
)
17493 FPrintF( stderr
, "Error %d: %s.\n", err
, gai_strerror( err
) );
17497 FPrintF( stderr
, "Error %#m\n", err
);
17502 CFArrayRef addresses
;
17504 CFDataRef addrData
;
17505 const struct sockaddr
* sockAddr
;
17506 Boolean wasResolved
= false;
17508 addresses
= CFHostGetAddressing( inHost
, &wasResolved
);
17509 check( wasResolved
);
17513 count
= CFArrayGetCount( addresses
);
17514 for( i
= 0; i
< count
; ++i
)
17516 addrData
= CFArrayGetCFDataAtIndex( addresses
, i
, &err
);
17517 require_noerr( err
, exit
);
17519 sockAddr
= (const struct sockaddr
*) CFDataGetBytePtr( addrData
);
17520 FPrintF( stdout
, "%##a\n", sockAddr
);
17526 FPrintF( stdout
, "---\n" );
17527 FPrintF( stdout
, "End time: %{du:time}\n", &now
);
17529 if( gCFHost_WaitSecs
> 0 ) sleep( (unsigned int) gCFHost_WaitSecs
);
17532 exit( err
? 1 : 0 );
17535 //===========================================================================================================================
17538 // Note: Based on ajn's supplemental test tool.
17539 //===========================================================================================================================
17541 static void DNSConfigAddCmd( void )
17544 CFMutableDictionaryRef dict
= NULL
;
17545 CFMutableArrayRef array
= NULL
;
17547 SCDynamicStoreRef store
= NULL
;
17548 CFStringRef key
= NULL
;
17551 // Create dictionary.
17553 dict
= CFDictionaryCreateMutable( NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
17554 require_action( dict
, exit
, err
= kNoMemoryErr
);
17556 // Add DNS server IP addresses.
17558 array
= CFArrayCreateMutable( NULL
, (CFIndex
) gDNSConfigAdd_IPAddrCount
, &kCFTypeArrayCallBacks
);
17559 require_action( array
, exit
, err
= kNoMemoryErr
);
17561 for( i
= 0; i
< gDNSConfigAdd_IPAddrCount
; ++i
)
17563 CFStringRef addrStr
;
17565 addrStr
= CFStringCreateWithCString( NULL
, gDNSConfigAdd_IPAddrArray
[ i
], kCFStringEncodingUTF8
);
17566 require_action( addrStr
, exit
, err
= kUnknownErr
);
17568 CFArrayAppendValue( array
, addrStr
);
17569 CFRelease( addrStr
);
17572 CFDictionarySetValue( dict
, kSCPropNetDNSServerAddresses
, array
);
17573 ForgetCF( &array
);
17575 // Add domains, if any.
17577 array
= CFArrayCreateMutable( NULL
, (CFIndex
) Min( gDNSConfigAdd_DomainCount
, 1 ), &kCFTypeArrayCallBacks
);
17578 require_action( array
, exit
, err
= kNoMemoryErr
);
17580 if( gDNSConfigAdd_DomainCount
> 0 )
17582 for( i
= 0; i
< gDNSConfigAdd_DomainCount
; ++i
)
17584 CFStringRef domainStr
;
17586 domainStr
= CFStringCreateWithCString( NULL
, gDNSConfigAdd_DomainArray
[ i
], kCFStringEncodingUTF8
);
17587 require_action( domainStr
, exit
, err
= kUnknownErr
);
17589 CFArrayAppendValue( array
, domainStr
);
17590 CFRelease( domainStr
);
17595 // There are no domains, but the domain array needs to be non-empty, so add a zero-length string to the array.
17597 CFArrayAppendValue( array
, CFSTR( "" ) );
17600 CFDictionarySetValue( dict
, kSCPropNetDNSSupplementalMatchDomains
, array
);
17601 ForgetCF( &array
);
17603 // Add interface, if any.
17605 if( gDNSConfigAdd_Interface
)
17607 err
= CFDictionarySetCString( dict
, kSCPropInterfaceName
, gDNSConfigAdd_Interface
, kSizeCString
);
17608 require_noerr( err
, exit
);
17610 CFDictionarySetValue( dict
, kSCPropNetDNSConfirmedServiceID
, gDNSConfigAdd_ID
);
17613 // Set dictionary in dynamic store.
17615 store
= SCDynamicStoreCreate( NULL
, CFSTR( kDNSSDUtilIdentifier
), NULL
, NULL
);
17616 err
= map_scerror( store
);
17617 require_noerr( err
, exit
);
17619 key
= SCDynamicStoreKeyCreateNetworkServiceEntity( NULL
, kSCDynamicStoreDomainState
, gDNSConfigAdd_ID
, kSCEntNetDNS
);
17620 require_action( key
, exit
, err
= kUnknownErr
);
17622 success
= SCDynamicStoreSetValue( store
, key
, dict
);
17623 require_action( success
, exit
, err
= kUnknownErr
);
17626 CFReleaseNullSafe( dict
);
17627 CFReleaseNullSafe( array
);
17628 CFReleaseNullSafe( store
);
17629 CFReleaseNullSafe( key
);
17630 gExitCode
= err
? 1 : 0;
17633 //===========================================================================================================================
17634 // DNSConfigRemoveCmd
17635 //===========================================================================================================================
17637 static void DNSConfigRemoveCmd( void )
17640 SCDynamicStoreRef store
= NULL
;
17641 CFStringRef key
= NULL
;
17644 store
= SCDynamicStoreCreate( NULL
, CFSTR( kDNSSDUtilIdentifier
), NULL
, NULL
);
17645 err
= map_scerror( store
);
17646 require_noerr( err
, exit
);
17648 key
= SCDynamicStoreKeyCreateNetworkServiceEntity( NULL
, kSCDynamicStoreDomainState
, gDNSConfigRemove_ID
, kSCEntNetDNS
);
17649 require_action( key
, exit
, err
= kUnknownErr
);
17651 success
= SCDynamicStoreRemoveValue( store
, key
);
17652 require_action( success
, exit
, err
= kUnknownErr
);
17655 CFReleaseNullSafe( store
);
17656 CFReleaseNullSafe( key
);
17657 gExitCode
= err
? 1 : 0;
17660 //===========================================================================================================================
17662 //===========================================================================================================================
17664 static OSStatus
_XPCDictionaryCreateFromString( const char *inString
, xpc_object_t
*outDict
);
17666 static void XPCSendCmd( void )
17669 xpc_object_t msg
, reply
;
17671 err
= _XPCDictionaryCreateFromString( gXPCSend_MessageStr
, &msg
);
17672 require_noerr_quiet( err
, exit
);
17674 FPrintF( stdout
, "Service: %s\n", gXPCSend_ServiceName
);
17675 FPrintF( stdout
, "Message: %s\n", gXPCSend_MessageStr
);
17676 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
17677 FPrintF( stdout
, "---\n" );
17678 FPrintF( stdout
, "XPC Message:\n%{xpc}\n", msg
);
17680 err
= xpc_send_message_sync( gXPCSend_ServiceName
, 0, 0, msg
, &reply
);
17681 xpc_forget( &msg
);
17682 require_noerr_quiet( err
, exit
);
17684 FPrintF( stdout
, "XPC Reply:\n%{xpc}\n", reply
);
17685 FPrintF( stdout
, "---\n" );
17686 FPrintF( stdout
, "End time: %{du:time}\n", NULL
);
17687 xpc_forget( &reply
);
17690 if( err
) ErrQuit( 1, "error: %#m\n", err
);
17693 //===========================================================================================================================
17694 // _XPCDictionaryCreateFromString
17695 //===========================================================================================================================
17697 #define kXPCObjectPrefix_Bool "bool:"
17698 #define kXPCObjectPrefix_Data "data:"
17699 #define kXPCObjectPrefix_Int64 "int:"
17700 #define kXPCObjectPrefix_String "string:"
17701 #define kXPCObjectPrefix_UInt64 "uint:"
17702 #define kXPCObjectPrefix_UUID "uuid:"
17704 typedef struct XPCListItem XPCListItem
;
17707 XPCListItem
* next
;
17712 static OSStatus
_XPCListItemCreate( xpc_object_t inObject
, const char *inKey
, XPCListItem
**outItem
);
17713 static void _XPCListItemFree( XPCListItem
*inItem
);
17714 static void _XPCListFree( XPCListItem
*inList
);
17716 static OSStatus
_XPCObjectFromString( const char *inString
, xpc_object_t
*outObject
);
17718 static OSStatus
_XPCDictionaryCreateFromString( const char *inString
, xpc_object_t
*outDict
)
17721 xpc_object_t container
;
17722 const char * ptr
= inString
;
17723 const char * const end
= inString
+ strlen( inString
);
17724 XPCListItem
* list
= NULL
;
17726 container
= xpc_dictionary_create( NULL
, NULL
, 0 );
17727 require_action( container
, exit
, err
= kNoMemoryErr
);
17731 xpc_type_t containerType
;
17732 xpc_object_t value
;
17734 char keyStr
[ 256 ];
17735 char valStr
[ 256 ];
17737 // At this point, zero or more of the current container's elements have been parsed.
17738 // Skip the white space leading up to the container's next element, if any, or the container's end.
17740 while( isspace_safe( *ptr
) ) ++ptr
;
17742 // Check if we're done with the current container.
17745 if( c
== '\0' ) break;
17747 containerType
= xpc_get_type( container
);
17748 if( ( ( containerType
== XPC_TYPE_DICTIONARY
) && ( c
== '}' ) ) ||
17749 ( ( containerType
== XPC_TYPE_ARRAY
) && ( c
== ']' ) ) )
17751 XPCListItem
* item
;
17754 require_action_quiet( item
, exit
, err
= kMalformedErr
);
17758 // Add the current container to its parent container.
17762 xpc_dictionary_set_value( item
->obj
, item
->key
, container
);
17766 xpc_array_append_value( item
->obj
, container
);
17769 // Continue with the parent container.
17771 xpc_release( container
);
17772 container
= xpc_retain( item
->obj
);
17774 _XPCListItemFree( item
);
17778 // If the current container is a dictionary, parse the key string.
17780 if( containerType
== XPC_TYPE_DICTIONARY
)
17782 err
= _ParseEscapedString( ptr
, end
, "={}[]" kWhiteSpaceCharSet
, keyStr
, sizeof( keyStr
), NULL
, NULL
, &ptr
);
17783 require_noerr_quiet( err
, exit
);
17786 require_action_quiet( c
== '=', exit
, err
= kMalformedErr
);
17790 // Check if the value is a dictionary ({...}) or an array ([...]).
17793 if( ( c
== '{' ) || ( c
== '[' ) )
17795 XPCListItem
* item
;
17799 // Save the current container.
17801 err
= _XPCListItemCreate( container
, ( containerType
== XPC_TYPE_DICTIONARY
) ? keyStr
: NULL
, &item
);
17802 require_noerr( err
, exit
);
17808 // Create and continue with the child container.
17810 xpc_release( container
);
17813 container
= xpc_dictionary_create( NULL
, NULL
, 0 );
17814 require_action( container
, exit
, err
= kNoMemoryErr
);
17818 container
= xpc_array_create( NULL
, 0 );
17819 require_action( container
, exit
, err
= kNoMemoryErr
);
17824 // Parse the value string.
17826 err
= _ParseEscapedString( ptr
, end
, "{}[]" kWhiteSpaceCharSet
, valStr
, sizeof( valStr
), NULL
, NULL
, &ptr
);
17827 require_noerr_quiet( err
, exit
);
17829 err
= _XPCObjectFromString( valStr
, &value
);
17830 require_noerr_quiet( err
, exit
);
17832 if( containerType
== XPC_TYPE_DICTIONARY
)
17834 xpc_dictionary_set_value( container
, keyStr
, value
);
17838 xpc_array_append_value( container
, value
);
17840 xpc_forget( &value
);
17842 require_action_quiet( !list
, exit
, err
= kMalformedErr
);
17844 check( container
);
17845 check( xpc_get_type( container
) == XPC_TYPE_DICTIONARY
);
17847 *outDict
= container
;
17852 xpc_release_null_safe( container
);
17853 if( list
) _XPCListFree( list
);
17857 static OSStatus
_XPCObjectFromString( const char *inString
, xpc_object_t
*outObject
)
17860 xpc_object_t object
;
17866 else if( stricmp_prefix( inString
, kXPCObjectPrefix_Bool
) == 0 )
17868 const char * const str
= inString
+ sizeof_string( kXPCObjectPrefix_Bool
);
17871 if( IsTrueString( str
, kSizeCString
) )
17875 else if( IsFalseString( str
, kSizeCString
) )
17885 object
= xpc_bool_create( value
);
17886 require_action( object
, exit
, err
= kNoMemoryErr
);
17891 else if( stricmp_prefix( inString
, kXPCObjectPrefix_Data
) == 0 )
17893 const char * const str
= inString
+ sizeof_string( kXPCObjectPrefix_Data
);
17897 err
= HexToDataCopy( str
, kSizeCString
, kHexToData_DefaultFlags
, &dataPtr
, &dataLen
, NULL
);
17898 require_noerr( err
, exit
);
17900 object
= xpc_data_create( dataPtr
, dataLen
);
17902 require_action( object
, exit
, err
= kNoMemoryErr
);
17907 else if( stricmp_prefix( inString
, kXPCObjectPrefix_Int64
) == 0 )
17909 const char * const str
= inString
+ sizeof_string( kXPCObjectPrefix_Int64
);
17912 i64
= _StringToInt64( str
, &err
);
17913 require_noerr_quiet( err
, exit
);
17915 object
= xpc_int64_create( i64
);
17916 require_action( object
, exit
, err
= kNoMemoryErr
);
17921 else if( stricmp_prefix( inString
, kXPCObjectPrefix_String
) == 0 )
17923 const char * const str
= inString
+ sizeof_string( kXPCObjectPrefix_String
);
17925 object
= xpc_string_create( str
);
17926 require_action( object
, exit
, err
= kNoMemoryErr
);
17931 else if( stricmp_prefix( inString
, kXPCObjectPrefix_UInt64
) == 0 )
17933 const char * const str
= inString
+ sizeof_string( kXPCObjectPrefix_UInt64
);
17936 u64
= _StringToUInt64( str
, &err
);
17937 require_noerr_quiet( err
, exit
);
17939 object
= xpc_uint64_create( u64
);
17940 require_action( object
, exit
, err
= kNoMemoryErr
);
17945 else if( stricmp_prefix( inString
, kXPCObjectPrefix_UUID
) == 0 )
17947 const char * const str
= inString
+ sizeof_string( kXPCObjectPrefix_UUID
);
17950 err
= uuid_parse( str
, uuid
);
17951 require_noerr_action_quiet( err
, exit
, err
= kValueErr
);
17953 object
= xpc_uuid_create( uuid
);
17954 require_action( object
, exit
, err
= kNoMemoryErr
);
17957 // Unsupported prefix
17965 *outObject
= object
;
17972 static OSStatus
_XPCListItemCreate( xpc_object_t inObject
, const char *inKey
, XPCListItem
**outItem
)
17975 XPCListItem
* item
;
17977 item
= (XPCListItem
*) calloc( 1, sizeof( *item
) );
17978 require_action( item
, exit
, err
= kNoMemoryErr
);
17980 item
->obj
= xpc_retain( inObject
);
17981 if( ( xpc_get_type( item
->obj
) == XPC_TYPE_DICTIONARY
) && inKey
)
17983 item
->key
= strdup( inKey
);
17984 require_action( item
->key
, exit
, err
= kNoMemoryErr
);
17992 if( item
) _XPCListItemFree( item
);
17996 static void _XPCListItemFree( XPCListItem
*inItem
)
17998 xpc_forget( &inItem
->obj
);
17999 ForgetMem( &inItem
->key
);
18003 static void _XPCListFree( XPCListItem
*inList
)
18005 XPCListItem
* item
;
18007 while( ( item
= inList
) != NULL
)
18009 inList
= item
->next
;
18010 _XPCListItemFree( item
);
18013 #endif // TARGET_OS_DARWIN
18015 #if( MDNSRESPONDER_PROJECT )
18016 //===========================================================================================================================
18017 // InterfaceMonitorCmd
18018 //===========================================================================================================================
18020 static void _InterfaceMonitorPrint( mdns_interface_monitor_t inMonitor
);
18021 static void _InterfaceMonitorSignalHandler( void *inContext
);
18023 static void InterfaceMonitorCmd( void )
18026 mdns_interface_monitor_t monitor
;
18027 dispatch_source_t signalSource
= NULL
;
18029 __block
int exitCode
;
18031 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
18032 require_noerr_quiet( err
, exit
);
18034 monitor
= mdns_interface_monitor_create( ifIndex
);
18035 require_action( monitor
, exit
, err
= kNoResourcesErr
);
18038 mdns_interface_monitor_set_queue( monitor
, dispatch_get_main_queue() );
18039 mdns_interface_monitor_set_event_handler( monitor
,
18040 ^( mdns_event_t inEvent
, OSStatus inError
)
18044 case mdns_event_error
:
18045 FPrintF( stderr
, "error: Interface monitor failed: %#m\n", inError
);
18046 mdns_interface_monitor_invalidate( monitor
);
18050 case mdns_event_invalidated
:
18051 FPrintF( stdout
, "Interface monitor invalidated.\n" );
18052 mdns_release( monitor
);
18056 FPrintF( stdout
, "Unhandled event '%s' (%ld)\n", mdns_event_to_string( inEvent
), (long) inEvent
);
18060 mdns_interface_monitor_set_update_handler( monitor
,
18061 ^( __unused mdns_interface_flags_t inChangeFlags
)
18063 _InterfaceMonitorPrint( monitor
);
18066 _InterfaceMonitorPrint( monitor
);
18067 mdns_interface_monitor_activate( monitor
);
18069 signal( SIGINT
, SIG_IGN
);
18070 err
= DispatchSignalSourceCreate( SIGINT
, _InterfaceMonitorSignalHandler
, monitor
, &signalSource
);
18071 require_noerr( err
, exit
);
18072 dispatch_resume( signalSource
);
18077 if( err
) ErrQuit( 1, "error: %#m\n", err
);
18080 static void _InterfaceMonitorPrint( mdns_interface_monitor_t inMonitor
)
18082 FPrintF( stdout
, "%{du:time} %@\n", NULL
, inMonitor
);
18085 static void _InterfaceMonitorSignalHandler( void *inContext
)
18087 mdns_interface_monitor_invalidate( (mdns_interface_monitor_t
) inContext
);
18090 //===========================================================================================================================
18092 //===========================================================================================================================
18094 static void _DNSProxyCallback( DNSXConnRef inConnection
, DNSXErrorType inError
);
18095 static void _DNSProxyCmdSignalHandler( void *inContext
);
18097 static void DNSProxyCmd( void )
18101 DNSXConnRef connection
;
18102 IfIndex inputIfIndexes
[ MaxInputIf
];
18103 dispatch_source_t sigIntSource
= NULL
;
18104 dispatch_source_t sigTermSource
= NULL
;
18105 uint32_t outputIfIndex
;
18106 char ifName
[ kInterfaceNameBufLen
];
18108 if( gDNSProxy_InputInterfaceCount
> MaxInputIf
)
18110 FPrintF( stderr
, "error: Invalid input interface count: %zu > %d (max).\n",
18111 gDNSProxy_InputInterfaceCount
, MaxInputIf
);
18116 for( i
= 0; i
< gDNSProxy_InputInterfaceCount
; ++i
)
18120 err
= InterfaceIndexFromArgString( gDNSProxy_InputInterfaces
[ i
], &ifIndex
);
18121 require_noerr_quiet( err
, exit
);
18123 inputIfIndexes
[ i
] = ifIndex
;
18125 while( i
< MaxInputIf
) inputIfIndexes
[ i
++ ] = 0; // Remaining interface indexes are required to be 0.
18127 if( gDNSProxy_OutputInterface
)
18129 err
= InterfaceIndexFromArgString( gDNSProxy_OutputInterface
, &outputIfIndex
);
18130 require_noerr_quiet( err
, exit
);
18134 outputIfIndex
= kDNSIfindexAny
;
18137 FPrintF( stdout
, "Input Interfaces:" );
18138 for( i
= 0; i
< gDNSProxy_InputInterfaceCount
; ++i
)
18140 const uint32_t ifIndex
= (uint32_t) inputIfIndexes
[ i
];
18142 FPrintF( stdout
, "%s %u (%s)", ( i
== 0 ) ? "" : ",", ifIndex
, InterfaceIndexToName( ifIndex
, ifName
) );
18144 FPrintF( stdout
, "\n" );
18145 FPrintF( stdout
, "Output Interface: %u (%s)\n", outputIfIndex
, InterfaceIndexToName( outputIfIndex
, ifName
) );
18146 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
18147 FPrintF( stdout
, "---\n" );
18150 err
= DNSXEnableProxy( &connection
, kDNSProxyEnable
, inputIfIndexes
, outputIfIndex
, dispatch_get_main_queue(),
18151 _DNSProxyCallback
);
18152 require_noerr_quiet( err
, exit
);
18154 signal( SIGINT
, SIG_IGN
);
18155 err
= DispatchSignalSourceCreate( SIGINT
, _DNSProxyCmdSignalHandler
, connection
, &sigIntSource
);
18156 require_noerr( err
, exit
);
18157 dispatch_activate( sigIntSource
);
18159 signal( SIGTERM
, SIG_IGN
);
18160 err
= DispatchSignalSourceCreate( SIGTERM
, _DNSProxyCmdSignalHandler
, connection
, &sigTermSource
);
18161 require_noerr( err
, exit
);
18162 dispatch_activate( sigTermSource
);
18167 if( err
) ErrQuit( 1, "error: %#m\n", err
);
18170 static void _DNSProxyCallback( DNSXConnRef inConnection
, DNSXErrorType inError
)
18172 Unused( inConnection
);
18174 if( inError
) ErrQuit( 1, "error: DNS proxy failed: %#m\n", inError
);
18177 static void _DNSProxyCmdSignalHandler( void *inContext
)
18179 DNSXConnRef
const connection
= (DNSXConnRef
) inContext
;
18180 struct timeval now
;
18182 gettimeofday( &now
, NULL
);
18184 DNSXRefDeAlloc( connection
);
18186 FPrintF( stdout
, "---\n" );
18187 FPrintF( stdout
, "End time: %{du:time}\n", &now
);
18191 #endif // MDNSRESPONDER_PROJECT
18193 //===========================================================================================================================
18194 // DaemonVersionCmd
18195 //===========================================================================================================================
18197 static void DaemonVersionCmd( void )
18200 uint32_t size
, version
;
18203 size
= (uint32_t) sizeof( version
);
18204 err
= DNSServiceGetProperty( kDNSServiceProperty_DaemonVersion
, &version
, &size
);
18205 require_noerr( err
, exit
);
18207 FPrintF( stdout
, "Daemon version: %s\n", SourceVersionToCString( version
, strBuf
) );
18210 if( err
) exit( 1 );
18213 //===========================================================================================================================
18215 //===========================================================================================================================
18217 static void Exit( void *inContext
)
18219 const char * const reason
= (const char *) inContext
;
18221 FPrintF( stdout
, "---\n" );
18222 FPrintF( stdout
, "End time: %{du:time}\n", NULL
);
18223 if( reason
) FPrintF( stdout
, "End reason: %s\n", reason
);
18227 //===========================================================================================================================
18228 // _PrintFExtensionTimestampHandler
18229 //===========================================================================================================================
18232 _PrintFExtensionTimestampHandler(
18233 PrintFContext
* inContext
,
18234 PrintFFormat
* inFormat
,
18235 PrintFVAList
* inArgs
,
18236 void * inUserContext
)
18238 struct timeval now
;
18239 const struct timeval
* tv
;
18240 struct tm
* localTime
;
18243 char dateTimeStr
[ 32 ];
18245 Unused( inUserContext
);
18247 tv
= va_arg( inArgs
->args
, const struct timeval
* );
18248 require_action_quiet( !inFormat
->suppress
, exit
, n
= 0 );
18252 gettimeofday( &now
, NULL
);
18255 localTime
= localtime( &tv
->tv_sec
);
18256 len
= strftime( dateTimeStr
, sizeof( dateTimeStr
), "%Y-%m-%d %H:%M:%S", localTime
);
18257 if( len
== 0 ) dateTimeStr
[ 0 ] = '\0';
18259 n
= PrintFCore( inContext
, "%s.%06u", dateTimeStr
, (unsigned int) tv
->tv_usec
);
18265 //===========================================================================================================================
18266 // _PrintFExtensionDNSMessageHandler
18267 //===========================================================================================================================
18270 _PrintFExtensionDNSMessageHandler(
18271 PrintFContext
* inContext
,
18272 PrintFFormat
* inFormat
,
18273 PrintFVAList
* inArgs
,
18274 void * inUserContext
)
18277 const void * msgPtr
;
18282 Boolean printRawRData
;
18284 Unused( inUserContext
);
18286 msgPtr
= va_arg( inArgs
->args
, const void * );
18287 msgLen
= va_arg( inArgs
->args
, size_t );
18288 require_action_quiet( !inFormat
->suppress
, exit
, n
= 0 );
18290 isMDNS
= ( inFormat
->altForm
> 0 ) ? true : false;
18291 printRawRData
= ( inFormat
->precision
> 0 ) ? true : false;
18292 err
= DNSMessageToText( msgPtr
, msgLen
, isMDNS
, printRawRData
, &text
);
18295 n
= PrintFCore( inContext
, "%*{text}", inFormat
->fieldWidth
, text
, kSizeCString
);
18300 n
= PrintFCore( inContext
, "%*.1H", inFormat
->fieldWidth
, msgPtr
, (int) msgLen
, (int) msgLen
);
18307 //===========================================================================================================================
18308 // _PrintFExtensionCallbackFlagsHandler
18309 //===========================================================================================================================
18312 _PrintFExtensionCallbackFlagsHandler(
18313 PrintFContext
* inContext
,
18314 PrintFFormat
* inFormat
,
18315 PrintFVAList
* inArgs
,
18316 void * inUserContext
)
18318 DNSServiceFlags flags
;
18321 Unused( inUserContext
);
18323 flags
= va_arg( inArgs
->args
, DNSServiceFlags
);
18324 require_action_quiet( !inFormat
->suppress
, exit
, n
= 0 );
18326 n
= PrintFCore( inContext
, "%08X %s%c %c%c",
18327 flags
, DNSServiceFlagsToAddRmvStr( flags
),
18328 ( flags
& kDNSServiceFlagsMoreComing
) ? '+' : ' ',
18329 ( flags
& kDNSServiceFlagAnsweredFromCache
) ? 'C' : ' ',
18330 ( flags
& kDNSServiceFlagsExpiredAnswer
) ? '*' : ' ' );
18336 //===========================================================================================================================
18337 // _PrintFExtensionDNSRecordDataHandler
18338 //===========================================================================================================================
18341 _PrintFExtensionDNSRecordDataHandler(
18342 PrintFContext
* inContext
,
18343 PrintFFormat
* inFormat
,
18344 PrintFVAList
* inArgs
,
18345 void * inUserContext
)
18347 const void * rdataPtr
;
18348 unsigned int rdataLen
, rdataType
;
18351 Unused( inUserContext
);
18353 rdataType
= va_arg( inArgs
->args
, unsigned int );
18354 rdataPtr
= va_arg( inArgs
->args
, const void * );
18355 rdataLen
= va_arg( inArgs
->args
, unsigned int );
18356 require_action_quiet( !inFormat
->suppress
, exit
, n
= 0 );
18358 check( inFormat
->fieldWidth
< INT_MAX
);
18359 fieldWidth
= inFormat
->leftJustify
? -( (int) inFormat
->fieldWidth
) : ( (int) inFormat
->fieldWidth
);
18363 char * rdataStr
= NULL
;
18365 DNSRecordDataToString( rdataPtr
, rdataLen
, rdataType
, NULL
, 0, &rdataStr
);
18368 n
= PrintFCore( inContext
, "%*s", fieldWidth
, rdataStr
);
18373 n
= PrintFCore( inContext
, "%*H", fieldWidth
, rdataPtr
, rdataLen
, rdataLen
);
18378 n
= PrintFCore( inContext
, "%*s", fieldWidth
, "<< ZERO-LENGTH RDATA >>" );
18385 //===========================================================================================================================
18386 // GetDNSSDFlagsFromOpts
18387 //===========================================================================================================================
18389 static DNSServiceFlags
GetDNSSDFlagsFromOpts( void )
18391 DNSServiceFlags flags
;
18393 flags
= (DNSServiceFlags
) gDNSSDFlags
;
18394 if( flags
& kDNSServiceFlagsShareConnection
)
18396 FPrintF( stderr
, "*** Warning: kDNSServiceFlagsShareConnection (0x%X) is explicitly set in flag parameters.\n",
18397 kDNSServiceFlagsShareConnection
);
18400 if( gDNSSDFlag_AllowExpiredAnswers
) flags
|= kDNSServiceFlagsAllowExpiredAnswers
;
18401 if( gDNSSDFlag_BrowseDomains
) flags
|= kDNSServiceFlagsBrowseDomains
;
18402 if( gDNSSDFlag_DenyCellular
) flags
|= kDNSServiceFlagsDenyCellular
;
18403 if( gDNSSDFlag_DenyConstrained
) flags
|= kDNSServiceFlagsDenyConstrained
;
18404 if( gDNSSDFlag_DenyExpensive
) flags
|= kDNSServiceFlagsDenyExpensive
;
18405 if( gDNSSDFlag_ForceMulticast
) flags
|= kDNSServiceFlagsForceMulticast
;
18406 if( gDNSSDFlag_IncludeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
18407 if( gDNSSDFlag_KnownUnique
) flags
|= kDNSServiceFlagsKnownUnique
;
18408 if( gDNSSDFlag_NoAutoRename
) flags
|= kDNSServiceFlagsNoAutoRename
;
18409 if( gDNSSDFlag_PathEvaluationDone
) flags
|= kDNSServiceFlagsPathEvaluationDone
;
18410 if( gDNSSDFlag_RegistrationDomains
) flags
|= kDNSServiceFlagsRegistrationDomains
;
18411 if( gDNSSDFlag_ReturnIntermediates
) flags
|= kDNSServiceFlagsReturnIntermediates
;
18412 if( gDNSSDFlag_Shared
) flags
|= kDNSServiceFlagsShared
;
18413 if( gDNSSDFlag_SuppressUnusable
) flags
|= kDNSServiceFlagsSuppressUnusable
;
18414 if( gDNSSDFlag_Timeout
) flags
|= kDNSServiceFlagsTimeout
;
18415 if( gDNSSDFlag_UnicastResponse
) flags
|= kDNSServiceFlagsUnicastResponse
;
18416 if( gDNSSDFlag_Unique
) flags
|= kDNSServiceFlagsUnique
;
18417 if( gDNSSDFlag_WakeOnResolve
) flags
|= kDNSServiceFlagsWakeOnResolve
;
18422 //===========================================================================================================================
18423 // CreateConnectionFromArgString
18424 //===========================================================================================================================
18427 CreateConnectionFromArgString(
18428 const char * inString
,
18429 dispatch_queue_t inQueue
,
18430 DNSServiceRef
* outSDRef
,
18431 ConnectionDesc
* outDesc
)
18434 DNSServiceRef sdRef
= NULL
;
18435 ConnectionType type
;
18436 int32_t pid
= -1; // Initializing because the analyzer claims pid may be used uninitialized.
18437 uint8_t uuid
[ 16 ];
18439 if( strcasecmp( inString
, kConnectionArg_Normal
) == 0 )
18441 err
= DNSServiceCreateConnection( &sdRef
);
18442 require_noerr( err
, exit
);
18443 type
= kConnectionType_Normal
;
18445 else if( stricmp_prefix( inString
, kConnectionArgPrefix_PID
) == 0 )
18447 const char * const pidStr
= inString
+ sizeof_string( kConnectionArgPrefix_PID
);
18449 err
= StringToInt32( pidStr
, &pid
);
18452 FPrintF( stderr
, "Invalid delegate connection PID value: %s\n", pidStr
);
18457 memset( uuid
, 0, sizeof( uuid
) );
18458 err
= DNSServiceCreateDelegateConnection( &sdRef
, pid
, uuid
);
18461 FPrintF( stderr
, "DNSServiceCreateDelegateConnection() returned %#m for PID %d\n", err
, pid
);
18464 type
= kConnectionType_DelegatePID
;
18466 else if( stricmp_prefix( inString
, kConnectionArgPrefix_UUID
) == 0 )
18468 const char * const uuidStr
= inString
+ sizeof_string( kConnectionArgPrefix_UUID
);
18470 check_compile_time_code( sizeof( uuid
) == sizeof( uuid_t
) );
18472 err
= StringToUUID( uuidStr
, kSizeCString
, false, uuid
);
18475 FPrintF( stderr
, "Invalid delegate connection UUID value: %s\n", uuidStr
);
18480 err
= DNSServiceCreateDelegateConnection( &sdRef
, 0, uuid
);
18483 FPrintF( stderr
, "DNSServiceCreateDelegateConnection() returned %#m for UUID %#U\n", err
, uuid
);
18486 type
= kConnectionType_DelegateUUID
;
18490 FPrintF( stderr
, "Unrecognized connection string \"%s\".\n", inString
);
18495 err
= DNSServiceSetDispatchQueue( sdRef
, inQueue
);
18496 require_noerr( err
, exit
);
18501 outDesc
->type
= type
;
18502 if( type
== kConnectionType_DelegatePID
) outDesc
->delegate
.pid
= pid
;
18503 else if( type
== kConnectionType_DelegateUUID
) memcpy( outDesc
->delegate
.uuid
, uuid
, 16 );
18508 if( sdRef
) DNSServiceRefDeallocate( sdRef
);
18512 //===========================================================================================================================
18513 // InterfaceIndexFromArgString
18514 //===========================================================================================================================
18516 static OSStatus
InterfaceIndexFromArgString( const char *inString
, uint32_t *outIndex
)
18523 ifIndex
= if_nametoindex( inString
);
18526 err
= StringToUInt32( inString
, &ifIndex
);
18529 FPrintF( stderr
, "error: Invalid interface value: %s\n", inString
);
18540 *outIndex
= ifIndex
;
18547 //===========================================================================================================================
18548 // RecordDataFromArgString
18549 //===========================================================================================================================
18551 static OSStatus
RecordDataFromArgString( const char *inString
, uint8_t **outDataPtr
, size_t *outDataLen
)
18554 uint8_t * dataPtr
= NULL
;
18561 else if( stricmp_prefix( inString
, kRDataArgPrefix_Domain
) == 0 )
18563 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_Domain
);
18565 err
= StringToDomainName( str
, &dataPtr
, &dataLen
);
18566 require_noerr_quiet( err
, exit
);
18571 else if( stricmp_prefix( inString
, kRDataArgPrefix_File
) == 0 )
18573 const char * const path
= inString
+ sizeof_string( kRDataArgPrefix_File
);
18575 err
= CopyFileDataByPath( path
, (char **) &dataPtr
, &dataLen
);
18576 require_noerr( err
, exit
);
18577 require_action( dataLen
<= kDNSRecordDataLengthMax
, exit
, err
= kSizeErr
);
18580 // Hexadecimal string
18582 else if( stricmp_prefix( inString
, kRDataArgPrefix_HexString
) == 0 )
18584 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_HexString
);
18586 err
= HexToDataCopy( str
, kSizeCString
, kHexToData_DefaultFlags
, &dataPtr
, &dataLen
, NULL
);
18587 require_noerr( err
, exit
);
18588 require_action( dataLen
<= kDNSRecordDataLengthMax
, exit
, err
= kSizeErr
);
18591 // IPv4 address string
18593 else if( stricmp_prefix( inString
, kRDataArgPrefix_IPv4
) == 0 )
18595 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_IPv4
);
18597 err
= StringToARecordData( str
, &dataPtr
, &dataLen
);
18598 require_noerr_quiet( err
, exit
);
18601 // IPv6 address string
18603 else if( stricmp_prefix( inString
, kRDataArgPrefix_IPv6
) == 0 )
18605 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_IPv6
);
18607 err
= StringToAAAARecordData( str
, &dataPtr
, &dataLen
);
18608 require_noerr_quiet( err
, exit
);
18613 else if( stricmp_prefix( inString
, kRDataArgPrefix_SRV
) == 0 )
18615 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_SRV
);
18617 err
= CreateSRVRecordDataFromString( str
, &dataPtr
, &dataLen
);
18618 require_noerr( err
, exit
);
18621 // String with escaped hex and octal bytes
18623 else if( stricmp_prefix( inString
, kRDataArgPrefix_String
) == 0 )
18625 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_String
);
18626 const char * const end
= str
+ strlen( str
);
18633 success
= _ParseQuotedEscapedString( str
, end
, "", NULL
, 0, NULL
, &totalLen
, NULL
);
18634 require_action( success
, exit
, err
= kParamErr
);
18635 require_action( totalLen
<= kDNSRecordDataLengthMax
, exit
, err
= kSizeErr
);
18637 dataLen
= totalLen
;
18638 dataPtr
= (uint8_t *) malloc( dataLen
);
18639 require_action( dataPtr
, exit
, err
= kNoMemoryErr
);
18641 success
= _ParseQuotedEscapedString( str
, end
, "", (char *) dataPtr
, dataLen
, &copiedLen
, NULL
, NULL
);
18642 require_action( success
, exit
, err
= kParamErr
);
18643 check( copiedLen
== dataLen
);
18654 else if( stricmp_prefix( inString
, kRDataArgPrefix_TXT
) == 0 )
18656 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_TXT
);
18658 err
= CreateTXTRecordDataFromString( str
, ',', &dataPtr
, &dataLen
);
18659 require_noerr( err
, exit
);
18662 // Unrecognized format
18666 FPrintF( stderr
, "Unrecognized record data string \"%s\".\n", inString
);
18672 *outDataLen
= dataLen
;
18673 *outDataPtr
= dataPtr
;
18677 FreeNullSafe( dataPtr
);
18681 //===========================================================================================================================
18682 // RecordTypeFromArgString
18683 //===========================================================================================================================
18687 uint16_t value
; // Record type's numeric value.
18688 const char * name
; // Record type's name as a string (e.g., "A", "PTR", "SRV").
18692 static const RecordType kRecordTypes
[] =
18696 { kDNSServiceType_A
, "A" },
18697 { kDNSServiceType_AAAA
, "AAAA" },
18698 { kDNSServiceType_PTR
, "PTR" },
18699 { kDNSServiceType_SRV
, "SRV" },
18700 { kDNSServiceType_TXT
, "TXT" },
18701 { kDNSServiceType_CNAME
, "CNAME" },
18702 { kDNSServiceType_SOA
, "SOA" },
18703 { kDNSServiceType_NSEC
, "NSEC" },
18704 { kDNSServiceType_NS
, "NS" },
18705 { kDNSServiceType_MX
, "MX" },
18706 { kDNSServiceType_ANY
, "ANY" },
18707 { kDNSServiceType_OPT
, "OPT" },
18709 // Less common types.
18711 { kDNSServiceType_MD
, "MD" },
18712 { kDNSServiceType_NS
, "NS" },
18713 { kDNSServiceType_MD
, "MD" },
18714 { kDNSServiceType_MF
, "MF" },
18715 { kDNSServiceType_MB
, "MB" },
18716 { kDNSServiceType_MG
, "MG" },
18717 { kDNSServiceType_MR
, "MR" },
18718 { kDNSServiceType_NULL
, "NULL" },
18719 { kDNSServiceType_WKS
, "WKS" },
18720 { kDNSServiceType_HINFO
, "HINFO" },
18721 { kDNSServiceType_MINFO
, "MINFO" },
18722 { kDNSServiceType_RP
, "RP" },
18723 { kDNSServiceType_AFSDB
, "AFSDB" },
18724 { kDNSServiceType_X25
, "X25" },
18725 { kDNSServiceType_ISDN
, "ISDN" },
18726 { kDNSServiceType_RT
, "RT" },
18727 { kDNSServiceType_NSAP
, "NSAP" },
18728 { kDNSServiceType_NSAP_PTR
, "NSAP_PTR" },
18729 { kDNSServiceType_SIG
, "SIG" },
18730 { kDNSServiceType_KEY
, "KEY" },
18731 { kDNSServiceType_PX
, "PX" },
18732 { kDNSServiceType_GPOS
, "GPOS" },
18733 { kDNSServiceType_LOC
, "LOC" },
18734 { kDNSServiceType_NXT
, "NXT" },
18735 { kDNSServiceType_EID
, "EID" },
18736 { kDNSServiceType_NIMLOC
, "NIMLOC" },
18737 { kDNSServiceType_ATMA
, "ATMA" },
18738 { kDNSServiceType_NAPTR
, "NAPTR" },
18739 { kDNSServiceType_KX
, "KX" },
18740 { kDNSServiceType_CERT
, "CERT" },
18741 { kDNSServiceType_A6
, "A6" },
18742 { kDNSServiceType_DNAME
, "DNAME" },
18743 { kDNSServiceType_SINK
, "SINK" },
18744 { kDNSServiceType_APL
, "APL" },
18745 { kDNSServiceType_DS
, "DS" },
18746 { kDNSServiceType_SSHFP
, "SSHFP" },
18747 { kDNSServiceType_IPSECKEY
, "IPSECKEY" },
18748 { kDNSServiceType_RRSIG
, "RRSIG" },
18749 { kDNSServiceType_DNSKEY
, "DNSKEY" },
18750 { kDNSServiceType_DHCID
, "DHCID" },
18751 { kDNSServiceType_NSEC3
, "NSEC3" },
18752 { kDNSServiceType_NSEC3PARAM
, "NSEC3PARAM" },
18753 { kDNSServiceType_HIP
, "HIP" },
18754 { kDNSServiceType_SPF
, "SPF" },
18755 { kDNSServiceType_UINFO
, "UINFO" },
18756 { kDNSServiceType_UID
, "UID" },
18757 { kDNSServiceType_GID
, "GID" },
18758 { kDNSServiceType_UNSPEC
, "UNSPEC" },
18759 { kDNSServiceType_TKEY
, "TKEY" },
18760 { kDNSServiceType_TSIG
, "TSIG" },
18761 { kDNSServiceType_IXFR
, "IXFR" },
18762 { kDNSServiceType_AXFR
, "AXFR" },
18763 { kDNSServiceType_MAILB
, "MAILB" },
18764 { kDNSServiceType_MAILA
, "MAILA" }
18767 static OSStatus
RecordTypeFromArgString( const char *inString
, uint16_t *outValue
)
18771 const RecordType
* type
;
18772 const RecordType
* const end
= kRecordTypes
+ countof( kRecordTypes
);
18774 for( type
= kRecordTypes
; type
< end
; ++type
)
18776 if( strcasecmp( type
->name
, inString
) == 0 )
18778 *outValue
= type
->value
;
18783 err
= StringToInt32( inString
, &i32
);
18784 require_noerr_quiet( err
, exit
);
18785 require_action_quiet( ( i32
>= 0 ) && ( i32
<= UINT16_MAX
), exit
, err
= kParamErr
);
18787 *outValue
= (uint16_t) i32
;
18793 //===========================================================================================================================
18794 // RecordClassFromArgString
18795 //===========================================================================================================================
18797 static OSStatus
RecordClassFromArgString( const char *inString
, uint16_t *outValue
)
18802 if( strcasecmp( inString
, "IN" ) == 0 )
18804 *outValue
= kDNSServiceClass_IN
;
18809 err
= StringToInt32( inString
, &i32
);
18810 require_noerr_quiet( err
, exit
);
18811 require_action_quiet( ( i32
>= 0 ) && ( i32
<= UINT16_MAX
), exit
, err
= kParamErr
);
18813 *outValue
= (uint16_t) i32
;
18819 //===========================================================================================================================
18820 // InterfaceIndexToName
18821 //===========================================================================================================================
18823 static char * InterfaceIndexToName( uint32_t inIfIndex
, char inNameBuf
[ kInterfaceNameBufLen
] )
18825 switch( inIfIndex
)
18827 case kDNSServiceInterfaceIndexAny
:
18828 strlcpy( inNameBuf
, "Any", kInterfaceNameBufLen
);
18831 case kDNSServiceInterfaceIndexLocalOnly
:
18832 strlcpy( inNameBuf
, "LocalOnly", kInterfaceNameBufLen
);
18835 case kDNSServiceInterfaceIndexUnicast
:
18836 strlcpy( inNameBuf
, "Unicast", kInterfaceNameBufLen
);
18839 case kDNSServiceInterfaceIndexP2P
:
18840 strlcpy( inNameBuf
, "P2P", kInterfaceNameBufLen
);
18843 #if( defined( kDNSServiceInterfaceIndexBLE ) )
18844 case kDNSServiceInterfaceIndexBLE
:
18845 strlcpy( inNameBuf
, "BLE", kInterfaceNameBufLen
);
18853 name
= if_indextoname( inIfIndex
, inNameBuf
);
18854 if( !name
) strlcpy( inNameBuf
, "NO NAME", kInterfaceNameBufLen
);
18859 return( inNameBuf
);
18862 //===========================================================================================================================
18863 // RecordTypeToString
18864 //===========================================================================================================================
18866 static const char * RecordTypeToString( unsigned int inValue
)
18868 const RecordType
* type
;
18869 const RecordType
* const end
= kRecordTypes
+ countof( kRecordTypes
);
18871 for( type
= kRecordTypes
; type
< end
; ++type
)
18873 if( type
->value
== inValue
) return( type
->name
);
18878 //===========================================================================================================================
18879 // DNSRecordDataToString
18880 //===========================================================================================================================
18883 DNSRecordDataToString(
18884 const void * inRDataPtr
,
18886 unsigned int inRDataType
,
18887 const void * inMsgPtr
,
18889 char ** outString
)
18892 const uint8_t * const rdataPtr
= (uint8_t *) inRDataPtr
;
18893 const uint8_t * const rdataEnd
= rdataPtr
+ inRDataLen
;
18895 const uint8_t * ptr
;
18897 char domainNameStr
[ kDNSServiceMaxDomainName
];
18903 if( inRDataType
== kDNSServiceType_A
)
18905 require_action_quiet( inRDataLen
== 4, exit
, err
= kMalformedErr
);
18907 ASPrintF( &rdataStr
, "%.4a", rdataPtr
);
18908 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
18913 else if( inRDataType
== kDNSServiceType_AAAA
)
18915 require_action_quiet( inRDataLen
== 16, exit
, err
= kMalformedErr
);
18917 ASPrintF( &rdataStr
, "%.16a", rdataPtr
);
18918 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
18921 // PTR, CNAME, or NS Record
18923 else if( ( inRDataType
== kDNSServiceType_PTR
) ||
18924 ( inRDataType
== kDNSServiceType_CNAME
) ||
18925 ( inRDataType
== kDNSServiceType_NS
) )
18929 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, NULL
);
18930 require_noerr( err
, exit
);
18934 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, NULL
);
18935 require_noerr( err
, exit
);
18938 rdataStr
= strdup( domainNameStr
);
18939 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
18944 else if( inRDataType
== kDNSServiceType_SRV
)
18946 const dns_fixed_fields_srv
* fields
;
18947 const uint8_t * target
;
18948 unsigned int priority
, weight
, port
;
18950 require_action_quiet( inRDataLen
> sizeof( dns_fixed_fields_srv
), exit
, err
= kMalformedErr
);
18952 fields
= (const dns_fixed_fields_srv
*) rdataPtr
;
18953 priority
= dns_fixed_fields_srv_get_priority( fields
);
18954 weight
= dns_fixed_fields_srv_get_weight( fields
);
18955 port
= dns_fixed_fields_srv_get_port( fields
);
18956 target
= (const uint8_t *) &fields
[ 1 ];
18960 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, target
, domainNameStr
, NULL
);
18961 require_noerr( err
, exit
);
18965 err
= DomainNameToString( target
, rdataEnd
, domainNameStr
, NULL
);
18966 require_noerr( err
, exit
);
18969 ASPrintF( &rdataStr
, "%u %u %u %s", priority
, weight
, port
, domainNameStr
);
18970 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
18975 else if( inRDataType
== kDNSServiceType_TXT
)
18977 require_action_quiet( inRDataLen
> 0, exit
, err
= kMalformedErr
);
18979 if( inRDataLen
== 1 )
18981 ASPrintF( &rdataStr
, "%#H", rdataPtr
, (int) inRDataLen
, INT_MAX
);
18982 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
18986 ASPrintF( &rdataStr
, "%#{txt}", rdataPtr
, inRDataLen
);
18987 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
18993 else if( inRDataType
== kDNSServiceType_SOA
)
18995 const dns_fixed_fields_soa
* fields
;
18996 uint32_t serial
, refresh
, retry
, expire
, minimum
;
19000 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, &ptr
);
19001 require_noerr( err
, exit
);
19003 require_action_quiet( ptr
< rdataEnd
, exit
, err
= kMalformedErr
);
19005 rdataStr
= strdup( domainNameStr
);
19006 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
19008 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, ptr
, domainNameStr
, &ptr
);
19009 require_noerr( err
, exit
);
19013 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, &ptr
);
19014 require_noerr( err
, exit
);
19016 rdataStr
= strdup( domainNameStr
);
19017 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
19019 err
= DomainNameToString( ptr
, rdataEnd
, domainNameStr
, &ptr
);
19020 require_noerr( err
, exit
);
19023 require_action_quiet( ( rdataEnd
- ptr
) == sizeof( dns_fixed_fields_soa
), exit
, err
= kMalformedErr
);
19025 fields
= (const dns_fixed_fields_soa
*) ptr
;
19026 serial
= dns_fixed_fields_soa_get_serial( fields
);
19027 refresh
= dns_fixed_fields_soa_get_refresh( fields
);
19028 retry
= dns_fixed_fields_soa_get_retry( fields
);
19029 expire
= dns_fixed_fields_soa_get_expire( fields
);
19030 minimum
= dns_fixed_fields_soa_get_minimum( fields
);
19032 n
= AppendPrintF( &rdataStr
, " %s %u %u %u %u %u\n", domainNameStr
, serial
, refresh
, retry
, expire
, minimum
);
19033 require_action( n
> 0, exit
, err
= kUnknownErr
);
19038 else if( inRDataType
== kDNSServiceType_NSEC
)
19040 unsigned int windowBlock
, bitmapLen
, i
, recordType
;
19041 const uint8_t * bitmapPtr
;
19045 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, &ptr
);
19046 require_noerr( err
, exit
);
19050 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, &ptr
);
19051 require_noerr( err
, exit
);
19054 require_action_quiet( ptr
< rdataEnd
, exit
, err
= kMalformedErr
);
19056 rdataStr
= strdup( domainNameStr
);
19057 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
19059 for( ; ptr
< rdataEnd
; ptr
+= ( 2 + bitmapLen
) )
19061 require_action_quiet( ( ptr
+ 2 ) < rdataEnd
, exit
, err
= kMalformedErr
);
19063 windowBlock
= ptr
[ 0 ];
19064 bitmapLen
= ptr
[ 1 ];
19065 bitmapPtr
= &ptr
[ 2 ];
19067 require_action_quiet( ( bitmapLen
>= 1 ) && ( bitmapLen
<= 32 ) , exit
, err
= kMalformedErr
);
19068 require_action_quiet( ( bitmapPtr
+ bitmapLen
) <= rdataEnd
, exit
, err
= kMalformedErr
);
19070 for( i
= 0; i
< BitArray_MaxBits( bitmapLen
); ++i
)
19072 if( BitArray_GetBit( bitmapPtr
, bitmapLen
, i
) )
19074 recordType
= ( windowBlock
* 256 ) + i
;
19075 n
= AppendPrintF( &rdataStr
, " %s", RecordTypeToString( recordType
) );
19076 require_action( n
> 0, exit
, err
= kUnknownErr
);
19084 else if( inRDataType
== kDNSServiceType_MX
)
19086 uint16_t preference
;
19087 const uint8_t * exchange
;
19089 require_action_quiet( ( rdataPtr
+ 2 ) < rdataEnd
, exit
, err
= kMalformedErr
);
19091 preference
= ReadBig16( rdataPtr
);
19092 exchange
= &rdataPtr
[ 2 ];
19096 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, exchange
, domainNameStr
, NULL
);
19097 require_noerr( err
, exit
);
19101 err
= DomainNameToString( exchange
, rdataEnd
, domainNameStr
, NULL
);
19102 require_noerr( err
, exit
);
19105 n
= ASPrintF( &rdataStr
, "%u %s", preference
, domainNameStr
);
19106 require_action( n
> 0, exit
, err
= kUnknownErr
);
19109 // Unhandled record type
19113 err
= kNotHandledErr
;
19118 *outString
= rdataStr
;
19123 FreeNullSafe( rdataStr
);
19127 //===========================================================================================================================
19128 // DNSMessageToText
19129 //===========================================================================================================================
19131 #define DNSFlagsOpCodeToString( X ) ( \
19132 ( (X) == kDNSOpCode_Query ) ? "Query" : \
19133 ( (X) == kDNSOpCode_InverseQuery ) ? "IQuery" : \
19134 ( (X) == kDNSOpCode_Status ) ? "Status" : \
19135 ( (X) == kDNSOpCode_Notify ) ? "Notify" : \
19136 ( (X) == kDNSOpCode_Update ) ? "Update" : \
19139 #define DNSFlagsRCodeToString( X ) ( \
19140 ( (X) == kDNSRCode_NoError ) ? "NoError" : \
19141 ( (X) == kDNSRCode_FormatError ) ? "FormErr" : \
19142 ( (X) == kDNSRCode_ServerFailure ) ? "ServFail" : \
19143 ( (X) == kDNSRCode_NXDomain ) ? "NXDomain" : \
19144 ( (X) == kDNSRCode_NotImplemented ) ? "NotImp" : \
19145 ( (X) == kDNSRCode_Refused ) ? "Refused" : \
19150 const uint8_t * inMsgPtr
,
19152 const Boolean inMDNS
,
19153 const Boolean inPrintRaw
,
19157 DataBuffer dataBuf
;
19159 const DNSHeader
* hdr
;
19160 const uint8_t * ptr
;
19161 unsigned int id
, flags
, opcode
, rcode
;
19162 unsigned int questionCount
, answerCount
, authorityCount
, additionalCount
, i
, totalRRCount
;
19163 uint8_t name
[ kDomainNameLengthMax
];
19164 char nameStr
[ kDNSServiceMaxDomainName
];
19166 DataBuffer_Init( &dataBuf
, NULL
, 0, SIZE_MAX
);
19167 #define _Append( ... ) do { err = DataBuffer_AppendF( &dataBuf, __VA_ARGS__ ); require_noerr( err, exit ); } while( 0 )
19169 require_action_quiet( inMsgLen
>= kDNSHeaderLength
, exit
, err
= kSizeErr
);
19171 hdr
= (DNSHeader
*) inMsgPtr
;
19172 id
= DNSHeaderGetID( hdr
);
19173 flags
= DNSHeaderGetFlags( hdr
);
19174 questionCount
= DNSHeaderGetQuestionCount( hdr
);
19175 answerCount
= DNSHeaderGetAnswerCount( hdr
);
19176 authorityCount
= DNSHeaderGetAuthorityCount( hdr
);
19177 additionalCount
= DNSHeaderGetAdditionalCount( hdr
);
19178 opcode
= DNSFlagsGetOpCode( flags
);
19179 rcode
= DNSFlagsGetRCode( flags
);
19181 _Append( "ID: 0x%04X (%u)\n", id
, id
);
19182 _Append( "Flags: 0x%04X %c/%s %cAA%cTC%cRD%cRA%?s%?s %s\n",
19184 ( flags
& kDNSHeaderFlag_Response
) ? 'R' : 'Q', DNSFlagsOpCodeToString( opcode
),
19185 ( flags
& kDNSHeaderFlag_AuthAnswer
) ? ' ' : '!',
19186 ( flags
& kDNSHeaderFlag_Truncation
) ? ' ' : '!',
19187 ( flags
& kDNSHeaderFlag_RecursionDesired
) ? ' ' : '!',
19188 ( flags
& kDNSHeaderFlag_RecursionAvailable
) ? ' ' : '!',
19189 !inMDNS
, ( flags
& kDNSHeaderFlag_AuthenticData
) ? " AD" : "!AD",
19190 !inMDNS
, ( flags
& kDNSHeaderFlag_CheckingDisabled
) ? " CD" : "!CD",
19191 DNSFlagsRCodeToString( rcode
) );
19192 _Append( "Question count: %u\n", questionCount
);
19193 _Append( "Answer count: %u\n", answerCount
);
19194 _Append( "Authority count: %u\n", authorityCount
);
19195 _Append( "Additional count: %u\n", additionalCount
);
19197 ptr
= (const uint8_t *) &hdr
[ 1 ];
19198 for( i
= 0; i
< questionCount
; ++i
)
19200 uint16_t qtype
, qclass
;
19203 err
= DNSMessageExtractQuestion( inMsgPtr
, inMsgLen
, ptr
, name
, &qtype
, &qclass
, &ptr
);
19204 require_noerr( err
, exit
);
19206 err
= DomainNameToString( name
, NULL
, nameStr
, NULL
);
19207 require_noerr( err
, exit
);
19209 isQU
= ( inMDNS
&& ( qclass
& kQClassUnicastResponseBit
) ) ? true : false;
19210 if( inMDNS
) qclass
&= ~kQClassUnicastResponseBit
;
19212 if( i
== 0 ) _Append( "\nQUESTION SECTION\n" );
19214 _Append( "%-30s %2s %?2s%?2u %-5s\n",
19215 nameStr
, inMDNS
? ( isQU
? "QU" : "QM" ) : "",
19216 ( qclass
== kDNSServiceClass_IN
), "IN", ( qclass
!= kDNSServiceClass_IN
), qclass
, RecordTypeToString( qtype
) );
19219 totalRRCount
= answerCount
+ authorityCount
+ additionalCount
;
19220 for( i
= 0; i
< totalRRCount
; ++i
)
19225 const uint8_t * rdataPtr
;
19228 Boolean cacheFlush
;
19230 err
= DNSMessageExtractRecord( inMsgPtr
, inMsgLen
, ptr
, name
, &type
, &class, &ttl
, &rdataPtr
, &rdataLen
, &ptr
);
19231 require_noerr( err
, exit
);
19233 err
= DomainNameToString( name
, NULL
, nameStr
, NULL
);
19234 require_noerr( err
, exit
);
19236 cacheFlush
= ( inMDNS
&& ( class & kRRClassCacheFlushBit
) ) ? true : false;
19237 if( inMDNS
) class &= ~kRRClassCacheFlushBit
;
19240 if( !inPrintRaw
) DNSRecordDataToString( rdataPtr
, rdataLen
, type
, inMsgPtr
, inMsgLen
, &rdataStr
);
19243 ASPrintF( &rdataStr
, "%#H", rdataPtr
, (int) rdataLen
, (int) rdataLen
);
19244 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
19247 if( answerCount
&& ( i
== 0 ) ) _Append( "\nANSWER SECTION\n" );
19248 else if( authorityCount
&& ( i
== answerCount
) ) _Append( "\nAUTHORITY SECTION\n" );
19249 else if( additionalCount
&& ( i
== ( answerCount
+ authorityCount
) ) ) _Append( "\nADDITIONAL SECTION\n" );
19251 _Append( "%-42s %6u %2s %?2s%?2u %-5s %s\n",
19252 nameStr
, ttl
, cacheFlush
? "CF" : "",
19253 ( class == kDNSServiceClass_IN
), "IN", ( class != kDNSServiceClass_IN
), class,
19254 RecordTypeToString( type
), rdataStr
);
19259 err
= DataBuffer_Append( &dataBuf
, "", 1 );
19260 require_noerr( err
, exit
);
19262 err
= DataBuffer_Detach( &dataBuf
, (uint8_t **) outText
, &len
);
19263 require_noerr( err
, exit
);
19266 DataBuffer_Free( &dataBuf
);
19270 //===========================================================================================================================
19271 // WriteDNSQueryMessage
19272 //===========================================================================================================================
19275 WriteDNSQueryMessage(
19276 uint8_t inMsg
[ kDNSQueryMessageMaxLen
],
19279 const char * inQName
,
19282 size_t * outMsgLen
)
19285 uint8_t qname
[ kDomainNameLengthMax
];
19287 err
= DomainNameFromString( qname
, inQName
, NULL
);
19288 require_noerr_quiet( err
, exit
);
19290 err
= DNSMessageWriteQuery( inMsgID
, inFlags
, qname
, inQType
, inQClass
, inMsg
, outMsgLen
);
19291 require_noerr_quiet( err
, exit
);
19297 //===========================================================================================================================
19298 // DispatchSignalSourceCreate
19299 //===========================================================================================================================
19302 DispatchSignalSourceCreate(
19304 DispatchHandler inEventHandler
,
19306 dispatch_source_t
* outSource
)
19309 dispatch_source_t source
;
19311 source
= dispatch_source_create( DISPATCH_SOURCE_TYPE_SIGNAL
, (uintptr_t) inSignal
, 0, dispatch_get_main_queue() );
19312 require_action( source
, exit
, err
= kUnknownErr
);
19314 dispatch_set_context( source
, inContext
);
19315 dispatch_source_set_event_handler_f( source
, inEventHandler
);
19317 *outSource
= source
;
19324 //===========================================================================================================================
19325 // DispatchSocketSourceCreate
19326 //===========================================================================================================================
19329 DispatchSocketSourceCreate(
19331 dispatch_source_type_t inType
,
19332 dispatch_queue_t inQueue
,
19333 DispatchHandler inEventHandler
,
19334 DispatchHandler inCancelHandler
,
19336 dispatch_source_t
* outSource
)
19339 dispatch_source_t source
;
19341 source
= dispatch_source_create( inType
, (uintptr_t) inSock
, 0, inQueue
? inQueue
: dispatch_get_main_queue() );
19342 require_action( source
, exit
, err
= kUnknownErr
);
19344 dispatch_set_context( source
, inContext
);
19345 dispatch_source_set_event_handler_f( source
, inEventHandler
);
19346 dispatch_source_set_cancel_handler_f( source
, inCancelHandler
);
19348 *outSource
= source
;
19355 //===========================================================================================================================
19356 // DispatchTimerCreate
19357 //===========================================================================================================================
19360 DispatchTimerCreate(
19361 dispatch_time_t inStart
,
19362 uint64_t inIntervalNs
,
19363 uint64_t inLeewayNs
,
19364 dispatch_queue_t inQueue
,
19365 DispatchHandler inEventHandler
,
19366 DispatchHandler inCancelHandler
,
19368 dispatch_source_t
* outTimer
)
19371 dispatch_source_t timer
;
19373 timer
= dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, inQueue
? inQueue
: dispatch_get_main_queue() );
19374 require_action( timer
, exit
, err
= kUnknownErr
);
19376 dispatch_source_set_timer( timer
, inStart
, inIntervalNs
, inLeewayNs
);
19377 dispatch_set_context( timer
, inContext
);
19378 dispatch_source_set_event_handler_f( timer
, inEventHandler
);
19379 dispatch_source_set_cancel_handler_f( timer
, inCancelHandler
);
19388 //===========================================================================================================================
19389 // DispatchProcessMonitorCreate
19390 //===========================================================================================================================
19393 DispatchProcessMonitorCreate(
19395 unsigned long inFlags
,
19396 dispatch_queue_t inQueue
,
19397 DispatchHandler inEventHandler
,
19398 DispatchHandler inCancelHandler
,
19400 dispatch_source_t
* outMonitor
)
19403 dispatch_source_t monitor
;
19405 monitor
= dispatch_source_create( DISPATCH_SOURCE_TYPE_PROC
, (uintptr_t) inPID
, inFlags
,
19406 inQueue
? inQueue
: dispatch_get_main_queue() );
19407 require_action( monitor
, exit
, err
= kUnknownErr
);
19409 dispatch_set_context( monitor
, inContext
);
19410 dispatch_source_set_event_handler_f( monitor
, inEventHandler
);
19411 dispatch_source_set_cancel_handler_f( monitor
, inCancelHandler
);
19413 *outMonitor
= monitor
;
19420 //===========================================================================================================================
19421 // ServiceTypeDescription
19422 //===========================================================================================================================
19426 const char * name
; // Name of the service type in two-label "_service._proto" format.
19427 const char * description
; // Description of the service type.
19431 // A Non-comprehensive table of DNS-SD service types
19433 static const ServiceType kServiceTypes
[] =
19435 { "_acp-sync._tcp", "AirPort Base Station Sync" },
19436 { "_adisk._tcp", "Automatic Disk Discovery" },
19437 { "_afpovertcp._tcp", "Apple File Sharing" },
19438 { "_airdrop._tcp", "AirDrop" },
19439 { "_airplay._tcp", "AirPlay" },
19440 { "_airport._tcp", "AirPort Base Station" },
19441 { "_daap._tcp", "Digital Audio Access Protocol (iTunes)" },
19442 { "_eppc._tcp", "Remote AppleEvents" },
19443 { "_ftp._tcp", "File Transfer Protocol" },
19444 { "_home-sharing._tcp", "Home Sharing" },
19445 { "_homekit._tcp", "HomeKit" },
19446 { "_http._tcp", "World Wide Web HTML-over-HTTP" },
19447 { "_https._tcp", "HTTP over SSL/TLS" },
19448 { "_ipp._tcp", "Internet Printing Protocol" },
19449 { "_ldap._tcp", "Lightweight Directory Access Protocol" },
19450 { "_mediaremotetv._tcp", "Media Remote" },
19451 { "_net-assistant._tcp", "Apple Remote Desktop" },
19452 { "_od-master._tcp", "OpenDirectory Master" },
19453 { "_nfs._tcp", "Network File System" },
19454 { "_presence._tcp", "Peer-to-peer messaging / Link-Local Messaging" },
19455 { "_pdl-datastream._tcp", "Printer Page Description Language Data Stream" },
19456 { "_raop._tcp", "Remote Audio Output Protocol" },
19457 { "_rfb._tcp", "Remote Frame Buffer" },
19458 { "_scanner._tcp", "Bonjour Scanning" },
19459 { "_smb._tcp", "Server Message Block over TCP/IP" },
19460 { "_sftp-ssh._tcp", "Secure File Transfer Protocol over SSH" },
19461 { "_sleep-proxy._udp", "Sleep Proxy Server" },
19462 { "_ssh._tcp", "SSH Remote Login Protocol" },
19463 { "_teleport._tcp", "teleport" },
19464 { "_tftp._tcp", "Trivial File Transfer Protocol" },
19465 { "_workstation._tcp", "Workgroup Manager" },
19466 { "_webdav._tcp", "World Wide Web Distributed Authoring and Versioning (WebDAV)" },
19467 { "_webdavs._tcp", "WebDAV over SSL/TLS" }
19470 static const char * ServiceTypeDescription( const char *inName
)
19472 const ServiceType
* serviceType
;
19473 const ServiceType
* const end
= kServiceTypes
+ countof( kServiceTypes
);
19475 for( serviceType
= kServiceTypes
; serviceType
< end
; ++serviceType
)
19477 if( ( stricmp_prefix( inName
, serviceType
->name
) == 0 ) )
19479 const char * const ptr
= &inName
[ strlen( serviceType
->name
) ];
19481 if( ( ptr
[ 0 ] == '\0' ) || ( ( ptr
[ 0 ] == '.' ) && ( ptr
[ 1 ] == '\0' ) ) )
19483 return( serviceType
->description
);
19490 //===========================================================================================================================
19491 // SocketContextCreate
19492 //===========================================================================================================================
19494 static OSStatus
SocketContextCreate( SocketRef inSock
, void * inUserContext
, SocketContext
**outContext
)
19497 SocketContext
* context
;
19499 context
= (SocketContext
*) calloc( 1, sizeof( *context
) );
19500 require_action( context
, exit
, err
= kNoMemoryErr
);
19502 context
->refCount
= 1;
19503 context
->sock
= inSock
;
19504 context
->userContext
= inUserContext
;
19506 *outContext
= context
;
19513 //===========================================================================================================================
19514 // SocketContextRetain
19515 //===========================================================================================================================
19517 static SocketContext
* SocketContextRetain( SocketContext
*inContext
)
19519 ++inContext
->refCount
;
19520 return( inContext
);
19523 //===========================================================================================================================
19524 // SocketContextRelease
19525 //===========================================================================================================================
19527 static void SocketContextRelease( SocketContext
*inContext
)
19529 if( --inContext
->refCount
== 0 )
19531 ForgetSocket( &inContext
->sock
);
19536 //===========================================================================================================================
19537 // SocketContextCancelHandler
19538 //===========================================================================================================================
19540 static void SocketContextCancelHandler( void *inContext
)
19542 SocketContextRelease( (SocketContext
*) inContext
);
19545 //===========================================================================================================================
19547 //===========================================================================================================================
19549 static OSStatus
StringToInt32( const char *inString
, int32_t *outValue
)
19555 value
= strtol( inString
, &endPtr
, 0 );
19556 require_action_quiet( ( *endPtr
== '\0' ) && ( endPtr
!= inString
), exit
, err
= kParamErr
);
19557 require_action_quiet( ( value
>= INT32_MIN
) && ( value
<= INT32_MAX
), exit
, err
= kRangeErr
);
19559 *outValue
= (int32_t) value
;
19566 //===========================================================================================================================
19568 //===========================================================================================================================
19570 static OSStatus
StringToUInt32( const char *inString
, uint32_t *outValue
)
19576 value
= (uint32_t) strtol( inString
, &endPtr
, 0 );
19577 require_action_quiet( ( *endPtr
== '\0' ) && ( endPtr
!= inString
), exit
, err
= kParamErr
);
19586 #if( TARGET_OS_DARWIN )
19587 //===========================================================================================================================
19589 //===========================================================================================================================
19591 static int64_t _StringToInt64( const char *inString
, OSStatus
*outError
)
19598 set_errno_compat( 0 );
19599 val
= strtoll( inString
, &end
, 0 );
19600 errnoVal
= errno_compat();
19602 require_action_quiet( ( *end
== '\0' ) && ( end
!= inString
), exit
, err
= kMalformedErr
);
19603 require_action_quiet( ( ( val
!= LLONG_MIN
) && ( val
!= LLONG_MAX
) ) || ( errnoVal
!= ERANGE
), exit
, err
= kRangeErr
);
19604 require_action_quiet( ( val
>= INT64_MIN
) && ( val
<= INT64_MAX
), exit
, err
= kRangeErr
);
19608 if( outError
) *outError
= err
;
19609 return( (int64_t)val
);
19612 //===========================================================================================================================
19614 //===========================================================================================================================
19616 static uint64_t _StringToUInt64( const char *inString
, OSStatus
*outError
)
19619 unsigned long long val
;
19623 set_errno_compat( 0 );
19624 val
= strtoull( inString
, &end
, 0 );
19625 errnoVal
= errno_compat();
19627 require_action_quiet( ( *end
== '\0' ) && ( end
!= inString
), exit
, err
= kMalformedErr
);
19628 require_action_quiet( ( val
!= ULLONG_MAX
) || ( errnoVal
!= ERANGE
), exit
, err
= kRangeErr
);
19629 require_action_quiet( val
<= UINT64_MAX
, exit
, err
= kRangeErr
);
19633 if( outError
) *outError
= err
;
19634 return( (uint64_t)val
);
19637 //===========================================================================================================================
19639 //===========================================================================================================================
19641 static pid_t
_StringToPID( const char *inString
, OSStatus
*outError
)
19646 val
= _StringToInt64( inString
, &err
);
19647 require_noerr_quiet( err
, exit
);
19648 require_action_quiet( val
== (pid_t
) val
, exit
, err
= kRangeErr
);
19652 if( outError
) *outError
= err
;
19653 return( (pid_t
) val
);
19656 //===========================================================================================================================
19657 // _ParseEscapedString
19659 // Note: Similar to ParseEscapedString() from CoreUtils except that _ParseEscapedString() takes an optional C string
19660 // containing delimiter characters instead of being limited to one delimiter character. Also, when the function returns
19661 // due to a delimiter, the output pointer is set to the delimiter character instead of the character after the delimiter.
19662 //===========================================================================================================================
19665 _ParseEscapedString(
19666 const char * inSrc
,
19667 const char * inEnd
,
19668 const char * inDelimiters
,
19671 size_t * outCopiedLen
,
19672 size_t * outActualLen
,
19673 const char ** outPtr
)
19677 char * dst
= inBufPtr
;
19678 const char * const lim
= ( inBufLen
> 0 ) ? &inBufPtr
[ inBufLen
- 1 ] : inBufPtr
;
19683 if( !inDelimiters
) inDelimiters
= "";
19684 while( ptr
< inEnd
)
19690 for( del
= inDelimiters
; ( *del
!= '\0' ) && ( c
!= *del
); ++del
) {}
19691 if( *del
!= '\0' ) break;
19695 require_action_quiet( ptr
< inEnd
, exit
, err
= kUnderrunErr
);
19699 if( dst
< lim
) *dst
++ = (char) c
;
19701 if( inBufLen
> 0 ) *dst
= '\0';
19703 if( outCopiedLen
) *outCopiedLen
= (size_t)( dst
- inBufPtr
);
19704 if( outActualLen
) *outActualLen
= len
;
19705 if( outPtr
) *outPtr
= ptr
;
19713 //===========================================================================================================================
19714 // StringToARecordData
19715 //===========================================================================================================================
19717 static OSStatus
StringToARecordData( const char *inString
, uint8_t **outPtr
, size_t *outLen
)
19720 uint32_t * addrPtr
;
19721 const size_t addrLen
= sizeof( *addrPtr
);
19724 addrPtr
= (uint32_t *) malloc( addrLen
);
19725 require_action( addrPtr
, exit
, err
= kNoMemoryErr
);
19727 err
= _StringToIPv4Address( inString
, kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
, addrPtr
,
19728 NULL
, NULL
, NULL
, &end
);
19729 if( !err
&& ( *end
!= '\0' ) ) err
= kMalformedErr
;
19730 require_noerr_quiet( err
, exit
);
19732 *addrPtr
= HostToBig32( *addrPtr
);
19734 *outPtr
= (uint8_t *) addrPtr
;
19739 FreeNullSafe( addrPtr
);
19743 //===========================================================================================================================
19744 // StringToAAAARecordData
19745 //===========================================================================================================================
19747 static OSStatus
StringToAAAARecordData( const char *inString
, uint8_t **outPtr
, size_t *outLen
)
19751 const size_t addrLen
= 16;
19754 addrPtr
= (uint8_t *) malloc( addrLen
);
19755 require_action( addrPtr
, exit
, err
= kNoMemoryErr
);
19757 err
= _StringToIPv6Address( inString
,
19758 kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
| kStringToIPAddressFlagsNoScope
,
19759 addrPtr
, NULL
, NULL
, NULL
, &end
);
19760 if( !err
&& ( *end
!= '\0' ) ) err
= kMalformedErr
;
19761 require_noerr_quiet( err
, exit
);
19768 FreeNullSafe( addrPtr
);
19772 //===========================================================================================================================
19773 // StringToDomainName
19774 //===========================================================================================================================
19776 static OSStatus
StringToDomainName( const char *inString
, uint8_t **outPtr
, size_t *outLen
)
19782 uint8_t nameBuf
[ kDomainNameLengthMax
];
19784 err
= DomainNameFromString( nameBuf
, inString
, &end
);
19785 require_noerr_quiet( err
, exit
);
19787 nameLen
= (size_t)( end
- nameBuf
);
19788 namePtr
= _memdup( nameBuf
, nameLen
);
19789 require_action( namePtr
, exit
, err
= kNoMemoryErr
);
19793 if( outLen
) *outLen
= nameLen
;
19799 #if( TARGET_OS_DARWIN )
19800 //===========================================================================================================================
19801 // GetDefaultDNSServer
19802 //===========================================================================================================================
19804 static OSStatus
GetDefaultDNSServer( sockaddr_ip
*outAddr
)
19807 dns_config_t
* config
;
19808 struct sockaddr
* addr
;
19811 config
= dns_configuration_copy();
19812 require_action( config
, exit
, err
= kUnknownErr
);
19815 for( i
= 0; i
< config
->n_resolver
; ++i
)
19817 const dns_resolver_t
* const resolver
= config
->resolver
[ i
];
19819 if( !resolver
->domain
&& ( resolver
->n_nameserver
> 0 ) )
19821 addr
= resolver
->nameserver
[ 0 ];
19825 require_action_quiet( addr
, exit
, err
= kNotFoundErr
);
19827 SockAddrCopy( addr
, outAddr
);
19831 if( config
) dns_configuration_free( config
);
19836 //===========================================================================================================================
19837 // GetMDNSMulticastAddrV4
19838 //===========================================================================================================================
19840 static void _MDNSMulticastAddrV4Init( void *inContext
);
19842 static const struct sockaddr
* GetMDNSMulticastAddrV4( void )
19844 static struct sockaddr_in sMDNSMulticastAddrV4
;
19845 static dispatch_once_t sMDNSMulticastAddrV4InitOnce
= 0;
19847 dispatch_once_f( &sMDNSMulticastAddrV4InitOnce
, &sMDNSMulticastAddrV4
, _MDNSMulticastAddrV4Init
);
19848 return( (const struct sockaddr
*) &sMDNSMulticastAddrV4
);
19851 static void _MDNSMulticastAddrV4Init( void *inContext
)
19853 struct sockaddr_in
* const addr
= (struct sockaddr_in
*) inContext
;
19855 memset( addr
, 0, sizeof( *addr
) );
19856 SIN_LEN_SET( addr
);
19857 addr
->sin_family
= AF_INET
;
19858 addr
->sin_port
= htons( kMDNSPort
);
19859 addr
->sin_addr
.s_addr
= htonl( 0xE00000FB ); // The mDNS IPv4 multicast address is 224.0.0.251
19862 //===========================================================================================================================
19863 // GetMDNSMulticastAddrV6
19864 //===========================================================================================================================
19866 static void _MDNSMulticastAddrV6Init( void *inContext
);
19868 static const struct sockaddr
* GetMDNSMulticastAddrV6( void )
19870 static struct sockaddr_in6 sMDNSMulticastAddrV6
;
19871 static dispatch_once_t sMDNSMulticastAddrV6InitOnce
= 0;
19873 dispatch_once_f( &sMDNSMulticastAddrV6InitOnce
, &sMDNSMulticastAddrV6
, _MDNSMulticastAddrV6Init
);
19874 return( (const struct sockaddr
*) &sMDNSMulticastAddrV6
);
19877 static void _MDNSMulticastAddrV6Init( void *inContext
)
19879 struct sockaddr_in6
* const addr
= (struct sockaddr_in6
*) inContext
;
19881 memset( addr
, 0, sizeof( *addr
) );
19882 SIN6_LEN_SET( addr
);
19883 addr
->sin6_family
= AF_INET6
;
19884 addr
->sin6_port
= htons( kMDNSPort
);
19885 addr
->sin6_addr
.s6_addr
[ 0 ] = 0xFF; // The mDNS IPv6 multicast address is FF02::FB.
19886 addr
->sin6_addr
.s6_addr
[ 1 ] = 0x02;
19887 addr
->sin6_addr
.s6_addr
[ 15 ] = 0xFB;
19890 //===========================================================================================================================
19891 // CreateMulticastSocket
19892 //===========================================================================================================================
19895 CreateMulticastSocket(
19896 const struct sockaddr
* inAddr
,
19898 const char * inIfName
,
19899 uint32_t inIfIndex
,
19902 SocketRef
* outSock
)
19905 SocketRef sock
= kInvalidSocketRef
;
19906 const int family
= inAddr
->sa_family
;
19909 require_action_quiet( ( family
== AF_INET
) ||( family
== AF_INET6
), exit
, err
= kUnsupportedErr
);
19911 err
= ServerSocketOpen( family
, SOCK_DGRAM
, IPPROTO_UDP
, inPort
, &port
, kSocketBufferSize_DontSet
, &sock
);
19912 require_noerr_quiet( err
, exit
);
19914 err
= SocketSetMulticastInterface( sock
, inIfName
, inIfIndex
);
19915 require_noerr_quiet( err
, exit
);
19917 if( family
== AF_INET
)
19919 err
= setsockopt( sock
, IPPROTO_IP
, IP_MULTICAST_LOOP
, (char *) &(uint8_t){ 1 }, (socklen_t
) sizeof( uint8_t ) );
19920 err
= map_socket_noerr_errno( sock
, err
);
19921 require_noerr_quiet( err
, exit
);
19925 err
= setsockopt( sock
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, (char *) &(int){ 1 }, (socklen_t
) sizeof( int ) );
19926 err
= map_socket_noerr_errno( sock
, err
);
19927 require_noerr_quiet( err
, exit
);
19932 err
= SocketJoinMulticast( sock
, inAddr
, inIfName
, inIfIndex
);
19933 require_noerr_quiet( err
, exit
);
19936 if( outPort
) *outPort
= port
;
19938 sock
= kInvalidSocketRef
;
19941 ForgetSocket( &sock
);
19945 //===========================================================================================================================
19946 // DecimalTextToUInt32
19947 //===========================================================================================================================
19949 static OSStatus
DecimalTextToUInt32( const char *inSrc
, const char *inEnd
, uint32_t *outValue
, const char **outPtr
)
19953 const char * ptr
= inSrc
;
19955 require_action_quiet( ( ptr
< inEnd
) && isdigit_safe( *ptr
), exit
, err
= kMalformedErr
);
19957 value
= (uint64_t)( *ptr
++ - '0' );
19960 if( ( ptr
< inEnd
) && isdigit_safe( *ptr
) )
19962 err
= kMalformedErr
;
19968 while( ( ptr
< inEnd
) && isdigit_safe( *ptr
) )
19970 value
= ( value
* 10 ) + (uint64_t)( *ptr
++ - '0' );
19971 require_action_quiet( value
<= UINT32_MAX
, exit
, err
= kRangeErr
);
19975 *outValue
= (uint32_t) value
;
19976 if( outPtr
) *outPtr
= ptr
;
19983 //===========================================================================================================================
19984 // CheckIntegerArgument
19985 //===========================================================================================================================
19987 static OSStatus
CheckIntegerArgument( int inArgValue
, const char *inArgName
, int inMin
, int inMax
)
19989 if( ( inArgValue
>= inMin
) && ( inArgValue
<= inMax
) ) return( kNoErr
);
19991 FPrintF( stderr
, "error: Invalid %s: %d. Valid range is [%d, %d].\n", inArgName
, inArgValue
, inMin
, inMax
);
19992 return( kRangeErr
);
19995 //===========================================================================================================================
19996 // CheckDoubleArgument
19997 //===========================================================================================================================
19999 static OSStatus
CheckDoubleArgument( double inArgValue
, const char *inArgName
, double inMin
, double inMax
)
20001 if( ( inArgValue
>= inMin
) && ( inArgValue
<= inMax
) ) return( kNoErr
);
20003 FPrintF( stderr
, "error: Invalid %s: %.1f. Valid range is [%.1f, %.1f].\n", inArgName
, inArgValue
, inMin
, inMax
);
20004 return( kRangeErr
);
20007 //===========================================================================================================================
20009 //===========================================================================================================================
20011 static OSStatus
CheckRootUser( void )
20013 if( geteuid() == 0 ) return( kNoErr
);
20015 FPrintF( stderr
, "error: This command must to be run as root.\n" );
20016 return( kPermissionErr
);
20019 //===========================================================================================================================
20022 // Note: Based on systemf() from CoreUtils framework.
20023 //===========================================================================================================================
20025 extern char ** environ
;
20027 static OSStatus
SpawnCommand( pid_t
*outPID
, const char *inFormat
, ... )
20036 va_start( args
, inFormat
);
20037 VASPrintF( &command
, inFormat
, args
);
20039 require_action( command
, exit
, err
= kUnknownErr
);
20041 argv
[ 0 ] = "/bin/sh";
20043 argv
[ 2 ] = command
;
20045 err
= posix_spawn( &pid
, argv
[ 0 ], NULL
, NULL
, argv
, environ
);
20047 require_noerr_quiet( err
, exit
);
20049 if( outPID
) *outPID
= pid
;
20055 //===========================================================================================================================
20056 // OutputFormatFromArgString
20057 //===========================================================================================================================
20059 static OSStatus
OutputFormatFromArgString( const char *inArgString
, OutputFormatType
*outFormat
)
20062 OutputFormatType format
;
20064 format
= (OutputFormatType
) CLIArgToValue( "format", inArgString
, &err
,
20065 kOutputFormatStr_JSON
, kOutputFormatType_JSON
,
20066 kOutputFormatStr_XML
, kOutputFormatType_XML
,
20067 kOutputFormatStr_Binary
, kOutputFormatType_Binary
,
20069 if( outFormat
) *outFormat
= format
;
20073 //===========================================================================================================================
20074 // OutputPropertyList
20075 //===========================================================================================================================
20077 static OSStatus
OutputPropertyList( CFPropertyListRef inPList
, OutputFormatType inType
, const char *inOutputFilePath
)
20080 CFDataRef results
= NULL
;
20081 FILE * file
= NULL
;
20083 // Convert plist to a specific format.
20087 case kOutputFormatType_JSON
:
20088 results
= CFCreateJSONData( inPList
, kJSONFlags_None
, NULL
);
20089 require_action( results
, exit
, err
= kUnknownErr
);
20092 case kOutputFormatType_XML
:
20093 results
= CFPropertyListCreateData( NULL
, inPList
, kCFPropertyListXMLFormat_v1_0
, 0, NULL
);
20094 require_action( results
, exit
, err
= kUnknownErr
);
20097 case kOutputFormatType_Binary
:
20098 results
= CFPropertyListCreateData( NULL
, inPList
, kCFPropertyListBinaryFormat_v1_0
, 0, NULL
);
20099 require_action( results
, exit
, err
= kUnknownErr
);
20107 // Write formatted results to file or stdout.
20109 if( inOutputFilePath
)
20111 file
= fopen( inOutputFilePath
, "wb" );
20112 err
= map_global_value_errno( file
, file
);
20113 require_noerr( err
, exit
);
20120 err
= WriteANSIFile( file
, CFDataGetBytePtr( results
), (size_t) CFDataGetLength( results
) );
20121 require_noerr_quiet( err
, exit
);
20123 // Write a trailing newline for JSON-formatted results.
20125 if( inType
== kOutputFormatType_JSON
)
20127 err
= WriteANSIFile( file
, "\n", 1 );
20128 require_noerr_quiet( err
, exit
);
20132 if( file
&& ( file
!= stdout
) ) fclose( file
);
20133 CFReleaseNullSafe( results
);
20137 //===========================================================================================================================
20138 // CreateSRVRecordDataFromString
20139 //===========================================================================================================================
20141 static OSStatus
CreateSRVRecordDataFromString( const char *inString
, uint8_t **outPtr
, size_t *outLen
)
20144 DataBuffer dataBuf
;
20148 uint8_t target
[ kDomainNameLengthMax
];
20150 DataBuffer_Init( &dataBuf
, NULL
, 0, ( 3 * 2 ) + kDomainNameLengthMax
);
20152 // Parse and set the priority, weight, and port values (all three are unsigned 16-bit values).
20155 for( i
= 0; i
< 3; ++i
)
20161 value
= strtol( ptr
, &next
, 0 );
20162 require_action_quiet( ( next
!= ptr
) && ( *next
== ',' ), exit
, err
= kMalformedErr
);
20163 require_action_quiet( ( value
>= 0 ) && ( value
<= UINT16_MAX
), exit
, err
= kRangeErr
);
20166 WriteBig16( buf
, value
);
20168 err
= DataBuffer_Append( &dataBuf
, buf
, sizeof( buf
) );
20169 require_noerr( err
, exit
);
20172 // Set the target domain name.
20174 err
= DomainNameFromString( target
, ptr
, &end
);
20175 require_noerr_quiet( err
, exit
);
20177 err
= DataBuffer_Append( &dataBuf
, target
, (size_t)( end
- target
) );
20178 require_noerr( err
, exit
);
20180 err
= DataBuffer_Detach( &dataBuf
, outPtr
, outLen
);
20181 require_noerr( err
, exit
);
20184 DataBuffer_Free( &dataBuf
);
20188 //===========================================================================================================================
20189 // CreateTXTRecordDataFromString
20190 //===========================================================================================================================
20192 static OSStatus
CreateTXTRecordDataFromString(const char *inString
, int inDelimiter
, uint8_t **outPtr
, size_t *outLen
)
20195 DataBuffer dataBuf
;
20197 uint8_t txtStr
[ 256 ]; // Buffer for single TXT string: 1 length byte + up to 255 bytes of data.
20199 DataBuffer_Init( &dataBuf
, NULL
, 0, kDNSRecordDataLengthMax
);
20204 uint8_t * dst
= &txtStr
[ 1 ];
20205 const uint8_t * const lim
= &txtStr
[ 256 ];
20208 while( *src
&& ( *src
!= inDelimiter
) )
20210 if( ( c
= *src
++ ) == '\\' )
20212 require_action_quiet( *src
!= '\0', exit
, err
= kUnderrunErr
);
20215 require_action_quiet( dst
< lim
, exit
, err
= kOverrunErr
);
20216 *dst
++ = (uint8_t) c
;
20218 txtStr
[ 0 ] = (uint8_t)( dst
- &txtStr
[ 1 ] );
20219 err
= DataBuffer_Append( &dataBuf
, txtStr
, 1 + txtStr
[ 0 ] );
20220 require_noerr( err
, exit
);
20222 if( *src
== '\0' ) break;
20226 err
= DataBuffer_Detach( &dataBuf
, outPtr
, outLen
);
20227 require_noerr( err
, exit
);
20230 DataBuffer_Free( &dataBuf
);
20234 //===========================================================================================================================
20235 // CreateNSECRecordData
20236 //===========================================================================================================================
20238 DECLARE_QSORT_NUMERIC_COMPARATOR( _QSortCmpUnsigned
);
20239 DEFINE_QSORT_NUMERIC_COMPARATOR( unsigned int, _QSortCmpUnsigned
)
20241 #define kNSECBitmapMaxLength 32 // 32 bytes (256 bits). See <https://tools.ietf.org/html/rfc4034#section-4.1.2>.
20244 CreateNSECRecordData(
20245 const uint8_t * inNextDomainName
,
20248 unsigned int inTypeCount
,
20253 DataBuffer rdataDB
;
20254 unsigned int * array
= NULL
;
20255 unsigned int i
, type
, maxBit
, currBlock
, bitmapLen
;
20256 uint8_t fields
[ 2 + kNSECBitmapMaxLength
];
20257 uint8_t * const bitmap
= &fields
[ 2 ];
20259 va_start( args
, inTypeCount
);
20260 DataBuffer_Init( &rdataDB
, NULL
, 0, kDNSRecordDataLengthMax
);
20262 // Append Next Domain Name.
20264 err
= DataBuffer_Append( &rdataDB
, inNextDomainName
, DomainNameLength( inNextDomainName
) );
20265 require_noerr( err
, exit
);
20267 // Append Type Bit Maps.
20270 memset( bitmap
, 0, kNSECBitmapMaxLength
);
20271 if( inTypeCount
> 0 )
20273 array
= (unsigned int *) malloc( inTypeCount
* sizeof_element( array
) );
20274 require_action( array
, exit
, err
= kNoMemoryErr
);
20276 for( i
= 0; i
< inTypeCount
; ++i
)
20278 type
= va_arg( args
, unsigned int );
20279 require_action_quiet( type
<= UINT16_MAX
, exit
, err
= kRangeErr
);
20282 qsort( array
, inTypeCount
, sizeof_element( array
), _QSortCmpUnsigned
);
20284 currBlock
= array
[ 0 ] / 256;
20285 for( i
= 0; i
< inTypeCount
; ++i
)
20287 const unsigned int block
= array
[ i
] / 256;
20288 const unsigned int bit
= array
[ i
] % 256;
20290 if( block
!= currBlock
)
20292 bitmapLen
= BitArray_MaxBytes( maxBit
+ 1 );
20293 fields
[ 0 ] = (uint8_t) currBlock
;
20294 fields
[ 1 ] = (uint8_t) bitmapLen
;
20296 err
= DataBuffer_Append( &rdataDB
, fields
, 2 + bitmapLen
);
20297 require_noerr( err
, exit
);
20301 memset( bitmap
, 0, bitmapLen
);
20303 BitArray_SetBit( bitmap
, bit
);
20304 if( bit
> maxBit
) maxBit
= bit
;
20312 bitmapLen
= BitArray_MaxBytes( maxBit
+ 1 );
20313 fields
[ 0 ] = (uint8_t) currBlock
;
20314 fields
[ 1 ] = (uint8_t) bitmapLen
;
20316 err
= DataBuffer_Append( &rdataDB
, fields
, 2 + bitmapLen
);
20317 require_noerr( err
, exit
);
20319 err
= DataBuffer_Detach( &rdataDB
, outPtr
, outLen
);
20320 require_noerr( err
, exit
);
20324 DataBuffer_Free( &rdataDB
);
20325 FreeNullSafe( array
);
20329 //===========================================================================================================================
20331 //===========================================================================================================================
20334 _AppendSOARecordData(
20336 const uint8_t * inMName
,
20337 const uint8_t * inRName
,
20339 uint32_t inRefresh
,
20342 uint32_t inMinimumTTL
,
20348 const uint8_t * inNamePtr
,
20353 const uint8_t * inMName
,
20354 const uint8_t * inRName
,
20356 uint32_t inRefresh
,
20359 uint32_t inMinimumTTL
,
20364 size_t rdlengthOffset
= 0;
20365 uint8_t * rdlengthPtr
;
20369 err
= _DataBuffer_AppendDNSRecord( inDB
, inNamePtr
, inNameLen
, inType
, inClass
, inTTL
, NULL
, 0 );
20370 require_noerr( err
, exit
);
20372 rdlengthOffset
= DataBuffer_GetLen( inDB
) - 2;
20375 err
= _AppendSOARecordData( inDB
, inMName
, inRName
, inSerial
, inRefresh
, inRetry
, inExpire
, inMinimumTTL
, &rdataLen
);
20376 require_noerr( err
, exit
);
20380 rdlengthPtr
= DataBuffer_GetPtr( inDB
) + rdlengthOffset
;
20381 WriteBig16( rdlengthPtr
, rdataLen
);
20384 if( outLen
) *outLen
= inNameLen
+ sizeof( dns_fixed_fields_record
) + rdataLen
;
20392 _AppendSOARecordData(
20394 const uint8_t * inMName
,
20395 const uint8_t * inRName
,
20397 uint32_t inRefresh
,
20400 uint32_t inMinimumTTL
,
20404 dns_fixed_fields_soa fields
;
20405 const size_t mnameLen
= DomainNameLength( inMName
);
20406 const size_t rnameLen
= DomainNameLength( inRName
);
20410 err
= DataBuffer_Append( inDB
, inMName
, mnameLen
);
20411 require_noerr( err
, exit
);
20413 err
= DataBuffer_Append( inDB
, inRName
, rnameLen
);
20414 require_noerr( err
, exit
);
20416 dns_fixed_fields_soa_init( &fields
, inSerial
, inRefresh
, inRetry
, inExpire
, inMinimumTTL
);
20417 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
20418 require_noerr( err
, exit
);
20420 if( outLen
) *outLen
= mnameLen
+ rnameLen
+ sizeof( fields
);
20427 //===========================================================================================================================
20428 // CreateSOARecordData
20429 //===========================================================================================================================
20432 CreateSOARecordData(
20433 const uint8_t * inMName
,
20434 const uint8_t * inRName
,
20436 uint32_t inRefresh
,
20439 uint32_t inMinimumTTL
,
20444 DataBuffer rdataDB
;
20446 DataBuffer_Init( &rdataDB
, NULL
, 0, kDNSRecordDataLengthMax
);
20448 err
= _AppendSOARecordData( &rdataDB
, inMName
, inRName
, inSerial
, inRefresh
, inRetry
, inExpire
, inMinimumTTL
, NULL
);
20449 require_noerr( err
, exit
);
20451 err
= DataBuffer_Detach( &rdataDB
, outPtr
, outLen
);
20452 require_noerr( err
, exit
);
20455 DataBuffer_Free( &rdataDB
);
20459 //===========================================================================================================================
20460 // _DataBuffer_AppendDNSQuestion
20461 //===========================================================================================================================
20464 _DataBuffer_AppendDNSQuestion(
20466 const uint8_t * inNamePtr
,
20472 dns_fixed_fields_question fields
;
20474 err
= DataBuffer_Append( inDB
, inNamePtr
, inNameLen
);
20475 require_noerr( err
, exit
);
20477 dns_fixed_fields_question_init( &fields
, inType
, inClass
);
20478 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
20479 require_noerr( err
, exit
);
20485 //===========================================================================================================================
20486 // _DataBuffer_AppendDNSRecord
20487 //===========================================================================================================================
20490 _DataBuffer_AppendDNSRecord(
20492 const uint8_t * inNamePtr
,
20497 const uint8_t * inRDataPtr
,
20498 size_t inRDataLen
)
20501 dns_fixed_fields_record fields
;
20503 require_action_quiet( inRDataLen
< kDNSRecordDataLengthMax
, exit
, err
= kSizeErr
);
20505 err
= DataBuffer_Append( inDB
, inNamePtr
, inNameLen
);
20506 require_noerr( err
, exit
);
20508 dns_fixed_fields_record_init( &fields
, inType
, inClass
, inTTL
, (uint16_t) inRDataLen
);
20509 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
20510 require_noerr( err
, exit
);
20514 err
= DataBuffer_Append( inDB
, inRDataPtr
, inRDataLen
);
20515 require_noerr( err
, exit
);
20522 //===========================================================================================================================
20523 // _NanoTime64ToTimestamp
20524 //===========================================================================================================================
20526 static char * _NanoTime64ToTimestamp( NanoTime64 inTime
, char *inBuf
, size_t inMaxLen
)
20530 NanoTimeToTimeVal( inTime
, &tv
);
20531 return( MakeFractionalDateString( &tv
, inBuf
, inMaxLen
) );
20534 //===========================================================================================================================
20535 // _MDNSInterfaceListCreate
20536 //===========================================================================================================================
20538 static Boolean
_MDNSInterfaceIsBlacklisted( SocketRef inInfoSock
, const char *inIfName
);
20540 static OSStatus
_MDNSInterfaceListCreate( MDNSInterfaceSubset inSubset
, size_t inItemSize
, MDNSInterfaceItem
**outList
)
20543 struct ifaddrs
* ifaList
;
20544 const struct ifaddrs
* ifa
;
20545 MDNSInterfaceItem
* interfaceList
;
20546 MDNSInterfaceItem
** ptr
;
20547 SocketRef infoSock
;
20550 interfaceList
= NULL
;
20551 infoSock
= kInvalidSocketRef
;
20552 if( inItemSize
== 0 ) inItemSize
= sizeof( MDNSInterfaceItem
);
20553 require_action_quiet( inItemSize
>= sizeof( MDNSInterfaceItem
), exit
, err
= kSizeErr
);
20555 infoSock
= socket( AF_INET
, SOCK_DGRAM
, 0 );
20556 err
= map_socket_creation_errno( infoSock
);
20557 require_noerr( err
, exit
);
20559 err
= getifaddrs( &ifaList
);
20560 err
= map_global_noerr_errno( err
);
20561 require_noerr( err
, exit
);
20563 ptr
= &interfaceList
;
20564 for( ifa
= ifaList
; ifa
; ifa
= ifa
->ifa_next
)
20566 MDNSInterfaceItem
* item
;
20568 const unsigned int flagsMask
= IFF_UP
| IFF_MULTICAST
| IFF_POINTOPOINT
;
20569 const unsigned int flagsNeeded
= IFF_UP
| IFF_MULTICAST
;
20571 if( ( ifa
->ifa_flags
& flagsMask
) != flagsNeeded
) continue;
20572 if( !ifa
->ifa_addr
|| !ifa
->ifa_name
) continue;
20573 family
= ifa
->ifa_addr
->sa_family
;
20574 if( ( family
!= AF_INET
) && ( family
!= AF_INET6
) ) continue;
20576 for( item
= interfaceList
; item
&& ( strcmp( item
->ifName
, ifa
->ifa_name
) != 0 ); item
= item
->next
) {}
20579 NetTransportType type
;
20581 const char * const ifName
= ifa
->ifa_name
;
20583 if( _MDNSInterfaceIsBlacklisted( infoSock
, ifName
) ) continue;
20584 err
= SocketGetInterfaceInfo( infoSock
, ifName
, NULL
, &ifIndex
, NULL
, NULL
, NULL
, NULL
, NULL
, &type
);
20585 require_noerr( err
, exit
);
20587 if( ifIndex
== 0 ) continue;
20588 if( type
== kNetTransportType_AWDL
)
20590 if( inSubset
== kMDNSInterfaceSubset_NonAWDL
) continue;
20594 if( inSubset
== kMDNSInterfaceSubset_AWDL
) continue;
20596 item
= (MDNSInterfaceItem
*) calloc( 1, inItemSize
);
20597 require_action( item
, exit
, err
= kNoMemoryErr
);
20602 item
->ifName
= strdup( ifName
);
20603 require_action( item
->ifName
, exit
, err
= kNoMemoryErr
);
20605 item
->ifIndex
= ifIndex
;
20606 if( type
== kNetTransportType_AWDL
) item
->isAWDL
= true;
20607 else if( type
== kNetTransportType_WiFi
) item
->isWiFi
= true;
20609 if( family
== AF_INET
) item
->hasIPv4
= true;
20610 else item
->hasIPv6
= true;
20612 require_action_quiet( interfaceList
, exit
, err
= kNotFoundErr
);
20616 *outList
= interfaceList
;
20617 interfaceList
= NULL
;
20621 if( ifaList
) freeifaddrs( ifaList
);
20622 _MDNSInterfaceListFree( interfaceList
);
20623 ForgetSocket( &infoSock
);
20627 static Boolean
_MDNSInterfaceIsBlacklisted( SocketRef inInfoSock
, const char *inIfName
)
20631 static const char * const kMDNSInterfacePrefixBlacklist
[] = { "llw" };
20634 // Check if the interface name's prefix matches the prefix blacklist.
20636 for( i
= 0; i
< (int) countof( kMDNSInterfacePrefixBlacklist
); ++i
)
20638 const char * const prefix
= kMDNSInterfacePrefixBlacklist
[ i
];
20640 if( strcmp_prefix( inIfName
, prefix
) == 0 )
20642 const char * ptr
= &inIfName
[ strlen( prefix
) ];
20644 while( isdigit_safe( *ptr
) ) ++ptr
;
20645 if( *ptr
== '\0' ) return( true );
20649 // Check if the interface is used for inter-(co)processor networking.
20651 memset( &ifr
, 0, sizeof( ifr
) );
20652 strlcpy( ifr
.ifr_name
, inIfName
, sizeof( ifr
.ifr_name
) );
20653 err
= ioctl( inInfoSock
, SIOCGIFFUNCTIONALTYPE
, &ifr
);
20654 err
= map_global_value_errno( err
!= -1, err
);
20655 if( !err
&& ( ifr
.ifr_functional_type
== IFRTYPE_FUNCTIONAL_INTCOPROC
) ) return( true );
20660 //===========================================================================================================================
20661 // _MDNSInterfaceListFree
20662 //===========================================================================================================================
20664 static void _MDNSInterfaceListFree( MDNSInterfaceItem
*inList
)
20666 MDNSInterfaceItem
* item
;
20668 while( ( item
= inList
) != NULL
)
20670 inList
= item
->next
;
20671 FreeNullSafe( item
->ifName
);
20676 //===========================================================================================================================
20677 // _MDNSInterfaceGetAny
20678 //===========================================================================================================================
20680 static OSStatus
_MDNSInterfaceGetAny( MDNSInterfaceSubset inSubset
, char inNameBuf
[ IF_NAMESIZE
+ 1 ], uint32_t *outIndex
)
20683 MDNSInterfaceItem
* list
;
20684 const MDNSInterfaceItem
* item
;
20687 err
= _MDNSInterfaceListCreate( inSubset
, 0, &list
);
20688 require_noerr_quiet( err
, exit
);
20689 require_action_quiet( list
, exit
, err
= kNotFoundErr
);
20691 for( item
= list
; item
; item
= item
->next
)
20693 if( item
->hasIPv4
&& item
->hasIPv6
) break;
20695 if( !item
) item
= list
;
20696 if( inNameBuf
) strlcpy( inNameBuf
, item
->ifName
, IF_NAMESIZE
+ 1 );
20697 if( outIndex
) *outIndex
= item
->ifIndex
;
20700 _MDNSInterfaceListFree( list
);
20704 //===========================================================================================================================
20705 // _SetComputerName
20706 //===========================================================================================================================
20708 static OSStatus
_SetComputerName( CFStringRef inComputerName
, CFStringEncoding inEncoding
)
20711 SCPreferencesRef prefs
;
20714 prefs
= SCPreferencesCreateWithAuthorization( NULL
, CFSTR( kDNSSDUtilIdentifier
), NULL
, NULL
);
20715 err
= map_scerror( prefs
);
20716 require_noerr_quiet( err
, exit
);
20718 ok
= SCPreferencesSetComputerName( prefs
, inComputerName
, inEncoding
);
20719 err
= map_scerror( ok
);
20720 require_noerr_quiet( err
, exit
);
20722 ok
= SCPreferencesCommitChanges( prefs
);
20723 err
= map_scerror( ok
);
20724 require_noerr_quiet( err
, exit
);
20726 ok
= SCPreferencesApplyChanges( prefs
);
20727 err
= map_scerror( ok
);
20728 require_noerr_quiet( err
, exit
);
20731 CFReleaseNullSafe( prefs
);
20735 //===========================================================================================================================
20736 // _SetComputerNameWithUTF8CString
20737 //===========================================================================================================================
20739 static OSStatus
_SetComputerNameWithUTF8CString( const char *inComputerName
)
20742 CFStringRef computerName
;
20744 computerName
= CFStringCreateWithCString( NULL
, inComputerName
, kCFStringEncodingUTF8
);
20745 require_action( computerName
, exit
, err
= kNoMemoryErr
);
20747 err
= _SetComputerName( computerName
, kCFStringEncodingUTF8
);
20748 require_noerr_quiet( err
, exit
);
20751 CFReleaseNullSafe( computerName
);
20755 //===========================================================================================================================
20756 // _SetLocalHostName
20757 //===========================================================================================================================
20759 static OSStatus
_SetLocalHostName( CFStringRef inLocalHostName
)
20762 SCPreferencesRef prefs
;
20765 prefs
= SCPreferencesCreateWithAuthorization( NULL
, CFSTR( kDNSSDUtilIdentifier
), NULL
, NULL
);
20766 err
= map_scerror( prefs
);
20767 require_noerr_quiet( err
, exit
);
20769 ok
= SCPreferencesSetLocalHostName( prefs
, inLocalHostName
);
20770 err
= map_scerror( ok
);
20771 require_noerr_quiet( err
, exit
);
20773 ok
= SCPreferencesCommitChanges( prefs
);
20774 err
= map_scerror( ok
);
20775 require_noerr_quiet( err
, exit
);
20777 ok
= SCPreferencesApplyChanges( prefs
);
20778 err
= map_scerror( ok
);
20779 require_noerr_quiet( err
, exit
);
20782 CFReleaseNullSafe( prefs
);
20786 //===========================================================================================================================
20787 // _SetLocalHostNameWithUTF8CString
20788 //===========================================================================================================================
20790 static OSStatus
_SetLocalHostNameWithUTF8CString( const char *inLocalHostName
)
20793 CFStringRef localHostName
;
20795 localHostName
= CFStringCreateWithCString( NULL
, inLocalHostName
, kCFStringEncodingUTF8
);
20796 require_action( localHostName
, exit
, err
= kNoMemoryErr
);
20798 err
= _SetLocalHostName( localHostName
);
20799 require_noerr_quiet( err
, exit
);
20802 CFReleaseNullSafe( localHostName
);
20806 //===========================================================================================================================
20807 // MDNSColliderCreate
20808 //===========================================================================================================================
20812 kMDNSColliderOpCode_Invalid
= 0,
20813 kMDNSColliderOpCode_Send
= 1,
20814 kMDNSColliderOpCode_Wait
= 2,
20815 kMDNSColliderOpCode_SetProbeActions
= 3,
20816 kMDNSColliderOpCode_LoopPush
= 4,
20817 kMDNSColliderOpCode_LoopPop
= 5,
20818 kMDNSColliderOpCode_Exit
= 6
20820 } MDNSColliderOpCode
;
20824 MDNSColliderOpCode opcode
;
20827 } MDNSCInstruction
;
20829 #define kMaxLoopDepth 16
20831 struct MDNSColliderPrivate
20833 CFRuntimeBase base
; // CF object base.
20834 dispatch_queue_t queue
; // Queue for collider's events.
20835 dispatch_source_t readSourceV4
; // Read dispatch source for IPv4 socket.
20836 dispatch_source_t readSourceV6
; // Read dispatch source for IPv6 socket.
20837 SocketRef sockV4
; // IPv4 UDP socket for mDNS.
20838 SocketRef sockV6
; // IPv6 UDP socket for mDNS.
20839 uint8_t * target
; // Record name being targeted. (malloced)
20840 uint8_t * responsePtr
; // Response message pointer. (malloced)
20841 size_t responseLen
; // Response message length.
20842 uint8_t * probePtr
; // Probe query message pointer. (malloced)
20843 size_t probeLen
; // Probe query message length.
20844 unsigned int probeCount
; // Count of probe queries received for collider's record.
20845 uint32_t probeActionMap
; // Bitmap of actions to take for
20846 MDNSCInstruction
* program
; // Program to execute.
20847 uint32_t pc
; // Program's program counter.
20848 uint32_t loopCounts
[ kMaxLoopDepth
]; // Stack of loop counters.
20849 uint32_t loopDepth
; // Current loop depth.
20850 dispatch_source_t waitTimer
; // Timer for program's wait commands.
20851 uint32_t interfaceIndex
; // Interface over which to send and receive mDNS msgs.
20852 MDNSColliderStopHandler_f stopHandler
; // User's stop handler.
20853 void * stopContext
; // User's stop handler context.
20854 MDNSColliderProtocols protocols
; // Protocols to use, i.e., IPv4, IPv6.
20855 Boolean stopped
; // True if the collider has been stopped.
20856 uint8_t msgBuf
[ kMDNSMessageSizeMax
]; // mDNS message buffer.
20859 static void _MDNSColliderStop( MDNSColliderRef inCollider
, OSStatus inError
);
20860 static void _MDNSColliderReadHandler( void *inContext
);
20861 static void _MDNSColliderExecuteProgram( void *inContext
);
20862 static OSStatus
_MDNSColliderSendResponse( MDNSColliderRef inCollider
, SocketRef inSock
, const struct sockaddr
*inDest
);
20863 static OSStatus
_MDNSColliderSendProbe( MDNSColliderRef inCollider
, SocketRef inSock
, const struct sockaddr
*inDest
);
20865 CF_CLASS_DEFINE( MDNSCollider
);
20867 ulog_define_ex( kDNSSDUtilIdentifier
, MDNSCollider
, kLogLevelInfo
, kLogFlags_None
, "MDNSCollider", NULL
);
20868 #define mc_ulog( LEVEL, ... ) ulog( &log_category_from_name( MDNSCollider ), (LEVEL), __VA_ARGS__ )
20870 static OSStatus
MDNSColliderCreate( dispatch_queue_t inQueue
, MDNSColliderRef
*outCollider
)
20873 MDNSColliderRef obj
= NULL
;
20875 CF_OBJECT_CREATE( MDNSCollider
, obj
, err
, exit
);
20877 ReplaceDispatchQueue( &obj
->queue
, inQueue
);
20878 obj
->sockV4
= kInvalidSocketRef
;
20879 obj
->sockV6
= kInvalidSocketRef
;
20881 *outCollider
= obj
;
20888 //===========================================================================================================================
20889 // _MDNSColliderFinalize
20890 //===========================================================================================================================
20892 static void _MDNSColliderFinalize( CFTypeRef inObj
)
20894 MDNSColliderRef
const me
= (MDNSColliderRef
) inObj
;
20896 check( !me
->waitTimer
);
20897 check( !me
->readSourceV4
);
20898 check( !me
->readSourceV6
);
20899 check( !IsValidSocket( me
->sockV4
) );
20900 check( !IsValidSocket( me
->sockV6
) );
20901 ForgetMem( &me
->target
);
20902 ForgetMem( &me
->responsePtr
);
20903 ForgetMem( &me
->probePtr
);
20904 ForgetMem( &me
->program
);
20905 dispatch_forget( &me
->queue
);
20908 //===========================================================================================================================
20909 // MDNSColliderStart
20910 //===========================================================================================================================
20912 static void _MDNSColliderStart( void *inContext
);
20914 static OSStatus
MDNSColliderStart( MDNSColliderRef me
)
20918 require_action_quiet( me
->target
, exit
, err
= kNotPreparedErr
);
20919 require_action_quiet( me
->responsePtr
, exit
, err
= kNotPreparedErr
);
20920 require_action_quiet( me
->probePtr
, exit
, err
= kNotPreparedErr
);
20921 require_action_quiet( me
->program
, exit
, err
= kNotPreparedErr
);
20922 require_action_quiet( me
->interfaceIndex
, exit
, err
= kNotPreparedErr
);
20923 require_action_quiet( me
->protocols
, exit
, err
= kNotPreparedErr
);
20926 dispatch_async_f( me
->queue
, me
, _MDNSColliderStart
);
20933 static void _MDNSColliderStart( void *inContext
)
20936 MDNSColliderRef
const me
= (MDNSColliderRef
) inContext
;
20937 SocketRef sock
= kInvalidSocketRef
;
20938 SocketContext
* sockCtx
= NULL
;
20940 if( me
->protocols
& kMDNSColliderProtocol_IPv4
)
20942 err
= CreateMulticastSocket( GetMDNSMulticastAddrV4(), kMDNSPort
, NULL
, me
->interfaceIndex
, true, NULL
, &sock
);
20943 require_noerr( err
, exit
);
20945 err
= SocketContextCreate( sock
, me
, &sockCtx
);
20946 require_noerr( err
, exit
);
20947 sock
= kInvalidSocketRef
;
20949 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _MDNSColliderReadHandler
, SocketContextCancelHandler
,
20950 sockCtx
, &me
->readSourceV4
);
20951 require_noerr( err
, exit
);
20952 me
->sockV4
= sockCtx
->sock
;
20955 dispatch_resume( me
->readSourceV4
);
20958 if( me
->protocols
& kMDNSColliderProtocol_IPv6
)
20960 err
= CreateMulticastSocket( GetMDNSMulticastAddrV6(), kMDNSPort
, NULL
, me
->interfaceIndex
, true, NULL
, &sock
);
20961 require_noerr( err
, exit
);
20963 err
= SocketContextCreate( sock
, me
, &sockCtx
);
20964 require_noerr( err
, exit
);
20965 sock
= kInvalidSocketRef
;
20967 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _MDNSColliderReadHandler
, SocketContextCancelHandler
,
20968 sockCtx
, &me
->readSourceV6
);
20969 require_noerr( err
, exit
);
20970 me
->sockV6
= sockCtx
->sock
;
20973 dispatch_resume( me
->readSourceV6
);
20976 _MDNSColliderExecuteProgram( me
);
20980 ForgetSocket( &sock
);
20981 ForgetSocketContext( &sockCtx
);
20982 if( err
) _MDNSColliderStop( me
, err
);
20985 //===========================================================================================================================
20986 // MDNSColliderStop
20987 //===========================================================================================================================
20989 static void _MDNSColliderUserStop( void *inContext
);
20991 static void MDNSColliderStop( MDNSColliderRef me
)
20994 dispatch_async_f( me
->queue
, me
, _MDNSColliderUserStop
);
20997 static void _MDNSColliderUserStop( void *inContext
)
20999 MDNSColliderRef
const me
= (MDNSColliderRef
) inContext
;
21001 _MDNSColliderStop( me
, kCanceledErr
);
21005 //===========================================================================================================================
21006 // MDNSColliderSetProtocols
21007 //===========================================================================================================================
21009 static void MDNSColliderSetProtocols( MDNSColliderRef me
, MDNSColliderProtocols inProtocols
)
21011 me
->protocols
= inProtocols
;
21014 //===========================================================================================================================
21015 // MDNSColliderSetInterfaceIndex
21016 //===========================================================================================================================
21018 static void MDNSColliderSetInterfaceIndex( MDNSColliderRef me
, uint32_t inInterfaceIndex
)
21020 me
->interfaceIndex
= inInterfaceIndex
;
21023 //===========================================================================================================================
21024 // MDNSColliderSetProgram
21025 //===========================================================================================================================
21027 #define kMDNSColliderProgCmd_Done "done"
21028 #define kMDNSColliderProgCmd_Loop "loop"
21029 #define kMDNSColliderProgCmd_Send "send"
21030 #define kMDNSColliderProgCmd_Probes "probes"
21031 #define kMDNSColliderProgCmd_Wait "wait"
21033 typedef uint32_t MDNSColliderProbeAction
;
21035 #define kMDNSColliderProbeAction_None 0
21036 #define kMDNSColliderProbeAction_Respond 1
21037 #define kMDNSColliderProbeAction_RespondUnicast 2
21038 #define kMDNSColliderProbeAction_RespondMulticast 3
21039 #define kMDNSColliderProbeAction_Probe 4
21040 #define kMDNSColliderProbeAction_MaxValue kMDNSColliderProbeAction_Probe
21042 #define kMDNSColliderProbeActionBits_Count 3
21043 #define kMDNSColliderProbeActionBits_Mask ( ( 1U << kMDNSColliderProbeActionBits_Count ) - 1 )
21044 #define kMDNSColliderProbeActionMaxProbeCount ( 32 / kMDNSColliderProbeActionBits_Count )
21046 check_compile_time( kMDNSColliderProbeAction_MaxValue
<= kMDNSColliderProbeActionBits_Mask
);
21048 static OSStatus
_MDNSColliderParseProbeActionString( const char *inString
, size_t inLen
, uint32_t *outBitmap
);
21050 static OSStatus
MDNSColliderSetProgram( MDNSColliderRef me
, const char *inProgramStr
)
21054 unsigned int loopDepth
;
21058 MDNSCInstruction
* program
= NULL
;
21059 uint32_t loopStart
[ kMaxLoopDepth
];
21062 for( cmd
= inProgramStr
; *cmd
; cmd
= next
)
21064 for( end
= cmd
; *end
&& ( *end
!= ';' ); ++end
) {}
21065 require_action_quiet( end
!= cmd
, exit
, err
= kMalformedErr
);
21066 next
= ( *end
== ';' ) ? ( end
+ 1 ) : end
;
21070 program
= (MDNSCInstruction
*) calloc( insCount
+ 1, sizeof( *program
) );
21071 require_action( program
, exit
, err
= kNoMemoryErr
);
21075 for( cmd
= inProgramStr
; *cmd
; cmd
= next
)
21082 MDNSCInstruction
* const ins
= &program
[ insCount
];
21084 while( isspace_safe( *cmd
) ) ++cmd
;
21085 for( end
= cmd
; *end
&& ( *end
!= ';' ); ++end
) {}
21086 next
= ( *end
== ';' ) ? ( end
+ 1 ) : end
;
21088 for( ptr
= cmd
; ( ptr
< end
) && !isspace_safe( *ptr
); ++ptr
) {}
21089 cmdLen
= (size_t)( ptr
- cmd
);
21093 if( strnicmpx( cmd
, cmdLen
, kMDNSColliderProgCmd_Done
) == 0 )
21095 while( ( ptr
< end
) && isspace_safe( *ptr
) ) ++ptr
;
21096 require_action_quiet( ptr
== end
, exit
, err
= kMalformedErr
);
21098 require_action_quiet( loopDepth
> 0, exit
, err
= kMalformedErr
);
21100 ins
->opcode
= kMDNSColliderOpCode_LoopPop
;
21101 ins
->operand
= loopStart
[ --loopDepth
];
21106 else if( strnicmpx( cmd
, cmdLen
, kMDNSColliderProgCmd_Loop
) == 0 )
21108 for( arg
= ptr
; ( arg
< end
) && isspace_safe( *arg
); ++arg
) {}
21109 err
= DecimalTextToUInt32( arg
, end
, &value
, &ptr
);
21110 require_noerr_quiet( err
, exit
);
21111 require_action_quiet( value
> 0, exit
, err
= kValueErr
);
21113 while( ( ptr
< end
) && isspace_safe( *ptr
) ) ++ptr
;
21114 require_action_quiet( ptr
== end
, exit
, err
= kMalformedErr
);
21116 ins
->opcode
= kMDNSColliderOpCode_LoopPush
;
21117 ins
->operand
= value
;
21119 require_action_quiet( loopDepth
< kMaxLoopDepth
, exit
, err
= kNoSpaceErr
);
21120 loopStart
[ loopDepth
++ ] = insCount
+ 1;
21125 else if( strnicmpx( cmd
, cmdLen
, kMDNSColliderProgCmd_Probes
) == 0 )
21127 for( arg
= ptr
; ( arg
< end
) && isspace_safe( *arg
); ++arg
) {}
21128 for( ptr
= arg
; ( ptr
< end
) && !isspace_safe( *ptr
); ++ptr
) {}
21129 argLen
= (size_t)( ptr
- arg
);
21132 err
= _MDNSColliderParseProbeActionString( arg
, argLen
, &value
);
21133 require_noerr_quiet( err
, exit
);
21140 while( ( ptr
< end
) && isspace_safe( *ptr
) ) ++ptr
;
21141 require_action_quiet( ptr
== end
, exit
, err
= kMalformedErr
);
21143 ins
->opcode
= kMDNSColliderOpCode_SetProbeActions
;
21144 ins
->operand
= value
;
21149 else if( strnicmpx( cmd
, cmdLen
, kMDNSColliderProgCmd_Send
) == 0 )
21151 while( ( ptr
< end
) && isspace_safe( *ptr
) ) ++ptr
;
21152 require_action_quiet( ptr
== end
, exit
, err
= kMalformedErr
);
21154 ins
->opcode
= kMDNSColliderOpCode_Send
;
21159 else if( strnicmpx( cmd
, cmdLen
, kMDNSColliderProgCmd_Wait
) == 0 )
21161 for( arg
= ptr
; ( arg
< end
) && isspace_safe( *arg
); ++arg
) {}
21162 err
= DecimalTextToUInt32( arg
, end
, &value
, &ptr
);
21163 require_noerr_quiet( err
, exit
);
21165 while( ( ptr
< end
) && isspace_safe( *ptr
) ) ++ptr
;
21166 require_action_quiet( ptr
== end
, exit
, err
= kMalformedErr
);
21168 ins
->opcode
= kMDNSColliderOpCode_Wait
;
21169 ins
->operand
= value
;
21172 // Unrecognized command
21181 require_action_quiet( loopDepth
== 0, exit
, err
= kMalformedErr
);
21183 program
[ insCount
].opcode
= kMDNSColliderOpCode_Exit
;
21185 FreeNullSafe( me
->program
);
21186 me
->program
= program
;
21191 FreeNullSafe( program
);
21195 static OSStatus
_MDNSColliderParseProbeActionString( const char *inString
, size_t inLen
, uint32_t *outBitmap
)
21199 const char * const end
= &inString
[ inLen
];
21209 MDNSColliderProbeAction action
;
21212 if( isdigit_safe( c
) )
21217 count
= ( count
* 10 ) + ( c
- '0' );
21218 require_action_quiet( count
<= ( kMDNSColliderProbeActionMaxProbeCount
- index
), exit
, err
= kCountErr
);
21219 require_action_quiet( ptr
< end
, exit
, err
= kUnderrunErr
);
21222 } while( isdigit_safe( c
) );
21223 require_action_quiet( count
> 0, exit
, err
= kCountErr
);
21227 require_action_quiet( index
< kMDNSColliderProbeActionMaxProbeCount
, exit
, err
= kMalformedErr
);
21233 case 'n': action
= kMDNSColliderProbeAction_None
; break;
21234 case 'r': action
= kMDNSColliderProbeAction_Respond
; break;
21235 case 'u': action
= kMDNSColliderProbeAction_RespondUnicast
; break;
21236 case 'm': action
= kMDNSColliderProbeAction_RespondMulticast
; break;
21237 case 'p': action
= kMDNSColliderProbeAction_Probe
; break;
21238 default: err
= kMalformedErr
; goto exit
;
21243 require_action_quiet( ( c
== '-' ) && ( ptr
< end
), exit
, err
= kMalformedErr
);
21245 while( count
-- > 0 )
21247 bitmap
|= ( action
<< ( index
* kMDNSColliderProbeActionBits_Count
) );
21252 *outBitmap
= bitmap
;
21259 //===========================================================================================================================
21260 // MDNSColliderSetStopHandler
21261 //===========================================================================================================================
21263 static void MDNSColliderSetStopHandler( MDNSColliderRef me
, MDNSColliderStopHandler_f inStopHandler
, void *inStopContext
)
21265 me
->stopHandler
= inStopHandler
;
21266 me
->stopContext
= inStopContext
;
21269 //===========================================================================================================================
21270 // MDNSColliderSetRecord
21271 //===========================================================================================================================
21273 #define kMDNSColliderDummyStr "\x16" "mdnscollider-sent-this" "\x05" "local"
21274 #define kMDNSColliderDummyName ( (const uint8_t *) kMDNSColliderDummyStr )
21275 #define kMDNSColliderDummyNameLen sizeof( kMDNSColliderDummyStr )
21278 MDNSColliderSetRecord(
21279 MDNSColliderRef me
,
21280 const uint8_t * inName
,
21282 const void * inRDataPtr
,
21283 size_t inRDataLen
)
21288 uint8_t * targetPtr
= NULL
;
21290 uint8_t * responsePtr
= NULL
;
21291 size_t responseLen
;
21292 uint8_t * probePtr
= NULL
;
21295 DataBuffer_Init( &msgDB
, NULL
, 0, kMDNSMessageSizeMax
);
21297 err
= DomainNameDup( inName
, &targetPtr
, &targetLen
);
21298 require_noerr_quiet( err
, exit
);
21300 // Create response message.
21302 memset( &header
, 0, sizeof( header
) );
21303 DNSHeaderSetFlags( &header
, kDNSHeaderFlag_Response
| kDNSHeaderFlag_AuthAnswer
);
21304 DNSHeaderSetAnswerCount( &header
, 1 );
21306 err
= DataBuffer_Append( &msgDB
, &header
, sizeof( header
) );
21307 require_noerr( err
, exit
);
21309 err
= _DataBuffer_AppendDNSRecord( &msgDB
, targetPtr
, targetLen
, inType
, kDNSServiceClass_IN
| kRRClassCacheFlushBit
,
21310 1976, inRDataPtr
, inRDataLen
);
21311 require_noerr( err
, exit
);
21313 err
= DataBuffer_Detach( &msgDB
, &responsePtr
, &responseLen
);
21314 require_noerr( err
, exit
);
21316 // Create probe message.
21318 memset( &header
, 0, sizeof( header
) );
21319 DNSHeaderSetQuestionCount( &header
, 2 );
21320 DNSHeaderSetAuthorityCount( &header
, 1 );
21322 err
= DataBuffer_Append( &msgDB
, &header
, sizeof( header
) );
21323 require_noerr( err
, exit
);
21325 err
= _DataBuffer_AppendDNSQuestion( &msgDB
, targetPtr
, targetLen
, kDNSServiceType_ANY
, kDNSServiceClass_IN
);
21326 require_noerr( err
, exit
);
21328 err
= _DataBuffer_AppendDNSQuestion( &msgDB
, kMDNSColliderDummyName
, kMDNSColliderDummyNameLen
,
21329 kDNSServiceType_NULL
, kDNSServiceClass_IN
);
21330 require_noerr( err
, exit
);
21332 err
= _DataBuffer_AppendDNSRecord( &msgDB
, targetPtr
, targetLen
, inType
, kDNSServiceClass_IN
,
21333 1976, inRDataPtr
, inRDataLen
);
21334 require_noerr( err
, exit
);
21336 err
= DataBuffer_Detach( &msgDB
, &probePtr
, &probeLen
);
21337 require_noerr( err
, exit
);
21339 FreeNullSafe( me
->target
);
21340 me
->target
= targetPtr
;
21343 FreeNullSafe( me
->responsePtr
);
21344 me
->responsePtr
= responsePtr
;
21345 me
->responseLen
= responseLen
;
21346 responsePtr
= NULL
;
21348 FreeNullSafe( me
->probePtr
);
21349 me
->probePtr
= probePtr
;
21350 me
->probeLen
= probeLen
;
21354 DataBuffer_Free( &msgDB
);
21355 FreeNullSafe( targetPtr
);
21356 FreeNullSafe( responsePtr
);
21357 FreeNullSafe( probePtr
);
21361 //===========================================================================================================================
21362 // _MDNSColliderStop
21363 //===========================================================================================================================
21365 static void _MDNSColliderStop( MDNSColliderRef me
, OSStatus inError
)
21367 dispatch_source_forget( &me
->waitTimer
);
21368 dispatch_source_forget( &me
->readSourceV4
);
21369 dispatch_source_forget( &me
->readSourceV6
);
21370 me
->sockV4
= kInvalidSocketRef
;
21371 me
->sockV6
= kInvalidSocketRef
;
21375 me
->stopped
= true;
21376 if( me
->stopHandler
) me
->stopHandler( me
->stopContext
, inError
);
21381 //===========================================================================================================================
21382 // _MDNSColliderReadHandler
21383 //===========================================================================================================================
21385 static MDNSColliderProbeAction
_MDNSColliderGetProbeAction( uint32_t inBitmap
, unsigned int inProbeNumber
);
21386 static const char * _MDNSColliderProbeActionToString( MDNSColliderProbeAction inAction
);
21388 static void _MDNSColliderReadHandler( void *inContext
)
21391 struct timeval now
;
21392 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
21393 MDNSColliderRef
const me
= (MDNSColliderRef
) sockCtx
->userContext
;
21395 sockaddr_ip sender
;
21396 const DNSHeader
* hdr
;
21397 const uint8_t * ptr
;
21398 const struct sockaddr
* dest
;
21399 int probeFound
, probeIsQU
;
21400 unsigned int qCount
, i
;
21401 MDNSColliderProbeAction action
;
21403 gettimeofday( &now
, NULL
);
21405 err
= SocketRecvFrom( sockCtx
->sock
, me
->msgBuf
, sizeof( me
->msgBuf
), &msgLen
, &sender
, sizeof( sender
),
21406 NULL
, NULL
, NULL
, NULL
);
21407 require_noerr( err
, exit
);
21409 require_quiet( msgLen
>= kDNSHeaderLength
, exit
);
21410 hdr
= (const DNSHeader
*) me
->msgBuf
;
21412 probeFound
= false;
21414 qCount
= DNSHeaderGetQuestionCount( hdr
);
21415 ptr
= (const uint8_t *) &hdr
[ 1 ];
21416 for( i
= 0; i
< qCount
; ++i
)
21418 uint16_t qtype
, qclass
;
21419 uint8_t qname
[ kDomainNameLengthMax
];
21421 err
= DNSMessageExtractQuestion( me
->msgBuf
, msgLen
, ptr
, qname
, &qtype
, &qclass
, &ptr
);
21422 require_noerr_quiet( err
, exit
);
21424 if( ( qtype
== kDNSServiceType_NULL
) && ( qclass
== kDNSServiceClass_IN
) &&
21425 DomainNameEqual( qname
, kMDNSColliderDummyName
) )
21427 probeFound
= false;
21431 if( qtype
!= kDNSServiceType_ANY
) continue;
21432 if( ( qclass
& ~kQClassUnicastResponseBit
) != kDNSServiceClass_IN
) continue;
21433 if( !DomainNameEqual( qname
, me
->target
) ) continue;
21438 probeIsQU
= ( qclass
& kQClassUnicastResponseBit
) ? true : false;
21441 require_quiet( probeFound
, exit
);
21444 action
= _MDNSColliderGetProbeAction( me
->probeActionMap
, me
->probeCount
);
21446 mc_ulog( kLogLevelInfo
, "Received probe from %##a at %{du:time} (action: %s):\n\n%#1{du:dnsmsg}",
21447 &sender
, &now
, _MDNSColliderProbeActionToString( action
), me
->msgBuf
, msgLen
);
21449 if( ( action
== kMDNSColliderProbeAction_Respond
) ||
21450 ( action
== kMDNSColliderProbeAction_RespondUnicast
) ||
21451 ( action
== kMDNSColliderProbeAction_RespondMulticast
) )
21453 if( ( ( action
== kMDNSColliderProbeAction_Respond
) && probeIsQU
) ||
21454 ( action
== kMDNSColliderProbeAction_RespondUnicast
) )
21458 else if( ( ( action
== kMDNSColliderProbeAction_Respond
) && !probeIsQU
) ||
21459 ( action
== kMDNSColliderProbeAction_RespondMulticast
) )
21461 dest
= ( sender
.sa
.sa_family
== AF_INET
) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
21464 err
= _MDNSColliderSendResponse( me
, sockCtx
->sock
, dest
);
21465 require_noerr( err
, exit
);
21467 else if( action
== kMDNSColliderProbeAction_Probe
)
21469 dest
= ( sender
.sa
.sa_family
== AF_INET
) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
21471 err
= _MDNSColliderSendProbe( me
, sockCtx
->sock
, dest
);
21472 require_noerr( err
, exit
);
21479 static MDNSColliderProbeAction
_MDNSColliderGetProbeAction( uint32_t inBitmap
, unsigned int inProbeNumber
)
21481 MDNSColliderProbeAction action
;
21483 if( ( inProbeNumber
>= 1 ) && ( inProbeNumber
<= kMDNSColliderProbeActionMaxProbeCount
) )
21485 action
= ( inBitmap
>> ( ( inProbeNumber
- 1 ) * kMDNSColliderProbeActionBits_Count
) ) &
21486 kMDNSColliderProbeActionBits_Mask
;
21490 action
= kMDNSColliderProbeAction_None
;
21495 static const char * _MDNSColliderProbeActionToString( MDNSColliderProbeAction inAction
)
21499 case kMDNSColliderProbeAction_None
: return( "None" );
21500 case kMDNSColliderProbeAction_Respond
: return( "Respond" );
21501 case kMDNSColliderProbeAction_RespondUnicast
: return( "Respond (unicast)" );
21502 case kMDNSColliderProbeAction_RespondMulticast
: return( "Respond (multicast)" );
21503 case kMDNSColliderProbeAction_Probe
: return( "Probe" );
21504 default: return( "???" );
21508 //===========================================================================================================================
21509 // _MDNSColliderExecuteProgram
21510 //===========================================================================================================================
21512 static void _MDNSColliderExecuteProgram( void *inContext
)
21515 MDNSColliderRef
const me
= (MDNSColliderRef
) inContext
;
21518 dispatch_forget( &me
->waitTimer
);
21523 const MDNSCInstruction
* const ins
= &me
->program
[ me
->pc
++ ];
21526 switch( ins
->opcode
)
21528 case kMDNSColliderOpCode_Send
:
21529 if( IsValidSocket( me
->sockV4
) )
21531 err
= _MDNSColliderSendResponse( me
, me
->sockV4
, GetMDNSMulticastAddrV4() );
21532 require_noerr( err
, exit
);
21534 if( IsValidSocket( me
->sockV6
) )
21536 err
= _MDNSColliderSendResponse( me
, me
->sockV6
, GetMDNSMulticastAddrV6() );
21537 require_noerr( err
, exit
);
21541 case kMDNSColliderOpCode_Wait
:
21542 waitMs
= ins
->operand
;
21545 err
= DispatchTimerOneShotCreate( dispatch_time_milliseconds( waitMs
), 1, me
->queue
,
21546 _MDNSColliderExecuteProgram
, me
, &me
->waitTimer
);
21547 require_noerr( err
, exit
);
21548 dispatch_resume( me
->waitTimer
);
21553 case kMDNSColliderOpCode_SetProbeActions
:
21554 me
->probeCount
= 0;
21555 me
->probeActionMap
= ins
->operand
;
21558 case kMDNSColliderOpCode_LoopPush
:
21559 check( me
->loopDepth
< kMaxLoopDepth
);
21560 me
->loopCounts
[ me
->loopDepth
++ ] = ins
->operand
;
21563 case kMDNSColliderOpCode_LoopPop
:
21564 check( me
->loopDepth
> 0 );
21565 if( --me
->loopCounts
[ me
->loopDepth
- 1 ] > 0 )
21567 me
->pc
= ins
->operand
;
21575 case kMDNSColliderOpCode_Exit
:
21581 dlogassert( "Unhandled opcode %u\n", ins
->opcode
);
21588 if( err
|| stop
) _MDNSColliderStop( me
, err
);
21591 //===========================================================================================================================
21592 // _MDNSColliderSendResponse
21593 //===========================================================================================================================
21595 static OSStatus
_MDNSColliderSendResponse( MDNSColliderRef me
, SocketRef inSock
, const struct sockaddr
*inDest
)
21600 n
= sendto( inSock
, (char *) me
->responsePtr
, me
->responseLen
, 0, inDest
, SockAddrGetSize( inDest
) );
21601 err
= map_socket_value_errno( inSock
, n
== (ssize_t
) me
->responseLen
, n
);
21605 //===========================================================================================================================
21606 // _MDNSColliderSendProbe
21607 //===========================================================================================================================
21609 static OSStatus
_MDNSColliderSendProbe( MDNSColliderRef me
, SocketRef inSock
, const struct sockaddr
*inDest
)
21614 n
= sendto( inSock
, (char *) me
->probePtr
, me
->probeLen
, 0, inDest
, SockAddrGetSize( inDest
) );
21615 err
= map_socket_value_errno( inSock
, n
== (ssize_t
) me
->probeLen
, n
);
21619 //===========================================================================================================================
21620 // ServiceBrowserCreate
21621 //===========================================================================================================================
21623 typedef struct SBDomain SBDomain
;
21624 typedef struct SBServiceType SBServiceType
;
21625 typedef struct SBServiceBrowse SBServiceBrowse
;
21626 typedef struct SBServiceInstance SBServiceInstance
;
21627 typedef struct SBIPAddress SBIPAddress
;
21629 struct ServiceBrowserPrivate
21631 CFRuntimeBase base
; // CF object base.
21632 dispatch_queue_t queue
; // Queue for service browser's events.
21633 DNSServiceRef connection
; // Shared connection for DNS-SD ops.
21634 DNSServiceRef domainsQuery
; // Query for recommended browsing domains.
21635 char * domain
; // If non-null, then browsing is limited to this domain.
21636 StringListItem
* serviceTypeList
; // If non-null, then browsing is limited to these service types.
21637 ServiceBrowserCallback_f userCallback
; // User's callback. Called when browsing stops.
21638 void * userContext
; // User's callback context.
21639 SBDomain
* domainList
; // List of domains and their browse results.
21640 dispatch_source_t stopTimer
; // Timer to stop browsing after browseTimeSecs.
21641 uint32_t ifIndex
; // If non-zero, then browsing is limited to this interface.
21642 unsigned int browseTimeSecs
; // Amount of time to spend browsing in seconds.
21643 Boolean includeAWDL
; // True if the IncludeAWDL flag should be used for DNS-SD ops that
21644 // use the "any" interface.
21649 SBDomain
* next
; // Next domain object in list.
21650 ServiceBrowserRef browser
; // Pointer to parent service browser.
21651 char * name
; // Name of the domain.
21652 DNSServiceRef servicesQuery
; // Query for services (_services._dns-sd._udp.<domain> PTR record) in domain.
21653 SBServiceType
* typeList
; // List of service types to browse for in this domain.
21656 struct SBServiceType
21658 SBServiceType
* next
; // Next service type object in list.
21659 char * name
; // Name of the service type.
21660 SBServiceBrowse
* browseList
; // List of browses for this service type.
21663 struct SBServiceBrowse
21665 SBServiceBrowse
* next
; // Next browse object in list.
21666 ServiceBrowserRef browser
; // Pointer to parent service browser.
21667 DNSServiceRef browse
; // Reference to DNSServiceBrowse op.
21668 SBServiceInstance
* instanceList
; // List of service instances that were discovered by this browse.
21669 uint64_t startTicks
; // Value of UpTicks() when the browse op began.
21670 uint32_t ifIndex
; // If non-zero, then the browse is limited to this interface.
21673 struct SBServiceInstance
21675 SBServiceInstance
* next
; // Next service instance object in list.
21676 ServiceBrowserRef browser
; // Pointer to parent service browser.
21677 char * name
; // Name of the service instance.
21678 char * fqdn
; // Fully qualified domain name of service instance (for logging/debugging).
21679 uint32_t ifIndex
; // Index of interface over which this service instance was discovered.
21680 uint64_t discoverTimeUs
; // Time it took to discover this service instance in microseconds.
21681 DNSServiceRef resolve
; // Reference to DNSServiceResolve op for this service instance.
21682 uint64_t resolveStartTicks
; // Value of UpTicks() when the DNSServiceResolve op began.
21683 uint64_t resolveTimeUs
; // Time it took to resolve this service instance.
21684 char * hostname
; // Service instance's hostname. Result of DNSServiceResolve.
21685 uint16_t port
; // Service instance's port number. Result of DNSServiceResolve.
21686 uint8_t * txtPtr
; // Service instance's TXT record data. Result of DNSServiceResolve.
21687 size_t txtLen
; // Length of service instance's TXT record data.
21688 DNSServiceRef getAddrInfo
; // Reference to DNSServiceGetAddrInfo op for service instance's hostname.
21689 uint64_t gaiStartTicks
; // Value of UpTicks() when the DNSServiceGetAddrInfo op began.
21690 SBIPAddress
* ipaddrList
; // List of IP addresses that the hostname resolved to.
21695 SBIPAddress
* next
; // Next IP address object in list.
21696 sockaddr_ip sip
; // IPv4 or IPv6 address.
21697 uint64_t resolveTimeUs
; // Time it took to resolve this IP address in microseconds.
21702 SBRDomain
* domainList
; // List of domains in which services were found.
21703 int32_t refCount
; // This object's reference count.
21705 } ServiceBrowserResultsPrivate
;
21707 static void _ServiceBrowserStop( ServiceBrowserRef me
, OSStatus inError
);
21708 static OSStatus
_ServiceBrowserAddDomain( ServiceBrowserRef inBrowser
, const char *inDomain
);
21709 static OSStatus
_ServiceBrowserRemoveDomain( ServiceBrowserRef inBrowser
, const char *inName
);
21710 static void _ServiceBrowserTimerHandler( void *inContext
);
21711 static void DNSSD_API
21712 _ServiceBrowserDomainsQueryCallback(
21713 DNSServiceRef inSDRef
,
21714 DNSServiceFlags inFlags
,
21715 uint32_t inInterfaceIndex
,
21716 DNSServiceErrorType inError
,
21717 const char * inFullName
,
21720 uint16_t inRDataLen
,
21721 const void * inRDataPtr
,
21723 void * inContext
);
21724 static void DNSSD_API
21725 _ServiceBrowserServicesQueryCallback(
21726 DNSServiceRef inSDRef
,
21727 DNSServiceFlags inFlags
,
21728 uint32_t inInterfaceIndex
,
21729 DNSServiceErrorType inError
,
21730 const char * inFullName
,
21733 uint16_t inRDataLen
,
21734 const void * inRDataPtr
,
21736 void * inContext
);
21737 static void DNSSD_API
21738 _ServiceBrowserBrowseCallback(
21739 DNSServiceRef inSDRef
,
21740 DNSServiceFlags inFlags
,
21741 uint32_t inInterfaceIndex
,
21742 DNSServiceErrorType inError
,
21743 const char * inName
,
21744 const char * inRegType
,
21745 const char * inDomain
,
21746 void * inContext
);
21747 static void DNSSD_API
21748 _ServiceBrowserResolveCallback(
21749 DNSServiceRef inSDRef
,
21750 DNSServiceFlags inFlags
,
21751 uint32_t inInterfaceIndex
,
21752 DNSServiceErrorType inError
,
21753 const char * inFullName
,
21754 const char * inHostname
,
21757 const unsigned char * inTXTPtr
,
21758 void * inContext
);
21759 static void DNSSD_API
21760 _ServiceBrowserGAICallback(
21761 DNSServiceRef inSDRef
,
21762 DNSServiceFlags inFlags
,
21763 uint32_t inInterfaceIndex
,
21764 DNSServiceErrorType inError
,
21765 const char * inHostname
,
21766 const struct sockaddr
* inSockAddr
,
21768 void * inContext
);
21770 _ServiceBrowserAddServiceType(
21771 ServiceBrowserRef inBrowser
,
21772 SBDomain
* inDomain
,
21773 const char * inName
,
21774 uint32_t inIfIndex
);
21776 _ServiceBrowserRemoveServiceType(
21777 ServiceBrowserRef inBrowser
,
21778 SBDomain
* inDomain
,
21779 const char * inName
,
21780 uint32_t inIfIndex
);
21782 _ServiceBrowserAddServiceInstance(
21783 ServiceBrowserRef inBrowser
,
21784 SBServiceBrowse
* inBrowse
,
21785 uint32_t inIfIndex
,
21786 const char * inName
,
21787 const char * inRegType
,
21788 const char * inDomain
,
21789 uint64_t inDiscoverTimeUs
);
21791 _ServiceBrowserRemoveServiceInstance(
21792 ServiceBrowserRef inBrowser
,
21793 SBServiceBrowse
* inBrowse
,
21794 const char * inName
,
21795 uint32_t inIfIndex
);
21797 _ServiceBrowserAddIPAddress(
21798 ServiceBrowserRef inBrowser
,
21799 SBServiceInstance
* inInstance
,
21800 const struct sockaddr
* inSockAddr
,
21801 uint64_t inResolveTimeUs
);
21803 _ServiceBrowserRemoveIPAddress(
21804 ServiceBrowserRef inBrowser
,
21805 SBServiceInstance
* inInstance
,
21806 const struct sockaddr
* inSockAddr
);
21807 static OSStatus
_ServiceBrowserCreateResults( ServiceBrowserRef me
, ServiceBrowserResults
**outResults
);
21808 static OSStatus
_SBDomainCreate( const char *inName
, ServiceBrowserRef inBrowser
, SBDomain
**outDomain
);
21809 static void _SBDomainFree( SBDomain
*inDomain
);
21810 static OSStatus
_SBServiceTypeCreate( const char *inName
, SBServiceType
**outType
);
21811 static void _SBServiceTypeFree( SBServiceType
*inType
);
21812 static OSStatus
_SBServiceBrowseCreate( uint32_t inIfIndex
, ServiceBrowserRef inBrowser
, SBServiceBrowse
**outBrowse
);
21813 static void _SBServiceBrowseFree( SBServiceBrowse
*inBrowse
);
21815 _SBServiceInstanceCreate(
21816 const char * inName
,
21817 const char * inType
,
21818 const char * inDomain
,
21819 uint32_t inIfIndex
,
21820 uint64_t inDiscoverTimeUs
,
21821 ServiceBrowserRef inBrowser
,
21822 SBServiceInstance
** outInstance
);
21823 static void _SBServiceInstanceFree( SBServiceInstance
*inInstance
);
21825 _SBIPAddressCreate(
21826 const struct sockaddr
* inSockAddr
,
21827 uint64_t inResolveTimeUs
,
21828 SBIPAddress
** outIPAddress
);
21829 static void _SBIPAddressFree( SBIPAddress
*inIPAddress
);
21830 static void _SBIPAddressFreeList( SBIPAddress
*inList
);
21831 static OSStatus
_SBRDomainCreate( const char *inName
, SBRDomain
**outDomain
);
21832 static void _SBRDomainFree( SBRDomain
*inDomain
);
21833 static OSStatus
_SBRServiceTypeCreate( const char *inName
, SBRServiceType
**outType
);
21834 static void _SBRServiceTypeFree( SBRServiceType
*inType
);
21836 _SBRServiceInstanceCreate(
21837 const char * inName
,
21838 uint32_t inInterfaceIndex
,
21839 const char * inHostname
,
21841 const uint8_t * inTXTPtr
,
21843 uint64_t inDiscoverTimeUs
,
21844 uint64_t inResolveTimeUs
,
21845 SBRServiceInstance
** outInstance
);
21846 static void _SBRServiceInstanceFree( SBRServiceInstance
*inInstance
);
21848 _SBRIPAddressCreate(
21849 const struct sockaddr
* inSockAddr
,
21850 uint64_t inResolveTimeUs
,
21851 SBRIPAddress
** outIPAddress
);
21852 static void _SBRIPAddressFree( SBRIPAddress
*inIPAddress
);
21854 #define ForgetSBIPAddressList( X ) ForgetCustom( X, _SBIPAddressFreeList )
21856 CF_CLASS_DEFINE( ServiceBrowser
);
21858 ulog_define_ex( kDNSSDUtilIdentifier
, ServiceBrowser
, kLogLevelTrace
, kLogFlags_None
, "ServiceBrowser", NULL
);
21859 #define sb_ulog( LEVEL, ... ) ulog( &log_category_from_name( ServiceBrowser ), (LEVEL), __VA_ARGS__ )
21862 ServiceBrowserCreate(
21863 dispatch_queue_t inQueue
,
21864 uint32_t inInterfaceIndex
,
21865 const char * inDomain
,
21866 unsigned int inBrowseTimeSecs
,
21867 Boolean inIncludeAWDL
,
21868 ServiceBrowserRef
* outBrowser
)
21871 ServiceBrowserRef obj
;
21873 CF_OBJECT_CREATE( ServiceBrowser
, obj
, err
, exit
);
21875 ReplaceDispatchQueue( &obj
->queue
, inQueue
);
21876 obj
->ifIndex
= inInterfaceIndex
;
21879 obj
->domain
= strdup( inDomain
);
21880 require_action( obj
->domain
, exit
, err
= kNoMemoryErr
);
21882 obj
->browseTimeSecs
= inBrowseTimeSecs
;
21883 obj
->includeAWDL
= inIncludeAWDL
;
21890 CFReleaseNullSafe( obj
);
21894 //===========================================================================================================================
21895 // _ServiceBrowserFinalize
21896 //===========================================================================================================================
21898 static void _ServiceBrowserFinalize( CFTypeRef inObj
)
21900 ServiceBrowserRef
const me
= (ServiceBrowserRef
) inObj
;
21901 StringListItem
* serviceType
;
21903 dispatch_forget( &me
->queue
);
21904 check( !me
->connection
);
21905 check( !me
->domainsQuery
);
21906 ForgetMem( &me
->domain
);
21907 while( ( serviceType
= me
->serviceTypeList
) != NULL
)
21909 me
->serviceTypeList
= serviceType
->next
;
21910 ForgetMem( &serviceType
->str
);
21911 free( serviceType
);
21913 check( !me
->domainList
);
21914 check( !me
->stopTimer
);
21917 //===========================================================================================================================
21918 // ServiceBrowserStart
21919 //===========================================================================================================================
21921 static void _ServiceBrowserStart( void *inContext
);
21923 static void ServiceBrowserStart( ServiceBrowserRef me
)
21926 dispatch_async_f( me
->queue
, me
, _ServiceBrowserStart
);
21929 static void _ServiceBrowserStart( void *inContext
)
21932 ServiceBrowserRef
const me
= (ServiceBrowserRef
) inContext
;
21934 err
= DNSServiceCreateConnection( &me
->connection
);
21935 require_noerr( err
, exit
);
21937 err
= DNSServiceSetDispatchQueue( me
->connection
, me
->queue
);
21938 require_noerr( err
, exit
);
21942 err
= _ServiceBrowserAddDomain( me
, me
->domain
);
21943 require_noerr( err
, exit
);
21947 DNSServiceRef sdRef
;
21948 const char * const recordName
= "b._dns-sd._udp.local.";
21949 const uint32_t ifIndex
= kDNSServiceInterfaceIndexLocalOnly
;
21951 // Perform PTR meta-query for "b._dns-sd._udp.local." to enumerate recommended browsing domains.
21952 // See <https://tools.ietf.org/html/rfc6763#section-11>.
21954 sb_ulog( kLogLevelTrace
, "Starting PTR QueryRecord on interface %d for %s", (int32_t) ifIndex
, recordName
);
21956 sdRef
= me
->connection
;
21957 err
= DNSServiceQueryRecord( &sdRef
, kDNSServiceFlagsShareConnection
, ifIndex
, recordName
,
21958 kDNSServiceType_PTR
, kDNSServiceClass_IN
, _ServiceBrowserDomainsQueryCallback
, me
);
21959 require_noerr( err
, exit
);
21961 me
->domainsQuery
= sdRef
;
21964 err
= DispatchTimerCreate( dispatch_time_seconds( me
->browseTimeSecs
), DISPATCH_TIME_FOREVER
,
21965 100 * kNanosecondsPerMillisecond
, me
->queue
, _ServiceBrowserTimerHandler
, NULL
, me
, &me
->stopTimer
);
21966 require_noerr( err
, exit
);
21967 dispatch_resume( me
->stopTimer
);
21970 if( err
) _ServiceBrowserStop( me
, err
);
21973 //===========================================================================================================================
21974 // ServiceBrowserAddServiceType
21975 //===========================================================================================================================
21977 static OSStatus
ServiceBrowserAddServiceType( ServiceBrowserRef me
, const char *inServiceType
)
21980 StringListItem
* item
;
21981 StringListItem
** itemPtr
;
21982 StringListItem
* newItem
= NULL
;
21984 for( itemPtr
= &me
->serviceTypeList
; ( item
= *itemPtr
) != NULL
; itemPtr
= &item
->next
)
21986 if( strcmp( item
->str
, inServiceType
) == 0 ) break;
21990 newItem
= (StringListItem
*) calloc( 1, sizeof( *newItem
) );
21991 require_action( newItem
, exit
, err
= kNoMemoryErr
);
21993 newItem
->str
= strdup( inServiceType
);
21994 require_action( newItem
->str
, exit
, err
= kNoMemoryErr
);
21996 *itemPtr
= newItem
;
22002 FreeNullSafe( newItem
);
22006 //===========================================================================================================================
22007 // ServiceBrowserSetCallback
22008 //===========================================================================================================================
22010 static void ServiceBrowserSetCallback( ServiceBrowserRef me
, ServiceBrowserCallback_f inCallback
, void *inContext
)
22012 me
->userCallback
= inCallback
;
22013 me
->userContext
= inContext
;
22016 //===========================================================================================================================
22017 // ServiceBrowserResultsRetain
22018 //===========================================================================================================================
22020 static void ServiceBrowserResultsRetain( ServiceBrowserResults
*inResults
)
22022 ServiceBrowserResultsPrivate
* const results
= (ServiceBrowserResultsPrivate
*) inResults
;
22024 atomic_add_32( &results
->refCount
, 1 );
22027 //===========================================================================================================================
22028 // ServiceBrowserResultsRelease
22029 //===========================================================================================================================
22031 static void ServiceBrowserResultsRelease( ServiceBrowserResults
*inResults
)
22033 ServiceBrowserResultsPrivate
* const results
= (ServiceBrowserResultsPrivate
*) inResults
;
22034 SBRDomain
* domain
;
22036 if( atomic_add_and_fetch_32( &results
->refCount
, -1 ) == 0 )
22038 while( ( domain
= inResults
->domainList
) != NULL
)
22040 inResults
->domainList
= domain
->next
;
22041 _SBRDomainFree( domain
);
22047 //===========================================================================================================================
22048 // _ServiceBrowserStop
22049 //===========================================================================================================================
22051 static void _ServiceBrowserStop( ServiceBrowserRef me
, OSStatus inError
)
22056 SBServiceBrowse
* b
;
22057 SBServiceInstance
* i
;
22059 dispatch_source_forget( &me
->stopTimer
);
22060 DNSServiceForget( &me
->domainsQuery
);
22061 for( d
= me
->domainList
; d
; d
= d
->next
)
22063 DNSServiceForget( &d
->servicesQuery
);
22064 for( t
= d
->typeList
; t
; t
= t
->next
)
22066 for( b
= t
->browseList
; b
; b
= b
->next
)
22068 DNSServiceForget( &b
->browse
);
22069 for( i
= b
->instanceList
; i
; i
= i
->next
)
22071 DNSServiceForget( &i
->resolve
);
22072 DNSServiceForget( &i
->getAddrInfo
);
22077 DNSServiceForget( &me
->connection
);
22079 if( me
->userCallback
)
22081 ServiceBrowserResults
* results
= NULL
;
22083 err
= _ServiceBrowserCreateResults( me
, &results
);
22084 if( !err
) err
= inError
;
22086 me
->userCallback( results
, err
, me
->userContext
);
22087 me
->userCallback
= NULL
;
22088 me
->userContext
= NULL
;
22089 if( results
) ServiceBrowserResultsRelease( results
);
22092 while( ( d
= me
->domainList
) != NULL
)
22094 me
->domainList
= d
->next
;
22095 _SBDomainFree( d
);
22100 //===========================================================================================================================
22101 // _ServiceBrowserAddDomain
22102 //===========================================================================================================================
22104 static OSStatus
_ServiceBrowserAddDomain( ServiceBrowserRef me
, const char *inDomain
)
22108 SBDomain
** domainPtr
;
22109 SBDomain
* newDomain
= NULL
;
22111 for( domainPtr
= &me
->domainList
; ( domain
= *domainPtr
) != NULL
; domainPtr
= &domain
->next
)
22113 if( strcasecmp( domain
->name
, inDomain
) == 0 ) break;
22115 require_action_quiet( !domain
, exit
, err
= kDuplicateErr
);
22117 err
= _SBDomainCreate( inDomain
, me
, &newDomain
);
22118 require_noerr_quiet( err
, exit
);
22120 if( me
->serviceTypeList
)
22122 const StringListItem
* item
;
22124 for( item
= me
->serviceTypeList
; item
; item
= item
->next
)
22126 err
= _ServiceBrowserAddServiceType( me
, newDomain
, item
->str
, me
->ifIndex
);
22127 if( err
== kDuplicateErr
) err
= kNoErr
;
22128 require_noerr( err
, exit
);
22134 DNSServiceRef sdRef
;
22135 DNSServiceFlags flags
;
22137 // Perform PTR meta-query for _services._dns-sd._udp.<domain> to enumerate service types in domain.
22138 // See <https://tools.ietf.org/html/rfc6763#section-9>.
22140 ASPrintF( &recordName
, "_services._dns-sd._udp.%s", newDomain
->name
);
22141 require_action( recordName
, exit
, err
= kNoMemoryErr
);
22143 flags
= kDNSServiceFlagsShareConnection
;
22144 if( ( me
->ifIndex
== kDNSServiceInterfaceIndexAny
) && me
->includeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
22146 sb_ulog( kLogLevelTrace
, "Starting PTR QueryRecord on interface %d for %s", (int32_t) me
->ifIndex
, recordName
);
22148 sdRef
= newDomain
->browser
->connection
;
22149 err
= DNSServiceQueryRecord( &sdRef
, flags
, me
->ifIndex
, recordName
, kDNSServiceType_PTR
, kDNSServiceClass_IN
,
22150 _ServiceBrowserServicesQueryCallback
, newDomain
);
22151 free( recordName
);
22152 require_noerr( err
, exit
);
22154 newDomain
->servicesQuery
= sdRef
;
22157 *domainPtr
= newDomain
;
22162 if( newDomain
) _SBDomainFree( newDomain
);
22166 //===========================================================================================================================
22167 // _ServiceBrowserRemoveDomain
22168 //===========================================================================================================================
22170 static OSStatus
_ServiceBrowserRemoveDomain( ServiceBrowserRef me
, const char *inName
)
22174 SBDomain
** domainPtr
;
22176 for( domainPtr
= &me
->domainList
; ( domain
= *domainPtr
) != NULL
; domainPtr
= &domain
->next
)
22178 if( strcasecmp( domain
->name
, inName
) == 0 ) break;
22183 *domainPtr
= domain
->next
;
22184 _SBDomainFree( domain
);
22189 err
= kNotFoundErr
;
22195 //===========================================================================================================================
22196 // _ServiceBrowserTimerHandler
22197 //===========================================================================================================================
22199 static void _ServiceBrowserTimerHandler( void *inContext
)
22201 ServiceBrowserRef
const me
= (ServiceBrowserRef
) inContext
;
22203 _ServiceBrowserStop( me
, kNoErr
);
22206 //===========================================================================================================================
22207 // _ServiceBrowserDomainsQueryCallback
22208 //===========================================================================================================================
22210 static void DNSSD_API
22211 _ServiceBrowserDomainsQueryCallback(
22212 DNSServiceRef inSDRef
,
22213 DNSServiceFlags inFlags
,
22214 uint32_t inInterfaceIndex
,
22215 DNSServiceErrorType inError
,
22216 const char * inFullName
,
22219 uint16_t inRDataLen
,
22220 const void * inRDataPtr
,
22224 ServiceBrowserRef
const me
= (ServiceBrowserRef
) inContext
;
22226 char domainStr
[ kDNSServiceMaxDomainName
];
22232 sb_ulog( kLogLevelTrace
, "QueryRecord result: %s on interface %d for %s -> %{du:rdata}%?{end} (error: %#m)",
22233 DNSServiceFlagsToAddRmvStr( inFlags
), (int32_t) inInterfaceIndex
, inFullName
, inType
, inRDataPtr
, inRDataLen
,
22234 !inError
, inError
);
22236 require_noerr( inError
, exit
);
22238 err
= DomainNameToString( inRDataPtr
, ( (const uint8_t *) inRDataPtr
) + inRDataLen
, domainStr
, NULL
);
22239 require_noerr( err
, exit
);
22241 if( inFlags
& kDNSServiceFlagsAdd
)
22243 err
= _ServiceBrowserAddDomain( me
, domainStr
);
22244 if( err
== kDuplicateErr
) err
= kNoErr
;
22245 require_noerr( err
, exit
);
22249 err
= _ServiceBrowserRemoveDomain( me
, domainStr
);
22250 if( err
== kNotFoundErr
) err
= kNoErr
;
22251 require_noerr( err
, exit
);
22258 //===========================================================================================================================
22259 // _ServiceBrowserServicesQueryCallback
22260 //===========================================================================================================================
22262 static void DNSSD_API
22263 _ServiceBrowserServicesQueryCallback(
22264 DNSServiceRef inSDRef
,
22265 DNSServiceFlags inFlags
,
22266 uint32_t inInterfaceIndex
,
22267 DNSServiceErrorType inError
,
22268 const char * inFullName
,
22271 uint16_t inRDataLen
,
22272 const void * inRDataPtr
,
22277 SBDomain
* const domain
= (SBDomain
*) inContext
;
22278 ServiceBrowserRef
const me
= domain
->browser
;
22279 const uint8_t * src
;
22280 const uint8_t * end
;
22283 uint8_t serviceType
[ 2 * ( 1 + kDomainLabelLengthMax
) + 1 ];
22284 char serviceTypeStr
[ kDNSServiceMaxDomainName
];
22290 sb_ulog( kLogLevelTrace
, "QueryRecord result: %s on interface %d for %s -> %{du:rdata}%?{end} (error: %#m)",
22291 DNSServiceFlagsToAddRmvStr( inFlags
), (int32_t) inInterfaceIndex
, inFullName
, inType
, inRDataPtr
, inRDataLen
,
22292 !inError
, inError
);
22294 require_noerr( inError
, exit
);
22296 check( inType
== kDNSServiceType_PTR
);
22297 check( inClass
== kDNSServiceClass_IN
);
22299 // The first two labels of the domain name in the RDATA describe a service type.
22300 // See <https://tools.ietf.org/html/rfc6763#section-9>.
22302 src
= (const uint8_t *) inRDataPtr
;
22303 end
= src
+ inRDataLen
;
22305 for( i
= 0; i
< 2; ++i
)
22309 require_action_quiet( ( end
- src
) > 0, exit
, err
= kUnderrunErr
);
22312 require_action_quiet( ( labelLen
> 0 ) && ( labelLen
<= kDomainLabelLengthMax
), exit
, err
= kMalformedErr
);
22313 require_action_quiet( ( (size_t)( end
- src
) ) >= ( 1 + labelLen
), exit
, err
= kUnderrunErr
);
22315 memcpy( dst
, src
, 1 + labelLen
);
22316 src
+= 1 + labelLen
;
22317 dst
+= 1 + labelLen
;
22321 err
= DomainNameToString( serviceType
, NULL
, serviceTypeStr
, NULL
);
22322 require_noerr( err
, exit
);
22324 if( inFlags
& kDNSServiceFlagsAdd
)
22326 err
= _ServiceBrowserAddServiceType( me
, domain
, serviceTypeStr
, inInterfaceIndex
);
22327 if( err
== kDuplicateErr
) err
= kNoErr
;
22328 require_noerr( err
, exit
);
22332 err
= _ServiceBrowserRemoveServiceType( me
, domain
, serviceTypeStr
, inInterfaceIndex
);
22333 if( err
== kNotFoundErr
) err
= kNoErr
;
22334 require_noerr( err
, exit
);
22341 //===========================================================================================================================
22342 // _ServiceBrowserBrowseCallback
22343 //===========================================================================================================================
22345 static void DNSSD_API
22346 _ServiceBrowserBrowseCallback(
22347 DNSServiceRef inSDRef
,
22348 DNSServiceFlags inFlags
,
22349 uint32_t inInterfaceIndex
,
22350 DNSServiceErrorType inError
,
22351 const char * inName
,
22352 const char * inRegType
,
22353 const char * inDomain
,
22357 const uint64_t nowTicks
= UpTicks();
22358 SBServiceBrowse
* const browse
= (SBServiceBrowse
*) inContext
;
22359 ServiceBrowserRef
const me
= (ServiceBrowserRef
) browse
->browser
;
22363 sb_ulog( kLogLevelTrace
, "Browse result: %s on interface %d for %s.%s%s%?{end} (error: %#m)",
22364 DNSServiceFlagsToAddRmvStr( inFlags
), (int32_t) inInterfaceIndex
, inName
, inRegType
, inDomain
, !inError
, inError
);
22366 require_noerr( inError
, exit
);
22368 if( inFlags
& kDNSServiceFlagsAdd
)
22370 err
= _ServiceBrowserAddServiceInstance( me
, browse
, inInterfaceIndex
, inName
, inRegType
, inDomain
,
22371 UpTicksToMicroseconds( nowTicks
- browse
->startTicks
) );
22372 if( err
== kDuplicateErr
) err
= kNoErr
;
22373 require_noerr( err
, exit
);
22377 err
= _ServiceBrowserRemoveServiceInstance( me
, browse
, inName
, inInterfaceIndex
);
22378 if( err
== kNotFoundErr
) err
= kNoErr
;
22379 require_noerr( err
, exit
);
22386 //===========================================================================================================================
22387 // _ServiceBrowserResolveCallback
22388 //===========================================================================================================================
22390 static void DNSSD_API
22391 _ServiceBrowserResolveCallback(
22392 DNSServiceRef inSDRef
,
22393 DNSServiceFlags inFlags
,
22394 uint32_t inInterfaceIndex
,
22395 DNSServiceErrorType inError
,
22396 const char * inFullName
,
22397 const char * inHostname
,
22400 const unsigned char * inTXTPtr
,
22404 const uint64_t nowTicks
= UpTicks();
22405 SBServiceInstance
* const instance
= (SBServiceInstance
*) inContext
;
22406 ServiceBrowserRef
const me
= (ServiceBrowserRef
) instance
->browser
;
22411 sb_ulog( kLogLevelTrace
, "Resolve result on interface %d for %s -> %s:%u %#{txt}%?{end} (error %#m)",
22412 (int32_t) inInterfaceIndex
, inFullName
, inHostname
, inPort
, inTXTPtr
, (size_t) inTXTLen
, !inError
, inError
);
22414 require_noerr( inError
, exit
);
22416 if( !MemEqual( instance
->txtPtr
, instance
->txtLen
, inTXTPtr
, inTXTLen
) )
22418 FreeNullSafe( instance
->txtPtr
);
22419 instance
->txtPtr
= _memdup( inTXTPtr
, inTXTLen
);
22420 require_action( instance
->txtPtr
, exit
, err
= kNoMemoryErr
);
22422 instance
->txtLen
= inTXTLen
;
22425 instance
->port
= ntohs( inPort
);
22427 if( !instance
->hostname
|| ( strcasecmp( instance
->hostname
, inHostname
) != 0 ) )
22429 DNSServiceRef sdRef
;
22431 if( !instance
->hostname
) instance
->resolveTimeUs
= UpTicksToMicroseconds( nowTicks
- instance
->resolveStartTicks
);
22433 err
= ReplaceString( &instance
->hostname
, NULL
, inHostname
, kSizeCString
);
22434 require_noerr( err
, exit
);
22436 DNSServiceForget( &instance
->getAddrInfo
);
22437 ForgetSBIPAddressList( &instance
->ipaddrList
);
22439 sb_ulog( kLogLevelTrace
, "Starting GetAddrInfo on interface %d for %s",
22440 (int32_t) instance
->ifIndex
, instance
->hostname
);
22442 sdRef
= me
->connection
;
22443 instance
->gaiStartTicks
= UpTicks();
22444 err
= DNSServiceGetAddrInfo( &sdRef
, kDNSServiceFlagsShareConnection
, instance
->ifIndex
,
22445 kDNSServiceProtocol_IPv4
| kDNSServiceProtocol_IPv6
, instance
->hostname
, _ServiceBrowserGAICallback
, instance
);
22446 require_noerr( err
, exit
);
22448 instance
->getAddrInfo
= sdRef
;
22455 //===========================================================================================================================
22456 // _ServiceBrowserGAICallback
22457 //===========================================================================================================================
22459 static void DNSSD_API
22460 _ServiceBrowserGAICallback(
22461 DNSServiceRef inSDRef
,
22462 DNSServiceFlags inFlags
,
22463 uint32_t inInterfaceIndex
,
22464 DNSServiceErrorType inError
,
22465 const char * inHostname
,
22466 const struct sockaddr
* inSockAddr
,
22471 const uint64_t nowTicks
= UpTicks();
22472 SBServiceInstance
* const instance
= (SBServiceInstance
*) inContext
;
22473 ServiceBrowserRef
const me
= (ServiceBrowserRef
) instance
->browser
;
22478 sb_ulog( kLogLevelTrace
, "GetAddrInfo result: %s on interface %d for (%s ->) %s -> %##a%?{end} (error: %#m)",
22479 DNSServiceFlagsToAddRmvStr( inFlags
), (int32_t) inInterfaceIndex
, instance
->fqdn
, inHostname
, inSockAddr
,
22480 !inError
, inError
);
22482 require_noerr( inError
, exit
);
22484 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
22486 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
22490 if( inFlags
& kDNSServiceFlagsAdd
)
22492 err
= _ServiceBrowserAddIPAddress( me
, instance
, inSockAddr
,
22493 UpTicksToMicroseconds( nowTicks
- instance
->gaiStartTicks
) );
22494 if( err
== kDuplicateErr
) err
= kNoErr
;
22495 require_noerr( err
, exit
);
22499 err
= _ServiceBrowserRemoveIPAddress( me
, instance
, inSockAddr
);
22500 if( err
== kNotFoundErr
) err
= kNoErr
;
22501 require_noerr( err
, exit
);
22508 //===========================================================================================================================
22509 // _ServiceBrowserAddServiceType
22510 //===========================================================================================================================
22513 _ServiceBrowserAddServiceType(
22514 ServiceBrowserRef me
,
22515 SBDomain
* inDomain
,
22516 const char * inName
,
22517 uint32_t inIfIndex
)
22520 SBServiceType
* type
;
22521 SBServiceType
** typePtr
;
22522 SBServiceType
* newType
= NULL
;
22523 SBServiceBrowse
* browse
;
22524 SBServiceBrowse
** browsePtr
;
22525 SBServiceBrowse
* newBrowse
= NULL
;
22526 DNSServiceRef sdRef
;
22527 DNSServiceFlags flags
;
22529 for( typePtr
= &inDomain
->typeList
; ( type
= *typePtr
) != NULL
; typePtr
= &type
->next
)
22531 if( strcasecmp( type
->name
, inName
) == 0 ) break;
22535 err
= _SBServiceTypeCreate( inName
, &newType
);
22536 require_noerr_quiet( err
, exit
);
22541 for( browsePtr
= &type
->browseList
; ( browse
= *browsePtr
) != NULL
; browsePtr
= &browse
->next
)
22543 if( browse
->ifIndex
== inIfIndex
) break;
22545 require_action_quiet( !browse
, exit
, err
= kDuplicateErr
);
22547 err
= _SBServiceBrowseCreate( inIfIndex
, me
, &newBrowse
);
22548 require_noerr_quiet( err
, exit
);
22550 flags
= kDNSServiceFlagsShareConnection
;
22551 if( ( newBrowse
->ifIndex
== kDNSServiceInterfaceIndexAny
) && me
->includeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
22553 sb_ulog( kLogLevelTrace
, "Starting Browse on interface %d for %s%s",
22554 (int32_t) newBrowse
->ifIndex
, type
->name
, inDomain
->name
);
22556 sdRef
= me
->connection
;
22557 newBrowse
->startTicks
= UpTicks();
22558 err
= DNSServiceBrowse( &sdRef
, flags
, newBrowse
->ifIndex
, type
->name
, inDomain
->name
, _ServiceBrowserBrowseCallback
,
22560 require_noerr( err
, exit
);
22562 newBrowse
->browse
= sdRef
;
22563 *browsePtr
= newBrowse
;
22568 *typePtr
= newType
;
22573 if( newBrowse
) _SBServiceBrowseFree( newBrowse
);
22574 if( newType
) _SBServiceTypeFree( newType
);
22578 //===========================================================================================================================
22579 // _ServiceBrowserRemoveServiceType
22580 //===========================================================================================================================
22583 _ServiceBrowserRemoveServiceType(
22584 ServiceBrowserRef me
,
22585 SBDomain
* inDomain
,
22586 const char * inName
,
22587 uint32_t inIfIndex
)
22590 SBServiceType
* type
;
22591 SBServiceType
** typePtr
;
22592 SBServiceBrowse
* browse
;
22593 SBServiceBrowse
** browsePtr
;
22597 for( typePtr
= &inDomain
->typeList
; ( type
= *typePtr
) != NULL
; typePtr
= &type
->next
)
22599 if( strcasecmp( type
->name
, inName
) == 0 ) break;
22601 require_action_quiet( type
, exit
, err
= kNotFoundErr
);
22603 for( browsePtr
= &type
->browseList
; ( browse
= *browsePtr
) != NULL
; browsePtr
= &browse
->next
)
22605 if( browse
->ifIndex
== inIfIndex
) break;
22607 require_action_quiet( browse
, exit
, err
= kNotFoundErr
);
22609 *browsePtr
= browse
->next
;
22610 _SBServiceBrowseFree( browse
);
22611 if( !type
->browseList
)
22613 *typePtr
= type
->next
;
22614 _SBServiceTypeFree( type
);
22622 //===========================================================================================================================
22623 // _ServiceBrowserAddServiceInstance
22624 //===========================================================================================================================
22627 _ServiceBrowserAddServiceInstance(
22628 ServiceBrowserRef me
,
22629 SBServiceBrowse
* inBrowse
,
22630 uint32_t inIfIndex
,
22631 const char * inName
,
22632 const char * inRegType
,
22633 const char * inDomain
,
22634 uint64_t inDiscoverTimeUs
)
22637 DNSServiceRef sdRef
;
22638 SBServiceInstance
* instance
;
22639 SBServiceInstance
** instancePtr
;
22640 SBServiceInstance
* newInstance
= NULL
;
22642 for( instancePtr
= &inBrowse
->instanceList
; ( instance
= *instancePtr
) != NULL
; instancePtr
= &instance
->next
)
22644 if( ( instance
->ifIndex
== inIfIndex
) && ( strcasecmp( instance
->name
, inName
) == 0 ) ) break;
22646 require_action_quiet( !instance
, exit
, err
= kDuplicateErr
);
22648 err
= _SBServiceInstanceCreate( inName
, inRegType
, inDomain
, inIfIndex
, inDiscoverTimeUs
, me
, &newInstance
);
22649 require_noerr_quiet( err
, exit
);
22651 sb_ulog( kLogLevelTrace
, "Starting Resolve on interface %d for %s.%s%s",
22652 (int32_t) newInstance
->ifIndex
, inName
, inRegType
, inDomain
);
22654 sdRef
= me
->connection
;
22655 newInstance
->resolveStartTicks
= UpTicks();
22656 err
= DNSServiceResolve( &sdRef
, kDNSServiceFlagsShareConnection
, newInstance
->ifIndex
, inName
, inRegType
, inDomain
,
22657 _ServiceBrowserResolveCallback
, newInstance
);
22658 require_noerr( err
, exit
);
22660 newInstance
->resolve
= sdRef
;
22661 *instancePtr
= newInstance
;
22662 newInstance
= NULL
;
22665 if( newInstance
) _SBServiceInstanceFree( newInstance
);
22669 //===========================================================================================================================
22670 // _ServiceBrowserRemoveServiceInstance
22671 //===========================================================================================================================
22674 _ServiceBrowserRemoveServiceInstance(
22675 ServiceBrowserRef me
,
22676 SBServiceBrowse
* inBrowse
,
22677 const char * inName
,
22678 uint32_t inIfIndex
)
22681 SBServiceInstance
* instance
;
22682 SBServiceInstance
** ptr
;
22686 for( ptr
= &inBrowse
->instanceList
; ( instance
= *ptr
) != NULL
; ptr
= &instance
->next
)
22688 if( ( instance
->ifIndex
== inIfIndex
) && ( strcasecmp( instance
->name
, inName
) == 0 ) ) break;
22690 require_action_quiet( instance
, exit
, err
= kNotFoundErr
);
22692 *ptr
= instance
->next
;
22693 _SBServiceInstanceFree( instance
);
22700 //===========================================================================================================================
22701 // _ServiceBrowserAddIPAddress
22702 //===========================================================================================================================
22705 _ServiceBrowserAddIPAddress(
22706 ServiceBrowserRef me
,
22707 SBServiceInstance
* inInstance
,
22708 const struct sockaddr
* inSockAddr
,
22709 uint64_t inResolveTimeUs
)
22712 SBIPAddress
* ipaddr
;
22713 SBIPAddress
** ipaddrPtr
;
22714 SBIPAddress
* newIPAddr
= NULL
;
22718 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
22720 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
22725 for( ipaddrPtr
= &inInstance
->ipaddrList
; ( ipaddr
= *ipaddrPtr
) != NULL
; ipaddrPtr
= &ipaddr
->next
)
22727 if( SockAddrCompareAddr( &ipaddr
->sip
, inSockAddr
) == 0 ) break;
22729 require_action_quiet( !ipaddr
, exit
, err
= kDuplicateErr
);
22731 err
= _SBIPAddressCreate( inSockAddr
, inResolveTimeUs
, &newIPAddr
);
22732 require_noerr_quiet( err
, exit
);
22734 *ipaddrPtr
= newIPAddr
;
22739 if( newIPAddr
) _SBIPAddressFree( newIPAddr
);
22743 //===========================================================================================================================
22744 // _ServiceBrowserRemoveIPAddress
22745 //===========================================================================================================================
22748 _ServiceBrowserRemoveIPAddress(
22749 ServiceBrowserRef me
,
22750 SBServiceInstance
* inInstance
,
22751 const struct sockaddr
* inSockAddr
)
22754 SBIPAddress
* ipaddr
;
22755 SBIPAddress
** ipaddrPtr
;
22759 for( ipaddrPtr
= &inInstance
->ipaddrList
; ( ipaddr
= *ipaddrPtr
) != NULL
; ipaddrPtr
= &ipaddr
->next
)
22761 if( SockAddrCompareAddr( &ipaddr
->sip
.sa
, inSockAddr
) == 0 ) break;
22763 require_action_quiet( ipaddr
, exit
, err
= kNotFoundErr
);
22765 *ipaddrPtr
= ipaddr
->next
;
22766 _SBIPAddressFree( ipaddr
);
22773 //===========================================================================================================================
22774 // _ServiceBrowserCreateResults
22775 //===========================================================================================================================
22777 static OSStatus
_ServiceBrowserCreateResults( ServiceBrowserRef me
, ServiceBrowserResults
**outResults
)
22782 SBServiceBrowse
* b
;
22783 SBServiceInstance
* i
;
22785 ServiceBrowserResultsPrivate
* results
;
22786 SBRDomain
** domainPtr
;
22788 results
= (ServiceBrowserResultsPrivate
*) calloc( 1, sizeof( *results
) );
22789 require_action( results
, exit
, err
= kNoMemoryErr
);
22791 results
->refCount
= 1;
22793 domainPtr
= &results
->domainList
;
22794 for( d
= me
->domainList
; d
; d
= d
->next
)
22796 SBRDomain
* domain
;
22797 SBRServiceType
** typePtr
;
22799 err
= _SBRDomainCreate( d
->name
, &domain
);
22800 require_noerr_quiet( err
, exit
);
22801 *domainPtr
= domain
;
22802 domainPtr
= &domain
->next
;
22804 typePtr
= &domain
->typeList
;
22805 for( t
= d
->typeList
; t
; t
= t
->next
)
22807 SBRServiceType
* type
;
22808 SBRServiceInstance
** instancePtr
;
22810 err
= _SBRServiceTypeCreate( t
->name
, &type
);
22811 require_noerr_quiet( err
, exit
);
22813 typePtr
= &type
->next
;
22815 instancePtr
= &type
->instanceList
;
22816 for( b
= t
->browseList
; b
; b
= b
->next
)
22818 for( i
= b
->instanceList
; i
; i
= i
->next
)
22820 SBRServiceInstance
* instance
;
22821 SBRIPAddress
** ipaddrPtr
;
22823 err
= _SBRServiceInstanceCreate( i
->name
, i
->ifIndex
, i
->hostname
, i
->port
, i
->txtPtr
, i
->txtLen
,
22824 i
->discoverTimeUs
, i
->resolveTimeUs
, &instance
);
22825 require_noerr_quiet( err
, exit
);
22826 *instancePtr
= instance
;
22827 instancePtr
= &instance
->next
;
22829 ipaddrPtr
= &instance
->ipaddrList
;
22830 for( a
= i
->ipaddrList
; a
; a
= a
->next
)
22832 SBRIPAddress
* ipaddr
;
22834 err
= _SBRIPAddressCreate( &a
->sip
.sa
, a
->resolveTimeUs
, &ipaddr
);
22835 require_noerr_quiet( err
, exit
);
22837 *ipaddrPtr
= ipaddr
;
22838 ipaddrPtr
= &ipaddr
->next
;
22845 *outResults
= (ServiceBrowserResults
*) results
;
22850 if( results
) ServiceBrowserResultsRelease( (ServiceBrowserResults
*) results
);
22854 //===========================================================================================================================
22856 //===========================================================================================================================
22858 static OSStatus
_SBDomainCreate( const char *inName
, ServiceBrowserRef inBrowser
, SBDomain
**outDomain
)
22863 obj
= (SBDomain
*) calloc( 1, sizeof( *obj
) );
22864 require_action( obj
, exit
, err
= kNoMemoryErr
);
22866 obj
->name
= strdup( inName
);
22867 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
22869 obj
->browser
= inBrowser
;
22876 if( obj
) _SBDomainFree( obj
);
22880 //===========================================================================================================================
22882 //===========================================================================================================================
22884 static void _SBDomainFree( SBDomain
*inDomain
)
22886 SBServiceType
* type
;
22888 ForgetMem( &inDomain
->name
);
22889 DNSServiceForget( &inDomain
->servicesQuery
);
22890 while( ( type
= inDomain
->typeList
) != NULL
)
22892 inDomain
->typeList
= type
->next
;
22893 _SBServiceTypeFree( type
);
22898 //===========================================================================================================================
22899 // _SBServiceTypeCreate
22900 //===========================================================================================================================
22902 static OSStatus
_SBServiceTypeCreate( const char *inName
, SBServiceType
**outType
)
22905 SBServiceType
* obj
;
22907 obj
= (SBServiceType
*) calloc( 1, sizeof( *obj
) );
22908 require_action( obj
, exit
, err
= kNoMemoryErr
);
22910 obj
->name
= strdup( inName
);
22911 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
22918 if( obj
) _SBServiceTypeFree( obj
);
22922 //===========================================================================================================================
22923 // _SBServiceTypeFree
22924 //===========================================================================================================================
22926 static void _SBServiceTypeFree( SBServiceType
*inType
)
22928 SBServiceBrowse
* browse
;
22930 ForgetMem( &inType
->name
);
22931 while( ( browse
= inType
->browseList
) != NULL
)
22933 inType
->browseList
= browse
->next
;
22934 _SBServiceBrowseFree( browse
);
22939 //===========================================================================================================================
22940 // _SBServiceBrowseCreate
22941 //===========================================================================================================================
22943 static OSStatus
_SBServiceBrowseCreate( uint32_t inIfIndex
, ServiceBrowserRef inBrowser
, SBServiceBrowse
**outBrowse
)
22946 SBServiceBrowse
* obj
;
22948 obj
= (SBServiceBrowse
*) calloc( 1, sizeof( *obj
) );
22949 require_action( obj
, exit
, err
= kNoMemoryErr
);
22951 obj
->ifIndex
= inIfIndex
;
22952 obj
->browser
= inBrowser
;
22960 //===========================================================================================================================
22961 // _SBServiceBrowseFree
22962 //===========================================================================================================================
22964 static void _SBServiceBrowseFree( SBServiceBrowse
*inBrowse
)
22966 SBServiceInstance
* instance
;
22968 DNSServiceForget( &inBrowse
->browse
);
22969 while( ( instance
= inBrowse
->instanceList
) != NULL
)
22971 inBrowse
->instanceList
= instance
->next
;
22972 _SBServiceInstanceFree( instance
);
22977 //===========================================================================================================================
22978 // _SBServiceInstanceCreate
22979 //===========================================================================================================================
22982 _SBServiceInstanceCreate(
22983 const char * inName
,
22984 const char * inType
,
22985 const char * inDomain
,
22986 uint32_t inIfIndex
,
22987 uint64_t inDiscoverTimeUs
,
22988 ServiceBrowserRef inBrowser
,
22989 SBServiceInstance
** outInstance
)
22992 SBServiceInstance
* obj
;
22994 obj
= (SBServiceInstance
*) calloc( 1, sizeof( *obj
) );
22995 require_action( obj
, exit
, err
= kNoMemoryErr
);
22997 obj
->name
= strdup( inName
);
22998 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
23000 ASPrintF( &obj
->fqdn
, "%s.%s%s", obj
->name
, inType
, inDomain
);
23001 require_action( obj
->fqdn
, exit
, err
= kNoMemoryErr
);
23003 obj
->ifIndex
= inIfIndex
;
23004 obj
->discoverTimeUs
= inDiscoverTimeUs
;
23005 obj
->browser
= inBrowser
;
23007 *outInstance
= obj
;
23012 if( obj
) _SBServiceInstanceFree( obj
);
23016 //===========================================================================================================================
23017 // _SBServiceInstanceFree
23018 //===========================================================================================================================
23020 static void _SBServiceInstanceFree( SBServiceInstance
*inInstance
)
23022 ForgetMem( &inInstance
->name
);
23023 ForgetMem( &inInstance
->fqdn
);
23024 DNSServiceForget( &inInstance
->resolve
);
23025 ForgetMem( &inInstance
->hostname
);
23026 ForgetMem( &inInstance
->txtPtr
);
23027 DNSServiceForget( &inInstance
->getAddrInfo
);
23028 ForgetSBIPAddressList( &inInstance
->ipaddrList
);
23029 free( inInstance
);
23032 //===========================================================================================================================
23033 // _SBIPAddressCreate
23034 //===========================================================================================================================
23036 static OSStatus
_SBIPAddressCreate( const struct sockaddr
*inSockAddr
, uint64_t inResolveTimeUs
, SBIPAddress
**outIPAddress
)
23041 obj
= (SBIPAddress
*) calloc( 1, sizeof( *obj
) );
23042 require_action( obj
, exit
, err
= kNoMemoryErr
);
23044 SockAddrCopy( inSockAddr
, &obj
->sip
);
23045 obj
->resolveTimeUs
= inResolveTimeUs
;
23047 *outIPAddress
= obj
;
23054 //===========================================================================================================================
23055 // _SBIPAddressFree
23056 //===========================================================================================================================
23058 static void _SBIPAddressFree( SBIPAddress
*inIPAddress
)
23060 free( inIPAddress
);
23063 //===========================================================================================================================
23064 // _SBIPAddressFreeList
23065 //===========================================================================================================================
23067 static void _SBIPAddressFreeList( SBIPAddress
*inList
)
23069 SBIPAddress
* ipaddr
;
23071 while( ( ipaddr
= inList
) != NULL
)
23073 inList
= ipaddr
->next
;
23074 _SBIPAddressFree( ipaddr
);
23078 //===========================================================================================================================
23079 // _SBRDomainCreate
23080 //===========================================================================================================================
23082 static OSStatus
_SBRDomainCreate( const char *inName
, SBRDomain
**outDomain
)
23087 obj
= (SBRDomain
*) calloc( 1, sizeof( *obj
) );
23088 require_action( obj
, exit
, err
= kNoMemoryErr
);
23090 obj
->name
= strdup( inName
);
23091 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
23098 if( obj
) _SBRDomainFree( obj
);
23102 //===========================================================================================================================
23104 //===========================================================================================================================
23106 static void _SBRDomainFree( SBRDomain
*inDomain
)
23108 SBRServiceType
* type
;
23110 ForgetMem( &inDomain
->name
);
23111 while( ( type
= inDomain
->typeList
) != NULL
)
23113 inDomain
->typeList
= type
->next
;
23114 _SBRServiceTypeFree( type
);
23119 //===========================================================================================================================
23120 // _SBRServiceTypeCreate
23121 //===========================================================================================================================
23123 static OSStatus
_SBRServiceTypeCreate( const char *inName
, SBRServiceType
**outType
)
23126 SBRServiceType
* obj
;
23128 obj
= (SBRServiceType
*) calloc( 1, sizeof( *obj
) );
23129 require_action( obj
, exit
, err
= kNoMemoryErr
);
23131 obj
->name
= strdup( inName
);
23132 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
23139 if( obj
) _SBRServiceTypeFree( obj
);
23143 //===========================================================================================================================
23144 // _SBRServiceTypeFree
23145 //===========================================================================================================================
23147 static void _SBRServiceTypeFree( SBRServiceType
*inType
)
23149 SBRServiceInstance
* instance
;
23151 ForgetMem( &inType
->name
);
23152 while( ( instance
= inType
->instanceList
) != NULL
)
23154 inType
->instanceList
= instance
->next
;
23155 _SBRServiceInstanceFree( instance
);
23160 //===========================================================================================================================
23161 // _SBRServiceInstanceCreate
23162 //===========================================================================================================================
23165 _SBRServiceInstanceCreate(
23166 const char * inName
,
23167 uint32_t inInterfaceIndex
,
23168 const char * inHostname
,
23170 const uint8_t * inTXTPtr
,
23172 uint64_t inDiscoverTimeUs
,
23173 uint64_t inResolveTimeUs
,
23174 SBRServiceInstance
** outInstance
)
23177 SBRServiceInstance
* obj
;
23179 obj
= (SBRServiceInstance
*) calloc( 1, sizeof( *obj
) );
23180 require_action( obj
, exit
, err
= kNoMemoryErr
);
23182 obj
->name
= strdup( inName
);
23183 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
23187 obj
->hostname
= strdup( inHostname
);
23188 require_action( obj
->hostname
, exit
, err
= kNoMemoryErr
);
23192 obj
->txtPtr
= (uint8_t *) _memdup( inTXTPtr
, inTXTLen
);
23193 require_action( obj
->txtPtr
, exit
, err
= kNoMemoryErr
);
23194 obj
->txtLen
= inTXTLen
;
23196 obj
->discoverTimeUs
= inDiscoverTimeUs
;
23197 obj
->resolveTimeUs
= inResolveTimeUs
;
23198 obj
->ifIndex
= inInterfaceIndex
;
23199 obj
->port
= inPort
;
23201 *outInstance
= obj
;
23206 if( obj
) _SBRServiceInstanceFree( obj
);
23210 //===========================================================================================================================
23211 // _SBRServiceInstanceFree
23212 //===========================================================================================================================
23214 static void _SBRServiceInstanceFree( SBRServiceInstance
*inInstance
)
23216 SBRIPAddress
* ipaddr
;
23218 ForgetMem( &inInstance
->name
);
23219 ForgetMem( &inInstance
->hostname
);
23220 ForgetMem( &inInstance
->txtPtr
);
23221 while( ( ipaddr
= inInstance
->ipaddrList
) != NULL
)
23223 inInstance
->ipaddrList
= ipaddr
->next
;
23224 _SBRIPAddressFree( ipaddr
);
23226 free( inInstance
);
23229 //===========================================================================================================================
23230 // _SBRIPAddressCreate
23231 //===========================================================================================================================
23234 _SBRIPAddressCreate(
23235 const struct sockaddr
* inSockAddr
,
23236 uint64_t inResolveTimeUs
,
23237 SBRIPAddress
** outIPAddress
)
23240 SBRIPAddress
* obj
;
23242 obj
= (SBRIPAddress
*) calloc( 1, sizeof( *obj
) );
23243 require_action( obj
, exit
, err
= kNoMemoryErr
);
23245 SockAddrCopy( inSockAddr
, &obj
->sip
);
23246 obj
->resolveTimeUs
= inResolveTimeUs
;
23248 *outIPAddress
= obj
;
23255 //===========================================================================================================================
23256 // _SBRIPAddressFree
23257 //===========================================================================================================================
23259 static void _SBRIPAddressFree( SBRIPAddress
*inIPAddress
)
23261 free( inIPAddress
);
23264 //===========================================================================================================================
23267 // Note: This was copied from CoreUtils because the SocketWriteAll function is currently not exported in the framework.
23268 //===========================================================================================================================
23270 static OSStatus
_SocketWriteAll( SocketRef inSock
, const void *inData
, size_t inSize
, int32_t inTimeoutSecs
)
23273 const uint8_t * src
;
23274 const uint8_t * end
;
23276 struct timeval timeout
;
23279 FD_ZERO( &writeSet
);
23280 src
= (const uint8_t *) inData
;
23281 end
= src
+ inSize
;
23284 FD_SET( inSock
, &writeSet
);
23285 timeout
.tv_sec
= inTimeoutSecs
;
23286 timeout
.tv_usec
= 0;
23287 n
= select( (int)( inSock
+ 1 ), NULL
, &writeSet
, NULL
, &timeout
);
23288 if( n
== 0 ) { err
= kTimeoutErr
; goto exit
; }
23289 err
= map_socket_value_errno( inSock
, n
> 0, n
);
23290 require_noerr( err
, exit
);
23292 n
= send( inSock
, (char *) src
, (size_t)( end
- src
), 0 );
23293 err
= map_socket_value_errno( inSock
, n
>= 0, n
);
23294 if( err
== EINTR
) continue;
23295 require_noerr( err
, exit
);
23305 //===========================================================================================================================
23306 // _ParseIPv4Address
23308 // Warning: "inBuffer" may be modified even in error cases.
23310 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
23311 //===========================================================================================================================
23313 static OSStatus
_ParseIPv4Address( const char *inStr
, uint8_t inBuffer
[ 4 ], const char **outStr
)
23329 for( ; ( c
= *inStr
) != '\0'; ++inStr
)
23331 if( isdigit_safe( c
) )
23333 v
= ( *dst
* 10 ) + ( c
- '0' );
23334 require_action_quiet( v
<= 255, exit
, err
= kRangeErr
);
23335 *dst
= (uint8_t) v
;
23339 require_action_quiet( segments
<= 4, exit
, err
= kOverrunErr
);
23343 else if( ( c
== '.' ) && sawDigit
)
23345 require_action_quiet( segments
< 4, exit
, err
= kMalformedErr
);
23354 require_action_quiet( segments
== 4, exit
, err
= kUnderrunErr
);
23363 //===========================================================================================================================
23364 // _StringToIPv4Address
23366 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
23367 //===========================================================================================================================
23370 _StringToIPv4Address(
23371 const char * inStr
,
23372 StringToIPAddressFlags inFlags
,
23375 uint32_t * outSubnet
,
23376 uint32_t * outRouter
,
23377 const char ** outStr
)
23387 uint32_t subnetMask
;
23390 require_action( inStr
, exit
, err
= kParamErr
);
23392 // Parse the address-only part of the address (e.g. "1.2.3.4").
23394 err
= _ParseIPv4Address( inStr
, buf
, &inStr
);
23395 require_noerr_quiet( err
, exit
);
23396 ip
= (uint32_t)( ( buf
[ 0 ] << 24 ) | ( buf
[ 1 ] << 16 ) | ( buf
[ 2 ] << 8 ) | buf
[ 3 ] );
23399 // Parse the port (if any).
23405 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPort
), exit
, err
= kUnexpectedErr
);
23406 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) port
= ( port
* 10 ) + ( c
- '0' );
23407 require_action_quiet( port
<= 65535, exit
, err
= kRangeErr
);
23411 // Parse the prefix length (if any).
23419 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPrefix
), exit
, err
= kUnexpectedErr
);
23420 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) prefix
= ( prefix
* 10 ) + ( c
- '0' );
23421 require_action_quiet( ( prefix
>= 0 ) && ( prefix
<= 32 ), exit
, err
= kRangeErr
);
23424 subnetMask
= ( prefix
> 0 ) ? ( UINT32_C( 0xFFFFFFFF ) << ( 32 - prefix
) ) : 0;
23425 router
= ( ip
& subnetMask
) | 1;
23428 // Return the results. Only fill in port/prefix/router results if the info was found to allow for defaults.
23430 if( outIP
) *outIP
= ip
;
23431 if( outPort
&& hasPort
) *outPort
= port
;
23432 if( outSubnet
&& hasPrefix
) *outSubnet
= subnetMask
;
23433 if( outRouter
&& hasPrefix
) *outRouter
= router
;
23434 if( outStr
) *outStr
= inStr
;
23441 //===========================================================================================================================
23442 // _ParseIPv6Address
23444 // Note: Parsed according to the rules specified in RFC 3513.
23445 // Warning: "inBuffer" may be modified even in error cases.
23447 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
23448 //===========================================================================================================================
23450 static OSStatus
_ParseIPv6Address( const char *inStr
, int inAllowV4Mapped
, uint8_t inBuffer
[ 16 ], const char **outStr
)
23452 // Table to map uppercase hex characters - '0' to their numeric values.
23453 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F
23454 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 };
23459 uint8_t * colonPtr
;
23466 // Pre-zero the address to simplify handling of compressed addresses (e.g. "::1").
23468 for( i
= 0; i
< 16; ++i
) inBuffer
[ i
] = 0;
23470 // Special case leading :: (e.g. "::1") to simplify processing later.
23472 if( *inStr
== ':' )
23475 require_action_quiet( *inStr
== ':', exit
, err
= kMalformedErr
);
23478 // Parse the address.
23486 while( ( ( c
= *inStr
++ ) != '\0' ) && ( c
!= '%' ) && ( c
!= '/' ) && ( c
!= ']' ) )
23488 if( ( c
>= 'a' ) && ( c
<= 'f' ) ) c
-= ( 'a' - 'A' );
23489 if( ( ( c
>= '0' ) && ( c
<= '9' ) ) || ( ( c
>= 'A' ) && ( c
<= 'F' ) ) )
23492 check( c
< (int) countof( kASCIItoHexTable
) );
23493 v
= ( v
<< 4 ) | kASCIItoHexTable
[ c
];
23494 require_action_quiet( v
<= 0xFFFF, exit
, err
= kRangeErr
);
23503 require_action_quiet( !colonPtr
, exit
, err
= kMalformedErr
);
23507 require_action_quiet( *inStr
!= '\0', exit
, err
= kUnderrunErr
);
23508 require_action_quiet( ( dst
+ 2 ) <= lim
, exit
, err
= kOverrunErr
);
23509 *dst
++ = (uint8_t)( ( v
>> 8 ) & 0xFF );
23510 *dst
++ = (uint8_t)( v
& 0xFF );
23516 // Handle IPv4-mapped/compatible addresses (e.g. ::FFFF:1.2.3.4).
23518 if( inAllowV4Mapped
&& ( c
== '.' ) && ( ( dst
+ 4 ) <= lim
) )
23520 err
= _ParseIPv4Address( ptr
, dst
, &inStr
);
23521 require_noerr_quiet( err
, exit
);
23524 ++inStr
; // Increment because the code below expects the end to be at "inStr - 1".
23530 require_action_quiet( ( dst
+ 2 ) <= lim
, exit
, err
= kOverrunErr
);
23531 *dst
++ = (uint8_t)( ( v
>> 8 ) & 0xFF );
23532 *dst
++ = (uint8_t)( v
& 0xFF );
23534 check( dst
<= lim
);
23537 require_action_quiet( dst
< lim
, exit
, err
= kOverrunErr
);
23538 n
= (int)( dst
- colonPtr
);
23539 for( i
= 1; i
<= n
; ++i
)
23541 lim
[ -i
] = colonPtr
[ n
- i
];
23542 colonPtr
[ n
- i
] = 0;
23546 require_action_quiet( dst
== lim
, exit
, err
= kUnderrunErr
);
23548 *outStr
= inStr
- 1;
23555 //===========================================================================================================================
23558 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
23559 //===========================================================================================================================
23561 static OSStatus
_ParseIPv6Scope( const char *inStr
, uint32_t *outScope
, const char **outStr
)
23563 #if( TARGET_OS_POSIX )
23565 char scopeStr
[ 64 ];
23572 // Copy into a local NULL-terminated string since that is what if_nametoindex expects.
23575 lim
= dst
+ ( countof( scopeStr
) - 1 );
23576 while( ( ( c
= *inStr
) != '\0' ) && ( c
!= ':' ) && ( c
!= '/' ) && ( c
!= ']' ) && ( dst
< lim
) )
23581 check( dst
<= lim
);
23583 // First try to map as a name and if that fails, treat it as a numeric scope.
23585 scope
= if_nametoindex( scopeStr
);
23588 for( ptr
= scopeStr
; ( ( c
= *ptr
) >= '0' ) && ( c
<= '9' ); ++ptr
)
23590 scope
= ( scope
* 10 ) + ( ( (uint8_t) c
) - '0' );
23592 require_action_quiet( c
== '\0', exit
, err
= kMalformedErr
);
23593 require_action_quiet( ( ptr
!= scopeStr
) && ( ( (int)( ptr
- scopeStr
) ) <= 10 ), exit
, err
= kMalformedErr
);
23605 const char * start
;
23609 for( start
= inStr
; ( ( c
= *inStr
) >= '0' ) && ( c
<= '9' ); ++inStr
)
23611 scope
= ( scope
* 10 ) + ( c
- '0' );
23613 require_action_quiet( ( inStr
!= start
) && ( ( (int)( inStr
- start
) ) <= 10 ), exit
, err
= kMalformedErr
);
23624 //===========================================================================================================================
23625 // _StringToIPv6Address
23627 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
23628 //===========================================================================================================================
23631 _StringToIPv6Address(
23632 const char * inStr
,
23633 StringToIPAddressFlags inFlags
,
23634 uint8_t outIPv6
[ 16 ],
23635 uint32_t * outScope
,
23638 const char ** outStr
)
23641 uint8_t ipv6
[ 16 ];
23652 require_action( inStr
, exit
, err
= kParamErr
);
23654 if( *inStr
== '[' ) ++inStr
; // Skip a leading bracket for []-wrapped addresses (e.g. "[::1]:80").
23656 // Parse the address-only part of the address (e.g. "1::1").
23658 err
= _ParseIPv6Address( inStr
, !( inFlags
& kStringToIPAddressFlagsNoIPv4Mapped
), ipv6
, &inStr
);
23659 require_noerr_quiet( err
, exit
);
23662 // Parse the scope, port, or prefix length.
23673 if( c
== '%' ) // Scope (e.g. "%en0" or "%5")
23675 require_action_quiet( !hasScope
, exit
, err
= kMalformedErr
);
23676 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoScope
), exit
, err
= kUnexpectedErr
);
23678 err
= _ParseIPv6Scope( inStr
, &scope
, &inStr
);
23679 require_noerr_quiet( err
, exit
);
23683 else if( c
== ':' ) // Port (e.g. ":80")
23685 require_action_quiet( !hasPort
, exit
, err
= kMalformedErr
);
23686 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPort
), exit
, err
= kUnexpectedErr
);
23687 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) port
= ( port
* 10 ) + ( c
- '0' );
23688 require_action_quiet( port
<= 65535, exit
, err
= kRangeErr
);
23691 else if( c
== '/' ) // Prefix Length (e.g. "/64")
23693 require_action_quiet( !hasPrefix
, exit
, err
= kMalformedErr
);
23694 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPrefix
), exit
, err
= kUnexpectedErr
);
23695 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) prefix
= ( prefix
* 10 ) + ( c
- '0' );
23696 require_action_quiet( ( prefix
>= 0 ) && ( prefix
<= 128 ), exit
, err
= kRangeErr
);
23699 else if( c
== ']' )
23701 require_action_quiet( !hasBracket
, exit
, err
= kMalformedErr
);
23711 // Return the results. Only fill in scope/port/prefix results if the info was found to allow for defaults.
23713 if( outIPv6
) for( i
= 0; i
< 16; ++i
) outIPv6
[ i
] = ipv6
[ i
];
23714 if( outScope
&& hasScope
) *outScope
= scope
;
23715 if( outPort
&& hasPort
) *outPort
= port
;
23716 if( outPrefix
&& hasPrefix
) *outPrefix
= prefix
;
23717 if( outStr
) *outStr
= inStr
;
23724 //===========================================================================================================================
23725 // _StringArray_Free
23727 // Note: This was copied from CoreUtils because the StringArray_Free function is currently not exported in the framework.
23728 //===========================================================================================================================
23730 static void _StringArray_Free( char **inArray
, size_t inCount
)
23734 for( i
= 0; i
< inCount
; ++i
)
23736 free( inArray
[ i
] );
23738 if( inCount
> 0 ) free( inArray
);
23741 //===========================================================================================================================
23742 // _ParseQuotedEscapedString
23744 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
23745 //===========================================================================================================================
23748 _ParseQuotedEscapedString(
23749 const char * inSrc
,
23750 const char * inEnd
,
23751 const char * inDelimiters
,
23754 size_t * outCopiedLen
,
23755 size_t * outTotalLen
,
23756 const char ** outSrc
)
23758 const unsigned char * src
;
23759 const unsigned char * end
;
23760 unsigned char * dst
;
23761 unsigned char * lim
;
23765 Boolean singleQuote
;
23766 Boolean doubleQuote
;
23768 if( inEnd
== NULL
) inEnd
= inSrc
+ strlen( inSrc
);
23769 src
= (const unsigned char *) inSrc
;
23770 end
= (const unsigned char *) inEnd
;
23771 dst
= (unsigned char *) inBuf
;
23772 lim
= dst
+ inMaxLen
;
23773 while( ( src
< end
) && isspace_safe( *src
) ) ++src
; // Skip leading spaces.
23774 if( src
>= end
) return( false );
23776 // Parse each argument from the string.
23778 // See <http://resources.mpi-inf.mpg.de/departments/rg1/teaching/unixffb-ss98/quoting-guide.html> for details.
23781 singleQuote
= false;
23782 doubleQuote
= false;
23788 // Single quotes protect everything (even backslashes, newlines, etc.) except single quotes.
23792 singleQuote
= false;
23796 else if( doubleQuote
)
23798 // Double quotes protect everything except double quotes and backslashes. A backslash can be
23799 // used to protect " or \ within double quotes. A backslash-newline pair disappears completely.
23800 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
23801 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
23802 // A backslash that does not precede ", \, x, X, or a newline is taken literally.
23806 doubleQuote
= false;
23809 else if( c
== '\\' )
23814 if( ( c2
== '"' ) || ( c2
== '\\' ) )
23819 else if( c2
== '\n' )
23824 else if( ( c2
== 'x' ) || ( c2
== 'X' ) )
23828 if( ( ( end
- src
) >= 2 ) && IsHexPair( src
) )
23830 c
= HexPairToByte( src
);
23834 else if( isoctal_safe( c2
) )
23836 if( ( ( end
- src
) >= 3 ) && IsOctalTriple( src
) )
23838 c
= OctalTripleToByte( src
);
23845 else if( strchr( inDelimiters
, c
) )
23849 else if( c
== '\\' )
23851 // A backslash protects the next character, except a newline, x, X and 2 hex bytes or 3 octal bytes.
23852 // A backslash followed by a newline disappears completely.
23853 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
23854 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
23864 else if( ( c
== 'x' ) || ( c
== 'X' ) )
23867 if( ( ( end
- src
) >= 2 ) && IsHexPair( src
) )
23869 c
= HexPairToByte( src
);
23873 else if( isoctal_safe( c
) )
23875 if( ( ( end
- src
) >= 3 ) && IsOctalTriple( src
) )
23877 c
= OctalTripleToByte( src
);
23891 else if( c
== '\'' )
23893 singleQuote
= true;
23896 else if( c
== '"' )
23898 doubleQuote
= true;
23904 if( inBuf
) *dst
= c
;
23910 if( outCopiedLen
) *outCopiedLen
= (size_t)( dst
- ( (unsigned char *) inBuf
) );
23911 if( outTotalLen
) *outTotalLen
= totalLen
;
23912 if( outSrc
) *outSrc
= (const char *) src
;
23916 //===========================================================================================================================
23917 // _ServerSocketOpenEx2
23919 // Note: Based on ServerSocketOpenEx() from CoreUtils. Added parameter to not use SO_REUSEPORT.
23920 //===========================================================================================================================
23923 _ServerSocketOpenEx2(
23927 const void * inAddr
,
23931 Boolean inNoPortReuse
,
23932 SocketRef
* outSock
)
23942 port
= ( inPort
< 0 ) ? -inPort
: inPort
; // Negated port number means "try this port, but allow dynamic".
23944 sock
= socket( inFamily
, inType
, inProtocol
);
23945 err
= map_socket_creation_errno( sock
);
23946 require_noerr_quiet( err
, exit
);
23948 #if( defined( SO_NOSIGPIPE ) )
23949 setsockopt( sock
, SOL_SOCKET
, SO_NOSIGPIPE
, &(int){ 1 }, (socklen_t
) sizeof( int ) );
23952 err
= SocketMakeNonBlocking( sock
);
23953 require_noerr( err
, exit
);
23955 // Set receive buffer size. This has to be done on the listening socket *before* listen is called because
23956 // accept does not return until after the window scale option is exchanged during the 3-way handshake.
23957 // Since accept returns a new socket, the only way to use a larger window scale option is to set the buffer
23958 // size on the listening socket since SO_RCVBUF is inherited by the accepted socket. See UNPv1e3 Section 7.5.
23960 err
= SocketSetBufferSize( sock
, SO_RCVBUF
, inRcvBufSize
);
23961 check_noerr( err
);
23963 // Allow port or address reuse because we may bind separate IPv4 and IPv6 sockets to the same port.
23965 if( ( inType
!= SOCK_DGRAM
) || !inNoPortReuse
)
23968 name
= ( inType
== SOCK_DGRAM
) ? SO_REUSEPORT
: SO_REUSEADDR
;
23969 err
= setsockopt( sock
, SOL_SOCKET
, name
, (char *) &option
, (socklen_t
) sizeof( option
) );
23970 err
= map_socket_noerr_errno( sock
, err
);
23971 require_noerr( err
, exit
);
23974 if( inFamily
== AF_INET
)
23976 // Bind to the port. If it fails, retry with a dynamic port.
23978 memset( &sip
.v4
, 0, sizeof( sip
.v4
) );
23979 SIN_LEN_SET( &sip
.v4
);
23980 sip
.v4
.sin_family
= AF_INET
;
23981 sip
.v4
.sin_port
= htons( (uint16_t) port
);
23982 sip
.v4
.sin_addr
.s_addr
= inAddr
? *( (const uint32_t *) inAddr
) : htonl( INADDR_ANY
);
23983 err
= bind( sock
, &sip
.sa
, (socklen_t
) sizeof( sip
.v4
) );
23984 err
= map_socket_noerr_errno( sock
, err
);
23985 if( err
&& ( inPort
< 0 ) )
23987 sip
.v4
.sin_port
= 0;
23988 err
= bind( sock
, &sip
.sa
, (socklen_t
) sizeof( sip
.v4
) );
23989 err
= map_socket_noerr_errno( sock
, err
);
23991 require_noerr( err
, exit
);
23993 #if( defined( AF_INET6 ) )
23994 else if( inFamily
== AF_INET6
)
23996 // Restrict this socket to IPv6 only because we're going to use a separate socket for IPv4.
23999 err
= setsockopt( sock
, IPPROTO_IPV6
, IPV6_V6ONLY
, (char *) &option
, (socklen_t
) sizeof( option
) );
24000 err
= map_socket_noerr_errno( sock
, err
);
24001 require_noerr( err
, exit
);
24003 // Bind to the port. If it fails, retry with a dynamic port.
24005 memset( &sip
.v6
, 0, sizeof( sip
.v6
) );
24006 SIN6_LEN_SET( &sip
.v6
);
24007 sip
.v6
.sin6_family
= AF_INET6
;
24008 sip
.v6
.sin6_port
= htons( (uint16_t) port
);
24009 sip
.v6
.sin6_addr
= inAddr
? *( (const struct in6_addr
*) inAddr
) : in6addr_any
;
24010 err
= bind( sock
, &sip
.sa
, (socklen_t
) sizeof( sip
.v6
) );
24011 err
= map_socket_noerr_errno( sock
, err
);
24012 if( err
&& ( inPort
< 0 ) )
24014 sip
.v6
.sin6_port
= 0;
24015 err
= bind( sock
, &sip
.sa
, (socklen_t
) sizeof( sip
.v6
) );
24016 err
= map_socket_noerr_errno( sock
, err
);
24018 require_noerr( err
, exit
);
24023 dlogassert( "Unsupported family: %d", inFamily
);
24024 err
= kUnsupportedErr
;
24028 if( inType
== SOCK_STREAM
)
24030 err
= listen( sock
, SOMAXCONN
);
24031 err
= map_socket_noerr_errno( sock
, err
);
24034 err
= listen( sock
, 5 );
24035 err
= map_socket_noerr_errno( sock
, err
);
24036 require_noerr( err
, exit
);
24042 len
= (socklen_t
) sizeof( sip
);
24043 err
= getsockname( sock
, &sip
.sa
, &len
);
24044 err
= map_socket_noerr_errno( sock
, err
);
24045 require_noerr( err
, exit
);
24047 *outPort
= SockAddrGetPort( &sip
);
24050 sock
= kInvalidSocketRef
;
24053 ForgetSocket( &sock
);
24057 //===========================================================================================================================
24060 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
24061 //===========================================================================================================================
24063 static void * _memdup( const void *inPtr
, size_t inLen
)
24067 mem
= malloc( ( inLen
> 0 ) ? inLen
: 1 ); // If inLen is 0, use 1 since malloc( 0 ) is not well defined.
24068 require( mem
, exit
);
24069 if( inLen
> 0 ) memcpy( mem
, inPtr
, inLen
);
24075 //===========================================================================================================================
24078 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
24079 //===========================================================================================================================
24081 static int _memicmp( const void *inP1
, const void *inP2
, size_t inLen
)
24083 const unsigned char * p1
;
24084 const unsigned char * e1
;
24085 const unsigned char * p2
;
24089 p1
= (const unsigned char *) inP1
;
24091 p2
= (const unsigned char *) inP2
;
24096 c1
= tolower( c1
);
24097 c2
= tolower( c2
);
24098 if( c1
< c2
) return( -1 );
24099 if( c1
> c2
) return( 1 );
24104 //===========================================================================================================================
24107 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
24108 //===========================================================================================================================
24110 static uint32_t _FNV1( const void *inData
, size_t inSize
)
24112 const uint8_t * src
= (const uint8_t *) inData
;
24113 const uint8_t * const end
= src
+ inSize
;
24116 hash
= 0x811c9dc5U
;
24117 while( src
!= end
)
24119 hash
*= 0x01000193;